fix(#2256): resolve capability-registry configSchema defaults in config-get (#2299)

cmdConfigGet resolved absent keys through only the 4-key SCHEMA_DEFAULTS
map, so the ~42 registry-declared configSchema defaults (including the
workflow.security_enforcement security gate, default true) returned
'Key not found' (rc=1) — diverging from the runtime's own
resolveConfigKey Level-4 resolver and letting '... || echo false'
guards silently read the gate as disabled.

Add a resolveSchemaDefault helper that layers SCHEMA_DEFAULTS over the
already-imported getCapabilityConfigSchema(cwd) accessor, wired into all
three absent-key branches. --default flag precedence, the legacy 4 keys,
and 'Key not found' for genuinely unknown keys are preserved.

Two pre-existing, security-relevant defects in the same surface, found
while writing the regression tests, are fixed inline (no-defer policy):
- The --default fallback path never masked secret-named keys, printing
  e.g. 'config-get brave_search --default <secret>' in plaintext. All
  six default-emission sites now route through emitResolvedDefault,
  which applies the same isSecretKey/maskSecret masking the found-key
  path uses.
- Dotted-key traversal used raw bracket access, so 'config-get __proto__'
  / 'constructor' walked the JS prototype chain and returned internals
  at rc=0 instead of erroring. Each segment is now own-property-gated.

Regression tests folded into tests/config-get-default.test.cjs cover
registry defaults (boolean/enum/number, read live from the registry),
the no-file/mid-traversal/final-undefined branches, --default and legacy
precedence, prototype-pollution keys, secret masking, and the
Key-not-found vs No-config-file negative cases.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-07-15 11:47:15 -04:00
committed by GitHub
parent 315d94f6d4
commit 9c65a2ea02
4 changed files with 492 additions and 21 deletions

View File

@@ -0,0 +1,5 @@
---
type: Security
pr: 2299
---
**`query config-get` no longer leaks secret values or walks the prototype chain** — the `--default` fallback path printed secret-named keys (e.g. `brave_search`) in plaintext instead of masking them, and dotted-key traversal used raw property access so `config-get __proto__`/`constructor` resolved to JavaScript internals at exit 0 instead of erroring. Both absent-key resolution and traversal are now masked and own-property-gated. (#2256)

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 2299
---
**`query config-get` now returns capability-registry defaults for absent keys** — keys declared with a default in the capability registry (e.g. `workflow.security_enforcement`, which defaults to `true`) previously reported "Key not found" (exit 1) when missing from config.json, diverging from the runtime's own resolver and letting `... || echo false` guards silently read the security gate as disabled. config-get now resolves these through the same registry defaults the runtime uses. (#2256)