Skip to content

release: 44 image findings on the Debian 13 base accepted as exceptions until 2027-02-01 (060 3.2.1, A-79, A-80) - #503

Merged
bartekus merged 3 commits into
mainfrom
impl/060-image-exceptions
Nov 5, 2026
Merged

bartekus merged 3 commits into
mainfrom
impl/060-image-exceptions

Conversation

@bartekus

@bartekus bartekus commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Spec 060 3.2.1: the owner's ruling on the image findings, as exceptions. Stacked on #491, because #491 adds the scan gate and the exceptions file this PR fills; #499's chain does not contain it. #499 needs this PR, #491, #500/#501 and #502.

Checks: cargo test -p rustev-release --locked --test image_scan passed; make gate passed; spec-spine couple OK (9 paths); check-authored-content.sh clean.

@bartekus bartekus added the statecraft:stacked-ci Run the full CI matrix on a stacked pull request label Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

AI review (findings)

Reviewing the diff for bugs, security problems, and inconsistencies.

Decision numbering gap. The founding-decisions table goes A-74 → A-75 → A-76, then this PR inserts A-79 and A-80 immediately after A-76 in the same hunk. No A-77 or A-78 rows appear anywhere in the diff. The resulting file has a gap A-76 → A-79 that is inconsistent with the otherwise unbroken sequential numbering.

Test assertion count vs. synthetic finding count. The new test builds found by cross-joining every exception with two platform strings ("linux/amd64" and "linux/arm64"), producing 88 Finding values (44 × 2). It then asserts that after expiry exactly 44 messages contain "expired on 2027-02-01". If check reports one diagnostic per uncovered finding, it would emit 88 such messages and the assertion count() == 44 would fail. The only way 44 is correct is if check reports one message per expired exception regardless of how many findings it covered. The original test's symmetric case (check(&ex, &rows, &[], …).len() == 44 for 44 unused exceptions with zero findings) is consistent with exception-side reporting, but the two-platform construction was added specifically to verify per-platform coverage, so the expected count looks wrong for that model too.

{"head": "fb95a6db5f4071ab33dcf08b4c958e6b3e2fd45a", "verdict": "findings", "findings": [{"path": "docs/decisions/00-founding-decisions.md", "line": 270, "summary": "Decision rows A-77 and A-78 are absent; the table jumps from A-76 to A-79, creating a numbering gap inconsistent with the otherwise unbroken sequential numbering convention."}, {"path": "tools/rustev-release/tests/image_scan.rs", "line": 88, "summary": "The test synthesises 88 findings (44 exceptions × 2 platforms) but asserts exactly 44 'expired' messages; if check reports one diagnostic per uncovered finding the assertion will fail."}]}

Subject: a219790...fb95a6d, diff digest dfa37f2edc0467941a31e5c772164246768d5e6213304d809e9e7562402abf92, 1 call(s). An AI comment is not an approval.

@bartekus
bartekus had a problem deploying to statecraft-review-exception November 3, 2026 23:28 — with GitHub Actions Error
@bartekus
bartekus force-pushed the impl/060-image-scan branch from a219790 to eb88a21 Compare November 4, 2026 00:31
@bartekus
bartekus force-pushed the impl/060-image-exceptions branch from fb95a6d to 4688b5d Compare November 4, 2026 00:31
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

AI review (findings)

Reviewing the diff for bugs, security problems, and inconsistencies.

Counting exceptions: gcc-14-base (5) + libc6 (22) + libgcc-s1 (5) + libgomp1 (5) + libstdc++6 (5) + zlib1g (2) = 44. Matches the assertion.

Decision row numbering gap: The base commit ends the table at A-76, and the diff's context lines show A-76 immediately followed by the closing line "No approval authorizes ratifying any other spec." The PR inserts A-79 and A-80 at that location, skipping A-77 and A-78 entirely. No other hunk in the diff adds those rows.

README base image vs. exception scope: The README still names gcr.io/distroless/cc-debian12:nonroot as the pinned base (unchanged context line). The CVE-2026-8674 warning added in this PR cites exception A-80, which covers findings grype reported on the cc-debian13 base. Spec 060's new text acknowledges the base hasn't moved yet ("on the cc-debian12 base, until the base move merges"), but an operator reading the README with the debian12 instruction will see A-80 cited for a scan that was done against a different base, making the exception reference misleading while the base remains at debian12.

