@melra/browser-runtime 0.3.0-alpha.10 → 0.3.0-alpha.12

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.
Files changed (69) hide show
  1. package/README.md +2 -1
  2. package/dist/bounded.d.ts +27 -0
  3. package/dist/bounded.d.ts.map +1 -0
  4. package/dist/bounded.js +50 -0
  5. package/dist/bounded.js.map +1 -0
  6. package/dist/browser-connection.d.ts.map +1 -1
  7. package/dist/browser-connection.js +22 -7
  8. package/dist/browser-connection.js.map +1 -1
  9. package/dist/index.d.ts +73 -3
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +307 -52
  12. package/dist/index.js.map +1 -1
  13. package/dist/pinning-proxy.d.ts +1 -1
  14. package/dist/pinning-proxy.d.ts.map +1 -1
  15. package/dist/pinning-proxy.js +1 -1
  16. package/dist/pinning-proxy.js.map +1 -1
  17. package/dist/stable-dom.d.ts +13 -1
  18. package/dist/stable-dom.d.ts.map +1 -1
  19. package/dist/stable-dom.js +22 -3
  20. package/dist/stable-dom.js.map +1 -1
  21. package/package.json +6 -5
  22. package/dist/browser-connection.test.d.ts +0 -2
  23. package/dist/browser-connection.test.d.ts.map +0 -1
  24. package/dist/browser-connection.test.js +0 -210
  25. package/dist/browser-connection.test.js.map +0 -1
  26. package/dist/dialogs.test.d.ts +0 -2
  27. package/dist/dialogs.test.d.ts.map +0 -1
  28. package/dist/dialogs.test.js +0 -165
  29. package/dist/dialogs.test.js.map +0 -1
  30. package/dist/frames.test.d.ts +0 -2
  31. package/dist/frames.test.d.ts.map +0 -1
  32. package/dist/frames.test.js +0 -157
  33. package/dist/frames.test.js.map +0 -1
  34. package/dist/history-tabs.test.d.ts +0 -2
  35. package/dist/history-tabs.test.d.ts.map +0 -1
  36. package/dist/history-tabs.test.js +0 -171
  37. package/dist/history-tabs.test.js.map +0 -1
  38. package/dist/input-fidelity.test.d.ts +0 -2
  39. package/dist/input-fidelity.test.d.ts.map +0 -1
  40. package/dist/input-fidelity.test.js +0 -260
  41. package/dist/input-fidelity.test.js.map +0 -1
  42. package/dist/network-policy.d.ts +0 -28
  43. package/dist/network-policy.d.ts.map +0 -1
  44. package/dist/network-policy.js +0 -121
  45. package/dist/network-policy.js.map +0 -1
  46. package/dist/network-policy.test.d.ts +0 -2
  47. package/dist/network-policy.test.d.ts.map +0 -1
  48. package/dist/network-policy.test.js +0 -39
  49. package/dist/network-policy.test.js.map +0 -1
  50. package/dist/pinning-proxy.test.d.ts +0 -2
  51. package/dist/pinning-proxy.test.d.ts.map +0 -1
  52. package/dist/pinning-proxy.test.js +0 -80
  53. package/dist/pinning-proxy.test.js.map +0 -1
  54. package/dist/popups-profile.test.d.ts +0 -2
  55. package/dist/popups-profile.test.d.ts.map +0 -1
  56. package/dist/popups-profile.test.js +0 -170
  57. package/dist/popups-profile.test.js.map +0 -1
  58. package/dist/see-act.test.d.ts +0 -2
  59. package/dist/see-act.test.d.ts.map +0 -1
  60. package/dist/see-act.test.js +0 -152
  61. package/dist/see-act.test.js.map +0 -1
  62. package/dist/selector.test.d.ts +0 -2
  63. package/dist/selector.test.d.ts.map +0 -1
  64. package/dist/selector.test.js +0 -34
  65. package/dist/selector.test.js.map +0 -1
  66. package/dist/stable-dom.test.d.ts +0 -2
  67. package/dist/stable-dom.test.d.ts.map +0 -1
  68. package/dist/stable-dom.test.js +0 -22
  69. package/dist/stable-dom.test.js.map +0 -1
