risk-nexus/findings/RISK-F-0004-tenant-engine-unfiltered-event-read.md

89 lines
3.2 KiB
Markdown
Raw Normal View History

---
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.