Disclosure clock #9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Disclosure clock | |
| on: | |
| schedule: | |
| - cron: '0 13 * * *' | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| # A manual dispatch overlapping the cron run would double-file warning issues | |
| # and race on branch creation. Queue instead of cancel: a half-run clock leaves | |
| # a branch pushed with no PR opened. | |
| concurrency: | |
| group: disclosure-clock | |
| cancel-in-progress: false | |
| jobs: | |
| clock: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13' | |
| cache: pip | |
| - run: pip install -r requirements.txt | |
| - name: Compute the work queue | |
| id: queue | |
| run: | | |
| python -m tools.clock_report > /tmp/clock.json | |
| cat /tmp/clock.json | |
| - name: Warn seven days out | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| for row in $(jq -r '.warn[] | @base64' /tmp/clock.json); do | |
| get() { echo "$row" | base64 -d | jq -r ".$1"; } | |
| id="$(get endor_id)" | |
| # All states: an open-only list refiles this daily once closed. | |
| # Titles are compared field by field rather than passed to --search, | |
| # whose full-text index tokenizes on hyphens: a search for | |
| # EL-2026-001 also matches a title holding EL-2026-0012 and would | |
| # silently suppress a warning that is actually due. | |
| existing="$(gh issue list --state all --label disclosure-clock \ | |
| --limit 500 --json title \ | |
| | jq --arg id "$id" '[.[] | select((.title | split(" ")[0]) == $id)] | length')" | |
| if [ "$existing" != "0" ]; then | |
| echo "$id already warned" | |
| continue | |
| fi | |
| gh issue create \ | |
| --title "$id deadline in $(get days_remaining) days" \ | |
| --label disclosure-clock \ | |
| --body "$(printf '@p80n-sec — disclosure deadline approaching.\n\n- Reference: %s\n- Project: %s\n- Reported: %s\n- Deadline expires: %s\n\nGather before the publish PR opens:\n\n- [ ] CVE or GHSA identifier\n- [ ] CVSS score\n- [ ] Title\n- [ ] Endor reference URL\n- [ ] Writeup link\n- [ ] Talk slug, if any\n\nEvery fact above is already public in the pending table on the site.' \ | |
| "$id" "$(get project)" "$(get reported)" "$(get deadline)")" | |
| done | |
| - name: Open publish-prep PRs at expiry | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| git config user.name 'github-actions[bot]' | |
| git config user.email '41898282+github-actions[bot]@users.noreply.github.com' | |
| for row in $(jq -r '.expire[] | @base64' /tmp/clock.json); do | |
| get() { echo "$row" | base64 -d | jq -r ".$1"; } | |
| id="$(get endor_id)" | |
| key="$(get key)" | |
| branch="disclosure/${id}-publish" | |
| if git ls-remote --exit-code --heads origin "$branch" >/dev/null 2>&1; then | |
| echo "$branch already exists" | |
| continue | |
| fi | |
| # Every step below skips only this record on failure. Under bash -e | |
| # a bare failure kills the step, and the loop never reaches the | |
| # records after it — so one bad row starves every later one, every | |
| # day. -f discards any leftovers from a skipped record. | |
| git checkout -f -B "$branch" origin/main || { echo "cannot branch for $id"; continue; } | |
| python -m tools.scaffold_publish "$key" || { echo "cannot scaffold $key"; continue; } | |
| git add -A || { echo "cannot stage $key"; continue; } | |
| # scaffold_publish is idempotent, so a record that already carries | |
| # the TODO block stages nothing and git commit would exit 1. | |
| if git diff --cached --quiet; then | |
| echo "$key already scaffolded, skipping" | |
| continue | |
| fi | |
| git commit -m "Scaffold publish TODOs for $id after disclosure deadline [skip ci]" \ | |
| || { echo "cannot commit $key"; continue; } | |
| git push -u origin "$branch" || { echo "cannot push $branch"; continue; } | |
| gh pr create --base main --head "$branch" \ | |
| --title "Prepare $id for publication — disclosure deadline expired" \ | |
| --body "$(printf '@p80n-sec — the %s day window on %s has expired.\n\nThis PR adds a TODO block to the record. It does not publish anything by itself: status stays in-progress and the published table does not change until a human fills in the TODOs, flips status to published, and merges.\n\n- [ ] Replace the TODO identifier with the real CVE or GHSA\n- [ ] Set the real CVSS score\n- [ ] Set the title\n- [ ] Set disclosed to the actual disclosure date\n- [ ] Add refs.nvd and refs.endor\n- [ ] Add the writeup link\n- [ ] Flip status to published\n- [ ] Rename findings/%s to the CVE-keyed folder name\n' \ | |
| "90+30" "$id" "$key")" \ | |
| || { echo "cannot open a PR for $branch"; continue; } | |
| done |