@@ -1,260 +0,0 @@
1
- // Copyright 2026 XAGI Labs Private Limited
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { createServer } from "node:http";
4
- import { mkdtemp, rm } from "node:fs/promises";
5
- import { tmpdir } from "node:os";
6
- import { join } from "node:path";
7
- import { BrowserOperationSchema } from "@melra/protocol";
8
- import { afterEach, describe, expect, it } from "vitest";
9
- import { BrowserRuntime, detectBrowserExecutable } from "./index.js";
10
- /**
11
- * Typing went through `.fill()`, which assigns the value and fires one `input`
12
- * event. Every widget that listens for keystrokes — autocomplete, comboboxes,
13
- * submit buttons enabled on `keyup` — saw nothing. There was also no way to wait
14
- * for the page to reach a state, so callers padded `settleTimeoutMs` and hoped.
15
- */
16
- const roots = [];
17
- const servers = [];
18
- afterEach(async () => {
19
- await Promise.all(servers.splice(0).map((server) => new Promise((done) => {
20
- server.close(() => done());
21
- })));
22
- await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
23
- });
24
- /**
25
- * A form that records the keystrokes it receives, a panel that appears late,
26
- * and enough height to scroll. `/done` is where submitting lands.
27
- */
28
- const PAGE = `<!doctype html><html><head><title>form</title></head><body>
29
- <form action="/done" method="get">
30
- <input id="email" name="email">
31
- <input id="password" name="password">
32
- <button id="sign-in" type="submit">Sign in</button>
33
- </form>
34
- <div id="keys">keys: 0</div>
35
- <div style="height: 4000px"></div>
36
- <script>
37
- let keys = 0;
38
- for (const field of document.querySelectorAll('input')) {
39
- field.addEventListener('keydown', () => {
40
- keys += 1;
41
- document.getElementById('keys').textContent = 'keys: ' + keys;
42
- });
43
- }
44
- setTimeout(() => {
45
- const panel = document.createElement('div');
46
- panel.id = 'late';
47
- panel.textContent = 'ready at last';
48
- document.body.append(panel);
49
- }, 400);
50
- </script>
51
- </body></html>`;
52
- async function fixture() {
53
- const executablePath = await detectBrowserExecutable();
54
- if (executablePath === undefined)
55
- return undefined;
56
- const server = createServer((request, response) => {
57
- response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
58
- response.end((request.url ?? "/").startsWith("/done")
59
- ? "<!doctype html><html><head><title>done</title></head><body><h1>done</h1></body></html>"
60
- : PAGE);
61
- });
62
- servers.push(server);
63
- await new Promise((done) => {
64
- server.listen(0, "127.0.0.1", done);
65
- });
66
- const address = server.address();
67
- if (address === null || typeof address === "string") {
68
- throw new Error("fixture_server_address_unavailable");
69
- }
70
- const root = await mkdtemp(join(tmpdir(), "melra-input-fidelity-"));
71
- roots.push(root);
72
- return {
73
- runtime: new BrowserRuntime({
74
- workspaceRoot: root,
75
- artifactDirectory: join(root, "artifacts"),
76
- executablePath,
77
- allowedDomains: ["127.0.0.1"],
78
- allowLocalhost: true,
79
- }),
80
- url: `http://127.0.0.1:${address.port}`,
81
- };
82
- }
83
- const op = (fields) => BrowserOperationSchema.parse({ kind: "browser", ...fields });
84
- const valueOf = (result, id) => result.elements.find((element) => element.id === id)?.value ?? null;
85
- describe("browser typing", () => {
86
- it("dispatches one key event per character", async () => {
87
- const context = await fixture();
88
- if (context === undefined)
89
- return;
90
- const { runtime, url } = context;
91
- try {
92
- await runtime.execute(op({ action: "navigate", url }));
93
- const typed = await runtime.execute(op({
94
- action: "type",
95
- target: { selector: "#email" },
96
- value: "hello",
97
- // Clearing an empty field costs a `Delete` keystroke of its own, so
98
- // skip it here to make the count exactly the text.
99
- clearFirst: false,
100
- }));
101
- expect(valueOf(typed, "email")).toBe("hello");
102
- // `.fill()` would leave this at 0: the page would have the text but no
103
- // idea anyone had typed it.
104
- expect(typed.text).toContain("keys: 5");
105
- }
106
- finally {
107
- await runtime.close();
108
- }
109
- }, 120_000);
110
- it("replaces the field by default and appends when told not to clear", async () => {
111
- const context = await fixture();
112
- if (context === undefined)
113
- return;
114
- const { runtime, url } = context;
115
- try {
116
- await runtime.execute(op({ action: "navigate", url }));
117
- await runtime.execute(op({ action: "type", target: { selector: "#email" }, value: "first" }));
118
- const replaced = await runtime.execute(op({ action: "type", target: { selector: "#email" }, value: "second" }));
119
- expect(valueOf(replaced, "email")).toBe("second");
120
- const appended = await runtime.execute(op({
121
- action: "type",
122
- target: { selector: "#email" },
123
- value: "-more",
124
- clearFirst: false,
125
- }));
126
- expect(valueOf(appended, "email")).toBe("second-more");
127
- }
128
- finally {
129
- await runtime.close();
130
- }
131
- }, 120_000);
132
- it("fills a whole form and submits it as one action", async () => {
133
- const context = await fixture();
134
- if (context === undefined)
135
- return;
136
- const { runtime, url } = context;
137
- try {
138
- await runtime.execute(op({ action: "navigate", url }));
139
- const filled = await runtime.execute(op({
140
- action: "fill_form",
141
- fields: [
142
- { target: { selector: "#email" }, value: "user@example.com" },
143
- { target: { selector: "#password" }, value: "hunter2" },
144
- ],
145
- target: { selector: "#sign-in" },
146
- }));
147
- expect(filled.filled).toBe(2);
148
- expect(filled.submitted).toBe(true);
149
- expect(filled.title).toBe("done");
150
- }
151
- finally {
152
- await runtime.close();
153
- }
154
- }, 120_000);
155
- it("fills without submitting when no submit target is given", async () => {
156
- const context = await fixture();
157
- if (context === undefined)
158
- return;
159
- const { runtime, url } = context;
160
- try {
161
- await runtime.execute(op({ action: "navigate", url }));
162
- const filled = await runtime.execute(op({
163
- action: "fill_form",
164
- fields: [{ target: { selector: "#password" }, value: "hunter2" }],
165
- }));
166
- expect(filled.submitted).toBe(false);
167
- expect(valueOf(filled, "password")).toBe("hunter2");
168
- }
169
- finally {
170
- await runtime.close();
171
- }
172
- }, 120_000);
173
- });
174
- describe("browser wait", () => {
175
- it("waits for an element that has not rendered yet", async () => {
176
- const context = await fixture();
177
- if (context === undefined)
178
- return;
179
- const { runtime, url } = context;
180
- try {
181
- await runtime.execute(op({ action: "navigate", url }));
182
- // The panel is added 400ms in, which is past the DOM-settle window, so
183
- // acting on it without waiting is a race.
184
- const waited = await runtime.execute(op({ action: "wait", target: { selector: "#late" }, state: "visible" }));
185
- expect(waited.waited).toBe(true);
186
- expect(waited.text).toContain("ready at last");
187
- }
188
- finally {
189
- await runtime.close();
190
- }
191
- }, 120_000);
192
- it("waits for a URL substring and for page text", async () => {
193
- const context = await fixture();
194
- if (context === undefined)
195
- return;
196
- const { runtime, url } = context;
197
- try {
198
- await runtime.execute(op({ action: "navigate", url }));
199
- await runtime.execute(op({ action: "click", target: { selector: "#sign-in" } }));
200
- const arrived = await runtime.execute(op({ action: "wait", urlContains: "/done" }));
201
- expect(arrived.url).toContain("/done");
202
- const text = await runtime.execute(op({ action: "wait", value: "done" }));
203
- expect(text.waited).toBe(true);
204
- }
205
- finally {
206
- await runtime.close();
207
- }
208
- }, 120_000);
209
- it("refuses a wait with no condition", async () => {
210
- const context = await fixture();
211
- if (context === undefined)
212
- return;
213
- const { runtime, url } = context;
214
- try {
215
- await runtime.execute(op({ action: "navigate", url }));
216
- await expect(runtime.execute(op({ action: "wait" }))).rejects.toThrow("browser_wait_requires_target_url_contains_or_value");
217
- }
218
- finally {
219
- await runtime.close();
220
- }
221
- }, 120_000);
222
- });
223
- describe("browser scroll", () => {
224
- it("scrolls by the requested distance and reports where it landed", async () => {
225
- const context = await fixture();
226
- if (context === undefined)
227
- return;
228
- const { runtime, url } = context;
229
- try {
230
- await runtime.execute(op({ action: "navigate", url }));
231
- const scrolled = await runtime.execute(op({ action: "scroll", direction: "down", pixels: 1_200 }));
232
- expect(scrolled.scrollY).toBe(1_200);
233
- const back = await runtime.execute(op({ action: "scroll", direction: "top" }));
234
- expect(back.scrollY).toBe(0);
235
- }
236
- finally {
237
- await runtime.close();
238
- }
239
- }, 120_000);
240
- });
241
- describe("browser results", () => {
242
- it("reports success, which is what derived evidence checks", async () => {
243
- const context = await fixture();
244
- if (context === undefined)
245
- return;
246
- const { runtime, url } = context;
247
- try {
248
- // Policy derives `result_equals success true` for a browser mutation the
249
- // caller declared no evidence for. Without the flag every such task
250
- // verified as `partial` however well it went.
251
- expect((await runtime.execute(op({ action: "navigate", url }))).success).toBe(true);
252
- expect((await runtime.execute(op({ action: "click", target: { selector: "#sign-in" } })))
253
- .success).toBe(true);
254
- }
255
- finally {
256
- await runtime.close();
257
- }
258
- }, 120_000);
259
- });
260
- //# sourceMappingURL=input-fidelity.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"input-fidelity.test.js","sourceRoot":"","sources":["../src/input-fidelity.test.ts"],"names":[],"mappings":"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"}
@@ -1,28 +0,0 @@
1
- export declare function isPrivateAddress(address: string): boolean;
2
- export interface NetworkPolicy {
3
- allowedDomains: string[];
4
- allowLocalhost: boolean;
5
- /**
6
- * Lifts every destination check below, including the SSRF block on private
7
- * ranges and cloud metadata. Set only when the operator has asked for
8
- * unhinged mode; see `LocalPolicy.unhinged` in `@melra/policy-core`.
9
- */
10
- unhinged?: boolean;
11
- }
12
- /**
13
- * A destination that passed policy, together with the address it resolved to.
14
- *
15
- * The address matters because checking a name and then letting something else
16
- * resolve it again is not a check: between the two lookups a hostile DNS server
17
- * can answer public once and private the second time. Whatever connects has to
18
- * connect to `address`, not to `url.hostname`.
19
- */
20
- export interface SafeDestination {
21
- url: URL;
22
- /** The literal address to open the socket to: the host itself when it was
23
- * already an IP, otherwise the resolver answer this check accepted. */
24
- address: string;
25
- }
26
- export declare function assertSafeDestination(rawUrl: string, policy: NetworkPolicy): Promise<SafeDestination>;
27
- export declare function assertSafeUrl(rawUrl: string, policy: NetworkPolicy): Promise<URL>;
28
- //# sourceMappingURL=network-policy.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"network-policy.d.ts","sourceRoot":"","sources":["../src/network-policy.ts"],"names":[],"mappings":"AAqCA,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAkCzD;AASD,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAWD;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,GAAG,CAAC;IACT;2EACuE;IACvE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,eAAe,CAAC,CA0C1B;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,GAAG,CAAC,CAEd"}
@@ -1,121 +0,0 @@
1
- // Copyright 2026 XAGI Labs Private Limited
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { isIP } from "node:net";
4
- import { lookup } from "node:dns/promises";
5
- function ipv4Number(address) {
6
- return address
7
- .split(".")
8
- .map(Number)
9
- .reduce((value, octet) => (value << 8) + octet, 0) >>> 0;
10
- }
11
- function inIpv4Range(address, base, bits) {
12
- const mask = bits === 0 ? 0 : (0xffffffff << (32 - bits)) >>> 0;
13
- return (ipv4Number(address) & mask) === (ipv4Number(base) & mask);
14
- }
15
- function mappedIpv4Address(address) {
16
- const normalized = address.toLowerCase();
17
- if (!normalized.startsWith("::ffff:"))
18
- return undefined;
19
- const suffix = normalized.slice("::ffff:".length);
20
- if (isIP(suffix) === 4)
21
- return suffix;
22
- const groups = suffix.split(":");
23
- if (groups.length !== 2 || groups.some((group) => !/^[a-f0-9]{1,4}$/.test(group))) {
24
- return undefined;
25
- }
26
- const high = Number.parseInt(groups[0], 16);
27
- const low = Number.parseInt(groups[1], 16);
28
- return [
29
- high >>> 8,
30
- high & 0xff,
31
- low >>> 8,
32
- low & 0xff,
33
- ].join(".");
34
- }
35
- export function isPrivateAddress(address) {
36
- const version = isIP(address);
37
- if (version === 4) {
38
- return [
39
- ["0.0.0.0", 8],
40
- ["10.0.0.0", 8],
41
- ["100.64.0.0", 10],
42
- ["127.0.0.0", 8],
43
- ["169.254.0.0", 16],
44
- ["172.16.0.0", 12],
45
- ["192.0.0.0", 24],
46
- ["192.168.0.0", 16],
47
- ["198.18.0.0", 15],
48
- ["224.0.0.0", 4],
49
- ["240.0.0.0", 4],
50
- ].some(([base, bits]) => inIpv4Range(address, base, bits));
51
- }
52
- if (version === 6) {
53
- const normalized = address.toLowerCase();
54
- const mapped = mappedIpv4Address(normalized);
55
- if (mapped !== undefined)
56
- return isPrivateAddress(mapped);
57
- return (normalized === "::" ||
58
- normalized === "::1" ||
59
- normalized.startsWith("fc") ||
60
- normalized.startsWith("fd") ||
61
- normalized.startsWith("fe8") ||
62
- normalized.startsWith("fe9") ||
63
- normalized.startsWith("fea") ||
64
- normalized.startsWith("feb") ||
65
- normalized.startsWith("ff"));
66
- }
67
- return false;
68
- }
69
- function isLoopbackAddress(address) {
70
- if (isIP(address) === 4)
71
- return inIpv4Range(address, "127.0.0.0", 8);
72
- const normalized = address.toLowerCase();
73
- const mapped = mappedIpv4Address(normalized);
74
- return normalized === "::1" || (mapped !== undefined && isLoopbackAddress(mapped));
75
- }
76
- function domainAllowed(host, allowed) {
77
- return allowed.some((entry) => entry === "*" ||
78
- host === entry.toLowerCase() ||
79
- host.endsWith(`.${entry.toLowerCase()}`));
80
- }
81
- export async function assertSafeDestination(rawUrl, policy) {
82
- const url = new URL(rawUrl);
83
- // Unhinged mode still parses the URL — a value the browser cannot navigate to
84
- // is a bug either way — but asserts nothing about where it points.
85
- if (policy.unhinged)
86
- return { url, address: url.hostname };
87
- if (!["http:", "https:"].includes(url.protocol)) {
88
- throw new Error("browser_protocol_not_allowed");
89
- }
90
- if (url.username !== "" || url.password !== "") {
91
- throw new Error("browser_url_credentials_not_allowed");
92
- }
93
- const host = url.hostname.toLowerCase();
94
- const localName = host === "localhost" || host.endsWith(".localhost");
95
- if (localName && !policy.allowLocalhost) {
96
- throw new Error("browser_private_destination_blocked");
97
- }
98
- if (!domainAllowed(host, policy.allowedDomains)) {
99
- throw new Error("browser_domain_not_allowed");
100
- }
101
- if (isIP(host)) {
102
- if (isPrivateAddress(host) &&
103
- !(policy.allowLocalhost && isLoopbackAddress(host))) {
104
- throw new Error("browser_private_destination_blocked");
105
- }
106
- return { url, address: host };
107
- }
108
- const addresses = await lookup(host, { all: true, verbatim: true });
109
- if (addresses.length === 0 ||
110
- addresses.some((item) => isPrivateAddress(item.address) &&
111
- !(policy.allowLocalhost && isLoopbackAddress(item.address)))) {
112
- throw new Error("browser_private_destination_blocked");
113
- }
114
- // Every answer passed, so any of them is safe to pin; the first is the one the
115
- // resolver preferred.
116
- return { url, address: addresses[0].address };
117
- }
118
- export async function assertSafeUrl(rawUrl, policy) {
119
- return (await assertSafeDestination(rawUrl, policy)).url;
120
- }
121
- //# sourceMappingURL=network-policy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"network-policy.js","sourceRoot":"","sources":["../src/network-policy.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=network-policy.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"network-policy.test.d.ts","sourceRoot":"","sources":["../src/network-policy.test.ts"],"names":[],"mappings":""}
@@ -1,39 +0,0 @@
1
- // Copyright 2026 XAGI Labs Private Limited
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { describe, expect, it } from "vitest";
4
- import { assertSafeUrl, isPrivateAddress } from "./network-policy.js";
5
- describe("browser network policy", () => {
6
- it("classifies private IPv4 and IPv6 destinations", () => {
7
- expect(isPrivateAddress("127.0.0.1")).toBe(true);
8
- expect(isPrivateAddress("169.254.169.254")).toBe(true);
9
- expect(isPrivateAddress("10.1.2.3")).toBe(true);
10
- expect(isPrivateAddress("::1")).toBe(true);
11
- expect(isPrivateAddress("::ffff:ac10:1")).toBe(true);
12
- expect(isPrivateAddress("::ffff:192.168.1.1")).toBe(true);
13
- expect(isPrivateAddress("8.8.8.8")).toBe(false);
14
- });
15
- it("blocks credentials and localhost by default", async () => {
16
- const policy = { allowedDomains: ["*"], allowLocalhost: false };
17
- await expect(assertSafeUrl("http://127.0.0.1", policy)).rejects.toThrow("browser_private_destination_blocked");
18
- await expect(assertSafeUrl("https://user:pass@example.com", policy)).rejects.toThrow("browser_url_credentials_not_allowed");
19
- });
20
- it("asserts nothing about the destination when unhinged", async () => {
21
- // Every check this file exists to make is off in this mode, including the
22
- // SSRF block on cloud metadata. Only URL syntax still has to hold.
23
- const policy = {
24
- allowedDomains: [],
25
- allowLocalhost: false,
26
- unhinged: true,
27
- };
28
- for (const url of [
29
- "http://169.254.169.254/latest/meta-data/",
30
- "http://127.0.0.1:9000",
31
- "https://user:pass@example.com",
32
- "file:///etc/passwd",
33
- ]) {
34
- await expect(assertSafeUrl(url, policy)).resolves.toBeInstanceOf(URL);
35
- }
36
- await expect(assertSafeUrl("not a url", policy)).rejects.toThrow();
37
- });
38
- });
39
- //# sourceMappingURL=network-policy.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"network-policy.test.js","sourceRoot":"","sources":["../src/network-policy.test.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,sCAAsC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEtE,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,MAAM,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,MAAM,GAAG,EAAE,cAAc,EAAE,CAAC,GAAG,CAAC,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;QAChE,MAAM,MAAM,CAAC,aAAa,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACrE,qCAAqC,CACtC,CAAC;QACF,MAAM,MAAM,CACV,aAAa,CAAC,+BAA+B,EAAE,MAAM,CAAC,CACvD,CAAC,OAAO,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,0EAA0E;QAC1E,mEAAmE;QACnE,MAAM,MAAM,GAAG;YACb,cAAc,EAAE,EAAE;YAClB,cAAc,EAAE,KAAK;YACrB,QAAQ,EAAE,IAAI;SACf,CAAC;QACF,KAAK,MAAM,GAAG,IAAI;YAChB,0CAA0C;YAC1C,uBAAuB;YACvB,+BAA+B;YAC/B,oBAAoB;SACrB,EAAE,CAAC;YACF,MAAM,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACxE,CAAC;QACD,MAAM,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=pinning-proxy.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pinning-proxy.test.d.ts","sourceRoot":"","sources":["../src/pinning-proxy.test.ts"],"names":[],"mappings":""}
@@ -1,80 +0,0 @@
1
- // Copyright 2026 XAGI Labs Private Limited
2
- // SPDX-License-Identifier: Apache-2.0
3
- import { createServer, request as httpRequest } from "node:http";
4
- import { connect } from "node:net";
5
- import { describe, expect, it, vi } from "vitest";
6
- // `rebind.test` resolves nowhere in reality, which is what makes this a test of
7
- // pinning rather than of DNS: only a proxy that connects to the address the
8
- // policy check returned can reach the server below. One that resolved the name
9
- // a second time — the rebinding window — would fail outright.
10
- vi.mock("node:dns/promises", () => ({
11
- lookup: async () => [{ address: "127.0.0.1", family: 4 }],
12
- }));
13
- const { startPinningProxy } = await import("./pinning-proxy.js");
14
- async function listen() {
15
- const server = createServer((_req, res) => res.end("pinned"));
16
- await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
17
- return {
18
- port: server.address().port,
19
- close: () => server.close(),
20
- };
21
- }
22
- function throughProxy(proxyPort, target) {
23
- return new Promise((resolve, reject) => {
24
- // Absolute-form request URI: how a browser addresses a proxy.
25
- const req = httpRequest({ host: "127.0.0.1", port: proxyPort, path: target }, (res) => {
26
- let body = "";
27
- res.on("data", (chunk) => (body += String(chunk)));
28
- res.on("end", () => resolve({ status: res.statusCode ?? 0, body }));
29
- });
30
- req.on("error", reject);
31
- req.end();
32
- });
33
- }
34
- describe("pinning proxy", () => {
35
- it("connects to the address the policy check returned", async () => {
36
- const upstream = await listen();
37
- const proxy = await startPinningProxy({
38
- allowedDomains: ["*"],
39
- allowLocalhost: true,
40
- });
41
- try {
42
- const port = Number(new URL(proxy.server).port);
43
- const response = await throughProxy(port, `http://rebind.test:${upstream.port}/`);
44
- expect(response).toEqual({ status: 200, body: "pinned" });
45
- }
46
- finally {
47
- await proxy.close();
48
- upstream.close();
49
- }
50
- });
51
- it("refuses a destination policy denies", async () => {
52
- const upstream = await listen();
53
- const proxy = await startPinningProxy({
54
- allowedDomains: ["*"],
55
- allowLocalhost: false,
56
- });
57
- try {
58
- const port = Number(new URL(proxy.server).port);
59
- const response = await throughProxy(port, `http://rebind.test:${upstream.port}/`);
60
- expect(response.status).toBe(502);
61
- // CONNECT carries no path to check, so the authority has to be enough.
62
- const refusal = await new Promise((resolve, reject) => {
63
- const socket = connect(port, "127.0.0.1", () => {
64
- socket.write(`CONNECT rebind.test:443 HTTP/1.1\r\nHost: rebind.test:443\r\n\r\n`);
65
- });
66
- socket.on("data", (chunk) => {
67
- resolve(String(chunk));
68
- socket.destroy();
69
- });
70
- socket.on("error", reject);
71
- });
72
- expect(refusal).toContain("502");
73
- }
74
- finally {
75
- await proxy.close();
76
- upstream.close();
77
- }
78
- });
79
- });
80
- //# sourceMappingURL=pinning-proxy.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pinning-proxy.test.js","sourceRoot":"","sources":["../src/pinning-proxy.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=popups-profile.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"popups-profile.test.d.ts","sourceRoot":"","sources":["../src/popups-profile.test.ts"],"names":[],"mappings":""}