Test logic: The found vector produces 88 entries (44 exceptions × 2 platforms) but the "expired" and "unused" counts both check for 44. That is consistent with check reporting one message per exception entry rather than per (exception × platform) pair, which matches the existing test baseline.

{"head": "4688b5da678e76303fd7cfa9bc0519ec34272360", "verdict": "findings", "findings": [{"path": "docs/decisions/00-founding-decisions.md", "line": 270, "summary": "Decision table jumps from A-76 to A-79, skipping A-77 and A-78 with no rows for them anywhere in the diff."}, {"path": "integrations/rustev-serve/README.md", "line": 140, "summary": "The added CVE-2026-8674 warning cites exception A-80, which covers the cc-debian13 base scan, while the README still names cc-debian12 as the pinned base, making the exception reference misleading before the base move merges."}]}

Subject: eb88a21...4688b5d, diff digest 659de10cd15a1d5d2a0a601071c3c9a5638c476dd148c7767f578f84d85b3b4e, 1 call(s). An AI comment is not an approval.

@bartekus
bartekus had a problem deploying to statecraft-review-exception November 4, 2026 00:46 — with GitHub Actions Error
@bartekus
bartekus force-pushed the impl/060-image-scan branch from eb88a21 to 42d9427 Compare November 4, 2026 02:38
@bartekus
bartekus force-pushed the impl/060-image-exceptions branch from 4688b5d to 4008f22 Compare November 4, 2026 02:39
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

AI review (findings)

Looking at the diff, I'll check for concrete defects in the exception list, decision log, and test logic.

Decision numbering gap

The founding decisions document shows:

| A-76 | ... |
+| A-79 | ... |
+| A-80 | ... |

A-77 and A-78 do not appear anywhere in the diff or in the context lines provided. The table jumps directly from A-76 to A-79, leaving two decision numbers unaccounted for in the log.

Libc6 "not reached" rationale is internally contradictory

In image-exceptions.json, 21 of the 22 libc6 entries carry the reason "not reached: rustev-serve links only libc, libm and libgcc_s; TLS is statically linked rustls". Because libc6 is one of those three linked libraries, the rationale "links only libc, libm and libgcc_s" does not explain why the libc6 CVEs are unreachable — it restates that the library is linked. The same boilerplate reason is correctly applied to libgomp1, libstdc++6, and zlib1g (which are present in the distroless image but not linked by the binary), but it is circular when applied to libc6 entries where the vulnerable package is a direct runtime dependency. CVE-2026-8674 is handled correctly with its own distinct reason; the other 21 libc6 entries should state why those specific code paths are not exercised rather than repeating the linkage claim.

No other concrete defects found. The 44-entry count is correct (5 gcc-14-base + 22 libc6 + 5 libgcc-s1 + 5 libgomp1 + 5 libstdc++6 + 2 zlib1g = 44). The test logic (88 findings from 44 exceptions × 2 platforms, checking expiry on 2027-02-02) is internally consistent. The cc-debian12/cc-debian13 discrepancy in the README is acknowledged and intentional per the spec text. A-80 is validated to exist by the test's rows.contains_key loop.

{"head": "4008f220ed697541b5a88aefefc66f618cf6beb8", "verdict": "findings", "findings": [{"path": "docs/decisions/00-founding-decisions.md", "line": null, "summary": "Decision numbering jumps from A-76 directly to A-79, leaving A-77 and A-78 absent from the log with no explanation."}, {"path": "tools/rustev-release/image-exceptions.json", "line": null, "summary": "Twenty-one libc6 entries use the reason 'links only libc, libm and libgcc_s' to claim 'not reached', which is circular because libc6 is one of those three linked libraries; the reason does not explain why the specific vulnerable code paths are unreachable."}]}

Subject: 42d9427...4008f22, diff digest d614a3ebfb56d2f4c0b195bdf35f7efac23a9cac0947b4baeea3d26b65e96df4, 1 call(s). An AI comment is not an approval.

@bartekus
bartekus temporarily deployed to statecraft-review-exception November 4, 2026 02:46 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

AI review (findings)

Reviewing the diff for bugs, security problems, and inconsistencies across the six changed files.

Counts and dates

