Skip to content

Commit 9a28db8

Browse files
ci: green the six red workflows + close the #47 residual (#185)
## What `main` had **6 of 17 workflows red**. This makes five green, explains why the sixth is deliberately untouched, closes the one genuinely-unresolved finding from #47, and closes #81's crate-doc item. Every claim below was verified by running the tool locally — nothing is asserted from reading config. Where a local run and CI disagreed, CI won and the commit history records why. ## The six red workflows — honest scoreboard | Workflow | Root cause | Status | |---|---|---| | rust-ci (clippy / audit / deny) | `Iterator::last` on BTreeMap; 6 advisories; 5 dead ignores | **fixed, green in CI** | | elixir-ci | `mix hex.audit`: hpax + 4x mint CVEs | **fixed, green in CI** | | Governance | line-1-only SPDX check; 24d-stale pins | **fixed, green in CI** | | Secret Scanner | caller under-grants `pull-requests: write` + `actions: read` | **fixed, verified: 0 jobs -> 3 jobs, all pass** | | ghcr-publish | podman/Docker credential-store mismatch | **fix applied, UNVERIFIABLE pre-merge** | | security-scan.yml | zero jobs, no log | **still red, uncharacterised** | `ghcr-publish` and `security-scan.yml` trigger on `push: [main]` only, so **this PR's checks can never show them** — they need confirming on main after merge. An earlier revision of this description claimed "five green". That was wrong: it accounted for four workflows and quietly dropped these two. The table above is the real count. ## Two bugs found that were not style nits **An O(N²) write path.** `verisim-temporal::append()` derived the next version number with `versions.keys().last()` on a `BTreeMap` — an O(n) walk on *every write*, making an N-version history O(N²) to build. Clippy flagged one of five occurrences; all five are fixed. **A gate that could not fail.** My first attempt emptied `deny.toml`'s ignore list because a local `cargo deny` said "advisories ok". That result was meaningless: the configured `db-path` had just been created *empty*, so the check compared against zero advisories. CI, which clones a fresh database, correctly failed. The ignore is restored with the real justification, and both traps are documented inline for the next editor: - an unpopulated advisory database yields a meaningless green; - `cargo deny check advisories` under **default features** does not surface these advisories while `--all-features` and the CI container do — so a default-feature local pass is not evidence. **A `startup_failure` with no log.** Refreshing the standards pins regressed Hypatia Security Scan: between the two pins the reusable changed its declared permission from `security-events: read` to `write`, and GitHub refuses a reusable call whose caller grants less than the reusable declares. The failure mode produces no log at all. Caller updated; the other two refreshed pins were checked for the same trap and are fine. ## quick-xml cannot be fixed here Reached only via `oxigraph`, and **oxigraph 0.5.9 is the latest published release** and still requires `^0.37`. No resolution of this tree avoids it. Exposure is limited — `oxigraph-backend` is not a default feature — and the ignore names its removal condition (oxigraph publishing against quick-xml ≥ 0.41). ## Why Secret Scanner is left red Re-pinning `secret-scanner-reusable` off `d135b05` is a real security uplift (that pin carries `continue-on-error: true` gitleaks), but making the gate real surfaces 23/23 false positives until the `.gitleaks.toml` baseline lands in `hyperpolymath/standards`. Re-pinning ahead of that would simply be reverted. I checked it is *not* the permission-escalation bug above — caller and reusable both declare `contents: read`. ## #81: the crate docs were never missing `verisim-storage` and `verisim-wal` both carried thorough crate documentation — module tables, the WAL's full on-disk entry layout, worked examples — written with `//` instead of `//!`, so rustdoc published none of it and never compiled the examples. Converting the blocks takes crate-level doc coverage to **16/16** and brings both examples under test for the first time (`cargo test --doc` → 5 passed). They passed first time; they had simply never been checked. ## Verification — all exit 0 ``` cargo clippy --workspace --all-targets --no-deps -- -D warnings cargo test --workspace 629 passed, 0 failed, 2 ignored cargo test --doc -p verisim-storage -p verisim-wal 5 passed cargo audit cargo deny check advisories bans licenses sources (default + --all-features) cargo fmt --all --check mix hex.audit / mix format --check-formatted / mix compile --warnings-as-errors reuse lint 773/773 files, MPL-2.0 + CC-BY-SA-4.0 coqc 8.20.1: all 9 formal/ modules; just check-assumptions panic-attack 2.5.5 assail . ``` All nine open issues have been re-measured and carry a disposition comment; four were substantially stale. Refs #47, #78, #79, #80, #81, #84, #113 🤖 Generated with [Claude Code](https://claude.com/claude-code) ## Known flake, so a future red is not misread as this PR `test parse_dependent/2 accepts a query with PROOF clause` (`elixir-orchestration/test/verisim/query/vcl_bridge_test.exs:93`) failed once on this branch with `** (EXIT) no process` — the `VCLBridge` GenServer was not alive at that instant, a supervisor-restart race. It passed on the re-run and passes locally (19 tests, 0 failures). Unrelated to the dependency bumps here, but worth a `start_supervised!`/`wait_for` fix in its own right. ## Also needs confirming on main `scorecard.yml`'s refreshed pin does not run pre-merge. Its reusable diff across the pin range was `+1/-0` and its declared permissions are unchanged (`contents: read`), so the risk is low — but it is unverified here. ## Verified outcome **38 pass / 3 skip / 0 failures.** Every check on the branch is green, including `pr-fuzzing (address)` (13m6s) and both fuzz-target compile jobs. The Secret Scanner fix is confirmed real rather than vacuous: the run went from **zero jobs** to **three** — `gitleaks`, `rust-secrets`, `shell-secrets` — all passing. `rust-secrets` and `shell-secrets` are genuine gates and had been running on nothing at all. Note `gitleaks` itself still carries `continue-on-error: true` upstream at this pin, so its green is not yet a real gate; that is unchanged by this PR and remains gated on the `.gitleaks.toml` baseline in `hyperpolymath/standards`. Worth checking estate-wide: any repo calling `secret-scanner-reusable` while granting only `contents: read` is silently running **no** secret scanning. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c2ce48b commit 9a28db8

