Härdning: kedjesvep i drift — och svepets första egna fynd

KEDJESVEPET. Verifieringen fanns men anropades bara vid tvist: ett brott
kunde stå oupptäckt i åratal och upptäckas i sämsta tänkbara ögonblick.
Nu går driften igenom varje kedja och varje försegling i varje
organisation — POST /api/kedjesvep för arbetsledaren, `node server.mjs
--kedjesvep` för nattlig cron utan server, med felkod vid brott så att
larmet är gratis. Endpoint och svep delar EN implementation
(kedjestatus): två implementationer av "är kedjan hel?" kommer att svara
olika den dag det betyder något — det var T-13/T-14:s rot, tillämpad i
förebyggande syfte.

SVEPETS FÖRSTA FYND VAR PÅ RIKTIGT. Första körningen mot
integrationsmiljön rapporterade TRE brott där testerna saboterat två.
Det tredje var en bugg: leverantörshändelser bär `enhet: undefined`, och
den kanoniska formen serialiserade nyckeln som null medan databasens
rundresa släpper den. Skriv-digest och läs-digest skilde sig — varje
leverantörshändelse bröt sin egen kedjelänk. Klienthändelser gick fria
av en slump: JSON.parse kan inte producera undefined. Fixat vid roten:
kanonisk form är nu exakt den form som överlever rundresan, inte en
nästan likadan. Mekanismen fungerade precis som avsett — dag ett, mot
sina egna upphovsmän.

TRANSPORTEN. Säkerhetshuvuden på varje API-svar: nosniff, no-store
(ett API vars svar är personuppgifter får inte bli en cacheträff),
frame-ancestors 'none', no-referrer, HSTS. För stor kropp svarar 413 i
stället för ett intetsägande 500. Kroppstak 4 MiB och batchtak 500
verifierade som redan på plats — bokfört, inte antaget.

En skriptläxa på vägen: cron-svepets felkod är själva poängen, men under
`set -e` dödade den testskriptet vid tilldelningen — felkoden måste
fångas i samma andetag.

Utgåva 3.4. API-specen dokumenterar svepet. TÜV-2-rapporten har bilaga A
med samma-dags-härdningen, inklusive svepets eget fynd.

768 enhetstester, 224 integrationskontroller mot riktig Postgres,
återställningsprov, genomgång 4/4, portalspärr, typkontroll och lint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
This commit is contained in:
Claude
2026-08-06 20:55:21 +00:00
parent c1c56e60b5
commit 6665004ec7
5 changed files with 264 additions and 91 deletions
+14
View File
@@ -193,3 +193,17 @@ retention, and must continue to be stated as such.
---
*ALVA-DOC-0007 · Internal engineering review · Not endorsed by any inspection body*
---
## Appendix A · Post-audit hardening (same day)
Implemented after the examination closed, in response to §4 and the
verdict's root-cause note:
| Measure | Detail |
| --- | --- |
| Chain sweep | `POST /api/kedjesvep` (supervisor/admin) verifies every chain and seal in the organisation; `node server.mjs --kedjesvep` does the same across all organisations nightly, exiting non-zero on any break. Verification existed but was only ever invoked at dispute time — a break could stand undetected for years. |
| The sweep's first catch | On its first run against the integration environment, the sweep reported **three** breaks where the tests had sabotaged two. The third was real: supplier-protocol events carried `enhet: undefined`, which the canonical form serialised as `null` while the database round-trip drops the key — write-digest and read-digest differed, and every supplier event broke its own chain link. Client events escaped by accident (`JSON.parse` cannot produce `undefined`). Fixed at the root: canonical form now matches exactly what survives the round-trip. The examiner notes the mechanism worked precisely as intended — on day one, against its own authors. |
| One implementation of verification | Endpoint and sweep share `kedjestatus()`. Two implementations of "is the chain intact?" will disagree the day it matters — the T-13/T-14 root cause, applied prophylactically. |
| Transport hardening | `nosniff`, `no-store`, `frame-ancestors 'none'`, `Referrer-Policy: no-referrer`, HSTS on every API response; oversized bodies answer 413 instead of a generic 500. Body cap (4 MiB) and batch cap (500) verified as already present. |