Repository navigation
v0.79.0
Security
-
The kernel reads the file (ADR-0086). The file tools never made
ADR-0073's move to the kernel: they are in-process Go code, so
sandbox.CredentialPathwas the boundary and a miss there was the
file's content in the transcript rather than a missing prompt. Since
the lanes landed that matcher's list gained one entry while its
matching rule changed six times, four of them in one release, each
from a review finding and each a spelling the previous rule had not
considered.read_file,view_image,read_document,file_info,
search_filesandsummarize_filenow perform their reads in a
child of this binary under a profile built from the same
internal/sandboxlist, which denies credential material at the
kernel — measured:cat .envandstat .envrefused,
.env.exampleread,grep -rskipping that one file, at 27 ms per
spawn against a call that already costs a model round. A refused open
is the operator's question, and on approval the read runs in process,
which is the operator lane's authority applied to a file tool. The
matcher stays only to raise the prompt without spending a spawn: a
miss in it now costs a spawn and a blunter prompt, never a leak.
Verified at startup like the shell lanes; where it cannot be
verified the reads stay in process and the banner says so. -
The walks stop hiding names (ADR-0086 §3, withdrawing ADR-0085
§2). The kernel lists names and refuses content, so a name was never
the secret and withholding it was a second rule over the same
unbounded spelling domain.list_filesandlist_treelist a
credential-named entry like any other and carry no credential code at
all;search_filesnames the file it could not read
([not read: sub/.env — reading one needs the operator's approval]),
the shapegrep -rhas.credentialSkipNoteand the walk
withholding are deleted, and with them the contradiction where lagent
reported names and gem-agent reported a count. -
Review fixes to the kernel read path (ADR-0086, independent
review). The first cut deniedfile-read*, which covers
file-read-metadata; Go'sos.Rootlisting stats every entry, so one
credential-named file failed the whole directory read and
search_filesansweredno matches (0 files scanned)for the entire
project — silently, exit 0. The deny is nowfile-read-data: content
refused, stat left, the walk intact, and the[not read: …]note the
ADR promised actually reached. Also fixed: the child pipe silently
halved reads over 85 KB;file_infocollapsed a refusal into "not
found" so the operator was never asked; a cancelledsearch_files
lost its partial result; the child did not getsandbox.ChildEnv; any
EACCESbecame a credential question; the hidden subcommand ran any
tool; a missing work directory failed every read and leaked an
absolute path; the probe directory leaked; the profile lacked the tty
hardening; the degradation note named no next command. The reason all
of it shipped green is that no test ran the real child under the real
profile —TestFileChildUnderTheRealProfilenow does, and it is what
caught the rest on re-run.
Changed
- The operator's environment is no longer filtered, and the runtime's
own variables no longer reach any child (ADR-0087). The read lane's
environment scrub is deleted: it covered one child of six — the write
and operator lane shells, the!shell, MCP servers and hooks all
received the environment whole — and the one it covered passed
OPENAI_KEY,GH_PATandGPG_PASSPHRASEwhile catching
NPM_TOKEN. Which variable the program a command runs needs is not a
question gem-agent can answer, and an allowlist would move the
unbounded list rather than remove it. A bareenvin the read lane
now prints what you exported, as it already did in every other
lane; the environment gem-agent is launched with reaches every child
it spawns, and the remedy is the operator's: launch it from a shell
that does not hold what you would not give the model.
In exchange, the one set the runtime does know exhaustively — its own
GEMAGENT_namespace — is now handled correctly in the other
direction.sandbox.ChildEnvremovesGEMAGENT_PROJECT,
GEMAGENT_LOCATION,GEMAGENT_MODEL,GEMAGENT_STATE_DIRand
GEMAGENT_MCP_STDERRfrom every child, while the three exported for
children (GEMAGENT_WORK_DIR,GEMAGENT_SESSION_ID,
GEMAGENT_PROJECT_DIR) pass. The two halves partition the namespace
and an architecture test refuses aGEMAGENT_literal in neither, so
a variable a later release reads for itself never reaches a child —
the class system risk review R01 named, closed by construction rather
than by a regex recognising the wordkey. A nestedgem-agentin a
shell lane now reads its own config file instead of inheriting the
parent's; an MCP server wanting aGEMAGENT_value takes it from the
envblock of the MCP configuration. A companion run from a shell
lane that reads gem-agent's own state root —gem-usage-lensdoes,
to measure an isolated runtime where it writes — no longer inherits
GEMAGENT_STATE_DIRand takes it from its own--sessions-rootor
[sources]instead.