17 files changed

Lines changed: 281 additions & 156 deletions

File tree

.cargo/audit.toml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# cargo-audit configuration for VeriSimDB.
3+
# Run: cargo audit (CI: rustsec/audit-check in .github/workflows/rust-ci.yml)
4+
#
5+
# Keep this list in step with `deny.toml` `[advisories].ignore`. They are
6+
# separate tools with separate config and they resolve the dependency graph
7+
# differently — cargo-audit reads Cargo.lock directly, cargo-deny resolves
8+
# features — so the same advisory can surface in one and not the other. An
9+
# entry justified for one is justified for both; edit the two files together.
10+
#
11+
# Policy: an entry is only permitted when there is no version of this tree
12+
# that resolves the advisory. "We haven't got round to it" is not a reason —
13+
# bump the dependency instead. Re-check every entry when its blocking upstream
14+
# publishes a release.
15+
16+
[advisories]
17+
ignore = [
18+
# quick-xml 0.37.5 — two DoS advisories, both fixed upstream in 0.41.0.
19+
# Reached ONLY through oxigraph → {oxrdfxml, sparesults}. oxigraph 0.5.9 is
20+
# the latest published release and still requires quick-xml ^0.37, so no
21+
# resolution of this lockfile avoids it.
22+
# Exposure: verisim-graph's `oxigraph-backend` feature is NOT in `default`,
23+
# so a stock build never links quick-xml.
24+
# REMOVE WHEN: oxigraph publishes a release depending on quick-xml >= 0.41.
25+
"RUSTSEC-2026-0194", # quadratic duplicate-attribute check
26+
"RUSTSEC-2026-0195", # unbounded namespace-declaration allocation
27+
]
28+
29+
# Informational advisories (unmaintained / unsound / notice) are reported but
30+
# do not fail the build; vulnerabilities do.
31+
informational_warnings = ["unmaintained", "unsound", "notice"]

.github/workflows/ghcr-publish.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,19 @@ jobs:
6464
if: github.event_name != 'pull_request'
6565
run: |
6666
SHORT_SHA="${GITHUB_SHA::12}"
67+
# Point podman's credential store at the DOCKER config path instead
68+
# of its default ${XDG_RUNTIME_DIR}/containers/auth.json.
69+
# REGISTRY_AUTH_FILE is honoured by both `podman login` and
70+
# `podman push`, so one export covers the whole step.
71+
#
72+
# Why: the `attest-build-provenance` step below runs with
73+
# `push-to-registry: true` and resolves registry credentials via the
74+
# Docker config. With podman's default store it finds none and fails
75+
# with "No credentials found for registry ghcr.io" — AFTER the image
76+
# has already been pushed, which is why the image exists but the run
77+
# is red.
78+
mkdir -p "$HOME/.docker"
79+
export REGISTRY_AUTH_FILE="$HOME/.docker/config.json"
6780
echo "${{ secrets.GITHUB_TOKEN }}" \
6881
| podman login ghcr.io -u "${{ github.actor }}" --password-stdin
6982
# --digestfile records the real sha256 of the pushed manifest so the

