@webhands/core 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -88,8 +88,10 @@ deliberately local and single-session by design.
88
88
  does NOT bypass authentication or solve CAPTCHAs programmatically, and it is not
89
89
  intended to.
90
90
  - **No fingerprint-spoofing / anti-detect tricks.** It leans on being a *real*
91
- browser/profile/IP rather than spoofing. There is no proxy rotation or
92
- anti-detect build here.
91
+ browser/profile/IP rather than spoofing. There is no proxy *rotation* or
92
+ anti-detect build here. (A single, user-chosen SOCKS proxy for traffic/DNS
93
+ control is available opt-in via `--proxy`; see *Optional: route traffic and
94
+ DNS through a SOCKS proxy* below.)
93
95
  - **Your own session only.** A replayed/stolen cookie does not work anyway
94
96
  (clearance is bound to the browser fingerprint and IP, not just the cookie);
95
97
  the design assumes the session is genuinely yours.
@@ -174,6 +176,51 @@ reputation still matter. The realistic recipe is stealth +
174
176
  IP (see
175
177
  [`docs/adr/0002`](docs/adr/0002-real-session-over-fingerprint-spoofing.md)).
176
178
 
179
+ ## Optional: route traffic and DNS through a SOCKS proxy (opt-in, default OFF)
180
+
181
+ By default webhands connects directly on your own machine and IP. If you want
182
+ the browser to egress through a chosen SOCKS proxy (a VPN exit, an SSH/Tor SOCKS
183
+ endpoint, a residential proxy), pass `--proxy <socks-url>` to `serve` (or
184
+ `launch`). It routes **all** browser traffic AND DNS through that one proxy:
185
+
186
+ ```sh
187
+ # socks5h:// tunnels DNS through the proxy too (no DNS leak):
188
+ npx webhands serve --headed --proxy socks5h://127.0.0.1:1080
189
+
190
+ # with credentials:
191
+ npx webhands serve --proxy socks5h://user:pass@host:1080
192
+ ```
193
+
194
+ - **`socks5h://` means no DNS leak.** webhands adds Chromium's
195
+ `--host-resolver-rules` catch-all so even side channels (the DNS prefetcher)
196
+ cannot leak a raw local DNS query; only the proxy's own host is resolved
197
+ locally. This is the recommended form.
198
+ - **`socks5://` (or `socks://`) allows local DNS.** Use it when you deliberately
199
+ want split DNS. URL loads still resolve at the proxy, but Chromium may issue
200
+ some local DNS. Override either way with the programmatic `proxyNoLeak`
201
+ option.
202
+ - **A malformed `--proxy` value fails loudly** with a typed `InvalidProxyError`
203
+ (it never silently launches unproxied, which would leak the traffic you asked
204
+ to tunnel).
205
+
206
+ Programmatic equivalent:
207
+
208
+ ```ts
209
+ import {PlaywrightLaunchTransport} from '@webhands/core';
210
+
211
+ const transport = new PlaywrightLaunchTransport(
212
+ {}, // profile location
213
+ [], // extra hands
214
+ {proxy: 'socks5h://127.0.0.1:1080'}, // all traffic + DNS via the proxy, no leak
215
+ );
216
+ ```
217
+
218
+ **Honest caveat.** A proxy changes your IP and DNS path; it does **not** by
219
+ itself defeat bot detection, and a proxy/VPN/datacenter IP often reads WORSE
220
+ than a clean residential one. This is a deliberate, scoped opt-in deviation from
221
+ the "own IP" default (see
222
+ [`docs/adr/0009`](docs/adr/0009-opt-in-socks-proxy-all-traffic-and-dns.md)).
223
+
177
224
  ## Security note (the `serve` endpoint runs arbitrary code)
178
225
 
179
226
  The page verbs execute caller-supplied expressions: `eval` runs a JS expression
package/dist/errors.d.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  * has to re-derive paths.
16
16
  */
17
17
  /** The closed set of identifiable `core` error conditions. */
