blob: f01a6c40b3a3159d9da60d8fac6413b25246786d [file] [view]
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Contributing](#contributing)
- [Project structure](#project-structure)
- [Directory tree](#directory-tree)
- [Getting set up](#getting-set-up)
- [Making changes](#making-changes)
- [Running the dev loop](#running-the-dev-loop)
- [Opening a pull request](#opening-a-pull-request)
- [Confidentiality](#confidentiality)
- [Authoritative references](#authoritative-references)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- SPDX-License-Identifier: Apache-2.0
https://www.apache.org/licenses/LICENSE-2.0 -->
# Contributing
Thanks for helping improve this repository. It is a reusable framework
for running the ASF security-disclosure process as a set of agent-driven
skills. Adopting projects ship a per-project configuration layer
(`<project-config>/`) in their own tracker repo and consume this
framework as a submodule — see [`projects/_template/`](projects/_template/)
for the scaffold an adopter copies and fills in.
Before sending a patch, please skim this file end-to-end: it lays out
the layering the repository depends on, and a patch that ignores the
layering is hard to land no matter how correct it is in isolation.
## Project structure
The tree has four layers, each with a clearly-scoped job. The invariant
is that a skill running against an adopting project should be able to
resolve every piece of context it needs from some combination of the
four — no hard-coded project assumptions anywhere.
- **Root docs** carry the cross-cutting rules every contributor, agent,
and reviewer is expected to have read. [`README.md`](README.md) is the
canonical 16-step handling process, from report-arrival to CVE
publication. [`AGENTS.md`](AGENTS.md) is the editorial contract: tone,
brevity, confidentiality, linking conventions, the placeholder
substitution rule (`<PROJECT>`, `<tracker>`, `<upstream>`), 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
human-facing guides that sit alongside those.
- **Skills** live under
[`.claude/skills/`](.claude/skills/). Each is a `SKILL.md` that
encodes one workflow — importing a new report, syncing a tracker
against the world, allocating a CVE, drafting a fix PR, or
deduplicating two trackers. Skills use the `<PROJECT>` /
`<tracker>` / `<upstream>` placeholders everywhere and resolve them
at runtime. They must not contain project-specific strings.
- **Config** lives under [`config/`](config/) and wires the runtime
together.
[`config/active-project.md`](config/active-project.md) declares which
subtree under `projects/` is active (checked in);
[`config/user.md`](config/README.md#what-the-user-layer-does) carries
per-user preferences (tool access, PMC status, local clone paths) and
is **gitignored**. Two prek hooks keep `user.md` off the remote. See
[`config/README.md`](config/README.md) for the full tutorial.
- **Projects** live under [`projects/`](projects/), one subtree per
supported ASF project. The active subtree holds every
project-specific fact the skills depend on — the security model, the
scope labels, the milestone conventions, the release trains, the
canned reporter replies, the title-normalisation rules.
[`projects/_template/`](projects/_template/) is the bootstrap
scaffold for adding a new project.
- **Tools** live under [`tools/`](tools/), one subtree per external
system the skills talk to. Each subtree is project-agnostic; it
documents the adapter surface (search queries, threading rules, API
semantics, state machines) in terms of placeholders that the active
project fills in. The `vulnogram/generate-cve-json/` subtree is the
only Python package — a `uv`-managed CLI that emits paste-ready CVE
5.x JSON from a tracker body.
### Directory tree
```
.
├── README.md # Canonical 16-step handling process + conventions
├── 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
├── .claude/
│ └── skills/ # Agent workflows (invoked via the Skill tool)
│ ├── import-security-issue/SKILL.md
│ ├── import-security-issue-from-pr/SKILL.md
│ ├── sync-security-issue/SKILL.md
│ ├── allocate-cve/SKILL.md
│ ├── fix-security-issue/SKILL.md
│ ├── deduplicate-security-issue/SKILL.md
│ └── invalidate-security-issue/SKILL.md
├── config/ # Runtime configuration layer
│ ├── README.md # Configuration tutorial + placeholder rule
│ ├── active-project.md # Declares active_project (checked in)
│ ├── user.md # Per-user — gitignored, auto-bootstrapped by prek
│ ├── user.md.template # Bootstrap template with TODOs
│ └── user.md.example # Filled-in example
├── projects/ # Templates for adopting projects' configs
│ └── _template/ # Scaffold for bootstrapping a new project's
│ # `<project-config>/` (the per-project layer
│ # an adopter ships in their tracker repo).
│ # Files: project.md (manifest), security-model.md,
│ # canned-responses.md, release-trains.md,
│ # milestones.md, scope-labels.md, naming-
│ # conventions.md, title-normalization.md,
│ # fix-workflow.md, README.md — all stubbed
│ # with TODO placeholders.
├── tools/ # Project-agnostic adapters per external system
│ ├── gmail/
│ │ ├── tool.md # Adapter overview
│ │ ├── operations.md # MCP call signatures + no-update-no-delete rule
│ │ ├── threading.md # threadId and subject-matched fallback
│ │ ├── search-queries.md # Canonical reusable query templates
│ │ ├── ponymail-archive.md
│ │ └── asf-relay.md
│ ├── github/
│ │ ├── tool.md
│ │ ├── operations.md
│ │ ├── labels.md
│ │ ├── issue-template.md
│ │ └── project-board.md # GraphQL introspection + column-move recipe
│ ├── vulnogram/
│ │ ├── tool.md
│ │ ├── record.md # DRAFT / REVIEW / PUBLIC state machine
│ │ ├── allocation.md
│ │ └── generate-cve-json/ # Python package (uv-managed CLI)
│ │ ├── pyproject.toml
│ │ ├── src/generate_cve_json/
│ │ ├── tests/
│ │ ├── SKILL.md
│ │ └── README.md
│ └── cve-org/
│ └── tool.md # MITRE CVE Services v2 publication check
├── .pre-commit-config.yaml # prek hooks: doctoc, EOF, forbid/bootstrap
│ # user.md, ruff/mypy/pytest for generate-cve-json
└── .github/ # CI: pre-commit.yml, zizmor.yml, ISSUE_TEMPLATE
```
## Getting set up
You need three tools on your machine:
- **`uv`** — the Python runner used for `generate-cve-json`. Install via
`curl -LsSf https://astral.sh/uv/install.sh | sh` or your package
manager.
- **`prek`** — the `pre-commit`-compatible hook runner. Install via
`uv tool install prek` or `pipx install prek`.
- **`gh` CLI** — needed to drive tracker reads (and, later, writes) if
you plan to run any of the skills end-to-end. `brew install gh` or
platform equivalent.
First-time clone:
```bash
git clone git@github.com:<tracker>.git
cd <tracker-repo-name>
prek install # wire the hooks into .git/hooks
prek run --all-files # runs every hook on every file; does a
# one-time bootstrap of config/user.md
# from the template
```
The `bootstrap-user-config` hook will create
[`config/user.md`](config/README.md#what-the-user-layer-does) on the
first run. Open it, grep for `TODO`, and fill in the lines that apply
to your setup. The file is gitignored; a second hook
(`forbid-user-config`) refuses any commit that stages it, so you
cannot accidentally publish your local configuration.
Read [`config/README.md`](config/README.md) for the end-to-end
configuration tutorial, including the placeholder convention and how
the skills consume both layers.
## Making changes
Think about **which layer the change belongs in** before you start
editing:
| You want to change … | Edit under … |
|---|---|
| A step of the disclosure process that applies to every project | [`README.md`](README.md) |
| An editorial / confidentiality / style rule | [`AGENTS.md`](AGENTS.md) |
| Anything project-specific (canned reply, milestone convention, scope label, release-train state) | the adopter's own `<project-config>/` (lives in their tracker repo, not here) |
| An adapter surface for an external system (a new Gmail search template, a new GraphQL recipe, a new `gh` invocation, a new CVE-tool endpoint) | the matching [`tools/<system>/`](tools/) subtree |
| A skill's workflow | [`.claude/skills/<name>/SKILL.md`](.claude/skills/) |
| Bootstrap scaffolding for a new project | [`projects/_template/`](projects/_template/) |
Rules of thumb for each layer:
- **Root docs and skills are project-agnostic.** Never paste concrete
names like `<upstream>` or `<tracker>` into them. Use
the placeholders `<PROJECT>`, `<tracker>`, `<upstream>` in backticked
labels. URL targets in markdown links can point at concrete paths so
the links stay clickable during review — the placeholder lives in
the visible label only. The convention is documented in
[`AGENTS.md`](AGENTS.md) and enforced by reviewer taste.
- **Tool adapters are project-agnostic.** If a recipe varies per
project (different Gmail domains, different GitHub org, different
board node IDs), the adapter declares variables and the active
project's [`project.md`](<project-config>/project.md) fills them.
- **An adopter's `<project-config>/` carries concrete names freely** —
it exists for exactly that. The adopter's own per-project files can
reference their `<upstream>` repo directly, paste concrete package
versions, name release managers, etc. — none of that lives in this
framework repo.
- **Skills never mutate state without user confirmation.** If you add
a new action, write the proposal/confirm/apply shape into the skill
and the guardrails into `AGENTS.md`. See the existing skills for
the pattern.
## Running the dev loop
Every change should pass `prek run --all-files` locally before you
open a PR — CI runs the same config. The hook set:
- `doctoc` regenerates TOCs on every `.md` file (except skill `SKILL.md`
files, which keep YAML frontmatter at the top);
- `end-of-file-fixer`, `trailing-whitespace`, `mixed-line-ending`,
`check-merge-conflict`, `detect-private-key` — standard hygiene;
- `forbid-user-config` — refuses any commit that stages
`config/user.md`;
- `bootstrap-user-config` — creates `config/user.md` from the template
on first run;
- `ruff check` / `ruff format --check` / `mypy` / `pytest` against the
`tools/vulnogram/generate-cve-json/` Python package.
For the Python package directly:
```bash
cd tools/vulnogram/generate-cve-json
uv run pytest # unit tests
uv run ruff check # lint
uv run ruff format # auto-format (check-only in CI)
uv run mypy # type-check
```
The package is invoked by the [`sync-security-issue`](.claude/skills/sync-security-issue/SKILL.md)
and [`allocate-cve`](.claude/skills/allocate-cve/SKILL.md) skills via
`uv run --project tools/vulnogram/generate-cve-json generate-cve-json
<N> --attach` from the repo root — that is the canonical invocation
any new behaviour has to stay compatible with.
## Opening a pull request
- **Base branch:** `main`. Do not open PRs against any other branch
unless explicitly coordinated.
- **Scope:** keep one concern per PR. A skill-behaviour change, a
tool-adapter addition, and a doc update should land as separate PRs.
- **Commit message shape:** imperative-present subject, ≤72 chars,
plain prose body explaining *why*. Look at
[recent merged commits](https://github.com/apache/airflow-steward/commits/main)
for the cadence.
- **PR description:** one `## Summary` section with 1–3 bullets of
*what changed and why*, and one `## Test plan` section listing how
you verified the change.
- **CI:** `prek run --all-files` must pass. `zizmor` (GitHub Actions
linting) must pass. Both run automatically on every PR.
- **Reviews:** at least one approval from a repo collaborator. Any
change that edits [`AGENTS.md`](AGENTS.md) or the skill files should
get an extra set of eyes because those ripple into every future
sync.
## Confidentiality
This repository is private — only security-team members can read its
tracker contents. The repo's *contents* (issue bodies, comments,
labels, rollup entries, severity assessments) must never appear on
a public surface. The repo's *identifiers* (URLs, `#NNN`) are
public-safe and may be cross-referenced from public PRs, reporter
emails, and advisory text — see the
[Confidentiality of the tracker repository](AGENTS.md#confidentiality-of-the-tracker-repository)
section of `AGENTS.md` for the three-layer rule. Practical rules:
- A `<tracker>` URL or `#NNN` reference may appear in a public PR
description as a cross-reference identifier, **so long as the
surrounding text does not characterise the change as a security
fix** (no `CVE-`, no *"vulnerability"* / *"security fix"* /
*"advisory"* phrasing) before the advisory ships.
- Never paste tracker comment text, label transitions, or body
excerpts into a public surface — those are *contents*, not
identifiers.
- Never put reporter-identifying information into a `<upstream>` PR.
- When sharing a tracker URL with an external reporter, pair it with
a one-line note that the link is an identifier-only reference (the
page will 404 for them, and that is expected).
- Reporter-supplied CVSS scores are informational only. The security
team scores independently during CVE allocation. Full rationale in
[`AGENTS.md`](AGENTS.md).
- `config/user.md` stays gitignored. If you need to share a snippet
with someone, paste it in chat — do not commit it.
Anything you are unsure about, stop and ask on `security@apache.org`
before pushing.
## Authoritative references
When this file and a layer-specific doc disagree, the layer-specific
doc wins. Re-read it first:
- [`README.md`](README.md) — the 16-step disclosure process.
- [`AGENTS.md`](AGENTS.md) — editorial and confidentiality rules.
- [`config/README.md`](config/README.md) — configuration layer tutorial.
- [`projects/README.md`](projects/README.md) — current-projects index
and the new-project bootstrap path.
- [`projects/_template/`](projects/_template/) — scaffold to clone when
adding a new project.
- [`.claude/skills/<name>/SKILL.md`](.claude/skills/) — the workflow
spec each skill enforces.