From 15f64510836b0e01c78e495e37acc7a6573b0773 Mon Sep 17 00:00:00 2001 From: wshallwshall Date: Sun, 2 Aug 2026 19:44:48 -0500 Subject: [PATCH 1/2] docs(adr-0152): re-anchor rotted code citations by symbol -- two pointed at each other's code Every line citation in the memory-hygiene paragraph had rotted, and this class of rot is silent by construction: they render as markdown link text over a target carrying no line fragment, so the link always resolves and no link checker can ever see it. :163 claimed _secure_zero -> prose inside cell_aad()'s docstring; _secure_zero is :187 :185-199 claimed mlock residency -> _secure_zero's own body; _lock_memory is :207-225 :314-325 claimed DEK zeroization -> _install_key is :391-411 :462 claimed plaintext wipe -> self._write_v2 = write_v2; the wipes are :713 and :782 The first two had come to point at each other's code. A renumber would not have surfaced that -- only opening both targets does. Re-anchored by SYMBOL rather than by corrected line numbers, since new numbers rot identically at the next refactor. Also in the rung-1 row: "four" posture fields is seven (three _self_reported_*, plus _readout_source, _operator_declared, _readout_contradicts_declaration and _note), and the present-tense "ENGINE_UI_SEAM 12 -> 13" reads as current state when the seam is 16 today -- retained as release history and marked as such. Verified against origin/main 5ad42acb: the cited source files are byte-identical between that tree and this base, so the symbol anchors hold on either. No claim in the ADR changed. Only the pointers were wrong. --- ...mory-encryption-attestation-asvs-11-7-1.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/adr/0152-in-use-data-protection-for-phi-platform-memory-encryption-attestation-asvs-11-7-1.md b/docs/adr/0152-in-use-data-protection-for-phi-platform-memory-encryption-attestation-asvs-11-7-1.md index 09f69638..0c2dd953 100644 --- a/docs/adr/0152-in-use-data-protection-for-phi-platform-memory-encryption-attestation-asvs-11-7-1.md +++ b/docs/adr/0152-in-use-data-protection-for-phi-platform-memory-encryption-attestation-asvs-11-7-1.md @@ -10,7 +10,7 @@ Build state at acceptance: | | | |---|---| -| **Rung 1** — platform read-out, report-only | **Built.** `config/memory_encryption.py`; four `memory_encryption_self_reported_*` / `..._readout_source` fields on `GET /security/posture`, plus the in-body disclaimer `memory_encryption_note` (`ENGINE_UI_SEAM` 12 → 13). | +| **Rung 1** — platform read-out, report-only | **Built.** `config/memory_encryption.py`; **seven** `memory_encryption*` fields on `GET /security/posture` — three `_self_reported_*`, plus `_readout_source`, `_operator_declared`, `_readout_contradicts_declaration`, and the in-body disclaimer `memory_encryption_note`. *(Corrected 2026-08-02: this row said "four", counting only the read-out fields and omitting the declaration, contradiction and note fields; [`api/models.py`](../../messagefoundry/api/models.py) makes the same 3+1 slip in its own comment. On the seam: `ENGINE_UI_SEAM` moved 12 → 13 for this work — release history, carried correctly in `CHANGELOG.md` — but this row is present-tense "Built.", so read as current state it was wrong; the seam is **16** today.)* | | **Rung 2** — operator declaration | **Built.** `[security].memory_encryption_operator_declared` (default `false`); an **exposed PHI** instance without it **warns at every start**, and refuses only when the estate opts in via `[security].require_memory_encryption_declaration` (default `false`) under `enforcement = enforce`. A contradiction is warned + reported as the tri-state `memory_encryption_readout_contradicts_declaration`, never refused. See the *2026-07-22 amendment* below for why the refusal is opt-in and why the field is not called "attested". | | **Rung 3** — cryptographic attestation | **Not built.** No quote acquisition, no signature verification, no vendor-chain handling. | | **Windows rung 3** | **Recorded infeasible in practice** — see *Windows rung 3 — spike conclusion* below. It is **platform-blocked, not API-blocked**. | @@ -39,11 +39,18 @@ it because L3 is the stated target for a PHI system, not because the conventiona **Memory hygiene is not memory encryption, and OWASP has already ruled on that distinction.** During the 4.x→5.0 cull OWASP **deleted** 4.0.3's V8.3.6 (overwrite sensitive memory when no longer needed) as *"NOT PRACTICAL"* — and **kept** 11.7.1. So the zeroization/locking family is explicitly not what -this requirement asks for. We already ship that family and it does not move this cell: `_secure_zero` -over a `bytearray` via `ctypes.memset` ([`store/crypto.py:163`](../../messagefoundry/store/crypto.py)), -`VirtualLock`/`mlock` residency ([`:185-199`](../../messagefoundry/store/crypto.py)), DEK zeroization -after install ([`:314-325`](../../messagefoundry/store/crypto.py)) and plaintext zeroization -([`:462`](../../messagefoundry/store/crypto.py)) all landed in #198. +this requirement asks for. We already ship that family and it does not move this cell — all of it in +[`store/crypto.py`](../../messagefoundry/store/crypto.py): `_secure_zero()` over a `bytearray` via +`ctypes.memset`, `VirtualLock`/`mlock` residency in `_lock_memory()` / `_unlock_memory()`, DEK +zeroization after install in `_install_key()`, and plaintext zeroization at the `_secure_zero(pt)` +calls on the encrypt and decrypt paths. All landed in #198. + +*(Anchors corrected 2026-08-02, and the claim is unchanged — only the pointers were wrong. These were +line citations: `:163`, `:185-199`, `:314-325`, `:462`. Every one had rotted, and the first two had +come to point at **each other's** code — `:163` at prose inside `cell_aad()`'s docstring, `:185-199` at +the body of `_secure_zero` rather than the residency helpers. Re-anchored by **symbol**: these render as +markdown link text over a target carrying no line fragment, so the link always resolves and no link +checker can catch this class. New line numbers would rot the same way at the next refactor.)* **CPython can protect keys but not message bodies, and that asymmetry is structural.** A DEK is small, short-lived, and we own the buffer — hence the work above. An HL7 message is not: it is `str` end to From 653fb069e8b4ee52b0ab0666c316d0dc894ff309 Mon Sep 17 00:00:00 2001 From: wshallwshall Date: Sun, 2 Aug 2026 19:48:05 -0500 Subject: [PATCH 2/2] docs(phi): 11.7.1 read Fail against a retired prose lineage; the record is the scorecard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two defects in one paragraph, both live on main. 1. It stated 11.7.1 "still reads **Fail**". The rubric on the same main reads `na` under rule 1 -- so PHI.md publicly contradicted ASVS-ASSESSMENT-METHOD.md. 2. It named a dated prose file as "the ASVS L3 assessment of record". ADR 0156 retired that lineage: the record is docs/security/asvs-scorecard.toml, rendered and CI-gated. Citing a dated assessment as the verdict of record is the exact failure 0156 exists to stop -- prose asserts facts about code, and the code moves. The replacement states the verdict, names the real record, and says why the cell is out of scope (the verb is a property of the hosting substrate, which ASSESSMENT-METHOD §2 places outside the assessed software). It also keeps the operational point explicit, per §2.1: an out-of-scope verdict buys nothing -- the CPython-heap residual is unchanged either way and a deployment still needs the host-side control. The old wording is described rather than silently dropped, so the correction is legible. --- docs/PHI.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/PHI.md b/docs/PHI.md index 27c8fbb3..d7a73866 100644 --- a/docs/PHI.md +++ b/docs/PHI.md @@ -598,12 +598,22 @@ start**, and refuses only if the estate opts in via `[security].require_memory_e That turns the deployment requirement from prose into a declaration of record with a standing warning where it is absent. -**The scorecard is unchanged, and this document does not pre-empt it.** ADR 0152 holds that rung 2 is -what would justify `Fail → Partial`, and the evidence for that now exists — but 11.7.1 is scored on the -ASVS L3 assessment of record (`ASVS-L3-ASSESSMENT-2026-07-22.md`), which still reads **Fail**, -and re-scoring it is an owner decision rather than a side effect of shipping a build. A **Pass** would -in any case require SEV-SNP/TDX hardware plus a verified CPU-signed quote (ADR 0152 rung 3, not built), -and even then the CPython-heap residual above is unchanged. +**This document does not pre-empt the scorecard.** 11.7.1 is scored **`na`**: the requirement's verb is +*"full memory encryption is in use"* — a property of the hosting substrate, which +[`ASVS-ASSESSMENT-METHOD.md`](ASVS-ASSESSMENT-METHOD.md) §2 places outside the assessed software, so +rule 1 takes it out of scope. Re-scoring remains an owner decision rather than a side effect of +shipping a build. + +⚠️ **The record is the scorecard itself** — `docs/security/asvs-scorecard.toml`, rendered and CI-gated +([ADR 0156](adr/0156-asvs-scorecard-as-data-a-derived-count-verified-evidence-anchors-and-a-fail-closed-drift-gate.md)) +— **never a prose assessment.** ADR 0156 replaced the dated-document lineage precisely because prose +asserts facts about code and the code moves; do not cite a dated assessment file as the verdict of +record. *(This paragraph previously did exactly that, naming a dated assessment and reporting a `Fail` +that the record no longer carried.)* + +**An out-of-scope verdict buys nothing operationally**, which is the point of §2.1: the CPython-heap +residual above is unchanged either way, and a deployment still needs the host-side control. Rung 3 +(SEV-SNP/TDX plus a verified CPU-signed quote) remains unbuilt. ---