18
- export type ControllerErrorCode = 'missing-browser-binary' | 'missing-stealth-dependency' | 'missing-profile' | 'attach-not-chromium' | 'attach-no-context' | 'no-live-server' | 'session-already-active';
18
+ export type ControllerErrorCode = 'missing-browser-binary' | 'missing-stealth-dependency' | 'invalid-proxy' | 'missing-profile' | 'attach-not-chromium' | 'attach-no-context' | 'no-live-server' | 'session-already-active';
19
19
  /**
20
20
  * Base class for every identifiable `core` error. Branch on {@link code}.
21
21
  *
@@ -68,6 +68,26 @@ export declare class MissingStealthDependencyError extends ControllerError {
68
68
  cause?: unknown;
69
69
  });
70
70
  }
71
+ /**
72
+ * The `--proxy` value (a SOCKS URL) could not be parsed into a usable proxy
73
+ * config. webhands routes ALL traffic and DNS through one SOCKS proxy, so the
74
+ * value must be a `socks5://` or `socks5h://` URL with a host and port (an
75
+ * optional `user:pass@` is allowed). We refuse a malformed value LOUDLY with
76
+ * this typed condition rather than silently launching with no proxy (which
77
+ * would leak the very traffic the user asked to tunnel). The CLI maps the
78
+ * {@link code} to a fix message showing the expected URL shape.
79
+ *
80
+ * Mirrors {@link MissingStealthDependencyError}: a stable typed error whose
81
+ * brittle detection is confined to one spot (the proxy parser).
82
+ */
83
+ export declare class InvalidProxyError extends ControllerError {
84
+ readonly code = "invalid-proxy";
85
+ /** The offending raw `--proxy` value, echoed back so the user can see it. */
86
+ readonly value: string;
87
+ constructor(value: string, message?: string, options?: {
88
+ cause?: unknown;
89
+ });
90
+ }
71
91
  /**
72
92
  * The named profile has not been set up yet: its dedicated profile directory
73
93
  * does not exist on disk. A profile is created by the headed `setup-profile`
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,8DAA8D;AAC9D,MAAM,MAAM,mBAAmB,GAC5B,wBAAwB,GACxB,4BAA4B,GAC5B,iBAAiB,GACjB,qBAAqB,GACrB,mBAAmB,GACnB,gBAAgB,GAChB,wBAAwB,CAAC;AAE5B;;;;;;GAMG;AACH,8BAAsB,eAAgB,SAAQ,KAAK;IAClD,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAC5C,8EAA8E;IAC9E,QAAQ,CAAC,iBAAiB,EAAG,IAAI,CAAU;gBAE/B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAMxD;AAED;;;;GAIG;AACH,qBAAa,yBAA0B,SAAQ,eAAe;IAC7D,QAAQ,CAAC,IAAI,4BAA4B;IACzC,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAGxB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAA0D,EACnE,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAK5B;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,6BAA8B,SAAQ,eAAe;IACjE,QAAQ,CAAC,IAAI,gCAAgC;IAC7C,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAG3B,UAAU,SAAe,EACzB,OAAO,GAAE,MAA+Q,EACxR,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAK5B;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,eAAe;IACvD,QAAQ,CAAC,IAAI,qBAAqB;IAClC,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAG3B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,MAA0F,EACnG,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAM5B;AAED;;;;;;GAMG;AACH,qBAAa,sBAAuB,SAAQ,eAAe;IAC1D,QAAQ,CAAC,IAAI,yBAAyB;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAGxB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAuJ,EAChK,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAK5B;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,eAAe;IACxD,QAAQ,CAAC,IAAI,uBAAuB;IACpC,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAGzB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,MAA+H,EACxI,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAK5B;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAkB,SAAQ,eAAe;IACrD,QAAQ,CAAC,IAAI,oBAAoB;gBAGhC,OAAO,GAAE,MAAoF,EAC7F,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAI5B;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,eAAe;IAC7D,QAAQ,CAAC,IAAI,4BAA4B;gBAGxC,OAAO,GAAE,MAAmE,EAC5E,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAI5B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAO1E"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,8DAA8D;AAC9D,MAAM,MAAM,mBAAmB,GAC5B,wBAAwB,GACxB,4BAA4B,GAC5B,eAAe,GACf,iBAAiB,GACjB,qBAAqB,GACrB,mBAAmB,GACnB,gBAAgB,GAChB,wBAAwB,CAAC;AAE5B;;;;;;GAMG;AACH,8BAAsB,eAAgB,SAAQ,KAAK;IAClD,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAC5C,8EAA8E;IAC9E,QAAQ,CAAC,iBAAiB,EAAG,IAAI,CAAU;gBAE/B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAMxD;AAED;;;;GAIG;AACH,qBAAa,yBAA0B,SAAQ,eAAe;IAC7D,QAAQ,CAAC,IAAI,4BAA4B;IACzC,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAGxB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAA0D,EACnE,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAK5B;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,6BAA8B,SAAQ,eAAe;IACjE,QAAQ,CAAC,IAAI,gCAAgC;IAC7C,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAG3B,UAAU,SAAe,EACzB,OAAO,GAAE,MAA+Q,EACxR,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAK5B;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAkB,SAAQ,eAAe;IACrD,QAAQ,CAAC,IAAI,mBAAmB;IAChC,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAGtB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,MAE8I,EACvJ,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAK5B;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,eAAe;IACvD,QAAQ,CAAC,IAAI,qBAAqB;IAClC,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAG3B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,MAA0F,EACnG,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAM5B;AAED;;;;;;GAMG;AACH,qBAAa,sBAAuB,SAAQ,eAAe;IAC1D,QAAQ,CAAC,IAAI,yBAAyB;IACtC,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAGxB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAuJ,EAChK,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAK5B;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,eAAe;IACxD,QAAQ,CAAC,IAAI,uBAAuB;IACpC,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAGzB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,MAA+H,EACxI,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAK5B;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAkB,SAAQ,eAAe;IACrD,QAAQ,CAAC,IAAI,oBAAoB;gBAGhC,OAAO,GAAE,MAAoF,EAC7F,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAI5B;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,eAAe;IAC7D,QAAQ,CAAC,IAAI,4BAA4B;gBAGxC,OAAO,GAAE,MAAmE,EAC5E,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC;CAI5B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,eAAe,CAO1E"}
package/dist/errors.js CHANGED
@@ -69,6 +69,27 @@ export class MissingStealthDependencyError extends ControllerError {
69
69
  this.dependency = dependency;
70
70
  }
71
71
  }
72
+ /**
73
+ * The `--proxy` value (a SOCKS URL) could not be parsed into a usable proxy
74
+ * config. webhands routes ALL traffic and DNS through one SOCKS proxy, so the
75
+ * value must be a `socks5://` or `socks5h://` URL with a host and port (an
76
+ * optional `user:pass@` is allowed). We refuse a malformed value LOUDLY with
77
+ * this typed condition rather than silently launching with no proxy (which
78
+ * would leak the very traffic the user asked to tunnel). The CLI maps the
79
+ * {@link code} to a fix message showing the expected URL shape.
80
+ *
81
+ * Mirrors {@link MissingStealthDependencyError}: a stable typed error whose
82
+ * brittle detection is confined to one spot (the proxy parser).
83
+ */
84
+ export class InvalidProxyError extends ControllerError {
85
+ code = 'invalid-proxy';
86
+ /** The offending raw `--proxy` value, echoed back so the user can see it. */
87
+ value;
88
+ constructor(value, message = `Invalid --proxy value ${JSON.stringify(value)}. Expected a SOCKS URL like socks5h://host:1080 or socks5://user:pass@host:1080 (socks5h tunnels DNS too; both route all traffic through the proxy).`, options) {
89
+ super(message, options);
90
+ this.value = value;
91
+ }
92
+ }
72
93
  /**
73
94
  * The named profile has not been set up yet: its dedicated profile directory
74
95
  * does not exist on disk. A profile is created by the headed `setup-profile`
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -5,9 +5,10 @@ export { StubTransport, type StubCall } from './stub-transport.js';
5
5
  export type { Hand, HandContext, HandContribution } from './hand-host.js';
6
6
  export { readHandsConfig, normalizeConfig, loadHands, HandLoadError, HANDS_CONFIG_FILENAME, type HandEntry, type HandsConfig, type LoadedHand, type LoadHandsOptions, } from './hand-loading.js';
7
7
  export { PlaywrightLaunchTransport, type PlaywrightLaunchTransportOptions, type StealthChromiumImporter, } from './playwright-launch-transport.js';
8
+ export { parseSocksProxy, hostResolverRulesArg, type ParsedSocksProxy, } from './socks-proxy.js';
8
9
  export { PlaywrightAttachTransport } from './playwright-attach-transport.js';
9
10
  export { setupProfile, buildPrompt, type PromptSink, type SetupProfileOptions, type SetupProfileResult, } from './setup-profile.js';
10
- export { ControllerError, MissingBrowserBinaryError, MissingStealthDependencyError, MissingProfileError, AttachNotChromiumError, AttachNoContextError, NoLiveServerError, SessionAlreadyActiveError, isControllerError, type ControllerErrorCode, } from './errors.js';
11
+ export { ControllerError, MissingBrowserBinaryError, MissingStealthDependencyError, InvalidProxyError, MissingProfileError, AttachNotChromiumError, AttachNoContextError, NoLiveServerError, SessionAlreadyActiveError, isControllerError, type ControllerErrorCode, } from './errors.js';
11
12
  export { resolveSessionEndpointPath, writeSessionEndpoint, readSessionEndpoint, clearSessionEndpoint, SESSION_ENDPOINT_FILENAME, type SessionEndpoint, } from './session-endpoint.js';
12
13
  export { startSessionServer, sessionAlreadyActive, type SessionServerOptions, type RunningSessionServer, } from './session-server.js';
13
14
  export { connectRemoteSession } from './remote-session.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,MAAM,EACN,MAAM,EACN,aAAa,EACb,UAAU,EACV,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,SAAS,EACT,aAAa,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AAElC,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,aAAa,GAClB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAC,aAAa,EAAE,KAAK,QAAQ,EAAC,MAAM,qBAAqB,CAAC;AAEjE,YAAY,EAAC,IAAI,EAAE,WAAW,EAAE,gBAAgB,EAAC,MAAM,gBAAgB,CAAC;AAExE,OAAO,EACN,eAAe,EACf,eAAe,EACf,SAAS,EACT,aAAa,EACb,qBAAqB,EACrB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,gBAAgB,GACrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,yBAAyB,EACzB,KAAK,gCAAgC,EACrC,KAAK,uBAAuB,GAC5B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAC,yBAAyB,EAAC,MAAM,kCAAkC,CAAC;AAE3E,OAAO,EACN,YAAY,EACZ,WAAW,EACX,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACvB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,eAAe,EACf,yBAAyB,EACzB,6BAA6B,EAC7B,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,iBAAiB,EACjB,KAAK,mBAAmB,GACxB,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,0BAA0B,EAC1B,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,yBAAyB,EACzB,KAAK,eAAe,GACpB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GACzB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EACN,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,GACvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,sBAAsB,GAC3B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,kBAAkB,EAClB,KAAK,aAAa,GAClB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAC,aAAa,EAAC,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,MAAM,EACN,MAAM,EACN,aAAa,EACb,UAAU,EACV,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,SAAS,EACT,aAAa,GACb,MAAM,WAAW,CAAC;AACnB,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AAElC,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,aAAa,GAClB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAC,aAAa,EAAE,KAAK,QAAQ,EAAC,MAAM,qBAAqB,CAAC;AAEjE,YAAY,EAAC,IAAI,EAAE,WAAW,EAAE,gBAAgB,EAAC,MAAM,gBAAgB,CAAC;AAExE,OAAO,EACN,eAAe,EACf,eAAe,EACf,SAAS,EACT,aAAa,EACb,qBAAqB,EACrB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,gBAAgB,GACrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,yBAAyB,EACzB,KAAK,gCAAgC,EACrC,KAAK,uBAAuB,GAC5B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACN,eAAe,EACf,oBAAoB,EACpB,KAAK,gBAAgB,GACrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAC,yBAAyB,EAAC,MAAM,kCAAkC,CAAC;AAE3E,OAAO,EACN,YAAY,EACZ,WAAW,EACX,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACvB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,eAAe,EACf,yBAAyB,EACzB,6BAA6B,EAC7B,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,iBAAiB,EACjB,KAAK,mBAAmB,GACxB,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,0BAA0B,EAC1B,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,yBAAyB,EACzB,KAAK,eAAe,GACpB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,GACzB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EACN,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,YAAY,EACZ,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,GACvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,sBAAsB,GAC3B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,kBAAkB,EAClB,KAAK,aAAa,GAClB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAC,aAAa,EAAC,MAAM,kCAAkC,CAAC"}
package/dist/index.js CHANGED
@@ -3,9 +3,10 @@ export { serializeCookies, deserializeCookies, COOKIES_EXPORT_VERSION, } from '.
3
3
  export { StubTransport } from './stub-transport.js';
4
4
  export { readHandsConfig, normalizeConfig, loadHands, HandLoadError, HANDS_CONFIG_FILENAME, } from './hand-loading.js';
5
5
  export { PlaywrightLaunchTransport, } from './playwright-launch-transport.js';
6
+ export { parseSocksProxy, hostResolverRulesArg, } from './socks-proxy.js';
6
7
  export { PlaywrightAttachTransport } from './playwright-attach-transport.js';
7
8
  export { setupProfile, buildPrompt, } from './setup-profile.js';
8
- export { ControllerError, MissingBrowserBinaryError, MissingStealthDependencyError, MissingProfileError, AttachNotChromiumError, AttachNoContextError, NoLiveServerError, SessionAlreadyActiveError, isControllerError, } from './errors.js';
9
+ export { ControllerError, MissingBrowserBinaryError, MissingStealthDependencyError, InvalidProxyError, MissingProfileError, AttachNotChromiumError, AttachNoContextError, NoLiveServerError, SessionAlreadyActiveError, isControllerError, } from './errors.js';
9
10
  export { resolveSessionEndpointPath, writeSessionEndpoint, readSessionEndpoint, clearSessionEndpoint, SESSION_ENDPOINT_FILENAME, } from './session-endpoint.js';
10
11
  export { startSessionServer, sessionAlreadyActive, } from './session-server.js';
11
12
  export { connectRemoteSession } from './remote-session.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AAElC,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,GAEtB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAC,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AAIjE,OAAO,EACN,eAAe,EACf,eAAe,EACf,SAAS,EACT,aAAa,EACb,qBAAqB,GAKrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,yBAAyB,GAGzB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAC,yBAAyB,EAAC,MAAM,kCAAkC,CAAC;AAE3E,OAAO,EACN,YAAY,EACZ,WAAW,GAIX,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,eAAe,EACf,yBAAyB,EACzB,6BAA6B,EAC7B,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,iBAAiB,GAEjB,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,0BAA0B,EAC1B,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,yBAAyB,GAEzB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,kBAAkB,EAClB,oBAAoB,GAGpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EACN,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,YAAY,GAKZ,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,GAGhB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,kBAAkB,GAElB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAC,aAAa,EAAC,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AAElC,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,GAEtB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAC,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AAIjE,OAAO,EACN,eAAe,EACf,eAAe,EACf,SAAS,EACT,aAAa,EACb,qBAAqB,GAKrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,yBAAyB,GAGzB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACN,eAAe,EACf,oBAAoB,GAEpB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAC,yBAAyB,EAAC,MAAM,kCAAkC,CAAC;AAE3E,OAAO,EACN,YAAY,EACZ,WAAW,GAIX,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,eAAe,EACf,yBAAyB,EACzB,6BAA6B,EAC7B,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,iBAAiB,GAEjB,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,0BAA0B,EAC1B,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,yBAAyB,GAEzB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,kBAAkB,EAClB,oBAAoB,GAGpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EACN,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,YAAY,GAKZ,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,GAGhB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,kBAAkB,GAElB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAC,aAAa,EAAC,MAAM,kCAAkC,CAAC"}
@@ -103,6 +103,29 @@ export interface PlaywrightLaunchTransportOptions {
103
103
  * Default: none.
104
104
  */
