Skip to content

secure-agent-setup: layered credential isolation + 7-day pinned tools#11

Merged
potiuk merged 1 commit into
mainfrom
secure-agent-setup
Apr 29, 2026
Merged

secure-agent-setup: layered credential isolation + 7-day pinned tools#11
potiuk merged 1 commit into
mainfrom
secure-agent-setup

Conversation

@potiuk

@potiuk potiuk commented Apr 29, 2026

Copy link
Copy Markdown
Member

Summary

Adds the framework's recommended secure agent setup — a layered defence for running Claude Code (or any other SKILL.md-aware agent) against pre-disclosure CVE content without unfettered host-credential access. The framework dogfoods the config in .claude/settings.json; adopters scaffold their own copy from the example block in the new doc.

The setup is four layers, each implemented in this PR:

Layer Mechanism
0. Clean env tools/agent-isolation/claude-iso.shenv -i + tiny passthrough list
1. FS sandbox .claude/settings.json sandbox.enabled: true + bubblewrap (Linux) / Seatbelt (macOS)
2. Tool permissions .claude/settings.json permissions.deny for Read / Edit / Write / Bash
3. Forced confirmation .claude/settings.json permissions.ask for git push, gh pr create, etc.

7-day cooldown for system-tool versions

Mirrors the framework's existing 7-day cooldown convention ([tool.uv] exclude-newer = "7 days" in pyproject.toml; weekly Dependabot with 7-day cooldown in dependabot.yml). Every host-system tool the secure setup depends on is pinned in tools/agent-isolation/pinned-versions.toml to a version released at least 7 days before the manifest was last touched.

Tool Pinned Released Purpose
bubblewrap 0.11.1 2026-03-21 Linux user-namespace sandbox (filesystem layer)
socat 1.8.1.1 2026-03-13 TCP relay for the network allowlist
claude-code 2.1.117 2026-04-22 Agent runtime (claude-code v2.1.123 from today is not picked up — within 7-day cooldown)

A side-effect-free check script (check-tool-updates.sh) compares pinned versions to upstream and prints upgrade candidates that have themselves aged past 7 days. No auto-install, no auto-edit of the manifest, no auto-PR — bumps require explicit maintainer review per the procedure in secure-agent-setup.md. The doc also walks through wiring the script into a weekly /schedule routine for ongoing visibility.

Files added

File Purpose
secure-agent-setup.md User-facing doc: threat model, layered defence, install commands per distro, bump procedure, adopter scaffold, verification, residual risks.
.claude/settings.json Framework's dogfooded secure config — sandbox + permissions for cwd-rooted Claude Code sessions in this repo.
tools/agent-isolation/pinned-versions.toml Pinned upstream versions with released dates that satisfy the 7-day cooldown.
tools/agent-isolation/check-tool-updates.sh Side-effect-free upstream-vs-pinned drift checker.
tools/agent-isolation/claude-iso.sh Layer-0 wrapper.
tools/agent-isolation/README.md Directory overview.

Files updated

README.md and AGENTS.md link to the new doc from the agent-prerequisites and Local-setup sections.

Test plan

  • prek run --all-files passes (doctoc generated TOCs for the two new .md files; all other hooks clean).
  • ✅ Check script runs end-to-end against upstream, prints expected drift table:
    TOOL           PINNED     PINNED@      UPSTREAM   UPSTREAM@    STATUS
    bubblewrap     0.11.1     2026-03-21   0.11.1     2026-03-21   ✓ up to date
    socat          1.8.1.1    2026-03-13   1.8.1.1    2026-02-12   ✓ up to date
    claude-code    2.1.117    2026-04-22   2.1.117    2026-04-22   ✓ up to date
    
    Latest claude-code v2.1.123 is intentionally not picked up — within 7-day cooldown.
  • .claude/settings.json parses as valid JSON (python3 -c 'import json; json.load(open(".claude/settings.json"))').
  • The actual filesystem-sandbox enforcement requires bubblewrap on the host — first invocation of Claude Code in this repo after merge will exercise it end-to-end.

