Stop handing a Bot's shell the deployment's secrets - #69
Conversation
A command ran with the whole environment of the process that started it. That process needs COMPUTER_TOKEN to authenticate its own callers, and in the one-container image the same environment carries DATABASE_URL, KEY_ENCRYPTION_KEY, the licence and the model key, because there is one environment and every service reads it. So the Bot was handed the credential that exists to keep the Bot out. COMPUTER_TOKEN is the only thing in front of the control surface on 4100, and the shell runs inside the process that serves it, so one command could drive the browser over loopback with no policy decision and no audit row. The gateway was optional for the one actor it exists to constrain. The database URL and the encryption key together opened the credential vault and the trail that is supposed to record what happened. A command now gets HOME, PATH, DEBIAN_FRONTEND and the locale. An allow-list rather than a deny-list, because the name worth removing is the one nobody thought of, and a variable added next year is covered without anybody remembering this file. DEBIAN_FRONTEND is not incidental: apt-get otherwise waits for an answer nobody is there to give, and the command times out looking like a broken package rather than a prompt. The test asserts the whole set rather than listing names to exclude, and runs a real shell, because the question is what a process actually inherits and a mock would only confirm what we believed it inherits.
|
Superseded by #68, which does this better and was opened first. #68 covers everything here and more: the proxy variables (this PR would have broken The one thing worth carrying over is The verification from here also applies to #68 and I have added it to that PR: driven through the Bot on a running deployment, |
What this changes
A Bot's shell command ran with
{...process.env}. That process requiresCOMPUTER_TOKEN, and in theone-container image the same environment carries
DATABASE_URL,KEY_ENCRYPTION_KEY, the licence andthe model key, because there is one environment and every service reads it.
COMPUTER_TOKENis the only thing in front of the control surface on 4100, and the shell runs insidethe process that serves it. So one command could drive the browser over loopback with no policy
decision and no audit row: the gateway was optional for the one actor it exists to constrain. The
database URL and the encryption key together opened the credential vault and the audit trail meant to
record what happened.
A command now receives
HOME,PATH,DEBIAN_FRONTENDand the locale. An allow-list, not adeny-list, because the name worth removing is the one nobody thought of.
Where it runs
Boundary and audit
Changelog
Unreleased.Proof
Driven in the running app on :3010, through the Bot, not only unit tested. Asked General Assistant
to run
env | cut -d= -f1 | sortbefore and after, same conversation, same command:COMPUTER_TOKENgone. What remains is the allow-list plus the four bash sets itself. The command stillworks:
whoamiandpwdreturned, exit 0, in/workspace.Driving it caught something a unit test could not: the first run still leaked, because the deployment
talks to the shared computer at
AGENT_COMPUTER_URLrather than a per-Bot container, so the fix wasnot in the path until that container was rebuilt. A green unit test would have read as done.
Also found and fixed in review: my first version of the test set
DATABASE_URLon the realprocess.env, which broke two unrelated tests that read one. It now asserts the whole variable setusing a marker name nothing else reads.
bun test748 pass / 0 fail (745 baseline plus 3 new). Red/green verified both directions:reverting the fix fails 2 of the 3, restoring it passes all 3.
bun run typecheckexit 0,biome lintandbiome formatclean.Not fixed here
Adjacent, same file, deliberately left for their own changes:
bash -lcsources/workspace/.bash_profile, which a Bot can write viacomputer_write_file, so a later command runs itwhile the audit row records only the innocuous text. And output accumulates unbounded before it is
clamped. Say the word and I will take them.