diff --git a/REGISTER.md b/REGISTER.md index fc14f38..86a6174 100644 --- a/REGISTER.md +++ b/REGISTER.md @@ -2,12 +2,16 @@ Generated by `tools/register_index.py` from `findings/`. Do not edit by hand. Last built 2026-08-19. -3 open of 3 findings; 0 notes below the floor. +7 open of 7 findings; 2 notes below the floor. ## Findings | ID | Finding | System | Severity | Disclosure | Escalation | Fix owner | Status | Review by | | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| [RISK-F-0007](findings/RISK-F-0007-unverified-tenant-boundary.md) | No consumer's tenant boundary is verified anywhere | estate | **high** | embargoed | **required** (t4, pending-operator) | unset | open | 2026-09-18 | +| [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 | **required** (t3, pending-operator) | railiance-platform | open | 2026-09-18 | +| [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 | embargoed | none | audit-core | open | 2026-11-17 | +| [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 | 2026-09-18 | | [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 | **high** | embargoed | none | ops-warden | open | 2026-09-18 | | [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 | **required** (t6, pending-operator) | ops-warden | open | 2026-11-17 | | [RISK-F-0001](findings/RISK-F-0001-flex-auth-unauthenticated-check.md) | flex-auth /v1/check authenticates no caller | flex-auth | **critical** | embargoed | **required** (t1, pending-operator) | flex-auth | open | 2026-08-26 | @@ -26,10 +30,23 @@ Held from publication with a stated condition. A hold with no moving condition i | Finding | Since | Lifts when | Re-decided | | --- | --- | --- | --- | +| RISK-F-0007 | 2026-08-19 | a verification exists for at least one consumer boundary | 2026-09-18 | +| RISK-F-0006 | 2026-08-19 | a backup exists and a restore has been demonstrated once | 2026-09-18 | +| RISK-F-0005 | 2026-08-19 | AUDIT-WP-0008-T04 lands a tenant filter in the read path | 2026-11-17 | +| RISK-F-0004 | 2026-08-19 | the read path filters by tenant in code | 2026-09-18 | | RISK-F-0003 | 2026-08-19 | the five exec_capable lanes graded under WARDEN-WP-0032-T05 | 2026-09-18 | | RISK-F-0002 | 2026-08-19 | FLEX-WP-0015-T02 shipped and ops-warden policy.enabled true in production | 2026-11-17 | | RISK-F-0001 | 2026-08-19 | FLEX-WP-0015-T02 ships to production | 2026-08-26 | +## Notes (below the floor) + +Seen, deliberately not findings. Not graded, not reviewed, not published. + +| ID | Note | Why below the floor | +| --- | --- | --- | +| [RISK-N-0002](notes/RISK-N-0002-erasure-versus-audit.md) | Erasure duty and audit immutability have not been reconciled | no obligation exists yet — the estate holds no real person's data, so there is nothing to erase and no counterparty to owe it to | +| [RISK-N-0001](notes/RISK-N-0001-noisy-neighbour-uncharacterised.md) | Noisy-neighbour behaviour is uncharacterised | no decision changes today — no tenant shares a saturating workload, and what is missing is measurement work, not a defect to route | + ## How to read this Severity is `docs/method/severity.md`; disclosure `docs/method/disclosure.md`; diff --git a/docs/method/escalation.md b/docs/method/escalation.md index c261c79..4304a07 100644 --- a/docs/method/escalation.md +++ b/docs/method/escalation.md @@ -134,15 +134,48 @@ Stated so the rule bites in both directions. ## Testing the rule against the register -The draft was tested against all three open findings before it was proposed; -the rulings are in `docs/rulings/2026-08-19-first-grading.md`. +The draft was tested against the first three findings before it was proposed +(`docs/rulings/2026-08-19-first-grading.md`) and then against the four filed +out of the backlog the same day +(`docs/rulings/2026-08-19-second-grading.md`). -Summary: `RISK-F-0001` escalates on trigger 1. `RISK-F-0002` does not escalate -on triggers 1-5 — agreeing with the reporter's own reading — but the -`RISK-F-0001`/`RISK-F-0002` ordering constraint escalates once on trigger 6, -which is the disagreement the rule was worth writing to surface. `RISK-F-0003` -does not escalate; it is known, owned, tracked, and moving. +| Finding | Escalates | Trigger | +| --- | --- | --- | +| `RISK-F-0001` flex-auth oracle | yes | 1 — governs access to tenant data | +| `RISK-F-0002` signing gate off | yes, once | 6 — ordering hazard, jointly with `RISK-F-0001` | +| `RISK-F-0003` ungraded lanes | no | known, owned, tracked, moving | +| `RISK-F-0004` tenant-engine reads | no | no real tenant data yet | +| `RISK-F-0005` audit-core reads | no | known, owned, tracked, moving | +| `RISK-F-0006` apps-pg no backup | yes | 3 — recurring spend nobody can self-authorise | +| `RISK-F-0007` unverified boundary | yes | 4 — estate-wide and unowned | -One escalation, covering two findings, out of three findings. That ratio is -the rule doing its job. If it ever reaches "most findings", the rule is wrong, -not the register. +`RISK-F-0002` is the case worth reading: `ops-warden` judged it did not need +the operator, the register agrees on triggers 1-5, and it escalates anyway on +6. That disagreement is the reason the rule was worth writing down. + +## The ratio, and what it would mean + +Four of seven escalate. That is more than this rule should produce in steady +state, and it is not evidence the rule is loose — it is what a **first sweep** +looks like. Three of the four are the backlog: items that sat outside the +register precisely because nobody had decided them, and undecided items are +exactly the population that needs the person who can decide. + +The test therefore applies to intake, not to sweeps: + +> If more than one in three **newly reported** findings escalates over any +> rolling ninety days, the rule is wrong, not the register. + +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. + +## Escalations are batched + +Four escalations are one conversation, not four interruptions. Open items go +to the operator together, in register order, each with its own decision and +its own default-if-no-answer. Escalating is not the same as escalating +separately, and a rule that produces four messages in an afternoon has made the +operator the queue by a different route. + +`make check` lists everything awaiting an answer, so the batch is a report and +not somebody's recollection. diff --git a/docs/method/severity.md b/docs/method/severity.md index 0199e52..f67a5f4 100644 --- a/docs/method/severity.md +++ b/docs/method/severity.md @@ -108,15 +108,37 @@ Every finding is graded twice: - `severity_at_production` — the same impact, with likelihood re-read for a system carrying real users and real tenant data. -Build mode is a legitimate likelihood input. "No consumer holds real data -here yet" genuinely lowers reach. It is *not* an impact input: what the defect -would do on the day it is exercised does not change because the calendar has -not reached production. +Build mode legitimately lowers **both** axes, for different reasons: +likelihood, where the reach itself depends on a production deployment that has +not happened; and impact, where the data that would be exposed does not exist +yet. What it must never lower is `severity_at_production` — the defect does +not improve because the calendar has not reached it. + +> *Amended 2026-08-19 (`RISK-WP-0001-T07`).* This paragraph originally said +> build mode was a likelihood input and never an impact one. Grading the +> unverified tenant boundary broke that: what build mode changes there is the +> consequence of an occurrence, not the reach of it. The instrument was wrong +> on first hard use and is corrected rather than worked around. Where the two grades differ, the production transition is a mandatory re-score. `docs/method/review.md` binds the review date to it, so the re-score is a scheduled event and not somebody's memory. +## Non-adversarial findings + +Likelihood is written as reach because most findings are about someone getting +somewhere. Where a finding is about loss, corruption or outage — no backup, no +recovery path, an eviction-prone deployment — there is no attacker to model. + +For those, likelihood reads as **the chance of the triggering event inside one +review interval**: `L1` would be surprising, `L2` is an ordinary failure the +estate has seen before, `L3` is expected in the normal course of running, +`L4` is already happening. Impact is unchanged: what is lost, and whether it +comes back. + +> *Added 2026-08-19 (`RISK-WP-0001-T07`).* Forced by `RISK-F-0006`, where the +> defect is an absent backup and the reach reading produced nonsense. + ## The floor `INTENT.md`: if a finding would not change anyone's decision, it is a note, diff --git a/docs/rulings/2026-08-19-second-grading.md b/docs/rulings/2026-08-19-second-grading.md new file mode 100644 index 0000000..986d9f7 --- /dev/null +++ b/docs/rulings/2026-08-19-second-grading.md @@ -0,0 +1,100 @@ +--- +id: RISK-RULING-2026-08-19-B +type: ruling +title: "What was waiting outside the register: four findings, two notes" +status: recorded +owner: risk-nexus +date: "2026-08-19" +workplan: RISK-WP-0001-T07 +--- + +# What was waiting outside the register — 2026-08-19 + +`INTENT.md` named six things as visible but unfiled, held back "pending this +repo setting the precedent for what warrants a record of its own". The +precedent now exists (`docs/method/severity.md`), so each is ruled here. "Still +waiting on the precedent" is no longer an available answer. + +| Item | Ruling | Why | +| --- | --- | --- | +| `tenant-engine` unfiltered event read | `RISK-F-0004`, high | Live cross-tenant read, owner can act, ordering changes | +| `audit-core` unfiltered read path | `RISK-F-0005`, medium | Bounded by a deployment flag, not by code | +| `apps-pg` no backup | `RISK-F-0006`, high, escalated | Unrecoverable loss; needs spend nobody can self-authorise | +| Unverified tenant boundary | `RISK-F-0007`, high, escalated | Estate-wide, unowned, two confirmed instances | +| Noisy neighbours uncharacterised | `RISK-N-0001`, note | Measurement work, no decision changes today | +| Erasure versus audit | `RISK-N-0002`, note | No obligation exists yet; regulatory-intake work | + +Four in, two out. If everything had cleared the floor, the floor would not be +one. + +## The three from RISK-F-0001's unfiled list + +They were recorded inside another finding "so they are visible, not filed as +findings, because filing them was not asked for". Filing is now asked for. + +Each is filed on the owning repo's own self-report, unverified by this repo, +and says so in its own text. `risk-nexus` reads from everywhere and is +downstream by construction; the system remains authoritative about itself. +Each owner has been asked to confirm or correct, and two of the three +(`RISK-F-0004`, `RISK-F-0006`) have `fix_tracking: unset`, which is a question +put to the owner rather than a criticism. + +`RISK-F-0006` is the surprise of the round. It is the least dramatic item on +the list — a missing configuration block — and it grades highest of the three, +because it is the only one where nothing comes back afterwards. It also +escalates, on spend, which is the trigger that most needed a real case to +prove it is not decorative. + +## The unverified tenant boundary + +`RISK-F-0007`, and the one this register was arguably built to hold. +`INTENT.md` calls it the estate's largest known gap and complains that it lives +as open question 3 of an unratified draft, with no owner, no severity and no +date. It now has three of the four; `fix_owner` stays `unset` deliberately, +because the escalation is precisely about who owns it. + +Two design points, both deliberate: + +- **It is not a duplicate of `RISK-F-0004` and `RISK-F-0005`.** Those are two + instances; this is the absence of anything that would have found them. The + instances get fixed by their owners and this one does not go away when they + do. +- **It grades `high` today and `critical` at production**, which is the first + real use of `production_rescore`. Today's consequence is bounded because the + estate holds no real tenant data. That bound is a fact about the calendar, so + it belongs in the build-mode column and not in a comfortable headline. + +## What the notes are for + +`RISK-N-0001` and `RISK-N-0002` are the floor doing its job: both are real, +both are known, neither changes a decision this month. The note keeps "we saw +it" without inflating a register that has to stay small enough to read. + +Both carry a `revisit` condition that is an event rather than a date, and both +events are ones the estate cannot miss — two tenants sharing a saturating +workload, and the first real person's data. Neither note is a promise to +remember; both are promises to be re-read when the condition arrives. + +## What this round did to the instruments + +Two amendments, both dated in `docs/method/severity.md`: + +1. **Build mode lowers impact as well as likelihood.** The original text said + it was a likelihood input and never an impact one. `RISK-F-0007` broke that + in one sentence: what build mode changes for an unverified boundary is the + consequence of an occurrence, not the reach of it. Corrected rather than + worked around. +2. **Non-adversarial findings get their own likelihood reading.** `RISK-F-0006` + has no attacker in it, and reading likelihood as "reach" produced nonsense. + Likelihood there is the chance of the triggering event inside one review + interval. + +An instrument that survives its first hard use unchanged has probably not been +used hard. + +## Register size + +Seven findings, six open, two notes. `INTENT.md` requires the register stay +small enough to read, and `REGISTER.md` is still one screen. The next thing +that would break it is a batch — a review round producing ten entries at once. +When that happens the answer is a harder floor, not a wider table. diff --git a/findings/RISK-F-0004-tenant-engine-unfiltered-event-read.md b/findings/RISK-F-0004-tenant-engine-unfiltered-event-read.md new file mode 100644 index 0000000..5539f97 --- /dev/null +++ b/findings/RISK-F-0004-tenant-engine-unfiltered-event-read.md @@ -0,0 +1,88 @@ +--- +id: RISK-F-0004 +type: finding +title: "tenant-engine events() returns the entire event log unfiltered" +status: open +reported_by: tenant-engine +reported_via: flex-auth +routed_by: risk-nexus +date_reported: "2026-08-17" +date_filed: "2026-08-19" +system: tenant-engine +environment: production +fix_owner: tenant-engine +fix_tracking: unset +related: [RISK-F-0001] +# Graded by risk-nexus 2026-08-19 — docs/rulings/2026-08-19-second-grading.md +severity: high +severity_at_production: critical +impact: I3 +likelihood: L3 +fidelity_modifier: false +production_rescore: true +disclosure: embargoed +embargo_condition: "the read path filters by tenant in code" +embargo_since: "2026-08-19" +embargo_review: "2026-09-18" +escalation: none +last_reviewed: "2026-08-19" +review_by: "2026-09-18" +graded_by: risk-nexus +ruling: RISK-RULING-2026-08-19-B +--- + +# RISK-F-0004 — the tenant event log is readable across tenants + +## What is true, as reported + +`tenant-engine`'s `events()` returns the entire event log with no tenant +filter. Reported by `tenant-engine` as a live cross-tenant read at `E2` on the +Tenancy Posture E ladder, surfaced in the same review round as `RISK-F-0001` +and recorded inside that finding as visible-but-unfiled. + +**This repo has not verified it and does not own the code.** It is filed on the +owning repo's own self-report. `tenant-engine` confirms or corrects it. + +## Why it is being filed now + +It was held back pending the precedent this register set for what warrants a +record of its own. That precedent now exists (`docs/method/severity.md`), and +"still waiting on the precedent" has stopped being an available answer. This +finding clears the floor on both tests: `tenant-engine` can act, and recording +it changes when they act. + +## What is not established + +- Whether any caller other than `tenant-engine` itself currently reaches + `events()`. +- What the log contains — whether the rows are metadata or carry tenant + payload. The grade assumes cross-tenant *visibility*, not payload + disclosure, and would rise if it is the latter. +- Whether a fix is tracked anywhere. `fix_tracking` is `unset` and this repo + has asked. + +## Register ruling — 2026-08-19 + +`high` today (`I3` × `L3`), `critical` at production, embargoed until the read +path filters in code, no escalation. + +`I3`: a cross-tenant read crosses a tenant boundary inside one system. `L3`: +no additional step is needed by anything that can already call the method, and +the authorization that would otherwise constrain the caller is `RISK-F-0001`, +which authenticates nobody. + +`production_rescore: true`. Today the log holds no real counterparty's events, +which lowers what one occurrence costs; it does not lower what the defect is. +At production the same read is `I4` — real tenant data crossing a boundary +that nothing verifies — and the re-score is owed before any production +declaration completes. + +No escalation: no real tenant data yet (trigger 1 reads *real*), owned, and +not yet stalled. It becomes an escalation on the day the estate takes real +tenant data, and that is what `production_rescore` is for. + +## Reviews + +- **2026-08-19** — filed and graded from `RISK-F-0001`'s unfiled list. + Open at review: does `tenant-engine` confirm; is a fix tracked; what does + the log actually contain. diff --git a/findings/RISK-F-0005-audit-core-unfiltered-read-path.md b/findings/RISK-F-0005-audit-core-unfiltered-read-path.md new file mode 100644 index 0000000..01d9f6b --- /dev/null +++ b/findings/RISK-F-0005-audit-core-unfiltered-read-path.md @@ -0,0 +1,77 @@ +--- +id: RISK-F-0005 +type: finding +title: "audit-core read path applies no tenant filter; the bound is deployment, not code" +status: open +reported_by: audit-core +reported_via: flex-auth +routed_by: risk-nexus +date_reported: "2026-08-17" +date_filed: "2026-08-19" +system: audit-core +environment: production +fix_owner: audit-core +fix_tracking: AUDIT-WP-0008-T04 +related: [RISK-F-0004] +# Graded by risk-nexus 2026-08-19 — docs/rulings/2026-08-19-second-grading.md +severity: medium +severity_at_production: high +impact: I3 +likelihood: L2 +fidelity_modifier: false +production_rescore: true +disclosure: embargoed +embargo_condition: "AUDIT-WP-0008-T04 lands a tenant filter in the read path" +embargo_since: "2026-08-19" +embargo_review: "2026-11-17" +escalation: none +last_reviewed: "2026-08-19" +review_by: "2026-11-17" +graded_by: risk-nexus +ruling: RISK-RULING-2026-08-19-B +--- + +# RISK-F-0005 — the audit read path is bounded by a flag, not by code + +## What is true, as reported + +`audit-core`'s read path applies no tenant filter. A credential holding +`may_read` can read any tenant's events. The production sender is deployed with +`may_read: false`, so the exposure today is bounded **by deployment +configuration and not by code**. Self-reported in the same review round as +`RISK-F-0001`; tracked by `audit-core` as `AUDIT-WP-0008-T04`. + +Not verified by this repo. `audit-core` owns it and confirms or corrects it. + +## Why the bound is the finding + +A defect held shut by a flag is one edit from open, and the edit does not look +dangerous to whoever makes it — enabling a read credential is an ordinary +operational act. The register carries it because the safety here lives in a +value someone can change without reading this file. + +That is also why it is filed separately from `RISK-F-0004` rather than as one +"cross-tenant reads" entry: different owner, different fix, and this one has a +tracked fix already. + +## Register ruling — 2026-08-19 + +`medium` today (`I3` × `L2`), `high` at production, embargoed until +`AUDIT-WP-0008-T04` lands, no escalation. + +`I3`: crossing a tenant boundary in the audit store. `L2`: it needs a +credential with `may_read`, which the estate grants somewhere but not to the +production sender. It is not `L3` because the flag is genuinely off today, and +not `L1` because the flag is a deployment value rather than an absent code +path. + +`production_rescore: true` for the same reason as `RISK-F-0004`: the audit log +does not hold a real counterparty's events yet. + +No escalation. Known, owned, tracked, moving. + +## Reviews + +- **2026-08-19** — filed and graded from `RISK-F-0001`'s unfiled list. + Open at review: has `AUDIT-WP-0008-T04` moved; is `may_read: false` still + true of every production credential, not only the sender. diff --git a/findings/RISK-F-0006-apps-pg-no-backup-configured.md b/findings/RISK-F-0006-apps-pg-no-backup-configured.md new file mode 100644 index 0000000..bb0aeab --- /dev/null +++ b/findings/RISK-F-0006-apps-pg-no-backup-configured.md @@ -0,0 +1,88 @@ +--- +id: RISK-F-0006 +type: finding +title: "apps-pg has no backup configured at all: R0 means no recovery" +status: open +reported_by: railiance-platform +reported_via: flex-auth +routed_by: risk-nexus +date_reported: "2026-08-17" +date_filed: "2026-08-19" +system: railiance-platform +environment: production +fix_owner: railiance-platform +fix_tracking: unset +related: [RISK-F-0001] +# Graded by risk-nexus 2026-08-19 — docs/rulings/2026-08-19-second-grading.md +severity: high +severity_at_production: critical +impact: I4 +likelihood: L2 +fidelity_modifier: false +production_rescore: true +disclosure: embargoed +embargo_condition: "a backup exists and a restore has been demonstrated once" +embargo_since: "2026-08-19" +embargo_review: "2026-09-18" +escalation: required +escalation_trigger: 3 +escalation_status: pending-operator +last_reviewed: "2026-08-19" +review_by: "2026-09-18" +graded_by: risk-nexus +ruling: RISK-RULING-2026-08-19-B +--- + +# RISK-F-0006 — the platform database cannot be recovered + +## What is true, as reported + +`apps-pg` (`railiance-platform`) has no backup configured at all: no +`barmanObjectStore`, no retention policy, and `BestEffort` QoS on the pod. +Reported in the same round as `RISK-F-0001`, whose author noted that `R0` here +means **no recovery**, not merely no erasure policy. + +Not verified by this repo. `railiance-platform` owns it. + +## Why it is graded highest of the round despite being the least exciting + +The other findings in this round are about someone reaching something. This one +is about nothing coming back. There is no attacker in it and no clever step — +an ordinary node failure, a lost volume, a mistaken `DROP`, and the data that +was in `apps-pg` is gone. + +`BestEffort` QoS makes the pod the first candidate for eviction under memory +pressure. Eviction alone is an availability event and recoverable. What is not +recoverable is anything that damages the volume, and that is the case with no +answer today. + +## Register ruling — 2026-08-19 + +`high` today (`I4` × `L2`, non-adversarial reading), `critical` at production, +embargoed, and **escalated on trigger 3 (spend)**. + +`I4`: `apps-pg` is shared platform infrastructure, so the loss is not confined +to one system, and it is unrecoverable rather than degraded. `L2` on the +non-adversarial reading the scale gained for this finding: irrecoverable loss +needs a volume or corruption event, which is an ordinary failure the estate +has seen the shape of but not an expected weekly occurrence. + +`production_rescore: true`: at production this is `L3`, because the surface +area of ordinary operational events grows with real traffic and real operators. + +**Escalation, trigger 3.** Object storage for backups is recurring spend that +is not currently committed, and no repo can authorise it for itself. The ask +is narrow: approve a backup target and its cost, or state that the estate +accepts running `apps-pg` with no recovery and for how long. The second is a +legitimate answer in build mode and needs saying out loud rather than +happening by default. + +The disclosure condition deliberately includes a demonstrated restore. A +backup nobody has restored from is a claim, not a control — the same class of +error as the gate in `RISK-F-0002`. + +## Reviews + +- **2026-08-19** — filed and graded from `RISK-F-0001`'s unfiled list. + Open at review: has the spend been ruled on; is a backup configured; has a + restore been demonstrated; does `railiance-platform` track it anywhere. diff --git a/findings/RISK-F-0007-unverified-tenant-boundary.md b/findings/RISK-F-0007-unverified-tenant-boundary.md new file mode 100644 index 0000000..623d8ea --- /dev/null +++ b/findings/RISK-F-0007-unverified-tenant-boundary.md @@ -0,0 +1,99 @@ +--- +id: RISK-F-0007 +type: finding +title: "No consumer's tenant boundary is verified anywhere" +status: open +reported_by: net-kingdom +reported_via: risk-nexus +routed_by: risk-nexus +date_reported: "2026-08-19" +date_filed: "2026-08-19" +system: estate +environment: build +fix_owner: unset +fix_tracking: unset +related: [RISK-F-0004, RISK-F-0005] +# Graded by risk-nexus 2026-08-19 — docs/rulings/2026-08-19-second-grading.md +severity: high +severity_at_production: critical +impact: I3 +likelihood: L3 +fidelity_modifier: false +production_rescore: true +disclosure: embargoed +embargo_condition: "a verification exists for at least one consumer boundary" +embargo_since: "2026-08-19" +embargo_review: "2026-09-18" +escalation: required +escalation_trigger: 4 +escalation_status: pending-operator +last_reviewed: "2026-08-19" +review_by: "2026-09-18" +graded_by: risk-nexus +ruling: RISK-RULING-2026-08-19-B +--- + +# RISK-F-0007 — nothing checks that tenants stay apart + +## What is true + +No consumer's tenant boundary is verified anywhere in the estate. This is +Broken Object Level Authorization, first on the OWASP API Security Top 10 +since that list launched. + +It is currently **open question 3 in NetKingdom's *Tenancy Posture*, which is a +draft and not a register** — which is precisely why `INTENT.md` names it as the +thing that had nowhere to live. Filing it here does not amend that standard, +does not ratify it, and does not settle the question it poses. A finding may +say a standard is wrong; it cannot change one. + +## Why it is a finding and not a research note + +The floor asks whether an owner could act and whether recording it changes a +decision. Both are yes, and the second is now demonstrable rather than +asserted: every time a repo has looked at its own boundary this month, it has +found a defect. `RISK-F-0004` (`tenant-engine`, unfiltered event read) and +`RISK-F-0005` (`audit-core`, no tenant filter on read) are two instances found +by two repos in one round. + +An absent verification is not the same as an absent boundary. But the sample so +far is two for two, and the register does not get to assume the unexamined +cases are better than the examined ones. + +## What is not established + +- Whether any consumer boundary *is* in fact correct. Nobody has checked; that + is the finding. +- Who owns the fix. `fix_owner` is `unset` deliberately — see the escalation. + +## Register ruling — 2026-08-19 + +`high` today (`I3` × `L3`), `critical` at production, embargoed, **escalated on +trigger 4 (ownership)**. + +Today's impact is `I3` and not `I4` because there is no real tenant data behind +the unverified boundary yet; the consequence of one occurrence is bounded by +what the estate is currently holding. `L3`: no additional step is needed by +anything already inside, and the two confirmed instances say the reach is real +rather than theoretical. + +`production_rescore: true`, and this is the finding that matters most on that +day: at production the same defect is `I4` × `L3` — `critical` — and the +re-score is not optional. + +**Escalation, trigger 4.** This is the ownership case the trigger was written +for, in its purest form: the defect is estate-wide, no repo owns "every +consumer's boundary", and a routing exchange cannot settle it because there is +nobody to route it to. Only the operator can say who verifies tenant +boundaries — a repo, a test suite, a review obligation on each consumer — or +that the estate deliberately carries it unverified until production. + +Until that is answered, `fix_owner` stays `unset` rather than being assigned to +a repo that has not accepted it. An unowned finding with an honest `unset` is +better than a routed one nobody agreed to. + +## Reviews + +- **2026-08-19** — filed and graded. Open at review: has an owner been named; + have any further instances been found; is the Tenancy Posture question still + open. diff --git a/notes/RISK-N-0001-noisy-neighbour-uncharacterised.md b/notes/RISK-N-0001-noisy-neighbour-uncharacterised.md new file mode 100644 index 0000000..d4b2abf --- /dev/null +++ b/notes/RISK-N-0001-noisy-neighbour-uncharacterised.md @@ -0,0 +1,35 @@ +--- +id: RISK-N-0001 +type: note +title: "Noisy-neighbour behaviour is uncharacterised" +date: "2026-08-19" +source: "NetKingdom Tenancy Posture (draft), open question" +floor_reason: "no decision changes today — no tenant shares a saturating workload, and what is missing is measurement work, not a defect to route" +revisit: "when two tenants first share a saturating workload, or at the production transition" +ruling: RISK-RULING-2026-08-19-B +--- + +# RISK-N-0001 — noisy neighbours, uncharacterised + +Named in `INTENT.md` as waiting: NetKingdom's *Tenancy Posture* leaves the +noisy-neighbour behaviour of shared components uncharacterised — how one +tenant's load degrades another's service is not measured anywhere. + +**Ruled a note, not a finding, on 2026-08-19.** + +The floor asks two questions. *Could an owner act?* Yes — +`railiance-platform` could characterise it. *Does recording it change a +decision?* Today, no. Nothing in the estate currently has two tenants sharing a +workload heavy enough for the answer to matter, so a register entry would +change nobody's ordering and would sit there being read past. + +What is missing here is measurement, not remediation. That is work someone +schedules, not a defect the register routes — and the register is explicitly +not the place where undone work is logged. + +It comes back the day two tenants first share a saturating workload, or at the +production transition, whichever is first. If characterisation then shows one +tenant can degrade another, that is a finding with a severity, and this note is +its provenance. + +Filed as a note so that "we saw it" survives without inflating the register. diff --git a/notes/RISK-N-0002-erasure-versus-audit.md b/notes/RISK-N-0002-erasure-versus-audit.md new file mode 100644 index 0000000..3775861 --- /dev/null +++ b/notes/RISK-N-0002-erasure-versus-audit.md @@ -0,0 +1,38 @@ +--- +id: RISK-N-0002 +type: note +title: "Erasure duty and audit immutability have not been reconciled" +date: "2026-08-19" +source: "NetKingdom Tenancy Posture (draft), open question" +floor_reason: "no obligation exists yet — the estate holds no real person's data, so there is nothing to erase and no counterparty to owe it to" +revisit: "on the day the estate first holds a real person's data; belongs to the regulatory-intake workplan, not this one" +ruling: RISK-RULING-2026-08-19-B +--- + +# RISK-N-0002 — erasure versus audit + +Named in `INTENT.md` as waiting: an audit trail that must be immutable and an +erasure obligation that may require destroying what is in it cannot both be +satisfied naively, and the estate has not reconciled them. + +**Ruled a note, not a finding, on 2026-08-19.** + +This is a real design tension and it will need a real answer — whether key +destruction satisfies an erasure obligation is exactly the question +`INTENT.md` says was researched, used once, and discarded. + +It is a note today for one reason: there is no obligation. The estate holds no +real person's data, so nothing is owed to anyone. Escalation trigger 2 reads +"creates or reveals an obligation with an outside counterparty", and this +creates none yet. A finding needs a decision that changes, and today the +decision is "not yet". + +It is also not shaped like this register's work. It is a regulatory question +first and an architecture question second, and `INTENT.md` puts regulatory +intake in this repo but keeps "what the estate must therefore do" with the +owning repo. Reconciling it belongs to the regulatory-intake workplan +(`RISK-WP-0001` residual), not to finding triage. + +It comes back on the day the estate first holds a real person's data. That day +is also when it stops being a note, because from then on the obligation is real +and trigger 2 fires.