Context
F159 Slice 2 (of 5) per maintainer's implementation plan in RFC #434.
ADR-001 amendment (Slice 1, PR #452) established the opt-in native provider path with 6 security hard gates. This slice implements the first two gates:
- Account-binding fail-closed — credential resolution via
catConfig.accountRef → resolveForClient, no wildcard scan fallback
- Symlink-safe sandbox — 3-stage path validation (lexical boundary → shared
isDenylisted() → fs.realpath escape check)
Scope
New files
packages/api/src/domains/cats/services/agents/providers/catagent/catagent-credentials.ts — fail-closed credential resolution (env override → bound account)
packages/api/src/domains/cats/services/agents/providers/catagent/catagent-tools.ts — read-only tool registry + symlink-safe sandbox
packages/api/test/catagent-security-baseline.test.js — 25 tests covering credentials, path validation, denylist, descendant filtering, symlink escape, tool registry, and rg injection prevention
Security properties
- Path validation reuses shared
isDenylisted() from workspace-security.ts (AC-B2)
list_files filters denylisted entries from readdir results
search_content filters denylisted paths from rg output
rg injection prevented via -- separator before untrusted pattern
- No write/edit/delete, no shell/exec, no network tools (ADR-001 F159 boundary)
Acceptance criteria
Refs: RFC #434, ADR-001 PR #452, spike PR #397 (closed)
Context
F159 Slice 2 (of 5) per maintainer's implementation plan in RFC #434.
ADR-001 amendment (Slice 1, PR #452) established the opt-in native provider path with 6 security hard gates. This slice implements the first two gates:
catConfig.accountRef → resolveForClient, no wildcard scan fallbackisDenylisted()→fs.realpathescape check)Scope
New files
packages/api/src/domains/cats/services/agents/providers/catagent/catagent-credentials.ts— fail-closed credential resolution (env override → bound account)packages/api/src/domains/cats/services/agents/providers/catagent/catagent-tools.ts— read-only tool registry + symlink-safe sandboxpackages/api/test/catagent-security-baseline.test.js— 25 tests covering credentials, path validation, denylist, descendant filtering, symlink escape, tool registry, and rg injection preventionSecurity properties
isDenylisted()fromworkspace-security.ts(AC-B2)list_filesfilters denylisted entries fromreaddirresultssearch_contentfilters denylisted paths fromrgoutputrginjection prevented via--separator before untrusted patternAcceptance criteria
resolveApiCredentialsreturns null (fail-closed) when no bound account resolvesresolveSecurePathblocks../traversal, sibling prefix attacks, symlink escapes.env*,*.pem,*.key,id_rsa*,.git/,secrets/list_filesdoes not expose denylisted entriessearch_contentdoes not return denylisted descendantsRefs: RFC #434, ADR-001 PR #452, spike PR #397 (closed)