.github/workflows/governance.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
21
# SPDX-License-Identifier: MPL-2.0
2+
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
33
name: Governance
44

55
on:
@@ -14,4 +14,4 @@ permissions:
1414

1515
jobs:
1616
governance:
17-
uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@d7c22711e830e1f383846472f6e9b99debdb201e
17+
uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@45108f95e404e8e17896a56596d0db1cc7883c46

.github/workflows/hypatia-scan.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
21
# SPDX-License-Identifier: MPL-2.0
2+
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
33
name: Hypatia Security Scan
44

55
on:
@@ -13,8 +13,12 @@ on:
1313

1414
permissions:
1515
contents: read
16-
security-events: read
16+
# Must be `write`, not `read`: hypatia-scan-reusable.yml declares
17+
# `security-events: write` as of standards 45108f95e404, and GitHub refuses
18+
# to start a reusable-workflow call whose caller grants less than the
19+
# reusable declares (the failure mode is `startup_failure`, with no log).
20+
security-events: write
1721

1822
jobs:
1923
scan:
20-
uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@d7c22711e830e1f383846472f6e9b99debdb201e
24+
uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@45108f95e404e8e17896a56596d0db1cc7883c46

.github/workflows/pages.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
13
name: GitHub Pages (Ddraig SSG)
24
on:
35
push:
@@ -18,9 +20,9 @@ jobs:
1820
image: ghcr.io/stefan-hoeck/idris2-pack@sha256:f0758996a931fb35d9ecb1de273c4d59dabe2a09b433afc7e357f65a08b7e1ff
1921
steps:
2022
- name: Checkout Site
21-
uses: actions/checkout@v7
23+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
2224
- name: Checkout Ddraig SSG
23-
uses: actions/checkout@v7
25+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
2426
with:
2527
repository: hyperpolymath/ddraig-ssg
2628
path: .ddraig-ssg
@@ -37,7 +39,7 @@ jobs:
3739
fi
3840
./.ddraig-ssg/build/exec/ddraig build src _site https://hyperpolymath.github.io/${GITHUB_REPOSITORY#*/}
3941
- name: Upload artifact
40-
uses: actions/upload-pages-artifact@v5
42+
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
4143
with:
4244
path: '_site'
4345
deploy:
@@ -50,4 +52,4 @@ jobs:
5052
steps:
5153
- name: Deploy to GitHub Pages
5254
id: deployment
53-
uses: actions/deploy-pages@v5
55+
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0

.github/workflows/scorecard.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
21
# SPDX-License-Identifier: MPL-2.0
2+
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
33
name: OSSF Scorecard
44

55
on:
@@ -14,7 +14,7 @@ permissions:
1414

1515
jobs:
1616
scorecard:
17-
uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@d7c22711e830e1f383846472f6e9b99debdb201e
17+
uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@45108f95e404e8e17896a56596d0db1cc7883c46
1818
permissions:
1919
contents: read
2020
security-events: write

.github/workflows/secret-scanner.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ concurrency:
1212

1313
permissions:
1414
contents: read
15+
# secret-scanner-reusable.yml's `gitleaks` job declares JOB-level
16+
# `pull-requests: write` + `actions: read` (its own comment: "Reusable-
17+
# workflow permission blocks OVERRIDE the caller's, so this MUST live here
18+
# at source"). A caller granting less than the reusable declares is refused
19+
# before any job starts — the observed failure was `startup_failure` with
20+
# zero jobs and no log, which is the signature of caller-under-grant, NOT a
21+
# scan result. gitleaks never ran at all.
22+
pull-requests: write
23+
actions: read
1524

1625
jobs:
1726
scan:

Cargo.lock

Lines changed: 22 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

