@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 +49 -2
- package/dist/errors.d.ts +21 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/playwright-launch-transport.d.ts +23 -0
- package/dist/playwright-launch-transport.d.ts.map +1 -1
- package/dist/playwright-launch-transport.js +27 -2
- package/dist/playwright-launch-transport.js.map +1 -1
- package/dist/socks-proxy.d.ts +61 -0
- package/dist/socks-proxy.d.ts.map +1 -0
- package/dist/socks-proxy.js +84 -0
- package/dist/socks-proxy.js.map +1 -0
- package/package.json +1 -1
- package/src/errors.ts +30 -0
- package/src/index.ts +7 -0
- package/src/playwright-launch-transport.ts +50 -2
- package/src/socks-proxy.ts +127 -0
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`
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -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":"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"}
|
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`
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;
|
|
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
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":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAC,QAAQ,EAAiC,MAAM,YAAY,CAAC;AACpE,OAAO,EACN,yBAAyB,EACzB,mBAAmB,EACnB,6BAA6B,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAC,gBAAgB,EAA8B,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EACN,sBAAsB,GAEtB,MAAM,uBAAuB,CAAC;
|
|
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
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
|
-
|
|
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
|
+
}
|