diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f97efcc..21a1ff8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -78,7 +78,8 @@ jobs: - uses: actions/checkout@v7 with: persist-credentials: false - - run: docker build --no-cache-filter vulnerabilities --target vulnerability-report --output "$RUNNER_TEMP/vulnerabilities" . + - name: Fetch govulncheck's report from vuln.go.dev + run: docker build --no-cache-filter vulnerabilities --target vulnerability-report --output "$RUNNER_TEMP/vulnerabilities" . - name: File each one in todo.md id: file run: python3 release-tooling/file_vulnerabilities.py "$RUNNER_TEMP/vulnerabilities/report.json" todo.md diff --git a/Dockerfile b/Dockerfile index c638dd6..25c6caf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,7 @@ COPY --from=vulnerabilities /report.json / FROM python:3.14.8-slim AS release-tooling COPY release-tooling/ /release-tooling/ +# The filer's test files an item into the plan itself. COPY todo.md /todo.md RUN python3 -m unittest discover -s /release-tooling -p '*_test.py' && touch /tested diff --git a/README.md b/README.md index d80f8e1..9e4340d 100644 --- a/README.md +++ b/README.md @@ -1225,9 +1225,10 @@ A known vulnerability in the Go version the gate builds with, or in a module fej requires, holds the release and no merge. CI checks on every pull request, every push to `main`, and daily. On `main` and in the daily run, CI files each vulnerability as a `security` item atop the next release in `todo.md`, through a pull request from the branch -`vulnerabilities` unless one is open; each item names the version to raise to. A failed run -that names no vulnerability means vuln.go.dev did not answer: rerun it. When GitHub pauses -the daily run after 60 days with no activity, re-enable it under Actions. The same check: +`vulnerabilities` unless one is open; each item names the version to raise to. When the +step "Fetch govulncheck's report from vuln.go.dev" fails, vuln.go.dev did not answer: +rerun it. When GitHub pauses the daily run after 60 days with no activity, re-enable it +under Actions. The same check: ```sh docker compose run --rm vulncheck diff --git a/release-tooling/file_vulnerabilities.py b/release-tooling/file_vulnerabilities.py index 6db3942..ede4260 100644 --- a/release-tooling/file_vulnerabilities.py +++ b/release-tooling/file_vulnerabilities.py @@ -3,6 +3,16 @@ Usage: file_vulnerabilities.py Exits 1 while any vulnerability is known, filed now or before, and 0 on a clean report. + +The item is written the way ~/.claude/bin/rank_todo.py writes one, which CI cannot run: +todo.md's `Next ID = N` gives its ID, the release of the plan's top row (the next release) +gives its release, and its Score is todo.md's `Score = …` formula over the scores below and +the weight todo.md's goal table gives goal 1.1. The test that files into the repository's +own todo.md fails the gate when the plan's shape drifts from what this script reads. + +A security item scores R, S and C 1, since the fix is a version bump, and A and G 8, since a +known vulnerability in the binaries works against README goal 1.1: installing fejkdata the +way one installs other tools, which image scanners then flag. """ import ast @@ -23,6 +33,7 @@ class Module: path: str found_in: str fixed: list[str] = field(default_factory=list) + unfixed: bool = False osvs: dict[str, dict] = field(default_factory=dict) @property @@ -59,7 +70,10 @@ def vulnerable_modules(report: str) -> list[Module]: frame = finding["trace"][0] module = modules.setdefault(frame["module"], Module(frame["module"], frame["version"])) module.osvs[finding["osv"]] = osvs[finding["osv"]] - module.fixed.append(finding.get("fixed_version", "")) + if "fixed_version" in finding: + module.fixed.append(finding["fixed_version"]) + else: + module.unfixed = True return sorted(modules.values(), key=lambda m: (m.path != "stdlib", m.path)) @@ -68,6 +82,7 @@ def version_key(version: str): def evaluate(node, values): + """The value of todo.md's score formula: numbers, letters, unary minus and + - * /.""" match node: case ast.Constant(value=value): return value @@ -93,7 +108,7 @@ def score(todo: str) -> str: def tagline(module: Module) -> str: - if "" in module.fixed: + if module.unfixed: return f"Ship no release while fejkdata carries {module.carried}, some of whose known vulnerabilities no version fixes yet." fixed = module.version(max(module.fixed, key=version_key)) if module.path == "stdlib":