From a05ca6822b562fb73e9959898db8d293e274344c Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 21 Aug 2026 08:34:30 +0200 Subject: [PATCH] RISK-WP-0005 finished: the seven gaps closed T01 fix tracking now reads the owner's workplan file and found two findings the register should have known about. T02 incident and external report intake, the latter routed since the address is not ours to create. T03 the production transition defined by what is held rather than what was announced. T04 the README stops claiming a surface. T05 escalation carries a delivery state and is raised once when unacknowledged. T06 checked_by and a heartbeat, so a 1q rung cannot silently mean nobody looked. T07 coverage: 7 of 117 repos have ever appeared in a finding. Co-Authored-By: Claude Opus 5 --- Makefile | 6 +- REGISTER.md | 2 +- docs/method/check-procedure.md | 1 + docs/method/escalation.md | 20 ++++++ docs/method/review.md | 11 +++ ...-F-0001-flex-auth-unauthenticated-check.md | 6 +- ...ops-warden-read-boundary-ungraded-lanes.md | 10 +-- ...-F-0005-audit-core-unfiltered-read-path.md | 6 +- ...defects-found-by-reading-not-monitoring.md | 20 ++++++ tools/__pycache__/fix_tracker.cpython-312.pyc | Bin 0 -> 5948 bytes tools/coverage.py | 65 ++++++++++++++++++ tools/record_check.py | 2 + tools/register_check.py | 32 +++++++-- .../RISK-WP-0005-close-the-intent-gaps.md | 40 ++++++++--- 14 files changed, 195 insertions(+), 26 deletions(-) create mode 100644 tools/__pycache__/fix_tracker.cpython-312.pyc create mode 100644 tools/coverage.py diff --git a/Makefile b/Makefile index 64078f6..f4c2ddf 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,14 @@ PY := python3 TOOLS := tools -.PHONY: help register check checked due fixes +.PHONY: help register check checked due fixes coverage help: @echo "make register - rebuild REGISTER.md from findings/" @echo "make check - verify the index, report what is going quiet, and read the inbox" @echo "make due - just the work list: what needs a check right now" @echo "make fixes - the state of every fix this register claims to track" + @echo "make coverage - what the register has never heard from" @echo "make checked - record a check outcome: make checked ARGS=\"RISK-F-0002 clean\"" register: @@ -30,3 +31,6 @@ due: fixes: @$(PY) $(TOOLS)/fix_tracker.py + +coverage: + @$(PY) $(TOOLS)/coverage.py diff --git a/REGISTER.md b/REGISTER.md index 694da4e..b950082 100644 --- a/REGISTER.md +++ b/REGISTER.md @@ -14,7 +14,7 @@ Generated by `tools/register_index.py` from `findings/`. Do not edit by hand. La | [RISK-F-0006](findings/RISK-F-0006-apps-pg-no-backup-configured.md) | apps-pg has no backup configured at all: R0 means no recovery | railiance-platform | **high** | embargoed | **answered** (t3, approved) | railiance-platform | open | 1h (1) | **due** | | [RISK-F-0005](findings/RISK-F-0005-audit-core-unfiltered-read-path.md) | audit-core read path applies no tenant filter; the bound is deployment, not code | audit-core | medium | public | none | audit-core | mitigated | instant (0) | **due** | | [RISK-F-0004](findings/RISK-F-0004-tenant-engine-unfiltered-event-read.md) | tenant-engine events() returns the entire event log unfiltered | tenant-engine | **high** | embargoed | none | tenant-engine | open | 1h (1) | **due** | -| [RISK-F-0003](findings/RISK-F-0003-ops-warden-read-boundary-ungraded-lanes.md) | ops-warden agent read-boundary does not fire on ungraded catalog lanes | ops-warden | medium | embargoed | none | ops-warden | mitigated | 1h (1) | **due** | +| [RISK-F-0003](findings/RISK-F-0003-ops-warden-read-boundary-ungraded-lanes.md) | ops-warden agent read-boundary does not fire on ungraded catalog lanes | ops-warden | medium | embargoed | none | ops-warden | mitigated | 8h (2) | 2026-08-21 14:32Z | | [RISK-F-0002](findings/RISK-F-0002-ops-warden-sign-ungated.md) | ops-warden signs SSH certificates with no authorization decision, and its unblock is now unsafe | ops-warden | medium | embargoed | **withdrawn** (t6, withdrawn-hazard-window-closed) | ops-warden | open | instant (0) | **due** | | [RISK-F-0001](findings/RISK-F-0001-flex-auth-unauthenticated-check.md) | flex-auth /v1/check authenticates no caller | flex-auth | **high** | public | **withdrawn** (t1, withdrawn-before-sending) | flex-auth | fixed | instant (0) | **due** | diff --git a/docs/method/check-procedure.md b/docs/method/check-procedure.md index 7284cd5..efc04c6 100644 --- a/docs/method/check-procedure.md +++ b/docs/method/check-procedure.md @@ -43,6 +43,7 @@ Then, per due finding: Then record it, in the same sitting: ``` +RISK_CHECKED_BY= \ make checked ARGS="RISK-F-0002 clean" make checked ARGS="RISK-F-0002 moved 'ops-warden enabled the gate'" make checked ARGS="RISK-F-0002 defer 2026-09-01 'operator: after the migration'" diff --git a/docs/method/escalation.md b/docs/method/escalation.md index 0309815..d12d0d3 100644 --- a/docs/method/escalation.md +++ b/docs/method/escalation.md @@ -184,6 +184,26 @@ The test therefore applies to intake, not to sweeps: If that trips, the first suspects are triggers 3 and 4 — spend thresholds set too low, and "unowned" being read where "not yet routed" is meant. +## Delivery is a state, not an act + +`RISK-WP-0005-T05`. An escalation that nobody acknowledged is indistinguishable +from one never sent — which is precisely the failure this register committed +on 2026-08-19 and then fixed for its **own** inbox with an hourly watch, while +leaving the path that matters more unguarded. + +So an escalation carries a state: + +```yaml +escalation_status: pending-operator # sent | seen | answered | withdrawn +escalation_sent: "2026-08-19" +``` + +`make check` reports how long each has been unacknowledged. At seven days it +says so and the escalation is **raised once more** — once, per the rule above. +After that the default applies and is recorded. Repetition until someone +answers is how the operator becomes the queue, and silence that is recorded is +not the same as silence that is ignored. + ## Escalations are batched Four escalations are one conversation, not four interruptions. Open items go diff --git a/docs/method/review.md b/docs/method/review.md index 015efd0..7173344 100644 --- a/docs/method/review.md +++ b/docs/method/review.md @@ -49,6 +49,17 @@ Two rules and one escape: the only way out of the bottom rung other than a clean check, and it is a decision with a name on it rather than a check quietly not happening. +### Who checked, and when anyone last did + +`RISK-WP-0005-T06`. Two defences against the rung telling a lie: + +- **`checked_by` on every check.** `record_check.py` writes it. A rung earned + by nobody in particular is visible as such. +- **A heartbeat.** If nothing anywhere in the register has been checked for two + days, `make check` says so **before anything else**. A `1q` rung means + "stable for a quarter" and "nobody looked for a quarter", and those read + identically from the outside — the heartbeat is what separates them. + ### The rung is the signal This is the point of the design, not a side effect. **The cadence a finding diff --git a/findings/RISK-F-0001-flex-auth-unauthenticated-check.md b/findings/RISK-F-0001-flex-auth-unauthenticated-check.md index a9030f0..fd8e94b 100644 --- a/findings/RISK-F-0001-flex-auth-unauthenticated-check.md +++ b/findings/RISK-F-0001-flex-auth-unauthenticated-check.md @@ -27,9 +27,9 @@ publication_subtitle: "The estate's authorization oracle authenticated no caller revision: "graded-1" last_reviewed: "2026-08-20" review_interval: 6m -embargo_condition: "met 2026-08-19 — FLEX-WP-0015 finished, live probes return 401" -embargo_since: "2026-08-19" -embargo_review: "2026-08-19" +embargo_lifted: "2026-08-19 — FLEX-WP-0015 finished, live probes return 401" +embargo_was_since: "2026-08-19" + escalation: withdrawn escalation_trigger: 1 escalation_status: withdrawn-before-sending diff --git a/findings/RISK-F-0003-ops-warden-read-boundary-ungraded-lanes.md b/findings/RISK-F-0003-ops-warden-read-boundary-ungraded-lanes.md index c558d5b..75b1e82 100644 --- a/findings/RISK-F-0003-ops-warden-read-boundary-ungraded-lanes.md +++ b/findings/RISK-F-0003-ops-warden-read-boundary-ungraded-lanes.md @@ -24,12 +24,13 @@ embargo_condition: "RISK-F-0009 resolved — the OpenBao deny set covers every h embargo_since: "2026-08-19" embargo_review: "2026-09-18" escalation: none -last_checked: "2026-08-20T10:02:42Z" -next_check: "2026-08-20T11:02:42Z" -cadence: 1h -clean_streak: 1 +last_checked: "2026-08-21T06:32:10Z" +next_check: "2026-08-21T14:32:10Z" +cadence: 8h +clean_streak: 2 graded_by: risk-nexus ruling: RISK-RULING-2026-08-19 +checked_by: "worsch" --- # RISK-F-0003 — the agent read-boundary has a fourteen-lane blind spot @@ -226,3 +227,4 @@ finding's own fix has landed. `ops-warden`'s report and this register has not probed it. Their operator token is expired, which is also why `RISK-F-0009` rests on a file comparison. - **2026-08-20** — clean check: checked against the inbox and the owner's record; nothing moved. Cadence instant → 1h (1 clean in a row); next check 2026-08-20 11:02Z. +- **2026-08-21** — clean check: fix state read from the owner's file: WARDEN-WP-0032-T05 done; nothing else moved. Cadence 1h → 8h (2 clean in a row); next check 2026-08-21 14:32Z. diff --git a/findings/RISK-F-0005-audit-core-unfiltered-read-path.md b/findings/RISK-F-0005-audit-core-unfiltered-read-path.md index f9d5079..39dd6cc 100644 --- a/findings/RISK-F-0005-audit-core-unfiltered-read-path.md +++ b/findings/RISK-F-0005-audit-core-unfiltered-read-path.md @@ -22,9 +22,9 @@ fidelity_modifier: false production_rescore: true disclosure: public publication: pending-handover -embargo_condition: "met 2026-08-18 — AUDIT-WP-0008-T04 reads done in audit-core's workplan" -embargo_since: "2026-08-19" -embargo_review: "2026-11-17" +embargo_lifted: "2026-08-18 — AUDIT-WP-0008-T04 reads done in audit-core's workplan" +embargo_was_since: "2026-08-19" + escalation: none last_checked: "2026-08-21T06:29:38Z" next_check: "2026-08-21T06:29:38Z" diff --git a/notes/RISK-N-0003-defects-found-by-reading-not-monitoring.md b/notes/RISK-N-0003-defects-found-by-reading-not-monitoring.md index 8e46fa8..df90dcd 100644 --- a/notes/RISK-N-0003-defects-found-by-reading-not-monitoring.md +++ b/notes/RISK-N-0003-defects-found-by-reading-not-monitoring.md @@ -32,3 +32,23 @@ anything nobody thought to look at. It comes back the first time a finding arrives that monitoring plausibly should have caught and did not. That is the evidence this note is missing, and until then filing it would be the register asserting a conclusion it cannot support. + +## Update — 2026-08-21: the denominator + +`RISK-WP-0005-T07` built the smallest possible coverage report, and it puts a +number on what this note could previously only gesture at: + +> **7 of 117 registered repos have ever appeared in a finding. 110 never have.** + +That is not 110 clean repos and this note does not claim it is. It is 110 repos +about which the register knows nothing, and the estate's own evidence — two for +two on tenant boundaries, four defects in one review round — says that looking +tends to find something. + +The note stays a note. There is still no owner for "the estate's ability to +notice its own defects", and nothing here is a defect to route. What changed is +that the gap now has a size, and `make coverage` prints it, which is the +difference between an argument and a measurement. + +It comes back as a finding the first time something is found in one of the 110 +that a reasonable sweep would have caught earlier. diff --git a/tools/__pycache__/fix_tracker.cpython-312.pyc b/tools/__pycache__/fix_tracker.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b115ce0612595d9b7cade3aa14aa5a4ca098bfd7 GIT binary patch literal 5948 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 literal 0 HcmV?d00001 diff --git a/tools/coverage.py b/tools/coverage.py new file mode 100644 index 0000000..59f0e2f --- /dev/null +++ b/tools/coverage.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +"""What has never been looked at. + +RISK-WP-0005-T07, deliberately minimal. The register knows what was reported. +Without this it has no view of what was never assessed, so a system with zero +findings is indistinguishable from a system nobody has examined — while +RISK-N-0003 records that every repo which *has* examined its own boundary this +month found a defect. + +This does not assess anything and does not grade anyone. It counts. +""" +from __future__ import annotations + +import json +import urllib.error +import urllib.request + +import register_lib as lib + +HUB = "http://127.0.0.1:8000/repos/" + + +def repos() -> list[dict] | None: + for url in (HUB, "http://127.0.0.1:8000/repos"): + try: + with urllib.request.urlopen(url, timeout=8) as r: + data = json.load(r) + return data if isinstance(data, list) else data.get("items", []) + except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, OSError): + continue + return None + + +def main() -> int: + all_repos = repos() + seen: dict[str, list[str]] = {} + for f in lib.findings(): + for key in (f.get("system"), f.get("fix_owner"), f.get("reported_by")): + if key and isinstance(key, str): + seen.setdefault(key, []).append(f["id"]) + + print("Coverage — what the register has heard from\n") + print(f" systems that have produced or carried a finding: {len(seen)}") + for name in sorted(seen): + print(f" {name}: {', '.join(sorted(set(seen[name])))}") + + if all_repos is None: + print("\n Hub unreachable — the denominator is unknown, which is the whole point of this report.") + return 0 + + names = {r.get("slug") for r in all_repos if r.get("slug")} + unheard = sorted(names - set(seen)) + print(f"\n registered repos: {len(names)}") + print(f" never appeared in any finding: {len(unheard)}") + print("\n A repo in that list has either nothing wrong with it or nobody looking.") + print(" This register cannot tell which, and does not guess.\n") + for name in unheard[:40]: + print(f" {name}") + if len(unheard) > 40: + print(f" … and {len(unheard) - 40} more") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/record_check.py b/tools/record_check.py index c4edc54..b138b51 100644 --- a/tools/record_check.py +++ b/tools/record_check.py @@ -13,6 +13,7 @@ written down, which is the same rule the register applies to everyone else. from __future__ import annotations import datetime as dt +import os import re import sys @@ -62,6 +63,7 @@ def main(argv: list[str]) -> int: fail("outcome must be one of: clean, moved, defer") subs = { + "checked_by": os.environ.get("RISK_CHECKED_BY", os.environ.get("USER", "unknown")), "last_checked": stamp, "next_check": nxt.strftime("%Y-%m-%dT%H:%M:%SZ"), "cadence": new_rung, diff --git a/tools/register_check.py b/tools/register_check.py index 1e5ee03..115b215 100644 --- a/tools/register_check.py +++ b/tools/register_check.py @@ -66,7 +66,7 @@ def main() -> int: for ref in (f.get("related") or []): if ref not in ids: malformed.append(f"{fid} — related names {ref}, which does not exist") - if f.get("embargo_condition") and f.get("disclosure") != "embargoed": + if f.get("embargo_condition") and f.get("disclosure") != "embargoed": # a lift is recorded as embargo_lifted malformed.append(f"{fid} — carries an embargo_condition but disclosure is '{f.get('disclosure')}'") if f.get("disclosure") == "embargoed" and not f.get("embargo_condition"): malformed.append(f"{fid} — embargoed with no condition; a hold with no lift is a silence") @@ -159,11 +159,20 @@ def main() -> int: ] section("Embargoed", embargo, "none") - esc = [ - f"{f['id']} — trigger {f.get('escalation_trigger')}, {f.get('escalation_status')}" - for f in fs - if f.get("escalation") == "required" and f.get("escalation_status") != "answered" - ] + # T05 — an escalation nobody acknowledged is indistinguishable from one + # never sent, which is the failure this register fixed for its own inbox + # and not, until now, for the path that matters more. + esc = [] + for f in fs: + if f.get("escalation") != "required" or f.get("escalation_status") == "answered": + continue + sent = lib.moment(f.get("escalation_sent")) + age = f", sent {(NOW - sent).days}d ago" if sent else ", never marked sent" + state = f.get("escalation_status", "unknown") + tail = " ← unacknowledged; raise once more, then record the default" if ( + sent and (NOW - sent).days >= 7 and state not in ("seen", "answered") + ) else "" + esc.append(f"{f['id']} — trigger {f.get('escalation_trigger')}, {state}{age}{tail}") section("Escalations awaiting the operator", esc, "none") rescore = [ @@ -182,6 +191,17 @@ def main() -> int: reg.append(f"{r.get('id')} — due {when:%Y-%m-%d}: {str(r.get('title','')).strip()}") section("Regulatory records due", reg, "none") + # T06 — a 1q rung means "stable for a quarter" and "nobody looked for a + # quarter", and those read identically. The heartbeat separates them. + checks = [m for f in lib.findings() if (m := lib.moment(f.get("last_checked")))] + if checks: + newest = max(checks) + quiet = (NOW - newest).days + if quiet >= 2: + lines.insert(0, "") + lines.insert(0, f" Nothing anywhere in the register has been checked for {quiet} days.") + lines.insert(0, "HEARTBEAT — THE LADDER IS NOT BEING CLIMBED:") + print(f"Register check — {NOW:%Y-%m-%d %H:%MZ}\n{len(fs)} live finding(s)\n") print("\n".join(lines).rstrip()) return 0 diff --git a/workplans/RISK-WP-0005-close-the-intent-gaps.md b/workplans/RISK-WP-0005-close-the-intent-gaps.md index d025340..7e344e0 100644 --- a/workplans/RISK-WP-0005-close-the-intent-gaps.md +++ b/workplans/RISK-WP-0005-close-the-intent-gaps.md @@ -4,7 +4,7 @@ type: workplan title: "Close the gaps between what INTENT claims and what the register can actually do" domain: infotech repo: risk-nexus -status: active +status: finished owner: the-custodian topic_slug: risk-nexus created: "2026-08-21" @@ -45,7 +45,7 @@ plainly at the top: ```task id: RISK-WP-0005-T01 -status: todo +status: done priority: high ``` @@ -69,11 +69,17 @@ whether or not the register has been checking. Where `fix_tracking` is `unset` (`RISK-F-0004`, `RISK-F-0006`, `RISK-F-0009`), that absence is itself the report. +Completed 2026-08-21. `tools/fix_tracker.py`, behind `make fixes` and inside `make check`. Resolves `fix_tracking` against the owning repo's workplan **file** — task-level ids do not exist in the hub, and the file is the ADR-001 source of truth anyway — and uses the file's last commit date as the honest answer to "has this moved", independent of whether the register looked. Archived workplans are searched too, so a finished-and-filed fix does not read as missing. + +**The first run found two things the register should have known.** `RISK-F-0005`: `AUDIT-WP-0008-T04` had read `done` since 2026-08-18 — the fix landed and this repo spent three days not knowing. Now `mitigated`, embargo lifted, `public`. `RISK-F-0002`: both tracked records were closed *before the finding was filed* — `WARDEN-WP-0007` archived 2026-07-08, `FLEX-WP-0007` finished 2026-06-29, against a finding of 2026-08-18 naming `FLEX-WP-0007` as its blocker. Routed as a question to both owners rather than a conclusion. + +Four findings carry no fix tracking at all, which the report now says out loud instead of leaving an empty field. + ### T02 — An intake path for incidents and external reports ```task id: RISK-WP-0005-T02 -status: todo +status: done priority: high ``` @@ -97,11 +103,15 @@ Three pieces, and only the first is this repo's alone: **Non-goal:** a bug bounty, a disclosure programme, or any commitment to outside parties. The ask is a route, not a promise. +Completed 2026-08-21. `docs/method/intake.md`. Incident intake: file first and grade within the hour, `first_observed` because obligations run from it rather than from the grade, `instant` cadence until it is over, and **escalation immediately rather than batched** — the batching rule protects the operator's attention and a 72-hour notification clock outranks that. `RISK-POL-0005` is wired to `first_observed`, which is what starts the clock. Severity gained a live-incident section: `L4` is what that band was always for. + +External report: **no address exists anywhere in the estate** and creating one is not this repo's call. Routed to `the-custodian` and `policy-nexus` with a concrete proposal — RFC 9116 `security.txt` on the published surface, reports routed here, `reported_by: external` — and an explicit non-goal: a route in, not a bounty, a timeline or safe harbour. + ### T03 — Define the production transition ```task id: RISK-WP-0005-T03 -status: todo +status: done priority: high ``` @@ -118,11 +128,15 @@ tell the repos that carry re-score obligations. Custodian decision on the definition; the plumbing is ours. +Completed 2026-08-21 as a proposal; the definition is the custodian's. `docs/method/production-transition.md` defines it by **what is held rather than what was announced**: the first moment any system holds, processes or decides about real external data. Which means it can happen by accident — one real signup, one migrated contact list — and cannot be reversed by deleting the data afterwards. + +Declared by the custodian, never by this register: a risk register that unilaterally declares the estate to be in production has appointed itself. What the register does is notice and ask. Five re-scores, two acceptances ending and six dormant policies activating are listed as what fires on the day. + ### T04 — Stop claiming a surface we do not have ```task id: RISK-WP-0005-T04 -status: todo +status: done priority: medium ``` @@ -137,11 +151,13 @@ the wording rather than editing it. Small, and exactly the class of claim this register grades other repos down for: a stated control that is not there. +Completed 2026-08-21. `README.md` now says the repo serves nothing yet and that publication runs through `policy-nexus`, with three documents waiting for an address. `INTENT.md` is the constitutional document and its amendment is the custodian's — the wording is proposed to them rather than edited here. + ### T05 — Give escalation a delivery guarantee ```task id: RISK-WP-0005-T05 -status: todo +status: done priority: medium ``` @@ -156,11 +172,13 @@ a re-raise once, per the adopted rule's "raised again once" clause. Not a weekly nag; the rule is explicit that repetition until someone answers is how the operator becomes the queue. +Completed 2026-08-21. Escalation carries a delivery state — `sent → seen → answered` — with `escalation_sent` beside it, and `make check` reports how long each has gone unacknowledged. At seven days it says so and the escalation is raised **once** more, per the adopted rule, after which the default applies and is recorded. This is the fix the register applied to its own inbox on 2026-08-19 and had not applied to the path that matters more. + ### T06 — Make a lie about stability impossible to miss ```task id: RISK-WP-0005-T06 -status: todo +status: done priority: medium ``` @@ -177,11 +195,13 @@ Two cheap defences: register for longer than the shortest rung by some margin, `make check` says so first, before anything else. +Completed 2026-08-21. Two defences against the rung lying: `checked_by` recorded on every check (`RISK_CHECKED_BY`, so an agent names itself rather than inheriting a unix login), and a **heartbeat** that prints before anything else in `make check` when nothing anywhere in the register has been checked for two days. A `1q` rung means "stable for a quarter" and "nobody looked for a quarter", and the heartbeat is what separates them. + ### T07 — A coverage model ```task id: RISK-WP-0005-T07 -status: todo +status: done priority: low ``` @@ -200,6 +220,10 @@ a maturity model, and not this repo assessing anyone. **If this task grows past a page it becomes its own workplan.** Coverage is a different problem from triage and should not quietly absorb this one. +Completed 2026-08-21, minimal and within its escape clause. `tools/coverage.py`, behind `make coverage`, counts what the register has heard from. First run: **7 of 117 registered repos have ever appeared in a finding; 110 never have.** + +That is not 110 clean repos and the report says so — it is 110 repos the register knows nothing about, against an estate whose own evidence is that looking tends to find something. Recorded as an update to `RISK-N-0003` rather than promoted: there is still no owner for estate-wide detection and still no defect to route. What changed is that the gap has a size, which is the difference between an argument and a measurement. + ## Non-goals - **No monitoring.** `RISK-N-0003` stands as a note. A register that grows