fix(agent-mode): reject invalid env variable names before commit (#2502)

`rowsToRecord` was persisting every non-empty trimmed key, so names
failing `ENV_VAR_NAME_RE` (e.g. `BAD KEY`, `FOO=BAR`) flowed through
`onChange` into settings and were applied verbatim by backend spawn
paths, which read `envOverrides` directly without re-sanitizing.
Filter invalid rows at the commit boundary so the persisted record
and subprocess env only contain valid POSIX identifiers.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Zero Liu 2026-05-20 23:28:13 -07:00 committed by GitHub
parent cdcd9bb601
commit 898c31ac1b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -43,6 +43,10 @@ function rowsToRecord(rows: Row[]): Record<string, string> | undefined {
for (const row of rows) {
const name = row.name.trim();
if (!name) continue;
// Skip names that fail validation so malformed keys never reach
// backend spawn paths, which read `envOverrides` from settings
// directly without re-sanitizing.
if (!ENV_VAR_NAME_RE.test(name)) continue;
out[name] = row.value;
}
return Object.keys(out).length > 0 ? out : undefined;
@ -60,9 +64,10 @@ const COMMIT_DEBOUNCE_MS = 400;
* the last edit always lands. The parent's `value` is consulted only at
* mount external reloads while the editor is open are uncommon.
*
* Validation is permissive: invalid names surface an inline warning but
* don't block typing. `sanitizeEnvOverrides` drops anything malformed at
* save time, so the persisted shape is always safe.
* Validation is permissive in the input: invalid names surface an inline
* warning but don't block typing. `rowsToRecord` then drops malformed
* rows before commit, so the persisted record (and the env passed to
* backend subprocesses) only ever contains valid POSIX identifiers.
*/
export const EnvOverridesSetting: React.FC<Props> = ({
value,