audits/assail-classifications.a2ml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,15 @@
5959
(category "UnsafeDeserialization")
6060
(audit "VeriSimSearch.res:80-200 — six try-wrapped stringify→parseExn round-trips")
6161
(rationale "parse of self-stringified JSON inside try; cannot throw on external input"))
62+
63+
;; ── Allocation bounded by an explicit guard the scanner cannot see ──
64+
;; assail flags the growth pattern (String::new + push_str in a loop)
65+
;; syntactically; it does not prove bounds, so the guard added below does
66+
;; not clear the finding. Re-verified 2026-07-21: still reported after the
67+
;; fix, hence this entry rather than a code change alone.
68+
(classification
69+
(file "rust-core/verisim-repl/src/main.rs")
70+
(category "UnboundedAllocation")
71+
(audit "main.rs — REPL backslash-continuation buffer `query_buf`; guarded by MAX_QUERY_BUF_BYTES (1 MiB) since 2026-07-21")
72+
(rationale "growth is bounded: the continuation branch checks query_buf.len() + line + 1 against MAX_QUERY_BUF_BYTES and clears the buffer on overflow, so the allocation cannot exceed 1 MiB regardless of input length"))
6273
)

deny.toml

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,40 @@
55
[advisories]
66
db-path = "~/.cargo/advisory-db"
77
db-urls = ["https://github.com/rustsec/advisory-db"]
8-
# Transitive deps in the `burn` ML stack we cannot fix downstream.
9-
# Each entry is reviewed when bumping burn; remove once upstream migrates.
8+
# Reviewed exceptions. Keep this list minimal, dated, and justified: every
9+
# entry must name the blocking upstream and the condition for its removal.
10+
#
11+
# Removed 2026-07-21: the five burn-era entries (RUSTSEC-2024-0436 paste,
12+
# RUSTSEC-2025-0134 rustls-pemfile, RUSTSEC-2025-0141 bincode,
13+
# RUSTSEC-2026-0002 lru, RUSTSEC-2026-0105 core2). The `burn` ML stack is no
14+
# longer a dependency at all (0 burn crates in Cargo.lock) and cargo-deny
15+
# reported every one as `advisory-not-detected`. They suppressed nothing.
16+
#
17+
# WARNING when editing this file: `db-path` below is created empty on first
18+
# run, and a cargo-deny invocation against an unpopulated advisory database
19+
# reports "advisories ok" having checked NOTHING. Confirm the database is
20+
# populated (it should contain crates/<name>/RUSTSEC-*.md) before trusting a
21+
# green local result. CI clones a fresh database every run and is the
22+
# authority; a local pass that CI contradicts means the local database, not CI,
23+
# is wrong.
24+
#
25+
# Note also that cargo-deny's advisory detection is sensitive to how the
26+
# dependency graph is resolved: `cargo deny check advisories` with default
27+
# features does NOT surface the quick-xml advisories below, while
28+
# `--all-features` and the CI container both do. The entries stay because the
29+
# stricter resolution is the one that matters.
1030
ignore = [
11-
"RUSTSEC-2024-0436", # paste (unmaintained) — burn-wgpu → cubecl-cpu → tracel-llvm
12-
"RUSTSEC-2025-0134", # rustls-pemfile (unmaintained) — axum-server
13-
"RUSTSEC-2025-0141", # bincode (unmaintained) — burn-core
14-
"RUSTSEC-2026-0002", # lru (unsound IterMut) — transitive
15-
"RUSTSEC-2026-0105", # core2 (unmaintained + yanked) — rav1e → ravif → image → burn-dataset
31+
# quick-xml 0.37.5 — two DoS advisories, both fixed upstream in 0.41.0.
32+
# Reached ONLY through oxigraph -> {oxrdfxml, sparesults}. oxigraph 0.5.9 is
33+
# the latest published release and still requires quick-xml ^0.37, so there
34+
# is no resolution of this tree that avoids it.
35+
# Exposure: verisim-graph's `oxigraph-backend` feature is NOT in `default`
36+
# (rust-core/verisim-graph/Cargo.toml, `default = []`), so a stock build
37+
# does not link quick-xml; only RDF/XML and SPARQL-XML parsing of untrusted
38+
# input on an opt-in build is affected.
39+
# REMOVE WHEN: oxigraph publishes a release requiring quick-xml >= 0.41.
40+
"RUSTSEC-2026-0194", # quadratic duplicate-attribute check
41+
"RUSTSEC-2026-0195", # unbounded namespace-declaration allocation
1642
]
1743

1844
[licenses]

0 commit comments

Comments
 (0)