Skip to content

feat(F159): security baseline — account-binding + symlink-safe sandbox #459

@bouillipx

Description

@bouillipx

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:

  1. Account-binding fail-closed — credential resolution via catConfig.accountRef → resolveForClient, no wildcard scan fallback
  2. 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

  • resolveApiCredentials returns null (fail-closed) when no bound account resolves
  • resolveSecurePath blocks ../ traversal, sibling prefix attacks, symlink escapes
  • Denylist blocks .env*, *.pem, *.key, id_rsa*, .git/, secrets/
  • list_files does not expose denylisted entries
  • search_content does not return denylisted descendants
  • 25/25 tests green, Biome + TypeScript lint clean
  • Codex review: approved after 2 rounds (3 findings fixed + 1 descendant-filtering P1 fixed)

Refs: RFC #434, ADR-001 PR #452, spike PR #397 (closed)

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature:F159CatAgent Native Provider — Opt-in API PathtriagedMaintainer reviewed, replied, and made an initial triage decision

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions