From 6faa56eefa195a25d9dc9c15ab1345bb4f347975 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 3 May 2026 17:25:56 +0200 Subject: [PATCH] docs: restructure into docs//, slim README to install + family overview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The top-level README is reshaped to be the framework's adoption entry point — overview + 3-family table + 3-step bootstrap + the submodule-update reminder + cross-references, in 113 lines. All detailed content moves into a new docs/ tree split by skill family, so adopters can read only the family they care about. Layout / ├── README.md (slim — 113 lines) ├── AGENTS.md, CONTRIBUTING.md, NOTICE, LICENSE (unchanged at root) └── docs/ ├── prerequisites.md (general prereqs — Claude Code, │ Gmail MCP, GitHub auth, etc.) ├── setup/ │ ├── README.md (skill-family overview + link map) │ ├── secure-agent-setup.md (moved from root) │ └── secure-agent-internals.md (moved from root) ├── security/ │ ├── README.md (skill-family overview + link map) │ ├── process.md (16-step lifecycle + Mermaid + │ │ label lifecycle — extracted from │ │ README's Process reference section) │ ├── roles.md (Who-this-guide-is-for + Shared │ │ conventions + role guides for │ │ issue triager / remediation │ │ developer / release manager — │ │ extracted from README) │ ├── how-to-fix-a-security-issue.md (moved from root) │ └── new-members-onboarding.md (moved from root) └── pr-management/ └── README.md (skill-family overview + adopter contract pointer) What stays in the README - Project overview (1 paragraph) - Skill-family table (the three families: setup, security, pr-management) - 3-step adopter bootstrap (git submodule add + cp -r _template/ + ln -s .claude/skills/) - Keeping the submodule current (post-merge hook) - Cross-reference pointers (prerequisites, AGENTS.md, CONTRIBUTING.md, projects/_template/) What moved out - Process reference (16 steps + Mermaid) → docs/security/process.md - Label lifecycle → docs/security/process.md - Role guides (For triager / dev / RM) → docs/security/roles.md - Who this guide is for + Shared conventions → docs/security/roles.md - Prerequisites for running the agent skills → docs/prerequisites.md - secure-agent-setup.md (root) → docs/setup/ - secure-agent-internals.md (root) → docs/setup/ - how-to-fix-a-security-issue.md (root) → docs/security/ - new-members-onboarding.md (root) → docs/security/ Cross-references updated everywhere — links from non-moved files to the moved ones now use the new path; links inside the moved files to root-level files (README, AGENTS.md, .claude/skills/, tools/, projects/) gain the `../../` prefix. doctoc TOCs regenerated. All pre-commit hooks pass. Generated-by: Claude Code (Claude Opus 4.7) --- .../setup-isolated-setup-install/SKILL.md | 6 +- .../setup-isolated-setup-update/SKILL.md | 12 +- .../setup-isolated-setup-verify/SKILL.md | 6 +- .../skills/setup-shared-config-sync/SKILL.md | 6 +- .claude/skills/setup-steward-upgrade/SKILL.md | 16 +- AGENTS.md | 6 +- CONTRIBUTING.md | 8 +- README.md | 1111 +---------------- docs/pr-management/README.md | 75 ++ docs/prerequisites.md | 148 +++ docs/security/README.md | 86 ++ .../security/how-to-fix-a-security-issue.md | 20 +- .../security/new-members-onboarding.md | 32 +- docs/security/process.md | 389 ++++++ docs/security/roles.md | 369 ++++++ docs/setup/README.md | 78 ++ .../setup/secure-agent-internals.md | 12 +- .../setup/secure-agent-setup.md | 34 +- tools/agent-isolation/README.md | 8 +- tools/agent-isolation/check-tool-updates.sh | 2 +- tools/agent-isolation/claude-iso.sh | 8 +- tools/agent-isolation/pinned-versions.toml | 4 +- tools/agent-isolation/sandbox-bypass-warn.sh | 2 +- .../sandbox-status-line-rich.sh | 2 +- tools/agent-isolation/sandbox-status-line.sh | 2 +- tools/dev/check-placeholders.sh | 6 +- 26 files changed, 1298 insertions(+), 1150 deletions(-) create mode 100644 docs/pr-management/README.md create mode 100644 docs/prerequisites.md create mode 100644 docs/security/README.md rename how-to-fix-a-security-issue.md => docs/security/how-to-fix-a-security-issue.md (86%) rename new-members-onboarding.md => docs/security/new-members-onboarding.md (90%) create mode 100644 docs/security/process.md create mode 100644 docs/security/roles.md create mode 100644 docs/setup/README.md rename secure-agent-internals.md => docs/setup/secure-agent-internals.md (96%) rename secure-agent-setup.md => docs/setup/secure-agent-setup.md (97%) diff --git a/.claude/skills/setup-isolated-setup-install/SKILL.md b/.claude/skills/setup-isolated-setup-install/SKILL.md index f061f93f..06105b64 100644 --- a/.claude/skills/setup-isolated-setup-install/SKILL.md +++ b/.claude/skills/setup-isolated-setup-install/SKILL.md @@ -36,7 +36,7 @@ when_to_use: | This skill is the **on-ramp** for adopters who do not yet have the secure setup running. It is a thin walkthrough wrapper around the canonical install path documented in -[`secure-agent-setup.md`](../../../secure-agent-setup.md). The +[`docs/setup/secure-agent-setup.md`](../../../docs/setup/secure-agent-setup.md). The authoritative content lives there; this skill exists so an adopter can say *"set up the secure agent setup"* in a fresh session and land in the right step-by-step flow without first reading the @@ -86,7 +86,7 @@ Before walking any install step, confirm with the user: preserved vs replaced. 4. **Sync repo (optional).** Whether the user maintains a private dotfile-style `~/.claude-config` repo per - [Syncing user-scope config across machines](../../../secure-agent-setup.md#syncing-user-scope-config-across-machines). + [Syncing user-scope config across machines](../../../docs/setup/secure-agent-setup.md#syncing-user-scope-config-across-machines). If yes, the skill installs user-scope scripts as **symlinks** into `~/.claude-config/scripts/` rather than `cp`-ing into `~/.claude/scripts/` — the symlink approach is what makes @@ -95,7 +95,7 @@ Before walking any install step, confirm with the user: ## Walk-through Follow the canonical step list at -[secure-agent-setup.md → Adopter setup → Via a Claude Code prompt](../../../secure-agent-setup.md#via-a-claude-code-prompt). +[docs/setup/secure-agent-setup.md → Adopter setup → Via a Claude Code prompt](../../../docs/setup/secure-agent-setup.md#via-a-claude-code-prompt). Each step in that list maps 1:1 to a step in this skill. Do not re-write the list here — read the doc, follow it, and surface each sub-step with the user. The doc names are the source of truth; the diff --git a/.claude/skills/setup-isolated-setup-update/SKILL.md b/.claude/skills/setup-isolated-setup-update/SKILL.md index 70ca9498..8ac504bb 100644 --- a/.claude/skills/setup-isolated-setup-update/SKILL.md +++ b/.claude/skills/setup-isolated-setup-update/SKILL.md @@ -28,7 +28,7 @@ when_to_use: | This skill is the **drift report** for an already-installed secure setup. It walks the canonical update-check at -[`secure-agent-setup.md` → Keeping the setup updated → Via a Claude Code prompt](../../../secure-agent-setup.md#via-a-claude-code-prompt-2) +[`docs/setup/secure-agent-setup.md` → Keeping the setup updated → Via a Claude Code prompt](../../../docs/setup/secure-agent-setup.md#via-a-claude-code-prompt-2) and surfaces what is older / newer / has drifted, without applying any change. @@ -65,13 +65,13 @@ any change. ## What to check The canonical step list is in -[secure-agent-setup.md → Keeping the setup updated → Via a Claude Code prompt](../../../secure-agent-setup.md#via-a-claude-code-prompt-2). +[docs/setup/secure-agent-setup.md → Keeping the setup updated → Via a Claude Code prompt](../../../docs/setup/secure-agent-setup.md#via-a-claude-code-prompt-2). Walk each: 1. **Framework checkout.** `cd` into the user's `airflow-steward` clone, `git fetch origin main`, report what changed under `tools/agent-isolation/`, `.claude/settings.json`, and - `secure-agent-setup.md` since the local checkout was last + `docs/setup/secure-agent-setup.md` since the local checkout was last updated. Print the `git pull --ff-only` command for the user to run; do not run it. 2. **Pinned upstream tools.** Run @@ -80,7 +80,7 @@ Walk each: has aged past the framework's 7-day cooldown. Include the upstream changelog link for each. Do not bump the manifest; that is a separate - [Bumping a pinned version](../../../secure-agent-setup.md#bumping-a-pinned-version) + [Bumping a pinned version](../../../docs/setup/secure-agent-setup.md#bumping-a-pinned-version) PR by hand. 3. **User-scope script-copy drift.** For every user-scope file the doc tells the adopter to install @@ -119,7 +119,7 @@ follow-up: arrived, and reminds the user to handle the parent-tracker submodule pointer if applicable. - Pinned-tool upgrade candidate worth adopting → manifest bump PR - per [Bumping a pinned version](../../../secure-agent-setup.md#bumping-a-pinned-version). + per [Bumping a pinned version](../../../docs/setup/secure-agent-setup.md#bumping-a-pinned-version). - User-scope script drift → re-`cp` from the framework checkout, or — if the script lives in `~/.claude-config/` and the user wants the change propagated to other machines — invoke @@ -127,7 +127,7 @@ follow-up: - Settings.json shape drift → the user merges the new framework block into their tracker's `.claude/settings.json` by hand (the section to copy from is documented in - [The framework's own `.claude/settings.json`](../../../secure-agent-setup.md#the-frameworks-own-claudesettingsjson)). + [The framework's own `.claude/settings.json`](../../../docs/setup/secure-agent-setup.md#the-frameworks-own-claudesettingsjson)). - A previously-blocked denial command now succeeds → stop and surface as a regression, not a routine update; the user should investigate before bumping anything. diff --git a/.claude/skills/setup-isolated-setup-verify/SKILL.md b/.claude/skills/setup-isolated-setup-verify/SKILL.md index 34bdd134..7ab30b1f 100644 --- a/.claude/skills/setup-isolated-setup-verify/SKILL.md +++ b/.claude/skills/setup-isolated-setup-verify/SKILL.md @@ -2,7 +2,7 @@ name: setup-isolated-setup-verify description: | Walk the verification checklist documented in - `secure-agent-setup.md` and report ✓ done / ✗ missing / ⚠ partial + `docs/setup/secure-agent-setup.md` and report ✓ done / ✗ missing / ⚠ partial for each piece of the secure agent setup — project + user-scope `settings.json` wiring, hook scripts present + executable, `claude-iso` sourced, pinned tool versions installed at the @@ -28,7 +28,7 @@ when_to_use: | This skill is the **assertion** layer over the secure setup. It runs the checklist documented in -[`secure-agent-setup.md` → Verification → Via a Claude Code prompt](../../../secure-agent-setup.md#via-a-claude-code-prompt-1) +[`docs/setup/secure-agent-setup.md` → Verification → Via a Claude Code prompt](../../../docs/setup/secure-agent-setup.md#via-a-claude-code-prompt-1) and reports each check's status to the user with concrete evidence (file paths, command output, version strings). @@ -57,7 +57,7 @@ and reports each check's status to the user with concrete evidence ## The 7 checks The canonical list lives in -[secure-agent-setup.md → Verification → Via a Claude Code prompt](../../../secure-agent-setup.md#via-a-claude-code-prompt-1). +[docs/setup/secure-agent-setup.md → Verification → Via a Claude Code prompt](../../../docs/setup/secure-agent-setup.md#via-a-claude-code-prompt-1). Walk each in order: 1. Project `.claude/settings.json` shape — `sandbox.enabled: true`, diff --git a/.claude/skills/setup-shared-config-sync/SKILL.md b/.claude/skills/setup-shared-config-sync/SKILL.md index 477e4064..c58f031b 100644 --- a/.claude/skills/setup-shared-config-sync/SKILL.md +++ b/.claude/skills/setup-shared-config-sync/SKILL.md @@ -3,7 +3,7 @@ name: setup-shared-config-sync description: | Commit + push the user's shared Claude config to the `~/.claude-config` private dotfile-style sync repo (per - `secure-agent-setup.md` → Syncing user-scope config across + `docs/setup/secure-agent-setup.md` → Syncing user-scope config across machines). Inspects `~/.claude-config` for uncommitted local edits and unpushed commits, drafts a commit message for any unstaged changes, asks for explicit approval, then commits and @@ -40,7 +40,7 @@ modifications upstream. The sync repo lives at `~/.claude-config/`. This is the convention documented in -[`secure-agent-setup.md` → Syncing user-scope config across machines](../../../secure-agent-setup.md#syncing-user-scope-config-across-machines). +[`docs/setup/secure-agent-setup.md` → Syncing user-scope config across machines](../../../docs/setup/secure-agent-setup.md#syncing-user-scope-config-across-machines). Adopters who maintain a sync repo at a different path will need to fork this skill — the path is intentionally not parameterised because the doc specifies one canonical location and forking the @@ -89,7 +89,7 @@ skill is cleaner than per-invocation path-passing. or is not a git repo, surface that and stop — the user has not yet set up a sync repo per the doc, and the right next action is for them to follow - [Setting up a fresh host](../../../secure-agent-setup.md#setting-up-a-fresh-host). + [Setting up a fresh host](../../../docs/setup/secure-agent-setup.md#setting-up-a-fresh-host). 2. **`git fetch origin`** to learn the remote's current state. Report: diff --git a/.claude/skills/setup-steward-upgrade/SKILL.md b/.claude/skills/setup-steward-upgrade/SKILL.md index 082fd691..a2f575a9 100644 --- a/.claude/skills/setup-steward-upgrade/SKILL.md +++ b/.claude/skills/setup-steward-upgrade/SKILL.md @@ -5,7 +5,7 @@ description: | latest `origin/main` and surface what changed — the commits pulled, the files touched (with focus on the secure-setup blast radius: `.claude/settings.json`, `tools/agent-isolation/`, - `secure-agent-setup.md`, `secure-agent-internals.md`, + `docs/setup/secure-agent-setup.md`, `docs/setup/secure-agent-internals.md`, `pinned-versions.toml`), and the next-step recommendation. Never applies user-side propagation itself — that is the job of `setup-isolated-setup-update` (drift report) and the framework @@ -19,7 +19,7 @@ when_to_use: | date", or after Claude Code surfaces something new from the framework's release notes. Also appropriate as the entry point to a periodic update routine — recommended cadence per - secure-agent-setup.md is once per Claude Code upgrade or once + docs/setup/secure-agent-setup.md is once per Claude Code upgrade or once a month, whichever comes first; this skill is the *first* step of that routine, with `setup-isolated-setup-update` (read-only drift report) and any subsequent re-`cp` / `/setup-shared-config-sync` runs @@ -67,8 +67,8 @@ which is read-only and runs naturally as the next step. - **Show what arrived.** After a successful pull, surface the commit list and a per-file change summary, with explicit focus on the secure-setup blast radius (`.claude/settings.json`, - `tools/agent-isolation/`, `secure-agent-setup.md`, - `secure-agent-internals.md`, + `tools/agent-isolation/`, `docs/setup/secure-agent-setup.md`, + `docs/setup/secure-agent-internals.md`, `tools/agent-isolation/pinned-versions.toml`). The user should walk away knowing whether this upgrade has user-side follow-through to do. @@ -107,8 +107,8 @@ which is read-only and runs naturally as the next step. - List per-file changes with secure-setup focus: `git -C diff --name-status HEAD..@{u}` — call out entries under `.claude/settings.json`, - `tools/agent-isolation/**`, `secure-agent-setup.md`, - `secure-agent-internals.md`, `pinned-versions.toml` if they + `tools/agent-isolation/**`, `docs/setup/secure-agent-setup.md`, + `docs/setup/secure-agent-internals.md`, `pinned-versions.toml` if they appear. 4. **Confirm with the user before pulling.** Show the commits @@ -171,7 +171,7 @@ which is read-only and runs naturally as the next step. - **If `pinned-versions.toml` changed**, name the specific tool(s) bumped and remind the user that the host install - commands in `secure-agent-setup.md → Required tools` may + commands in `docs/setup/secure-agent-setup.md → Required tools` may now reference newer versions; the user runs the `apt-get` / `dnf` / `npm install` themselves (the skill does not touch system tools). @@ -192,7 +192,7 @@ which is read-only and runs naturally as the next step. `claude-code`). Those bumps happen on the host via the package manager, surfaced by `check-tool-updates.sh` and approved per the - [Bumping a pinned version](../../../secure-agent-setup.md#bumping-a-pinned-version) + [Bumping a pinned version](../../../docs/setup/secure-agent-setup.md#bumping-a-pinned-version) flow. - Not for syncing user-scope edits to `~/.claude-config`. That is `setup-shared-config-sync`'s job. diff --git a/AGENTS.md b/AGENTS.md index f8511a9a..400ee758 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -79,8 +79,8 @@ The framework has two layers: Repo-root files: - [`README.md`](README.md) — the end-to-end process for handling security issues (generic lifecycle). -- [`how-to-fix-a-security-issue.md`](how-to-fix-a-security-issue.md) — high-level description of the fix workflow. -- [`new-members-onboarding.md`](new-members-onboarding.md) — onboarding guide for new security team members. +- [`docs/security/how-to-fix-a-security-issue.md`](docs/security/how-to-fix-a-security-issue.md) — high-level description of the fix workflow. +- [`docs/security/new-members-onboarding.md`](docs/security/new-members-onboarding.md) — onboarding guide for new security team members. - [`projects/_template/`](projects/_template/) — bootstrap scaffold for a new adopter's `/`. - [`tools//`](tools/) — tool adapters (GitHub operations, issue-template schema, project-board GraphQL, …) for the external tools the skills invoke. - [`.claude/skills//SKILL.md`](.claude/skills/) — the agentic workflows. @@ -330,7 +330,7 @@ the user to follow up with submodule update on the parent. operate against pre-disclosure CVE content; running Claude Code (or another `SKILL.md`-aware agent) with default-permissive access to `~/`, env vars, and arbitrary network egress is a real exfiltration -risk. See [`secure-agent-setup.md`](secure-agent-setup.md) for the +risk. See [`docs/setup/secure-agent-setup.md`](docs/setup/secure-agent-setup.md) for the layered defence the framework dogfoods (`.claude/settings.json` sandbox + tool permissions + clean-env wrapper, with system tools pinned per-tool with a 7-day default upstream cooldown). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9e152692..3117a716 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,8 +44,8 @@ four — no hard-coded project assumptions anywhere. brevity, confidentiality, linking conventions, the placeholder substitution rule (``, ``, ``), and the informational-only treatment of reporter-supplied CVSS scores. - [`how-to-fix-a-security-issue.md`](how-to-fix-a-security-issue.md) and - [`new-members-onboarding.md`](new-members-onboarding.md) are + [`docs/security/how-to-fix-a-security-issue.md`](docs/security/how-to-fix-a-security-issue.md) and + [`docs/security/new-members-onboarding.md`](docs/security/new-members-onboarding.md) are human-facing guides that sit alongside those. - **Skills** live under [`.claude/skills/`](.claude/skills/). Each is a `SKILL.md` that @@ -85,8 +85,8 @@ four — no hard-coded project assumptions anywhere. ├── AGENTS.md # Editorial rules: tone, brevity, confidentiality, │ # placeholder substitution, reporter-CVSS policy ├── CONTRIBUTING.md # This file -├── how-to-fix-a-security-issue.md # Human-facing fix guide -├── new-members-onboarding.md # Human-facing onboarding guide +├── docs/security/how-to-fix-a-security-issue.md # Human-facing fix guide +├── docs/security/new-members-onboarding.md # Human-facing onboarding guide │ ├── .claude/ │ └── skills/ # Agent workflows (invoked via the Skill tool) diff --git a/README.md b/README.md index 85af7ed6..34200114 100644 --- a/README.md +++ b/README.md @@ -2,1023 +2,69 @@ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* -- [Overview](#overview) -- [Who this guide is for](#who-this-guide-is-for) -- [Prerequisites for running the agent skills](#prerequisites-for-running-the-agent-skills) - - [1. An agent that speaks the `SKILL.md` convention](#1-an-agent-that-speaks-the-skillmd-convention) - - [2. Email connection (Gmail MCP, today)](#2-email-connection-gmail-mcp-today) - - [3. GitHub connection (GitHub MCP / `gh` CLI)](#3-github-connection-github-mcp--gh-cli) - - [4. PMC membership (only for CVE allocation)](#4-pmc-membership-only-for-cve-allocation) - - [5. Browser (for the human-click steps)](#5-browser-for-the-human-click-steps) - - [6. Local `` clone (only for `security-issue-fix`)](#6-local-upstream-clone-only-for-security-issue-fix) - - [7. `uv` (for `generate-cve-json`)](#7-uv-for-generate-cve-json) -- [Shared conventions](#shared-conventions) - - [Keeping the reporter informed](#keeping-the-reporter-informed) - - [Recording status transitions on the tracker](#recording-status-transitions-on-the-tracker) - - [Confidentiality](#confidentiality) -- [Skills](#skills) - - [Setup](#setup) - - [Security workflow](#security-workflow) - - [PR triage and review](#pr-triage-and-review) -- [For issue triagers — Steps 1–6](#for-issue-triagers--steps-16) - - [Daily triage loop](#daily-triage-loop) - - [Assessing a report](#assessing-a-report) - - [Allocating the CVE](#allocating-the-cve) - - [Tools you use most](#tools-you-use-most) -- [For remediation developers — Steps 7–11](#for-remediation-developers--steps-711) - - [Picking up a tracker](#picking-up-a-tracker) - - [Attempting an automated fix](#attempting-an-automated-fix) - - [Opening the public fix PR manually](#opening-the-public-fix-pr-manually) - - [Private-PR fallback](#private-pr-fallback) - - [Handoff to the release manager](#handoff-to-the-release-manager) - - [Tools you use most](#tools-you-use-most-1) -- [For release managers — Steps 12–15](#for-release-managers--steps-1215) - - [Handoff from the remediation developer](#handoff-from-the-remediation-developer) - - [Sending the advisory](#sending-the-advisory) - - [Capturing the public archive URL](#capturing-the-public-archive-url) - - [Publishing the CVE and closing the issue](#publishing-the-cve-and-closing-the-issue) - - [Post-release credit corrections](#post-release-credit-corrections) - - [Tools you use most](#tools-you-use-most-2) -- [Process reference: the 16 steps](#process-reference-the-16-steps) - - [Step 1 — Report arrives on security@](#step-1--report-arrives-on-security) - - [Step 2 — Import the report](#step-2--import-the-report) - - [Step 3 — Discuss CVE-worthiness](#step-3--discuss-cve-worthiness) - - [Step 4 — Escalate stalled discussions](#step-4--escalate-stalled-discussions) - - [Step 5 — Land the valid/invalid consensus](#step-5--land-the-validinvalid-consensus) - - [Step 6 — Allocate the CVE](#step-6--allocate-the-cve) - - [Step 7 — Self-assign and implement the fix](#step-7--self-assign-and-implement-the-fix) - - [Step 8 — Open a public PR (straightforward cases)](#step-8--open-a-public-pr-straightforward-cases) - - [Step 9 — Open a private PR (exceptional cases)](#step-9--open-a-private-pr-exceptional-cases) - - [Step 10 — Link the PR and apply `pr created`](#step-10--link-the-pr-and-apply-pr-created) - - [Step 11 — PR merged](#step-11--pr-merged) - - [Step 12 — Fix released](#step-12--fix-released) - - [Step 13 — Send the advisory](#step-13--send-the-advisory) - - [Step 14 — Capture the public advisory URL](#step-14--capture-the-public-advisory-url) - - [Step 15 — Publish the CVE record and close the issue](#step-15--publish-the-cve-record-and-close-the-issue) - - [Step 16 — Credit corrections](#step-16--credit-corrections) -- [Label lifecycle](#label-lifecycle) - - [State diagram](#state-diagram) - - [Label reference](#label-reference) -- [Adopting the framework](#adopting-the-framework) - - [Bootstrapping a new adopter](#bootstrapping-a-new-adopter) - - [Per-skill-family contract](#per-skill-family-contract) - - [How skills resolve `/` paths](#how-skills-resolve-project-config-paths) +- [apache-steward](#apache-steward) + - [Skill families](#skill-families) + - [Adopting the framework](#adopting-the-framework) - [Keeping the submodule current](#keeping-the-submodule-current) + - [Cross-references](#cross-references) -## Overview + -This repository hosts a reusable, project-agnostic framework for -running an ASF project's security-issue handling process. (Currently -served from `apache/airflow-steward` for legacy reasons; future- -renamed to `apache/steward`.) The -lifecycle and conventions below are framework-level; everything -project-specific (identity, repositories, mailing lists, canned -responses, release trains, security model, scope labels, milestone -formats, title-normalisation rules, fix-workflow specifics) is -declared in the **adopting project's** `/` -directory — i.e. the `.apache-steward/` directory the adopting -project keeps at the root of its tracker repository. The framework -itself is pulled into the adopter's tracker as a git submodule at -`.apache-steward/apache-steward/`. +# apache-steward -A new project adopts the framework by: +Reusable, project-agnostic framework for ASF-project automation. +Currently served from `apache/airflow-steward` for legacy reasons; +future-renamed to `apache/steward`. Adopters pull this repository +into their own tracker as a git submodule and ship project-specific +configuration alongside it under `/`. -1. Cloning this repository as a submodule of its tracker repo at - `.apache-steward/apache-steward/`. -2. Copying the [`projects/_template/`](projects/_template/) - scaffold into the tracker's `.apache-steward/` directory and - filling in the project's identity, mailing lists, scope labels, - release-train roster, security model, and canned responses. -3. Symlinking `.claude/skills/` in the tracker repo to the - submodule's `.claude/skills/` so Claude Code (or another - `SKILL.md`-aware agent) loads the framework's skills against the - adopter's project configuration. +## Skill families -The private `` repository is the adopting project's security -team's shared tracker. Only members of the security team have access. -Issues are created from reports raised on the project's security -mailing list (see `/project.md → Mailing lists`) -and copied into `` by security-team members — the GitHub -author of a tracker is therefore **not always the reporter**, and -the real reporter is whoever sent the original email. +The framework ships three independent skill families. **Setup** is +a prerequisite for every other family — it installs the secure +agent setup that makes the other skills safe to run against +pre-disclosure content. **Security** and **PR management** are +options the adopter picks based on what the project needs to +automate. -Every tracker flows through two channels at the same time: - -- the original security-list mail thread, where the reporter is - kept informed at every status transition; -- a comment on the tracking issue, so the rest of the security team and the - release manager can follow along without reconstructing state from labels - and timestamps. - -The rest of this document is organised by audience. Pick the role that matches -what you are about to do, read its section, and jump into the -[process reference](#process-reference-the-16-steps) when you need the -step-level detail. - -## Who this guide is for - -Three roles share the handling process. Any security-team member can take on -any of them for a given issue, and in practice people rotate — but at any -moment a given tracking issue has exactly one person who owns the next move. - -Pick whichever applies to you now: - -- **I am new to the security team, or I mostly just want to comment on - issues.** Read [Shared conventions](#shared-conventions) below. The - adopting project's security-issues board — see - `/project.md → GitHub project board` — is the main - view. You do not need an agent for commenting. -- **I am a rotational triager** — running `import new reports` and - `sync all` a few times a week. Jump to - [For issue triagers — Steps 1–6](#for-issue-triagers--steps-16). -- **I picked up a tracker and am about to open a fix PR.** Jump to - [For remediation developers — Steps 7–11](#for-remediation-developers--steps-711). -- **I am the release manager for a cut containing a security fix.** Jump to - [For release managers — Steps 12–15](#for-release-managers--steps-1215). -- **I am looking up a specific step or label.** Go straight to - [Process reference](#process-reference-the-16-steps) or - [Label lifecycle](#label-lifecycle). - -## Prerequisites for running the agent skills - -If you only plan to **comment on issues** from the board, skip this -section — a browser and your `` collaborator access are -enough. - -If you plan to **run any of the agent skills** (`import`, `sync`, -`security-cve-allocate`, `fix`, `generate-cve-json`, `deduplicate`) — typically -as a rotational triager, remediation developer, or release manager — -check the following setup **before** invoking a skill. Each skill also -runs a short Step 0 pre-flight against the same list and stops with a -clear message if something is missing, so you do not discover a -missing piece half-way through a workflow. - -### 1. An agent that speaks the `SKILL.md` convention - -[Claude Code](https://www.anthropic.com/claude-code) is the reference -implementation the skills are written against. Any agent that reads -the `.claude/skills/*/SKILL.md` files and follows their step-by-step -instructions should work; there is no hard dependency on Claude Code -specifically. - -The agent runs against pre-disclosure CVE content (private mail -threads, draft advisories, in-flight tracker discussions). Run it -with the credential-isolation setup documented in -[`secure-agent-setup.md`](secure-agent-setup.md) — a layered -defence built around Claude Code's filesystem sandbox, tool-level -permission rules, and a clean-env wrapper that strips credential- -shaped variables from the agent's environment. The required system -tools (`bubblewrap`, `socat`, `claude-code` itself) are pinned with -a 7-day upstream-release cooldown, mirroring the same convention the -framework uses for its `[tool.uv] exclude-newer` and Dependabot -configs. - -### 2. Email connection (Gmail MCP, today) - -The import, sync, and security-cve-allocate skills **read the security-list -mail thread** associated with each tracker and draft replies on that -thread. Today this goes through the -[Claude Gmail MCP](https://docs.anthropic.com/en/docs/build-with-claude/mcp) -connected to the personal Gmail account of a security-team member -who is subscribed to the adopting project's security list (see -`/project.md → Mailing lists`). That is enough -access for the skills to see inbound reports and create drafts on -the right threads. - -There is an ASF-wide alternative on the horizon: -[`rbowen/ponymail-mcp`](https://github.com/rbowen/ponymail-mcp) (by -Rich Bowen, former ASF board director and ComDev lead) now supports -OAuth authentication and can read private ASF lists. Once ASF OAuth -is wired in, individual triagers should be able to run the skills -without connecting their personal Gmail — authenticating directly -against ASF credentials (and, eventually, the ASF's new MFA) will be -sufficient. Until then, Gmail MCP is the way. - -**Without this connection:** `security-issue-import` cannot find new -reports, `security-issue-sync` cannot reconcile status with the mail -thread, and no skill can draft replies to reporters. The skills will -refuse to start and tell you to configure the MCP first. - -### 3. GitHub connection (GitHub MCP / `gh` CLI) - -Every skill reads and writes `` issues. Claude Code ships -with the GitHub MCP by default, and the skills also use the `gh` -CLI directly for some calls. What the skills need: - -- Authenticated `gh auth status` on the shell the agent runs in. -- Collaborator access (any permission level) on `` — the - security-team roster is maintained per-project; for the active - project see - [`/release-trains.md`](/release-trains.md#security-team-roster). -- For `security-issue-fix`: a fork of `` on your GitHub - account (the skill pushes a branch there before opening the PR - via `gh pr create --web`). - -### 4. PMC membership (only for CVE allocation) - -The adopting project's CVE-tool allocation form is **PMC-gated** on -the server side — only the project's PMC members can submit a CVE -allocation. Non-PMC triagers can still run `security-cve-allocate`; the -skill detects this up front (it asks *"are you a PMC member of -``?"*) and produces a relay message for a PMC member to -click through instead. For Airflow the concrete tool is ASF's -Vulnogram at ; see -[`/project.md → CVE tooling`](/project.md#cve-tooling). - -The same PMC gate applies to ponymail URL lookups on private ASF -lists; until `ponymail-mcp` is wired in with ASF OAuth, only PMC -members can see private-list archives directly. - -### 5. Browser (for the human-click steps) - -Several parts of the process involve a form a human has to fill in -and click — the CVE-tool allocation form, the CVE record `#source` -paste, the `gh pr create --web` compose view. The skills prepare -the URL and the exact text to paste and hand it off to the browser; -they do not try to automate those clicks. - -### 6. Local `` clone (only for `security-issue-fix`) - -The fix skill writes the change in your local clone, runs local -checks and tests, pushes a branch to your fork, and opens a PR via -`gh pr create --web`. You need: - -- a clean clone of `` reachable from the agent's working - directory — the path comes from `config/user.md → - environment.upstream_clone`, set interactively the first time - you run the skill; -- the adopting project's dev toolchain installed per its contributing - docs — for Airflow see - [`/fix-workflow.md → Toolchain`](/fix-workflow.md#toolchain); -- a remote named for your GitHub fork that `gh pr create` can push - to. - -### 7. `uv` (for `generate-cve-json`) - -The `generate-cve-json` script is a small `uv`-managed Python -project. Install `uv` once -(); the script bootstraps the -rest. - -## Shared conventions - -These conventions bind every role. If you are unsure whether a rule applies to -you, it does. - -### Keeping the reporter informed - -The security team commits to keeping the original reporter informed about the -state of their report **at every status transition**, on the original mail -thread (not on the GitHub-notifications mirror thread). A short status update -should be sent to the reporter whenever any of the following happens: - -* the report has been acknowledged or assessed (valid / invalid); -* a CVE has been allocated; -* a fix PR has been opened; -* a fix PR has been **merged**; -* the issue has been scheduled for a specific release (milestone set); -* the release has shipped and the public advisory has been sent; -* the CVE record has been published on cve.org (completes the disclosure); -* any credits or fields visible in the eventual public advisory have changed. - -Each status update should plainly state what has changed, link to the relevant -artifact (PR URL, CVE ID, advisory link), and state what comes next. If the -reporter has not yet replied with their preferred credit, ask the -credit-preference question — but **do not re-ask it if it has already been -asked** on the same thread and is still awaiting a reply. Pinging the reporter -twice about the same open question is rude and gets us blocklisted; default to -the reporter's full name from the original email if they do not respond -before publication. - -Reusable wording for the common cases lives in -[`/canned-responses.md`](/canned-responses.md) — consult it before drafting a -reply from scratch. - -### Recording status transitions on the tracker - -**Every status transition must also be recorded as a comment on the GitHub -issue in ``**, not only sent by email. The two channels -serve different audiences: the email keeps the reporter informed; the issue -comment keeps the rest of the security team and the release manager informed -without forcing them to reconstruct the state from labels and timestamps. The -comment should briefly state what changed, link to the artifact (PR URL, CVE -ID, advisory link), and indicate whether the reporter has been notified. - -### Confidentiality - -Confidentiality of the private tracker (`` for the -adopting project) is both a **lifecycle rule** and a **writing rule**: -every transition you record on a tracker, every status comment, every -email draft has to respect it. The full rule set — forbidden surfaces, -allowed surfaces, scrubbing guidance, the exception buckets for private -`security@` / `private@` threads and in-repo `gh issue comment` calls — -lives in -[`AGENTS.md` — Confidentiality of the tracker repository](AGENTS.md#confidentiality-of-the-tracker-repository). -Read it before editing anything that might be seen outside the team. - -## Skills - -The framework's skills are flat folders under -[`.claude/skills/`](.claude/skills/) — each skill's `SKILL.md` is the -authoritative process documentation. Skills are named with a category -prefix so the function is visible at a glance: - -### Setup - -First-time install of the secure-agent setup, ongoing verification, -and framework-lifecycle housekeeping. - -| Skill | Purpose | -|---|---| -| [`setup-isolated-setup-install`](.claude/skills/setup-isolated-setup-install/SKILL.md) | First-time install of the secure agent setup. | -| [`setup-isolated-setup-verify`](.claude/skills/setup-isolated-setup-verify/SKILL.md) | Verify the secure setup landed correctly. | -| [`setup-isolated-setup-update`](.claude/skills/setup-isolated-setup-update/SKILL.md) | Surface drift between the installed setup and the framework's latest. | -| [`setup-steward-upgrade`](.claude/skills/setup-steward-upgrade/SKILL.md) | Pull the framework checkout to latest `origin/main`. | -| [`setup-steward-verify`](.claude/skills/setup-steward-verify/SKILL.md) | Verify the framework is integrated correctly into an adopter tracker. | -| [`setup-shared-config-sync`](.claude/skills/setup-shared-config-sync/SKILL.md) | Commit + push the user's shared Claude config to its sync repo. | - -### Security workflow - -The 16-step lifecycle for security-issue handling — from -`` import through CVE publication. See -[Process reference](#process-reference-the-16-steps) below for the -cross-skill flow as a diagram. - -| Skill | Purpose | -|---|---| -| [`security-issue-import`](.claude/skills/security-issue-import/SKILL.md) | Import new reports from `` into ``. | -| [`security-issue-import-from-pr`](.claude/skills/security-issue-import-from-pr/SKILL.md) | Open a tracker for a security-relevant fix opened as a public PR. | -| [`security-issue-import-from-md`](.claude/skills/security-issue-import-from-md/SKILL.md) | Bulk-import findings from a markdown report. | -| [`security-issue-sync`](.claude/skills/security-issue-sync/SKILL.md) | Reconcile a tracker against its mail thread, fix PR, release train, and archives. | -| [`security-cve-allocate`](.claude/skills/security-cve-allocate/SKILL.md) | Allocate a CVE for a tracker (Vulnogram URL + paste-ready JSON). | -| [`security-issue-fix`](.claude/skills/security-issue-fix/SKILL.md) | Implement the fix as a public PR in ``. | -| [`security-issue-deduplicate`](.claude/skills/security-issue-deduplicate/SKILL.md) | Merge two trackers describing the same root-cause vulnerability. | -| [`security-issue-invalidate`](.claude/skills/security-issue-invalidate/SKILL.md) | Close a tracker as invalid with a polite-but-firm reporter reply. | - -### PR triage and review - -Maintainer-facing PR-queue management — first-pass triage, stats -on the open backlog, and deep code review. Lifted from -`apache/airflow` into the framework so adopters with a public -contributor PR queue can reuse the same playbook. - -| Skill | Purpose | -|---|---| -| [`pr-management-triage`](.claude/skills/pr-management-triage/SKILL.md) | Sweep open PRs, classify against the project's quality criteria, propose a disposition (draft / comment / close / rebase / rerun / mark ready / ping), execute on confirmation. | -| [`pr-management-stats`](.claude/skills/pr-management-stats/SKILL.md) | Read-only summary tables of the open PR backlog grouped by area label — measure where queue pressure sits. | -| [`pr-management-code-review`](.claude/skills/pr-management-code-review/SKILL.md) | Walk a maintainer through deep code review, one PR at a time. Reads the diff, applies the project's review criteria, drafts an `approve` / `request-changes` / `comment` review with inline comments, posts on confirmation. | - -Project-specific content (committers team handle, area-label -prefix, CI-check → doc URL map, comment-template wording, review- -criteria source files) lives in the adopter's `/` -under the per-skill scaffolds in -[`projects/_template/pr-management-config.md`](projects/_template/pr-management-config.md), -[`projects/_template/pr-management-triage-comment-templates.md`](projects/_template/pr-management-triage-comment-templates.md), -[`projects/_template/pr-management-triage-ci-check-map.md`](projects/_template/pr-management-triage-ci-check-map.md), -and -[`projects/_template/pr-management-code-review-criteria.md`](projects/_template/pr-management-code-review-criteria.md). -The framework currently ships with airflow-flavored defaults -inline in the supporting files of each skill — non-airflow -adopters override by forking the relevant supporting file into -their own `.claude/skills/` until a follow-up PR completes the -content extraction. - -## For issue triagers — Steps 1–6 - -You own the tracker from an inbound report on `` -through to a CVE allocated, a scope label applied, and the issue ready for a -remediation developer to pick up. Step 6 (the CVE allocation itself) is -PMC-gated: **only the adopting project's PMC members can submit the -CVE-tool allocation form**. If you are not on the PMC you relay a -pre-drafted request to a PMC -member — either way you are the one who lands the resulting CVE ID back into -the tracker. - -### Daily triage loop - -A typical triage sweep runs three skills in order: - -1. **`import new reports`** — - [`security-issue-import`](.claude/skills/security-issue-import/SKILL.md) - scans `` for threads not yet imported, - classifies each candidate (real report vs. automated-scan / consolidated / - media / spam), and proposes a tracker per valid report plus a - receipt-of-confirmation Gmail draft. See - [Step 2](#step-2--import-the-report). -2. **`sync all`** — - [`security-issue-sync`](.claude/skills/security-issue-sync/SKILL.md) - reconciles every open tracker against its mail thread, the fix PR, the - release train, and the users@ archive. Proposes label / milestone / - assignee / body changes in one pass. -3. **`allocate CVE for issue #N`** — - [`security-cve-allocate`](.claude/skills/security-cve-allocate/SKILL.md) when a report has - been assessed as valid. See [Step 6](#step-6--allocate-the-cve). - -Nothing is applied without an explicit confirmation — each skill is a -proposal engine, not an auto-pilot. - -### Assessing a report - -For each `needs triage` tracker, drive the validity assessment in comments, -pulling at least one other security-team member into the discussion. Use the -canned-response templates from [`/canned-responses.md`](/canned-responses.md) -for negative assessments so the tone stays polite-but-firm. - -When the report is confirmed valid, apply exactly one scope label from -the project's scope set (declared in -[`/scope-labels.md`](/scope-labels.md)). -If a report affects more than one scope, split into per-scope trackers -before allocation — the `security-issue-sync` skill surfaces this as -a blocker. See -[Step 5](#step-5--land-the-validinvalid-consensus). - -If discussion stalls for about 30 days, escalate to a broader audience per -[Step 4](#step-4--escalate-stalled-discussions). - -### Allocating the CVE - -Use [`security-cve-allocate`](.claude/skills/security-cve-allocate/SKILL.md). The skill asks up -front whether you are on the PMC; if not, it reshapes the recipe into an -``@``-mention relay message you forward to a PMC member on the tracker or on -the `` thread. Once the allocated `CVE-YYYY-NNNNN` -is pasted back, the skill wires it into the tracker in one pass (the *CVE -tool link* body field, the `cve allocated` label, a status-change comment, a -refreshed CVE-JSON attachment) and hands off to `security-issue-sync` to -reconcile the rest of the tracker. See [Step 6](#step-6--allocate-the-cve) -for the full detail. - -### Tools you use most - -- [`security-issue-import`](.claude/skills/security-issue-import/SKILL.md) — - *"import new reports"* at the start of each triage sweep. The entry point - into the process for `` reports. -- [`security-issue-import-from-pr`](.claude/skills/security-issue-import-from-pr/SKILL.md) — - *"import a tracker from PR "* when a security-relevant fix landed - publicly without going through `` and the team has agreed - it warrants a CVE. Lands directly in the `Assessed` column. -- [`security-issue-sync`](.claude/skills/security-issue-sync/SKILL.md) — - *"sync "* or *"sync all"*. Surfaces stalled issues, missing - fields, credit replies, and scope-split requirements in one combined - proposal. -- [`security-cve-allocate`](.claude/skills/security-cve-allocate/SKILL.md) — *"allocate a CVE - for "*. -- [`generate-cve-json`](tools/vulnogram/generate-cve-json/SKILL.md) — to - refresh the paste-ready JSON embedded in the issue body on demand. -- [`security-issue-deduplicate`](.claude/skills/security-issue-deduplicate/SKILL.md) — - when two trackers describe the same root-cause bug discovered - independently. -- [`security-issue-invalidate`](.claude/skills/security-issue-invalidate/SKILL.md) — - *"close NN as invalid"* once Step 5 lands a consensus-invalid - decision. Applies the `invalid` label, archives the project-board - item, and (for ``-imported trackers) drafts a reply - to the reporter explaining the reasoning. - -## For remediation developers — Steps 7–11 - -You own the tracker from a CVE allocated to a merged public fix PR in -`` (including the `pr merged` hand-off where the tracker sits -waiting for the release train to ship). The role name matches the -`remediation developer` credit you receive in the published CVE record (see -`credits[]` with `type: "remediation developer"` in the generated CVE JSON). - -### Picking up a tracker - -Pick a tracker that has a scope label, `cve allocated`, and clear consensus -on the fix shape. Self-assign yourself on GitHub so the board reflects -ownership. See [Step 7](#step-7--self-assign-and-implement-the-fix). - -### Attempting an automated fix - -Before writing the fix by hand, consider letting the -[`security-issue-fix`](.claude/skills/security-issue-fix/SKILL.md) skill try -it first. Invoked as *"try to fix issue #N"* (or *"draft a PR for #N"*), the -skill: - -- runs `security-issue-sync` first to make sure the tracker's state is - current; -- reads the full tracker discussion and the linked `security@` mail - thread and decides whether the issue is *easily fixable* — clear - consensus on the fix shape, small scope, known location in - ``. If it is not, the skill stops and tells you what - more the tracker needs before it is safe to attempt; -- if it is, proposes an implementation plan (which file(s) to touch, - what to change, what tests to add) and **waits for your explicit - confirmation** before making any edits; -- writes the change in your local `` clone, runs the - local static checks and tests, and iterates on failures; -- opens the public PR from your fork via `gh pr create --web` with a - scrubbed title and body — every public surface (commit message, - branch name, PR title, PR body, newsfragment) is grep-checked for - `CVE-`, the `` repo slug, `vulnerability`, *"security fix"* - and similar leakage before being written or pushed; -- updates the `` tracking issue with the new PR - link and applies the `pr created` label, handing back off to - `security-issue-sync`. - -The skill refuses to proceed in cases where a human decision still -needs to happen: reports that are still being assessed, reports not -yet classified as valid vulnerabilities, and changes that require the -private-PR fallback in -[Step 9](#step-9--open-a-private-pr-exceptional-cases). If it refuses, -fall back to the manual flow below. - -Even when the skill succeeds end-to-end, you remain the PR's author -and reviewer-facing contact on the public `` PR. Stay -on the PR through review and merge. - -### Opening the public fix PR manually - -If you are writing the fix by hand, write the code change in your local -`` clone, run the local checks and tests, and open the PR -via `gh pr create --web`. The PR description **must not** reveal the CVE, -the security nature of the change, or link back to `` — -see [Step 8](#step-8--open-a-public-pr-straightforward-cases) and the -confidentiality rules in -[`AGENTS.md`](AGENTS.md#confidentiality-of-the-tracker-repository). - -Request a `backport-to-v3-2-test` (or equivalent) label on the public PR -when the fix should ship on a patch train. - -### Private-PR fallback - -In exceptional cases — highly critical fixes, or code that needs private -review — open the PR against the `main` branch of `` -instead of ``. CI does not run there, so run static checks and -tests manually before asking for review. Once approved, re-open the PR in -`` by pushing the branch public. See -[Step 9](#step-9--open-a-private-pr-exceptional-cases). - -### Handoff to the release manager - -Once the `` PR merges, `security-issue-sync` moves the tracker -from `pr created` to `pr merged` and sets the milestone of the release the -fix will ship in. The tracker then waits for the release train. When the -release ships, sync swaps `pr merged` → `fix released` and the tracker -becomes the release manager's responsibility. See -[Step 11](#step-11--pr-merged) and [Step 12](#step-12--fix-released). - -### Tools you use most - -- [`security-issue-fix`](.claude/skills/security-issue-fix/SKILL.md) — - *"try to fix issue #N"*. Proposes a plan, writes the code, runs local - tests, and opens a `--web` PR with a scrubbed title/body. See - [Attempting an automated fix](#attempting-an-automated-fix) above for - the full flow and the cases where the skill refuses to proceed. -- [`security-issue-sync`](.claude/skills/security-issue-sync/SKILL.md) — to - keep the tracker's labels, milestone, and assignee aligned with the PR - state as it moves through review and merge. - -## For release managers — Steps 12–15 - -You own the tracker from the moment the fix actually ships (`fix released`) -to a closed tracking issue with a PUBLISHED CVE record. The hand-off from -the remediation developer is automatic: `security-issue-sync` detects the -milestone version on PyPI / the Helm registry, swaps `pr merged` → -`fix released`, and assigns the advisory-send to you. - -### Handoff from the remediation developer - -Watch your `fix released` queue on the board. Until the `pr merged` → -`fix released` swap fires, the tracker is still the remediation developer's -(Step 11 territory). Once it fires, it is yours. See -[Step 12](#step-12--fix-released). - -### Sending the advisory - -Review the attached CVE JSON on the tracker, fill any missing body fields -(CWE, severity, affected versions), and send the advisory emails to -`` / `` from the ASF CVE tool. -Add `announced - emails sent` and remove `fix released`. **Do not close the -issue yet** — see [Step 13](#step-13--send-the-advisory). - -### Capturing the public archive URL - -This is a handoff the sync skill handles for you: once the advisory has -been archived on the users@ list, the next `security-issue-sync` run finds -the URL, populates the *Public advisory URL* body field, regenerates the -CVE JSON attachment, and moves the label to `announced`. See -[Step 14](#step-14--capture-the-public-advisory-url). - -### Publishing the CVE and closing the issue - -For every `announced` issue: open Vulnogram at -`https://cveprocess.apache.org/cve5/#source`, paste the latest -attached CVE JSON, save, and move the record from REVIEW to PUBLIC. -Then close the issue (do not update any labels). This is the terminal -step of the lifecycle. See -[Step 15](#step-15--publish-the-cve-record-and-close-the-issue). - -An issue that sits on `announced` for more than a day or two -is a signal to ping the RM. - -### Post-release credit corrections - -If credits need correction after announcement, respond to the announcement -emails with the missing credits, update the ASF CVE tool, and ask the ASF -security team to push the information to `cve.org`. See -[Step 16](#step-16--credit-corrections). - -### Tools you use most - -- [`security-issue-sync`](.claude/skills/security-issue-sync/SKILL.md) — - *"sync announced"* at the start of each release window, to - see the `announced` backlog needing a Vulnogram push. Also - *"sync CVE-YYYY-NNNN"* to drill into one specific CVE before sending the - advisory. -- [`generate-cve-json`](tools/vulnogram/generate-cve-json/SKILL.md) — to - regenerate the attachment on demand when a body field changes after the - URL has been captured. - -## Process reference: the 16 steps - -This is the authoritative outline of the 16-step lifecycle. Each step -links to the skill or document that owns the deep mechanics — the -brief descriptions below are an overview, not a substitute for the -linked skill's `SKILL.md`. If the role sections above conflict with -what is here, this reference wins. - -```mermaid -flowchart TD - S1[1. Report arrives on security-list] - S2[2. Import the report] - ALT2([alt: import from public PR]) - S3[3. Discuss CVE-worthiness] - S4[4. Escalate stalled discussions] - S5{5. Valid or invalid?} - INV([Invalid: close + reporter reply]) - S6[6. Allocate CVE] - S7[7. Self-assign + implement] - S8[8. Open public PR] - S9[9. Open private PR exceptional] - S10[10. Link PR + apply pr created] - S11[11. PR merged] - S12[12. Fix released] - S13[13. Send advisory] - S14[14. Capture public archive URL] - S15[15. Publish CVE + close issue] - S16[16. Credit corrections] - - S1 --> S2 - ALT2 --> S5 - S2 --> S3 - S3 --> S4 - S3 --> S5 - S4 --> S5 - S5 -->|valid| S6 - S5 -->|invalid| INV - S6 --> S7 - S7 --> S8 - S7 --> S9 - S8 --> S10 - S9 --> S10 - S10 --> S11 - S11 --> S12 - S12 --> S13 - S13 --> S14 - S14 --> S15 - S15 --> S16 - - classDef triager fill:#fff3cd,stroke:#664d03,color:#000 - classDef remed fill:#cfe2ff,stroke:#055160,color:#000 - classDef releaseMgr fill:#d4edda,stroke:#0f5132,color:#000 - classDef terminal fill:#f8d7da,stroke:#842029,color:#000 - - class S1,S2,ALT2,S3,S4,S5 triager - class S6,S7,S8,S9,S10,S11 remed - class S12,S13,S14,S15,S16 releaseMgr - class INV terminal -``` - -Colour key: yellow = triager (Steps 1–5), blue = remediation -developer (Steps 6–11), green = release manager (Steps 12–16), -red = terminal close. - -### Step 1 — Report arrives on security@ - -The reporter sends the issue to the adopting project's -`` (or to `security@apache.org`, which forwards to the -project list). - -### Step 2 — Import the report - -[`security-issue-import`](.claude/skills/security-issue-import/SKILL.md) -scans `` for un-imported threads, classifies each -candidate (real / automated-scan / consolidated / spam), extracts the -issue-template fields from the root message, and proposes one tracker -per valid report plus a Gmail receipt-of-confirmation draft. Nothing -is applied without explicit confirmation. The newly-created tracker -lands with `needs triage`. - -If the report matches a known-invalid pattern, the skill drafts the -matching canned reply from -[`/canned-responses.md`](/canned-responses.md) -and does **not** create a tracker — invalid noise never enters the -board. - -**Alternate entry — fix already opened as a public PR.** Use -[`security-issue-import-from-pr`](.claude/skills/security-issue-import-from-pr/SKILL.md). -The tracker lands directly in the `Assessed` column with the scope -label applied (validity already decided informally), so Step 5 is -skipped and the tracker is ready for `security-cve-allocate` -immediately. - -**Alternate entry — bulk import from markdown.** Use -[`security-issue-import-from-md`](.claude/skills/security-issue-import-from-md/SKILL.md) -when triaging the output of an AI security review or third-party -scanner. Each finding becomes one tracker. - -### Step 3 — Discuss CVE-worthiness - -Drive the validity assessment in tracker comments. Pull at least -one other security-team member into the discussion. Use canned -responses from -[`/canned-responses.md`](/canned-responses.md) -for negative assessments so the tone stays polite-but-firm. - -### Step 4 — Escalate stalled discussions - -If discussion stalls for ~30 days, escalate in **two phases**: - -* **Phase 1 — short call for ideas.** A 3-4-paragraph message that - states the report exists and asks the wider audience for input. - No AI analysis, no proposed fixes — phase 1 is deliberately bare so - domain experts can weigh in with novel ideas without being anchored - to a pre-baked solution. -* **Phase 2 — AI-generated design-space analysis.** Triggered if - phase 1 stays silent for ~7 more days. The agent drafts a - structured analysis (TL;DR, why-the-obvious-fix-is-insufficient, - options A/B/C with trade-offs, open design questions, tagged - reviewers per a documented selection methodology). The triager - reviews and approves before posting. - -Audiences are the same for both phases: ``, -`security@apache.org`, the original reporter. Both phases land as -rollup entries on the tracker (per -[`tools/github/status-rollup.md`](tools/github/status-rollup.md)) -with the action label `Sync (Step 4 escalation)`. - -### Step 5 — Land the valid/invalid consensus - -If valid, apply exactly one scope label from -[`/scope-labels.md`](/scope-labels.md); -remove `needs triage`. If invalid, -[`security-issue-invalidate`](.claude/skills/security-issue-invalidate/SKILL.md) -labels `invalid`, posts a closing comment, archives the board item, -and (for ``-imported trackers) drafts a polite-but-firm -reporter reply. If consensus cannot be reached, follow -[ASF voting](https://www.apache.org/foundation/voting.html#apache-voting-process) -on ``. - -If a candidate duplicate is detected, -[`security-issue-deduplicate`](.claude/skills/security-issue-deduplicate/SKILL.md) -merges two trackers in place — preserving every reporter's credit, -every mailing-list thread reference, and every independent -attack-vector description. The kept issue's body is updated, the -duplicate is closed with the `duplicate` label, and the CVE JSON -attachment is regenerated so both finders land in `credits[]`. - -### Step 6 — Allocate the CVE - -[`security-cve-allocate`](.claude/skills/security-cve-allocate/SKILL.md) -opens the project's CVE allocation tool (for Airflow, ASF Vulnogram -at ; in general see -[`/project.md → CVE tooling`](/project.md#cve-tooling)), -normalises the title per -[`/title-normalization.md`](/title-normalization.md), -and — if the triager isn't on the PMC — builds an `@`-mention relay -message for a PMC member. Once the allocated `CVE-YYYY-NNNNN` is -pasted back, the skill wires it into the tracker (CVE tool link -body field, `cve allocated` label, status-change comment, refreshed -CVE-JSON attachment) and hands off to `security-issue-sync` to -reconcile the rest. - -### Step 7 — Self-assign and implement the fix - -A security team member self-assigns and implements the fix. -Optional automation: -[`security-issue-fix`](.claude/skills/security-issue-fix/SKILL.md) -proposes an implementation plan, writes the change in your local -`` clone, runs local tests, and opens a public PR via -`gh pr create --web` with a scrubbed title + body. Every public -surface (commit message, branch name, PR title, PR body, newsfragment) -is grep-checked for `CVE-`, the `` slug, *"vulnerability"*, -*"security fix"*, and similar leakage before being written or pushed. - -The skill refuses to proceed for reports still being assessed, -reports not yet classified as valid, and changes that require the -private-PR fallback (Step 9). Even when it succeeds end-to-end, you -remain the PR's author and reviewer-facing contact — stay on the PR -through review and merge. - -Delegation to a trusted third-party individual is permitted under -LAZY CONSENSUS, sharing only the information required to implement -the fix. - -### Step 8 — Open a public PR (straightforward cases) - -The PR description **must not** reveal the CVE, the security nature -of the change, or link back to ``. See -[`AGENTS.md → Confidentiality`](AGENTS.md#confidentiality-of-the-tracker-repository). -Request the appropriate `backport-to-vN-N-test` label on the public -PR when the fix should ship on a patch train. - -### Step 9 — Open a private PR (exceptional cases) - -For highly critical fixes or code that needs private review, open -the PR against ``'s `main` branch first (not the -`tracker_default_branch` set in `/project.md`). CI -does not run there — run static checks + tests manually. Once -approved, push the branch to `` and re-open the PR there. - -### Step 10 — Link the PR and apply `pr created` - -The remediation developer links the PR in the tracker description -and applies `pr created` on ``. - -### Step 11 — PR merged - -When the `` PR merges, swap `pr created` → `pr merged` -and set the milestone of the release the fix will ship in (per -[`/milestones.md`](/milestones.md)). -Close any private variant in ``. The tracker waits at -`pr merged` until the release ships — this can be hours (fast core -patches) or weeks (provider waves on a fixed cadence). - -### Step 12 — Fix released - -When the release containing the fix ships to users (PyPI / Helm -registry / equivalent), -[`security-issue-sync`](.claude/skills/security-issue-sync/SKILL.md) -detects the release version on the next run and proposes the -`pr merged` → `fix released` swap, which is the hand-off cue from -remediation developer to release manager. The same pass proposes -posting a one-shot **release-manager hand-off comment** with a -numbered checklist (Steps 13–15 from the RM's perspective) and -links to the paste-ready CVE JSON, the Vulnogram `#source` and -`#email` tabs, and canned-response templates. - -### Step 13 — Send the advisory - -The release manager fills the remaining CVE fields: - -* CWE — see [cwe.mitre.org](https://cwe.mitre.org/data/index.html); -* affected versions (`0, < `); -* short public summary; -* severity score per the - [ASF severity rating](https://security.apache.org/blog/severityrating) - (lazy consensus during discussion; voting if there's disagreement; - RM has the final say to keep the announcement on schedule); -* references — `patch` PR URL on ``; -* credits — `reporter`, `remediation developer`. - -The RM generates the description, sets the CVE to REVIEW (then -READY), and sends the announcement emails from the project's CVE -tool. Apply `announced - emails sent`, remove `fix released`. **The -issue stays open** at this point — it closes only at Step 15. - -### Step 14 — Capture the public advisory URL - -Once the announcement is archived on the users@ list, the next -`security-issue-sync` run finds the URL, populates the *Public -advisory URL* body field (a dedicated field on the issue template — -never reuse the *"Security mailing list thread"* field), regenerates -the CVE JSON attachment (now carrying a `vendor-advisory` reference), -and adds the `announced` label. The same pass proposes a one-shot -**publication-ready notification comment** for the release manager. - -Until *Public advisory URL* is populated, the sync skill will not -propose `announced` — publishing a CVE with an empty -`vendor-advisory` reference would leak a broken record into -`cve.org`. - -### Step 15 — Publish the CVE record and close the issue - -The release manager opens the project's CVE tool's `#source` view at -`https://cveprocess.apache.org/cve5/#source`, copies the -latest CVE JSON attachment from the tracker (the one regenerated in -Step 14), pastes it into the form, saves, and moves the record from -READY to PUBLIC — propagating to [`cve.org`](https://cve.org). Then -closes the tracker (no label updates). `security-issue-sync` -follows the close with an `archiveProjectV2Item` mutation so the -closed tracker leaves the active board (see -[`tools/github/project-board.md` — *Archive a board item*](tools/github/project-board.md#archive-a-board-item--terminal-state-cleanup)). - -A tracker that sits on `announced` for more than a day or two is a -signal to ping the RM. - -### Step 16 — Credit corrections - -If credits need correction post-announcement, the release manager: - -* responds to the announcement emails with the missing credits; -* updates the project's CVE tool with the missing credits; -* asks the ASF security team to push the information to - [`cve.org`](https://cve.org). - -## Label lifecycle - -### State diagram - -The diagram below shows the typical state flow. Each node is a label (or a -cluster of labels that co-exist); each edge is a process step that moves -the issue forward. Closing dispositions (`invalid`, `not CVE worthy`, -`duplicate`, `wontfix`) can terminate the flow at any point after -`needs triage`. - -```mermaid -flowchart TD - A([report on project security list]) -->|step 2: security-issue-import| B[needs triage] - A2([security-relevant fix in public PR]) -->|step 2 alt: security-issue-import-from-pr| C - B -->|step 5: consensus invalid| X1([invalid / not CVE worthy / duplicate / wontfix]) - B -->|step 5: consensus valid| C["scope label
(project-specific — see
projects/<PROJECT>/scope-labels.md)"] - C -->|step 6: CVE reserved by PMC member| D[cve allocated] - D -->|step 10: public PR opened| E[pr created] - E -->|step 11: PR merges| F[pr merged] - F -->|step 12: release ships| G[fix released] - G -->|step 13: advisory sent| H[announced - emails sent] - H -->|step 14: archive URL captured| J[announced] - J -->|step 15: RM moves CVE to PUBLIC + close| Z([issue closed]) - - classDef closed fill:#f8d7da,stroke:#842029,color:#000; - classDef done fill:#d1e7dd,stroke:#0f5132,color:#000; - class X1,Z closed; - class H,J done; -``` - -The dashed-equivalent entry from `A2` represents the deliberate-import -path described in [Step 2](#step-2--import-the-report) above: -trackers opened from a public PR skip the `needs triage` column and -land directly at `scope label` (the `Assessed` column on the project -board) because the validity assessment has already happened -informally before invocation. - -### Label reference - -The table below repeats the same flow in tabular form. An issue typically -moves through these labels left-to-right. - -**Scope labels are project-specific** — the adopting project's concrete -scope labels live in -[`/scope-labels.md`](projects/) (for the currently -adopting project, [`/scope-labels.md`](/scope-labels.md)). -The table below uses `` as a placeholder for whichever scope -labels the adopting project defines. - -| Label | Meaning | Added at step | Removed at step | -| --- | --- | --- | --- | -| `needs triage` | Freshly filed; assessment not yet started. | 1 | 5 | -| `` | Scope of the vulnerability. Exactly one project-specific scope label is set. | 5 | never (sticks for the lifetime of the issue) | -| `cve allocated` | A CVE has been reserved for the issue. Allocation itself is PMC-gated (only the adopting project's PMC members can submit the CVE-tool allocation form); a non-PMC triager relays a request to a PMC member via the [`security-cve-allocate`](.claude/skills/security-cve-allocate/SKILL.md) skill. | 6 | never | -| `pr created` | A public fix PR has been opened on `` but has not yet merged. | 10 | 11 (replaced by `pr merged`) | -| `pr merged` | The fix PR has merged into ``; no release with the fix has shipped yet. | 11 | 12 (replaced by `fix released` when the release ships) | -| `fix released` | A release containing the fix has shipped to users; advisory has not been sent yet. | 12 | 13 (replaced by `announced - emails sent`) | -| `announced - emails sent` | The public advisory has been sent to the project's announce and users mailing lists (see `/project.md → Mailing lists`). The issue **stays open** after this label is applied; closing is gated on the RM completing Step 15. | 13 | never (stays on the issue after closing for audit history) | -| `announced` | The public advisory URL has been captured in the tracking issue's *Public advisory URL* body field and the attached CVE JSON has been regenerated so its `references[]` now carries the `vendor-advisory` URL. The tracking issue is waiting for the release manager to copy the CVE JSON into the project's CVE tool, move the record to PUBLIC, and close the issue (Step 15). No label changes at close — the issue closes with `announced` still set. | 14 | never (stays on the issue after closing) | -| `wontfix` / `invalid` / `not CVE worthy` / `duplicate` | Closing dispositions for reports that are not valid or not CVE-worthy. | 5 / 6 | — | - -The [`security-issue-sync`](.claude/skills/security-issue-sync/SKILL.md) -skill keeps these labels honest: on every run it detects the current state -of the issue, the fix PR, and the release train, and proposes the label -transitions the process requires. +| Family | Purpose | Detail | +|---|---|---| +| [**setup**](docs/setup/README.md) | Isolated agent setup — sandboxing, pinned tools, framework upgrades. The prerequisite. | 6 skills, [`docs/setup/`](docs/setup/) | +| [**security**](docs/security/README.md) | 16-step security-issue handling lifecycle — from `security@` import through CVE publication. | 8 skills, [`docs/security/`](docs/security/) | +| [**pr-management**](docs/pr-management/README.md) | Maintainer-facing PR-queue management — triage, stats, deep code review. | 3 skills, [`docs/pr-management/`](docs/pr-management/) | ## Adopting the framework -Projects don't live in this repository — adopters pull the framework -into their own tracker repo as a git submodule (see *Repository -purpose* in [`AGENTS.md`](AGENTS.md#repository-purpose)) and ship -their per-project configuration alongside it under -`/` (which resolves to `.apache-steward/` in the -adopter's tracker root). - -### Bootstrapping a new adopter - Three one-time steps to integrate the framework into a new tracker -repo: +or upstream repo: -1. **Add the framework as a submodule** of your tracker repo at +1. **Add the framework as a submodule** at `.apache-steward/apache-steward/`: ```bash - cd path/to/your/tracker-repo + cd path/to/your/repo git submodule add https://github.com/apache/airflow-steward .apache-steward/apache-steward ``` -2. **Copy the [`projects/_template/`](projects/_template/) scaffold** - into `.apache-steward/`, then `grep -rn TODO .apache-steward/` to - find every placeholder you need to fill in. The required files - vary by which skill families you're adopting — see - [Per-skill-family contract](#per-skill-family-contract) below. +2. **Copy the [`projects/_template/`](projects/_template/) + scaffold** into `.apache-steward/`, then `grep -rn TODO + .apache-steward/` to find every placeholder you need to fill in. + The required files vary by which skill families you adopt — + see the per-family adopter contract in each + [`docs//README.md`](docs/) and the file-by-file index + in + [`projects/_template/README.md`](projects/_template/README.md). ```bash cp -r .apache-steward/apache-steward/projects/_template/. .apache-steward/ ``` -3. **Symlink `.claude/skills/`** in your tracker repo to the - framework's skill directory, so Claude Code (or another - `SKILL.md`-aware agent) loads the framework's skills against the - adopter's project configuration: +3. **Symlink `.claude/skills/`** to the framework's skill + directory, so Claude Code (or another `SKILL.md`-aware agent) + loads the framework's skills against your project configuration: ```bash ln -s .apache-steward/apache-steward/.claude/skills .claude/skills @@ -1030,72 +76,16 @@ skill checks each of these and reports `✓ done / ✗ missing / ⚠ partial` for the adopter integration — run it after step 3 to confirm the install landed. -### Per-skill-family contract - -The `/` files an adopter needs to populate depend -on which skill families they want to use. Pick the families that -match your project's needs — adopters who only use one family can -delete the other family's scaffold files from their -`.apache-steward/` directory. - -#### Security workflow — Steps 1–16 lifecycle - -Every skill in the [Security workflow](#security-workflow) -section above consults at least -[`/project.md`](projects/_template/project.md) for -identity. Beyond that, each skill reads only the files it needs: -canned responses, scope labels, milestone conventions, -title-normalisation rules, security-model pointers, release-train -roster, fix-workflow specifics. The adopter's *file-by-file* index -is in -[`projects/_template/README.md`](projects/_template/README.md) — -copy that into your `/` and fill in every `TODO`. - -#### PR triage and review - -The [PR triage and review](#pr-triage-and-review) skills consult -four scaffold files for project-specific knobs: - -| File | Used by | -|---|---| -| [`pr-management-config.md`](projects/_template/pr-management-config.md) | `pr-management-triage`, `pr-management-stats` | -| [`pr-management-triage-comment-templates.md`](projects/_template/pr-management-triage-comment-templates.md) | `pr-management-triage` | -| [`pr-management-triage-ci-check-map.md`](projects/_template/pr-management-triage-ci-check-map.md) | `pr-management-triage` | -| [`pr-management-code-review-criteria.md`](projects/_template/pr-management-code-review-criteria.md) | `pr-management-code-review` | - -Each PR-skill's `SKILL.md` carries an `## Adopter configuration` -section that documents which of these files it reads. The framework -currently embeds airflow-flavoured defaults inline in the skills' -supporting files; non-airflow adopters override by forking the -relevant supporting file into their own -`.claude/skills//`. A follow-up PR will complete the -extraction so the skills read exclusively from `/`. - -### How skills resolve `/` paths - -The framework uses a documented placeholder convention: the literal -string `/` in any skill file resolves to the -directory containing the adopter's `project.md` — typically -`.apache-steward/` in the tracker root. The substitution rule is -documented in -[`AGENTS.md` § Placeholder convention](AGENTS.md#placeholder-convention-used-in-skill-files); -it's a pure-text reference, no env-var or build step required. The -adopter keeps the actual content gitignored at the adopter level if -they want to (the framework itself is project-agnostic and ships no -per-project content). - -### Keeping the submodule current +## Keeping the submodule current **Always run `git submodule update --init --recursive` after every -pull on the tracker repository.** A plain `git pull` on the tracker -advances the framework submodule *pointer* in the index but does -**not** update the framework's working tree — skills will run -against the version of the framework checked out on disk, which is -the *previous* version after any pull that bumped the submodule -pointer. Wire it into a post-merge hook to make it automatic: +pull on the tracker repository.** A plain `git pull` advances the +framework submodule *pointer* in the index but does **not** update +the framework's working tree — skills will run against the +*previous* version after any pull that bumped the pointer. Wire it +into a post-merge hook to make it automatic: ```bash -# In the adopter tracker repo, one-time setup: cat >.git/hooks/post-merge <<'SH' #!/bin/sh exec git submodule update --init --recursive @@ -1108,3 +98,16 @@ The framework's skill upgrades the framework checkout itself; if the user is consuming the framework as a tracker submodule, the skill reminds them to follow up with submodule update on the parent tracker. + +## Cross-references + +- [`docs/prerequisites.md`](docs/prerequisites.md) — what a triager, + remediation developer, release manager, or PR maintainer needs + installed before invoking any framework skill (Claude Code, + Gmail MCP, GitHub auth, browser, `uv`, etc.). +- [`AGENTS.md`](AGENTS.md) — agent instructions, placeholder + convention, framework conventions. +- [`CONTRIBUTING.md`](CONTRIBUTING.md) — for framework + contributors. +- [`projects/_template/`](projects/_template/) — the adopter- + scaffold directory you copy into `/`. diff --git a/docs/pr-management/README.md b/docs/pr-management/README.md new file mode 100644 index 00000000..d1790c76 --- /dev/null +++ b/docs/pr-management/README.md @@ -0,0 +1,75 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [PR management skill family](#pr-management-skill-family) + - [Skills](#skills) + - [Adopter contract](#adopter-contract) + - [Cross-references](#cross-references) + + + + + +# PR management skill family + +Maintainer-facing PR-queue management for projects with a public +contributor PR queue. Three skills that compose into a complete +triage + review pass: + +1. **Triage** — sweep open PRs, classify against the project's + quality criteria, propose a disposition (draft / comment / + close / rebase / rerun / mark ready / ping), execute on + maintainer confirmation. +2. **Stats** — read-only summary tables of the open PR backlog, + grouped by area label, so the maintainer can see where queue + pressure is sitting before / after a triage sweep. +3. **Code review** — deep, line-aware code review one PR at a + time. Reads the diff, applies the project's review criteria, + drafts an `APPROVE` / `REQUEST_CHANGES` / `COMMENT` review + with inline comments, posts on confirmation. + +Why a framework skill family? These skills were originally +maintained inside `apache/airflow` as `breeze pr auto-triage` and +`breeze pr stats` — useful for any ASF project with a meaningful +contributor-PR queue, but locked behind airflow-specific +toolchain. Lifting them into the framework lets other adopters +reuse the playbook with their own +[adopter-config files](../../projects/_template/) for project-specific +knobs (committers team handle, area-label prefix, comment-template +wording, CI-check → doc-URL map, review-criteria source files). + +## Skills + +| Skill | Purpose | +|---|---| +| [`pr-management-triage`](../../.claude/skills/pr-management-triage/SKILL.md) | First-pass triage. Successor to `breeze pr auto-triage`. | +| [`pr-management-stats`](../../.claude/skills/pr-management-stats/SKILL.md) | Read-only summary tables grouped by area label. | +| [`pr-management-code-review`](../../.claude/skills/pr-management-code-review/SKILL.md) | Deep code review, one PR at a time. | + +## Adopter contract + +The skills resolve project-specific content from these files in +the adopter's `/` directory: + +| File | Used by | +|---|---| +| [`pr-management-config.md`](../../projects/_template/pr-management-config.md) | `pr-management-triage`, `pr-management-stats` | +| [`pr-management-triage-comment-templates.md`](../../projects/_template/pr-management-triage-comment-templates.md) | `pr-management-triage` | +| [`pr-management-triage-ci-check-map.md`](../../projects/_template/pr-management-triage-ci-check-map.md) | `pr-management-triage` | +| [`pr-management-code-review-criteria.md`](../../projects/_template/pr-management-code-review-criteria.md) | `pr-management-code-review` | + +The framework currently ships with airflow-flavoured defaults +inline in the supporting files of each skill (e.g. +[`pr-management-triage/comment-templates.md`](../../.claude/skills/pr-management-triage/comment-templates.md) +embeds airflow's PR-quality-criteria URL). A follow-up PR will +complete the extraction so the skills read exclusively from +`/`. Until then, non-airflow adopters override by +forking the relevant supporting file into their own +`.claude/skills//`. + +## Cross-references + +- [Top-level README — Adopting the framework](../../README.md#adopting-the-framework) — 3-step bootstrap. +- [`projects/_template/README.md`](../../projects/_template/README.md) — adopter scaffold index, including the four PR-management config files. diff --git a/docs/prerequisites.md b/docs/prerequisites.md new file mode 100644 index 00000000..a45ae1f3 --- /dev/null +++ b/docs/prerequisites.md @@ -0,0 +1,148 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Prerequisites for running framework skills](#prerequisites-for-running-framework-skills) + - [Prerequisites for running the agent skills](#prerequisites-for-running-the-agent-skills) + - [1. An agent that speaks the `SKILL.md` convention](#1-an-agent-that-speaks-the-skillmd-convention) + - [2. Email connection (Gmail MCP, today)](#2-email-connection-gmail-mcp-today) + - [3. GitHub connection (GitHub MCP / `gh` CLI)](#3-github-connection-github-mcp--gh-cli) + - [4. PMC membership (only for CVE allocation)](#4-pmc-membership-only-for-cve-allocation) + - [5. Browser (for the human-click steps)](#5-browser-for-the-human-click-steps) + - [6. Local `` clone (only for `security-issue-fix`)](#6-local-upstream-clone-only-for-security-issue-fix) + - [7. `uv` (for `generate-cve-json`)](#7-uv-for-generate-cve-json) + + + + + +# Prerequisites for running framework skills + +If you only plan to **comment on issues** from the project +board, skip this document — a browser and your tracker +collaborator access are enough. If you plan to **invoke any +framework skill**, check the following before running it. +Each skill also runs a short Step 0 pre-flight against this +list and stops with a clear message if something is missing. + +## Prerequisites for running the agent skills + +If you only plan to **comment on issues** from the board, skip this +section — a browser and your `` collaborator access are +enough. + +If you plan to **run any of the agent skills** (`import`, `sync`, +`security-cve-allocate`, `fix`, `generate-cve-json`, `deduplicate`) — typically +as a rotational triager, remediation developer, or release manager — +check the following setup **before** invoking a skill. Each skill also +runs a short Step 0 pre-flight against the same list and stops with a +clear message if something is missing, so you do not discover a +missing piece half-way through a workflow. + +### 1. An agent that speaks the `SKILL.md` convention + +[Claude Code](https://www.anthropic.com/claude-code) is the reference +implementation the skills are written against. Any agent that reads +the `.claude/skills/*/SKILL.md` files and follows their step-by-step +instructions should work; there is no hard dependency on Claude Code +specifically. + +The agent runs against pre-disclosure CVE content (private mail +threads, draft advisories, in-flight tracker discussions). Run it +with the credential-isolation setup documented in +[`docs/setup/secure-agent-setup.md`](docs/setup/secure-agent-setup.md) — a layered +defence built around Claude Code's filesystem sandbox, tool-level +permission rules, and a clean-env wrapper that strips credential- +shaped variables from the agent's environment. The required system +tools (`bubblewrap`, `socat`, `claude-code` itself) are pinned with +a 7-day upstream-release cooldown, mirroring the same convention the +framework uses for its `[tool.uv] exclude-newer` and Dependabot +configs. + +### 2. Email connection (Gmail MCP, today) + +The import, sync, and security-cve-allocate skills **read the security-list +mail thread** associated with each tracker and draft replies on that +thread. Today this goes through the +[Claude Gmail MCP](https://docs.anthropic.com/en/docs/build-with-claude/mcp) +connected to the personal Gmail account of a security-team member +who is subscribed to the adopting project's security list (see +`/project.md → Mailing lists`). That is enough +access for the skills to see inbound reports and create drafts on +the right threads. + +There is an ASF-wide alternative on the horizon: +[`rbowen/ponymail-mcp`](https://github.com/rbowen/ponymail-mcp) (by +Rich Bowen, former ASF board director and ComDev lead) now supports +OAuth authentication and can read private ASF lists. Once ASF OAuth +is wired in, individual triagers should be able to run the skills +without connecting their personal Gmail — authenticating directly +against ASF credentials (and, eventually, the ASF's new MFA) will be +sufficient. Until then, Gmail MCP is the way. + +**Without this connection:** `security-issue-import` cannot find new +reports, `security-issue-sync` cannot reconcile status with the mail +thread, and no skill can draft replies to reporters. The skills will +refuse to start and tell you to configure the MCP first. + +### 3. GitHub connection (GitHub MCP / `gh` CLI) + +Every skill reads and writes `` issues. Claude Code ships +with the GitHub MCP by default, and the skills also use the `gh` +CLI directly for some calls. What the skills need: + +- Authenticated `gh auth status` on the shell the agent runs in. +- Collaborator access (any permission level) on `` — the + security-team roster is maintained per-project; for the active + project see + [`/release-trains.md`](/release-trains.md#security-team-roster). +- For `security-issue-fix`: a fork of `` on your GitHub + account (the skill pushes a branch there before opening the PR + via `gh pr create --web`). + +### 4. PMC membership (only for CVE allocation) + +The adopting project's CVE-tool allocation form is **PMC-gated** on +the server side — only the project's PMC members can submit a CVE +allocation. Non-PMC triagers can still run `security-cve-allocate`; the +skill detects this up front (it asks *"are you a PMC member of +``?"*) and produces a relay message for a PMC member to +click through instead. For Airflow the concrete tool is ASF's +Vulnogram at ; see +[`/project.md → CVE tooling`](/project.md#cve-tooling). + +The same PMC gate applies to ponymail URL lookups on private ASF +lists; until `ponymail-mcp` is wired in with ASF OAuth, only PMC +members can see private-list archives directly. + +### 5. Browser (for the human-click steps) + +Several parts of the process involve a form a human has to fill in +and click — the CVE-tool allocation form, the CVE record `#source` +paste, the `gh pr create --web` compose view. The skills prepare +the URL and the exact text to paste and hand it off to the browser; +they do not try to automate those clicks. + +### 6. Local `` clone (only for `security-issue-fix`) + +The fix skill writes the change in your local clone, runs local +checks and tests, pushes a branch to your fork, and opens a PR via +`gh pr create --web`. You need: + +- a clean clone of `` reachable from the agent's working + directory — the path comes from `config/user.md → + environment.upstream_clone`, set interactively the first time + you run the skill; +- the adopting project's dev toolchain installed per its contributing + docs — for Airflow see + [`/fix-workflow.md → Toolchain`](/fix-workflow.md#toolchain); +- a remote named for your GitHub fork that `gh pr create` can push + to. + +### 7. `uv` (for `generate-cve-json`) + +The `generate-cve-json` script is a small `uv`-managed Python +project. Install `uv` once +(); the script bootstraps the +rest. diff --git a/docs/security/README.md b/docs/security/README.md new file mode 100644 index 00000000..66e82143 --- /dev/null +++ b/docs/security/README.md @@ -0,0 +1,86 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Security workflow skill family](#security-workflow-skill-family) + - [Skills](#skills) + - [Deep documentation](#deep-documentation) + - [Adopter contract](#adopter-contract) + - [Cross-references](#cross-references) + + + + + +# Security workflow skill family + +End-to-end automation for an ASF project's security-issue handling +process — from inbound report on the project's `security@` mailing +list through to a published CVE record on `cve.org`. Eight skills +that compose into the canonical 16-step lifecycle. + +Why a framework skill family? The 16-step process exists across +the foundation; every project's security team runs essentially +the same workflow with project-specific scope labels, mailing-list +addresses, milestone formats, and canned-response wording. Lifting +the workflow into a project-agnostic framework lets each adopter +plug their specifics into [`/`](../../projects/_template/) +and reuse the skills verbatim. + +## Skills + +| Skill | Purpose | +|---|---| +| [`security-issue-import`](../../.claude/skills/security-issue-import/SKILL.md) | Import new reports from `` into ``. | +| [`security-issue-import-from-pr`](../../.claude/skills/security-issue-import-from-pr/SKILL.md) | Open a tracker for a security-relevant fix opened as a public PR. | +| [`security-issue-import-from-md`](../../.claude/skills/security-issue-import-from-md/SKILL.md) | Bulk-import findings from a markdown report. | +| [`security-issue-sync`](../../.claude/skills/security-issue-sync/SKILL.md) | Reconcile a tracker against its mail thread, fix PR, release train, and archives. | +| [`security-cve-allocate`](../../.claude/skills/security-cve-allocate/SKILL.md) | Allocate a CVE for a tracker (Vulnogram URL + paste-ready JSON). | +| [`security-issue-fix`](../../.claude/skills/security-issue-fix/SKILL.md) | Implement the fix as a public PR in ``. | +| [`security-issue-deduplicate`](../../.claude/skills/security-issue-deduplicate/SKILL.md) | Merge two trackers describing the same root-cause vulnerability. | +| [`security-issue-invalidate`](../../.claude/skills/security-issue-invalidate/SKILL.md) | Close a tracker as invalid with a polite-but-firm reporter reply. | + +## Deep documentation + +- [**`process.md`**](process.md) — the 16-step lifecycle with + Mermaid diagram + per-step description; the label lifecycle + state diagram + label reference table. The authoritative + process reference. +- [**`roles.md`**](roles.md) — who owns which steps (issue + triager / remediation developer / release manager), the shared + conventions every role observes (keeping the reporter informed, + recording status transitions, confidentiality), and the + role-by-role workflow walkthroughs. +- [**`how-to-fix-a-security-issue.md`**](how-to-fix-a-security-issue.md) — + hands-on guide for a remediation developer picking up a + CVE-allocated tracker and shipping the fix. +- [**`new-members-onboarding.md`**](new-members-onboarding.md) — + onboarding for a new security-team member: tracker access, mail + list subscription, expected reading, first triage shadow. + +## Adopter contract + +The skills resolve project-specific content from the security- +workflow files in +[`/`](../../projects/_template/) — see the +adopter scaffold's +[`README.md`](../../projects/_template/README.md) for the +file-by-file index. Required at minimum: + +- `project.md` — identity, repos, mailing lists, tools +- `canned-responses.md` — reporter-facing reply templates +- `scope-labels.md` — scope label → CVE product mapping +- `release-trains.md` — release-manager attribution +- `title-normalization.md` — CVE-title regex cascade + +Optional but commonly needed: +`milestones.md`, `fix-workflow.md`, `security-model.md`, +`naming-conventions.md`. + +## Cross-references + +- [Top-level README — Adopting the framework](../../README.md#adopting-the-framework) — 3-step bootstrap. +- [`docs/prerequisites.md`](../prerequisites.md) — what a security + triager / remediation developer / release manager needs + installed before invoking any skill. diff --git a/how-to-fix-a-security-issue.md b/docs/security/how-to-fix-a-security-issue.md similarity index 86% rename from how-to-fix-a-security-issue.md rename to docs/security/how-to-fix-a-security-issue.md index e04d041d..5f870986 100644 --- a/how-to-fix-a-security-issue.md +++ b/docs/security/how-to-fix-a-security-issue.md @@ -14,10 +14,10 @@ High-level overview of how the security team handles a vulnerability report from inbound email through published CVE. This page is project-agnostic; the concrete lists, repos, release trains, and tooling for the adopting project live under -[`/`](projects/) — for Airflow, see +[`/`](../../projects/) — for Airflow, see [`/project.md`](/project.md). -The end-to-end 16-step lifecycle is in [`README.md`](README.md). This +The end-to-end 16-step lifecycle is in [`README.md`](../../README.md). This page is the two-minute summary. ## Process @@ -33,7 +33,7 @@ page is the two-minute summary. 2. **Triage.** A rotating triager imports new reports into the private `` repository (see the - [`security-issue-import`](.claude/skills/security-issue-import/SKILL.md) + [`security-issue-import`](../../.claude/skills/security-issue-import/SKILL.md) skill), classifies each candidate, and drafts a receipt-of-confirmation reply to the reporter. The team then discusses CVE-worthiness in the issue comments and — once the @@ -43,7 +43,7 @@ page is the two-minute summary. For the rarer case where a security-relevant fix lands as a public PR on `` without ever hitting ``, the triager uses - [`security-issue-import-from-pr`](.claude/skills/security-issue-import-from-pr/SKILL.md) + [`security-issue-import-from-pr`](../../.claude/skills/security-issue-import-from-pr/SKILL.md) instead. The skill creates the tracker directly with a scope label and the `Assessed` board column — the deliberate import implies the validity assessment has already happened informally, @@ -52,7 +52,7 @@ page is the two-minute summary. When the team's discussion lands a *consensus-invalid* decision, the triager applies that decision via the - [`security-issue-invalidate`](.claude/skills/security-issue-invalidate/SKILL.md) + [`security-issue-invalidate`](../../.claude/skills/security-issue-invalidate/SKILL.md) skill: it adds the `invalid` label, posts a short closing comment, archives the project-board item, and — when the tracker has an inbound `` thread — drafts a @@ -70,17 +70,17 @@ page is the two-minute summary. A PMC member of the adopting project allocates a CVE through the project's CVE tool (for Airflow, ASF Vulnogram). The allocation is PMC-gated; non-PMC triagers use the - [`security-cve-allocate`](.claude/skills/security-cve-allocate/SKILL.md) skill to + [`security-cve-allocate`](../../.claude/skills/security-cve-allocate/SKILL.md) skill to produce a relay message for a PMC member to click through. 4. **Remediation.** A security-team member writes the fix in the public `` repository (see the - [`security-issue-fix`](.claude/skills/security-issue-fix/SKILL.md) + [`security-issue-fix`](../../.claude/skills/security-issue-fix/SKILL.md) skill, which can draft the PR automatically). The public PR is scrubbed of CVE references, tracker-repo references, and any *"security fix"* signal — per the confidentiality rules in - [`AGENTS.md`](AGENTS.md#confidentiality-of-the-tracker-repository). + [`AGENTS.md`](../../AGENTS.md#confidentiality-of-the-tracker-repository). 5. **Release + advisory.** The release manager for the cut that carries the fix sends the @@ -105,7 +105,7 @@ page is the two-minute summary. from raising reports about issues they were not originally aware of. Such tools may themselves violate our security practices. * **Keep the reporter informed at every status transition** — see - the [*Keeping the reporter informed*](README.md#keeping-the-reporter-informed) + the [*Keeping the reporter informed*](../../README.md#keeping-the-reporter-informed) section of `README.md` for the full list of transitions and the drafting rules. * **Confidentiality first.** Tracker URLs and `#NNN` identifiers @@ -115,6 +115,6 @@ page is the two-minute summary. of a public PR (the words `CVE-`, *"vulnerability"*, *"security fix"*, *"advisory"*) stays embargoed until the advisory ships. See the - [Confidentiality of the tracker repository](AGENTS.md#confidentiality-of-the-tracker-repository) + [Confidentiality of the tracker repository](../../AGENTS.md#confidentiality-of-the-tracker-repository) section of `AGENTS.md` for the three-layer rule and the sharing-with-non-team-recipients pattern. diff --git a/new-members-onboarding.md b/docs/security/new-members-onboarding.md similarity index 90% rename from new-members-onboarding.md rename to docs/security/new-members-onboarding.md index bbab155e..007ece81 100644 --- a/new-members-onboarding.md +++ b/docs/security/new-members-onboarding.md @@ -23,7 +23,7 @@ is the soft-landing guide — it tells you how the team works, where the action happens, and what is expected of you in the first few weeks. -Read this end-to-end once, then use [`README.md`](README.md) as the +Read this end-to-end once, then use [`README.md`](../../README.md) as the operational reference when you start actually handling issues. The README is organised by role (triager / remediation developer / release manager); pick the role that matches what you are about to do and jump @@ -85,7 +85,7 @@ for the lookup command and the latest snapshot. - **Private PRs on the `` `main` branch** — an exceptional path used for highly-critical fixes that need private code review before going public. See - [Step 9](README.md#step-9--open-a-private-pr-exceptional-cases) of + [Step 9](../../README.md#step-9--open-a-private-pr-exceptional-cases) of the process. Some discussions with an obvious answer can be handled on the mailing @@ -98,7 +98,7 @@ discoverable for everyone else on the team. Take the pressure off. You do not have to drive any issue in your first week. A good starting routine: -1. **Read [`README.md`](README.md) once, skim the role sections.** Pick +1. **Read [`README.md`](../../README.md) once, skim the role sections.** Pick the role you think you are most likely to take on first, and read it in full. 2. **Open the adopting project's board** — its URL is in @@ -112,8 +112,8 @@ week. A good starting routine: 4. **Read [`/canned-responses.md`](/canned-responses.md).** These are the reply templates we send to reporters. They shape most of the tone you will eventually need to match when you draft a reply yourself. -5. **Read [`AGENTS.md`](AGENTS.md) at least the - [Confidentiality](AGENTS.md#confidentiality-of-the-tracker-repository) +5. **Read [`AGENTS.md`](../../AGENTS.md) at least the + [Confidentiality](../../AGENTS.md#confidentiality-of-the-tracker-repository) section.** The rule of thumb: nothing about the tracker repository (``) — issue numbers, labels, discussions, even the repo name — leaves the private channels. @@ -137,7 +137,7 @@ any per-user config. Once you have observed the process for a while, you can start taking on more of the work. The three rotating roles — issue triager, remediation developer, release manager — are defined in -[`README.md` — Who this guide is for](README.md#who-this-guide-is-for), +[`README.md` — Who this guide is for](../../README.md#who-this-guide-is-for), which describes the step ranges each role owns. From the onboarding perspective: @@ -158,14 +158,14 @@ You can volunteer to provide a fix for a specific issue even before formally taking on the remediation-developer role — just comment on the tracker and self-assign. You can also ask whether it makes sense to involve someone else to provide the fix (see -[Step 7](README.md#step-7--self-assign-and-implement-the-fix) for the +[Step 7](../../README.md#step-7--self-assign-and-implement-the-fix) for the delegation rules). # Using the agent skills A lot of the repetitive work on this team has been automated into agent skills that live under -[`.claude/skills/`](.claude/skills/). They are plain `SKILL.md` +[`.claude/skills/`](../../.claude/skills/). They are plain `SKILL.md` files with YAML frontmatter, so Claude Code picks them up automatically and other agents that follow the emerging skill convention can use them too. @@ -178,17 +178,17 @@ a few times a week are: - **`import new reports`** — converts un-imported security-list threads into trackers and drafts the receipt-of-confirmation reply. - See [Step 2](README.md#step-2--import-the-report). + See [Step 2](../../README.md#step-2--import-the-report). - **`sync all issues`** — reconciles every open tracker with its mail thread, its fix PR, the release train, and the users-list archive. - **`allocate CVE for issue #N`** — when a report is assessed as valid. - See [Step 6](README.md#step-6--allocate-the-cve). + See [Step 6](../../README.md#step-6--allocate-the-cve). There is also a fourth command for anyone willing to take on a remediation-developer turn: - **`try to fix issue #N`** — - [`security-issue-fix`](.claude/skills/security-issue-fix/SKILL.md) + [`security-issue-fix`](../../.claude/skills/security-issue-fix/SKILL.md) attempts to land the fix for a triaged tracker in one go. It runs a pre-fix sync, reads the discussion on the tracker to build a fix plan, shows you the plan, and — only after you confirm — writes the @@ -200,8 +200,8 @@ remediation-developer turn: repo slug / `vulnerability` / `security fix` leakage before being written or pushed. The skill refuses to operate on reports that are still being assessed, or on issues that need the private-PR fallback of - [Step 9](README.md#step-9--open-a-private-pr-exceptional-cases). See - [For remediation developers — Steps 7–11](README.md#for-remediation-developers--steps-711) + [Step 9](../../README.md#step-9--open-a-private-pr-exceptional-cases). See + [For remediation developers — Steps 7–11](../../README.md#for-remediation-developers--steps-711) for the full expectations around a fix PR. Every skill is a **proposal engine**, not an auto-pilot — it reads the @@ -211,7 +211,7 @@ for every label change, every body edit, every email draft, and every line of code the fix skill writes. The full list of skills, and what each one does, is in -[`AGENTS.md` — Reusable skills](AGENTS.md#reusable-skills). +[`AGENTS.md` — Reusable skills](../../AGENTS.md#reusable-skills). # Proactive work and process improvements @@ -227,9 +227,9 @@ worth fixing. PRs against any part of our process are welcome. The documents that shape the team are small enough to read in one sitting: -- [`README.md`](README.md) — the end-to-end handling process. +- [`README.md`](../../README.md) — the end-to-end handling process. - [`/canned-responses.md`](/canned-responses.md) — reply templates. -- [`AGENTS.md`](AGENTS.md) — agent-facing conventions and confidentiality rules. +- [`AGENTS.md`](../../AGENTS.md) — agent-facing conventions and confidentiality rules. - [`config/README.md`](config/README.md) — how the per-project and per-user configuration layers work, with a tutorial for your own `config/user.md`. diff --git a/docs/security/process.md b/docs/security/process.md new file mode 100644 index 00000000..7beae858 --- /dev/null +++ b/docs/security/process.md @@ -0,0 +1,389 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Security workflow — process and label lifecycle](#security-workflow--process-and-label-lifecycle) + - [Process reference: the 16 steps](#process-reference-the-16-steps) + - [Step 1 — Report arrives on security@](#step-1--report-arrives-on-security) + - [Step 2 — Import the report](#step-2--import-the-report) + - [Step 3 — Discuss CVE-worthiness](#step-3--discuss-cve-worthiness) + - [Step 4 — Escalate stalled discussions](#step-4--escalate-stalled-discussions) + - [Step 5 — Land the valid/invalid consensus](#step-5--land-the-validinvalid-consensus) + - [Step 6 — Allocate the CVE](#step-6--allocate-the-cve) + - [Step 7 — Self-assign and implement the fix](#step-7--self-assign-and-implement-the-fix) + - [Step 8 — Open a public PR (straightforward cases)](#step-8--open-a-public-pr-straightforward-cases) + - [Step 9 — Open a private PR (exceptional cases)](#step-9--open-a-private-pr-exceptional-cases) + - [Step 10 — Link the PR and apply `pr created`](#step-10--link-the-pr-and-apply-pr-created) + - [Step 11 — PR merged](#step-11--pr-merged) + - [Step 12 — Fix released](#step-12--fix-released) + - [Step 13 — Send the advisory](#step-13--send-the-advisory) + - [Step 14 — Capture the public advisory URL](#step-14--capture-the-public-advisory-url) + - [Step 15 — Publish the CVE record and close the issue](#step-15--publish-the-cve-record-and-close-the-issue) + - [Step 16 — Credit corrections](#step-16--credit-corrections) + - [Label lifecycle](#label-lifecycle) + - [State diagram](#state-diagram) + - [Label reference](#label-reference) + + + + + +# Security workflow — process and label lifecycle + +The authoritative reference for the 16-step security-issue +lifecycle and the label-lifecycle state diagram. The +[role guides](roles.md) point into specific steps; the +[security skills](../../README.md#skills) execute the steps. + +## Process reference: the 16 steps + +This is the authoritative outline of the 16-step lifecycle. Each step +links to the skill or document that owns the deep mechanics — the +brief descriptions below are an overview, not a substitute for the +linked skill's `SKILL.md`. If the role sections above conflict with +what is here, this reference wins. + +```mermaid +flowchart TD + S1[1. Report arrives on security-list] + S2[2. Import the report] + ALT2([alt: import from public PR]) + S3[3. Discuss CVE-worthiness] + S4[4. Escalate stalled discussions] + S5{5. Valid or invalid?} + INV([Invalid: close + reporter reply]) + S6[6. Allocate CVE] + S7[7. Self-assign + implement] + S8[8. Open public PR] + S9[9. Open private PR exceptional] + S10[10. Link PR + apply pr created] + S11[11. PR merged] + S12[12. Fix released] + S13[13. Send advisory] + S14[14. Capture public archive URL] + S15[15. Publish CVE + close issue] + S16[16. Credit corrections] + + S1 --> S2 + ALT2 --> S5 + S2 --> S3 + S3 --> S4 + S3 --> S5 + S4 --> S5 + S5 -->|valid| S6 + S5 -->|invalid| INV + S6 --> S7 + S7 --> S8 + S7 --> S9 + S8 --> S10 + S9 --> S10 + S10 --> S11 + S11 --> S12 + S12 --> S13 + S13 --> S14 + S14 --> S15 + S15 --> S16 + + classDef triager fill:#fff3cd,stroke:#664d03,color:#000 + classDef remed fill:#cfe2ff,stroke:#055160,color:#000 + classDef releaseMgr fill:#d4edda,stroke:#0f5132,color:#000 + classDef terminal fill:#f8d7da,stroke:#842029,color:#000 + + class S1,S2,ALT2,S3,S4,S5 triager + class S6,S7,S8,S9,S10,S11 remed + class S12,S13,S14,S15,S16 releaseMgr + class INV terminal +``` + +Colour key: yellow = triager (Steps 1–5), blue = remediation +developer (Steps 6–11), green = release manager (Steps 12–16), +red = terminal close. + +### Step 1 — Report arrives on security@ + +The reporter sends the issue to the adopting project's +`` (or to `security@apache.org`, which forwards to the +project list). + +### Step 2 — Import the report + +[`security-issue-import`](../../.claude/skills/security-issue-import/SKILL.md) +scans `` for un-imported threads, classifies each +candidate (real / automated-scan / consolidated / spam), extracts the +issue-template fields from the root message, and proposes one tracker +per valid report plus a Gmail receipt-of-confirmation draft. Nothing +is applied without explicit confirmation. The newly-created tracker +lands with `needs triage`. + +If the report matches a known-invalid pattern, the skill drafts the +matching canned reply from +[`/canned-responses.md`](/canned-responses.md) +and does **not** create a tracker — invalid noise never enters the +board. + +**Alternate entry — fix already opened as a public PR.** Use +[`security-issue-import-from-pr`](../../.claude/skills/security-issue-import-from-pr/SKILL.md). +The tracker lands directly in the `Assessed` column with the scope +label applied (validity already decided informally), so Step 5 is +skipped and the tracker is ready for `security-cve-allocate` +immediately. + +**Alternate entry — bulk import from markdown.** Use +[`security-issue-import-from-md`](../../.claude/skills/security-issue-import-from-md/SKILL.md) +when triaging the output of an AI security review or third-party +scanner. Each finding becomes one tracker. + +### Step 3 — Discuss CVE-worthiness + +Drive the validity assessment in tracker comments. Pull at least +one other security-team member into the discussion. Use canned +responses from +[`/canned-responses.md`](/canned-responses.md) +for negative assessments so the tone stays polite-but-firm. + +### Step 4 — Escalate stalled discussions + +If discussion stalls for ~30 days, escalate in **two phases**: + +* **Phase 1 — short call for ideas.** A 3-4-paragraph message that + states the report exists and asks the wider audience for input. + No AI analysis, no proposed fixes — phase 1 is deliberately bare so + domain experts can weigh in with novel ideas without being anchored + to a pre-baked solution. +* **Phase 2 — AI-generated design-space analysis.** Triggered if + phase 1 stays silent for ~7 more days. The agent drafts a + structured analysis (TL;DR, why-the-obvious-fix-is-insufficient, + options A/B/C with trade-offs, open design questions, tagged + reviewers per a documented selection methodology). The triager + reviews and approves before posting. + +Audiences are the same for both phases: ``, +`security@apache.org`, the original reporter. Both phases land as +rollup entries on the tracker (per +[`tools/github/status-rollup.md`](../../tools/github/status-rollup.md)) +with the action label `Sync (Step 4 escalation)`. + +### Step 5 — Land the valid/invalid consensus + +If valid, apply exactly one scope label from +[`/scope-labels.md`](/scope-labels.md); +remove `needs triage`. If invalid, +[`security-issue-invalidate`](../../.claude/skills/security-issue-invalidate/SKILL.md) +labels `invalid`, posts a closing comment, archives the board item, +and (for ``-imported trackers) drafts a polite-but-firm +reporter reply. If consensus cannot be reached, follow +[ASF voting](https://www.apache.org/foundation/voting.html#apache-voting-process) +on ``. + +If a candidate duplicate is detected, +[`security-issue-deduplicate`](../../.claude/skills/security-issue-deduplicate/SKILL.md) +merges two trackers in place — preserving every reporter's credit, +every mailing-list thread reference, and every independent +attack-vector description. The kept issue's body is updated, the +duplicate is closed with the `duplicate` label, and the CVE JSON +attachment is regenerated so both finders land in `credits[]`. + +### Step 6 — Allocate the CVE + +[`security-cve-allocate`](../../.claude/skills/security-cve-allocate/SKILL.md) +opens the project's CVE allocation tool (for Airflow, ASF Vulnogram +at ; in general see +[`/project.md → CVE tooling`](/project.md#cve-tooling)), +normalises the title per +[`/title-normalization.md`](/title-normalization.md), +and — if the triager isn't on the PMC — builds an `@`-mention relay +message for a PMC member. Once the allocated `CVE-YYYY-NNNNN` is +pasted back, the skill wires it into the tracker (CVE tool link +body field, `cve allocated` label, status-change comment, refreshed +CVE-JSON attachment) and hands off to `security-issue-sync` to +reconcile the rest. + +### Step 7 — Self-assign and implement the fix + +A security team member self-assigns and implements the fix. +Optional automation: +[`security-issue-fix`](../../.claude/skills/security-issue-fix/SKILL.md) +proposes an implementation plan, writes the change in your local +`` clone, runs local tests, and opens a public PR via +`gh pr create --web` with a scrubbed title + body. Every public +surface (commit message, branch name, PR title, PR body, newsfragment) +is grep-checked for `CVE-`, the `` slug, *"vulnerability"*, +*"security fix"*, and similar leakage before being written or pushed. + +The skill refuses to proceed for reports still being assessed, +reports not yet classified as valid, and changes that require the +private-PR fallback (Step 9). Even when it succeeds end-to-end, you +remain the PR's author and reviewer-facing contact — stay on the PR +through review and merge. + +Delegation to a trusted third-party individual is permitted under +LAZY CONSENSUS, sharing only the information required to implement +the fix. + +### Step 8 — Open a public PR (straightforward cases) + +The PR description **must not** reveal the CVE, the security nature +of the change, or link back to ``. See +[`AGENTS.md → Confidentiality`](../../AGENTS.md#confidentiality-of-the-tracker-repository). +Request the appropriate `backport-to-vN-N-test` label on the public +PR when the fix should ship on a patch train. + +### Step 9 — Open a private PR (exceptional cases) + +For highly critical fixes or code that needs private review, open +the PR against ``'s `main` branch first (not the +`tracker_default_branch` set in `/project.md`). CI +does not run there — run static checks + tests manually. Once +approved, push the branch to `` and re-open the PR there. + +### Step 10 — Link the PR and apply `pr created` + +The remediation developer links the PR in the tracker description +and applies `pr created` on ``. + +### Step 11 — PR merged + +When the `` PR merges, swap `pr created` → `pr merged` +and set the milestone of the release the fix will ship in (per +[`/milestones.md`](/milestones.md)). +Close any private variant in ``. The tracker waits at +`pr merged` until the release ships — this can be hours (fast core +patches) or weeks (provider waves on a fixed cadence). + +### Step 12 — Fix released + +When the release containing the fix ships to users (PyPI / Helm +registry / equivalent), +[`security-issue-sync`](../../.claude/skills/security-issue-sync/SKILL.md) +detects the release version on the next run and proposes the +`pr merged` → `fix released` swap, which is the hand-off cue from +remediation developer to release manager. The same pass proposes +posting a one-shot **release-manager hand-off comment** with a +numbered checklist (Steps 13–15 from the RM's perspective) and +links to the paste-ready CVE JSON, the Vulnogram `#source` and +`#email` tabs, and canned-response templates. + +### Step 13 — Send the advisory + +The release manager fills the remaining CVE fields: + +* CWE — see [cwe.mitre.org](https://cwe.mitre.org/data/index.html); +* affected versions (`0, < `); +* short public summary; +* severity score per the + [ASF severity rating](https://security.apache.org/blog/severityrating) + (lazy consensus during discussion; voting if there's disagreement; + RM has the final say to keep the announcement on schedule); +* references — `patch` PR URL on ``; +* credits — `reporter`, `remediation developer`. + +The RM generates the description, sets the CVE to REVIEW (then +READY), and sends the announcement emails from the project's CVE +tool. Apply `announced - emails sent`, remove `fix released`. **The +issue stays open** at this point — it closes only at Step 15. + +### Step 14 — Capture the public advisory URL + +Once the announcement is archived on the users@ list, the next +`security-issue-sync` run finds the URL, populates the *Public +advisory URL* body field (a dedicated field on the issue template — +never reuse the *"Security mailing list thread"* field), regenerates +the CVE JSON attachment (now carrying a `vendor-advisory` reference), +and adds the `announced` label. The same pass proposes a one-shot +**publication-ready notification comment** for the release manager. + +Until *Public advisory URL* is populated, the sync skill will not +propose `announced` — publishing a CVE with an empty +`vendor-advisory` reference would leak a broken record into +`cve.org`. + +### Step 15 — Publish the CVE record and close the issue + +The release manager opens the project's CVE tool's `#source` view at +`https://cveprocess.apache.org/cve5/#source`, copies the +latest CVE JSON attachment from the tracker (the one regenerated in +Step 14), pastes it into the form, saves, and moves the record from +READY to PUBLIC — propagating to [`cve.org`](https://cve.org). Then +closes the tracker (no label updates). `security-issue-sync` +follows the close with an `archiveProjectV2Item` mutation so the +closed tracker leaves the active board (see +[`tools/github/project-board.md` — *Archive a board item*](../../tools/github/project-board.md#archive-a-board-item--terminal-state-cleanup)). + +A tracker that sits on `announced` for more than a day or two is a +signal to ping the RM. + +### Step 16 — Credit corrections + +If credits need correction post-announcement, the release manager: + +* responds to the announcement emails with the missing credits; +* updates the project's CVE tool with the missing credits; +* asks the ASF security team to push the information to + [`cve.org`](https://cve.org). + +## Label lifecycle + +### State diagram + +The diagram below shows the typical state flow. Each node is a label (or a +cluster of labels that co-exist); each edge is a process step that moves +the issue forward. Closing dispositions (`invalid`, `not CVE worthy`, +`duplicate`, `wontfix`) can terminate the flow at any point after +`needs triage`. + +```mermaid +flowchart TD + A([report on project security list]) -->|step 2: security-issue-import| B[needs triage] + A2([security-relevant fix in public PR]) -->|step 2 alt: security-issue-import-from-pr| C + B -->|step 5: consensus invalid| X1([invalid / not CVE worthy / duplicate / wontfix]) + B -->|step 5: consensus valid| C["scope label
(project-specific — see
projects/<PROJECT>/scope-labels.md)"] + C -->|step 6: CVE reserved by PMC member| D[cve allocated] + D -->|step 10: public PR opened| E[pr created] + E -->|step 11: PR merges| F[pr merged] + F -->|step 12: release ships| G[fix released] + G -->|step 13: advisory sent| H[announced - emails sent] + H -->|step 14: archive URL captured| J[announced] + J -->|step 15: RM moves CVE to PUBLIC + close| Z([issue closed]) + + classDef closed fill:#f8d7da,stroke:#842029,color:#000; + classDef done fill:#d1e7dd,stroke:#0f5132,color:#000; + class X1,Z closed; + class H,J done; +``` + +The dashed-equivalent entry from `A2` represents the deliberate-import +path described in [Step 2](#step-2--import-the-report) above: +trackers opened from a public PR skip the `needs triage` column and +land directly at `scope label` (the `Assessed` column on the project +board) because the validity assessment has already happened +informally before invocation. + +### Label reference + +The table below repeats the same flow in tabular form. An issue typically +moves through these labels left-to-right. + +**Scope labels are project-specific** — the adopting project's concrete +scope labels live in +[`/scope-labels.md`](../../projects/) (for the currently +adopting project, [`/scope-labels.md`](/scope-labels.md)). +The table below uses `` as a placeholder for whichever scope +labels the adopting project defines. + +| Label | Meaning | Added at step | Removed at step | +| --- | --- | --- | --- | +| `needs triage` | Freshly filed; assessment not yet started. | 1 | 5 | +| `` | Scope of the vulnerability. Exactly one project-specific scope label is set. | 5 | never (sticks for the lifetime of the issue) | +| `cve allocated` | A CVE has been reserved for the issue. Allocation itself is PMC-gated (only the adopting project's PMC members can submit the CVE-tool allocation form); a non-PMC triager relays a request to a PMC member via the [`security-cve-allocate`](../../.claude/skills/security-cve-allocate/SKILL.md) skill. | 6 | never | +| `pr created` | A public fix PR has been opened on `` but has not yet merged. | 10 | 11 (replaced by `pr merged`) | +| `pr merged` | The fix PR has merged into ``; no release with the fix has shipped yet. | 11 | 12 (replaced by `fix released` when the release ships) | +| `fix released` | A release containing the fix has shipped to users; advisory has not been sent yet. | 12 | 13 (replaced by `announced - emails sent`) | +| `announced - emails sent` | The public advisory has been sent to the project's announce and users mailing lists (see `/project.md → Mailing lists`). The issue **stays open** after this label is applied; closing is gated on the RM completing Step 15. | 13 | never (stays on the issue after closing for audit history) | +| `announced` | The public advisory URL has been captured in the tracking issue's *Public advisory URL* body field and the attached CVE JSON has been regenerated so its `references[]` now carries the `vendor-advisory` URL. The tracking issue is waiting for the release manager to copy the CVE JSON into the project's CVE tool, move the record to PUBLIC, and close the issue (Step 15). No label changes at close — the issue closes with `announced` still set. | 14 | never (stays on the issue after closing) | +| `wontfix` / `invalid` / `not CVE worthy` / `duplicate` | Closing dispositions for reports that are not valid or not CVE-worthy. | 5 / 6 | — | + +The [`security-issue-sync`](../../.claude/skills/security-issue-sync/SKILL.md) +skill keeps these labels honest: on every run it detects the current state +of the issue, the fix PR, and the release train, and proposes the label +transitions the process requires. diff --git a/docs/security/roles.md b/docs/security/roles.md new file mode 100644 index 00000000..ff1d07dd --- /dev/null +++ b/docs/security/roles.md @@ -0,0 +1,369 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Security workflow — roles, conventions, and role guides](#security-workflow--roles-conventions-and-role-guides) + - [Who this guide is for](#who-this-guide-is-for) + - [Shared conventions](#shared-conventions) + - [Keeping the reporter informed](#keeping-the-reporter-informed) + - [Recording status transitions on the tracker](#recording-status-transitions-on-the-tracker) + - [Confidentiality](#confidentiality) + - [For issue triagers — Steps 1–6](#for-issue-triagers--steps-16) + - [Daily triage loop](#daily-triage-loop) + - [Assessing a report](#assessing-a-report) + - [Allocating the CVE](#allocating-the-cve) + - [Tools you use most](#tools-you-use-most) + - [For remediation developers — Steps 7–11](#for-remediation-developers--steps-711) + - [Picking up a tracker](#picking-up-a-tracker) + - [Attempting an automated fix](#attempting-an-automated-fix) + - [Opening the public fix PR manually](#opening-the-public-fix-pr-manually) + - [Private-PR fallback](#private-pr-fallback) + - [Handoff to the release manager](#handoff-to-the-release-manager) + - [Tools you use most](#tools-you-use-most-1) + - [For release managers — Steps 12–15](#for-release-managers--steps-1215) + - [Handoff from the remediation developer](#handoff-from-the-remediation-developer) + - [Sending the advisory](#sending-the-advisory) + - [Capturing the public archive URL](#capturing-the-public-archive-url) + - [Publishing the CVE and closing the issue](#publishing-the-cve-and-closing-the-issue) + - [Post-release credit corrections](#post-release-credit-corrections) + - [Tools you use most](#tools-you-use-most-2) + + + + + +# Security workflow — roles, conventions, and role guides + +Three roles share the security-issue handling process — +issue triager (Steps 1–6), remediation developer (Steps 7–11), +and release manager (Steps 12–15). This document covers who +owns what, the shared conventions every role observes, and +the per-role workflow. The detailed step descriptions live +in [`process.md`](process.md). + +## Who this guide is for + +Three roles share the handling process. Any security-team member can take on +any of them for a given issue, and in practice people rotate — but at any +moment a given tracking issue has exactly one person who owns the next move. + +Pick whichever applies to you now: + +- **I am new to the security team, or I mostly just want to comment on + issues.** Read [Shared conventions](#shared-conventions) below. The + adopting project's security-issues board — see + `/project.md → GitHub project board` — is the main + view. You do not need an agent for commenting. +- **I am a rotational triager** — running `import new reports` and + `sync all` a few times a week. Jump to + [For issue triagers — Steps 1–6](#for-issue-triagers--steps-16). +- **I picked up a tracker and am about to open a fix PR.** Jump to + [For remediation developers — Steps 7–11](#for-remediation-developers--steps-711). +- **I am the release manager for a cut containing a security fix.** Jump to + [For release managers — Steps 12–15](#for-release-managers--steps-1215). +- **I am looking up a specific step or label.** Go straight to + [Process reference](process.md#process-reference-the-16-steps) or + [Label lifecycle](process.md#label-lifecycle). + +## Shared conventions + +These conventions bind every role. If you are unsure whether a rule applies to +you, it does. + +### Keeping the reporter informed + +The security team commits to keeping the original reporter informed about the +state of their report **at every status transition**, on the original mail +thread (not on the GitHub-notifications mirror thread). A short status update +should be sent to the reporter whenever any of the following happens: + +* the report has been acknowledged or assessed (valid / invalid); +* a CVE has been allocated; +* a fix PR has been opened; +* a fix PR has been **merged**; +* the issue has been scheduled for a specific release (milestone set); +* the release has shipped and the public advisory has been sent; +* the CVE record has been published on cve.org (completes the disclosure); +* any credits or fields visible in the eventual public advisory have changed. + +Each status update should plainly state what has changed, link to the relevant +artifact (PR URL, CVE ID, advisory link), and state what comes next. If the +reporter has not yet replied with their preferred credit, ask the +credit-preference question — but **do not re-ask it if it has already been +asked** on the same thread and is still awaiting a reply. Pinging the reporter +twice about the same open question is rude and gets us blocklisted; default to +the reporter's full name from the original email if they do not respond +before publication. + +Reusable wording for the common cases lives in +[`/canned-responses.md`](/canned-responses.md) — consult it before drafting a +reply from scratch. + +### Recording status transitions on the tracker + +**Every status transition must also be recorded as a comment on the GitHub +issue in ``**, not only sent by email. The two channels +serve different audiences: the email keeps the reporter informed; the issue +comment keeps the rest of the security team and the release manager informed +without forcing them to reconstruct the state from labels and timestamps. The +comment should briefly state what changed, link to the artifact (PR URL, CVE +ID, advisory link), and indicate whether the reporter has been notified. + +### Confidentiality + +Confidentiality of the private tracker (`` for the +adopting project) is both a **lifecycle rule** and a **writing rule**: +every transition you record on a tracker, every status comment, every +email draft has to respect it. The full rule set — forbidden surfaces, +allowed surfaces, scrubbing guidance, the exception buckets for private +`security@` / `private@` threads and in-repo `gh issue comment` calls — +lives in +[`AGENTS.md` — Confidentiality of the tracker repository](../../AGENTS.md#confidentiality-of-the-tracker-repository). +Read it before editing anything that might be seen outside the team. + +## For issue triagers — Steps 1–6 + +You own the tracker from an inbound report on `` +through to a CVE allocated, a scope label applied, and the issue ready for a +remediation developer to pick up. Step 6 (the CVE allocation itself) is +PMC-gated: **only the adopting project's PMC members can submit the +CVE-tool allocation form**. If you are not on the PMC you relay a +pre-drafted request to a PMC +member — either way you are the one who lands the resulting CVE ID back into +the tracker. + +### Daily triage loop + +A typical triage sweep runs three skills in order: + +1. **`import new reports`** — + [`security-issue-import`](../../.claude/skills/security-issue-import/SKILL.md) + scans `` for threads not yet imported, + classifies each candidate (real report vs. automated-scan / consolidated / + media / spam), and proposes a tracker per valid report plus a + receipt-of-confirmation Gmail draft. See + [Step 2](process.md#step-2--import-the-report). +2. **`sync all`** — + [`security-issue-sync`](../../.claude/skills/security-issue-sync/SKILL.md) + reconciles every open tracker against its mail thread, the fix PR, the + release train, and the users@ archive. Proposes label / milestone / + assignee / body changes in one pass. +3. **`allocate CVE for issue #N`** — + [`security-cve-allocate`](../../.claude/skills/security-cve-allocate/SKILL.md) when a report has + been assessed as valid. See [Step 6](process.md#step-6--allocate-the-cve). + +Nothing is applied without an explicit confirmation — each skill is a +proposal engine, not an auto-pilot. + +### Assessing a report + +For each `needs triage` tracker, drive the validity assessment in comments, +pulling at least one other security-team member into the discussion. Use the +canned-response templates from [`/canned-responses.md`](/canned-responses.md) +for negative assessments so the tone stays polite-but-firm. + +When the report is confirmed valid, apply exactly one scope label from +the project's scope set (declared in +[`/scope-labels.md`](/scope-labels.md)). +If a report affects more than one scope, split into per-scope trackers +before allocation — the `security-issue-sync` skill surfaces this as +a blocker. See +[Step 5](process.md#step-5--land-the-validinvalid-consensus). + +If discussion stalls for about 30 days, escalate to a broader audience per +[Step 4](process.md#step-4--escalate-stalled-discussions). + +### Allocating the CVE + +Use [`security-cve-allocate`](../../.claude/skills/security-cve-allocate/SKILL.md). The skill asks up +front whether you are on the PMC; if not, it reshapes the recipe into an +``@``-mention relay message you forward to a PMC member on the tracker or on +the `` thread. Once the allocated `CVE-YYYY-NNNNN` +is pasted back, the skill wires it into the tracker in one pass (the *CVE +tool link* body field, the `cve allocated` label, a status-change comment, a +refreshed CVE-JSON attachment) and hands off to `security-issue-sync` to +reconcile the rest of the tracker. See [Step 6](process.md#step-6--allocate-the-cve) +for the full detail. + +### Tools you use most + +- [`security-issue-import`](../../.claude/skills/security-issue-import/SKILL.md) — + *"import new reports"* at the start of each triage sweep. The entry point + into the process for `` reports. +- [`security-issue-import-from-pr`](../../.claude/skills/security-issue-import-from-pr/SKILL.md) — + *"import a tracker from PR "* when a security-relevant fix landed + publicly without going through `` and the team has agreed + it warrants a CVE. Lands directly in the `Assessed` column. +- [`security-issue-sync`](../../.claude/skills/security-issue-sync/SKILL.md) — + *"sync "* or *"sync all"*. Surfaces stalled issues, missing + fields, credit replies, and scope-split requirements in one combined + proposal. +- [`security-cve-allocate`](../../.claude/skills/security-cve-allocate/SKILL.md) — *"allocate a CVE + for "*. +- [`generate-cve-json`](../../tools/vulnogram/generate-cve-json/SKILL.md) — to + refresh the paste-ready JSON embedded in the issue body on demand. +- [`security-issue-deduplicate`](../../.claude/skills/security-issue-deduplicate/SKILL.md) — + when two trackers describe the same root-cause bug discovered + independently. +- [`security-issue-invalidate`](../../.claude/skills/security-issue-invalidate/SKILL.md) — + *"close NN as invalid"* once Step 5 lands a consensus-invalid + decision. Applies the `invalid` label, archives the project-board + item, and (for ``-imported trackers) drafts a reply + to the reporter explaining the reasoning. + +## For remediation developers — Steps 7–11 + +You own the tracker from a CVE allocated to a merged public fix PR in +`` (including the `pr merged` hand-off where the tracker sits +waiting for the release train to ship). The role name matches the +`remediation developer` credit you receive in the published CVE record (see +`credits[]` with `type: "remediation developer"` in the generated CVE JSON). + +### Picking up a tracker + +Pick a tracker that has a scope label, `cve allocated`, and clear consensus +on the fix shape. Self-assign yourself on GitHub so the board reflects +ownership. See [Step 7](process.md#step-7--self-assign-and-implement-the-fix). + +### Attempting an automated fix + +Before writing the fix by hand, consider letting the +[`security-issue-fix`](../../.claude/skills/security-issue-fix/SKILL.md) skill try +it first. Invoked as *"try to fix issue #N"* (or *"draft a PR for #N"*), the +skill: + +- runs `security-issue-sync` first to make sure the tracker's state is + current; +- reads the full tracker discussion and the linked `security@` mail + thread and decides whether the issue is *easily fixable* — clear + consensus on the fix shape, small scope, known location in + ``. If it is not, the skill stops and tells you what + more the tracker needs before it is safe to attempt; +- if it is, proposes an implementation plan (which file(s) to touch, + what to change, what tests to add) and **waits for your explicit + confirmation** before making any edits; +- writes the change in your local `` clone, runs the + local static checks and tests, and iterates on failures; +- opens the public PR from your fork via `gh pr create --web` with a + scrubbed title and body — every public surface (commit message, + branch name, PR title, PR body, newsfragment) is grep-checked for + `CVE-`, the `` repo slug, `vulnerability`, *"security fix"* + and similar leakage before being written or pushed; +- updates the `` tracking issue with the new PR + link and applies the `pr created` label, handing back off to + `security-issue-sync`. + +The skill refuses to proceed in cases where a human decision still +needs to happen: reports that are still being assessed, reports not +yet classified as valid vulnerabilities, and changes that require the +private-PR fallback in +[Step 9](process.md#step-9--open-a-private-pr-exceptional-cases). If it refuses, +fall back to the manual flow below. + +Even when the skill succeeds end-to-end, you remain the PR's author +and reviewer-facing contact on the public `` PR. Stay +on the PR through review and merge. + +### Opening the public fix PR manually + +If you are writing the fix by hand, write the code change in your local +`` clone, run the local checks and tests, and open the PR +via `gh pr create --web`. The PR description **must not** reveal the CVE, +the security nature of the change, or link back to `` — +see [Step 8](process.md#step-8--open-a-public-pr-straightforward-cases) and the +confidentiality rules in +[`AGENTS.md`](../../AGENTS.md#confidentiality-of-the-tracker-repository). + +Request a `backport-to-v3-2-test` (or equivalent) label on the public PR +when the fix should ship on a patch train. + +### Private-PR fallback + +In exceptional cases — highly critical fixes, or code that needs private +review — open the PR against the `main` branch of `` +instead of ``. CI does not run there, so run static checks and +tests manually before asking for review. Once approved, re-open the PR in +`` by pushing the branch public. See +[Step 9](process.md#step-9--open-a-private-pr-exceptional-cases). + +### Handoff to the release manager + +Once the `` PR merges, `security-issue-sync` moves the tracker +from `pr created` to `pr merged` and sets the milestone of the release the +fix will ship in. The tracker then waits for the release train. When the +release ships, sync swaps `pr merged` → `fix released` and the tracker +becomes the release manager's responsibility. See +[Step 11](process.md#step-11--pr-merged) and [Step 12](process.md#step-12--fix-released). + +### Tools you use most + +- [`security-issue-fix`](../../.claude/skills/security-issue-fix/SKILL.md) — + *"try to fix issue #N"*. Proposes a plan, writes the code, runs local + tests, and opens a `--web` PR with a scrubbed title/body. See + [Attempting an automated fix](#attempting-an-automated-fix) above for + the full flow and the cases where the skill refuses to proceed. +- [`security-issue-sync`](../../.claude/skills/security-issue-sync/SKILL.md) — to + keep the tracker's labels, milestone, and assignee aligned with the PR + state as it moves through review and merge. + +## For release managers — Steps 12–15 + +You own the tracker from the moment the fix actually ships (`fix released`) +to a closed tracking issue with a PUBLISHED CVE record. The hand-off from +the remediation developer is automatic: `security-issue-sync` detects the +milestone version on PyPI / the Helm registry, swaps `pr merged` → +`fix released`, and assigns the advisory-send to you. + +### Handoff from the remediation developer + +Watch your `fix released` queue on the board. Until the `pr merged` → +`fix released` swap fires, the tracker is still the remediation developer's +(Step 11 territory). Once it fires, it is yours. See +[Step 12](process.md#step-12--fix-released). + +### Sending the advisory + +Review the attached CVE JSON on the tracker, fill any missing body fields +(CWE, severity, affected versions), and send the advisory emails to +`` / `` from the ASF CVE tool. +Add `announced - emails sent` and remove `fix released`. **Do not close the +issue yet** — see [Step 13](process.md#step-13--send-the-advisory). + +### Capturing the public archive URL + +This is a handoff the sync skill handles for you: once the advisory has +been archived on the users@ list, the next `security-issue-sync` run finds +the URL, populates the *Public advisory URL* body field, regenerates the +CVE JSON attachment, and moves the label to `announced`. See +[Step 14](process.md#step-14--capture-the-public-advisory-url). + +### Publishing the CVE and closing the issue + +For every `announced` issue: open Vulnogram at +`https://cveprocess.apache.org/cve5/#source`, paste the latest +attached CVE JSON, save, and move the record from REVIEW to PUBLIC. +Then close the issue (do not update any labels). This is the terminal +step of the lifecycle. See +[Step 15](process.md#step-15--publish-the-cve-record-and-close-the-issue). + +An issue that sits on `announced` for more than a day or two +is a signal to ping the RM. + +### Post-release credit corrections + +If credits need correction after announcement, respond to the announcement +emails with the missing credits, update the ASF CVE tool, and ask the ASF +security team to push the information to `cve.org`. See +[Step 16](process.md#step-16--credit-corrections). + +### Tools you use most + +- [`security-issue-sync`](../../.claude/skills/security-issue-sync/SKILL.md) — + *"sync announced"* at the start of each release window, to + see the `announced` backlog needing a Vulnogram push. Also + *"sync CVE-YYYY-NNNN"* to drill into one specific CVE before sending the + advisory. +- [`generate-cve-json`](../../tools/vulnogram/generate-cve-json/SKILL.md) — to + regenerate the attachment on demand when a body field changes after the + URL has been captured. diff --git a/docs/setup/README.md b/docs/setup/README.md new file mode 100644 index 00000000..76211c38 --- /dev/null +++ b/docs/setup/README.md @@ -0,0 +1,78 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Setup skill family](#setup-skill-family) + - [Skills](#skills) + - [Deep documentation](#deep-documentation) + - [Typical lifecycle](#typical-lifecycle) + - [Cross-references](#cross-references) + + + + + +# Setup skill family + +The **setup** skill family is the prerequisite for running any +framework skill. It walks a new adopter (or a fresh dev machine on +an existing adopter) through the secure-agent install — pinned +system tools, the project-scope `.claude/settings.json` sandbox +block, the `claude-iso` clean-env wrapper, the user-scope hooks — +and through the ongoing housekeeping (verify install drift, pull +framework updates, sync shared user-scope config across machines). + +Why a dedicated install skill family? The framework's other skills +run against pre-disclosure CVE content, private mailing lists, and +in-flight tracker discussions. Without the layered defence the +setup skills install (sandbox + permission rules + clean-env +wrapper), a misconfigured agent can leak credentials or +pre-disclosure content into the model provider's training data or +into a public PR. The setup family is what makes the rest of the +framework safe to use. + +## Skills + +| Skill | Purpose | +|---|---| +| [`setup-isolated-setup-install`](../../.claude/skills/setup-isolated-setup-install/SKILL.md) | First-time install of the secure agent setup. | +| [`setup-isolated-setup-verify`](../../.claude/skills/setup-isolated-setup-verify/SKILL.md) | Verify the secure setup landed correctly. | +| [`setup-isolated-setup-update`](../../.claude/skills/setup-isolated-setup-update/SKILL.md) | Surface drift between the installed setup and the framework's latest. | +| [`setup-steward-upgrade`](../../.claude/skills/setup-steward-upgrade/SKILL.md) | Pull the framework checkout to latest `origin/main`. | +| [`setup-steward-verify`](../../.claude/skills/setup-steward-verify/SKILL.md) | Verify the framework is integrated correctly into an adopter tracker. | +| [`setup-shared-config-sync`](../../.claude/skills/setup-shared-config-sync/SKILL.md) | Commit + push the user's shared Claude config to its sync repo. | + +## Deep documentation + +- [**`secure-agent-setup.md`**](secure-agent-setup.md) — full + install walkthrough. The authoritative reference the + `setup-isolated-setup-install` skill steps through. +- [**`secure-agent-internals.md`**](secure-agent-internals.md) — + how the layered defence works (sandbox + permission rules + + clean-env wrapper) and why each layer exists. + +## Typical lifecycle + +```text +new dev machine + ↓ setup-isolated-setup-install +isolated setup installed + ↓ setup-isolated-setup-verify (any time, especially after Claude Code upgrade) +verified + ↓ setup-isolated-setup-update (monthly / after Claude Code upgrade) +drift surfaced + ↓ setup-steward-upgrade (when framework releases something new) +framework checkout up to date +``` + +`setup-shared-config-sync` is orthogonal — it commits the user's +`~/.claude/CLAUDE.md` and other shared config to a private sync +repo so a fresh dev machine can pick it up (run after editing any +file under `~/.claude-config/`). + +## Cross-references + +- [Top-level README — Adopting the framework](../../README.md#adopting-the-framework) — 3-step bootstrap. +- [`docs/prerequisites.md`](../prerequisites.md) — what each framework + skill needs (Claude Code, Gmail MCP, GitHub auth, browser, etc.). diff --git a/secure-agent-internals.md b/docs/setup/secure-agent-internals.md similarity index 96% rename from secure-agent-internals.md rename to docs/setup/secure-agent-internals.md index 1aff905c..4263b51b 100644 --- a/secure-agent-internals.md +++ b/docs/setup/secure-agent-internals.md @@ -80,7 +80,7 @@ It does **not** defend against: | **3. Forced confirmation** | Claude Code's `permissions.ask` | Visible-to-others writes (`git push`, `gh pr create`, …) without an explicit yes. | Layers 1, 2, and 3 are configured by the same -[`.claude/settings.json`](.claude/settings.json) the framework +[`.claude/settings.json`](../../.claude/settings.json) the framework dogfoods. Adopters copy the same shape into their own tracker repo (see [Adopter setup](secure-agent-setup.md#adopter-setup) @@ -107,7 +107,7 @@ a Bash subprocess (an MCP server's child process, a `gh` CLI call, a `python` snippet the model decided to run) from reading a denied path. `permissions.deny` stops the agent's Read tool from reading the same path. A secure setup needs both: the framework's -[`.claude/settings.json`](.claude/settings.json) deny-lists +[`.claude/settings.json`](../../.claude/settings.json) deny-lists `Read(~/.config/gh/**)` *and* allow-reads `~/.config/gh/` in the sandbox, so `gh` can see its token but the agent can never read the file. @@ -151,7 +151,7 @@ same — denied paths are unreachable from within the subprocess. No system packages need pinning on macOS — Seatbelt ships with the OS. The framework's -[`pinned-versions.toml`](tools/agent-isolation/pinned-versions.toml) +[`pinned-versions.toml`](../../tools/agent-isolation/pinned-versions.toml) only pins `bubblewrap`, `socat`, and `claude-code` itself; Seatbelt does not appear because its version *is* the OS version. @@ -239,7 +239,7 @@ content: surfaces it to the context window. No layer above can prevent that once a Read happens. *Mitigation: never commit secrets to the tracker repo; the framework's - [`AGENTS.md` — Confidentiality of ``](AGENTS.md#confidentiality-of-the-tracker-repository) + [`AGENTS.md` — Confidentiality of ``](../../AGENTS.md#confidentiality-of-the-tracker-repository) rule is the policy backstop.* - **Domain fronting / CDN abuse via allow-listed hosts.** The `sandbox.network.allowedDomains` allowlist matches by SNI; an @@ -264,6 +264,6 @@ content: [Sandbox-bypass visibility hook](secure-agent-setup.md#sandbox-bypass-visibility-hook) — the install instructions for the surfacing pieces this document only describes mechanically. -- [`AGENTS.md`](AGENTS.md) — placeholder convention used in skill +- [`AGENTS.md`](../../AGENTS.md) — placeholder convention used in skill files. -- [`README.md`](README.md) — framework overview. +- [`README.md`](../../README.md) — framework overview. diff --git a/secure-agent-setup.md b/docs/setup/secure-agent-setup.md similarity index 97% rename from secure-agent-setup.md rename to docs/setup/secure-agent-setup.md index d623a6cf..532556f3 100644 --- a/secure-agent-setup.md +++ b/docs/setup/secure-agent-setup.md @@ -115,12 +115,12 @@ privilege-elevating runs without you saying so. ``` The skills are at -[`.claude/skills/setup-steward-verify/`](.claude/skills/setup-steward-verify/SKILL.md), -[`.claude/skills/setup-isolated-setup-install/`](.claude/skills/setup-isolated-setup-install/SKILL.md), -[`.claude/skills/setup-isolated-setup-verify/`](.claude/skills/setup-isolated-setup-verify/SKILL.md), -[`.claude/skills/setup-steward-upgrade/`](.claude/skills/setup-steward-upgrade/SKILL.md), -[`.claude/skills/setup-isolated-setup-update/`](.claude/skills/setup-isolated-setup-update/SKILL.md), -[`.claude/skills/setup-shared-config-sync/`](.claude/skills/setup-shared-config-sync/SKILL.md). +[`.claude/skills/setup-steward-verify/`](../../.claude/skills/setup-steward-verify/SKILL.md), +[`.claude/skills/setup-isolated-setup-install/`](../../.claude/skills/setup-isolated-setup-install/SKILL.md), +[`.claude/skills/setup-isolated-setup-verify/`](../../.claude/skills/setup-isolated-setup-verify/SKILL.md), +[`.claude/skills/setup-steward-upgrade/`](../../.claude/skills/setup-steward-upgrade/SKILL.md), +[`.claude/skills/setup-isolated-setup-update/`](../../.claude/skills/setup-isolated-setup-update/SKILL.md), +[`.claude/skills/setup-shared-config-sync/`](../../.claude/skills/setup-shared-config-sync/SKILL.md). Each skill references back into the canonical sections of this document rather than duplicating them, so anything the skill walks you through has a longer-form section here you can read for @@ -188,7 +188,7 @@ semantics or sandbox flags is caught broadly within hours of release. The current pins live in machine-readable form in -[`tools/agent-isolation/pinned-versions.toml`](tools/agent-isolation/pinned-versions.toml): +[`tools/agent-isolation/pinned-versions.toml`](../../tools/agent-isolation/pinned-versions.toml): | Tool | Pinned version | Released | Cooldown | Purpose | |---|---|---|---|---| @@ -325,7 +325,7 @@ actually lands the bump. ## The framework's own `.claude/settings.json` The framework dogfoods the secure config in -[`.claude/settings.json`](.claude/settings.json). The full block is +[`.claude/settings.json`](../../.claude/settings.json). The full block is below, annotated. ```jsonc @@ -394,7 +394,7 @@ blocks the agent's Read tool from reading the same path. Layer 0 — strip credential-shaped env vars from the parent shell before invoking `claude` — is implemented by -[`tools/agent-isolation/claude-iso.sh`](tools/agent-isolation/claude-iso.sh). +[`tools/agent-isolation/claude-iso.sh`](../../tools/agent-isolation/claude-iso.sh). There are two valid ways to make `claude-iso` available on your shell. Pick whichever matches how you use Claude Code; the wrapper @@ -493,7 +493,7 @@ honouring the bypass, but in a long session the prompt is easy to skim past, especially when several appear in quick succession. The framework ships a `PreToolUse` hook in -[`tools/agent-isolation/sandbox-bypass-warn.sh`](tools/agent-isolation/sandbox-bypass-warn.sh) +[`tools/agent-isolation/sandbox-bypass-warn.sh`](../../tools/agent-isolation/sandbox-bypass-warn.sh) that makes every bypass attempt visually impossible to miss: a bold red banner with the command and the model's stated reason printed to stderr, before the permission prompt appears. @@ -507,7 +507,7 @@ about to approve. ### Why install it user-scope, not project-scope Unlike the framework's -[`.claude/settings.json`](.claude/settings.json) (which is +[`.claude/settings.json`](../../.claude/settings.json) (which is repo-scoped — only sessions started inside the tracker repo see it), this hook is most useful in **`~/.claude/settings.json`** — the user-scope config that applies @@ -601,7 +601,7 @@ this session — a session that is inadvertently running with unnoticed for hours. The framework ships -[`tools/agent-isolation/sandbox-status-line.sh`](tools/agent-isolation/sandbox-status-line.sh) +[`tools/agent-isolation/sandbox-status-line.sh`](../../tools/agent-isolation/sandbox-status-line.sh) to render exactly that: - ` [sandbox]` in green when the active settings set @@ -655,7 +655,7 @@ renderer rather than replacing it. For adopters who want a richer variant out of the box, the framework also ships -[`tools/agent-isolation/sandbox-status-line-rich.sh`](tools/agent-isolation/sandbox-status-line-rich.sh). +[`tools/agent-isolation/sandbox-status-line-rich.sh`](../../tools/agent-isolation/sandbox-status-line-rich.sh). Same sandbox-state detection, plus folder name (hash-coloured for a stable per-repo identity), git branch + dirty marker + ahead/behind, per-branch PR title (cached for 5 min, silent when `gh` is missing or @@ -673,7 +673,7 @@ echo '{"model":{"display_name":"Sonnet 4.6"},"workspace":{"current_dir":"'"$PWD" ``` Expected output, *inside* this repo (its -[`.claude/settings.json`](.claude/settings.json) sets +[`.claude/settings.json`](../../.claude/settings.json) sets `sandbox.enabled: true`, and assuming `.claude/settings.local.json` either does not exist or does not override the key): `Sonnet 4.6 [sandbox]` with `[sandbox]` rendered in green. From a @@ -840,7 +840,7 @@ sub-section that follows. 1. Install the pinned tools per [Install commands](#install-commands) above. 2. Copy - [`.claude/settings.json`](.claude/settings.json) from the framework + [`.claude/settings.json`](../../.claude/settings.json) from the framework submodule into `/.claude/settings.json`. Adjust: - The `sandbox.network.allowedDomains` list — drop the framework domains you don't actually use, add any project-specific hosts. @@ -1210,7 +1210,7 @@ gaps; together they are the actual sandbox. and Seatbelt (macOS) enforce the policy at the OS layer, the SNI / DoH blind spot, the feedback-mechanism layering, and the residual risks the setup does not eliminate. -- [`AGENTS.md`](AGENTS.md) — placeholder convention used in skill +- [`AGENTS.md`](../../AGENTS.md) — placeholder convention used in skill files (``, ``, ``, …). -- [`README.md`](README.md) — framework overview and how the +- [`README.md`](../../README.md) — framework overview and how the secure setup fits the broader skill workflow. diff --git a/tools/agent-isolation/README.md b/tools/agent-isolation/README.md index f5cad041..32f900b2 100644 --- a/tools/agent-isolation/README.md +++ b/tools/agent-isolation/README.md @@ -15,7 +15,7 @@ # `tools/agent-isolation/` — secure agent setup helpers This directory ships the moving pieces the framework's -[`secure-agent-setup.md`](../../secure-agent-setup.md) document +[`docs/setup/secure-agent-setup.md`](../../docs/setup/secure-agent-setup.md) document references. It is not a Python project (unlike the sibling tools under `tools/vulnogram/` and `tools/gmail/oauth-draft/`) — these are plain shell scripts plus a TOML manifest of pinned upstream @@ -42,7 +42,7 @@ npm install -g --no-save @anthropic-ai/claude-code@2.1.117 # Source the wrapper into your shell: source /path/to/airflow-steward/tools/agent-isolation/claude-iso.sh -# Optional: make claude-iso the default `claude` (see secure-agent-setup.md +# Optional: make claude-iso the default `claude` (see docs/setup/secure-agent-setup.md # for the trade-off — the alias also strips env in non-tracker sessions): alias claude='claude-iso' @@ -56,8 +56,8 @@ bash /path/to/airflow-steward/tools/agent-isolation/check-tool-updates.sh ## Referenced by -- [`../../secure-agent-setup.md`](../../secure-agent-setup.md) — +- [`../../docs/setup/secure-agent-setup.md`](../../docs/setup/secure-agent-setup.md) — the user-facing setup document. Read that first. - [`../../.claude/settings.json`](../../.claude/settings.json) — the framework's own dogfooded secure config. Adopters scaffold their - own version from the example block in `secure-agent-setup.md`. + own version from the example block in `docs/setup/secure-agent-setup.md`. diff --git a/tools/agent-isolation/check-tool-updates.sh b/tools/agent-isolation/check-tool-updates.sh index 0c60b3ab..3f1ae247 100755 --- a/tools/agent-isolation/check-tool-updates.sh +++ b/tools/agent-isolation/check-tool-updates.sh @@ -213,7 +213,7 @@ To bump a pinned tool: 2. Edit `tools/agent-isolation/pinned-versions.toml` — update both `version` and `released` for that tool, plus the top-level `pinned_at` field to today's date. - 3. Update the install command in `secure-agent-setup.md` if the + 3. Update the install command in `docs/setup/secure-agent-setup.md` if the distro package version has shifted. 4. Open the bump as its own PR with a short rationale. diff --git a/tools/agent-isolation/claude-iso.sh b/tools/agent-isolation/claude-iso.sh index 8581513b..f1dc198a 100755 --- a/tools/agent-isolation/claude-iso.sh +++ b/tools/agent-isolation/claude-iso.sh @@ -19,7 +19,7 @@ # claude-iso.sh — launch Claude Code with a clean environment. # # This is layer 0 of the secure-agent setup (see -# `secure-agent-setup.md`): strip every credential-shaped +# `docs/setup/secure-agent-setup.md`): strip every credential-shaped # environment variable from the parent shell before exec'ing # Claude Code, so the agent never sees `$AWS_*`, `$GH_TOKEN`, # `$ANTHROPIC_API_KEY`, etc. that an unrelated terminal session @@ -27,7 +27,7 @@ # # Filesystem-level isolation (the bigger lift) is enforced by # Claude Code's `sandbox` feature — see the `.claude/settings.json` -# block in `secure-agent-setup.md`. This wrapper is the +# block in `docs/setup/secure-agent-setup.md`. This wrapper is the # environment-variable counterpart. # # Usage: @@ -45,7 +45,7 @@ claude_iso_main() { # the lookup uses the user's normal $PATH. Use a path-only lookup # (bash `type -P`, zsh `whence -p`) instead of `command -v`: with # `command -v`, an `alias claude=claude-iso` in the user's rc file - # (a documented setup option — see `secure-agent-setup.md`) would + # (a documented setup option — see `docs/setup/secure-agent-setup.md`) would # resolve back to the alias and recurse. local claude_bin if [[ -n "${ZSH_VERSION-}" ]]; then @@ -54,7 +54,7 @@ claude_iso_main() { claude_bin="$(type -P claude 2>/dev/null || true)" fi if [[ -z "$claude_bin" ]]; then - echo "claude-iso: 'claude' not found on PATH. Install per secure-agent-setup.md." >&2 + echo "claude-iso: 'claude' not found on PATH. Install per docs/setup/secure-agent-setup.md." >&2 return 127 fi diff --git a/tools/agent-isolation/pinned-versions.toml b/tools/agent-isolation/pinned-versions.toml index 20037c06..9f465777 100644 --- a/tools/agent-isolation/pinned-versions.toml +++ b/tools/agent-isolation/pinned-versions.toml @@ -38,7 +38,7 @@ # shipping pre-release fix-forwards routinely. # # Adopters consume this file by: -# 1. Reading the versions in `secure-agent-setup.md` and installing +# 1. Reading the versions in `docs/setup/secure-agent-setup.md` and installing # them on the host (the doc has the install commands per distro). # 2. Running `tools/agent-isolation/check-tool-updates.sh` weekly # (or via `/schedule`) to surface upstream releases that have @@ -69,7 +69,7 @@ docs = "https://github.com/containers/bubblewrap" upstream_releases = "https://api.github.com/repos/containers/bubblewrap/releases" # Install commands per distro (use the package manager's syntax for # requesting a specific version where supported). See -# `secure-agent-setup.md` for adopter-facing install steps. +# `docs/setup/secure-agent-setup.md` for adopter-facing install steps. install.apt = "apt-get install --no-install-recommends bubblewrap=0.11.1-*" install.dnf = "dnf install bubblewrap-0.11.1" install.brew = "# macOS does not need bubblewrap; it uses Seatbelt." diff --git a/tools/agent-isolation/sandbox-bypass-warn.sh b/tools/agent-isolation/sandbox-bypass-warn.sh index 1cc54bac..b865a489 100755 --- a/tools/agent-isolation/sandbox-bypass-warn.sh +++ b/tools/agent-isolation/sandbox-bypass-warn.sh @@ -64,7 +64,7 @@ # } # } # -# See `secure-agent-setup.md` → "Sandbox-bypass visibility hook" +# See `docs/setup/secure-agent-setup.md` → "Sandbox-bypass visibility hook" # for the install steps and the "Syncing user-scope config across # machines" section for the private-repo pattern that keeps this # hook in lockstep across hosts. diff --git a/tools/agent-isolation/sandbox-status-line-rich.sh b/tools/agent-isolation/sandbox-status-line-rich.sh index 4f007c35..6142bc1e 100755 --- a/tools/agent-isolation/sandbox-status-line-rich.sh +++ b/tools/agent-isolation/sandbox-status-line-rich.sh @@ -70,7 +70,7 @@ # } # } # -# See `secure-agent-setup.md` → "Sandbox-state status line" for the +# See `docs/setup/secure-agent-setup.md` → "Sandbox-state status line" for the # install steps and the trade-off discussion. The minimal sibling # remains the documented default; this variant is opt-in. diff --git a/tools/agent-isolation/sandbox-status-line.sh b/tools/agent-isolation/sandbox-status-line.sh index 58325462..c6a59dd7 100755 --- a/tools/agent-isolation/sandbox-status-line.sh +++ b/tools/agent-isolation/sandbox-status-line.sh @@ -53,7 +53,7 @@ # } # } # -# See `secure-agent-setup.md` → "Sandbox-state status line" for +# See `docs/setup/secure-agent-setup.md` → "Sandbox-state status line" for # install steps and the trade-off discussion. set -u diff --git a/tools/dev/check-placeholders.sh b/tools/dev/check-placeholders.sh index 2cea5a0f..2064c8f2 100755 --- a/tools/dev/check-placeholders.sh +++ b/tools/dev/check-placeholders.sh @@ -64,9 +64,9 @@ ALLOWLIST_PATHS=( "README.md" "AGENTS.md" "CONTRIBUTING.md" - "secure-agent-setup.md" - "how-to-fix-a-security-issue.md" - "new-members-onboarding.md" + "docs/setup/secure-agent-setup.md" + "docs/security/how-to-fix-a-security-issue.md" + "docs/security/new-members-onboarding.md" "pyproject.toml" "projects/_template/" "tools/dev/check-placeholders.sh"