Skip to content

docs(security): add release-blocking threat model#91

Merged
potiuk merged 2 commits into
apache:mainfrom
andreahlert:worktree-feat-threat-model
May 7, 2026
Merged

docs(security): add release-blocking threat model#91
potiuk merged 2 commits into
apache:mainfrom
andreahlert:worktree-feat-threat-model

Conversation

@andreahlert

Copy link
Copy Markdown
Collaborator

Summary

Why this overhead is the right call

ASF does not mandate a threat-model artefact, and no observed apache/* repo ships a comparable document. The framework's posture is different from a typical ASF project: Apache Steward is pre-TLP, ships release-blocking security automation, and is designed to be adopted by multiple ASF projects' security teams. The blast radius of a security-skill bug crosses every adopter. The threat model gates every new Mode C skill on adding a STRIDE row — a discipline proportional to that radius.

The existing setup-level threat model at docs/setup/secure-agent-internals.md ยง Threat model covers the agent host (env vars, filesystem leak, sandbox prompt-injection). This new document covers the security workflow lifecycle and the 8 security skills. The two are complementary, not overlapping.

What's in scope

  • The 8 skills in docs/security/README.md: import (3), sync, deduplicate, invalidate, cve-allocate, fix.
  • The privacy-LLM redactor + checker.
  • The agent host's sandbox configuration in .claude/settings.json.
  • Credential surfaces: gh, Vulnogram OAuth, Gmail OAuth.

Out of scope (called out in the doc)

  • Mode D auto-merge (not shipped; explicit re-audit trigger when proposed).
  • Generic Mode C beyond security-issue-fix (proposed but not shipped).
  • LLM provider infrastructure (treated as upstream concern).
  • Physical access to the maintainer's host.
  • Denial of service against ASF infrastructure (lists.apache.org, cve.org, GitHub).

Review notes

  • The doc went through three internal review iterations before this PR. Iteration 1 caught 6 mitigation rows that overstated framework-enforced controls (M.7 single-skill activation, M.22 commit signing, M.27 cve.org readback, M.29 settings lint, M.25 attribution mechanism, missing PR fix(security): address 2026-05 prompt-injection audit (issues 1-9) #81 links). Iteration 2 caught a TLS-pinning-vs-trust-store wording contradiction (C.2) and an M.18 citation that pointed at the wrong template. Iteration 3 reported clean.
  • Every mitigation that is currently aspirational (M.22, M.29, partial M.1 wiring per-skill, M.18 token rotation) is explicitly flagged in-line and cross-references a residual-risk entry with a re-eval trigger.
  • Repo style (em-dashes, doctoc TOC at top, SPDX header) followed.

Test plan

  • doctoc reports clean on threat-model.md and on the four wiring files.
  • All 17 file-path references in the document resolve to existing files in the worktree.
  • All (#anchor) body references resolve to actual headings.
  • All five PR fix(security): address 2026-05 prompt-injection audit (issues 1-9) #81 mentions are markdown-linked.
  • Cross-checks against primary sources (AGENTS.md, tools/vulnogram/record.md, security-issue-sync/SKILL.md, security-issue-fix/SKILL.md, docs/modes.md) for the load-bearing claims.
  • ASF Security review of the trust-boundary framing and the residual-risk list before merge.

Follow-ups (out of scope here)

Apache Steward automates the ASF 16-step security-issue lifecycle,
but until now the framework had no formal threat model: no enumeration
of trust boundaries, adversary personas, or per-skill STRIDE rows for
the security skill family. PR apache#81 landed nine prompt-injection findings
without a document the next finding could be filed against, and the
existing setup-level threat model in `docs/setup/secure-agent-internals.md`
covers the agent host but not the eight security skills that compose
the lifecycle.

This change adds `docs/security/threat-model.md` (660 lines) — five
trust boundaries, five adversary personas, a STRIDE matrix per skill
family (27 rows), four cross-skill threats, a 29-entry mitigation
cross-reference, eleven residual-risk entries with explicit re-eval
triggers, and a re-audit cadence that gates every new Mode C skill
on adding a STRIDE row. Backlinks added from the security index,
process, roles, and how-to-fix guides so any reader of the security
docs reaches the threat model in one click.

The doc is overhead beyond ASF convention (no observed apache/* repo
ships a comparable artefact), justified by the framework's pre-TLP
release-blocking posture and the per-adopter blast radius of any
security skill bug.
@andreahlert andreahlert requested a review from potiuk May 7, 2026 18:35

@potiuk potiuk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Absolutely love it !

The doctoc-generated TOC URL-encoded the unicode `↔` character in
the B1–B5 headings (anchors became `%E2%86%94` in the body links),
which markdownlint MD051 rejects as invalid link fragments. Replaces
`↔` with the word `and` in the heading text only — the body of the
document continues to use `↔` where it improves readability — so the
generated anchors are ASCII and round-trip cleanly.

Adds a `text` language identifier to the trust-boundary diagram
fenced code block to satisfy MD040.
@potiuk potiuk merged commit 82d259f into apache:main May 7, 2026
11 checks passed
potiuk pushed a commit that referenced this pull request May 7, 2026
…n` (#93)

* feat(security): implement M.29 sandbox-lint for `.claude/settings.json`

Mitigation M.29 in `docs/security/threat-model.md` (PR #91)
committed to lint the agent-host sandbox configuration in CI on
every PR that touches it. This is the implementation:

- `tools/sandbox-lint/` — new stdlib-only Python project. The CLI
  compares `.claude/settings.json` against the canonical baseline
  at `tools/sandbox-lint/expected.json` (set semantics on
  `denyRead`, `allowRead`, `allowWrite`, `allowedDomains`, `deny`,
  `ask`) and runs three layers of hard invariants — required
  `denyRead` entries, forbidden `allowRead` and `allowWrite`
  paths, required `permissions.deny` entries — against both the
  live settings and the baseline itself. The same invariants
  applied to the baseline catch the case where a future PR
  weakens both files in lockstep.
- `.github/workflows/sandbox-lint.yml` — runs the linter on every
  PR that touches `.claude/settings.json`, the baseline, or the
  linter code. Path-scoped so the rest of the matrix is
  unaffected.
- `.pre-commit-config.yaml` — adds `ruff check`, `ruff format
  --check`, `mypy`, and `pytest` hooks for the new project; the
  pytest hook also fires when `.claude/settings.json` changes
  because the test suite loads both files.
- `.github/workflows/tests.yml` — adds the new project to the
  per-project pytest matrix so the visible-signal lane reports
  pass/fail in the CI checks list.

Threat-model cross-references are in
`tools/sandbox-lint/README.md`. The X3 residual (a maintainer
editing the file locally outside a PR) remains accepted; the lint
gates the shipped configuration, not local overrides during a
single agent run.

Generated-by: Claude Opus 4.7

* fix(sandbox-lint): satisfy lychee and CodeQL on the M.29 PR

Two CI failures on PR #93 against `apache/airflow-steward`:

- `lychee` fails because `tools/sandbox-lint/README.md` linked to
  `docs/security/threat-model.md` paths and anchors — that file
  lands in a companion PR and is not on `main` yet. Replace the
  three direct links with prose references that note the
  threat-model doc is a companion PR; the lint stands on its own.
- `CodeQL` (`py/incomplete-url-substring-sanitization`) fires on
  the test sentinel `evil.example.com` because the rule keys on
  hostname-shaped literals appearing in `in` substring checks.
  This is test code asserting against a diff message, not URL
  validation, but the rule is right that hostname-shaped sentinels
  are a footgun. Rename to `sandbox-lint-test-extra-marker` so the
  sentinel is unambiguously not a URL pattern.

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants