Skip to content

fix(security): fail closed for unknown agent access tiers#14

Merged
BunsDev merged 1 commit into
mainfrom
fix/agent-tool-filter-fail-closed
May 31, 2026
Merged

fix(security): fail closed for unknown agent access tiers#14
BunsDev merged 1 commit into
mainfrom
fix/agent-tool-filter-fail-closed

Conversation

@BunsDev
Copy link
Copy Markdown
Member

@BunsDev BunsDev commented May 31, 2026

Summary

Closes the fail-open default arm in filter_tools_for_agent that #13 flagged. Unknown access strings now fall back to "read-only" with a stderr warning instead of silently passing the full tool list through.

What changed

  • New coven_shared::canonicalize_access_tier(input) — trims + lowercases, returns Some(&'static str) for canonical tiers, None otherwise. Silent.
  • New coven_shared::resolve_access_tier(input) — returns &'static str, prints a one-line stderr warning when it falls back to DEFAULT_FAMILIAR_ACCESS on unknown input. This is the single chokepoint the CLI tool-filter routes through.
  • CovenFamiliar::resolved_access now returns &'static str and canonicalizes through the helper, so unknown values from ~/.coven/familiars.toml are normalized at load time too.
  • filter_tools_for_agent rewritten: split the read-only and search-only branches into filter_read_only_tools and filter_search_only_tools helpers, route through resolve_access_tier, and add an unreachable!() arm that documents the canonicalization contract.
  • docs/familiars.md updated:
    • Tier table now describes the silent case/whitespace normalization vs. the loud fail-closed for unknown values.
    • search-only description corrected to match the code (Grep, Glob, Read, WebSearch, WebFetch) — the previous "no filesystem access" was wrong.

Behavior matrix

Input Old behavior New behavior
"full" full tools full tools
"read-only" read-only filter read-only filter
"search-only" search-only filter search-only filter
"READ-ONLY", "Read-Only", " full " 🚨 full tools canonicalized silently to the matching tier
"readonly", "i-am-evil", "", anything else 🚨 full tools read-only filter + stderr warning

Test plan

  • cargo test -p claurst-core --lib access_tier — 6 tests pass (canonicalize accepts canonical, normalizes case/whitespace, rejects unknowns; resolve falls back on unknown, passes canonical through; familiar_to_agent_definition still threads the tier).
  • cargo test -p claurst --bin coven-code filter — 5 tests pass (full unchanged, read-only excludes Bash/Edit/Write/NotebookEdit, search-only stays within whitelist, case/whitespace canonicalize silently, genuinely-unknown values fail closed to read-only).
  • cargo check -p claurst — clean.
  • Signed commit verified.

Fixes #13.

The tool-filter pipeline in `filter_tools_for_agent` matched on the
`access` string with a permissive default arm:

    _ => tools, // "full" — allow all tools unchanged

so any unrecognized value (typo, case mismatch, whitespace) silently
granted the full tool set — write, exec, everything. That directly
contradicts the opt-in semantics of `DEFAULT_FAMILIAR_ACCESS = "read-only"`
and meant a typo in `~/.coven/familiars.toml` or `settings.json` could
quietly hand a familiar `Bash`/`Edit`/`Write` access.

This commit:

- Adds `coven_shared::canonicalize_access_tier` to normalize case and
  surrounding whitespace silently (so `"READ-ONLY"`, `" full "`, etc.
  map to the canonical tier without complaint).
- Adds `coven_shared::resolve_access_tier` as the single chokepoint
  that returns one of the three canonical tiers, emitting a one-line
  stderr warning when it falls back to the default for a truly unknown
  value.
- Rewrites `filter_tools_for_agent` to route through `resolve_access_tier`
  and split the read-only / search-only branches into named helpers,
  with an `unreachable!()` arm that documents the canonicalization
  contract.
- Updates `CovenFamiliar::resolved_access` to return `&'static str` and
  canonicalize through the same helper, so unknown values from
  `~/.coven/familiars.toml` are normalized at load.
- Updates `docs/familiars.md` to describe the silent case/whitespace
  normalization vs. the loud fail-closed for unknown values, and
  corrects the `search-only` tier description to reflect what the code
  actually allows (`Grep`, `Glob`, `Read`, `WebSearch`, `WebFetch`)
  instead of the inaccurate "no filesystem access".

Tests added in both crates:

- `claurst_core::coven_shared::tests` — canonicalize accepts canonical
  forms, normalizes case/whitespace, rejects unknown strings; resolve
  falls back to default on unknown, passes canonical through.
- `claurst::tests` (main.rs) — full passes through unchanged, read-only
  excludes Bash/Edit/Write/NotebookEdit, search-only stays within the
  documented whitelist, case/whitespace canonicalize silently, and
  genuinely unknown strings (`"readonly"`, `"i-am-evil"`, `""`,
  `"writeable"`, `"full-access"`) fail closed to read-only.

Fixes #13.

Co-Authored-By: OpenCoven <noreply@opencoven.ai>
Copilot AI review requested due to automatic review settings May 31, 2026 03:45
@BunsDev BunsDev merged commit d952d56 into main May 31, 2026
@BunsDev BunsDev deleted the fix/agent-tool-filter-fail-closed branch May 31, 2026 03:46
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

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.

Security: agent tool-filter fails open for unknown access strings

2 participants