feat: add Kernel browser extension with domain allowlist support#2
feat: add Kernel browser extension with domain allowlist support#2ulziibay-kernel wants to merge 7 commits into
Conversation
New extension at examples/kernel-browser-extension/ that only signs requests matching an explicit domain allowlist, preventing interference with challenge flows like Cloudflare Turnstile. Key differences from the upstream examples/browser-extension/: - Allowlist-based: only signs requests matching signDomains patterns - Request type filtering via signTypes (default: main_frame, xmlhttprequest) - Runtime config via chrome.storage.local (updateable via CDP) - Build-time injection of SIGN_DOMAINS, SIGN_TYPES, SIGNATURE_AGENT_URL Made-with: Cursor
The type filter alone wasn't sufficient because challenge orchestration requests on the same domain (e.g. /cdn-cgi/challenge-platform/) were being signed. Two fixes: 1. Default signTypes changed from ["main_frame","xmlhttprequest"] to ["main_frame"] -- only top-level navigations get signed by default 2. Added EXCLUDED_PATHS for /cdn-cgi/challenge-platform/ and /cdn-cgi/challenge/ as a safety net within allowlisted domains Made-with: Cursor
…only Made-with: Cursor
Made-with: Cursor
|
Firetiger deploy monitoring skipped This PR didn't match the auto-monitor filter configured on your GitHub connection:
Reason: PR adds a browser extension in examples/ directory, not changes to API endpoints (packages/api/cmd/api/) or Temporal workflows (packages/api/lib/temporal) as specified in the filter. To monitor this PR anyway, reply with |
The domain allowlist added unnecessary complexity. The core fix is simpler: only sign main_frame navigation requests, which prevents interference with challenge flows (Cloudflare Turnstile, etc.) while signing every navigation the agent makes. - Removed signDomains config and domain matching logic - Removed signTypes config (hardcoded to main_frame) - Removed chrome.storage.local runtime config - Removed storage permission from manifest - Removed SIGN_DOMAINS/SIGN_TYPES env var injection from build script - Kept SIGNATURE_AGENT_URL injection (still needed) Made-with: Cursor
Made-with: Cursor
| }, | ||
| { urls: ["<all_urls>"] }, | ||
| ["blocking", "requestHeaders"], | ||
| ); |
There was a problem hiding this comment.
Domain allowlist filtering is completely missing
High Severity
The PR's core feature — domain allowlist support via signDomains glob patterns — is entirely absent from the implementation. The onBeforeSendHeaders listener only checks details.type !== "main_frame" but performs no domain filtering, so every main_frame request to every URL gets signed. The SIGN_DOMAINS and SIGN_TYPES env var injection described in the PR is also missing from build_web_artifacts.mjs, the chrome.storage.local runtime config code is absent, and the storage permission is not in manifest.json. The extension currently behaves identically to the upstream extension (minus sub-resource signing), defeating its stated purpose.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 8c02137. Configure here.
Adds path-based filtering so any request whose URL path contains "cdn-cgi/" is passed through without a Signature. Cloudflare's challenge orchestration (Turnstile, challenge-platform) lives under /cdn-cgi/, and signing those requests breaks the challenge flow with "Incompatible browser extension". Layers on top of the existing main_frame-only rule as a safety net for top-level navigations that land on a /cdn-cgi/ URL. Made-with: Cursor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 536ac81. Configure here.
|
|
||
| this.privateKey = fullSecretKey; | ||
| this.keyid = KEY_ID; | ||
| } |
There was a problem hiding this comment.
Key ID set asynchronously
Medium Severity
KEY_ID is initialized to "not-set-yet" and updated only after async jwkToKeyID resolves, while each signed request builds a new Ed25519Signer that copies the current KEY_ID synchronously. Early navigations can emit signatures verifiers reject.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 536ac81. Configure here.


Summary
examples/kernel-browser-extension/-- a Kernel-specific WBA extension that only signsmain_framenavigation requestsexamples/browser-extension/is unchangedKey differences from upstream extension
browser-extension)kernel-browser-extension)main_framenavigations onlySIGNATURE_AGENT_URLWhy main_frame only?
Per the WBA RFC, the agent's "request" is the navigation it initiates. Sub-resource fetches (scripts, stylesheets, challenge JS) are the browser's internal behavior, not the agent's intent. Signing them causes Cloudflare's Turnstile verification to fail because the challenge orchestration requests at
/cdn-cgi/challenge-platform/get unexpectedSignatureheaders.Test results
Tested against
web-bot-auth.org(Cloudflare free tier, Bot Fight Mode enabled, Worker origin) with headless Kernel browsers. Reproduced 3 times across separate runs:Test plan
main_framerequests (verified via Playwright route intercept)Note
Low Risk
Adds an isolated example under
examples/with no changes to core libraries or the upstream extension; signing scope is intentionally narrower to reduce interference with third-party challenge flows.Overview
Adds a new
examples/kernel-browser-extension/example: a Chrome MV3 extension that signs top-level navigations with RFC 9421 HTTP message signatures viaweb-bot-auth, packaged for enterprise force-install (CRX,update.xml, Chrome policy templates).Signing behavior differs from the upstream
browser-extension: theonBeforeSendHeadershandler only addsSignature/Signature-Input(and optionalSignature-Agent) formain_framerequests, skips paths containingcdn-cgi/, and leaves sub-resources unsigned so challenge flows (e.g. Cloudflare Turnstile) are not broken.Build pipeline uses tsup for the service worker,
build_web_artifacts.mjsto injectSIGNATURE_AGENT_URL, stamp manifest version, pack CRX, and fill policy templates with the generated extension ID;private_key.pemand generated policy files are gitignored.Reviewed by Cursor Bugbot for commit 536ac81. Bugbot is set up for automated code reviews on this repo. Configure here.