Skip to content

feat(quarantine): static scan + review gate for remote installs (SPEC-005 Part B, fixes #52) - #64

Merged
nmccready merged 2 commits into
mainfrom
feat/issue-52-quarantine-scan
Jul 3, 2026
Merged

feat(quarantine): static scan + review gate for remote installs (SPEC-005 Part B, fixes #52)#64
nmccready merged 2 commits into
mainfrom
feat/issue-52-quarantine-scan

Conversation

@nmccready

Copy link
Copy Markdown
Contributor

Closes out #52 / SPEC-005 Part B — remote content is guilty until a human reviews it (the ClawHub supply-chain posture, now default).

Flow

pull/update ──▶ scan ──▶ .agents/.quarantine/   (invisible to sync & index — dot-dir)
                              │
                 sync-agents quarantine          # findings per artifact
                              │
                 approve <name>|--all [--force]  # CRITICAL blocks without --force
                 reject <name>|--all             # delete without installing

What ships

  • Scanner (scan.go): net-then-execute (curl|bash, iwr|iex, base64→sh), credential access (upgraded to CRITICAL when the same file talks to the network), obfuscation (≥120-char base64 blobs, zero-width/bidi Unicode = CRITICAL), prompt-injection phrasing ("ignore previous instructions" = CRITICAL), exec-surface info notes, binary-file flags. Dumb by design — regexes force a human look, they don't adjudicate.
  • Quarantine store (quarantine.go): artifact + pending.json (origin, entry-level lock record, findings) under .agents/.quarantine/<bucket>/…. Empty quarantine leaves zero residue.
  • Approve semantics: promotes, writes origin, applies the lock — with two safety properties: forced approvals over CRITICAL findings are recorded in sources.lock as approved_with_findings (auditable), and tree entries only lock when their last artifact is approved — a half-approved tree never reads as current.
  • Escape hatches, all loud: --trust on pull/update (scan still prints to stderr), quarantine = off in .agents/config (default ON).
  • CLI: quarantine / approve / reject (+ --global everywhere).

Tests

9 new go tests: scanner heuristic table, gate-not-install (live tree + lock untouched), approve happy/blocked/forced-with-audit, reject, trust-prints-findings, tree partial-approve lock semantics. Source package coverage now 72.5%. Full suite green (go + all bats — package-level default stays off, so SPEC-003's tests run unchanged; the CLI wires the default ON from config).

Remaining from SPEC-005

Part C (sandboxed exec wrapper: sandbox-exec/bwrap) stays future work — quarantine + lockfile + hardened fetch kill ~90% of the install-time risk. Also future: routing legacy import through the same gate (it scans nothing today; it's already a thin wrapper slated to fold into source add).

🤖 Generated with Claude Code

…-005 Part B, fixes #52)

Remote content is guilty until reviewed:

- pull/update park fetched artifacts in .agents/.quarantine/ (dot-dir:
  invisible to sync and index by construction) after a static scan —
  net-exec (curl|bash), credential access + network, obfuscation
  (base64 blobs, zero-width/bidi Unicode), prompt-injection phrasing
- `sync-agents quarantine` lists pending artifacts with findings;
  `approve <name>|--all` promotes (CRITICAL findings block without
  --force; forced approvals recorded in sources.lock as
  approved_with_findings); `reject` deletes
- tree entries quarantine as a unit; the entry lock applies only when
  the last artifact is approved — a half-approved tree never reads
  as current
- `--trust` bypasses the gate per invocation (scan still prints);
  `quarantine = off` in .agents/config disables (default ON)

Part A (fetch hardening) shipped in #62; Part C (sandboxed exec
wrapper) remains future work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

🤖 AI Code Review Complete

Review performed on changed files. Check inline comments for specific findings.

Severity Levels:

  • 🔴 Critical: Must fix (bugs, security, data loss)
  • 🟡 Warning: Should fix (performance, maintainability)
  • 🔵 Suggestion: Nice to have (style, alternatives)
  • Nit: Optional (cosmetic)

Repo-wide coverage dipped to 69.6% (gate: 70%) because the new
CmdQuarantine/CmdApprove/CmdReject wrappers and ReadConfigQuarantine
were exercised only through the source package. Drives pull →
quarantine → approve → current through the App layer with an
in-memory fetcher. Total now 71.7%.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

🤖 AI Code Review Complete

Review performed on changed files. Check inline comments for specific findings.

Severity Levels:

  • 🔴 Critical: Must fix (bugs, security, data loss)
  • 🟡 Warning: Should fix (performance, maintainability)
  • 🔵 Suggestion: Nice to have (style, alternatives)
  • Nit: Optional (cosmetic)

@nmccready
nmccready merged commit 03f358b into main Jul 3, 2026
6 checks passed
@nmccready
nmccready deleted the feat/issue-52-quarantine-scan branch July 3, 2026 14:37
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