A-80 claims "accept all 44". Counting entries in image-exceptions.json: gcc-14-base ×5, libc6 ×17, libgcc-s1 ×5, libgomp1 ×5, libstdc++6 ×5, zlib1g ×2 = 44. Correct. The 90-day expiry: 2026-11-03 + 90 days = 2027-02-01. Verified. All 44 entries carry "decision": "A-80" and "expires": "2027-02-01". The split of 43 "not reached" + 1 CVE-2026-8674 matches A-80's narrative.

Test logic

found is built as 44 × 2 = 88 Finding objects (one per exception × two platforms). The expiry assertion filters v for messages containing "expired on 2027-02-01" and expects 44. Whether this produces 44 or 88 depends on whether check emits one expiry message per exception entry or per finding. The comment says "every entry has expired" (entry = exception), and the companion assertion assert_eq!(check(&ex, &rows, &[], "2026-11-03").len(), 44) (unused exception = one error per exception) is consistent with one-per-entry semantics, so the count is likely correct. No implementation contradiction visible from the hunk.

README / base-image inconsistency (concrete defect)

integrations/rustev-serve/README.md still reads gcr.io/distroless/cc-debian12:nonroot (the line is not changed in this diff). The new paragraph added in this PR cites CVE-2026-8674 as "accepted as an image exception until 2027-02-01, A-80". A-80, however, explicitly covers findings that "grype 0.120.1 reported on… gcr.io/distroless/cc-debian13:nonroot" (spec 060 Increment 3b text, A-80 row). The README is directing operators of a cc-debian12 image to an exception rationale that was recorded only for cc-debian13. The spec acknowledges the base move "until the base move merges", meaning cc-debian13 is not yet the operative base; citing an exception for it in the cc-debian12 README creates a misleading cross-reference for operators reading the current documentation.

No other concrete defects are supported by the evidence in the diff.

{"head": "4008f220ed697541b5a88aefefc66f618cf6beb8", "verdict": "findings", "findings": [{"path": "integrations/rustev-serve/README.md", "line": 141, "summary": "The README still documents cc-debian12:nonroot as the pinned base but the new CVE-2026-8674 paragraph cites exception A-80, which was accepted specifically for cc-debian13:nonroot findings; the exception is inapplicable to the base the README currently describes."}]}

Subject: 42d9427...4008f22, diff digest d614a3ebfb56d2f4c0b195bdf35f7efac23a9cac0947b4baeea3d26b65e96df4, 1 call(s). An AI comment is not an approval.

@bartekus
bartekus requested a deployment to statecraft-review-exception November 4, 2026 04:37 — with GitHub Actions Waiting
@bartekus
bartekus force-pushed the impl/060-image-scan branch from 42d9427 to dc3e189 Compare November 4, 2026 05:44
@bartekus
bartekus force-pushed the impl/060-image-exceptions branch from 4008f22 to c6505f8 Compare November 4, 2026 05:44
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

AI review (findings)

Reviewing the diff for bugs, security problems, and inconsistencies.

image-exceptions.json — reason text for libc6 entries

The 21 libc6 CVE exceptions (every entry except CVE-2026-8674) carry the reason "not reached: rustev-serve links only libc, libm and libgcc_s; TLS is statically linked rustls". That reason is accurate for the other packages (gcc-14-base, libgcc-s1, libgomp1, libstdc++6, zlib1g), where the binary genuinely does not link them. For libc6 it is self-contradictory: libc6 IS libc, which IS one of the three linked libraries. Non-linkage cannot be the reason those code paths in libc6 are not reached. The correct justification would be that the specific vulnerable code paths within libc6 (regex, getaddrinfo search-list, etc.) are not invoked by the binary. As written, the reason undercuts rather than supports the exception, and a future auditor checking those entries against A-80 would find the stated rationale inapplicable to the library it purports to cover.

specs/060-the-service-image-in-the-release/spec.md — contradictory base-state claim

Within the new Increment 3b paragraph the text first states: "The owner moved the base to gcr.io/distroless/cc-debian13:nonroot (decision 2; spec 010's binding record)" (past tense, completed action), and then, four sentences later: "While the image is absent from the release set (and on the cc-debian12 base, until the base move merges)". The second clause explicitly says the image is still on cc-debian12 and the move has not merged, directly contradicting the first clause.

