Repository navigation
feat(compliance): the check's audit row holds its report hash, so an edit shows - #562
Merged
Merged
Conversation
…edit shows
A stored compliance check could be edited in the database without a trace:
its audit row recorded only the verdict, and the audit log's hash chain
covers the audit rows, not the compliance_checks table. A database admin
could turn a failed rule into a passed one and keep the verdict.
The check's audit row now records "verdict X, report sha256 Y, reference
sha256 Z", and GET /api/conformity/checks/{id} answers reportSha256, the
SHA-256 of the report as stored, computed when asked. An edited report no
longer matches the hash its chained audit row holds; a rewrite of the chain
itself changes the rowHash of an anchor kept outside the database.
The compliance chapter gains "Proving the Results Were Not Changed": the
three records behind a check, how to keep the anchor in a pipeline, a
script that verifies a stored check, what each failure means, and the
limits. The API reference describes the new fields.
bmscomp
force-pushed
the
feat/compliance-report-hash
branch
from
October 30, 2026 22:27
b63f922 to
941312d
Compare
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge #560, then #561, first. This PR targets
mainso CI runs, and carries #561's and #560's commits. Once they squash-merge, I'll merge main in; the squash of this PR should then use only its own commit's message.Why
How do you prove a database admin didn't edit a stored compliance result by hand? With #560 and #561 alone you can't: the check's audit row recorded only
verdict X, and the hash chain coversaudit_events, notcompliance_checks. A failed rule could be turned into a passed one, verdict kept, and nothing would show.What
verdict FAIL, report sha256 …, reference sha256 …, so the chain holds the report's hash.GET /api/conformity/checks/{id}answersreportSha256, computed from the stored report when asked. An edited report no longer matches its audit row.ComplianceCheckRepository.storereturns the stored row instead of its id (two callers, one IT).The chapter states the honest limit: the chain is unkeyed SHA-256, so a whole-table rewrite only shows against an anchor kept where the database's administrators can't write.
Tests (local)
ComplianceResourceTest(9, one new: a hand edit ofreport_jsonin the database makesreportSha256differ from the one the audit row recorded),AuditVerifyTest,AuditChainTest: 19 passComplianceChecksITon Postgres (Testcontainers): 2 passTestMCPCaveat*,scripts/check-book-style.sh, lychee on the two pagesjqfilters checked against sample JSON shaped like the API's answers; not run against a live Kates API.Not done
kates compliance checkdoesn't print the stored check's id, so the book finds the anchor via the gate principal's newestcomplianceaudit row (one key per gate).rowHashcomparison.