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 <noreply@anthropic.com>
230 lines
11 KiB
Markdown
230 lines
11 KiB
Markdown
---
|
||
id: RISK-F-0003
|
||
type: finding
|
||
title: "ops-warden agent read-boundary does not fire on ungraded catalog lanes"
|
||
status: mitigated
|
||
reported_by: ops-warden
|
||
reported_via: ops-warden
|
||
routed_by: ops-warden
|
||
date_reported: "2026-08-19"
|
||
system: ops-warden
|
||
environment: production
|
||
fix_owner: ops-warden
|
||
fix_tracking: WARDEN-WP-0032-T05 (done) / T06 (structural)
|
||
# Graded by risk-nexus 2026-08-19 — docs/rulings/2026-08-19-first-grading.md
|
||
severity: medium
|
||
severity_at_production: medium
|
||
severity_superseded: "high (2026-08-19) — the CLI-layer gap is mitigated"
|
||
impact: I4
|
||
likelihood: L2
|
||
fidelity_modifier: false
|
||
production_rescore: false
|
||
disclosure: embargoed
|
||
embargo_condition: "RISK-F-0009 resolved — the OpenBao deny set covers every high-risk lane with a KV path"
|
||
embargo_since: "2026-08-19"
|
||
embargo_review: "2026-09-18"
|
||
escalation: none
|
||
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
|
||
|
||
> **Reporter update 2026-08-19 — exposure closed, structural fix outstanding.**
|
||
> All 14 ungraded lanes now carry an explicit `risk` grade (17 high, 10
|
||
> standard, 0 ungraded) and the read-boundary fires on them; verified exit 7
|
||
> with `WARDEN_AGENT_ID` set. The rule is recorded as ops-warden `ADR-0007`.
|
||
> **Still open:** nothing yet prevents a *new* lane being added ungraded
|
||
> (`WARDEN-WP-0032-T06`), and it remains unverified whether OpenBao's
|
||
> `agent-high-risk-boundary` policy covers these paths. `status` moved to
|
||
> `mitigated` by the reporter; severity remains risk-nexus's to set.
|
||
|
||
## What is true
|
||
|
||
`ADR-0004` (ops-warden) states that high-risk lanes refuse raw value streaming
|
||
to agent sessions. It is implemented in `src/warden/cli.py:1245`:
|
||
|
||
```python
|
||
if raw_value_stream and entry.is_high_risk and agent_id:
|
||
```
|
||
|
||
and `is_high_risk` is defined in `src/warden/routing/models.py:123` as:
|
||
|
||
```python
|
||
return self.risk == "high"
|
||
```
|
||
|
||
`risk` is an **optional** field on a catalog entry. Of the 27 entries in
|
||
`registry/routing/catalog.yaml`, **14 carry no `risk` value at all**. For those,
|
||
`is_high_risk` is `False`, so the boundary never fires regardless of what the
|
||
lane actually vends.
|
||
|
||
Five of the fourteen are `exec_capable: true`, meaning `warden access --fetch`
|
||
can proxy a real value for them:
|
||
|
||
| Lane | Owner | Status |
|
||
| --- | --- | --- |
|
||
| `openbao-api-key` | railiance-platform | active |
|
||
| `whynot-design-npm-publish` | railiance-platform | active |
|
||
| `key-cape-oidc-login` | key-cape | active |
|
||
| `issue-core-ingestion-api-key` | railiance-platform | active |
|
||
| `reuse-surface-hub-write-token` | railiance-platform | active |
|
||
|
||
For these five, an agent session with `WARDEN_AGENT_ID` set can stream a secret
|
||
value to stdout, which is the disclosure pattern of 2026-07-16 that WP-0026 and
|
||
`ADR-0004` were written to prevent. The control is not bypassed; it is simply
|
||
never reached.
|
||
|
||
## Why it matters beyond these five
|
||
|
||
The failure mode is **omission, not error**. Adding a lane without a `risk`
|
||
value produces a lane outside the boundary, silently, with no warning at load
|
||
and nothing in CI that notices. Every future lane inherits this default. The
|
||
nine other ungraded lanes are not `exec_capable` today, but `exec_capable` is
|
||
itself an editable field.
|
||
|
||
`ADR-0004` reads as a categorical rule. The implementation is an opt-in list.
|
||
|
||
## Exposure — stated only as far as ops-warden can support it
|
||
|
||
- ops-warden is the affected system and owns the catalog, the ADR, and the code.
|
||
- `WARDEN_AGENT_ID` is set by agent harnesses; ops-warden has not enumerated
|
||
which sessions currently set it, so the population actually exposed is
|
||
**not established** and should not be assumed to be zero or large.
|
||
- There is a second, independent layer for OpenBao-backed lanes: the OpenBao
|
||
policy `agent-high-risk-boundary` denies data-read for agent tokens on the
|
||
paths it covers. Whether it covers these five paths **has not been verified
|
||
by ops-warden** and should be checked against the live policy rather than
|
||
inferred — the paths were graded by the same omission this finding is about.
|
||
- No evidence of an actual disclosure through these lanes has been sought or
|
||
found. This is a control gap, not a known incident.
|
||
|
||
## How it was found
|
||
|
||
Doing `ZONE-WP-0001-T02` (zone-engine) — partitioning the estate for the
|
||
security-zone model. Grading every lane on `risk` was a prerequisite for
|
||
deriving zone membership, and the count of ungraded lanes is what surfaced it.
|
||
|
||
## Suggested direction, not a prescription
|
||
|
||
Two candidate fixes, both ops-warden's to choose between:
|
||
|
||
1. **Invert the default.** Treat absent `risk` as high, so a new lane is
|
||
protected until deliberately graded down. Fails safe; may be noisy.
|
||
2. **Make `risk` required**, enforced at catalog load and in CI, so an ungraded
|
||
lane cannot be committed. Fails loud; requires grading 14 lanes now.
|
||
|
||
Either way the grading is real work with real judgement in it, and the five
|
||
`exec_capable` lanes are the ones that matter first.
|
||
|
||
**Update 2026-08-19 — a third direction, and the one being taken.** The operator
|
||
has directed that the default derive from **maturity context**: in an early or
|
||
experimental context an absent grade is tolerable and explicitly accepted; in a
|
||
production context an absent grade resolves to `high`, or `critical` where the
|
||
context is critical. This validates against the data here — all five exposed
|
||
lanes are owned by production-serving components, so the rule would have caught
|
||
every one.
|
||
|
||
The durable form of that is a zone-model question (`ZONE-WP-0001-T03`), because
|
||
the `M0`-`M3` maturity ladder that would drive it has no join to catalog lanes
|
||
today. That work is months out, so the two are being run separately: the five
|
||
lanes are graded explicitly now under `WARDEN-WP-0032-T05`, and the structural
|
||
fix that makes absence impossible follows under `WARDEN-WP-0032-T06`.
|
||
|
||
Note for whoever sets severity: `.repo-classification.yaml` `category` is **not**
|
||
usable as the maturity signal. `railiance-platform`, which runs production
|
||
OpenBao and owns three of the five lanes above, is `category: tooling`.
|
||
|
||
## Related
|
||
|
||
- ops-warden `ADR-0004` — high-risk lanes refuse raw value streaming
|
||
- ops-warden `WP-0026` — disclosure hygiene, safe fetch transports
|
||
- `wiki/playbooks/agent-read-boundary.md`
|
||
- `RISK-F-0002` — ops-warden sign ungated (same system, different control)
|
||
- `zone-engine` `ZONE-WP-0001-T02` — the work that surfaced this
|
||
|
||
## Register ruling — 2026-08-19
|
||
|
||
`high` (`I4` × `L2`), embargoed until the five `exec_capable` lanes are graded
|
||
under `WARDEN-WP-0032-T05`, no escalation.
|
||
|
||
**Graded on its worst lane.** Four of the five are `I3`-shaped — a publish
|
||
token, an OIDC login, an ingestion key, a hub write token, each bad inside its
|
||
own system. `openbao-api-key` is not: it reaches the store the estate's other
|
||
secrets live in, and a disclosure there propagates past every boundary the
|
||
others respect.
|
||
|
||
**Likelihood `L2`** — an agent session with `WARDEN_AGENT_ID` is a foothold the
|
||
estate does grant. The register holds the line this finding drew: the exposed
|
||
population is not established, so it is neither assumed zero nor assumed large,
|
||
and the unverified OpenBao `agent-high-risk-boundary` coverage is a review
|
||
question rather than an assumption in either direction. Confirming that
|
||
coverage for these five paths would lower the grade.
|
||
|
||
**No fidelity modifier, and it was close.** `ADR-0004` reads categorical while
|
||
the implementation is opt-in, so the documentation asserts a boundary that does
|
||
not exist for 14 lanes. The modifier is scoped to attestations a system
|
||
produces at runtime, and documentation drift is not that. But if any compliance
|
||
evidence anywhere cites `ADR-0004` as implemented, that *is* a false
|
||
attestation and a separate finding. This register has not looked, and says so
|
||
rather than assuming.
|
||
|
||
**No escalation.** Known, owned, tracked (`WARDEN-WP-0032-T05` for the five
|
||
lanes, `WARDEN-WP-0032-T06` for the structural fix), and moving. Severity alone
|
||
deliberately does not reach the operator.
|
||
|
||
**On the maturity-context direction**, which is the operator's and not
|
||
re-decided here: the note that `.repo-classification.yaml` `category` cannot
|
||
carry the signal is confirmed from this repo's own registration work today.
|
||
`railiance-platform` runs production OpenBao and owns three of the five lanes
|
||
while classified `tooling`; `risk-nexus` classified itself with the same
|
||
vocabulary an hour ago. The maturity ladder needs its own field, and that is
|
||
`ZONE-WP-0001-T03`'s to build.
|
||
|
||
Reasoning: `docs/rulings/2026-08-19-first-grading.md`.
|
||
|
||
## Reviews
|
||
|
||
- **2026-08-19** — graded. Next review 2026-09-18 (`high` → 30 days).
|
||
Open at review: OpenBao policy coverage of the five paths; whether
|
||
`WARDEN-WP-0032-T05` has landed; whether any compliance evidence cites
|
||
`ADR-0004` as implemented.
|
||
|
||
## Check — 2026-08-20: mitigated on one layer, `high` → `medium`
|
||
|
||
`ops-warden` reports all 17 high-risk lanes now exiting 7 on
|
||
`warden access --fetch` with `WARDEN_AGENT_ID` set, verified under
|
||
`WARDEN-WP-0032-T05`/`T06`. The 14 ungraded lanes are graded; the boundary
|
||
fires. The live gap this finding described is closed on the CLI layer.
|
||
|
||
**Not clean, so the cadence stays at `instant`.** Something moved, and under
|
||
the ladder that resets the clock rather than earning a longer one. The next
|
||
check is the one that can start climbing.
|
||
|
||
**`high` → `medium`.** What remains is the omission shape rather than the
|
||
instance: whether a future lane can still be added without a grade. That is
|
||
`WARDEN-WP-0032-T06`, and the durable form of the operator's maturity-context
|
||
rule is `zone-engine`'s (`RISK-N-0004`). Not `low`, because "graded by hand
|
||
once" is not the same as "cannot be ungraded again".
|
||
|
||
**The embargo condition changed rather than being met.** The original condition
|
||
— the five `exec_capable` lanes graded — is satisfied. But `RISK-F-0009` shows
|
||
the *second* layer, the OpenBao deny set that protects the direct `bao kv get`
|
||
path, covers 6 of 17 high-risk lanes. Publishing this finding now would name
|
||
lanes that are still reachable by the path this control exists to close. The
|
||
condition is therefore re-pointed at `RISK-F-0009`.
|
||
|
||
That is the disclosure rule working across two findings rather than one: what
|
||
matters is whether the text shortens a path, not whether this particular
|
||
finding's own fix has landed.
|
||
|
||
**Verification limit, recorded rather than assumed:** the mitigation is
|
||
`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.
|