105
105
  readonly ignoreDefaultArgs?: boolean | readonly string[];
106
+ /**
107
+ * Route ALL browser traffic AND DNS through a single SOCKS proxy, given as a
108
+ * SOCKS URL: `socks5h://host:1080` (or `socks5://host:1080`, optionally with a
109
+ * `user:pass@` userinfo). When set, the transport forwards the proxy to
110
+ * Playwright's `proxy` launch option AND adds Chromium's `--host-resolver-rules`
111
+ * catch-all so no DNS query escapes locally (see {@link proxyNoLeak}).
112
+ *
113
+ * Scheme convention: `socks5h://` means "resolve DNS at the proxy" (no leak),
114
+ * `socks5://` means "SOCKS5, local DNS allowed" (Chromium still resolves URL
115
+ * hostnames at the proxy, but its DNS prefetcher etc. may issue local DNS). Use
116
+ * {@link proxyNoLeak} to override the scheme's implied DNS behaviour. A
117
+ * malformed value throws the typed {@link InvalidProxyError} rather than
118
+ * launching unproxied. Default: no proxy.
119
+ */
120
+ readonly proxy?: string;
121
+ /**
122
+ * Override whether the {@link proxy} enforces NO local DNS. `true` forces the
123
+ * leak-free catch-all even for a plain `socks5://` URL; `false` allows local
124
+ * DNS even for a `socks5h://` URL. When omitted, the SCHEME decides
125
+ * (`socks5h` => no leak, `socks5`/`socks` => local DNS allowed). Ignored when
126
+ * {@link proxy} is unset.
127
+ */
128
+ readonly proxyNoLeak?: boolean;
106
129
  /**
107
130
  * INTERNAL test seam: override how the stealth chromium is imported. Omit in
108
131
  * production (defaults to `import('patchright')`). See
@@ -1 +1 @@
1
- {"version":3,"file":"playwright-launch-transport.d.ts","sourceRoot":"","sources":["../src/playwright-launch-transport.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,QAAQ,EAAiC,MAAM,YAAY,CAAC;AAMpE,OAAO,EAAmB,KAAK,IAAI,EAAmB,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAEN,KAAK,sBAAsB,EAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAC,MAAM,WAAW,CAAC;AAE9D;;;;;;;;GAQG;AACH,KAAK,gBAAgB,GAAG,IAAI,CAAC,OAAO,QAAQ,EAAE,yBAAyB,CAAC,CAAC;AAEzE,oEAAoE;AACpE,UAAU,aAAa;IACtB,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,uBAAuB,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,WAAW,gCAAgC;IAChD;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAAC;IACzD;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,uBAAuB,CAAC;CACzD;AAmBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,yBAA0B,YAAW,SAAS;;IAU1D;;;;;;;;;;;;;;OAcG;gBAEF,QAAQ,GAAE,sBAA2B,EACrC,KAAK,GAAE,SAAS,IAAI,EAAO,EAC3B,OAAO,GAAE,gCAAqC;IAazC,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;CAyHhD"}
1
+ {"version":3,"file":"playwright-launch-transport.d.ts","sourceRoot":"","sources":["../src/playwright-launch-transport.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,QAAQ,EAAiC,MAAM,YAAY,CAAC;AAMpE,OAAO,EAAmB,KAAK,IAAI,EAAmB,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAEN,KAAK,sBAAsB,EAC3B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAC,MAAM,WAAW,CAAC;AAE9D;;;;;;;;GAQG;AACH,KAAK,gBAAgB,GAAG,IAAI,CAAC,OAAO,QAAQ,EAAE,yBAAyB,CAAC,CAAC;AAEzE,oEAAoE;AACpE,UAAU,aAAa;IACtB,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,uBAAuB,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,WAAW,gCAAgC;IAChD;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAAC;IACzD;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,uBAAuB,CAAC;CACzD;AAmBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,yBAA0B,YAAW,SAAS;;IAY1D;;;;;;;;;;;;;;OAcG;gBAEF,QAAQ,GAAE,sBAA2B,EACrC,KAAK,GAAE,SAAS,IAAI,EAAO,EAC3B,OAAO,GAAE,gCAAqC;IAezC,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;CA6IhD"}
@@ -3,6 +3,7 @@ import { chromium } from 'playwright';
3
3
  import { MissingBrowserBinaryError, MissingProfileError, MissingStealthDependencyError, } from './errors.js';
4
4
  import { composeWithHands } from './hand-host.js';
5
5
  import { resolveProfileLocation, } from './profile-location.js';
6
+ import { hostResolverRulesArg, parseSocksProxy } from './socks-proxy.js';
6
7
  /**
7
8
  * The package name of the optional stealth dependency. Kept as a runtime value
8
9
  * (not an `import('patchright')` literal) so TypeScript does NOT try to resolve
@@ -50,6 +51,8 @@ export class PlaywrightLaunchTransport {
50
51
  #noViewport;
51
52
  #extraLaunchArgs;
52
53
  #ignoreDefaultArgs;
54
+ #proxy;
55
+ #proxyNoLeak;
53
56
  #importStealthChromium;
54
57
  /**
55
58
  * @param location overrides for where profiles live (a `root` dir and/or an
@@ -74,6 +77,8 @@ export class PlaywrightLaunchTransport {
74
77
  this.#noViewport = options.noViewport;
75
78
  this.#extraLaunchArgs = options.extraLaunchArgs;
76
79
  this.#ignoreDefaultArgs = options.ignoreDefaultArgs;
80
+ this.#proxy = options.proxy;
81
+ this.#proxyNoLeak = options.proxyNoLeak;
77
82
  this.#importStealthChromium =
78
83
  options.importStealthChromium ?? defaultStealthImporter;
79
84
  }
@@ -129,13 +134,33 @@ export class PlaywrightLaunchTransport {
129
134
  else if (this.#stealth) {
130
135
  launchOptions.ignoreDefaultArgs = ['--enable-automation'];
131
136
  }
137
+ // Proxy: route ALL traffic + DNS through one SOCKS proxy. We parse the URL
138
+ // HERE (a malformed value is the typed InvalidProxyError, never a silent
139
+ // unproxied launch), forward it to Playwright's `proxy` option, and when
140
+ // no-leak is in effect add Chromium's --host-resolver-rules catch-all so even
141
+ // the DNS prefetcher cannot leak a raw local DNS query.
142
+ const hardeningArgs = [];
143
+ if (this.#proxy !== undefined && this.#proxy.trim() !== '') {
144
+ const parsed = parseSocksProxy(this.#proxy, this.#proxyNoLeak);
145
+ launchOptions.proxy = {
146
+ server: parsed.server,
147
+ ...(parsed.username !== undefined ? { username: parsed.username } : {}),
148
+ ...(parsed.password !== undefined ? { password: parsed.password } : {}),
149
+ };
150
+ if (parsed.noLeak) {
151
+ hardeningArgs.push(hostResolverRulesArg(parsed.host));
152
+ }
153
+ }
132
154
  // Extra launch args (the hardening escape hatch) are appended verbatim. We do
133
155
  // NOT set user-agent/locale/timezone/headers here: a wrong UA is a bigger
134
156
  // tell than none (Patchright warns against overriding them), so those stay
135
- // untouched by default.
157
+ // untouched by default. The proxy's no-leak DNS arg (if any) rides alongside.
136
158
  if (this.#extraLaunchArgs !== undefined &&
137
159
  this.#extraLaunchArgs.length > 0) {
138
- launchOptions.args = [...this.#extraLaunchArgs];
160
+ hardeningArgs.push(...this.#extraLaunchArgs);
161
+ }
162
+ if (hardeningArgs.length > 0) {
163
+ launchOptions.args = hardeningArgs;
139
164
  }
140
165
  let context;
141
166
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"playwright-launch-transport.js","sourceRoot":"","sources":["../src/playwright-launch-transport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"playwright-launch-transport.js","sourceRoot":"","sources":["../src/playwright-launch-transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * A parsed SOCKS proxy ready to hand to Playwright/Chromium.
3
+ *
4
+ * This is the SINGLE place webhands turns a user-facing `--proxy` SOCKS URL into
5
+ * the concrete launch inputs Chromium needs: the `proxy.server`/credentials
6
+ * Playwright forwards, plus the extra command-line arg that forces DNS through
7
+ * the proxy (no DNS leak). Keeping the brittle parsing + Chromium-flag knowledge
8
+ * in one module mirrors how the launch transport confines its other
9
+ * Playwright/Chromium details.
10
+ */
11
+ export interface ParsedSocksProxy {
12
+ /**
13
+ * The Playwright `proxy.server` value, always normalized to `socks5://host:port`.
14
+ *
15
+ * Chromium's `--proxy-server` understands `socks5://` but NOT the `socks5h://`
16
+ * convention, so we normalize the scheme here and carry the "resolve DNS at
17
+ * the proxy / block local DNS" intent separately in {@link noLeak} instead of
18
+ * in the scheme string.
19
+ */
20
+ readonly server: string;
21
+ /** Optional proxy username (from a `user:pass@` userinfo component). */
22
+ readonly username?: string;
23
+ /** Optional proxy password (from a `user:pass@` userinfo component). */
24
+ readonly password?: string;
25
+ /** The proxy host, used to build the DNS catch-all EXCLUDE rule. */
26
+ readonly host: string;
27
+ /**
28
+ * Whether to enforce NO local DNS (force every hostname to resolve at the
29
+ * proxy). When `true`, the transport adds a `--host-resolver-rules` catch-all
30
+ * so even Chromium components that bypass `--proxy-server` (DNS prefetcher,
31
+ * etc.) cannot leak a raw DNS query (see {@link hostResolverRulesArg}).
32
+ */
33
+ readonly noLeak: boolean;
34
+ }
35
+ /**
36
+ * Parse a user-facing `--proxy` SOCKS URL into a {@link ParsedSocksProxy}.
37
+ *
38
+ * Accepts `socks5h://`, `socks5://`, or `socks://` with a host and port and an
39
+ * optional `user:pass@` userinfo. Anything else (missing host/port, an http(s)
40
+ * proxy, a bare host with no scheme) is a typed {@link InvalidProxyError} so the
41
+ * caller refuses LOUDLY rather than launching unproxied.
42
+ *
43
+ * `forceNoLeak`, when set, overrides the scheme's implied DNS behaviour: pass
44
+ * `true` to enforce no local DNS even for a plain `socks5://` URL, or `false` to
45
+ * allow local DNS even for `socks5h://`. When omitted, the SCHEME decides
46
+ * (`socks5h` => no-leak, `socks5`/`socks` => local DNS allowed).
47
+ */
48
+ export declare function parseSocksProxy(value: string, forceNoLeak?: boolean): ParsedSocksProxy;
49
+ /**
50
+ * Build the Chromium `--host-resolver-rules` argument that prevents ANY local
51
+ * DNS resolution, the catch-all the Chromium SOCKS design doc prescribes for a
52
+ * leak-free SOCKS proxy.
53
+ *
54
+ * `MAP * ~NOTFOUND` maps every hostname to an invalid address so Chromium never
55
+ * issues a real local DNS query; `EXCLUDE <host>` lets Chromium still resolve
56
+ * the proxy server's own address (otherwise every request fails with
57
+ * PROXY_CONNECTION_FAILED). URL loads themselves resolve at the proxy via
58
+ * `--proxy-server`; this arg closes the side channels (DNS prefetcher, etc.).
59
+ */
60
+ export declare function hostResolverRulesArg(host: string): string;
61
+ //# sourceMappingURL=socks-proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socks-proxy.d.ts","sourceRoot":"","sources":["../src/socks-proxy.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAChC;;;;;;;OAOG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CACzB;AAqBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC9B,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,OAAO,GACnB,gBAAgB,CAuClB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD"}
@@ -0,0 +1,84 @@
1
+ import { InvalidProxyError } from './errors.js';
2
+ /**
3
+ * The SOCKS schemes we accept on a `--proxy` value.
4
+ *
5
+ * - `socks5h://` is the widely-used convention meaning "resolve DNS at the
6
+ * proxy" (no local DNS, no leak). We map it to {@link ParsedSocksProxy.noLeak}
7
+ * `true`.
8
+ * - `socks5://` means "SOCKS5, local DNS allowed" by the same convention. NOTE:
9
+ * Chromium ALREADY resolves URL hostnames at the proxy under `--proxy-server`,
10
+ * but other components (the DNS prefetcher) can still issue raw local DNS, so
11
+ * plain `socks5://` does NOT by itself guarantee no leak.
12
+ *
13
+ * `socks://` is accepted as an alias for `socks5://` (some tools emit it).
14
+ */
15
+ const SCHEME_NO_LEAK = {
16
+ 'socks5h:': true,
17
+ 'socks5:': false,
18
+ 'socks:': false,
19
+ };
20
+ /**
21
+ * Parse a user-facing `--proxy` SOCKS URL into a {@link ParsedSocksProxy}.
22
+ *
23
+ * Accepts `socks5h://`, `socks5://`, or `socks://` with a host and port and an
24
+ * optional `user:pass@` userinfo. Anything else (missing host/port, an http(s)
25
+ * proxy, a bare host with no scheme) is a typed {@link InvalidProxyError} so the
26
+ * caller refuses LOUDLY rather than launching unproxied.
27
+ *
28
+ * `forceNoLeak`, when set, overrides the scheme's implied DNS behaviour: pass
29
+ * `true` to enforce no local DNS even for a plain `socks5://` URL, or `false` to
30
+ * allow local DNS even for `socks5h://`. When omitted, the SCHEME decides
31
+ * (`socks5h` => no-leak, `socks5`/`socks` => local DNS allowed).
32
+ */
33
+ export function parseSocksProxy(value, forceNoLeak) {
34
+ const trimmed = value.trim();
35
+ if (trimmed === '') {
36
+ throw new InvalidProxyError(value);
37
+ }
38
+ let url;
39
+ try {
40
+ url = new URL(trimmed);
41
+ }
42
+ catch (cause) {
43
+ throw new InvalidProxyError(value, undefined, { cause });
44
+ }
45
+ const schemeNoLeak = SCHEME_NO_LEAK[url.protocol];
46
+ if (schemeNoLeak === undefined) {
47
+ // Not a SOCKS scheme (e.g. http://, https://, socks4://, or no scheme).
48
+ throw new InvalidProxyError(value);
49
+ }
50
+ if (url.hostname === '' || url.port === '') {
51
+ // A host AND an explicit port are both required: Chromium's --proxy-server
52
+ // needs the port, and we will not guess a default.
53
+ throw new InvalidProxyError(value);
54
+ }
55
+ const noLeak = forceNoLeak ?? schemeNoLeak;
56
+ const server = `socks5://${url.hostname}:${url.port}`;
57
+ const proxy = {
58
+ server,
59
+ host: url.hostname,
60
+ noLeak,
61
+ ...(url.username !== ''
62
+ ? { username: decodeURIComponent(url.username) }
63
+ : {}),
64
+ ...(url.password !== ''
65
+ ? { password: decodeURIComponent(url.password) }
66
+ : {}),
67
+ };
68
+ return proxy;
69
+ }
70
+ /**
71
+ * Build the Chromium `--host-resolver-rules` argument that prevents ANY local
72
+ * DNS resolution, the catch-all the Chromium SOCKS design doc prescribes for a
73
+ * leak-free SOCKS proxy.
74
+ *
75
+ * `MAP * ~NOTFOUND` maps every hostname to an invalid address so Chromium never
76
+ * issues a real local DNS query; `EXCLUDE <host>` lets Chromium still resolve
77
+ * the proxy server's own address (otherwise every request fails with
78
+ * PROXY_CONNECTION_FAILED). URL loads themselves resolve at the proxy via
79
+ * `--proxy-server`; this arg closes the side channels (DNS prefetcher, etc.).
80
+ */
81
+ export function hostResolverRulesArg(host) {
82
+ return `--host-resolver-rules=MAP * ~NOTFOUND , EXCLUDE ${host}`;
83
+ }
84
+ //# sourceMappingURL=socks-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socks-proxy.js","sourceRoot":"","sources":["../src/socks-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,iBAAiB,EAAC,MAAM,aAAa,CAAC;AAqC9C;;;;;;;;;;;;GAYG;AACH,MAAM,cAAc,GAAsC;IACzD,UAAU,EAAE,IAAI;IAChB,SAAS,EAAE,KAAK;IAChB,QAAQ,EAAE,KAAK;CACf,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAC9B,KAAa,EACb,WAAqB;IAErB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACJ,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,EAAC,KAAK,EAAC,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,YAAY,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAChC,wEAAwE;QACxE,MAAM,IAAI,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,IAAI,GAAG,CAAC,IAAI,KAAK,EAAE,EAAE,CAAC;QAC5C,2EAA2E;QAC3E,mDAAmD;QACnD,MAAM,IAAI,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,IAAI,YAAY,CAAC;IAC3C,MAAM,MAAM,GAAG,YAAY,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;IAEtD,MAAM,KAAK,GAAqB;QAC/B,MAAM;QACN,IAAI,EAAE,GAAG,CAAC,QAAQ;QAClB,MAAM;QACN,GAAG,CAAC,GAAG,CAAC,QAAQ,KAAK,EAAE;YACtB,CAAC,CAAC,EAAC,QAAQ,EAAE,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAC;YAC9C,CAAC,CAAC,EAAE,CAAC;QACN,GAAG,CAAC,GAAG,CAAC,QAAQ,KAAK,EAAE;YACtB,CAAC,CAAC,EAAC,QAAQ,EAAE,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAC;YAC9C,CAAC,CAAC,EAAE,CAAC;KACN,CAAC;IACF,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAChD,OAAO,mDAAmD,IAAI,EAAE,CAAC;AAClE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webhands/core",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Core library for webhands: drives a real, persistent browser via Playwright.",
5
5
  "keywords": [
6
6
  "browser",
package/src/errors.ts CHANGED
@@ -19,6 +19,7 @@
19
19
  export type ControllerErrorCode =
20
20
  | 'missing-browser-binary'
21
21
  | 'missing-stealth-dependency'
22
+ | 'invalid-proxy'
22
23
  | 'missing-profile'
23
24
  | 'attach-not-chromium'
24
25
  | 'attach-no-context'
@@ -96,6 +97,35 @@ export class MissingStealthDependencyError extends ControllerError {
96
97
  }
97
98
  }
98
99
 
100
+ /**
101
+ * The `--proxy` value (a SOCKS URL) could not be parsed into a usable proxy
102
+ * config. webhands routes ALL traffic and DNS through one SOCKS proxy, so the
103
+ * value must be a `socks5://` or `socks5h://` URL with a host and port (an
104
+ * optional `user:pass@` is allowed). We refuse a malformed value LOUDLY with
105
+ * this typed condition rather than silently launching with no proxy (which
106
+ * would leak the very traffic the user asked to tunnel). The CLI maps the
107
+ * {@link code} to a fix message showing the expected URL shape.
108
+ *
109
+ * Mirrors {@link MissingStealthDependencyError}: a stable typed error whose
110
+ * brittle detection is confined to one spot (the proxy parser).
111
+ */
112
+ export class InvalidProxyError extends ControllerError {
113
+ readonly code = 'invalid-proxy';
114
+ /** The offending raw `--proxy` value, echoed back so the user can see it. */
115
+ readonly value: string;
116
+
117
+ constructor(
118
+ value: string,
119
+ message: string = `Invalid --proxy value ${JSON.stringify(
120
+ value,
121
+ )}. Expected a SOCKS URL like socks5h://host:1080 or socks5://user:pass@host:1080 (socks5h tunnels DNS too; both route all traffic through the proxy).`,
122
+ options?: {cause?: unknown},
123
+ ) {
124
+ super(message, options);
125
+ this.value = value;
126
+ }
127
+ }
128
+
99
129
  /**
100
130
  * The named profile has not been set up yet: its dedicated profile directory
101
131
  * does not exist on disk. A profile is created by the headed `setup-profile`
package/src/index.ts CHANGED
@@ -42,6 +42,12 @@ export {
42
42
  type StealthChromiumImporter,
43
43
  } from './playwright-launch-transport.js';
44
44
 
45
+ export {
46
+ parseSocksProxy,
47
+ hostResolverRulesArg,
48
+ type ParsedSocksProxy,
49
+ } from './socks-proxy.js';
50
+
45
51
  export {PlaywrightAttachTransport} from './playwright-attach-transport.js';
46
52
 
47
53
  export {
@@ -56,6 +62,7 @@ export {
56
62
  ControllerError,
57
63
  MissingBrowserBinaryError,
58
64
  MissingStealthDependencyError,
65
+ InvalidProxyError,
59
66
  MissingProfileError,
60
67
  AttachNotChromiumError,
61
68
  AttachNoContextError,
@@ -10,6 +10,7 @@ import {
10
10
  resolveProfileLocation,
11
11
  type ProfileLocationOptions,
12
12
  } from './profile-location.js';
13
+ import {hostResolverRulesArg, parseSocksProxy} from './socks-proxy.js';
13
14
  import type {OpenTarget, Session, Transport} from './seam.js';
14
15
 
15
16
  /**
@@ -116,6 +117,29 @@ export interface PlaywrightLaunchTransportOptions {
116
117
  * Default: none.
117
118
  */
118
119
  readonly ignoreDefaultArgs?: boolean | readonly string[];
120
+ /**
121
+ * Route ALL browser traffic AND DNS through a single SOCKS proxy, given as a
122
+ * SOCKS URL: `socks5h://host:1080` (or `socks5://host:1080`, optionally with a
123
+ * `user:pass@` userinfo). When set, the transport forwards the proxy to
124
+ * Playwright's `proxy` launch option AND adds Chromium's `--host-resolver-rules`
125
+ * catch-all so no DNS query escapes locally (see {@link proxyNoLeak}).
126
+ *
127
+ * Scheme convention: `socks5h://` means "resolve DNS at the proxy" (no leak),
128
+ * `socks5://` means "SOCKS5, local DNS allowed" (Chromium still resolves URL
129
+ * hostnames at the proxy, but its DNS prefetcher etc. may issue local DNS). Use
130
+ * {@link proxyNoLeak} to override the scheme's implied DNS behaviour. A
131
+ * malformed value throws the typed {@link InvalidProxyError} rather than
132
+ * launching unproxied. Default: no proxy.
133
+ */
134
+ readonly proxy?: string;
135
+ /**
136
+ * Override whether the {@link proxy} enforces NO local DNS. `true` forces the
137
+ * leak-free catch-all even for a plain `socks5://` URL; `false` allows local
138
+ * DNS even for a `socks5h://` URL. When omitted, the SCHEME decides
139
+ * (`socks5h` => no leak, `socks5`/`socks` => local DNS allowed). Ignored when
140
+ * {@link proxy} is unset.
141
+ */
142
+ readonly proxyNoLeak?: boolean;
119
143
  /**
120
144
  * INTERNAL test seam: override how the stealth chromium is imported. Omit in
121
145
  * production (defaults to `import('patchright')`). See
@@ -173,6 +197,8 @@ export class PlaywrightLaunchTransport implements Transport {
173
197
  readonly #noViewport: boolean | undefined;
174
198
  readonly #extraLaunchArgs: readonly string[] | undefined;
175
199
  readonly #ignoreDefaultArgs: boolean | readonly string[] | undefined;
200
+ readonly #proxy: string | undefined;
201
+ readonly #proxyNoLeak: boolean | undefined;
176
202
  readonly #importStealthChromium: StealthChromiumImporter;
177
203
 
178
204
  /**
@@ -202,6 +228,8 @@ export class PlaywrightLaunchTransport implements Transport {
202
228
  this.#noViewport = options.noViewport;
203
229
  this.#extraLaunchArgs = options.extraLaunchArgs;
204
230
  this.#ignoreDefaultArgs = options.ignoreDefaultArgs;
231
+ this.#proxy = options.proxy;
232
+ this.#proxyNoLeak = options.proxyNoLeak;
205
233
  this.#importStealthChromium =
206
234
  options.importStealthChromium ?? defaultStealthImporter;
207
235
  }
@@ -266,15 +294,35 @@ export class PlaywrightLaunchTransport implements Transport {
266
294
  } else if (this.#stealth) {
267
295
  launchOptions.ignoreDefaultArgs = ['--enable-automation'];
268
296
  }
297
+ // Proxy: route ALL traffic + DNS through one SOCKS proxy. We parse the URL
298
+ // HERE (a malformed value is the typed InvalidProxyError, never a silent
299
+ // unproxied launch), forward it to Playwright's `proxy` option, and when
300
+ // no-leak is in effect add Chromium's --host-resolver-rules catch-all so even
301
+ // the DNS prefetcher cannot leak a raw local DNS query.
302
+ const hardeningArgs: string[] = [];
303
+ if (this.#proxy !== undefined && this.#proxy.trim() !== '') {
304
+ const parsed = parseSocksProxy(this.#proxy, this.#proxyNoLeak);
305
+ launchOptions.proxy = {
306
+ server: parsed.server,
307
+ ...(parsed.username !== undefined ? {username: parsed.username} : {}),
308
+ ...(parsed.password !== undefined ? {password: parsed.password} : {}),
309
+ };
310
+ if (parsed.noLeak) {
311
+ hardeningArgs.push(hostResolverRulesArg(parsed.host));
312
+ }
313
+ }
269
314
  // Extra launch args (the hardening escape hatch) are appended verbatim. We do
270
315
  // NOT set user-agent/locale/timezone/headers here: a wrong UA is a bigger
271
316
  // tell than none (Patchright warns against overriding them), so those stay
272
- // untouched by default.
317
+ // untouched by default. The proxy's no-leak DNS arg (if any) rides alongside.
273
318
  if (
274
319
  this.#extraLaunchArgs !== undefined &&
275
320
  this.#extraLaunchArgs.length > 0
276
321
  ) {
277
- launchOptions.args = [...this.#extraLaunchArgs];
322
+ hardeningArgs.push(...this.#extraLaunchArgs);
323
+ }
324
+ if (hardeningArgs.length > 0) {
325
+ launchOptions.args = hardeningArgs;
278
326
  }
279
327
 
280
328
  let context: BrowserContext;
@@ -0,0 +1,127 @@
1
+ import {InvalidProxyError} from './errors.js';
2
+
3
+ /**
4
+ * A parsed SOCKS proxy ready to hand to Playwright/Chromium.
5
+ *
6
+ * This is the SINGLE place webhands turns a user-facing `--proxy` SOCKS URL into
7
+ * the concrete launch inputs Chromium needs: the `proxy.server`/credentials
8
+ * Playwright forwards, plus the extra command-line arg that forces DNS through
9
+ * the proxy (no DNS leak). Keeping the brittle parsing + Chromium-flag knowledge
10
+ * in one module mirrors how the launch transport confines its other
11
+ * Playwright/Chromium details.
12
+ */
13
+ export interface ParsedSocksProxy {
14
+ /**
15
+ * The Playwright `proxy.server` value, always normalized to `socks5://host:port`.
16
+ *
17
+ * Chromium's `--proxy-server` understands `socks5://` but NOT the `socks5h://`
18
+ * convention, so we normalize the scheme here and carry the "resolve DNS at
19
+ * the proxy / block local DNS" intent separately in {@link noLeak} instead of
20
+ * in the scheme string.
21
+ */
22
+ readonly server: string;
23
+ /** Optional proxy username (from a `user:pass@` userinfo component). */
24
+ readonly username?: string;
25
+ /** Optional proxy password (from a `user:pass@` userinfo component). */
26
+ readonly password?: string;
27
+ /** The proxy host, used to build the DNS catch-all EXCLUDE rule. */
28
+ readonly host: string;
29
+ /**
30
+ * Whether to enforce NO local DNS (force every hostname to resolve at the
31
+ * proxy). When `true`, the transport adds a `--host-resolver-rules` catch-all
32
+ * so even Chromium components that bypass `--proxy-server` (DNS prefetcher,
33
+ * etc.) cannot leak a raw DNS query (see {@link hostResolverRulesArg}).
34
+ */
35
+ readonly noLeak: boolean;
36
+ }
37
+
38
+ /**
39
+ * The SOCKS schemes we accept on a `--proxy` value.
40
+ *
41
+ * - `socks5h://` is the widely-used convention meaning "resolve DNS at the
42
+ * proxy" (no local DNS, no leak). We map it to {@link ParsedSocksProxy.noLeak}
43
+ * `true`.
44
+ * - `socks5://` means "SOCKS5, local DNS allowed" by the same convention. NOTE:
45
+ * Chromium ALREADY resolves URL hostnames at the proxy under `--proxy-server`,
46
+ * but other components (the DNS prefetcher) can still issue raw local DNS, so
47
+ * plain `socks5://` does NOT by itself guarantee no leak.
48
+ *
49
+ * `socks://` is accepted as an alias for `socks5://` (some tools emit it).
50
+ */
51
+ const SCHEME_NO_LEAK: Readonly<Record<string, boolean>> = {
52
+ 'socks5h:': true,
53
+ 'socks5:': false,
54
+ 'socks:': false,
55
+ };
56
+
57
+ /**
58
+ * Parse a user-facing `--proxy` SOCKS URL into a {@link ParsedSocksProxy}.
59
+ *
60
+ * Accepts `socks5h://`, `socks5://`, or `socks://` with a host and port and an
61
+ * optional `user:pass@` userinfo. Anything else (missing host/port, an http(s)
62
+ * proxy, a bare host with no scheme) is a typed {@link InvalidProxyError} so the
63
+ * caller refuses LOUDLY rather than launching unproxied.
64
+ *
65
+ * `forceNoLeak`, when set, overrides the scheme's implied DNS behaviour: pass
66
+ * `true` to enforce no local DNS even for a plain `socks5://` URL, or `false` to
67
+ * allow local DNS even for `socks5h://`. When omitted, the SCHEME decides
68
+ * (`socks5h` => no-leak, `socks5`/`socks` => local DNS allowed).
69
+ */
70
+ export function parseSocksProxy(
71
+ value: string,
72
+ forceNoLeak?: boolean,
73
+ ): ParsedSocksProxy {
74
+ const trimmed = value.trim();
75
+ if (trimmed === '') {
76
+ throw new InvalidProxyError(value);
77
+ }
78
+
79
+ let url: URL;
80
+ try {
81
+ url = new URL(trimmed);
82
+ } catch (cause) {
83
+ throw new InvalidProxyError(value, undefined, {cause});
84
+ }
85
+
86
+ const schemeNoLeak = SCHEME_NO_LEAK[url.protocol];
87
+ if (schemeNoLeak === undefined) {
88
+ // Not a SOCKS scheme (e.g. http://, https://, socks4://, or no scheme).
89
+ throw new InvalidProxyError(value);
90
+ }
91
+ if (url.hostname === '' || url.port === '') {
92
+ // A host AND an explicit port are both required: Chromium's --proxy-server
93
+ // needs the port, and we will not guess a default.
94
+ throw new InvalidProxyError(value);
95
+ }
96
+
97
+ const noLeak = forceNoLeak ?? schemeNoLeak;
98
+ const server = `socks5://${url.hostname}:${url.port}`;
99
+
100
+ const proxy: ParsedSocksProxy = {
101
+ server,
102
+ host: url.hostname,
103
+ noLeak,
104
+ ...(url.username !== ''
105
+ ? {username: decodeURIComponent(url.username)}
106
+ : {}),
107
+ ...(url.password !== ''
108
+ ? {password: decodeURIComponent(url.password)}
109
+ : {}),
110
+ };
111
+ return proxy;
112
+ }
113
+
114
+ /**
115
+ * Build the Chromium `--host-resolver-rules` argument that prevents ANY local
116
+ * DNS resolution, the catch-all the Chromium SOCKS design doc prescribes for a
117
+ * leak-free SOCKS proxy.
118
+ *
119
+ * `MAP * ~NOTFOUND` maps every hostname to an invalid address so Chromium never
120
+ * issues a real local DNS query; `EXCLUDE <host>` lets Chromium still resolve
121
+ * the proxy server's own address (otherwise every request fails with
122
+ * PROXY_CONNECTION_FAILED). URL loads themselves resolve at the proxy via
123
+ * `--proxy-server`; this arg closes the side channels (DNS prefetcher, etc.).
124
+ */
125
+ export function hostResolverRulesArg(host: string): string {
126
+ return `--host-resolver-rules=MAP * ~NOTFOUND , EXCLUDE ${host}`;
127
+ }