@dbx-tools/appkit-web-search 0.6.71 → 0.6.73
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 +6 -4
- package/lib/src/_fetch-url.d.ts +25 -0
- package/lib/src/_fetch-url.js +79 -0
- package/lib/src/fetch.js +17 -4
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/_fetch-url.ts +110 -0
- package/src/fetch.ts +16 -3
package/README.md
CHANGED
|
@@ -205,14 +205,16 @@ URL's hostname, and a bare host also matches its subdomains; a path entry
|
|
|
205
205
|
design: `web_search` citations are silently filtered to the permitted set (the
|
|
206
206
|
model never surfaces a source it then can't fetch), while an explicit `web_fetch`
|
|
207
207
|
of a disallowed URL is refused with an error (a visible, correctable mistake).
|
|
208
|
+
`web_fetch` accepts only HTTPS targets that resolve to public addresses, and
|
|
209
|
+
reapplies both checks before every redirect request.
|
|
208
210
|
|
|
209
211
|
Which of the two modes is in force is named by `urlPolicy`, and the effective
|
|
210
212
|
mode is in the boot log:
|
|
211
213
|
|
|
212
|
-
| `urlPolicy` | Effect
|
|
213
|
-
| ---------------- |
|
|
214
|
-
| `"allowlist"` | Only `allowedUrls` entries are reachable
|
|
215
|
-
| `"unrestricted"` | Any URL is reachable
|
|
214
|
+
| `urlPolicy` | Effect |
|
|
215
|
+
| ---------------- | ---------------------------------------- |
|
|
216
|
+
| `"allowlist"` | Only `allowedUrls` entries are reachable |
|
|
217
|
+
| `"unrestricted"` | Any public HTTPS URL is reachable |
|
|
216
218
|
|
|
217
219
|
Leave `urlPolicy` unset and it follows `allowedUrls`: `"allowlist"` when there
|
|
218
220
|
are entries, `"unrestricted"` when there are none. Set it to say which one you
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network policy for page fetches.
|
|
3
|
+
*
|
|
4
|
+
* Every request, including redirects, is HTTPS, matches the configured URL
|
|
5
|
+
* allow-list, and resolves only to public addresses. Resolution happens in the
|
|
6
|
+
* request hook immediately before I/O so a redirect cannot bypass the policy.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { type UrlAllowList } from "./allowlist.ts";
|
|
11
|
+
/** DNS result shape used by Node and by deterministic tests. */
|
|
12
|
+
export interface ResolvedAddress {
|
|
13
|
+
address: string;
|
|
14
|
+
family: number;
|
|
15
|
+
}
|
|
16
|
+
/** Injectable DNS resolver for fetch-policy tests. */
|
|
17
|
+
export type ResolveAddresses = (hostname: string) => Promise<readonly ResolvedAddress[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Validate one concrete page-fetch target immediately before network I/O.
|
|
20
|
+
*
|
|
21
|
+
* Rejects embedded credentials, non-HTTPS URLs, disallowed hosts/paths, and
|
|
22
|
+
* hostnames whose DNS answers include any private, loopback, link-local, or
|
|
23
|
+
* reserved address.
|
|
24
|
+
*/
|
|
25
|
+
export declare function assertFetchUrlAllowed(value: string, allowList: UrlAllowList, resolve?: ResolveAddresses): Promise<void>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network policy for page fetches.
|
|
3
|
+
*
|
|
4
|
+
* Every request, including redirects, is HTTPS, matches the configured URL
|
|
5
|
+
* allow-list, and resolves only to public addresses. Resolution happens in the
|
|
6
|
+
* request hook immediately before I/O so a redirect cannot bypass the policy.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { lookup } from "node:dns/promises";
|
|
11
|
+
import { isIP } from "node:net";
|
|
12
|
+
import { ValidationError } from "@databricks/appkit";
|
|
13
|
+
import { assertUrlAllowed } from "./allowlist.js";
|
|
14
|
+
/** Resolve all addresses for a hostname without changing their family. */
|
|
15
|
+
async function resolveAddresses(hostname) {
|
|
16
|
+
return lookup(hostname, { all: true, verbatim: true });
|
|
17
|
+
}
|
|
18
|
+
/** Whether an IPv4 address is private, local, reserved, or non-unicast. */
|
|
19
|
+
function isBlockedIpv4(address) {
|
|
20
|
+
const [a = -1, b = -1, c = -1] = address.split(".").map(Number);
|
|
21
|
+
return (a === 0 ||
|
|
22
|
+
a === 10 ||
|
|
23
|
+
a === 127 ||
|
|
24
|
+
a >= 224 ||
|
|
25
|
+
(a === 100 && b >= 64 && b <= 127) ||
|
|
26
|
+
(a === 169 && b === 254) ||
|
|
27
|
+
(a === 172 && b >= 16 && b <= 31) ||
|
|
28
|
+
(a === 192 && b === 0 && c === 0) ||
|
|
29
|
+
(a === 192 && b === 0 && c === 2) ||
|
|
30
|
+
(a === 192 && b === 168) ||
|
|
31
|
+
(a === 198 && (b === 18 || b === 19)) ||
|
|
32
|
+
(a === 198 && b === 51 && c === 100) ||
|
|
33
|
+
(a === 203 && b === 0 && c === 113));
|
|
34
|
+
}
|
|
35
|
+
/** Whether an IPv6 address is private, local, reserved, or non-unicast. */
|
|
36
|
+
function isBlockedIpv6(address) {
|
|
37
|
+
const normalized = address.toLowerCase();
|
|
38
|
+
return (normalized === "::" ||
|
|
39
|
+
normalized === "::1" ||
|
|
40
|
+
normalized.startsWith("::ffff:") ||
|
|
41
|
+
normalized.startsWith("100:") ||
|
|
42
|
+
normalized.startsWith("2001:db8:") ||
|
|
43
|
+
normalized.startsWith("fc") ||
|
|
44
|
+
normalized.startsWith("fd") ||
|
|
45
|
+
/^fe[89ab]/.test(normalized) ||
|
|
46
|
+
normalized.startsWith("ff"));
|
|
47
|
+
}
|
|
48
|
+
/** Whether an address is globally routable enough for an outbound page fetch. */
|
|
49
|
+
function isPublicAddress(address) {
|
|
50
|
+
return address.family === 6 ? !isBlockedIpv6(address.address) : !isBlockedIpv4(address.address);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Validate one concrete page-fetch target immediately before network I/O.
|
|
54
|
+
*
|
|
55
|
+
* Rejects embedded credentials, non-HTTPS URLs, disallowed hosts/paths, and
|
|
56
|
+
* hostnames whose DNS answers include any private, loopback, link-local, or
|
|
57
|
+
* reserved address.
|
|
58
|
+
*/
|
|
59
|
+
export async function assertFetchUrlAllowed(value, allowList, resolve = resolveAddresses) {
|
|
60
|
+
let url;
|
|
61
|
+
try {
|
|
62
|
+
url = new URL(value);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw ValidationError.invalidValue("url", value, "an absolute HTTPS URL");
|
|
66
|
+
}
|
|
67
|
+
if (url.protocol !== "https:" || url.username || url.password) {
|
|
68
|
+
throw ValidationError.invalidValue("url", value, "an absolute HTTPS URL without embedded credentials");
|
|
69
|
+
}
|
|
70
|
+
assertUrlAllowed(url.href, allowList);
|
|
71
|
+
const literalFamily = isIP(url.hostname);
|
|
72
|
+
const addresses = literalFamily
|
|
73
|
+
? [{ address: url.hostname, family: literalFamily }]
|
|
74
|
+
: await resolve(url.hostname);
|
|
75
|
+
if (addresses.length === 0 || addresses.some((address) => !isPublicAddress(address))) {
|
|
76
|
+
throw ValidationError.invalidValue("url", value, "a URL that resolves only to public addresses");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiX2ZldGNoLXVybC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9fZmV0Y2gtdXJsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7OztHQVFHO0FBRUgsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQzNDLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDaEMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRXJELE9BQU8sRUFBRSxnQkFBZ0IsRUFBcUIsTUFBTSxnQkFBZ0IsQ0FBQztBQVdyRSwwRUFBMEU7QUFDMUUsS0FBSyxVQUFVLGdCQUFnQixDQUFDLFFBQWdCO0lBQzlDLE9BQU8sTUFBTSxDQUFDLFFBQVEsRUFBRSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7QUFDekQsQ0FBQztBQUVELDJFQUEyRTtBQUMzRSxTQUFTLGFBQWEsQ0FBQyxPQUFlO0lBQ3BDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ2hFLE9BQU8sQ0FDTCxDQUFDLEtBQUssQ0FBQztRQUNQLENBQUMsS0FBSyxFQUFFO1FBQ1IsQ0FBQyxLQUFLLEdBQUc7UUFDVCxDQUFDLElBQUksR0FBRztRQUNSLENBQUMsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxHQUFHLENBQUM7UUFDbEMsQ0FBQyxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxHQUFHLENBQUM7UUFDeEIsQ0FBQyxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLElB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Q0FBQyxJQUFJLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUMsZUFBZSxDQUFDLE9BQU8sQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNyRixNQUFNLGVBQWUsQ0FBQyxZQUFZLENBQ2hDLEtBQUssRUFDTCxLQUFLLEVBQ0wsOENBQThDLENBQy9DLENBQUM7SUFDSixDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTmV0d29yayBwb2xpY3kgZm9yIHBhZ2UgZmV0Y2hlcy5cbiAqXG4gKiBFdmVyeSByZXF1ZXN0LCBpbmNsdWRpbmcgcmVkaXJlY3RzLCBpcyBIVFRQUywgbWF0Y2hlcyB0aGUgY29uZmlndXJlZCBVUkxcbiAqIGFsbG93LWxpc3QsIGFuZCByZXNvbHZlcyBvbmx5IHRvIHB1YmxpYyBhZGRyZXNzZXMuIFJlc29sdXRpb24gaGFwcGVucyBpbiB0aGVcbiAqIHJlcXVlc3QgaG9vayBpbW1lZGlhdGVseSBiZWZvcmUgSS9PIHNvIGEgcmVkaXJlY3QgY2Fubm90IGJ5cGFzcyB0aGUgcG9saWN5LlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBsb29rdXAgfSBmcm9tIFwibm9kZTpkbnMvcHJvbWlzZXNcIjtcbmltcG9ydCB7IGlzSVAgfSBmcm9tIFwibm9kZTpuZXRcIjtcbmltcG9ydCB7IFZhbGlkYXRpb25FcnJvciB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcblxuaW1wb3J0IHsgYXNzZXJ0VXJsQWxsb3dlZCwgdHlwZSBVcmxBbGxvd0xpc3QgfSBmcm9tIFwiLi9hbGxvd2xpc3QudHNcIjtcblxuLyoqIEROUyByZXN1bHQgc2hhcGUgdXNlZCBieSBOb2RlIGFuZCBieSBkZXRlcm1pbmlzdGljIHRlc3RzLiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXNvbHZlZEFkZHJlc3Mge1xuICBhZGRyZXNzOiBzdHJpbmc7XG4gIGZhbWlseTogbnVtYmVyO1xufVxuXG4vKiogSW5qZWN0YWJsZSBETlMgcmVzb2x2ZXIgZm9yIGZldGNoLXBvbGljeSB0ZXN0cy4gKi9cbmV4cG9ydCB0eXBlIFJlc29sdmVBZGRyZXNzZXMgPSAoaG9zdG5hbWU6IHN0cmluZykgPT4gUHJvbWlzZTxyZWFkb25seSBSZXNvbHZlZEFkZHJlc3NbXT47XG5cbi8qKiBSZXNvbHZlIGFsbCBhZGRyZXNzZXMgZm9yIGEgaG9zdG5hbWUgd2l0aG91dCBjaGFuZ2luZyB0aGVpciBmYW1pbHkuICovXG5hc3luYyBmdW5jdGlvbiByZXNvbHZlQWRkcmVzc2VzKGhvc3RuYW1lOiBzdHJpbmcpOiBQcm9taXNlPHJlYWRvbmx5IFJlc29sdmVkQWRkcmVzc1tdPiB7XG4gIHJldHVybiBsb29rdXAoaG9zdG5hbWUsIHsgYWxsOiB0cnVlLCB2ZXJiYXRpbTogdHJ1ZSB9KTtcbn1cblxuLyoqIFdoZXRoZXIgYW4gSVB2NCBhZGRyZXNzIGlzIHByaXZhdGUsIGxvY2FsLCByZXNlcnZlZCwgb3Igbm9uLXVuaWNhc3QuICovXG5mdW5jdGlvbiBpc0Jsb2NrZWRJcHY0KGFkZHJlc3M6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBjb25zdCBbYSA9IC0xLCBiID0gLTEsIGMgPSAtMV0gPSBhZGRyZXNzLnNwbGl0KFwiLlwiKS5tYXAoTnVtYmVyKTtcbiAgcmV0dXJuIChcbiAgICBhID09PSAwIHx8XG4gICAgYSA9PT0gMTAgfHxcbiAgICBhID09PSAxMjcgfHxcbiAgICBhID49IDIyNCB8fFxuICAgIChhID09PSAxMDAgJiYgYiA+PSA2NCAmJiBiIDw9IDEyNykgfHxcbiAgICAoYSA9PT0gMTY5ICYmIGIgPT09IDI1NCkgfHxcbiAgICAoYSA9PT0gMTcyICYmIGIgPj0gMTYgJiYgYiA8PSAzMSkgfHxcbiAgICAoYSA9PT0gMTkyICYmIGIgPT09IDAgJiYgYyA9PT0gMCkgfHxcbiAgICAoYSA9PT0gMTkyICYmIGIgPT09IDAgJiYgYyA9PT0gMikgfHxcbiAgICAoYSA9PT0gMTkyICYmIGIgPT09IDE2OCkgfHxcbiAgICAoYSA9PT0gMTk4ICYmIChiID09PSAxOCB8fCBiID09PSAxOSkpIHx8XG4gICAgKGEgPT09IDE5OCAmJiBiID09PSA1MSAmJiBjID09PSAxMDApIHx8XG4gICAgKGEgPT09IDIwMyAmJiBiID09PSAwICYmIGMgPT09IDExMylcbiAgKTtcbn1cblxuLyoqIFdoZXRoZXIgYW4gSVB2NiBhZGRyZXNzIGlzIHByaXZhdGUsIGxvY2FsLCByZXNlcnZlZCwgb3Igbm9uLXVuaWNhc3QuICovXG5mdW5jdGlvbiBpc0Jsb2NrZWRJcHY2KGFkZHJlc3M6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBjb25zdCBub3JtYWxpemVkID0gYWRkcmVzcy50b0xvd2VyQ2FzZSgpO1xuICByZXR1cm4gKFxuICAgIG5vcm1hbGl6ZWQgPT09IFwiOjpcIiB8fFxuICAgIG5vcm1hbGl6ZWQgPT09IFwiOjoxXCIgfHxcbiAgICBub3JtYWxpemVkLnN0YXJ0c1dpdGgoXCI6OmZmZmY6XCIpIHx8XG4gICAgbm9ybWFsaXplZC5zdGFydHNXaXRoKFwiMTAwOlwiKSB8fFxuICAgIG5vcm1hbGl6ZWQuc3RhcnRzV2l0aChcIjIwMDE6ZGI4OlwiKSB8fFxuICAgIG5vcm1hbGl6ZWQuc3RhcnRzV2l0aChcImZjXCIpIHx8XG4gICAgbm9ybWFsaXplZC5zdGFydHNXaXRoKFwiZmRcIikgfHxcbiAgICAvXmZlWzg5YWJdLy50ZXN0KG5vcm1hbGl6ZWQpIHx8XG4gICAgbm9ybWFsaXplZC5zdGFydHNXaXRoKFwiZmZcIilcbiAgKTtcbn1cblxuLyoqIFdoZXRoZXIgYW4gYWRkcmVzcyBpcyBnbG9iYWxseSByb3V0YWJsZSBlbm91Z2ggZm9yIGFuIG91dGJvdW5kIHBhZ2UgZmV0Y2guICovXG5mdW5jdGlvbiBpc1B1YmxpY0FkZHJlc3MoYWRkcmVzczogUmVzb2x2ZWRBZGRyZXNzKTogYm9vbGVhbiB7XG4gIHJldHVybiBhZGRyZXNzLmZhbWlseSA9PT0gNiA/ICFpc0Jsb2NrZWRJcHY2KGFkZHJlc3MuYWRkcmVzcykgOiAhaXNCbG9ja2VkSXB2NChhZGRyZXNzLmFkZHJlc3MpO1xufVxuXG4vKipcbiAqIFZhbGlkYXRlIG9uZSBjb25jcmV0ZSBwYWdlLWZldGNoIHRhcmdldCBpbW1lZGlhdGVseSBiZWZvcmUgbmV0d29yayBJL08uXG4gKlxuICogUmVqZWN0cyBlbWJlZGRlZCBjcmVkZW50aWFscywgbm9uLUhUVFBTIFVSTHMsIGRpc2FsbG93ZWQgaG9zdHMvcGF0aHMsIGFuZFxuICogaG9zdG5hbWVzIHdob3NlIEROUyBhbnN3ZXJzIGluY2x1ZGUgYW55IHByaXZhdGUsIGxvb3BiYWNrLCBsaW5rLWxvY2FsLCBvclxuICogcmVzZXJ2ZWQgYWRkcmVzcy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFzc2VydEZldGNoVXJsQWxsb3dlZChcbiAgdmFsdWU6IHN0cmluZyxcbiAgYWxsb3dMaXN0OiBVcmxBbGxvd0xpc3QsXG4gIHJlc29sdmU6IFJlc29sdmVBZGRyZXNzZXMgPSByZXNvbHZlQWRkcmVzc2VzLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGxldCB1cmw6IFVSTDtcbiAgdHJ5IHtcbiAgICB1cmwgPSBuZXcgVVJMKHZhbHVlKTtcbiAgfSBjYXRjaCB7XG4gICAgdGhyb3cgVmFsaWRhdGlvbkVycm9yLmludmFsaWRWYWx1ZShcInVybFwiLCB2YWx1ZSwgXCJhbiBhYnNvbHV0ZSBIVFRQUyBVUkxcIik7XG4gIH1cbiAgaWYgKHVybC5wcm90b2NvbCAhPT0gXCJodHRwczpcIiB8fCB1cmwudXNlcm5hbWUgfHwgdXJsLnBhc3N3b3JkKSB7XG4gICAgdGhyb3cgVmFsaWRhdGlvbkVycm9yLmludmFsaWRWYWx1ZShcbiAgICAgIFwidXJsXCIsXG4gICAgICB2YWx1ZSxcbiAgICAgIFwiYW4gYWJzb2x1dGUgSFRUUFMgVVJMIHdpdGhvdXQgZW1iZWRkZWQgY3JlZGVudGlhbHNcIixcbiAgICApO1xuICB9XG4gIGFzc2VydFVybEFsbG93ZWQodXJsLmhyZWYsIGFsbG93TGlzdCk7XG5cbiAgY29uc3QgbGl0ZXJhbEZhbWlseSA9IGlzSVAodXJsLmhvc3RuYW1lKTtcbiAgY29uc3QgYWRkcmVzc2VzID0gbGl0ZXJhbEZhbWlseVxuICAgID8gW3sgYWRkcmVzczogdXJsLmhvc3RuYW1lLCBmYW1pbHk6IGxpdGVyYWxGYW1pbHkgfV1cbiAgICA6IGF3YWl0IHJlc29sdmUodXJsLmhvc3RuYW1lKTtcbiAgaWYgKGFkZHJlc3Nlcy5sZW5ndGggPT09IDAgfHwgYWRkcmVzc2VzLnNvbWUoKGFkZHJlc3MpID0+ICFpc1B1YmxpY0FkZHJlc3MoYWRkcmVzcykpKSB7XG4gICAgdGhyb3cgVmFsaWRhdGlvbkVycm9yLmludmFsaWRWYWx1ZShcbiAgICAgIFwidXJsXCIsXG4gICAgICB2YWx1ZSxcbiAgICAgIFwiYSBVUkwgdGhhdCByZXNvbHZlcyBvbmx5IHRvIHB1YmxpYyBhZGRyZXNzZXNcIixcbiAgICApO1xuICB9XG59XG4iXX0=
|
package/lib/src/fetch.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { log } from "@dbx-tools/shared-core";
|
|
18
18
|
import { gotScraping } from "got-scraping";
|
|
19
|
-
import {
|
|
19
|
+
import { assertFetchUrlAllowed } from "./_fetch-url.js";
|
|
20
20
|
import { toCallSettings, webFetchExecuteDefaults } from "./defaults.js";
|
|
21
21
|
import { decodeHtmlEntities, htmlToText } from "./html-text.js";
|
|
22
22
|
import { executeRead } from "./runtime.js";
|
|
@@ -42,11 +42,16 @@ function truncate(text, max) {
|
|
|
42
42
|
* `signal` cancels the in-flight fetch.
|
|
43
43
|
*/
|
|
44
44
|
export async function runWebFetch(request, config, signal) {
|
|
45
|
-
|
|
45
|
+
await assertFetchUrlAllowed(request.url, config.allowList);
|
|
46
46
|
const cap = Math.min(request.maxLength ?? config.fetchMaxLength, config.fetchMaxLength);
|
|
47
47
|
// The response is cached before the format reduction, so the same page read
|
|
48
48
|
// as text and as html shares one network round trip.
|
|
49
|
-
const page = await executeRead("page-fetch", toCallSettings(webFetchExecuteDefaults, config.timeoutMs, [
|
|
49
|
+
const page = await executeRead("page-fetch", toCallSettings(webFetchExecuteDefaults, config.timeoutMs, [
|
|
50
|
+
"web-search",
|
|
51
|
+
"fetch",
|
|
52
|
+
request.url,
|
|
53
|
+
...config.allowList.patterns,
|
|
54
|
+
]), async (executeSignal) => {
|
|
50
55
|
const response = await gotScraping({
|
|
51
56
|
url: request.url,
|
|
52
57
|
// got's own timeout aborts the socket and reports which phase timed
|
|
@@ -54,6 +59,13 @@ export async function runWebFetch(request, config, signal) {
|
|
|
54
59
|
timeout: { request: config.timeoutMs },
|
|
55
60
|
throwHttpErrors: false,
|
|
56
61
|
followRedirect: true,
|
|
62
|
+
hooks: {
|
|
63
|
+
beforeRequest: [
|
|
64
|
+
async (options) => {
|
|
65
|
+
await assertFetchUrlAllowed(String(options.url), config.allowList);
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
57
69
|
...(executeSignal ? { signal: executeSignal } : {}),
|
|
58
70
|
});
|
|
59
71
|
return {
|
|
@@ -63,6 +75,7 @@ export async function runWebFetch(request, config, signal) {
|
|
|
63
75
|
body: typeof response.body === "string" ? response.body : String(response.body ?? ""),
|
|
64
76
|
};
|
|
65
77
|
}, signal);
|
|
78
|
+
await assertFetchUrlAllowed(page.url, config.allowList);
|
|
66
79
|
const isHtml = !page.contentType || /html|xml/i.test(page.contentType);
|
|
67
80
|
const rawContent = request.format === "html" || !isHtml ? page.body : htmlToText(page.body);
|
|
68
81
|
const { content, truncated } = truncate(rawContent, cap);
|
|
@@ -83,4 +96,4 @@ export async function runWebFetch(request, config, signal) {
|
|
|
83
96
|
truncated,
|
|
84
97
|
};
|
|
85
98
|
}
|
|
86
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmV0Y2guanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZmV0Y2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQzdDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDM0MsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFbEQsT0FBTyxFQUFFLGNBQWMsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN4RSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsVUFBVSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDaEUsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUczQyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLGtCQUFrQixDQUFDLENBQUM7QUFVOUMsbUVBQW1FO0FBQ25FLFNBQVMsWUFBWSxDQUFDLElBQVk7SUFDaEMsTUFBTSxLQUFLLEdBQUcsa0NBQWtDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzVELE1BQU0sS0FBSyxHQUFHLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQ3BFLE9BQU8sS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0FBQzlDLENBQUM7QUFFRCxvRUFBb0U7QUFDcEUsU0FBUyxRQUFRLENBQUMsSUFBWSxFQUFFLEdBQVc7SUFDekMsSUFBSSxJQUFJLENBQUMsTUFBTSxJQUFJLEdBQUc7UUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLENBQUM7SUFDbkUsT0FBTyxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFDMUQsQ0FBQztBQUVEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLFdBQVcsQ0FDL0IsT0FBd0IsRUFDeEIsTUFBK0IsRUFDL0IsTUFBb0I7SUFFcEIsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDaEQsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsU0FBUyxJQUFJLE1BQU0sQ0FBQyxjQUFjLEVBQUUsTUFBTSxDQUFDLGNBQWMsQ0FBQyxDQUFDO0lBRXhGLDRFQUE0RTtJQUM1RSxxREFBcUQ7SUFDckQsTUFBTSxJQUFJLEdBQUcsTUFBTSxXQUFXLENBQzVCLFlBQVksRUFDWixjQUFjLENBQUMsdUJBQXVCLEVBQUUsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDLFlBQVksRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQy9GLEtBQUssRUFBRSxhQUFhLEVBQXdCLEVBQUU7UUFDNUMsTUFBTSxRQUFRLEdBQUcsTUFBTSxXQUFXLENBQUM7WUFDakMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxHQUFHO1lBQ2hCLG9FQUFvRTtZQUNwRSxtRUFBbUU7WUFDbkUsT0FBTyxFQUFFLEVBQUUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxTQUFTLEVBQUU7WUFDdEMsZUFBZSxFQUFFLEtBQUs7WUFDdEIsY0FBYyxFQUFFLElBQUk7WUFDcEIsR0FBRyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsYUFBYSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztTQUNwRCxDQUFDLENBQUM7UUFDSCxPQUFPO1lBQ0wsR0FBRyxFQUFFLFFBQVEsQ0FBQyxHQUFHLElBQUksT0FBTyxDQUFDLEdBQUc7WUFDaEMsVUFBVSxFQUFFLFFBQVEsQ0FBQyxVQUFVO1lBQy9CLFdBQVcsRUFBRSxRQUFRLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQztZQUM3QyxJQUFJLEVBQUUsT0FBTyxRQUFRLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDO1NBQ3RGLENBQUM7SUFDSixDQUFDLEVBQ0QsTUFBTSxDQUNQLENBQUM7SUFFRixNQUFNLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxXQUFXLElBQUksV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDdkUsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLE1BQU0sS0FBSyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDNUYsTUFBTSxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsR0FBRyxRQUFRLENBQUMsVUFBVSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3pELE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0lBRTNELE1BQU0sQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUFFO1FBQ3RCLEdBQUcsRUFBRSxJQUFJLENBQUMsR0FBRztRQUNiLE1BQU0sRUFBRSxJQUFJLENBQUMsVUFBVTtRQUN2QixLQUFLLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNO1FBQ3ZCLFFBQVEsRUFBRSxPQUFPLENBQUMsTUFBTTtRQUN4QixHQUFHLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0tBQzFDLENBQUMsQ0FBQztJQUVILE9BQU87UUFDTCxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUc7UUFDYixNQUFNLEVBQUUsSUFBSSxDQUFDLFVBQVU7UUFDdkIsR0FBRyxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzlELEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUMzQixPQUFPO1FBQ1AsU0FBUztLQUNWLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBQYWdlIGZldGNoaW5nIG92ZXIge0BsaW5rIGh0dHBzOi8vd3d3Lm5wbWpzLmNvbS9wYWNrYWdlL2dvdC1zY3JhcGluZyB8IGdvdC1zY3JhcGluZ31cbiAqIC0gYSBgZ290YCB3cmFwcGVyIHRoYXQgZ2VuZXJhdGVzIGJyb3dzZXItbGlrZSBUTFMgKyBoZWFkZXIgZmluZ2VycHJpbnRzLCBzb1xuICogZmV0Y2hlcyBzdXJ2aXZlIHRoZSBib3Qgd2FsbHMgYSBwbGFpbiBgZmV0Y2hgIHRyaXBzLiB7QGxpbmsgcnVuV2ViRmV0Y2h9XG4gKiBlbmZvcmNlcyB0aGUgVVJMIGFsbG93LWxpc3QgKGFuIGV4cGxpY2l0IGZldGNoIG9mIGEgZGlzYWxsb3dlZCBVUkwgaXNcbiAqIHJlZnVzZWQsIG5vdCBzaWxlbnRseSBlbXB0aWVkKSwgZmV0Y2hlcyB3aXRoIHRoZSBwbHVnaW4ncyB0aW1lb3V0LCBhbmRcbiAqIHJldHVybnMgZWl0aGVyIHRoZSByYXcgSFRNTCBvciBhIHJlYWRhYmxlIHBsYWluLXRleHQgcmVkdWN0aW9uLCBjYXBwZWQgYXRcbiAqIHRoZSBjb25maWd1cmVkIGxlbmd0aC5cbiAqXG4gKiBUaGUgSFRNTC10by10ZXh0IHJlZHVjdGlvbiBpcyBkZWxpYmVyYXRlbHkgZGVwZW5kZW5jeS1mcmVlIChzdHJpcFxuICogc2NyaXB0L3N0eWxlLCB1bndyYXAgdGFncywgZGVjb2RlIGEgaGFuZGZ1bCBvZiBlbnRpdGllcywgY29sbGFwc2VcbiAqIHdoaXRlc3BhY2UpOiBnb29kIGVub3VnaCB0byBmZWVkIGEgbW9kZWwsIGFuZCBpdCBrZWVwcyB0aGUgYWRkLW9uJ3NcbiAqIGRlcGVuZGVuY3kgc3VyZmFjZSB0byB0aGUgdHdvIGxpYnJhcmllcyB0aGUgdGFzayBjYWxsZWQgZm9yLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBsb2cgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgZ290U2NyYXBpbmcgfSBmcm9tIFwiZ290LXNjcmFwaW5nXCI7XG5pbXBvcnQgeyBhc3NlcnRVcmxBbGxvd2VkIH0gZnJvbSBcIi4vYWxsb3dsaXN0LnRzXCI7XG5pbXBvcnQgdHlwZSB7IFJlc29sdmVkV2ViU2VhcmNoQ29uZmlnIH0gZnJvbSBcIi4vY29uZmlnLnRzXCI7XG5pbXBvcnQgeyB0b0NhbGxTZXR0aW5ncywgd2ViRmV0Y2hFeGVjdXRlRGVmYXVsdHMgfSBmcm9tIFwiLi9kZWZhdWx0cy50c1wiO1xuaW1wb3J0IHsgZGVjb2RlSHRtbEVudGl0aWVzLCBodG1sVG9UZXh0IH0gZnJvbSBcIi4vaHRtbC10ZXh0LnRzXCI7XG5pbXBvcnQgeyBleGVjdXRlUmVhZCB9IGZyb20gXCIuL3J1bnRpbWUudHNcIjtcbmltcG9ydCB0eXBlIHsgV2ViRmV0Y2hSZXF1ZXN0LCBXZWJGZXRjaFJlc3VsdCB9IGZyb20gXCIuL3NjaGVtYS50c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwid2ViLXNlYXJjaC9mZXRjaFwiKTtcblxuLyoqIFRoZSBzbGljZSBvZiBhIGdvdC1zY3JhcGluZyByZXNwb25zZSB0aGlzIG1vZHVsZSByZWFkcy4gKi9cbmludGVyZmFjZSBGZXRjaGVkUGFnZSB7XG4gIHVybDogc3RyaW5nO1xuICBzdGF0dXNDb2RlOiBudW1iZXI7XG4gIGNvbnRlbnRUeXBlOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIGJvZHk6IHN0cmluZztcbn1cblxuLyoqIFB1bGwgdGhlIDx0aXRsZT4gdGV4dCBvdXQgb2YgYW4gSFRNTCBkb2N1bWVudCwgd2hlbiBwcmVzZW50LiAqL1xuZnVuY3Rpb24gZXh0cmFjdFRpdGxlKGh0bWw6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IG1hdGNoID0gLzx0aXRsZVtePl0qPihbXFxzXFxTXSo/KTxcXC90aXRsZT4vaS5leGVjKGh0bWwpO1xuICBjb25zdCB0aXRsZSA9IG1hdGNoPy5bMV0gPyBkZWNvZGVIdG1sRW50aXRpZXMobWF0Y2hbMV0pLnRyaW0oKSA6IFwiXCI7XG4gIHJldHVybiB0aXRsZS5sZW5ndGggPiAwID8gdGl0bGUgOiB1bmRlZmluZWQ7XG59XG5cbi8qKiBUcnVuY2F0ZSBgdGV4dGAgdG8gYG1heGAgY2hhcnMsIHJlcG9ydGluZyB3aGV0aGVyIGl0IHdhcyBjdXQuICovXG5mdW5jdGlvbiB0cnVuY2F0ZSh0ZXh0OiBzdHJpbmcsIG1heDogbnVtYmVyKTogeyBjb250ZW50OiBzdHJpbmc7IHRydW5jYXRlZDogYm9vbGVhbiB9IHtcbiAgaWYgKHRleHQubGVuZ3RoIDw9IG1heCkgcmV0dXJuIHsgY29udGVudDogdGV4dCwgdHJ1bmNhdGVkOiBmYWxzZSB9O1xuICByZXR1cm4geyBjb250ZW50OiB0ZXh0LnNsaWNlKDAsIG1heCksIHRydW5jYXRlZDogdHJ1ZSB9O1xufVxuXG4vKipcbiAqIEZldGNoIGEgc2luZ2xlIFVSTCBhbmQgcmV0dXJuIGl0cyBjb250ZW50IGluIHRoZSByZXF1ZXN0ZWQgZm9ybWF0LlxuICpcbiAqIFRocm93cyB3aGVuIHRoZSBVUkwgaXMgbm90IHBlcm1pdHRlZCBieSB0aGUgYWxsb3ctbGlzdCAodGhlIHZpc2libGUsXG4gKiBjb3JyZWN0YWJsZSBmYWlsdXJlIHRoZSBkZXNpZ24gY2FsbHMgZm9yIG9uIHRoZSBmZXRjaCBwYXRoKS4gVGhlIHJlcXVlc3Qnc1xuICogYG1heExlbmd0aGAgbmFycm93cyAobmV2ZXIgd2lkZW5zKSB0aGUgcGx1Z2luJ3MgYGZldGNoTWF4TGVuZ3RoYCBjYXAuXG4gKiBgc2lnbmFsYCBjYW5jZWxzIHRoZSBpbi1mbGlnaHQgZmV0Y2guXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBydW5XZWJGZXRjaChcbiAgcmVxdWVzdDogV2ViRmV0Y2hSZXF1ZXN0LFxuICBjb25maWc6IFJlc29sdmVkV2ViU2VhcmNoQ29uZmlnLFxuICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbik6IFByb21pc2U8V2ViRmV0Y2hSZXN1bHQ+IHtcbiAgYXNzZXJ0VXJsQWxsb3dlZChyZXF1ZXN0LnVybCwgY29uZmlnLmFsbG93TGlzdCk7XG4gIGNvbnN0IGNhcCA9IE1hdGgubWluKHJlcXVlc3QubWF4TGVuZ3RoID8/IGNvbmZpZy5mZXRjaE1heExlbmd0aCwgY29uZmlnLmZldGNoTWF4TGVuZ3RoKTtcblxuICAvLyBUaGUgcmVzcG9uc2UgaXMgY2FjaGVkIGJlZm9yZSB0aGUgZm9ybWF0IHJlZHVjdGlvbiwgc28gdGhlIHNhbWUgcGFnZSByZWFkXG4gIC8vIGFzIHRleHQgYW5kIGFzIGh0bWwgc2hhcmVzIG9uZSBuZXR3b3JrIHJvdW5kIHRyaXAuXG4gIGNvbnN0IHBhZ2UgPSBhd2FpdCBleGVjdXRlUmVhZChcbiAgICBcInBhZ2UtZmV0Y2hcIixcbiAgICB0b0NhbGxTZXR0aW5ncyh3ZWJGZXRjaEV4ZWN1dGVEZWZhdWx0cywgY29uZmlnLnRpbWVvdXRNcywgW1wid2ViLXNlYXJjaFwiLCBcImZldGNoXCIsIHJlcXVlc3QudXJsXSksXG4gICAgYXN5bmMgKGV4ZWN1dGVTaWduYWwpOiBQcm9taXNlPEZldGNoZWRQYWdlPiA9PiB7XG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGdvdFNjcmFwaW5nKHtcbiAgICAgICAgdXJsOiByZXF1ZXN0LnVybCxcbiAgICAgICAgLy8gZ290J3Mgb3duIHRpbWVvdXQgYWJvcnRzIHRoZSBzb2NrZXQgYW5kIHJlcG9ydHMgd2hpY2ggcGhhc2UgdGltZWRcbiAgICAgICAgLy8gb3V0OyB0aGUgaW50ZXJjZXB0b3IgdGltZW91dCBib3VuZHMgdGhlIHdob2xlIGF0dGVtcHQgYXJvdW5kIGl0LlxuICAgICAgICB0aW1lb3V0OiB7IHJlcXVlc3Q6IGNvbmZpZy50aW1lb3V0TXMgfSxcbiAgICAgICAgdGhyb3dIdHRwRXJyb3JzOiBmYWxzZSxcbiAgICAgICAgZm9sbG93UmVkaXJlY3Q6IHRydWUsXG4gICAgICAgIC4uLihleGVjdXRlU2lnbmFsID8geyBzaWduYWw6IGV4ZWN1dGVTaWduYWwgfSA6IHt9KSxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdXJsOiByZXNwb25zZS51cmwgPz8gcmVxdWVzdC51cmwsXG4gICAgICAgIHN0YXR1c0NvZGU6IHJlc3BvbnNlLnN0YXR1c0NvZGUsXG4gICAgICAgIGNvbnRlbnRUeXBlOiByZXNwb25zZS5oZWFkZXJzW1wiY29udGVudC10eXBlXCJdLFxuICAgICAgICBib2R5OiB0eXBlb2YgcmVzcG9uc2UuYm9keSA9PT0gXCJzdHJpbmdcIiA/IHJlc3BvbnNlLmJvZHkgOiBTdHJpbmcocmVzcG9uc2UuYm9keSA/PyBcIlwiKSxcbiAgICAgIH07XG4gICAgfSxcbiAgICBzaWduYWwsXG4gICk7XG5cbiAgY29uc3QgaXNIdG1sID0gIXBhZ2UuY29udGVudFR5cGUgfHwgL2h0bWx8eG1sL2kudGVzdChwYWdlLmNvbnRlbnRUeXBlKTtcbiAgY29uc3QgcmF3Q29udGVudCA9IHJlcXVlc3QuZm9ybWF0ID09PSBcImh0bWxcIiB8fCAhaXNIdG1sID8gcGFnZS5ib2R5IDogaHRtbFRvVGV4dChwYWdlLmJvZHkpO1xuICBjb25zdCB7IGNvbnRlbnQsIHRydW5jYXRlZCB9ID0gdHJ1bmNhdGUocmF3Q29udGVudCwgY2FwKTtcbiAgY29uc3QgdGl0bGUgPSBpc0h0bWwgPyBleHRyYWN0VGl0bGUocGFnZS5ib2R5KSA6IHVuZGVmaW5lZDtcblxuICBsb2dnZXIuZGVidWcoXCJmZXRjaGVkXCIsIHtcbiAgICB1cmw6IHBhZ2UudXJsLFxuICAgIHN0YXR1czogcGFnZS5zdGF0dXNDb2RlLFxuICAgIGJ5dGVzOiBwYWdlLmJvZHkubGVuZ3RoLFxuICAgIHJldHVybmVkOiBjb250ZW50Lmxlbmd0aCxcbiAgICAuLi4odHJ1bmNhdGVkID8geyB0cnVuY2F0ZWQ6IHRydWUgfSA6IHt9KSxcbiAgfSk7XG5cbiAgcmV0dXJuIHtcbiAgICB1cmw6IHBhZ2UudXJsLFxuICAgIHN0YXR1czogcGFnZS5zdGF0dXNDb2RlLFxuICAgIC4uLihwYWdlLmNvbnRlbnRUeXBlID8geyBjb250ZW50VHlwZTogcGFnZS5jb250ZW50VHlwZSB9IDoge30pLFxuICAgIC4uLih0aXRsZSA/IHsgdGl0bGUgfSA6IHt9KSxcbiAgICBjb250ZW50LFxuICAgIHRydW5jYXRlZCxcbiAgfTtcbn1cbiJdfQ==
|
|
99
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmV0Y2guanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZmV0Y2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUgsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQzdDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDM0MsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFeEQsT0FBTyxFQUFFLGNBQWMsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN4RSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsVUFBVSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDaEUsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUczQyxNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLGtCQUFrQixDQUFDLENBQUM7QUFVOUMsbUVBQW1FO0FBQ25FLFNBQVMsWUFBWSxDQUFDLElBQVk7SUFDaEMsTUFBTSxLQUFLLEdBQUcsa0NBQWtDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzVELE1BQU0sS0FBSyxHQUFHLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQ3BFLE9BQU8sS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0FBQzlDLENBQUM7QUFFRCxvRUFBb0U7QUFDcEUsU0FBUyxRQUFRLENBQUMsSUFBWSxFQUFFLEdBQVc7SUFDekMsSUFBSSxJQUFJLENBQUMsTUFBTSxJQUFJLEdBQUc7UUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLElB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LENBQUMsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFFM0QsTUFBTSxDQUFDLEtBQUssQ0FBQyxTQUFTLEVBQUU7UUFDdEIsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHO1FBQ2IsTUFBTSxFQUFFLElBQUksQ0FBQyxVQUFVO1FBQ3ZCLEtBQUssRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU07UUFDdkIsUUFBUSxFQUFFLE9BQU8sQ0FBQyxNQUFNO1FBQ3hCLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7S0FDMUMsQ0FBQyxDQUFDO0lBRUgsT0FBTztRQUNMLEdBQUcsRUFBRSxJQUFJLENBQUMsR0FBRztRQUNiLE1BQU0sRUFBRSxJQUFJLENBQUMsVUFBVTtRQUN2QixHQUFHLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsRUFBRSxXQUFXLEVBQUUsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDOUQsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzNCLE9BQU87UUFDUCxTQUFTO0tBQ1YsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBhZ2UgZmV0Y2hpbmcgb3ZlciB7QGxpbmsgaHR0cHM6Ly93d3cubnBtanMuY29tL3BhY2thZ2UvZ290LXNjcmFwaW5nIHwgZ290LXNjcmFwaW5nfVxuICogLSBhIGBnb3RgIHdyYXBwZXIgdGhhdCBnZW5lcmF0ZXMgYnJvd3Nlci1saWtlIFRMUyArIGhlYWRlciBmaW5nZXJwcmludHMsIHNvXG4gKiBmZXRjaGVzIHN1cnZpdmUgdGhlIGJvdCB3YWxscyBhIHBsYWluIGBmZXRjaGAgdHJpcHMuIHtAbGluayBydW5XZWJGZXRjaH1cbiAqIGVuZm9yY2VzIHRoZSBVUkwgYWxsb3ctbGlzdCAoYW4gZXhwbGljaXQgZmV0Y2ggb2YgYSBkaXNhbGxvd2VkIFVSTCBpc1xuICogcmVmdXNlZCwgbm90IHNpbGVudGx5IGVtcHRpZWQpLCBmZXRjaGVzIHdpdGggdGhlIHBsdWdpbidzIHRpbWVvdXQsIGFuZFxuICogcmV0dXJucyBlaXRoZXIgdGhlIHJhdyBIVE1MIG9yIGEgcmVhZGFibGUgcGxhaW4tdGV4dCByZWR1Y3Rpb24sIGNhcHBlZCBhdFxuICogdGhlIGNvbmZpZ3VyZWQgbGVuZ3RoLlxuICpcbiAqIFRoZSBIVE1MLXRvLXRleHQgcmVkdWN0aW9uIGlzIGRlbGliZXJhdGVseSBkZXBlbmRlbmN5LWZyZWUgKHN0cmlwXG4gKiBzY3JpcHQvc3R5bGUsIHVud3JhcCB0YWdzLCBkZWNvZGUgYSBoYW5kZnVsIG9mIGVudGl0aWVzLCBjb2xsYXBzZVxuICogd2hpdGVzcGFjZSk6IGdvb2QgZW5vdWdoIHRvIGZlZWQgYSBtb2RlbCwgYW5kIGl0IGtlZXBzIHRoZSBhZGQtb24nc1xuICogZGVwZW5kZW5jeSBzdXJmYWNlIHRvIHRoZSB0d28gbGlicmFyaWVzIHRoZSB0YXNrIGNhbGxlZCBmb3IuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IGxvZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyBnb3RTY3JhcGluZyB9IGZyb20gXCJnb3Qtc2NyYXBpbmdcIjtcbmltcG9ydCB7IGFzc2VydEZldGNoVXJsQWxsb3dlZCB9IGZyb20gXCIuL19mZXRjaC11cmwudHNcIjtcbmltcG9ydCB0eXBlIHsgUmVzb2x2ZWRXZWJTZWFyY2hDb25maWcgfSBmcm9tIFwiLi9jb25maWcudHNcIjtcbmltcG9ydCB7IHRvQ2FsbFNldHRpbmdzLCB3ZWJGZXRjaEV4ZWN1dGVEZWZhdWx0cyB9IGZyb20gXCIuL2RlZmF1bHRzLnRzXCI7XG5pbXBvcnQgeyBkZWNvZGVIdG1sRW50aXRpZXMsIGh0bWxUb1RleHQgfSBmcm9tIFwiLi9odG1sLXRleHQudHNcIjtcbmltcG9ydCB7IGV4ZWN1dGVSZWFkIH0gZnJvbSBcIi4vcnVudGltZS50c1wiO1xuaW1wb3J0IHR5cGUgeyBXZWJGZXRjaFJlcXVlc3QsIFdlYkZldGNoUmVzdWx0IH0gZnJvbSBcIi4vc2NoZW1hLnRzXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJ3ZWItc2VhcmNoL2ZldGNoXCIpO1xuXG4vKiogVGhlIHNsaWNlIG9mIGEgZ290LXNjcmFwaW5nIHJlc3BvbnNlIHRoaXMgbW9kdWxlIHJlYWRzLiAqL1xuaW50ZXJmYWNlIEZldGNoZWRQYWdlIHtcbiAgdXJsOiBzdHJpbmc7XG4gIHN0YXR1c0NvZGU6IG51bWJlcjtcbiAgY29udGVudFR5cGU6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgYm9keTogc3RyaW5nO1xufVxuXG4vKiogUHVsbCB0aGUgPHRpdGxlPiB0ZXh0IG91dCBvZiBhbiBIVE1MIGRvY3VtZW50LCB3aGVuIHByZXNlbnQuICovXG5mdW5jdGlvbiBleHRyYWN0VGl0bGUoaHRtbDogc3RyaW5nKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgbWF0Y2ggPSAvPHRpdGxlW14+XSo+KFtcXHNcXFNdKj8pPFxcL3RpdGxlPi9pLmV4ZWMoaHRtbCk7XG4gIGNvbnN0IHRpdGxlID0gbWF0Y2g/LlsxXSA/IGRlY29kZUh0bWxFbnRpdGllcyhtYXRjaFsxXSkudHJpbSgpIDogXCJcIjtcbiAgcmV0dXJuIHRpdGxlLmxlbmd0aCA+IDAgPyB0aXRsZSA6IHVuZGVmaW5lZDtcbn1cblxuLyoqIFRydW5jYXRlIGB0ZXh0YCB0byBgbWF4YCBjaGFycywgcmVwb3J0aW5nIHdoZXRoZXIgaXQgd2FzIGN1dC4gKi9cbmZ1bmN0aW9uIHRydW5jYXRlKHRleHQ6IHN0cmluZywgbWF4OiBudW1iZXIpOiB7IGNvbnRlbnQ6IHN0cmluZzsgdHJ1bmNhdGVkOiBib29sZWFuIH0ge1xuICBpZiAodGV4dC5sZW5ndGggPD0gbWF4KSByZXR1cm4geyBjb250ZW50OiB0ZXh0LCB0cnVuY2F0ZWQ6IGZhbHNlIH07XG4gIHJldHVybiB7IGNvbnRlbnQ6IHRleHQuc2xpY2UoMCwgbWF4KSwgdHJ1bmNhdGVkOiB0cnVlIH07XG59XG5cbi8qKlxuICogRmV0Y2ggYSBzaW5nbGUgVVJMIGFuZCByZXR1cm4gaXRzIGNvbnRlbnQgaW4gdGhlIHJlcXVlc3RlZCBmb3JtYXQuXG4gKlxuICogVGhyb3dzIHdoZW4gdGhlIFVSTCBpcyBub3QgcGVybWl0dGVkIGJ5IHRoZSBhbGxvdy1saXN0ICh0aGUgdmlzaWJsZSxcbiAqIGNvcnJlY3RhYmxlIGZhaWx1cmUgdGhlIGRlc2lnbiBjYWxscyBmb3Igb24gdGhlIGZldGNoIHBhdGgpLiBUaGUgcmVxdWVzdCdzXG4gKiBgbWF4TGVuZ3RoYCBuYXJyb3dzIChuZXZlciB3aWRlbnMpIHRoZSBwbHVnaW4ncyBgZmV0Y2hNYXhMZW5ndGhgIGNhcC5cbiAqIGBzaWduYWxgIGNhbmNlbHMgdGhlIGluLWZsaWdodCBmZXRjaC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJ1bldlYkZldGNoKFxuICByZXF1ZXN0OiBXZWJGZXRjaFJlcXVlc3QsXG4gIGNvbmZpZzogUmVzb2x2ZWRXZWJTZWFyY2hDb25maWcsXG4gIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuKTogUHJvbWlzZTxXZWJGZXRjaFJlc3VsdD4ge1xuICBhd2FpdCBhc3NlcnRGZXRjaFVybEFsbG93ZWQocmVxdWVzdC51cmwsIGNvbmZpZy5hbGxvd0xpc3QpO1xuICBjb25zdCBjYXAgPSBNYXRoLm1pbihyZXF1ZXN0Lm1heExlbmd0aCA/PyBjb25maWcuZmV0Y2hNYXhMZW5ndGgsIGNvbmZpZy5mZXRjaE1heExlbmd0aCk7XG5cbiAgLy8gVGhlIHJlc3BvbnNlIGlzIGNhY2hlZCBiZWZvcmUgdGhlIGZvcm1hdCByZWR1Y3Rpb24sIHNvIHRoZSBzYW1lIHBhZ2UgcmVhZFxuICAvLyBhcyB0ZXh0IGFuZCBhcyBodG1sIHNoYXJlcyBvbmUgbmV0d29yayByb3VuZCB0cmlwLlxuICBjb25zdCBwYWdlID0gYXdhaXQgZXhlY3V0ZVJlYWQoXG4gICAgXCJwYWdlLWZldGNoXCIsXG4gICAgdG9DYWxsU2V0dGluZ3Mod2ViRmV0Y2hFeGVjdXRlRGVmYXVsdHMsIGNvbmZpZy50aW1lb3V0TXMsIFtcbiAgICAgIFwid2ViLXNlYXJjaFwiLFxuICAgICAgXCJmZXRjaFwiLFxuICAgICAgcmVxdWVzdC51cmwsXG4gICAgICAuLi5jb25maWcuYWxsb3dMaXN0LnBhdHRlcm5zLFxuICAgIF0pLFxuICAgIGFzeW5jIChleGVjdXRlU2lnbmFsKTogUHJvbWlzZTxGZXRjaGVkUGFnZT4gPT4ge1xuICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBnb3RTY3JhcGluZyh7XG4gICAgICAgIHVybDogcmVxdWVzdC51cmwsXG4gICAgICAgIC8vIGdvdCdzIG93biB0aW1lb3V0IGFib3J0cyB0aGUgc29ja2V0IGFuZCByZXBvcnRzIHdoaWNoIHBoYXNlIHRpbWVkXG4gICAgICAgIC8vIG91dDsgdGhlIGludGVyY2VwdG9yIHRpbWVvdXQgYm91bmRzIHRoZSB3aG9sZSBhdHRlbXB0IGFyb3VuZCBpdC5cbiAgICAgICAgdGltZW91dDogeyByZXF1ZXN0OiBjb25maWcudGltZW91dE1zIH0sXG4gICAgICAgIHRocm93SHR0cEVycm9yczogZmFsc2UsXG4gICAgICAgIGZvbGxvd1JlZGlyZWN0OiB0cnVlLFxuICAgICAgICBob29rczoge1xuICAgICAgICAgIGJlZm9yZVJlcXVlc3Q6IFtcbiAgICAgICAgICAgIGFzeW5jIChvcHRpb25zKSA9PiB7XG4gICAgICAgICAgICAgIGF3YWl0IGFzc2VydEZldGNoVXJsQWxsb3dlZChTdHJpbmcob3B0aW9ucy51cmwpLCBjb25maWcuYWxsb3dMaXN0KTtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgICAgLi4uKGV4ZWN1dGVTaWduYWwgPyB7IHNpZ25hbDogZXhlY3V0ZVNpZ25hbCB9IDoge30pLFxuICAgICAgfSk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICB1cmw6IHJlc3BvbnNlLnVybCA/PyByZXF1ZXN0LnVybCxcbiAgICAgICAgc3RhdHVzQ29kZTogcmVzcG9uc2Uuc3RhdHVzQ29kZSxcbiAgICAgICAgY29udGVudFR5cGU6IHJlc3BvbnNlLmhlYWRlcnNbXCJjb250ZW50LXR5cGVcIl0sXG4gICAgICAgIGJvZHk6IHR5cGVvZiByZXNwb25zZS5ib2R5ID09PSBcInN0cmluZ1wiID8gcmVzcG9uc2UuYm9keSA6IFN0cmluZyhyZXNwb25zZS5ib2R5ID8/IFwiXCIpLFxuICAgICAgfTtcbiAgICB9LFxuICAgIHNpZ25hbCxcbiAgKTtcbiAgYXdhaXQgYXNzZXJ0RmV0Y2hVcmxBbGxvd2VkKHBhZ2UudXJsLCBjb25maWcuYWxsb3dMaXN0KTtcblxuICBjb25zdCBpc0h0bWwgPSAhcGFnZS5jb250ZW50VHlwZSB8fCAvaHRtbHx4bWwvaS50ZXN0KHBhZ2UuY29udGVudFR5cGUpO1xuICBjb25zdCByYXdDb250ZW50ID0gcmVxdWVzdC5mb3JtYXQgPT09IFwiaHRtbFwiIHx8ICFpc0h0bWwgPyBwYWdlLmJvZHkgOiBodG1sVG9UZXh0KHBhZ2UuYm9keSk7XG4gIGNvbnN0IHsgY29udGVudCwgdHJ1bmNhdGVkIH0gPSB0cnVuY2F0ZShyYXdDb250ZW50LCBjYXApO1xuICBjb25zdCB0aXRsZSA9IGlzSHRtbCA/IGV4dHJhY3RUaXRsZShwYWdlLmJvZHkpIDogdW5kZWZpbmVkO1xuXG4gIGxvZ2dlci5kZWJ1ZyhcImZldGNoZWRcIiwge1xuICAgIHVybDogcGFnZS51cmwsXG4gICAgc3RhdHVzOiBwYWdlLnN0YXR1c0NvZGUsXG4gICAgYnl0ZXM6IHBhZ2UuYm9keS5sZW5ndGgsXG4gICAgcmV0dXJuZWQ6IGNvbnRlbnQubGVuZ3RoLFxuICAgIC4uLih0cnVuY2F0ZWQgPyB7IHRydW5jYXRlZDogdHJ1ZSB9IDoge30pLFxuICB9KTtcblxuICByZXR1cm4ge1xuICAgIHVybDogcGFnZS51cmwsXG4gICAgc3RhdHVzOiBwYWdlLnN0YXR1c0NvZGUsXG4gICAgLi4uKHBhZ2UuY29udGVudFR5cGUgPyB7IGNvbnRlbnRUeXBlOiBwYWdlLmNvbnRlbnRUeXBlIH0gOiB7fSksXG4gICAgLi4uKHRpdGxlID8geyB0aXRsZSB9IDoge30pLFxuICAgIGNvbnRlbnQsXG4gICAgdHJ1bmNhdGVkLFxuICB9O1xufVxuIl19
|
package/lib/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/allowlist.ts","../src/config.ts","../src/defaults.ts","../src/fetch.ts","../src/html-text.ts","../src/plugin.ts","../src/provider.ts","../src/runtime.ts","../src/schema.ts","../src/scrape.ts","../src/search.ts","../src/tool.ts","../index.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/_fetch-url.ts","../src/allowlist.ts","../src/config.ts","../src/defaults.ts","../src/fetch.ts","../src/html-text.ts","../src/plugin.ts","../src/provider.ts","../src/runtime.ts","../src/schema.ts","../src/scrape.ts","../src/search.ts","../src/tool.ts","../index.ts"],"version":"5.9.3"}
|
package/package.json
CHANGED
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@databricks/appkit": "^0.43.0",
|
|
29
|
-
"@dbx-tools/model": "0.6.
|
|
30
|
-
"@dbx-tools/path": "0.6.
|
|
31
|
-
"@dbx-tools/shared-core": "0.6.
|
|
32
|
-
"@dbx-tools/shared-model": "0.6.
|
|
29
|
+
"@dbx-tools/model": "0.6.73",
|
|
30
|
+
"@dbx-tools/path": "0.6.73",
|
|
31
|
+
"@dbx-tools/shared-core": "0.6.73",
|
|
32
|
+
"@dbx-tools/shared-model": "0.6.73",
|
|
33
33
|
"@mastra/core": "^1.47.0",
|
|
34
34
|
"got-scraping": "^4.2.1",
|
|
35
35
|
"zod": "4.3.6"
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"./package.json": "./package.json"
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
|
-
"version": "0.6.
|
|
51
|
+
"version": "0.6.73",
|
|
52
52
|
"types": "./lib/index.d.ts",
|
|
53
53
|
"type": "module",
|
|
54
54
|
"exports": {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network policy for page fetches.
|
|
3
|
+
*
|
|
4
|
+
* Every request, including redirects, is HTTPS, matches the configured URL
|
|
5
|
+
* allow-list, and resolves only to public addresses. Resolution happens in the
|
|
6
|
+
* request hook immediately before I/O so a redirect cannot bypass the policy.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { lookup } from "node:dns/promises";
|
|
12
|
+
import { isIP } from "node:net";
|
|
13
|
+
import { ValidationError } from "@databricks/appkit";
|
|
14
|
+
|
|
15
|
+
import { assertUrlAllowed, type UrlAllowList } from "./allowlist.ts";
|
|
16
|
+
|
|
17
|
+
/** DNS result shape used by Node and by deterministic tests. */
|
|
18
|
+
export interface ResolvedAddress {
|
|
19
|
+
address: string;
|
|
20
|
+
family: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Injectable DNS resolver for fetch-policy tests. */
|
|
24
|
+
export type ResolveAddresses = (hostname: string) => Promise<readonly ResolvedAddress[]>;
|
|
25
|
+
|
|
26
|
+
/** Resolve all addresses for a hostname without changing their family. */
|
|
27
|
+
async function resolveAddresses(hostname: string): Promise<readonly ResolvedAddress[]> {
|
|
28
|
+
return lookup(hostname, { all: true, verbatim: true });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Whether an IPv4 address is private, local, reserved, or non-unicast. */
|
|
32
|
+
function isBlockedIpv4(address: string): boolean {
|
|
33
|
+
const [a = -1, b = -1, c = -1] = address.split(".").map(Number);
|
|
34
|
+
return (
|
|
35
|
+
a === 0 ||
|
|
36
|
+
a === 10 ||
|
|
37
|
+
a === 127 ||
|
|
38
|
+
a >= 224 ||
|
|
39
|
+
(a === 100 && b >= 64 && b <= 127) ||
|
|
40
|
+
(a === 169 && b === 254) ||
|
|
41
|
+
(a === 172 && b >= 16 && b <= 31) ||
|
|
42
|
+
(a === 192 && b === 0 && c === 0) ||
|
|
43
|
+
(a === 192 && b === 0 && c === 2) ||
|
|
44
|
+
(a === 192 && b === 168) ||
|
|
45
|
+
(a === 198 && (b === 18 || b === 19)) ||
|
|
46
|
+
(a === 198 && b === 51 && c === 100) ||
|
|
47
|
+
(a === 203 && b === 0 && c === 113)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Whether an IPv6 address is private, local, reserved, or non-unicast. */
|
|
52
|
+
function isBlockedIpv6(address: string): boolean {
|
|
53
|
+
const normalized = address.toLowerCase();
|
|
54
|
+
return (
|
|
55
|
+
normalized === "::" ||
|
|
56
|
+
normalized === "::1" ||
|
|
57
|
+
normalized.startsWith("::ffff:") ||
|
|
58
|
+
normalized.startsWith("100:") ||
|
|
59
|
+
normalized.startsWith("2001:db8:") ||
|
|
60
|
+
normalized.startsWith("fc") ||
|
|
61
|
+
normalized.startsWith("fd") ||
|
|
62
|
+
/^fe[89ab]/.test(normalized) ||
|
|
63
|
+
normalized.startsWith("ff")
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Whether an address is globally routable enough for an outbound page fetch. */
|
|
68
|
+
function isPublicAddress(address: ResolvedAddress): boolean {
|
|
69
|
+
return address.family === 6 ? !isBlockedIpv6(address.address) : !isBlockedIpv4(address.address);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Validate one concrete page-fetch target immediately before network I/O.
|
|
74
|
+
*
|
|
75
|
+
* Rejects embedded credentials, non-HTTPS URLs, disallowed hosts/paths, and
|
|
76
|
+
* hostnames whose DNS answers include any private, loopback, link-local, or
|
|
77
|
+
* reserved address.
|
|
78
|
+
*/
|
|
79
|
+
export async function assertFetchUrlAllowed(
|
|
80
|
+
value: string,
|
|
81
|
+
allowList: UrlAllowList,
|
|
82
|
+
resolve: ResolveAddresses = resolveAddresses,
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
let url: URL;
|
|
85
|
+
try {
|
|
86
|
+
url = new URL(value);
|
|
87
|
+
} catch {
|
|
88
|
+
throw ValidationError.invalidValue("url", value, "an absolute HTTPS URL");
|
|
89
|
+
}
|
|
90
|
+
if (url.protocol !== "https:" || url.username || url.password) {
|
|
91
|
+
throw ValidationError.invalidValue(
|
|
92
|
+
"url",
|
|
93
|
+
value,
|
|
94
|
+
"an absolute HTTPS URL without embedded credentials",
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
assertUrlAllowed(url.href, allowList);
|
|
98
|
+
|
|
99
|
+
const literalFamily = isIP(url.hostname);
|
|
100
|
+
const addresses = literalFamily
|
|
101
|
+
? [{ address: url.hostname, family: literalFamily }]
|
|
102
|
+
: await resolve(url.hostname);
|
|
103
|
+
if (addresses.length === 0 || addresses.some((address) => !isPublicAddress(address))) {
|
|
104
|
+
throw ValidationError.invalidValue(
|
|
105
|
+
"url",
|
|
106
|
+
value,
|
|
107
|
+
"a URL that resolves only to public addresses",
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/fetch.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { log } from "@dbx-tools/shared-core";
|
|
19
19
|
import { gotScraping } from "got-scraping";
|
|
20
|
-
import {
|
|
20
|
+
import { assertFetchUrlAllowed } from "./_fetch-url.ts";
|
|
21
21
|
import type { ResolvedWebSearchConfig } from "./config.ts";
|
|
22
22
|
import { toCallSettings, webFetchExecuteDefaults } from "./defaults.ts";
|
|
23
23
|
import { decodeHtmlEntities, htmlToText } from "./html-text.ts";
|
|
@@ -60,14 +60,19 @@ export async function runWebFetch(
|
|
|
60
60
|
config: ResolvedWebSearchConfig,
|
|
61
61
|
signal?: AbortSignal,
|
|
62
62
|
): Promise<WebFetchResult> {
|
|
63
|
-
|
|
63
|
+
await assertFetchUrlAllowed(request.url, config.allowList);
|
|
64
64
|
const cap = Math.min(request.maxLength ?? config.fetchMaxLength, config.fetchMaxLength);
|
|
65
65
|
|
|
66
66
|
// The response is cached before the format reduction, so the same page read
|
|
67
67
|
// as text and as html shares one network round trip.
|
|
68
68
|
const page = await executeRead(
|
|
69
69
|
"page-fetch",
|
|
70
|
-
toCallSettings(webFetchExecuteDefaults, config.timeoutMs, [
|
|
70
|
+
toCallSettings(webFetchExecuteDefaults, config.timeoutMs, [
|
|
71
|
+
"web-search",
|
|
72
|
+
"fetch",
|
|
73
|
+
request.url,
|
|
74
|
+
...config.allowList.patterns,
|
|
75
|
+
]),
|
|
71
76
|
async (executeSignal): Promise<FetchedPage> => {
|
|
72
77
|
const response = await gotScraping({
|
|
73
78
|
url: request.url,
|
|
@@ -76,6 +81,13 @@ export async function runWebFetch(
|
|
|
76
81
|
timeout: { request: config.timeoutMs },
|
|
77
82
|
throwHttpErrors: false,
|
|
78
83
|
followRedirect: true,
|
|
84
|
+
hooks: {
|
|
85
|
+
beforeRequest: [
|
|
86
|
+
async (options) => {
|
|
87
|
+
await assertFetchUrlAllowed(String(options.url), config.allowList);
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
79
91
|
...(executeSignal ? { signal: executeSignal } : {}),
|
|
80
92
|
});
|
|
81
93
|
return {
|
|
@@ -87,6 +99,7 @@ export async function runWebFetch(
|
|
|
87
99
|
},
|
|
88
100
|
signal,
|
|
89
101
|
);
|
|
102
|
+
await assertFetchUrlAllowed(page.url, config.allowList);
|
|
90
103
|
|
|
91
104
|
const isHtml = !page.contentType || /html|xml/i.test(page.contentType);
|
|
92
105
|
const rawContent = request.format === "html" || !isHtml ? page.body : htmlToText(page.body);
|