{"head": "c6505f8df7024750e6ad97d84c2e9a137d341a75", "verdict": "findings", "findings": [{"path": "tools/rustev-release/image-exceptions.json", "line": null, "summary": "The reason 'not reached: rustev-serve links only libc, libm and libgcc_s' is factually inapplicable to libc6 CVE exceptions because libc6 is libc, one of the three libraries the binary does link; the stated non-linkage rationale does not explain why those specific libc6 code paths are unreachable."}, {"path": "specs/060-the-service-image-in-the-release/spec.md", "line": null, "summary": "Increment 3b first says 'The owner moved the base to gcr.io/distroless/cc-debian13:nonroot' (past tense, done) and then says the image is 'on the cc-debian12 base, until the base move merges', directly contradicting the first statement within the same paragraph."}]}

Subject: dc3e189...c6505f8, diff digest 9a6f52e60da9f111debb171157c65f5f27b3062a939bab1900fd8db72c854e4c, 1 call(s). An AI comment is not an approval.

@bartekus
bartekus requested a deployment to statecraft-review-exception November 4, 2026 05:49 — with GitHub Actions Waiting
Base automatically changed from impl/060-image-scan to main November 4, 2026 07:13
@bartekus
bartekus force-pushed the impl/060-image-exceptions branch from c6505f8 to c856754 Compare November 4, 2026 07:15
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

AI review (findings)

Looking at the diff, I'll check each changed file for bugs, security problems, and inconsistencies.

docs/decisions/00-founding-decisions.md

The diff context shows the table jumping from A-76 directly to A-79, with A-77 and A-78 absent at the insertion point:

 | A-76 | The owner ratified spec `060` …
+| A-79 | The owner amended spec `060` 3.4.3 …
+| A-80 | The owner accepted on 2026-11-03 …

Decision rows are appended in sequential order; the base already has A-75 and A-76, and A-77/A-78 are nowhere in the hunk. That is a concrete numbering gap.

tools/rustev-release/image-exceptions.json

Counted 44 entries (5 gcc-14-base + 22 libc6 + 5 libgcc-s1 + 5 libgomp1 + 5 libstdc++6 + 2 zlib1g). All carry "decision": "A-80" and "expires": "2027-02-01". A-80 is dated 2026-11-03, exactly 90 days before the expiry, which satisfies the "at most 90 days" rule in spec 060 3.2. No count or date inconsistency here.

tools/rustev-release/tests/image_scan.rs

The found slice is built as 44 exceptions × 2 platforms = 88 entries. The expiry assertion then filters for messages containing "expired on 2027-02-01" and expects exactly 44. The existing empty-scan assertion (check(&ex, &rows, &[], …).len() == 44) establishes that check reports one message per exception (not per finding), so 44 is consistent; no bug apparent there.

The test spot-checks rows.get("A-76") but relies on rows.contains_key("A-80") to verify the new row was parsed. A-80 is added in this same PR, so that will pass.

specs/042-decision-service-and-remote-seams/spec.md and specs/060-the-service-image-in-the-release/spec.md

Prose is internally consistent with the exception file and the decision rows; the CVE-2026-8674 description ("~200-character search domain aborts glibc resolver") and the operator-control mitigation are stated the same way in both specs and the README.

Summary: one concrete defect — the gap in sequential decision-row numbering.

{"head": "c856754abebfd5b6f8cb85d00871f53e41d7e804", "verdict": "findings", "findings": [{"path": "docs/decisions/00-founding-decisions.md", "line": null, "summary": "Decision rows A-77 and A-78 are absent; the table jumps from A-76 directly to A-79, leaving a two-entry gap in the sequential numbering."}]}

Subject: b4b19e8...c856754, diff digest 9a6f52e60da9f111debb171157c65f5f27b3062a939bab1900fd8db72c854e4c, 1 call(s). An AI comment is not an approval.

@bartekus
bartekus requested a deployment to statecraft-review-exception November 4, 2026 07:18 — with GitHub Actions Waiting
@bartekus

bartekus commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Review note on c856754: the A-77/A-78 gap is a sequencing artifact, not a numbering error. A-77 is #481 (spec 061 ratification) and A-78 is spec 063's ratification (#483). Both land before this PR, which is held until then and rebased, so main never has the gap.

@bartekus
bartekus force-pushed the impl/060-image-exceptions branch from c856754 to 1d46233 Compare November 4, 2026 09:24
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

AI review (no-findings)

Reviewing the diff across all six changed paths for consistency, correctness, and security defects.

Counts and arithmetic

image-exceptions.json entries by package: gcc-14-base 5, libc6 22, libgcc-s1 5, libgomp1 5, libstdc++6 5, zlib1g 2 — total 44. This matches every narrative claim (A-80, spec 060 Increment 3b, decision comment in spec 042). Of those 44, one (CVE-2026-8674/libc6) has the "reachable" reason; the remaining 43 carry the "not reached" reason. The 43/1 split is consistent across all files.

Expiry arithmetic: A-80 row date 2026-11-03 + 90 days = 2027-02-01. ✓

Test logic (tools/rustev-release/tests/image_scan.rs)

found is built by cross-joining each of the 44 exception entries with two platforms → 88 Finding values. The three coverage assertions:

  • check(..., &found, "2026-11-03").is_empty() and check(..., &found, "2027-02-01").is_empty(): requires each exception to cover both-platform findings, consistent with exceptions being keyed on (id, package) rather than platform.
  • Expiry assertion counts messages containing "expired on 2027-02-01" and asserts == 44. Because exceptions are keyed on (id, package), the check function plausibly reports one expiry message per exception entry matched, not per finding, making 44 the correct expectation. The design intent stated in spec 060 ("every entry to expire the day after") corroborates per-entry reporting.
  • Empty-scan assertion len() == 44: consistent with the function returning one "unused" message per unmatched exception.

Security note for CVE-2026-8674

The README and spec 042 operator note correctly identify this as the one reachable finding and instruct operators to control /etc/resolv.conf and LOCALDOMAIN. The exception file mirrors this.

Reason text for libgcc-s1

The boilerplate reason "not reached: rustev-serve links only libc, libm and libgcc_s" is applied to libgcc-s1, which is libgcc_s — i.e., the library is in fact linked. The reason template is accurate for genuinely unlinked packages (libstdc++6, libgomp1, zlib1g) but imprecise for libgcc-s1, where the correct claim is that the linked library's vulnerable code paths are not exercised by the Rust binary. This is a documentation imprecision, not a security defect: the GCC CVEs in question typically affect toolchain-specific constructs absent in a Rust binary, the owner's acceptance is properly recorded, and no test validates reason strings.

No governance violation, security defect, or cross-file inconsistency that the supplied evidence supports.

{"head": "1d46233baff62a32a2af50b7908d9162284e0f52", "verdict": "no-findings", "findings": []}

Subject: 294c40d...1d46233, diff digest 08a75cf882b452b0da550ec49bf3293d6c110bb5da87d8022c6cb318b3b8e4a7, 1 call(s). An AI comment is not an approval.

@bartekus
bartekus force-pushed the impl/060-image-exceptions branch from 1d46233 to 7571df2 Compare November 4, 2026 10:53
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

AI review (findings)

Reviewing the diff for bugs, security problems, and inconsistencies.

Decision row numbering gap

docs/decisions/00-founding-decisions.md adds A-79 and A-80 immediately after A-77, skipping A-78 entirely. The table is explicitly sequential and no row explains the gap or reserves A-78. There is no other changed file in this PR that would account for it.

Exception reason text for libgcc-s1 is self-contradictory

tools/rustev-release/image-exceptions.json applies the reason "not reached: rustev-serve links only libc, libm and libgcc_s; TLS is statically linked rustls" to the five libgcc-s1 entries (lines for CVE-2026-102010, CVE-2026-114690, CVE-2026-114691, CVE-2026-115455, CVE-2026-95619 under libgcc-s1). That reason says the binary links libgcc_s, but libgcc-s1 is the package that provides libgcc_s, so the same sentence that justifies "not reached" also names the package as a direct dependency. The reasoning borrowed from the gcc-14-base / libgomp1 / libstdc++6 entries (which are genuinely not linked) does not apply here; it understates why those five CVEs are not reachable. This is a concrete inaccuracy in the exception rationale, not merely cosmetic, because A-80 and spec 060 3.2.1 record the reason as the basis for acceptance.

Everything else reviewed

  • Exception count: 5 (gcc-14-base) + 22 (libc6) + 5 (libgcc-s1) + 5 (libgomp1) + 5 (libstdc++6) + 2 (zlib1g) = 44 ✓, matches A-80 and the test assertion.
  • Expiry arithmetic: 90 days after 2026-11-03 is 2027-02-01; expires: "2027-02-01" is correct.
  • Test logic: found builds 88 platform-specific findings (44 × 2). The expiry assertion filters by substring "expired on 2027-02-01" and expects 44 (one per exception entry, not per finding); the remaining messages from uncovered findings would carry a different message, so the filter is sound.
  • CVE-2026-8674 is correctly given a distinct reason from the other 43; operator-control mitigation is documented in both the README and spec 042.
  • Spec-first requirement: spec 060 and spec 042 are both edited before the JSON and test change.
{"head": "7571df2400c4e71015d16bbef84dd8c40aa1e79d", "verdict": "findings", "findings": [{"path": "docs/decisions/00-founding-decisions.md", "line": null, "summary": "Decision row A-78 is absent: the table jumps from A-77 to A-79 with no row or explanation for the gap."}, {"path": "tools/rustev-release/image-exceptions.json", "line": null, "summary": "The five libgcc-s1 exception entries give the reason 'not reached: rustev-serve links only libc, libm and libgcc_s', but libgcc-s1 is the package that provides libgcc_s and is therefore a direct link dependency; the stated reason is self-contradictory for this package."}]}

Subject: 185713b...7571df2, diff digest 7ea0683e1f25a581ca2ba7dd6fb3538923c397309b2513c986b58ace5b0f5b98, 1 call(s). An AI comment is not an approval.

@bartekus
bartekus temporarily deployed to statecraft-review-exception November 4, 2026 11:05 — with GitHub Actions Inactive
@bartekus
bartekus force-pushed the impl/060-image-exceptions branch from 7571df2 to 082fbd0 Compare November 4, 2026 12:19
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

AI review (findings)

Looking at the diff across the six changed files, I find two concrete defects.

Finding 1 — Missing decision row A-78 (docs/decisions/00-founding-decisions.md)

The diff adds A-79 and A-80 immediately after A-77. The unchanged context lines show A-77 as the last existing entry, and the section closes with "No approval authorizes ratifying any other spec." There is no A-78 row anywhere in the added or surrounding lines. The gap is not explained by any adjacent text, and the sequential numbering convention used throughout the table makes the absence a concrete omission.

Finding 2 — Internally inconsistent "not reached" justification for libc6 and libgcc-s1 exceptions (tools/rustev-release/image-exceptions.json)

The spec text in 060 and the A-80 row both state "the binary links only libc, libm and libgcc_s." Yet the exception entries for 21 libc6 CVEs (all except CVE-2026-8674) and all 5 libgcc-s1 CVEs carry the reason "not reached: rustev-serve links only libc, libm and libgcc_s; TLS is statically linked rustls." That reason works as a linkage-based argument only for packages the binary does not link (gcc-14-base, libgomp1, libstdc++6, zlib1g). For libc6 and libgcc-s1, which are explicitly among the linked libraries, the reason neither explains why the vulnerable code paths are not exercised nor distinguishes those entries from CVE-2026-8674, which is separately described as "reachable." This is confirmed by spec 042's note that CVE-2026-8674 is "the one image finding that rustev-serve can reach" — a claim that implicitly acknowledges the other libc CVEs could also be reachable, contradicting the "not reached" label on 21 of them. The 27 affected entries (21 libc6 + 5 libgcc-s1 + 1 that is correct) have a justification that is factually inconsistent with the stated linkage facts.

{"head": "082fbd0c124b95d46636bcc8560ed81ff1c6c24c", "verdict": "findings", "findings": [{"path": "docs/decisions/00-founding-decisions.md", "line": 271, "summary": "Decision row A-78 is absent from the sequence: numbering jumps from A-77 to A-79 with no explanation."},{"path": "tools/rustev-release/image-exceptions.json", "line": null, "summary": "Twenty-one libc6 entries and five libgcc-s1 entries carry the reason 'not reached: rustev-serve links only libc, libm and libgcc_s', but those packages are explicitly among the linked libraries, making the library-linkage argument internally inconsistent for those 26 entries."}]}

Subject: b8a8cd8...082fbd0, diff digest 107bdf966e3f897d1f66c7cc1e2eca0f4981764495f2a87a34864ae4d0bb629b, 1 call(s). An AI comment is not an approval.

@bartekus
bartekus temporarily deployed to statecraft-review-exception November 4, 2026 12:22 — with GitHub Actions Inactive
…exceptions until 2027-02-01 (060 3.2.1, A-79, A-80)
…060 3.2.1, A-80)

The libc6 and gcc-14 entries named the binary's linkage as the reason,
which does not explain findings in the libraries the binary links. Each
entry now names the affected function or component and why rustev-serve
does not reach it.
@bartekus
bartekus force-pushed the impl/060-image-exceptions branch from 082fbd0 to ff2e4cb Compare November 4, 2026 21:20
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

AI review (findings)

Reviewing the diff for bugs, security defects, and governance inconsistencies.

image-exceptions.json — entry count
I counted 44 (id, package) pairs in the file (5 gcc-14-base + 22 libc6 + 5 libgcc-s1 + 5 libgomp1 + 5 libstdc++6 + 2 zlib1g). That matches the claims in A-80 and the test assertion assert_eq!(ex.exceptions.len(), 44). ✓

Expiry arithmetic
2026-11-03 + 90 days = 2027-02-01. All entries carry "expires": "2027-02-01". ✓

test coverage (image_scan.rs)
The test synthesises found as 44 × 2 platforms = 88 Finding values and then asserts that check(&ex, &rows, &found, "2027-02-02") produces exactly 44 messages containing "expired on 2027-02-01". Whether this is correct depends on whether check reports one message per exception entry or one per finding. The existing "empty scan" assertion (assert_eq!(check(&ex, &rows, &[], "2026-11-03").len(), 44)) suggests the function emits one message per exception when no finding covers it, which is consistent with 44 expired-entry messages. Without the full check implementation this cannot be confirmed from the diff, so it is not flagged as a definitive defect.

A-79 / spec 060 inconsistency — concrete finding
A-79 (docs/decisions/00-founding-decisions.md) states: "Recorded in 060's Decisions at approval." The diff to specs/060-the-service-image-in-the-release/spec.md inserts the amendment reference exclusively in the narrative increment body (the new Increment 3b, 2026-11-03 block), immediately before the unchanged ## Decisions at approval section header. No line is added inside the ## Decisions at approval section. A-79 is new in this PR, so the section cannot already contain the entry from a prior commit. The stated recording location does not match where the diff actually places the text.

{"head": "ff2e4cb49c3fd0c2e8f7d4a5eb8201ec58c36d8e", "verdict": "findings", "findings": [{"path": "docs/decisions/00-founding-decisions.md", "line": 272, "summary": "A-79 claims the 3.4.3 amendment is 'Recorded in 060's Decisions at approval', but the spec 060 diff adds the reference only to the narrative increment body before that section header, not inside the '## Decisions at approval' section."}]}

Subject: 3ab57f8...ff2e4cb, diff digest 53f8e1933f8f972f6a125306ecd049277176dbbdb88c673cd3f459fb9ce829f2, 1 call(s). An AI comment is not an approval.

@bartekus
bartekus temporarily deployed to statecraft-review-exception November 4, 2026 21:25 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

AI review (no-findings)

Reviewing the six changed files for concrete defects.

tools/rustev-release/image-exceptions.json — entry count

Counting by hand: gcc-14-base (5) + libc6 (22) + libgcc-s1 (5) + libgomp1 (5) + libstdc++6 (5) + zlib1g (2) = 44. Consistent with A-80's claim and the test assertion.

tools/rustev-release/tests/image_scan.rs — test logic

found is built by cross-producting 44 exceptions × 2 platforms = 88 synthetic findings. The test then:

  • asserts check(..., &found, "2026-11-03") and check(..., &found, "2027-02-01") are empty (exceptions valid through their last day)
  • asserts exactly 44 messages containing "expired on 2027-02-01" on "2027-02-02" — consistent if check emits one message per exception, not per finding (confirmed by the unused-exception assertion: check(&ex, &rows, &[], "2026-11-03").len() == 44)
  • the expiry check on "2027-02-02" only filters for "expired" strings; additional "uncovered finding" messages may exist but are not counted, which is a weaker assertion but not incorrect