Out of scope

  • An equivalent secure setup for non-Claude-Code agents (the doc is Claude-Code-specific because that's the reference implementation; other agents would need their own equivalent of sandbox / permissions).
  • An automated PR-opening "bump bot" for the pinned tools — the manual review step is deliberate, matching the same convention the framework uses for everything else.

🤖 Generated with Claude Code

Adds the framework's recommended secure-agent setup — a layered
defence for running Claude Code (or any other SKILL.md-aware
agent) against pre-disclosure CVE content without unfettered
access to host credentials. The framework dogfoods the
configuration via `.claude/settings.json`; adopters scaffold their
own copy from the example block in the new doc.

## Layered defence

The new setup is four layers, each implemented in this commit:

  Layer 0 — clean env       claude-iso.sh wrapper
  Layer 1 — fs sandbox       .claude/settings.json sandbox.*
  Layer 2 — tool perms       .claude/settings.json permissions.deny
  Layer 3 — confirm-on-write .claude/settings.json permissions.ask

Layers 1-3 share `.claude/settings.json`. Layer 0 is a separate
shell wrapper that strips credential-shaped env vars from the
parent shell before exec-ing claude.

## 7-day cooldown for system-tool versions

Mirrors the framework's existing 7-day cooldown convention
(`[tool.uv] exclude-newer = "7 days"` in pyproject.toml; weekly
Dependabot updates with 7-day cooldown in dependabot.yml). Every
host-system tool the secure setup depends on (bubblewrap, socat,
claude-code) is pinned in `tools/agent-isolation/pinned-versions.toml`
to a version that was released at least 7 days before the manifest
was last touched (`pinned_at`).

A side-effect-free check script
(`tools/agent-isolation/check-tool-updates.sh`) compares the pinned
versions to upstream and prints upgrade candidates that have
themselves aged past the 7-day cutoff. The script never installs,
never edits the manifest, never opens a PR — bumps require explicit
maintainer review per the procedure documented in
`secure-agent-setup.md`.

The doc also walks through wiring the script into a weekly
`/schedule` routine so upgrade candidates surface automatically
without manual prompting; the scheduled agent has no special
permission to install — the surfaced candidate is a proposal, not
an action.

## Files

| File | Purpose |
|---|---|
| `secure-agent-setup.md`                       | User-facing doc. Threat model, layered defence walkthrough, install commands per distro, bump procedure, adopter scaffold, verification commands, residual risks. |
| `.claude/settings.json`                       | The framework's own dogfooded secure config — sandbox + permissions for cwd-rooted Claude Code sessions in this repo. |
| `tools/agent-isolation/pinned-versions.toml`  | Machine-readable manifest of pinned upstream versions for `bubblewrap`, `socat`, `claude-code`. Each entry carries a `released` date that satisfies the 7-day cooldown. |
| `tools/agent-isolation/check-tool-updates.sh` | Reads the manifest and reports upstream releases newer than the pin AND aged past 7 days. Side-effect-free. |
| `tools/agent-isolation/claude-iso.sh`         | Layer-0 shell wrapper — `env -i` + tiny passthrough list. |
| `tools/agent-isolation/README.md`             | Files-and-usage overview for the new directory. |

`README.md` and `AGENTS.md` updates: link to the new doc from the
"agent prerequisites" section and from "Local setup".

## Pinned versions (at time of writing, all aged past 7 days)

  bubblewrap   0.11.1   released 2026-03-21
  socat        1.8.1.1  released 2026-03-13
  claude-code  2.1.117  released 2026-04-22

The check script verified these by querying upstream:

  $ bash tools/agent-isolation/check-tool-updates.sh
  TOOL           PINNED     PINNED@      UPSTREAM   UPSTREAM@    STATUS
  bubblewrap     0.11.1     2026-03-21   0.11.1     2026-03-21   ✓ up to date
  socat          1.8.1.1    2026-03-13   1.8.1.1    2026-02-12   ✓ up to date
  claude-code    2.1.117    2026-04-22   2.1.117    2026-04-22   ✓ up to date

Latest claude-code (v2.1.123, released 2026-04-29) is intentionally
NOT picked up — within the 7-day cooldown.

## Test plan

- ✅ `prek run --all-files` passes (doctoc generated TOCs for the
  two new .md files; all other hooks clean).
- ✅ The check script runs end-to-end, queries upstream, prints the
  expected status table.
- ✅ The example `.claude/settings.json` parses as valid JSON
  (`python3 -c 'import json; json.load(open(".claude/settings.json"))'`).
- The actual filesystem-sandbox enforcement requires bubblewrap on
  the host — the framework maintainer's first invocation of
  Claude Code with this settings.json after merge will exercise it
  end-to-end.

Generated-by: Claude Code (Claude Opus 4.7)
@potiuk potiuk merged commit 1b569a8 into main Apr 29, 2026
6 checks passed
@potiuk potiuk deleted the secure-agent-setup branch April 29, 2026 13:19
@raboof

raboof commented Apr 29, 2026

Copy link
Copy Markdown
Member

cool! big fan of bubblewrap. it remains a bit odd to have claude take care of wrapping instead of wrapping claude, but 🤷 😄

@potiuk

potiuk commented Apr 29, 2026

Copy link
Copy Markdown
Member Author

Yeah ... and I have some follow-up after testing (including my mint distro quirks and setting up my Yubikey)

@andreahlert andreahlert added the mode:platform Substrate / infra — not a mode (sandbox, CI, validators) label May 7, 2026
potiuk added a commit that referenced this pull request May 30, 2026
Fifth and final PR of the security genericization series.

Lifts the remaining 4 docs in docs/security/ to read config knobs
from projects/_template/project.md and the contract docs from
PR1-PR4 (cve_authority.*, governance.*, security_inbox.*,
forwarders.*, archive_system.*, scope_detection.*). Plus a final
scrub of 4 skills for leftover ASF/Vulnogram literals.

Byte-equivalent for the airflow-s adopter: every ASF/Airflow/
Vulnogram-specific value either resolves through a config knob
whose ASF default matches today's behaviour, OR stays as one
named-example aside in generic prose.

Per-target lifts:

- docs/security/threat-model.md (+107/-77) — Purpose/Scope/
  Assumptions reframed from "ASF"/"PMC" to governance-knob
  terms. STRIDE matrix rows A.6/A.7/C.1-C.4/E.1-E.2 lifted:
  Vulnogram -> <cve-tool>; security@apache.org -> <security-list>;
  DRAFT/REVIEW/READY/PUBLIC -> cve_authority.states sequence
  (allocated -> review-ready -> publish-ready -> public).
  Mitigations M.10/M.16/M.18/M.19/M.27 + residual risks
  #3/#8/#10/#11 + re-audit cadence ownership generalised.

- docs/security/forwarder-routing-policy.md (+42/-27) — references
  the optional security-issue-import-via-forwarder sub-skill from
  PR3 (#387) and the tools/forwarder-relay/README.md contract.
  Replaces "ASF-security relay" / "security@apache.org" with
  forwarders.enabled / <security-list> / foundation_security_address.
  ASF-Airflow shown as a named-example aside per concept.

- docs/security/how-to-fix-a-security-issue.md (+20/-8) —
  "governance-authorised member of the adopting project (per
  governance.cve_allocation_gate)" replaces "PMC member of
  apache/airflow"; <cve-tool> + cve_authority.* replaces Vulnogram-
  specific URLs and state names; archive_system.advisory_publication_signal_url
  replaces the lists.apache.org users-list URL.

- docs/security/new-members-onboarding.md (+26/-13) — onboarding-
  style register preserved. "PMC members and committers" reframed
  as "governance body that satisfies governance.cve_allocation_gate";
  per-user-config "PMC status" steps reference the governance knob;
  Vulnogram steps reference <cve-tool> via cve_authority.record_url_template.

- Final scrub of 4 skills (+17/-15 net): security-issue-import,
  security-issue-import-via-forwarder, security-issue-invalidate,
  security-issue-fix — leftover literal references caught and
  lifted to roster.bare_name_handles / governance.escalation_contact /
  forwarders.<adapter>.contact_handle.

Aggregate: 8 files, +240/-156 lines.

That closes the series. Five PRs (#381, #386, #387, #388, this)
transitioned the security skill family from Airflow/ASF-coupled to a
generic framework with ASF as the default-configured option. The
airflow-s adopter, with the ASF defaults baked into project.md, sees
byte-equivalent behaviour throughout. Non-ASF adopters override
specific dimensions (CVE authority, mail provider, archive system,
governance gate, scope axis) by changing only their <project-config>/
files.

Generated-by: Claude Code (Opus 4.7)
potiuk pushed a commit that referenced this pull request Jun 30, 2026
…ls (#651)

* feat(validator): add adapter authoring smoke check for contract:* tools

Add a SOFT advisory check (aspect #11) to skill-and-tool-validator that
verifies every contract:* adapter tool README declares the three authoring
fields the adapter contract requires: credential/privacy handling,
supported operations, and adopter config keys.

All three checks are SOFT so legacy adapters can be brought into
compliance deliberately without blocking unrelated changes. Twelve
advisories fire on the current tree; exit code stays 0. New
TestValidateAdapterAuthoring class adds 13 targeted tests.

Generated-by: Claude (Opus 4.7)

* fix two false positives and add to implmentation plan
potiuk pushed a commit to justinmclean/airflow-steward that referenced this pull request Jun 30, 2026
Add validate_modes_doc_consistency (SOFT, category modes-doc-consistency)
to the skill-and-tool-validator. The check compares docs/modes.md against
live skills/*/SKILL.md frontmatter across four dimensions:

1. Listed skill missing from disk — row exists in modes.md but
   skills/<slug>/ does not.
2. Mode mismatch — a skill's mode: frontmatter differs from the section
   it appears in (e.g. listed under Triage but mode: Mentoring).
3. Skill count mismatch — the integer in "Modes at a glance" Skill count
   column does not equal the actual row count in that mode's section.
4. Unlisted skill — a live skill has a valid mode: frontmatter value but
   is absent from the corresponding docs/modes.md section.

On the live tree the check surfaces two real gaps:
- reviewer-routing has mode: Triage but is absent from docs/modes.md
- good-first-issue-sweep has mode: Mentoring but is absent from docs/modes.md

Ships 19 tests in TestParseModesDocs + TestValidateModeDocConsistency;
all 283 validator tests pass.

Generated-by: Claude (Opus 4.7)
potiuk pushed a commit that referenced this pull request Jun 30, 2026
Add validate_modes_doc_consistency (SOFT, category modes-doc-consistency)
to the skill-and-tool-validator. The check compares docs/modes.md against
live skills/*/SKILL.md frontmatter across four dimensions:

1. Listed skill missing from disk — row exists in modes.md but
   skills/<slug>/ does not.
2. Mode mismatch — a skill's mode: frontmatter differs from the section
   it appears in (e.g. listed under Triage but mode: Mentoring).
3. Skill count mismatch — the integer in "Modes at a glance" Skill count
   column does not equal the actual row count in that mode's section.
4. Unlisted skill — a live skill has a valid mode: frontmatter value but
   is absent from the corresponding docs/modes.md section.

On the live tree the check surfaces two real gaps:
- reviewer-routing has mode: Triage but is absent from docs/modes.md
- good-first-issue-sweep has mode: Mentoring but is absent from docs/modes.md

Ships 19 tests in TestParseModesDocs + TestValidateModeDocConsistency;
all 283 validator tests pass.

Generated-by: Claude (Opus 4.7)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mode:platform Substrate / infra — not a mode (sandbox, CI, validators)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants