|
| 1 | +# Masking Surfaces |
| 2 | + |
| 3 | +ZeroProxy keeps two URL and browser identities at the same time: |
| 4 | + |
| 5 | +- the browser/proxy identity, used by the real browser, Service Worker, and |
| 6 | + `/zp/...` control routes; |
| 7 | +- the target identity, exposed back to page JavaScript as if the page had loaded |
| 8 | + directly from the target origin. |
| 9 | + |
| 10 | +This document lists the current masking surfaces in the page runtime, worker |
| 11 | +runtime, and static rewriter. It is a maintenance reference for places where a |
| 12 | +proxy URL, ZeroProxy helper, or browser fingerprint signal can otherwise leak |
| 13 | +into target-visible JavaScript. |
| 14 | + |
| 15 | +## Global Artifacts |
| 16 | + |
| 17 | +| Surface | Masked behavior | Backing details | |
| 18 | +|---|---|---| |
| 19 | +| `Function.prototype.toString` | Patched ZeroProxy functions and accessors stringify like native browser functions. | `web/runtime/abi/artifact-masking.mjs`, `web/runtime-prelude.mjs`, `web/worker-prelude.js` maintain WeakMap-backed source strings. | |
| 20 | +| Global object enumeration | ZeroProxy globals are hidden from `Object.keys`, `Object.getOwnPropertyNames`, `Object.getOwnPropertySymbols`, `Object.getOwnPropertyDescriptor(s)`, and `Reflect.ownKeys`. | Hidden names include `ZP`, `ZPRewriter`, `ZPRustRewriter`, `ZPHTTPRewriter`, `__ZP_*`, `__zp_*`, and ZeroProxy-marked symbols. | |
| 21 | +| Runtime DOM artifacts | Runtime asset scripts and ZeroProxy-only attributes are filtered from DOM enumeration and serialization. | `data-zp-*` attributes are hidden from `getAttribute*`, `hasAttribute`, `getAttributeNames`, and `NamedNodeMap`-style access. | |
| 22 | +| Selector probes | Selectors that explicitly target ZeroProxy internals return no match. | Examples include `data-zp-*`, `#__zp-boot`, `/zp/assets/`, `/zp/api/`, `src*=zp`, `zeroproxy`, and `x-zeroproxy-icon`. | |
| 23 | + |
| 24 | +## Document, Location, and History |
| 25 | + |
| 26 | +| Surface | Masked behavior | Backing details | |
| 27 | +|---|---|---| |
| 28 | +| `window.location` and `location` methods | `href`, `origin`, `protocol`, `host`, `hostname`, `port`, `pathname`, `search`, `hash`, `assign`, `replace`, `reload`, `toString`, `valueOf`, and `Symbol.toPrimitive` expose the virtual target URL. | Browser navigation still commits to encrypted share routes such as `/zp/p/<route>#k=...`. | |
| 29 | +| `document.URL`, `document.documentURI`, `document.baseURI`, `document.referrer` | Return target-visible document, base, and referrer values. | `baseURI` tracks target `<base href>` state without exposing the proxy route. | |
| 30 | +| `window.origin` | Returns the virtual target origin. | Defined from the visible document URL. | |
| 31 | +| `history.pushState` and `history.replaceState` | Same-origin checks and stored visible URLs use the virtual target origin. | The real browser history URL is refreshed to a share route. | |
| 32 | +| `hashchange` events | `oldURL` and `newURL` are target-visible URLs. | The proxy URL is not exposed through the event object. | |
| 33 | +| Child frame `location` | Contained frame scopes expose target-visible `Location` facades. | Cross-window access is mediated by child and boundary window proxies. | |
| 34 | + |
| 35 | +## URL Attributes and HTML Serialization |
| 36 | + |
| 37 | +| Surface | Masked behavior | Backing details | |
| 38 | +|---|---|---| |
| 39 | +| Navigation attributes | `a[href]`, `area[href]`, `form[action]`, `button[formaction]`, and `input[formaction]` read back as target URLs. | The raw browser attribute can be a share route or rewritten control URL. Dynamic relative anchor `href`s are asynchronously rewritten to share URLs while page getters keep returning the target URL. | |
| 40 | +| Resource attributes | `src`, `poster`, SVG `href`/`xlink:href`, stylesheet `href`, and related resource URLs read back as target URLs. | The real fetch path is commonly `/zp/api/fetch?...` or another ZeroProxy API route. | |
| 41 | +| `srcset` | Page reads see the target `srcset` list. | Rewritten fetch URLs are stored separately from the visible list. | |
| 42 | +| Script URLs | `script[src]` reads as the target script URL. | Executable scripts load through `/zp/api/script?...`; dynamic import and import maps are rewritten similarly. | |
| 43 | +| Script `nonce` | Page reads preserve the target-visible nonce. | Executed scripts are assigned the runtime nonce `zp`; the original value is backed by `data-zp-target-nonce`. | |
| 44 | +| Subresource `integrity` | Page reads preserve the target-visible integrity value. | The browser-visible `integrity` attribute is removed and backed by `data-zp-integrity` so rewritten bytes are not blocked by the original hash. | |
| 45 | +| Link relations | Stylesheet links expose target `href`s; icon links are suppressed to an internal data URL; preload, prefetch, preconnect, and similar speculative links are blocked or removed. | Original blocked relation and URL state is backed by hidden `data-zp-*` attributes. | |
| 46 | +| Navigation `target` | New-window targets are forced to safe same-context navigation. | Original targets are backed by `data-zp-blocked-target` and hidden from page reads. | |
| 47 | +| `base[href]` | Updates the virtual base URL without exposing the proxy backing route. | Serialization omits the ZeroProxy backing state. | |
| 48 | +| Meta policies | CSP and referrer policy tags are suppressed or virtualized when they would conflict with the membrane. | Blocked policy state is stored in hidden attributes. | |
| 49 | +| `innerHTML` and `outerHTML` getters | Serialized HTML removes ZeroProxy helper nodes and `data-zp-*` attributes and restores target-visible URLs, `srcset`, script state, nonce, and integrity. | Script text that contains ZeroProxy internals is not exposed through serialization. | |
| 50 | +| HTML insertion APIs | `innerHTML`, `outerHTML`, `insertAdjacentHTML`, `DOMParser`, templates, `document.write`, and `srcdoc` content are rewritten before execution. | Inserted markup receives the same URL, script, style, and policy treatment as static HTML. | |
| 51 | + |
| 52 | +## Network APIs |
| 53 | + |
| 54 | +| Surface | Masked behavior | Backing details | |
| 55 | +|---|---|---| |
| 56 | +| `fetch`, `Request`, `Response`, and `Headers` | Page code uses target URLs and normal fetch-shaped objects. | Requests route through ZeroProxy APIs and the Service Worker; upload streams are relayed through runtime-controlled channels. | |
| 57 | +| `XMLHttpRequest` | `responseURL`, request URL handling, events, upload object, and response fields are facade-backed. | Internal state slots are hidden on the XHR instance. | |
| 58 | +| `sendBeacon` | Beacon targets resolve against the virtual target URL. | Native beacon transport is replaced by the ZeroProxy fetch path. | |
| 59 | +| `WebSocket` and `WebSocketStream` | Constructors accept target `ws:`/`wss:` URLs. | Connections are opened through the ZeroProxy worker/kernel relay. | |
| 60 | +| `EventSource` | Event streams resolve against target URLs and route through the membrane. | Backing transport is proxied. | |
| 61 | +| Blocked network APIs | `RTCPeerConnection`, `WebTransport`, and unsupported direct network surfaces are blocked. | These fail closed with browser-shaped errors. | |
| 62 | + |
| 63 | +## Performance and Fingerprinting |
| 64 | + |
| 65 | +| Surface | Masked behavior | Backing details | |
| 66 | +|---|---|---| |
| 67 | +| `navigator.userAgent`, `appVersion`, `platform` | A fixed target persona is exposed. | Current runtime persona is Windows/Chrome-shaped in page and worker scopes. | |
| 68 | +| `navigator.userAgentData` | Brands, full versions, architecture, bitness, platform, platform version, model, mobile, and WoW64 fields are fixed. | `getHighEntropyValues` and `toJSON` are native-masked. | |
| 69 | +| Canvas readback | `CanvasRenderingContext2D.getImageData` and `HTMLCanvasElement.toDataURL` add small noise. | Used to reduce stable canvas fingerprint signals. | |
| 70 | +| Audio readback | `AudioBuffer.getChannelData` adds tiny noise to the first non-zero sample. | Used to reduce stable audio fingerprint signals. | |
| 71 | +| `performance.getEntries*` | Entry names expose target URLs, not `/zp/api/...` or asset URLs. | `rust-rewriter.wasm` and ZeroProxy assets are hidden; script timings can be synthesized for rewritten scripts. | |
| 72 | +| `PerformanceObserver` | Observer entry lists are wrapped so resource and navigation names are target-visible. | `takeRecords` and list query methods apply the same masking. | |
| 73 | + |
| 74 | +## Cookies and Storage |
| 75 | + |
| 76 | +| Surface | Masked behavior | Backing details | |
| 77 | +|---|---|---| |
| 78 | +| `document.cookie` | Reads and writes operate on target-origin cookie records. | Cookie records are synced with the Service Worker and filtered by domain, path, secure, SameSite, and expiry rules. | |
| 79 | +| `localStorage` and `sessionStorage` | Storage is scoped to `zp:<target-origin>:local` and `zp:<target-origin>:session`, while page code sees ordinary Storage objects. | Native localStorage and IndexedDB are used as backing stores. | |
| 80 | +| `storage` events | Event `url` is the virtual target URL. | Events are dispatched between runtime-managed same-origin windows. | |
| 81 | +| `indexedDB` | Database names are target-visible. | Native database names are prefixed with `zp:<target-origin>:idb:` and stripped in `indexedDB.databases()`. | |
| 82 | +| `caches` | Cache names are target-visible. | Native cache names are prefixed with `zp:<target-origin>:cache:` and stripped in `caches.keys()`. | |
| 83 | + |
| 84 | +## Frames, Messaging, and Workers |
| 85 | + |
| 86 | +| Surface | Masked behavior | Backing details | |
| 87 | +|---|---|---| |
| 88 | +| Child frame globals | `window`, `self`, `globalThis`, and `frames` resolve to a scoped facade; `top`, `parent`, and `opener` resolve to boundary facades. | Raw frame windows are wrapped to keep target/proxy identity separated. | |
| 89 | +| `postMessage` target origin | Target origins are accepted and mapped to the proxy origin when needed. | Message dispatch still uses the browser-required proxy origin underneath. | |
| 90 | +| `MessageEvent.origin` and `MessageEvent.source` | Event origin and source are virtualized back to target-visible values. | Source windows are wrapped with the appropriate facade. | |
| 91 | +| Worker `location` and `origin` | Dedicated and shared workers see target-visible worker URLs and origins. | Worker scripts load through `/zp/api/worker-script?...` and `worker-prelude.js`. | |
| 92 | +| Worker global enumeration | Worker `location`, `fetch`, `importScripts`, and ZeroProxy internals are hidden from global enumeration. | Worker `Object.*` and `Reflect.ownKeys` are patched similarly to page globals. | |
| 93 | +| Worker network APIs | `fetch` and `importScripts` accept target URLs; direct network APIs are blocked where unsupported by the membrane. | Worker fetches and scripts route through ZeroProxy APIs. | |
| 94 | +| Worker object URLs | Worker `URL.createObjectURL` and `revokeObjectURL` are wrapped for script blobs. | Page-visible blob worker URLs can differ from the internal wrapper blob. | |
| 95 | + |
| 96 | +## Known Limits |
| 97 | + |
| 98 | +- Browser-devtools protocol, native browser internals, and network logs can still |
| 99 | + observe browser/proxy backing URLs. Masking is for target page JavaScript, not |
| 100 | + for the local operator's debugging tools. |
| 101 | +- Exact CSS selector semantics for rewritten target URL attributes are not fully |
| 102 | + virtualized. Page getters return target URLs, and explicit ZeroProxy probes are |
| 103 | + blocked, but selectors that compare raw rewritten attribute values can still be |
| 104 | + limited by the browser's underlying DOM matcher. |
| 105 | +- The static Rust rewriter and dynamic JavaScript runtime must stay aligned: |
| 106 | + static markup, dynamically assigned attributes, inserted HTML, workers, and |
| 107 | + frame `srcdoc` all need the same target-visible contract. |
0 commit comments