Skip to content

Commit a86b02f

Browse files
committed
docs: add masking-surfaces documentation and link it in README
1 parent c8f856a commit a86b02f

2 files changed

Lines changed: 110 additions & 0 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ site behind a controlled privacy boundary.
1919
- Fail closed when a request or execution path is unknown or unsafe.
2020
- Verify compatibility with native-browser-vs-ZeroProxy browser tests.
2121

22+
For the full target-visible masking inventory, see
23+
[`docs/masking-surfaces.md`](docs/masking-surfaces.md).
24+
2225
## How It Works
2326

2427
ZeroProxy loads the target page inside its own controlled origin, prepares the

docs/masking-surfaces.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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

Comments
 (0)