risk-nexus/findings/RISK-F-0001-flex-auth-unauthenticated-check.md
tegwick a05ca6822b 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 <noreply@anthropic.com>
2026-08-21 08:34:30 +02:00

11 KiB
Raw Blame History

id type title status reported_by reported_via routed_by date_reported system environment fix_owner fix_tracking severity severity_at_production severity_superseded impact likelihood fidelity_modifier production_rescore disclosure publication publication_id publication_path publication_subtitle revision last_reviewed review_interval embargo_lifted embargo_was_since escalation escalation_trigger escalation_status date_fixed last_checked next_check cadence clean_streak waiting_on graded_by ruling
RISK-F-0001 finding flex-auth /v1/check authenticates no caller fixed flex-auth rapp-postgres rapp-postgres 2026-08-17 flex-auth production flex-auth FLEX-WP-0015 (finished 2026-08-19) high high critical (2026-08-19, graded on L3 before reading the inbox) I4 L2 false false public requested risk-f-0001-flex-auth-unauthenticated-check findings/flex-auth-unauthenticated-check/v1/index.html The estate's authorization oracle authenticated no caller for as long as the endpoint existed. Found by reading, not by monitoring; fixed in two days. graded-1 2026-08-20 6m 2026-08-19 — FLEX-WP-0015 finished, live probes return 401 2026-08-19 withdrawn 1 withdrawn-before-sending 2026-08-19 2026-08-20T20:44:46Z 2026-08-20T20:44:46Z instant 0
who what since would_change default default_at
policy-nexus publication.json entries for RISK-F-0001, RISK-F-0008 and the five public method documents 2026-08-20 publication: published plus the permanent URL comes back onto each record the findings stay disclosure: public with no address, which the register records as a claim rather than a publication 2026-09-17
risk-nexus RISK-RULING-2026-08-19

RISK-F-0001 — flex-auth authenticates no caller on the decision surface

What is true

POST /v1/check and POST /v1/batch_check authenticate no caller. Any workload with network reach to the ClusterIP Service can assert any subject and any tenant and receive an authoritative allow.

flex-auth is the estate's authorization oracle. Every service that delegates a decision to it is relying on an answer that anyone able to reach the pod can obtain for any identity they care to name.

Self-reported by flex-auth as A0 on their own inbound surface, in their Tenancy Posture review. Their words: "flex-auth is the estate's authorization oracle and it trusts its callers completely."

How it was found

Not by a probe, an incident, or an alert. By flex-auth assessing themselves against the Tenancy Posture A ladder during a review they were asked to do — and their own note says they did not know they were carrying it.

That provenance matters for triage: nothing was watching for this, and nothing would have found it. It has presumably been true for as long as the endpoint has existed.

Exposure, as far as the reporter stated it

  • The Service is ClusterIP, so reach requires a workload inside the cluster.
  • No claim was made that network policy restricts which workloads can reach it, and this record does not assume one. If a default-deny NetworkPolicy fronts the service, that materially changes the exposure and should be verified rather than inferredflex-auth did not state it either way, and I have not checked, because doing so would be reporting on a system I do not own.

What makes it worse than a single service's defect

A false allow from this endpoint is not confined to flex-auth. It is the answer other services act on. tenant-engine separately reports that its own mutations are authorized by flex-auth and that direct authority over its rows would mean "privilege escalation across NetKingdom rather than data tampering confined to one store". The same reasoning applies to a forged allow.

Owner and state

flex-auth owns the fix and has tracked it as FLEX-WP-0015-T02, to ship through the staged-promotion path rather than a direct apply. They classify it as the only urgent item of their five follow-ups. Nothing is asked of them by this record beyond what they have already committed to.

What this repo is asked to decide

  1. Severity. Not the reporter's to set.
  2. Disclosure. Build mode is currently public-by-default, and this is precisely the class of finding where that stops being obviously right — a live authorization bypass in the service every other service trusts. The controlled-disclosure scheme this repo anticipates does not exist yet, so the choice today is publish or hold, with no mechanism between them.
  3. Escalation. Whether this reaches the operator personally. The candidate triggers in INTENT include "anything exposing real tenant data" — this exposes the decision that governs access to it, which may or may not be the same thing, and that judgement is this repo's.

Three further defects surfaced from the same review round. They are recorded here so they are visible, not filed as findings, because filing them was not asked for:

  • tenant-engineevents() returns the entire event log unfiltered. A live cross-tenant read at E2.
  • audit-core — read path applies no tenant filter; a credential with may_read can read any tenant's events. Bounded by deployment (may_read: false on the production sender) and not by code. Tracked AUDIT-WP-0008-T04.
  • apps-pg (railiance-platform) — no backup configured at all: no barmanObjectStore, no retention policy, BestEffort QoS. R0 there means no recovery, not merely no erasure policy.

