security: close config trust-boundary gaps and gate completion loader

Merged
JE
4mo ago
+3642-169

Close config trust-boundary gaps and gate the completion loader

(Threat: T2 malicious repo/parent-dir .basecamp/config.json; cleartext key leak.)

The existing untrusted guard already gates base_url/profiles/llm_provider/llm_endpoint/llm_api_key. This closes the siblings that slipped through:

  • cache_dir / cache_enabled — an untrusted repo config could redirect every cache write (completion, resilience, TUI workspace, recents, traces) to any user-writable path. Now trust-gated; accepted values are filepath.Cleaned.
  • llm_model — trust-gated (silent paid-model substitution).
  • llm_max_concurrent / llm_token_budget — trust-gated (paid-LLM cost amplification).
  • llm_endpoint — scheme-validated on accept (rejects file:// etc.). HTTPS enforcement (so an http:// endpoint can't leak llm_api_key in cleartext) is applied at consumption via summarize.ValidateEndpoint in workspace.NewSession — not at startup in root.go — so a bad llm_endpoint can't brick unrelated commands that never use it.
  • Completion loader (completion/cache.go, completer.go) — repo/local profile loading now goes through the same TrustStore.IsTrusted check as the main config; completion descriptions are stripped of control chars.

Tests

config_test.go adds reject-from-local / accept-from-global / llm_endpoint-scheme cases.


Part 4/6 of the stacked security series. Base: security/03-oauth-hardening. (Shares root.go with part 5 — hence the stack ordering.)

📚 Stack (merge bottom-up)

  1. #478 — reject foreign-host URLs in api (base main)
  2. #479 — strip ANSI/OSC escapes from output
  3. #480 — harden OAuth discovery / token endpoints
  4. #481 — close config trust-boundary gaps + completion gate
  5. #482 — tighten config-dir perms + plugin scope argv
  6. #483 — bump toolchain/x-net + CI gates

Each is independent except #482 depends on #481 (shared root.go). #478 can land first/alone.