The second anchor scenario (docs/polysec-spec.md §2 UC-3): the TLS-lineage classic, generalized — no privileged action before the authenticating transition, plus an ordering invariant (the handshake must complete before application data is accepted). It's where PolySec catches the SKIP-TLS shape — state-machine confusion — at design time.
npm run polysec:auth # fully-mediated verdict → out/security-report.{json,md}
npm test # the assertions behind this scenario
$0 API, deterministic, exhaustive over the finite declared domains.
| sandbox (UC-1, lethal trifecta) | auth (UC-3) | |
|---|---|---|
| invariant class | confidentiality / coexistence | reachability + ordering |
| forbidden regions | one | two (privilege + ordering) |
| havoc hero variable | egress |
privileged |
| the famous bug it catches | prompt-injection exfiltration | SKIP-TLS state confusion |
| recovery | containment (flag persists) | restoration (reversible) |
next.cjs is a SAM v2 strict-profile machine. State (all booleans):
handshake, authenticated, appData, privileged, peerClosed.
- Ordering —
AUTHENTICATEandACCEPT_APP_DATAare default-denied untilhandshakecompletes (handshake ≺ auth,handshake ≺ appData). - Privilege gate —
DO_PRIVILEGEDis default-denied in every state whereauthenticatedis false. This is the gate. - Restoration —
TEARDOWNreturns the session to the pristine unauthenticated start. Unlike the trifecta's compromise flag, UC-3 recovery is genuinely reversible (no external damage, §2 UC-3), so the desired state is pristine, not merely contained.
Forbidden regions (security.json):
no-privileged-pre-auth—predicate: "privileged && !authenticated"handshake-precedes-appdata—predicate: "appData && !handshake"
- Clean from-init. Over all reachable states of the mediated machine, neither region is entered — the gates enforce both the privilege rule and the ordering.
- Havoc counterexample when privilege is left adversarial. With
security.privileged-adversarial.json, the designer treatsprivilegedas something an unmodeled channel could trigger (adversarial). Havoc drillsprivileged := truefrom an unauthenticated state →privileged ∧ ¬authenticated. The counterexample namesprivileged. The repair (security.json) makesprivilegedprotected— settable only via the auth-gatedDO_PRIVILEGED. - SKIP-TLS negative control.
next.skip-auth.cjschanges one line —DO_PRIVILEGEDguards onhandshakeinstead ofauthenticated. The from-init check reaches the forbidden region viaCOMPLETE_HANDSHAKE → DO_PRIVILEGED(noAUTHENTICATEin between) — a privileged effect after the handshake but before authentication. That reachable-bad-state is the SKIP-TLS vulnerability, emitted with the shortest witness. - Fully-mediated verdict. The mediated machine passes all three gates →
FULLY MEDIATED, written toout/security-report.{json,md}.
Controls. POSITIVE: next.cjs passes. NEGATIVE: next.skip-auth.cjs fails
from-init with the SKIP-TLS path. The verdict flips exactly on the conjunction.
This is exhaustive over the finite declared domains and the observable projection only (§0). Havoc over-approximates (safe direction); gate-deletion reachability is monotone, so a bypass it reports is also a from-init violation — its distinct value is the witness. Havoc is the check that reaches beyond the modeled machine — the SKIP-TLS demo shows it naming a privilege escalation an unmodeled channel could cause.
| file | role |
|---|---|
next.cjs |
mediated auth machine (positive control) |
next.skip-auth.cjs |
SKIP-TLS negative control (mis-guarded DO_PRIVILEGED) |
contract.json |
observable scope / action alphabet |
invariants.mjs |
privilege + ordering invariants (check.mjs entry point) |
security.json |
mediated manifest (two forbidden regions) |
security.privileged-adversarial.json |
pre-repair manifest (privilege adversarial) |
out/security-report.{json,md} |
the fully-mediated verdict (generated) |
With the Polygraph plugin installed, drive this machine
in natural language ($0 API):
- Render the machine + invariants: Using polyviz, render the state machine
and invariants for
examples/polysec-auth(next.cjs,invariants.mjs). - Elicit / grade invariants: Elicit invariants for
examples/polysec-auth/next.cjsusing itscontract.json.
This scenario ships no trace corpus, so /verify (which needs one) does not
apply here — use the sandbox for a full verify run.