test(openapi): write the route auth-parity meta-test both spec tables are exported for (#9851) - #9856
Conversation
… are exported for (#9851) `ORB_AND_CONTROL_ROUTE_SPECS` and `INTERNAL_AND_PUBLIC_ROUTE_SPECS` were each exported with a comment naming their consumer -- "the meta-test that asserts every entry's declared auth matches the middleware that actually gates it" -- and that test did not exist. Two exports maintained for a guarantee nobody built, while the `auth` on every ORB, internal, app and repo route went unverified. It asserts BEHAVIOUR rather than wiring, deliberately. Auth here is enforced inside handlers (only `/v1/internal/*` has a path-scoped middleware), so "read the middleware chain" has nothing uniform to read -- and a chain that exists proves less than a route that actually refuses. So both directions are exercised against a booted app: a route declaring it needs a credential must REFUSE without one, and a route declaring itself public must not answer 401. Getting it honest took three corrections, each of which is now a comment in the file: - The relay and webhook handlers reject a request they cannot IDENTIFY before verifying its signature, so a bare request never reached the auth check at all. The test now sends the GitHub identifying headers and nothing that authenticates, which is what proves the signature is required. - Three broker routes 404 until `ORB_BROKER_ENABLED`, and the relay 404s on an instance with no enrollment secret. The env now makes them REACHABLE instead of skipping them -- a flag-gated route still has a declared auth to honour. - Both directions share one env. They did not at first, and the mutation check caught it: with a different env a flag-gated route 404s in the public direction, so a wrongly-public declaration passed. Verified by flipping a real entry's auth to `public` and watching the suite fail. A 400 from a signature-authenticated route is accepted as a refusal: this test cannot synthesize a valid payload for every webhook shape, and what it still proves is that an unsigned request never succeeds. No production behaviour changes. Every current route already honours its declaration -- which is worth knowing, and was not knowable before.
|
Tip ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-29 14:54:21 UTC
Review summary Nits — 6 non-blocking
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. Decision record
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9856 +/- ##
==========================================
- Coverage 91.64% 90.77% -0.88%
==========================================
Files 916 916
Lines 112753 112753
Branches 27085 27085
==========================================
- Hits 103338 102347 -991
- Misses 8126 9315 +1189
+ Partials 1289 1091 -198
Flags with carried forward coverage won't be shown. Click here to find out more. |
…b's own refusal (#9864) #9856: gate success, CI green, mergeable_state "clean", autonomy auto, approvals satisfied, the bot posted "LoopOver approves" and the panel said "Suggested Action - Approve/Merge · safe to merge" -- and it never merged. Recurring for weeks, across many PRs, with no stated reason. The cause was never a LoopOver bug. GitHub REFUSED the merge: merge forbidden (403): Merging stacked PRs via this API is not supported. Use the web interface instead. Stacked PRs cannot be merged through the REST API at all. The executor recorded the refusal against that head and correctly stopped retrying. But that reason is invisible to every surface: mergeable_state reads `clean`, no gate models it, the audit fell through to the residual "merge withheld because no merge action was planned", and the public comment kept promising a merge that cannot happen. Four distinct refusals across 7 PRs all rendered identically (403 stacked; three 405 repo-policy variants). Surface it in both places: - agentHoldAuditDetail reports GitHub's message when the block is for THIS head, checked last among the specifics (everything above it is a state LoopOver can still change on its own; this one only a human can). - the unified comment downgrades an otherwise-ready status to HELD and names the refusal in the verdict box, with the remedy ("merge it yourself"). Same downgrade-only discipline as the guardrail hold: a real gate/CI block still wins. A block recorded against a DIFFERENT head is stale and deliberately ignored -- that commit was never attempted, so claiming GitHub refused it would be a lie and would hide whatever is actually holding the new push. Clock discipline: the decision path uses the pass's recorded instant (decisionClock.nowMs), never a fresh Date.now() -- the #9492 invariant, whose guard caught a first attempt that violated it. The publish path matches on head SHA alone and explains why: it is a reporting surface with no decision clock, and merge_blocked_until bounds when LoopOver may RETRY, not whether GitHub refused this commit.
Closes #9851.
The gap
Two files each export their full spec table with a comment naming the consumer:
That test did not exist. Neither symbol was referenced anywhere outside its own file — so the
authon every ORB, internal, app and repo route was unverified against what the route actually enforces, while two exports were maintained for a guarantee nobody had built. (Found by the dead-export sweep in #9852, which is what makes that check worth having.)What it asserts
Behaviour, not wiring — deliberately. Auth here is enforced inside handlers; only
/v1/internal/*has a path-scoped middleware, so "read the middleware chain" has nothing uniform to read. And a chain that exists proves less than a route that actually refuses. So both directions run against a booted app:Plus two guards on the check itself: both tables must be non-empty (a watcher that watches nothing passes forever), and every
authvalue must be one the document knows how to render.Getting it honest took three corrections
Each is now a comment in the file, because each is a way this test could have passed while proving nothing:
ORB_BROKER_ENABLED, and the relay 404s on an instance with no enrollment secret. The env makes them reachable rather than skipping them — a flag-gated route still has a declared auth to honour.Verified by mutation: flipping a real entry's
authtopublicfails the suite withPOST /v1/orb/token declares auth=public but answered 401. Before the third fix, that same mutation passed — which is exactly the failure mode this issue is about.Result
No production behaviour changes. Every current route already honours its declaration — which is worth knowing, and was not knowable before.
A 400 from a signature-authenticated route counts as a refusal: this test cannot synthesize a valid payload for every webhook shape, and what it still proves is that an unsigned request never succeeds.
Note
SpecEntryis declared separately in each of the two spec files and the copies have already diverged (the internal one grew arequestfield, the ORB one did not). Not touched here to keep this PR to the missing guarantee, but it is the same hand-mirrored-type class the MCP epic just finished removing elsewhere — worth its own change.