Skip to content

fix(security): P0-1 config/permissions trust model — deny-by-default project config + grant provenance#103

Merged
shoom1 merged 6 commits into
developfrom
fix/p01-config-trust-model
Jul 12, 2026
Merged

fix(security): P0-1 config/permissions trust model — deny-by-default project config + grant provenance#103
shoom1 merged 6 commits into
developfrom
fix/p01-config-trust-model

Conversation

@shoom1

@shoom1 shoom1 commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes P0-1 (Critical) from the 2026-07-10 comprehensive review: repository-controlled config and permission grants formed a host-RCE chain. A cloned/untrusted repo's ./.{app}/settings.json could select the stateful (host) executor backend, container image/user, bind mounts, outputs dir, OS-sandbox policy, and raw LLM logging (only permissions_enabled was stripped); and its ./.{app}/permissions.local.json allow-grants loaded as trusted and could be force-tracked into the repo. This PR establishes an explicit trust model: user/admin config (env vars, ~/.{app}) controls security boundaries; project config is untrusted. 6 commits; offline suite 1941 passed, 0 failed.

What's fixed

Half 1 — deny-by-default project settings (config.py). The project ./.{app}/settings.json and a cwd-relative .env are now restricted to an explicit allowlist _PROJECT_SETTABLE_KEYS of 23 benign fields (model/behavior, retry & request timeouts, sandbox resource limits, non-exec tool config, session_store, display/logging verbosity) via a wrapping _AllowlistFilterSource that keeps only allowlisted keys and logs a warning per dropped key (drops, never raises). Everything else — stateful_executor_backend, sandbox_image/sandbox_container_user/sandbox_data_mounts/sandbox_outputs_dir, os_sandbox_*, skill_scripts_enabled, skills_dirs, shell_sandbox_type/shell_docker_image, raw_llm_logging, workspace_dir, permission rules, secrets — is dropped. Real environment variables and the user ~/.{app}/settings.json stay fully trusted. The .env guard inspects every env_file entry with expanduser() and filters if any is cwd-relative (fail-safe over-filter); an absolute / ~-user-level / all-absolute-list env_file stays trusted.

Half 2 — grant provenance (settings_persistence.py, permissions/store.py, permissions/engine.py). Interactive "Allow always" grants now persist to a user-side ~/.{app}/project_grants.json keyed by the resolved project path (was: repo-local ./.{app}/permissions.local.json). The engine loads only the current project's entry (trusted allow+deny) and no longer loads the repo-local file at all; get_project_local_permissions_path is removed. A clone at a different path carries no grants (re-grant on first use). No migration. The DENY-only project settings.json load is preserved (a repo can still tighten policy).

Threat model

The untrusted parties are (a) a cloned/malicious repository's project-local config and (b) model-controlled data. Neither can now flip a security boundary or pre-authorize a tool. Verified end-to-end: a repo shipping sensitive settings.json + a permissions.local.json allow-rule has the sensitive settings dropped and the allow-rule untrusted, so a gated tool call still reaches the approval prompt.

Verification

  • Offline suite: 1941 passed, 1 skipped (gated LaTeX), 26 xfailed, 0 failed. New tests are host-side (real temp dirs, isolated $HOME, real settings/.env/grant files): tests/test_config_trust.py (allowlist + .env trust tiers) and tests/permissions/test_grant_provenance.py (grant relocation, clone-path isolation, chain blocked).
  • Built via subagent-driven development (per-task TDD + spec/quality review) and an opus whole-branch review. Its catches, all fixed: I-1 (a ~/... env_file was wrongly filtered — pydantic expands ~, but Path.is_absolute() doesn't), I-2 (a list env_file with a cwd-relative entry stayed trusted — fail-open), M-1 (a "never bricks the app" overclaim). The 23-key allowlist was verified against the live model — the design spec's illustrative names were corrected to the real fields.

Not in scope (separate cycles)

  • P0-5 (webfetch SSRF).
  • Deferred P0-1 sub-items: host/container capability split, container-image digest pinning, bind-mount special-file rejection.
  • Policy note (deferred): orchestrator remains project-settable (both backends enforce permissions; flagged by the review, kept per the approved spec).

https://claude.ai/code/session_01SwkKXQpy3JLEqrPkQA8QRv

@shoom1 shoom1 merged commit 1440b93 into develop Jul 12, 2026
2 checks passed
@shoom1 shoom1 deleted the fix/p01-config-trust-model branch July 12, 2026 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant