Skip to content

Validate the feed against the channel it describes - #142

Merged
jsokol merged 1 commit into
updates.simplerisk.comfrom
FEATURE-validate-feed
Aug 22, 2026
Merged

Validate the feed against the channel it describes#142
jsokol merged 1 commit into
updates.simplerisk.comfrom
FEATURE-validate-feed

Conversation

@jsokol

@jsokol jsokol commented Aug 22, 2026

Copy link
Copy Markdown
Member

Note

This PR's own check is red on purpose right now. It is detecting the live production defect that #141 fixes. Merge #141, and this goes green. That red run is the demonstration.

Description

The feed is a set of claims about published artifacts, and nothing checked them.

Yesterday a merge of updates-test.simplerisk.com into this branch replaced the GA bundle checksums with the testing channel's, and added upgrade hops through releases that never shipped. Nothing here went red. The break surfaced hours later in simplerisk/docker, whose image build verifies the bundle against this feed and fails closed — blocking every PR in that repo, with an error that reads like a permissions problem (S3 returns 403, not 404, for a missing object).

This asserts the claims against the channel the branch describes:

  • every manifest parses, and carries no DTD — these are generated files, so a DOCTYPE could only ever be an entity-expansion attack on the runner
  • Current_Version names a release that exists and publishes a checksum
  • every release publishing a bundle_sha256 has a bundle actually fetchable on this channel, hashing to exactly that value
  • upgrade_path routes only to releases that exist, and agrees with each release's next_release

Why "entries making a claim" and not "all entries"

I built the broader version first and it produced 113 errors — firing on every historical release back to 2013, because a channel serves only what it currently needs and old bundles are long gone. That is correct and unremarkable, not a defect.

Publishing a bundle_sha256 is what turns an entry into a checkable claim. That's the trigger. Only 2 of 111 entries qualify today, so this downloads two bundles, not a hundred.

The current release gets one extra check — it must have a checksum — since it's the one consumers actually download. An unverifiable current release is a defect; an unverifiable 2015 release isn't.

Verified against both states

Against a657849e (the broken feed): fails with exactly the 4 real defects, no false positives.

::error::20260820-001 bundle_sha256 mismatch on prod: feed says 61c78cca…, the served bytes hash to 3e279416…
::error::20260811-001 publishes a bundle_sha256 but its bundle is not fetchable on the prod channel (HTTP 403 (absent))
::error::20260709-001 publishes a bundle_sha256 but its bundle is not fetchable on the prod channel (HTTP 403 (absent))
::error::20260519-001 bundle_sha256 mismatch on prod: feed says 9741051f…, the served bytes hash to 7d7fb242…
FAILED — 4 problem(s)

Against the #141 fix: passes.

== bundles claimed by the feed (2: 20260820-001, 20260519-001)
  OK   20260820-001 bundle verified against the prod channel (3e27941634b3791a…)
  OK   20260519-001 bundle verified against the prod channel (7d7fb24214081ef5…)
PASSED — 10 checks

Make it required

Worth setting Validate feed against its channel as a required status check on both feed branches.

This is deliberately a validation gate rather than a branch restriction. GitHub has no rule that rejects a merge by source branch — so a "never merge test into prod" rule isn't expressible. A gate that checks the resulting content catches a bad write however it arrives: branch merge, hand edit, or a broken generator run.

Release Notes

None — internal change.

Manual Validation Steps

  1. Observe this PR's check failing against the current (broken) prod feed.
  2. Merge Restore prod bundle hashes and upgrade path clobbered by the test-branch merge #141; re-run this PR's check — it should go green.
  3. On a PR into updates-test.simplerisk.com, confirm the channel resolves to test and bundles are checked against bundles-test.simplerisk.com (it reads the base ref, since a topic branch name says nothing about which channel it lands on).
  4. Set the check required on both branches.

Type of Change

  • Build / CI configuration

Testing Results

  • Run against both real feed states (broken and fixed) — correct verdict, correct exit code, no false positives in either direction.
  • The over-broad first version was rejected on evidence (113 false errors), not intuition; the scoping rule above is the result.
  • actionlint clean. Script is dependency-free stdlib Python — runners ship no defusedxml, hence the DTD rejection instead.
  • Network failures are handled per-URL and reported as errors rather than raising, so one unreachable host produces a readable failure rather than a stack trace.
  • Not exercised: the test channel path against a real testing feed. The logic is shared; only the base URL differs. Step 3 covers it on the first test-branch PR.

CIA Impact Check

WILL NOT negatively impact Confidentiality, Integrity or Availability.

Adds a read-only check. It downloads published bundles over HTTPS and compares hashes; it writes nothing, holds no credentials, and needs only contents: read. Net positive on Integrity — it is the control that would have caught the current production defect at PR time instead of hours later in another repo.

The feed is a set of claims about published artifacts and nothing checked them.
On 2026-08-21 a merge of the updates-test branch into this one replaced the GA
bundle checksums with the testing channel's and added upgrade hops through
releases that never shipped. Nothing here went red. The break surfaced hours
later in simplerisk/docker, whose image build verifies the bundle against this
feed and fails closed -- blocking every PR in that repo.

Asserts the claims against reality:
  - every manifest parses, and carries no DTD (these are generated files; a
    DOCTYPE would only ever be an entity-expansion attack on the runner)
  - Current_Version names a release that exists and publishes a checksum
  - every release publishing a bundle_sha256 has a bundle actually fetchable on
    this channel, hashing to exactly that value
  - upgrade_path routes only to releases that exist, and agrees with each
    release's next_release

The checked set is deliberately "entries making a claim", not "all entries".
A historical release carries no checksum and its bundle is long gone from the
channel -- that is correct and unremarkable. Publishing a checksum is what makes
a claim checkable, so that is the trigger. Only 2 of 111 entries qualify today,
so this downloads two bundles.

Verified against both states: FAILS on a657849 with exactly the 4 real defects
and no false positives, PASSES on the #141 fix.

Worth making a required status check on both feed branches. Unlike a branch
restriction it catches a bad write however it arrives -- branch merge, hand
edit, or a broken generator run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jsokol
jsokol marked this pull request as ready for review August 22, 2026 20:57
@jsokol
jsokol merged commit 0536937 into updates.simplerisk.com Aug 22, 2026
1 check failed
@jsokol
jsokol deleted the FEATURE-validate-feed branch August 22, 2026 20:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant