Skip to content

Commit 902e01c

Browse files
docs: fix MDX build (drop HTML comment, switch adr/ links to absolute URLs)
The W3 docs commit (31284d4) introduced two Docusaurus-incompatible patterns that broke the deploy build: 1. An HTML comment '<!-- ─── W3: ... ───── -->' separating the W2 threat-model rows from the W3 additions. Docusaurus 3 parses these files as MDX and a bare-! in column 2 is treated as a JSX element start. Comment removed — it was a visual divider, the heading immediately below carries the same intent. 2. Markdown links to '../adr/0009...md' and '../adr/0010...md'. The adr/ tree is outside the Docusaurus plugin's content root (preset.docs.path is ../docs only), so the link checker bails with 'couldn't be resolved'. Switched to absolute github.com URLs which the link checker leaves alone. Local 'npm --prefix website run build' is now clean. Same fix pattern as commit c9750f4.
1 parent 7ff0755 commit 902e01c

2 files changed

Lines changed: 3 additions & 5 deletions

File tree

‎docs/api_contract.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ All require `X-API-Key: <ADMIN_API_KEY>`. Read-only.
129129

130130
### Proof pairing (`/v1/proof-pairing/*`)
131131

132-
Cross-device verification flow: a desktop opens a session, a phone scans the QR + generates a Groth16 proof, the desktop submits the proof, the backend mints a desktop JWT. Full protocol in [ADR-0009](../adr/0009-qr-proof-pairing-protocol.md).
132+
Cross-device verification flow: a desktop opens a session, a phone scans the QR + generates a Groth16 proof, the desktop submits the proof, the backend mints a desktop JWT. Full protocol in [ADR-0009](https://github.com/zeroauth-dev/ZeroAuth/blob/main/adr/0009-qr-proof-pairing-protocol.md).
133133

134134
| Method | Path | Scope | Description |
135135
|---|---|---|---|

‎docs/threat_model.md‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,14 @@
129129
| **Mitigation** | All inline handlers were removed; forms now use `addEventListener` from a single `<script>` block. CSP is enforced. |
130130
| **Test status** | Live `curl … | grep onsubmit=` returns 0 in CI. Could be promoted to a real test. |
131131

132-
<!-- ─── W3: QR-proof pairing (Android phone → desktop) ─────────────── -->
133-
134132
### A-11 — Pairing-nonce replay across two desktop sessions
135133

136134
| | |
137135
|---|---|
138136
| **Class** | Spoofing (STRIDE: S) |
139137
| **Surface** | `POST /v1/proof-pairing/sessions/:id/submit` |
140138
| **Description** | A passively-recorded `{proof, publicSignals, did}` from session S1 is replayed against a freshly-issued session S2 whose nonce the attacker observes (shoulder-surf of the desktop QR). Without an explicit nonce ↔ proof binding the same proof verifies for any session because `identityBinding = Poseidon(2)([biometricSecret, didHash])` is per-user, not per-session. |
141-
| **Mitigation** | [ADR-0009](../adr/0009-qr-proof-pairing-protocol.md) Option B′: phone computes `didHashSession = Poseidon(2)([didHash, sessionNonce])` and uses it as the circuit's `didHash` input. Server re-derives `Poseidon(2)([user.didHash, session.nonce])` from its own records and `crypto.timingSafeEqual`-compares to `publicSignals[1]`. Mismatch → 400 `pairing_nonce_mismatch`. |
139+
| **Mitigation** | [ADR-0009](https://github.com/zeroauth-dev/ZeroAuth/blob/main/adr/0009-qr-proof-pairing-protocol.md) Option B′: phone computes `didHashSession = Poseidon(2)([didHash, sessionNonce])` and uses it as the circuit's `didHash` input. Server re-derives `Poseidon(2)([user.didHash, session.nonce])` from its own records and `crypto.timingSafeEqual`-compares to `publicSignals[1]`. Mismatch → 400 `pairing_nonce_mismatch`. |
142140
| **Test status** | **Required before merge.** `tests/proof-pairing.test.ts` cases: (i) valid first submit succeeds; (ii) replay against same session returns 409; (iii) replay against fresh session with different nonce returns 400 with `pairing_nonce_mismatch`. |
143141
| **Audit signal** | `audit_events.action = 'pairing.replay_blocked'` with `metadata.session_id`. |
144142

@@ -204,7 +202,7 @@
204202
| **Class** | Tampering (STRIDE: T) |
205203
| **Surface** | The Android app's WebView running snarkjs |
206204
| **Description** | If snarkjs is loaded over HTTPS at runtime, a CDN/network compromise swaps it for a build that exfiltrates `biometricSecret` (the only private witness) or produces proofs against attacker-chosen commitments. Same class as `event-stream` / `ua-parser-js`. |
207-
| **Mitigation** | [ADR-0010](../adr/0010-android-webview-snarkjs-bundling.md): snarkjs is bundled in the APK at `android/app/src/main/assets/prover/`, SHA-256 pinned in the ADR, build fails on mismatch. WebView CSP: `default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'none'`. WebView in a separate renderer process. No `file://`, no `content://`, no DOM storage. Play Integrity verdict travels in `clientMeta.playIntegrityVerdict` for W4 server-side enforcement. |
205+
| **Mitigation** | [ADR-0010](https://github.com/zeroauth-dev/ZeroAuth/blob/main/adr/0010-android-webview-snarkjs-bundling.md): snarkjs is bundled in the APK at `android/app/src/main/assets/prover/`, SHA-256 pinned in the ADR, build fails on mismatch. WebView CSP: `default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'none'`. WebView in a separate renderer process. No `file://`, no `content://`, no DOM storage. Play Integrity verdict travels in `clientMeta.playIntegrityVerdict` for W4 server-side enforcement. |
208206
| **Test status** | **Required before Android app merge.** CI step diffs `assets/prover/*.sha256` against the ADR-pinned table; build fails on mismatch. |
209207
| **Audit signal** | If `clientMeta.snarkjsHash` ever travels in the submit, log mismatches as `audit_events.action = 'pairing.unexpected_prover_hash'`. |
210208

0 commit comments

Comments
 (0)