Decision rows

A-79 and A-80 are both dated 2026-11-03. A-80 says "90 days after this row" → 2026-11-03 + 90 = 2027-02-01. November has 30 days (27 remaining after Nov 3) + 31 (Dec) + 31 (Jan) = 89 days to Jan 31; +1 more = Feb 1. ✓ All expiry dates in the exception file match.

specs/042 and specs/060 consistency

The operator note in 042 (CVE-2026-8674, A-80, until 2027-02-01) matches the exception entry and the README addition. The amendment note at the end of 060 correctly notes decision row A-79 records it.

Reason text for libgomp1 entries

Each libgomp1 entry says "libstdc++ code of the gcc-14 source package" — slightly imprecise since libgomp is the OpenMP library, not libstdc++, but the operative claim ("rustev-serve imports no C++ symbol and does not load libstdc++ or libgomp") is what justifies unreachability. The CVEs share the gcc-14 source package. Imprecise but not a logical defect in the exception itself.

No concrete defects found.

{"head": "cbe23c7be5f390ea7d81397cb56480c42ec44da9", "verdict": "no-findings", "findings": []}

Subject: 3ab57f8...cbe23c7, diff digest 49d57f784fe291a794f02390d71f07be7f882375346d338a19499887625076c1, 1 call(s). An AI comment is not an approval.

@bartekus bartekus closed this Nov 4, 2026
@bartekus bartekus reopened this Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

AI review (no-findings)

Reviewing the six changed files for bugs, security problems, and inconsistencies.

docs/decisions/00-founding-decisions.md
A-79 and A-80 are both added in this PR. A-79 records the 3.4.3 amendment; A-80 records the 44 exception acceptances expiring 2027-02-01. 2026-11-03 + 90 days = 2027-02-01 (Nov has 27 remaining days after the 3rd, Dec 31, Jan 31, +1 = 90). Arithmetic is correct. Both rows correctly state "authorizes no release, push or publication."

tools/rustev-release/image-exceptions.json
Counting entries: 5 (gcc-14-base) + 22 (libc6) + 5 (libgcc-s1) + 5 (libgomp1) + 5 (libstdc++6) + 2 (zlib1g) = 44. Matches A-80's "accept all 44". All entries carry "decision": "A-80" and "expires": "2027-02-01". The one reachable finding (CVE-2026-8674/libc6) is correctly distinguished from the 43 not-reached entries.

tools/rustev-release/tests/image_scan.rs
The test constructs found as 44 × 2 platform = 88 synthetic findings. The "unused" assertion (check(&ex, &rows, &[], "2026-11-03").len() == 44) establishes that check emits one message per unused exception entry (not per finding), so the "expired" assertion of 44 on 2027-02-02 is internally consistent with that same resolution. The loop over ex.exceptions effectively checks that A-80 exists in the decisions rows for all 44 entries. The old assertion ex.exceptions.is_empty() is correctly removed.

specs/060-the-service-image-in-the-release/spec.md
Increment 3b documents the base move and exception acceptance. Cross-references to A-79 and A-80 are correct. The final paragraph is updated from "a decision row for this amendment, if needed, is added separately" to "Decision row A-79 records this amendment," which is accurate since A-79 is now present.

specs/042-decision-service-and-remote-seams/spec.md
Operator note added, referencing CVE-2026-8674, A-80, and 2027-02-01. Consistent with README and exception entry.

integrations/rustev-serve/README.md
Operator guidance added for CVE-2026-8674 with correct expiry date and decision reference. This is the only reachable finding and the guidance is accurate.

No concrete defects found.

{"head": "cbe23c7be5f390ea7d81397cb56480c42ec44da9", "verdict": "no-findings", "findings": []}

Subject: 3ab57f8...cbe23c7, diff digest 49d57f784fe291a794f02390d71f07be7f882375346d338a19499887625076c1, 1 call(s). An AI comment is not an approval.

@bartekus
bartekus added this pull request to the merge queue Nov 5, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 5, 2026
@bartekus
bartekus added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 6aec8b9 Nov 5, 2026
84 of 114 checks passed
@bartekus
bartekus deleted the impl/060-image-exceptions branch November 5, 2026 04:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

statecraft:stacked-ci Run the full CI matrix on a stacked pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant