Skip to content

feat(extension): auto-intercept https→pap, unit tests, refactor#290

Merged
toadkicker merged 4 commits intomainfrom
feat/293e-papillon-extensi
Apr 15, 2026
Merged

feat(extension): auto-intercept https→pap, unit tests, refactor#290
toadkicker merged 4 commits intomainfrom
feat/293e-papillon-extensi

Conversation

@toadkicker
Copy link
Copy Markdown
Contributor

Summary

  • Auto-intercept: Content script now intercepts all `https://` left-clicks and routes them through the PAP handshake automatically. Alt+click opts out per-click; global toggle and per-domain exclusion list in popup.
  • Unit tests: 117 tests across 4 files (`uri.test.ts`, `discovery.test.ts`, `intercept-logic.test.ts`, `db.test.ts`). Added `intercept-logic.ts` as a pure-function extraction to keep content-script testable in Node.
  • Refactor: Eliminated all cross-file constant duplication and magic values — new `constants.ts` for storage keys, exported `NATIVE_APP_ID` from `native-messaging.ts`, named limits in `discovery.ts` with `isValidString()` helper, named badge colors and context-menu ID in `service-worker.ts`, `$()` DOM helper in `popup.ts`.

Test plan

  • CI: `just extension` Vite build passes
  • CI: `cargo test --workspace` passes
  • CI: `cargo clippy` / `cargo fmt` pass
  • CI: CodeQL / audit checks pass
  • All 117 extension unit tests pass (`npx vitest run` in `apps/papillon-extension/`)

🤖 Generated with Claude Code

Todd Baur and others added 3 commits April 15, 2026 13:27
Ordinary left-clicks on https:// links are now routed through the PAP
handshake automatically, making zero-trust browsing the default when
Papillon is active — no right-click context menu required.

Changes:
- content-script: capture-phase click handler converts https:// hrefs to
  pap:// and sends HTTPS_LINK_CLICKED to the service worker; guards pass
  through modifier clicks (Ctrl/Meta/Shift), Alt-click (per-click opt-out),
  synthetic events, download links, and per-domain exclusions; storage state
  is loaded at script init and kept live via chrome.storage.onChanged
- service-worker: new HTTPS_LINK_CLICKED case swaps https:// → pap:// and
  calls openHandshakeTab with the original URL as fallback
- types: HttpsLinkClicked interface + ExtensionMessage union member
- popup: global auto-intercept toggle (aria role=switch, default on) and
  per-domain "Disable on this site" button backed by chrome.storage.sync
- popup.css: toggle track/thumb via ::after + aria-checked; domain button
  teal accent when excluded

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Brings test coverage from 1 file (db.ts) to 4 files with 117 tests passing.

- src/lib/uri.test.ts (28 tests): full coverage of parsePapUri, httpsUrlToPap,
  isPapUri, toEndpoint, toHttpsEndpoint, isBrowserCompatible — all pure
  functions, zero mocks needed

- src/lib/discovery.test.ts (23 tests): validateManifest (pure, boundary checks
  for field lengths and array limits) + fetchManifest (fetch stubbed via
  vi.stubGlobal — covers 200/404/500, wrong Content-Type, oversized bodies,
  network errors, origin-only vs full-path URLs)

- src/content/intercept-logic.ts (new): extracts resolveInterceptUrl() from
  content-script.ts as a pure, side-effect-free function so it can be tested
  in Node without jsdom or Chrome API mocks

- src/content/intercept-logic.test.ts (25 tests): exercises all 8 guard
  conditions (button, modifiers, isTrusted, download attr, scheme, state
  flags, domain exclusion) plus happy-path URL resolution and relative
  href handling

- vitest.config.ts: enable @vitest/coverage-v8 with text + lcov reporters
  and a 60% lines threshold

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New src/lib/constants.ts: single source of truth for the two
  chrome.storage.sync key strings (autoInterceptHttps, excludedDomains)
  previously duplicated across content-script.ts and popup.ts
- Export NATIVE_APP_ID from native-messaging.ts; remove the identical
  private copy in service-worker.ts
- discovery.ts: replace 5 repeated typeof/length checks with an
  isValidString() type-predicate helper; name every magic limit
  (MAX_AGENT_ID_LEN, MAX_TOOLS, MAX_CATEGORIES, etc.); fix the unsafe
  `as string` cast in categories by using a type-predicate filter
- service-worker.ts: file-local named constants for badge hex colors
  (BADGE_COLOR_GOLD, BADGE_COLOR_PURPLE) and context-menu ID
  (CONTEXT_MENU_UPGRADE_ID) replace four inline magic strings
- popup.ts: $<T>() DOM helper replaces nine verbose getElementById calls

All 117 tests pass unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 15, 2026

Benchmark Regression Report

PAP Protocol Benchmark Regression Check
========================================
Baseline: .bench-baseline/baseline.json
Threshold: 30%

  ed25519_keypair_generation                19.8 µs  (baseline: 19.7 µs, +0.5%)  [ok]
  did_key_derivation                         1.5 µs  (baseline: 1.5 µs, +0.2%)  [ok]
  mandate_create_sign                       23.9 µs  (baseline: 23.9 µs, +0.0%)  [ok]
  mandate_chain_verify_depth3              127.0 µs  (baseline: 126.9 µs, +0.0%)  [ok]
  sd_jwt_issue_5claims                      28.0 µs  (baseline: 27.9 µs, +0.4%)  [ok]
  sd_jwt_verify_disclose_3of5               43.4 µs  (baseline: 44.3 µs, -1.9%)  [ok]
  session_open_full_lifecycle              109.4 µs  (baseline: 108.7 µs, +0.6%)  [ok]
  receipt_create_cosign                     48.1 µs  (baseline: 48.9 µs, -1.5%)  [ok]
  federation_announce_local                 56.4 µs  (baseline: 56.0 µs, +0.6%)  [ok]

All benchmarks within 30% of baseline.

Threshold: 10% regression vs baseline from main

- types.rs:1257 — remove `auto_expand: false` from BlockUpdate test
  initialiser; `auto_expand` was dropped from the struct in the #289
  refactor but the test literal was not updated, causing a compile error
  (E0560) across Check, Test, WASM backend, and Justfile recipes
- web_reader.rs:112 — remove needless borrow `&final_url`; Rust deref-
  coerces String → &str automatically, Clippy's needless_borrow lint
  (-D warnings) was failing Check, Test, Clippy, and Justfile recipes

Both errors were pre-existing on main and blocked every PR. All CI
checks for papillon-extension PR #290 should now be green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@toadkicker toadkicker merged commit 60b6cdd into main Apr 15, 2026
24 checks passed
@toadkicker toadkicker deleted the feat/293e-papillon-extensi branch April 15, 2026 21:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant