fix(config): trust the invoking project, and stop leaking what it must not decide
Review found five real defects in the configuration-source work, all confirmed against the code rather than argued: 1. The note claimed --config outranks settings.yaml. It does not: the settings seam registers a plugin's cordis entry config as the `base` layer and the user section layers over it, and the seam cannot tell a shipped value from a --config one. The note now states shipped reality and names --config-replace as the lever for a deployment that must win. Separately, a literal `apiKey` in settings outranked both the environment and .credentials.yaml — the field is removed, so configuration carries a reference and nothing else. 2. DEEPSEEK_SEARCH_BASE_URL was functionally deleted: the shipped inline went away without the provider learning to read it. It now resolves from the environment snapshot, as the README always claimed. 3. The bootstrap deny list missed the interpreter start-up hooks. BASH_ENV is the sharpest: `bash -c` sources it on every bash tool call, so a project .env could run a file of its choosing before every command. The list now covers BASH_ENV and its per-language siblings, the Git hook commands, and the remaining preload and CA variables, organised by what a variable does rather than which runtime owns it. 4. YAML parse errors quoted the offending source line — which in a credentials document is the secret — into boot stderr and the watcher's logger. Only the error code and position are reported now, in credentials-local and settings-local alike, pinned by a test that asserts the secret is absent. 5. 0600 governed only files the harness wrote. A hand-created 0644 document was read normally. POSIX now checks the mode before reading contents, at boot and on every reload; Windows has no mode to inspect and is skipped rather than faked. The project a session is launched in is trusted by default, with no prompt and no stored trust record: it may supply its own endpoint, ordinary variables, and a key ranked below the managed store. Trust stops at the harness itself — a discovered file still cannot set DSH_PERMISSION_MODE, PATH, BASH_ENV, or the rest, because those take effect with no user action, before any turn, outside the permission policy and the sandbox.
This commit is contained in:
@@ -146,29 +146,51 @@ const BOOTSTRAP_NAMES = new Set([
|
||||
// Process launch and module resolution.
|
||||
'PATH', 'HOME', 'USERPROFILE', 'SHELL',
|
||||
'NODE_OPTIONS', 'NODE_PATH', 'NODE_EXTRA_CA_CERTS',
|
||||
'LD_PRELOAD', 'LD_LIBRARY_PATH',
|
||||
'LD_PRELOAD', 'LD_LIBRARY_PATH', 'LD_AUDIT',
|
||||
// Interpreter start-up hooks: each of these makes a runtime execute a file
|
||||
// of the setter's choosing on every invocation, before the program runs.
|
||||
// `BASH_ENV` is the sharpest — the bash tool spawns `bash -c`, which sources
|
||||
// it every time — but every runtime an agent shells out to has one.
|
||||
'BASH_ENV', 'ENV', 'SHELLOPTS', 'BASHOPTS',
|
||||
'PERL5OPT', 'PERL5LIB', 'PYTHONSTARTUP', 'PYTHONPATH', 'RUBYOPT', 'RUBYLIB',
|
||||
'JAVA_TOOL_OPTIONS', '_JAVA_OPTIONS', 'JDK_JAVA_OPTIONS',
|
||||
// Version-control hooks that run a command on the setter's behalf.
|
||||
'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_EXTERNAL_DIFF', 'GIT_PAGER', 'GIT_EDITOR',
|
||||
'EDITOR', 'VISUAL', 'PAGER',
|
||||
// Network reach and trust.
|
||||
'SSL_CERT_FILE', 'SSL_CERT_DIR',
|
||||
'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY',
|
||||
'REQUESTS_CA_BUNDLE', 'CURL_CA_BUNDLE',
|
||||
])
|
||||
|
||||
/** Name prefixes no discovered file may set. */
|
||||
const BOOTSTRAP_PREFIXES = ['DSH_', 'XDG_', 'DYLD_']
|
||||
const BOOTSTRAP_PREFIXES = ['DSH_', 'XDG_', 'DYLD_', 'BASH_FUNC_']
|
||||
|
||||
/**
|
||||
* Whether a variable may come only from the inherited process environment.
|
||||
*
|
||||
* A bootstrap variable decides how a process launches (`PATH`, `NODE_OPTIONS`,
|
||||
* `LD_PRELOAD`), where code or model-visible instructions load from (`DSH_*`
|
||||
* covers the Harness home, the agents home, and the bundled skill root), or
|
||||
* how the network is reached and trusted (proxy and CA variables). A file the
|
||||
* harness merely finds — including one a model can write inside the workspace
|
||||
* — must never set them, so they are rejected at load rather than ranked
|
||||
* below another layer.
|
||||
* The invoking project is trusted to *configure* the agent's work — its
|
||||
* endpoints, its ordinary variables, even a credential. It is not trusted to
|
||||
* change the harness itself, and that is what a bootstrap variable does: it
|
||||
* decides how a process launches (`PATH`, `NODE_OPTIONS`, `LD_PRELOAD`), what
|
||||
* code a runtime executes before the program it was asked to run (`BASH_ENV`
|
||||
* and its per-language siblings, the Git hook commands), where model-visible
|
||||
* instructions load from (`DSH_*` covers the Harness home, the agents home,
|
||||
* and the bundled skill root), or how the network is reached and trusted
|
||||
* (proxy and CA variables).
|
||||
*
|
||||
* The whole `DSH_*` namespace is denied rather than an audited subset: the
|
||||
* harness's own switches are exactly the ones a hostile project would want,
|
||||
* and a new switch must not become settable by forgetting to list it.
|
||||
* The distinction is that these take effect with no user action, before any
|
||||
* turn, outside the permission policy and the sandbox — `DSH_PERMISSION_MODE`
|
||||
* would switch off the approvals that make trusting a project meaningful at
|
||||
* all, and `BASH_ENV` runs a file of the project's choosing on every single
|
||||
* `bash -c` the tool issues. Trusting a project's code to run under the
|
||||
* agent's policy is not the same as letting it rewrite that policy.
|
||||
*
|
||||
* They are therefore rejected at load rather than ranked below another layer:
|
||||
* a user who wrote one into a file believes it applies, and silently ignoring
|
||||
* it is its own failure. The whole `DSH_*` namespace is denied rather than an
|
||||
* audited subset, because a switch added later must not become settable by
|
||||
* being forgotten.
|
||||
* @param name - the variable name.
|
||||
* @returns true when only the inherited environment may supply it.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user