Skip to content

security: relayUrl validation, remove dead pending code, fix about:blank#3

Open
DeetBot wants to merge 30 commits into
Unayung:masterfrom
bazfer:security-review-fixes
Open

security: relayUrl validation, remove dead pending code, fix about:blank#3
DeetBot wants to merge 30 commits into
Unayung:masterfrom
bazfer:security-review-fixes

Conversation

@DeetBot
Copy link
Copy Markdown

@DeetBot DeetBot commented May 22, 2026

Summary

  • HIGH fix: getRelayUrl() now validates that the stored URL has wss:// scheme before connecting. An invalid value falls back to DEFAULT_RELAY_URL with a console.warn. Without this, a compromised chrome.storage could redirect the extension to an attacker relay and exfil the auth token.
  • HIGH fix (options.js): save() validates wss:// scheme before writing to storage. Shows an error in the UI instead of silently persisting a bad URL.
  • Dead code removal: Remove requestFromRelay, the pending map, and all usage sites. The function was introduced early as infrastructure but never called. PR #11 added a timeout to it — still uncalled. Two usage sites (onRelayClosed pending drain, onRelayMessage pending dispatch) were also unreachable.
  • MEDIUM fix: Target.createTarget now allows about: alongside http: and https:. PR #10 added the scheme allowlist but omitted about:, breaking the about:blank default that Chrome DevTools uses when opening new tabs. Also restores about:blank as the fallback URL when params.url is absent (PR #10 had changed it to empty string, which threw Invalid URL).

Test plan

  • Set an http:// URL in extension options → save rejected with error message
  • Set wss://127.0.0.1:18792 → saves and connects normally
  • Target.createTarget with no URL → creates about:blank tab and attaches
  • Target.createTarget with javascript:alert(1) → throws "URL scheme not allowed"
  • Confirm no requestFromRelay or pending references remain

DeetBot and others added 30 commits April 2, 2026 23:23
- New readme reflecting Artífice branding and architecture
- Credits upstream (OpenClaw + Unayung resilient fork)
- Documents hybrid architecture (extension + Tauri app)
- Security model and install instructions
- manifest.json: name, description, tooltip rebranded
- icons: resized from Artífice dark brand asset (16/32/48/128px)
- Manifest validation (MV3, required fields)
- Required files check
- Icon sizes check
- JS syntax check (node --check)
- Secret scanning
- Title, heading, subtitle rebranded
- Accent color: OpenClaw orange → Artífice gold (#c8962e)
- Getting started text updated (references Artífice app, not OpenClaw)
- Gateway token → Client token
- Hints reference Artífice Connect app pairing
rebrand: options page for Artífice Connect
…nd options.js

- background.js: All console.log/warn prefixes [OpenClaw Relay] → [Artífice Relay]
- background.js: All chrome.action.setTitle strings OpenClaw Browser Relay → Artífice Connect
- options.js: HTTP header x-openclaw-relay-token → x-artifice-relay-token
- options.js: User-facing error message rebranded
- No functional logic, auth, or WebSocket behavior changed
rebrand: replace OpenClaw references with Artífice in background.js and options.js
- Replace localhost:port with configurable relay URL (default wss://relay.artificeia.mx)
- Token passed as query param (browser WebSocket can't set headers)
- Options page updated with URL field instead of port
- host_permissions updated for any relay domain
feat: configurable public WSS relay URL
- Bump version to 1.0.0
- Narrow host_permissions to relay.artificeia.mx
- Add privacy policy
- Update README
- Add homepage_url
chore: Chrome Web Store submission prep
fix: update options page copy
chore: final cleanup for CWS submission
Google rejected with 'Purple Potassium' — tabs permission unnecessary
since activeTab covers all implemented functionality.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove tabs permission (Chrome Web Store rejection)
Token no longer passed as ?token= query param — no more exposure
in server logs, Caddy access logs, or browser history. On open,
extension sends { method: 'auth', token } as first message and
waits for { method: 'auth', ok: true } before resolving.
Timeout bumped to 10s to cover auth round-trip.

Co-authored-by: Fernando Baz <bazfer@gmail.com>
Relay now requires version: 1 in the auth handshake. Without it the relay
rejects the connection with a protocol version mismatch error.
Send protocol version in auth message
…ed sendToRelay throws

- Target.createTarget now rejects non-http/https URLs (blocks file://, data:, blob: etc.)
- sendToRelay in error reply path (onRelayMessage) wrapped in try/catch
- sendToRelay in attachTab tab-announce path wrapped in try/catch
Security: URL scheme allowlist for Target.createTarget; catch unhandled sendToRelay throws
Relay-side timeout is 30s — if relay never replies, extension pending
entry leaked forever. 35s timer ensures cleanup slightly after relay
gives up, preventing unbounded map growth on long sessions.
Clear pending map entries after 35s timeout
- getRelayUrl(): validate wss:// scheme — invalid storage value falls back
  to DEFAULT_RELAY_URL with console.warn; prevents token exfil to attacker
  relay if chrome.storage is compromised
- options.js save(): reject non-wss:// URLs before writing to storage
- Remove requestFromRelay, pending map, and all usage sites — never called;
  dead code introduced by PR #11 adding a timeout to an uncalled function
- Target.createTarget: add about: to allowed URL schemes alongside http/https
  fixes regression from PR #10 where about:blank (DevTools default) was blocked
- H1 restoreState: verify targetId after re-attach — drop session if tab navigated away
  between worker death and restart (Chrome reuses tab IDs)
- H2/M1 handleForwardCdpCommand: unknown sessionId throws instead of falling through;
  sessionId-less fallback only resolves when exactly one tab is attached
- M2 attachTab: fix attachOrder off-by-one (was nextSession post-increment, now pre-capture)
- M3 checkRelayReachable: remove misleading 401='token rejected' message — probe sends
  no token so status code tells us nothing about token validity
- M4 ensureRelayConnection: null out stale socket handlers and close before opening new
  WebSocket; fix timeout handler to close socket before rejecting
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.

2 participants