fix: domain-separate auth signatures, enable CSP, harden key export (#76) #101

Merged
icub3d merged 2 commits from feature/76-signing-oracle-csp into main 2026-07-09 03:30:58 +00:00
Owner

Summary

Fixes the compounding webview key-handling risks in #76 as one coordinated change across server, client, SDK, and shared.

1. Signing oracle → domain separation

sign(data) used to sign any JS-supplied string with no domain separation — a compromised webview could forge auth challenges, messages, or any protocol artifact.

  • shared::auth::auth_signing_payload prepends a decentcom-auth: tag. Both the signer and the verifier build the signed bytes through this single helper, so a signature produced for auth can never be replayed as (or forged from) a signature over another artifact — and vice versa.
  • Server verify() now verifies against the tagged payload.
  • Tauri client: the generic sign command is replaced by sign_auth_challenge, which applies the tag itself. The webview supplies only the server-issued challenge, never the domain — so it is no longer a generic signing oracle. Removed the unused generic sign wrapper from useIdentity.
  • Rust SDK signs the tagged payload too, keeping bots/tools in lockstep.

This is a coordinated protocol change — server and all signers ship together (pre-1.0, no compatibility shim).

2. CSP

"csp": null → strict policy. script-src 'self' closes the XSS→IPC amplifier the issue calls out. connect-src/img-src stay network-permissive because the client connects to arbitrary self-hosted servers (fetch + WebSocket).

⚠️ Manual smoke-test requested: I can't launch the GUI here. Please confirm pnpm tauri dev and a packaged build still load the webview and connect to a server under the new CSP.

3. Key export hardening

  • inner_key_export enforces validate_passphrase(..).acceptable before touching the key — a direct IPC call can't bypass the JS-side check.
  • Strength floor raised: "Weak" is now rejected, not merely flagged.
  • Destination path comes from a native save dialog owned by the Rust command, not a JS-supplied path; the webview can no longer choose an arbitrary write target.

Tests

  • Server: signature over the untagged challenge is rejected (401) — proves domain separation; valid tagged signature still accepted. Updated all per-module auth test helpers to sign the tagged payload.
  • shared: payload tagging.
  • Client Rust: weak-passphrase export rejected before any keyring/file access; raised passphrase floor.
  • Green: full workspace cargo test + clippy -D warnings; frontend tsc, eslint --max-warnings=0, vitest (89), vite build.

Closes #76

## Summary Fixes the compounding webview key-handling risks in #76 as one coordinated change across server, client, SDK, and shared. ### 1. Signing oracle → domain separation `sign(data)` used to sign *any* JS-supplied string with no domain separation — a compromised webview could forge auth challenges, messages, or any protocol artifact. - **`shared::auth::auth_signing_payload`** prepends a `decentcom-auth:` tag. Both the signer and the verifier build the signed bytes through this single helper, so a signature produced for auth can never be replayed as (or forged from) a signature over another artifact — and vice versa. - **Server** `verify()` now verifies against the tagged payload. - **Tauri client**: the generic `sign` command is replaced by **`sign_auth_challenge`**, which applies the tag itself. The webview supplies only the server-issued challenge, never the domain — so it is no longer a generic signing oracle. Removed the unused generic `sign` wrapper from `useIdentity`. - **Rust SDK** signs the tagged payload too, keeping bots/tools in lockstep. This is a **coordinated protocol change** — server and all signers ship together (pre-1.0, no compatibility shim). ### 2. CSP `"csp": null` → strict policy. `script-src 'self'` closes the XSS→IPC amplifier the issue calls out. `connect-src`/`img-src` stay network-permissive because the client connects to arbitrary self-hosted servers (fetch + WebSocket). > ⚠️ **Manual smoke-test requested**: I can't launch the GUI here. Please confirm `pnpm tauri dev` and a packaged build still load the webview and connect to a server under the new CSP. ### 3. Key export hardening - `inner_key_export` enforces `validate_passphrase(..).acceptable` before touching the key — a direct IPC call can't bypass the JS-side check. - Strength floor raised: **"Weak" is now rejected**, not merely flagged. - Destination path comes from a **native save dialog owned by the Rust command**, not a JS-supplied path; the webview can no longer choose an arbitrary write target. ## Tests - Server: signature over the **untagged** challenge is rejected (401) — proves domain separation; valid tagged signature still accepted. Updated all per-module auth test helpers to sign the tagged payload. - `shared`: payload tagging. - Client Rust: weak-passphrase export rejected **before** any keyring/file access; raised passphrase floor. - Green: full workspace `cargo test` + `clippy -D warnings`; frontend `tsc`, `eslint --max-warnings=0`, `vitest` (89), `vite build`. Closes #76
fix(client): domain-separate auth signatures, enable CSP, harden key export
Some checks failed
Security Audit / Gitleaks (Secrets) (pull_request) Successful in 5s
Security Audit / Trivy (SCA & IaC) (pull_request) Failing after 19s
Security Audit / PNPM Audit (JS SCA) (pull_request) Failing after 20s
Security Audit / Semgrep (SAST) (pull_request) Failing after 37s
CI / Client — test & lint (pull_request) Successful in 1m7s
CI / Server — test & lint (pull_request) Has been cancelled
Security Audit / Cargo Audit (Rust SCA) (pull_request) Has been cancelled
ddd823fe35
Addresses the compounding webview key-handling risks in #76.

Signing oracle -> domain separation (coordinated server + client + SDK):
- shared::auth::auth_signing_payload prepends a `decentcom-auth:` tag; both
  signer and verifier build the signed bytes through it, so an auth signature
  can never be replayed as (or forged from) a signature over another artifact.
- Server verify() now checks against the tagged payload.
- The Tauri `sign` command is replaced by `sign_auth_challenge`, which applies
  the tag itself — the webview picks the challenge, never the domain — so the
  command is no longer a generic signing oracle. Removed the unused generic
  `sign` wrapper from useIdentity.
- Rust SDK signs the tagged payload too, keeping bots/tools in sync.

CSP:
- Replace `"csp": null` with a strict policy: `script-src 'self'` closes the
  XSS -> IPC amplifier; connect-src/img-src stay network-permissive because the
  client talks to arbitrary self-hosted servers.

Key export:
- inner_key_export enforces validate_passphrase(..).acceptable and the strength
  floor is raised so "Weak" is rejected, not merely flagged.
- The destination now comes from a native save dialog owned by the Rust command
  instead of a JS-supplied path.

Tests: server rejects a signature over the untagged challenge; shared payload
tagging; client rejects a weak-passphrase export before any keyring/file access;
passphrase floor. Full workspace test + clippy -D warnings green; frontend tsc,
eslint, vitest (89) and vite build green.

Closes #76

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge branch 'main' into feature/76-signing-oracle-csp
Some checks failed
Security Audit / Gitleaks (Secrets) (pull_request) Successful in 7s
Security Audit / PNPM Audit (JS SCA) (pull_request) Failing after 16s
Security Audit / Trivy (SCA & IaC) (pull_request) Failing after 19s
CI / Client — test & lint (pull_request) Successful in 38s
Security Audit / Semgrep (SAST) (pull_request) Failing after 1m3s
CI / Server — test & lint (pull_request) Successful in 5m18s
Security Audit / Cargo Audit (Rust SCA) (pull_request) Failing after 6m51s
e34bf2e0b6
icub3d merged commit bf44cbaba6 into main 2026-07-09 03:30:58 +00:00
icub3d deleted branch feature/76-signing-oracle-csp 2026-07-09 03:30:58 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
icub3d/decentcom!101
No description provided.