policy-nexus/build/findings/flex-auth-unauthenticated-check/v1/revisions/published-1/index.html
tegwick c1b60f322e
All checks were successful
Build and publish policy-nexus image / build-and-push (push) Successful in 1m10s
feat: publish Risk Nexus findings and methods
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a058f3-8ba0-7692-a042-9a870fc3d663
2026-09-01 01:56:46 +02:00

245 lines
27 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en"><meta charset="utf-8">
<meta name="policy-source-revision" content="a13d954f8597fd92201746e2d52f031a5a88d969">
<meta name="policy-source-digest" content="b2d1d0c526d5c9b5729b8353877bb9f29096667145f1498cfde73c9711206fda">
<title>flex-auth /v1/check authenticates no caller</title>
<style>
:root{
--paper:#EDEEF0; --surface:#F6F7F8; --surface-2:#E4E6E9;
--ink:#171D24; --ink-2:#4A5561; --ink-3:#737E8A;
--rule:#D3D7DC; --rule-strong:#B6BCC3;
--brass:#8A6A2E; --brass-soft:#EFE5CD; --brass-line:#C9AE74;
--clay:#8A3A2C; --clay-soft:#F2DFDA;
--l0:#DCE0E2; --l1:#B9C4C7; --l2:#8CA1A6; --l3:#567D84; --l4:#23555E;
--chip-fg:#F6F7F8;
--font-display:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
--font-body:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
--font-mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,monospace;
--measure:66ch;
}
@media (prefers-color-scheme:dark){
:root:not([data-theme="light"]){
--paper:#12161A; --surface:#191E24; --surface-2:#222831;
--ink:#E6E9EC; --ink-2:#A3ADB7; --ink-3:#78838E;
--rule:#2A3138; --rule-strong:#3B444D;
--brass:#C9A45C; --brass-soft:#33290F; --brass-line:#6B5426;
--clay:#D08A76; --clay-soft:#3A211B;
--l0:#262C32; --l1:#35424A; --l2:#4A626B; --l3:#6A939D; --l4:#97C4CD;
--chip-fg:#12161A;
}
}
:root[data-theme="dark"]{
--paper:#12161A; --surface:#191E24; --surface-2:#222831;
--ink:#E6E9EC; --ink-2:#A3ADB7; --ink-3:#78838E;
--rule:#2A3138; --rule-strong:#3B444D;
--brass:#C9A45C; --brass-soft:#33290F; --brass-line:#6B5426;
--clay:#D08A76; --clay-soft:#3A211B;
--l0:#262C32; --l1:#35424A; --l2:#4A626B; --l3:#6A939D; --l4:#97C4CD;
--chip-fg:#12161A;
}
*{box-sizing:border-box}
body{
margin:0; background:var(--paper); color:var(--ink);
font-family:var(--font-body); font-size:17px; line-height:1.62;
-webkit-font-smoothing:antialiased;
}
.wrap{max-width:1180px;margin:0 auto;padding:0 24px 96px}
.layout{display:grid;grid-template-columns:180px minmax(0,1fr);gap:56px;align-items:start}
@media (max-width:960px){.layout{grid-template-columns:1fr;gap:0}.rail{display:none}}
/* ---------- rail ---------- */
.rail{position:sticky;top:28px;padding-top:8px;font-family:var(--font-display);font-size:12px;line-height:1.5}
.rail ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px}
.rail a{color:var(--ink-3);text-decoration:none;display:flex;gap:9px}
.rail a:hover,.rail a:focus-visible{color:var(--brass)}
.rail .n{font-family:var(--font-mono);font-size:10px;color:var(--rule-strong);min-width:16px;padding-top:1px}
.rail .grp{margin-top:14px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--rule-strong)}
/* ---------- header ---------- */
header{padding:64px 0 40px;border-bottom:2px solid var(--ink);margin-bottom:44px}
.eyebrow{font-family:var(--font-mono);font-size:11.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);display:flex;flex-wrap:wrap;gap:14px;margin-bottom:22px}
.eyebrow .stat{color:var(--clay)}
h1{font-family:var(--font-display);font-weight:800;letter-spacing:-.035em;line-height:.94;font-size:clamp(46px,9vw,92px);margin:0 0 6px;text-wrap:balance}
.sub{font-family:var(--font-display);font-weight:500;font-size:clamp(16px,2.4vw,21px);letter-spacing:-.01em;color:var(--ink-2);margin:0 0 30px;max-width:34ch;line-height:1.3}
.metagrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:20px 28px;border-top:1px solid var(--rule);padding-top:20px}
.metagrid dt{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);margin-bottom:5px}
.metagrid dd{margin:0;font-family:var(--font-display);font-size:13.5px;line-height:1.45;color:var(--ink)}
/* ---------- typography ---------- */
section{margin-bottom:60px;scroll-margin-top:24px}
h2{font-family:var(--font-display);font-weight:750;letter-spacing:-.022em;font-size:clamp(24px,3.4vw,31px);line-height:1.12;margin:0 0 18px;text-wrap:balance;display:flex;gap:14px;align-items:baseline}
h2 .sn{font-family:var(--font-mono);font-size:12px;font-weight:400;color:var(--brass);letter-spacing:.06em;flex:none;padding-top:2px}
h3{font-family:var(--font-display);font-weight:700;font-size:16px;letter-spacing:-.008em;margin:34px 0 10px;color:var(--ink)}
p{margin:0 0 15px;max-width:var(--measure)}
ul,ol{max-width:var(--measure);margin:0 0 15px;padding-left:20px}
li{margin-bottom:7px}
strong{font-weight:600}
em{font-style:italic}
code{font-family:var(--font-mono);font-size:.855em;background:var(--surface-2);padding:1px 5px;border-radius:2px}
a{color:var(--brass)}
.lede{font-size:19px;line-height:1.55;color:var(--ink-2);max-width:60ch}
/* ---------- devices ---------- */
.callout{border-left:3px solid var(--brass);background:var(--brass-soft);padding:18px 22px;margin:0 0 24px;max-width:var(--measure)}
.callout p:last-child{margin-bottom:0}
.callout .lbl{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--brass);display:block;margin-bottom:8px}
.rule-quote{border-top:2px solid var(--ink);border-bottom:2px solid var(--ink);padding:26px 0;margin:28px 0;max-width:var(--measure)}
.rule-quote p{font-family:var(--font-display);font-weight:600;font-size:19px;line-height:1.38;letter-spacing:-.014em;margin:0;text-wrap:balance}
.hard{border-left:3px solid var(--clay);background:var(--clay-soft);padding:18px 22px;margin:0 0 24px;max-width:var(--measure)}
.hard .lbl{font-family:var(--font-mono);font-size:10px;letter-spacing:.13em;text-transform:uppercase;color:var(--clay);display:block;margin-bottom:8px}
.hard p:last-child{margin-bottom:0}
.dec{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.08em;color:var(--brass);text-transform:uppercase}
.vec{font-family:var(--font-mono);font-size:.9em;font-weight:600;background:var(--surface-2);padding:2px 7px;border-radius:2px;white-space:nowrap;letter-spacing:.04em}
/* ---------- tables ---------- */
.scroll{overflow-x:auto;margin:0 0 24px;-webkit-overflow-scrolling:touch}
table{border-collapse:collapse;width:100%;min-width:520px;font-family:var(--font-display);font-size:13.5px;line-height:1.45}
th{text-align:left;font-family:var(--font-mono);font-size:9.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink-3);font-weight:400;padding:0 16px 8px 0;border-bottom:1px solid var(--rule-strong);vertical-align:bottom}
td{padding:11px 16px 11px 0;border-bottom:1px solid var(--rule);vertical-align:top;color:var(--ink-2)}
td:first-child{color:var(--ink);font-weight:600}
tbody tr:last-child td{border-bottom:none}
.lvl{font-family:var(--font-mono);font-weight:600;font-size:12px;letter-spacing:.04em;color:var(--ink)}
/* ---------- ladders ---------- */
.breakout{margin:34px 0 40px}
.bhead{display:flex;justify-content:space-between;align-items:baseline;gap:20px;border-bottom:1px solid var(--rule-strong);padding-bottom:9px;margin-bottom:22px;flex-wrap:wrap}
.bhead h3{margin:0;font-size:13px;letter-spacing:.1em;text-transform:uppercase;font-family:var(--font-mono);font-weight:400;color:var(--ink-3)}
.bhead .note{font-family:var(--font-display);font-size:12.5px;color:var(--ink-3)}
.ladders{display:grid;gap:26px}
.ladder{display:grid;grid-template-columns:126px minmax(0,1fr);gap:18px;align-items:start}
@media (max-width:700px){.ladder{grid-template-columns:1fr;gap:10px}}
.ladder .pname{font-family:var(--font-display);font-weight:700;font-size:14px;letter-spacing:-.01em;padding-top:2px}
.ladder .pname span{display:block;font-family:var(--font-mono);font-size:10px;font-weight:400;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);margin-top:3px}
.rungs{display:grid;gap:3px;grid-template-columns:repeat(5,minmax(0,1fr))}
@media (max-width:700px){.rungs{grid-template-columns:repeat(2,minmax(0,1fr))}}
.rung{padding:9px 10px 11px;background:var(--surface);border-top:4px solid var(--l0);min-width:0}
.rung.r1{border-top-color:var(--l1)} .rung.r2{border-top-color:var(--l2)}
.rung.r3{border-top-color:var(--l3)} .rung.r4{border-top-color:var(--l4)}
.rung .code{font-family:var(--font-mono);font-size:11px;font-weight:600;letter-spacing:.06em;color:var(--ink);display:block;margin-bottom:4px}
.rung .txt{font-family:var(--font-display);font-size:11.5px;line-height:1.34;color:var(--ink-2);display:block}
.rung.na{opacity:.42}
/* ---------- matrix ---------- */
.matrix-shell{display:grid;grid-template-columns:auto minmax(0,1fr);gap:12px;align-items:stretch;margin-bottom:14px}
.ylab{writing-mode:vertical-rl;transform:rotate(180deg);font-family:var(--font-mono);font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3);text-align:center;padding-bottom:22px}
.mgrid{display:grid;grid-template-columns:34px repeat(5,minmax(0,1fr));gap:3px}
.mcell{background:var(--surface);min-height:60px;padding:6px;display:flex;flex-direction:column;justify-content:flex-end;gap:4px;min-width:0}
.mcell.tint1{background:color-mix(in srgb,var(--l1) 26%,var(--surface))}
.mcell.tint2{background:color-mix(in srgb,var(--l2) 26%,var(--surface))}
.mcell.tint3{background:color-mix(in srgb,var(--l3) 24%,var(--surface))}
.mcell.tint4{background:color-mix(in srgb,var(--l4) 22%,var(--surface))}
.mcell.void{background:repeating-linear-gradient(135deg,transparent,transparent 5px,var(--rule) 5px,var(--rule) 6px);opacity:.55}
.rlab,.clab{font-family:var(--font-mono);font-size:10px;font-weight:600;letter-spacing:.05em;color:var(--ink-3);display:flex;align-items:center;justify-content:center}
.rlab{min-height:60px}
.clab{padding-top:7px;min-height:22px}
.pin{font-family:var(--font-mono);font-size:9.5px;font-weight:600;letter-spacing:.02em;background:var(--ink);color:var(--paper);padding:2px 5px;border-radius:2px;line-height:1.3;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pin.ghost{background:transparent;color:var(--ink-2);border:1px dashed var(--rule-strong)}
.mnote{display:flex;gap:22px;flex-wrap:wrap;font-family:var(--font-display);font-size:12px;color:var(--ink-3);padding-top:6px}
.mnote .k{display:flex;align-items:center;gap:7px}
.sw{width:13px;height:13px;flex:none;background:var(--ink)}
.sw.g{background:transparent;border:1px dashed var(--rule-strong)}
.sw.v{background:repeating-linear-gradient(135deg,transparent,transparent 4px,var(--rule) 4px,var(--rule) 5px);border:1px solid var(--rule)}
@media (max-width:640px){
.mgrid{grid-template-columns:28px repeat(5,minmax(0,1fr))}
.mcell{min-height:52px;padding:4px}
.pin{font-size:8px;padding:1px 3px}
.rlab{min-height:52px}
}
/* ---------- methodology ---------- */
.verbs{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:2px;background:var(--rule);border:1px solid var(--rule)}
.verb{background:var(--surface);padding:18px 18px 20px}
.verb h4{font-family:var(--font-display);font-weight:750;font-size:15px;margin:0 0 7px;letter-spacing:-.01em}
.verb p{font-family:var(--font-display);font-size:12.5px;line-height:1.46;color:var(--ink-2);margin:0;max-width:none}
.verb .step{font-family:var(--font-mono);font-size:9.5px;letter-spacing:.13em;color:var(--brass);display:block;margin-bottom:9px}
/* ---------- questions ---------- */
.qs{display:flex;flex-direction:column;gap:0;border-top:1px solid var(--rule-strong)}
.q{display:grid;grid-template-columns:34px minmax(0,1fr) 170px;gap:18px;padding:16px 0;border-bottom:1px solid var(--rule);align-items:start}
@media (max-width:760px){.q{grid-template-columns:28px minmax(0,1fr);gap:12px}.q .owner{grid-column:2}}
.q .qn{font-family:var(--font-mono);font-size:11px;color:var(--brass);padding-top:3px}
.q .qt{font-family:var(--font-display);font-size:14px;line-height:1.48;color:var(--ink-2)}
.q .qt b{color:var(--ink);font-weight:700;display:block;margin-bottom:2px;font-size:14.5px}
.owner{font-family:var(--font-mono);font-size:10px;letter-spacing:.05em;color:var(--ink-3);padding-top:4px}
.owner .tag{display:inline-block;border:1px solid var(--rule-strong);padding:2px 7px;border-radius:2px}
.owner .tag.need{border-color:var(--clay);color:var(--clay)}
/* ---------- misc ---------- */
.numbers{font-family:var(--font-mono);font-size:12.5px;line-height:1.85;background:var(--surface);border-left:3px solid var(--l3);padding:16px 20px;margin:0 0 22px;overflow-x:auto;max-width:var(--measure)}
.numbers .v{color:var(--ink);font-weight:600}
.numbers .k{color:var(--ink-3)}
pre{font-family:var(--font-mono);font-size:12.5px;line-height:1.68;background:var(--surface);border-left:3px solid var(--rule-strong);padding:16px 20px;overflow-x:auto;margin:0 0 22px;max-width:var(--measure);color:var(--ink-2)}
.alt{border-bottom:1px solid var(--rule);padding:14px 0;max-width:var(--measure)}
.alt:last-of-type{border-bottom:none}
.alt b{font-family:var(--font-display);font-size:14px;display:block;margin-bottom:3px}
.alt p{font-size:14.5px;margin:0;color:var(--ink-2)}
.alt .verdict{font-family:var(--font-mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--clay)}
footer{border-top:2px solid var(--ink);margin-top:20px;padding-top:22px;font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;color:var(--ink-3);display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap}
.tm td,.tm th{text-align:center}
.tm td:first-child,.tm th:first-child{text-align:left}
.yes{color:var(--l4);font-weight:700}
.no{color:var(--clay);font-weight:700}
.kind{font-family:var(--font-mono);font-size:9px;letter-spacing:.09em;text-transform:uppercase;padding:2px 6px;border-radius:2px;white-space:nowrap;border:1px solid var(--rule-strong);color:var(--ink-3)}
.kind.adv{border-color:var(--clay);color:var(--clay)}
.routes{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:2px;background:var(--rule);border:1px solid var(--rule);margin:0 0 22px}
.route{background:var(--surface);padding:16px 18px}
.route h4{font-family:var(--font-display);font-weight:750;font-size:14px;margin:0 0 6px}
.route p{font-family:var(--font-display);font-size:12.5px;line-height:1.45;color:var(--ink-2);margin:0;max-width:none}
.route .tag{font-family:var(--font-mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--brass);display:block;margin-bottom:8px}
a:focus-visible,.rail a:focus-visible{outline:2px solid var(--brass);outline-offset:3px}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
</style>
<div class="wrap"><header><div class="eyebrow"><span>RISK-F-0001</span> <span class="stat">fixed · published-1</span> <span>risk-nexus</span> <span>reviewed 2026-08-20</span><span>generated from canonical source — do not edit</span></div><h1>flex-auth /v1/check authenticates no caller</h1><p class="sub">The estate&#x27;s authorization oracle authenticated no caller for as long as the endpoint existed. Found by reading, not by monitoring; fixed in two days.</p><p class="sub">Source: <code>risk-nexus · findings/RISK-F-0001-flex-auth-unauthenticated-check.md · a13d954f8597fd92201746e2d52f031a5a88d969</code></p><p class="sub">Review due: 2027-02-20</p></header><div class="layout"><nav class="rail" aria-label="Sections"><ol><li><a href="#what-is-true"><span class="n">·</span>What is true</a></li><li><a href="#how-it-was-found"><span class="n">·</span>How it was found</a></li><li><a href="#exposure-as-far-as-the-reporter-stated-it"><span class="n">·</span>Exposure, as far as the reporter stated it</a></li><li><a href="#what-makes-it-worse-than-a-single-service-s-defect"><span class="n">·</span>What makes it worse than a single service&#x27;s defect</a></li><li><a href="#owner-and-state"><span class="n">·</span>Owner and state</a></li><li><a href="#what-this-repo-is-asked-to-decide"><span class="n">·</span>What this repo is asked to decide</a></li><li><a href="#related-reported-at-the-same-time-and-not-yet-filed"><span class="n">·</span>Related, reported at the same time and not yet filed</a></li><li><a href="#register-ruling-2026-08-19"><span class="n">·</span>Register ruling — 2026-08-19</a></li><li><a href="#reviews"><span class="n">·</span>Reviews</a></li><li><a href="#re-grade-and-close-2026-08-19-same-day"><span class="n">·</span>Re-grade and close — 2026-08-19 (same day)</a></li><li><a href="#reviews"><span class="n">·</span>Reviews</a></li></ol></nav><main><section id="what-is-true"><h2>What is true</h2>
<p><code>POST /v1/check</code> and <code>POST /v1/batch_check</code> authenticate no caller. Any workload with network reach to the ClusterIP Service can assert any subject and any tenant and receive an authoritative <strong>allow</strong>.</p>
<p><code>flex-auth</code> 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.</p>
<p>Self-reported by <code>flex-auth</code> as <code>A0</code> 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."</p>
</section>
<section id="how-it-was-found"><h2>How it was found</h2>
<p>Not by a probe, an incident, or an alert. By <code>flex-auth</code> 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.</p>
<p>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.</p>
</section>
<section id="exposure-as-far-as-the-reporter-stated-it"><h2>Exposure, as far as the reporter stated it</h2>
<ul><li>The Service is <code>ClusterIP</code>, so reach requires a workload inside the cluster.</li><li>No claim was made that network policy restricts which workloads can reach it, and this record does not assume one. <strong>If a default-deny NetworkPolicy fronts the service, that materially changes the exposure and should be verified rather than inferred</strong><code>flex-auth</code> did not state it either way, and I have not checked, because doing so would be reporting on a system I do not own.</li></ul>
</section>
<section id="what-makes-it-worse-than-a-single-service-s-defect"><h2>What makes it worse than a single service's defect</h2>
<p>A false allow from this endpoint is not confined to <code>flex-auth</code>. It is the answer other services act on. <code>tenant-engine</code> separately reports that its own mutations are authorized by <code>flex-auth</code> 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.</p>
</section>
<section id="owner-and-state"><h2>Owner and state</h2>
<p><code>flex-auth</code> owns the fix and has tracked it as <code>FLEX-WP-0015-T02</code>, 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.</p>
</section>
<section id="what-this-repo-is-asked-to-decide"><h2>What this repo is asked to decide</h2>
<ol><li><strong>Severity.</strong> Not the reporter's to set.</li><li><strong>Disclosure.</strong> 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.</li><li><strong>Escalation.</strong> 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.</li></ol>
</section>
<section id="related-reported-at-the-same-time-and-not-yet-filed"><h2>Related, reported at the same time and not yet filed</h2>
<p>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:</p>
<ul><li><code>tenant-engine</code><code>events()</code> returns the entire event log unfiltered. A live cross-tenant read at <code>E2</code>.</li><li><code>audit-core</code> — read path applies no tenant filter; a credential with <code>may_read</code> can read any tenant's events. Bounded by deployment (<code>may_read: false</code> on the production sender) and not by code. Tracked <code>AUDIT-WP-0008-T04</code>.</li><li><code>apps-pg</code> (<code>railiance-platform</code>) — no backup configured at all: no <code>barmanObjectStore</code>, no retention policy, <code>BestEffort</code> QoS. <code>R0</code> there means no recovery, not merely no erasure policy.</li></ul>
<p>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: <strong>four live defects, none found by monitoring.</strong></p>
</section>
<section id="register-ruling-2026-08-19"><h2>Register ruling — 2026-08-19</h2>
<p><code>critical</code> (<code>I4</code> × <code>L3</code>, no fidelity modifier), embargoed until <code>FLEX-WP-0015-T02</code> ships, <strong>escalated</strong> to the operator on trigger 1.</p>
<p>The question this finding put — whether governing access to tenant data counts as exposing it — is answered <strong>yes</strong>. An authorization oracle that can be forged is not one step removed from the data; it is the step.</p>
<p>Impact is <code>I4</code> because a forged allow does not stay here: it is the answer other services act on, and <code>tenant-engine</code> has stated what direct authority over its rows would mean. Likelihood is <code>L3</code> — 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.</p>
<p>No fidelity modifier: this endpoint answers honestly about a caller it never checked. The false-record hazard lives in <code>RISK-F-0002</code>'s constraint, where a consumer of this endpoint would begin signing records asserting an authorization that was never made. <strong>That constraint binds this finding's remediation</strong>: <code>ops-warden</code>'s <code>policy.enabled</code> must not be turned on until <code>/v1/check</code> authenticates its callers, and the ordering is</p>
<pre>flex-auth warn-only -&gt; ops-warden gate presents its SA token -&gt; logs clean
-&gt; flex-auth fail-closed -&gt; ops-warden policy.enabled: true</pre>
<p>Nothing further is asked of <code>flex-auth</code> beyond what they have committed to, except one fact only they can supply: <strong>is there a default-deny NetworkPolicy in front of the Service?</strong> It is the single fact that would most change this grade, and it is the first question at review.</p>
<p>Reasoning: <code>docs/rulings/2026-08-19-first-grading.md</code>.</p>
</section>
<section id="reviews"><h2>Reviews</h2>
<ul><li><strong>2026-08-19</strong> — graded. Next review 2026-08-26 (<code>critical</code> → 7 days). Open at review: the NetworkPolicy question; whether <code>FLEX-WP-0015-T02</code> has moved; whether the embargo still holds.</li></ul>
</section>
<section id="re-grade-and-close-2026-08-19-same-day"><h2>Re-grade and close — 2026-08-19 (same day)</h2>
<p><strong>Corrected from <code>critical</code> to <code>high</code>, and closed as <code>fixed</code>.</strong> 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.</p>
<p><strong>The exposure was narrower than graded.</strong> <code>flex-auth</code> answered the NetworkPolicy question on 2026-08-18: both production Deployments ship a NetworkPolicy in the same manifest, and it is <em>narrower</em> than default-deny — ingress restricted to one <code>namespaceSelector</code> plus one <code>podSelector</code> on port 8080, egress empty. In force since before the period the <code>A0</code> describes. So the reachable set was never "any pod in the cluster"; it was the single paired workload per Deployment.</p>
<p>That is <code>L2</code>, not <code>L3</code>. Impact stays <code>I4</code> — what a forged allow reaches does not change — so the grade is <code>high</code>. <code>flex-auth</code> also corrected their own earlier phrasing to <code>ops-warden</code> in the same message, unprompted, and that correction is why the fact reached this register at all.</p>
<p>Three caveats <code>flex-auth</code> 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 <code>resource.system</code>, which is what made cross-system impersonation possible; and enforcement depends on a CNI they could not verify from a cluster where <code>kubectl</code> returned <code>Unauthorized</code>. They said so rather than letting a manifest stand in for a probe.</p>
<p><strong>It is fixed.</strong> On 2026-08-19 <code>flex-auth</code> reported both production Deployments enforcing ADR-0004 TokenReview, with live unbound-request probes returning 401 rather than a decision, on both the <code>user-engine</code> and <code>tenant-engine</code> pins. <code>FLEX-WP-0015</code> is finished and <code>tenancy.current.A</code> is 2. That is a probe against the running system, which is the standard <code>docs/method/review.md</code> sets for closing: something concrete read, not something been told.</p>
<p><strong>Disclosure flips to <code>public</code>.</strong> The embargo condition was "FLEX-WP-0015-T02 ships to production" and it is met. Handover to <code>policy-nexus</code> is the next step and is not done yet.</p>
<p><strong>The escalation is withdrawn without being sent.</strong> It was <code>pending-operator</code> 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.</p>
<p><strong>What this cost, recorded because it is the register's own defect.</strong> 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 <code>docs/method/review.md</code> — and this finding is the case that bought it.</p>
</section>
<section id="reviews"><h2>Reviews</h2>
<ul><li><strong>2026-08-19</strong> — re-graded <code>high</code>, closed <code>fixed</code>, disclosure <code>public</code>, escalation withdrawn. Remaining: handover to <code>policy-nexus</code>; the CNI enforcement question is <code>flex-auth</code>'s and no longer this finding's.</li><li><strong>2026-08-20</strong> — not clean: Publication handover requested; publication front-matter applied and the wait on policy-nexus typed. Cadence instant → instant; checked again immediately.</li></ul>
</section><footer><span>RISK-F-0001 · published-1 · fixed</span><span>risk-nexus · findings/RISK-F-0001-flex-auth-unauthenticated-check.md · a13d954f8597fd92201746e2d52f031a5a88d969</span></footer></main></div></div></html>