All four were found the same way, by repos reading their own code against a ladder, within a day of each other. That is a fact about the estate's observability worth carrying into triage: four live defects, none found by monitoring.

Register ruling — 2026-08-19

critical (I4 × L3, no fidelity modifier), embargoed until FLEX-WP-0015-T02 ships, escalated to the operator on trigger 1.

The question this finding put — whether governing access to tenant data counts as exposing it — is answered yes. An authorization oracle that can be forged is not one step removed from the data; it is the step.

Impact is I4 because a forged allow does not stay here: it is the answer other services act on, and tenant-engine has stated what direct authority over its rows would mean. Likelihood is L3 — the normal working set, inside the cluster, no additional step — and the register follows the reporter in neither assuming a default-deny NetworkPolicy nor assuming its absence.

No fidelity modifier: this endpoint answers honestly about a caller it never checked. The false-record hazard lives in RISK-F-0002's constraint, where a consumer of this endpoint would begin signing records asserting an authorization that was never made. That constraint binds this finding's remediation: ops-warden's policy.enabled must not be turned on until /v1/check authenticates its callers, and the ordering is

flex-auth warn-only -> ops-warden gate presents its SA token -> logs clean
                    -> flex-auth fail-closed -> ops-warden policy.enabled: true

Nothing further is asked of flex-auth beyond what they have committed to, except one fact only they can supply: is there a default-deny NetworkPolicy in front of the Service? It is the single fact that would most change this grade, and it is the first question at review.

Reasoning: docs/rulings/2026-08-19-first-grading.md.

Reviews

  • 2026-08-19 — graded. Next review 2026-08-26 (critical → 7 days). Open at review: the NetworkPolicy question; whether FLEX-WP-0015-T02 has moved; whether the embargo still holds.

Re-grade and close — 2026-08-19 (same day)

Corrected from critical to high, and closed as fixed. Both changes come from messages that were already in this repo's inbox when the first grade was set. The register graded before it read them.

The exposure was narrower than graded. flex-auth answered the NetworkPolicy question on 2026-08-18: both production Deployments ship a NetworkPolicy in the same manifest, and it is narrower than default-deny — ingress restricted to one namespaceSelector plus one podSelector on port 8080, egress empty. In force since before the period the A0 describes. So the reachable set was never "any pod in the cluster"; it was the single paired workload per Deployment.

That is L2, not L3. Impact stays I4 — what a forged allow reaches does not change — so the grade is high. flex-auth also corrected their own earlier phrasing to ops-warden in the same message, unprompted, and that correction is why the fact reached this register at all.

Three caveats flex-auth asked to be recorded rather than taken from them, and they are why the grade did not fall further: label selectors are network position, not identity; the policy could not bind the asserted resource.system, which is what made cross-system impersonation possible; and enforcement depends on a CNI they could not verify from a cluster where kubectl returned Unauthorized. They said so rather than letting a manifest stand in for a probe.

It is fixed. On 2026-08-19 flex-auth reported both production Deployments enforcing ADR-0004 TokenReview, with live unbound-request probes returning 401 rather than a decision, on both the user-engine and tenant-engine pins. FLEX-WP-0015 is finished and tenancy.current.A is 2. That is a probe against the running system, which is the standard docs/method/review.md sets for closing: something concrete read, not something been told.

Disclosure flips to public. The embargo condition was "FLEX-WP-0015-T02 ships to production" and it is met. Handover to policy-nexus is the next step and is not done yet.

The escalation is withdrawn without being sent. It was pending-operator for roughly four hours, and the fix landed first. Withdrawing it is correct — escalating a fixed defect makes the operator the queue for history — but the register does not get to be pleased about it. The escalation would have been sent on facts that were already stale, and only luck put the fix on the same day.

What this cost, recorded because it is the register's own defect. The NetworkPolicy answer arrived 2026-08-18. The fix notice arrived 2026-08-19 at 12:35. The first grading ran at 21:14 the same day, on neither. Reading the inbox is now step 0 of grading and question 0 of every review — see docs/method/review.md — and this finding is the case that bought it.

Reviews

  • 2026-08-19 — re-graded high, closed fixed, disclosure public, escalation withdrawn. Remaining: handover to policy-nexus; the CNI enforcement question is flex-auth's and no longer this finding's.
  • 2026-08-20 — not clean: Publication handover requested; publication front-matter applied and the wait on policy-nexus typed. Cadence instant → instant; checked again immediately.