@fedify/vocab-runtime 2.4.3 → 2.4.4
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/deno.json +1 -1
- package/dist/mod.cjs +2 -1
- package/dist/mod.js +2 -1
- package/dist/tests/{body-BxgQG9WC.mjs → body-CrrDpQuG.mjs} +1 -1
- package/dist/tests/{body-CvKsJb4Z.cjs → body-z_6Hg7ci.cjs} +1 -1
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/decimal.test.cjs +2 -2
- package/dist/tests/decimal.test.mjs +2 -2
- package/dist/tests/{docloader-BrNGu5hc.cjs → docloader-DUnO3Uvt.cjs} +3 -2
- package/dist/tests/{docloader-BVdiOb_c.mjs → docloader-Dh8AenhB.mjs} +3 -2
- package/dist/tests/docloader.test.cjs +48 -2
- package/dist/tests/docloader.test.mjs +48 -2
- package/dist/tests/{portable-media-CFeo3eUn.cjs → portable-media-DBl1-UE5.cjs} +2 -2
- package/dist/tests/{portable-media-B1iQbOVN.mjs → portable-media-DG7moKKk.mjs} +2 -2
- package/dist/tests/portable-media.test.cjs +1 -1
- package/dist/tests/portable-media.test.mjs +1 -1
- package/dist/tests/{request-oKt9AzpG.mjs → request-Cb7B8WAb.mjs} +1 -1
- package/dist/tests/{request-CpZhH4yT.cjs → request-nWurHb32.cjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/package.json +1 -1
- package/src/docloader.test.ts +84 -0
- package/src/docloader.ts +5 -0
package/deno.json
CHANGED
package/dist/mod.cjs
CHANGED
|
@@ -16,7 +16,7 @@ let _multiformats_base_x = require("@multiformats/base-x");
|
|
|
16
16
|
_multiformats_base_x = require_rolldown_runtime.__toESM(_multiformats_base_x, 1);
|
|
17
17
|
//#region deno.json
|
|
18
18
|
var name = "@fedify/vocab-runtime";
|
|
19
|
-
var version = "2.4.
|
|
19
|
+
var version = "2.4.4";
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/request.ts
|
|
22
22
|
/**
|
|
@@ -690,6 +690,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
|
|
|
690
690
|
options?.signal?.throwIfAborted();
|
|
691
691
|
const parsedUrl = new URL(url);
|
|
692
692
|
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new require_url.UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
|
|
693
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new require_url.UrlError("URLs with credentials are not allowed");
|
|
693
694
|
const currentUrl = parsedUrl.href;
|
|
694
695
|
if (!skipPreloadedContexts && currentUrl in require_contexts.preloadedContexts) {
|
|
695
696
|
logger.debug("Using preloaded context: {url}.", { url: currentUrl });
|
package/dist/mod.js
CHANGED
|
@@ -12,7 +12,7 @@ import { PublicKeyInfo } from "pkijs";
|
|
|
12
12
|
import baseX from "@multiformats/base-x";
|
|
13
13
|
//#region deno.json
|
|
14
14
|
var name = "@fedify/vocab-runtime";
|
|
15
|
-
var version = "2.4.
|
|
15
|
+
var version = "2.4.4";
|
|
16
16
|
//#endregion
|
|
17
17
|
//#region src/request.ts
|
|
18
18
|
/**
|
|
@@ -686,6 +686,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
|
|
|
686
686
|
options?.signal?.throwIfAborted();
|
|
687
687
|
const parsedUrl = new URL(url);
|
|
688
688
|
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
|
|
689
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new UrlError("URLs with credentials are not allowed");
|
|
689
690
|
const currentUrl = parsedUrl.href;
|
|
690
691
|
if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
|
|
691
692
|
logger.debug("Using preloaded context: {url}.", { url: currentUrl });
|
package/dist/tests/body.test.cjs
CHANGED
package/dist/tests/body.test.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as readBoundedText, t as BodyTooLargeError } from "./body-
|
|
1
|
+
import { i as readBoundedText, t as BodyTooLargeError } from "./body-CrrDpQuG.mjs";
|
|
2
2
|
import { deepStrictEqual, ok, rejects } from "node:assert/strict";
|
|
3
3
|
import { test } from "node:test";
|
|
4
4
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
require("./url-BNakuZ8k.cjs");
|
|
2
|
-
require("./docloader-
|
|
2
|
+
require("./docloader-DUnO3Uvt.cjs");
|
|
3
3
|
require("./key-C-AYkdJJ.cjs");
|
|
4
4
|
require("./multibase-B5Mea7Ip.cjs");
|
|
5
5
|
require("./digest-3FeH2Y-Q.cjs");
|
|
6
6
|
require("./langtag-Czjvxfzx.cjs");
|
|
7
7
|
require("./langstr-_PqsL1ae.cjs");
|
|
8
|
-
require("./portable-media-
|
|
8
|
+
require("./portable-media-DBl1-UE5.cjs");
|
|
9
9
|
let node_test = require("node:test");
|
|
10
10
|
let node_assert = require("node:assert");
|
|
11
11
|
//#region src/decimal.ts
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import "./url-DMxmp7ZG.mjs";
|
|
2
|
-
import "./docloader-
|
|
2
|
+
import "./docloader-Dh8AenhB.mjs";
|
|
3
3
|
import "./key-C2Db_TAJ.mjs";
|
|
4
4
|
import "./multibase-BPnF_L4e.mjs";
|
|
5
5
|
import "./digest-COC7xDiQ.mjs";
|
|
6
6
|
import "./langtag-CUppHs_s.mjs";
|
|
7
7
|
import "./langstr-DYRmJqFn.mjs";
|
|
8
|
-
import "./portable-media-
|
|
8
|
+
import "./portable-media-DG7moKKk.mjs";
|
|
9
9
|
import { test } from "node:test";
|
|
10
10
|
import { deepStrictEqual, throws } from "node:assert";
|
|
11
11
|
//#region src/decimal.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_request = require("./request-
|
|
2
|
-
const require_body = require("./body-
|
|
1
|
+
const require_request = require("./request-nWurHb32.cjs");
|
|
2
|
+
const require_body = require("./body-z_6Hg7ci.cjs");
|
|
3
3
|
const require_contexts = require("./contexts-C9Lj11SC.cjs");
|
|
4
4
|
const require_link = require("./link-DlKm8bEr.cjs");
|
|
5
5
|
const require_url = require("./url-BNakuZ8k.cjs");
|
|
@@ -293,6 +293,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
|
|
|
293
293
|
options?.signal?.throwIfAborted();
|
|
294
294
|
const parsedUrl = new URL(url);
|
|
295
295
|
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new require_url.UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
|
|
296
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new require_url.UrlError("URLs with credentials are not allowed");
|
|
296
297
|
const currentUrl = parsedUrl.href;
|
|
297
298
|
if (!skipPreloadedContexts && currentUrl in require_contexts.preloadedContexts) {
|
|
298
299
|
logger.debug("Using preloaded context: {url}.", { url: currentUrl });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-
|
|
2
|
-
import { i as readBoundedText, n as MAX_BODY_SIZE, r as readBoundedBytes, t as BodyTooLargeError } from "./body-
|
|
1
|
+
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-Cb7B8WAb.mjs";
|
|
2
|
+
import { i as readBoundedText, n as MAX_BODY_SIZE, r as readBoundedBytes, t as BodyTooLargeError } from "./body-CrrDpQuG.mjs";
|
|
3
3
|
import { t as preloadedContexts } from "./contexts-duRA_dDC.mjs";
|
|
4
4
|
import { t as HttpHeaderLink } from "./link-Cevmc87v.mjs";
|
|
5
5
|
import { n as UrlError, x as validatePublicUrl } from "./url-DMxmp7ZG.mjs";
|
|
@@ -293,6 +293,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
|
|
|
293
293
|
options?.signal?.throwIfAborted();
|
|
294
294
|
const parsedUrl = new URL(url);
|
|
295
295
|
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
|
|
296
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new UrlError("URLs with credentials are not allowed");
|
|
296
297
|
const currentUrl = parsedUrl.href;
|
|
297
298
|
if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
|
|
298
299
|
logger.debug("Using preloaded context: {url}.", { url: currentUrl });
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-emK7D4bc.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-nWurHb32.cjs");
|
|
3
3
|
const require_contexts = require("./contexts-C9Lj11SC.cjs");
|
|
4
4
|
const require_url = require("./url-BNakuZ8k.cjs");
|
|
5
|
-
const require_docloader = require("./docloader-
|
|
5
|
+
const require_docloader = require("./docloader-DUnO3Uvt.cjs");
|
|
6
6
|
const require_jsonld = require("./jsonld-CmDhHNTx.cjs");
|
|
7
7
|
let node_zlib = require("node:zlib");
|
|
8
8
|
let _logtape_logtape = require("@logtape/logtape");
|
|
@@ -2376,6 +2376,52 @@ const unsupportedUrls = [
|
|
|
2376
2376
|
"file:///collection",
|
|
2377
2377
|
`data:application/ld+json,${encodeURIComponent(JSON.stringify({ id: "https://example.com/data" }))}`
|
|
2378
2378
|
];
|
|
2379
|
+
function isCredentialUrlError(error) {
|
|
2380
|
+
return error instanceof require_url.UrlError && error.reason === "disallowed" && error.message === "URLs with credentials are not allowed";
|
|
2381
|
+
}
|
|
2382
|
+
(0, _fedify_fixture.test)("getDocumentLoader() rejects URLs with credentials", async (t) => {
|
|
2383
|
+
const start = "https://8.8.8.8/start";
|
|
2384
|
+
const target = "https://user:pass@8.8.8.8/document";
|
|
2385
|
+
for (const allowPrivateAddress of [false, true]) {
|
|
2386
|
+
const loader = require_docloader.getDocumentLoader({ allowPrivateAddress });
|
|
2387
|
+
for (const path of [
|
|
2388
|
+
"direct",
|
|
2389
|
+
"redirect",
|
|
2390
|
+
"Link",
|
|
2391
|
+
"HTML"
|
|
2392
|
+
]) await t.step(`${path}, allowPrivateAddress=${allowPrivateAddress}`, async () => {
|
|
2393
|
+
await withFetchStub((url) => {
|
|
2394
|
+
if (url !== start) return Response.json({});
|
|
2395
|
+
if (path === "redirect") return Response.redirect(target, 302);
|
|
2396
|
+
if (path === "HTML") return new Response(`<link rel="alternate" type="application/activity+json" href="${target}">`, { headers: { "Content-Type": "text/html" } });
|
|
2397
|
+
return new Response("", { headers: {
|
|
2398
|
+
"Content-Type": "text/plain",
|
|
2399
|
+
Link: `<${target}>; rel="alternate"; type="application/activity+json"`
|
|
2400
|
+
} });
|
|
2401
|
+
}, async (fetched) => {
|
|
2402
|
+
await (0, node_assert.rejects)(loader(path === "direct" ? target : start), isCredentialUrlError);
|
|
2403
|
+
(0, node_assert.deepStrictEqual)(fetched, path === "direct" ? [] : [start]);
|
|
2404
|
+
});
|
|
2405
|
+
});
|
|
2406
|
+
for (const url of [
|
|
2407
|
+
"https://user@8.8.8.8/document",
|
|
2408
|
+
"https://:pass@8.8.8.8/document",
|
|
2409
|
+
"https://%75ser:p%40ss@8.8.8.8/document",
|
|
2410
|
+
"http://user:pass@8.8.8.8/document"
|
|
2411
|
+
]) await t.step(`${url}, allowPrivateAddress=${allowPrivateAddress}`, async () => {
|
|
2412
|
+
await withFetchStub(() => Response.json({}), async (fetched) => {
|
|
2413
|
+
await (0, node_assert.rejects)(loader(url), isCredentialUrlError);
|
|
2414
|
+
(0, node_assert.deepStrictEqual)(fetched, []);
|
|
2415
|
+
});
|
|
2416
|
+
});
|
|
2417
|
+
for (const url of ["https://:@8.8.8.8/document", "https://8.8.8.8/@user?q=@pass"]) await t.step(`allows ${url}, allowPrivateAddress=${allowPrivateAddress}`, async () => {
|
|
2418
|
+
await withFetchStub(() => Response.json({ done: true }), async (fetched) => {
|
|
2419
|
+
(0, node_assert.deepStrictEqual)((await loader(url)).document, { done: true });
|
|
2420
|
+
(0, node_assert.deepStrictEqual)(fetched, [new URL(url).href]);
|
|
2421
|
+
});
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2379
2425
|
function isUnsupportedProtocolError(error, url) {
|
|
2380
2426
|
return error instanceof require_url.UrlError && error.message === `Unsupported protocol: ${new URL(url).protocol}` && error.reason === "disallowed";
|
|
2381
2427
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as FetchError } from "./request-
|
|
1
|
+
import { t as FetchError } from "./request-Cb7B8WAb.mjs";
|
|
2
2
|
import { n as cid_v1_default, t as preloadedContexts } from "./contexts-duRA_dDC.mjs";
|
|
3
3
|
import { n as UrlError } from "./url-DMxmp7ZG.mjs";
|
|
4
|
-
import { i as withDocumentLoaderTimeout, n as getRemoteDocument, r as resolveDocumentLoaderTimeout, t as getDocumentLoader } from "./docloader-
|
|
4
|
+
import { i as withDocumentLoaderTimeout, n as getRemoteDocument, r as resolveDocumentLoaderTimeout, t as getDocumentLoader } from "./docloader-Dh8AenhB.mjs";
|
|
5
5
|
import { t as jsonld_default } from "./jsonld-DweIJ07b.mjs";
|
|
6
6
|
import { gzipSync } from "node:zlib";
|
|
7
7
|
import { configure, reset } from "@logtape/logtape";
|
|
@@ -2394,6 +2394,52 @@ const unsupportedUrls = [
|
|
|
2394
2394
|
"file:///collection",
|
|
2395
2395
|
`data:application/ld+json,${encodeURIComponent(JSON.stringify({ id: "https://example.com/data" }))}`
|
|
2396
2396
|
];
|
|
2397
|
+
function isCredentialUrlError(error) {
|
|
2398
|
+
return error instanceof UrlError && error.reason === "disallowed" && error.message === "URLs with credentials are not allowed";
|
|
2399
|
+
}
|
|
2400
|
+
test("getDocumentLoader() rejects URLs with credentials", async (t) => {
|
|
2401
|
+
const start = "https://8.8.8.8/start";
|
|
2402
|
+
const target = "https://user:pass@8.8.8.8/document";
|
|
2403
|
+
for (const allowPrivateAddress of [false, true]) {
|
|
2404
|
+
const loader = getDocumentLoader({ allowPrivateAddress });
|
|
2405
|
+
for (const path of [
|
|
2406
|
+
"direct",
|
|
2407
|
+
"redirect",
|
|
2408
|
+
"Link",
|
|
2409
|
+
"HTML"
|
|
2410
|
+
]) await t.step(`${path}, allowPrivateAddress=${allowPrivateAddress}`, async () => {
|
|
2411
|
+
await withFetchStub((url) => {
|
|
2412
|
+
if (url !== start) return Response.json({});
|
|
2413
|
+
if (path === "redirect") return Response.redirect(target, 302);
|
|
2414
|
+
if (path === "HTML") return new Response(`<link rel="alternate" type="application/activity+json" href="${target}">`, { headers: { "Content-Type": "text/html" } });
|
|
2415
|
+
return new Response("", { headers: {
|
|
2416
|
+
"Content-Type": "text/plain",
|
|
2417
|
+
Link: `<${target}>; rel="alternate"; type="application/activity+json"`
|
|
2418
|
+
} });
|
|
2419
|
+
}, async (fetched) => {
|
|
2420
|
+
await rejects(loader(path === "direct" ? target : start), isCredentialUrlError);
|
|
2421
|
+
deepStrictEqual(fetched, path === "direct" ? [] : [start]);
|
|
2422
|
+
});
|
|
2423
|
+
});
|
|
2424
|
+
for (const url of [
|
|
2425
|
+
"https://user@8.8.8.8/document",
|
|
2426
|
+
"https://:pass@8.8.8.8/document",
|
|
2427
|
+
"https://%75ser:p%40ss@8.8.8.8/document",
|
|
2428
|
+
"http://user:pass@8.8.8.8/document"
|
|
2429
|
+
]) await t.step(`${url}, allowPrivateAddress=${allowPrivateAddress}`, async () => {
|
|
2430
|
+
await withFetchStub(() => Response.json({}), async (fetched) => {
|
|
2431
|
+
await rejects(loader(url), isCredentialUrlError);
|
|
2432
|
+
deepStrictEqual(fetched, []);
|
|
2433
|
+
});
|
|
2434
|
+
});
|
|
2435
|
+
for (const url of ["https://:@8.8.8.8/document", "https://8.8.8.8/@user?q=@pass"]) await t.step(`allows ${url}, allowPrivateAddress=${allowPrivateAddress}`, async () => {
|
|
2436
|
+
await withFetchStub(() => Response.json({ done: true }), async (fetched) => {
|
|
2437
|
+
deepStrictEqual((await loader(url)).document, { done: true });
|
|
2438
|
+
deepStrictEqual(fetched, [new URL(url).href]);
|
|
2439
|
+
});
|
|
2440
|
+
});
|
|
2441
|
+
}
|
|
2442
|
+
});
|
|
2397
2443
|
function isUnsupportedProtocolError(error, url) {
|
|
2398
2444
|
return error instanceof UrlError && error.message === `Unsupported protocol: ${new URL(url).protocol}` && error.reason === "disallowed";
|
|
2399
2445
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_request = require("./request-
|
|
2
|
-
const require_body = require("./body-
|
|
1
|
+
const require_request = require("./request-nWurHb32.cjs");
|
|
2
|
+
const require_body = require("./body-z_6Hg7ci.cjs");
|
|
3
3
|
const require_url = require("./url-BNakuZ8k.cjs");
|
|
4
4
|
const require_digest = require("./digest-3FeH2Y-Q.cjs");
|
|
5
5
|
//#region src/portable-media.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as getUserAgent } from "./request-
|
|
2
|
-
import { r as readBoundedBytes } from "./body-
|
|
1
|
+
import { r as getUserAgent } from "./request-Cb7B8WAb.mjs";
|
|
2
|
+
import { r as readBoundedBytes } from "./body-CrrDpQuG.mjs";
|
|
3
3
|
import { g as parseGatewayUrl, x as validatePublicUrl } from "./url-DMxmp7ZG.mjs";
|
|
4
4
|
import { a as verifyDigestMultibase, i as parseHashlink, r as parseDigestMultibase } from "./digest-COC7xDiQ.mjs";
|
|
5
5
|
//#region src/portable-media.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_digest = require("./digest-3FeH2Y-Q.cjs");
|
|
2
|
-
const require_portable_media = require("./portable-media-
|
|
2
|
+
const require_portable_media = require("./portable-media-DBl1-UE5.cjs");
|
|
3
3
|
let node_assert_strict = require("node:assert/strict");
|
|
4
4
|
let node_test = require("node:test");
|
|
5
5
|
let node_http = require("node:http");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as createHashlink, t as computeDigestMultibase } from "./digest-COC7xDiQ.mjs";
|
|
2
|
-
import { t as fetchPortableMedia } from "./portable-media-
|
|
2
|
+
import { t as fetchPortableMedia } from "./portable-media-DG7moKKk.mjs";
|
|
3
3
|
import { deepStrictEqual, equal, rejects } from "node:assert/strict";
|
|
4
4
|
import { test } from "node:test";
|
|
5
5
|
import { createServer } from "node:http";
|
|
@@ -3,7 +3,7 @@ let node_process = require("node:process");
|
|
|
3
3
|
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
4
4
|
//#region deno.json
|
|
5
5
|
var name = "@fedify/vocab-runtime";
|
|
6
|
-
var version = "2.4.
|
|
6
|
+
var version = "2.4.4";
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region src/request.ts
|
|
9
9
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-emK7D4bc.cjs");
|
|
2
|
-
const require_request = require("./request-
|
|
2
|
+
const require_request = require("./request-nWurHb32.cjs");
|
|
3
3
|
let node_test = require("node:test");
|
|
4
4
|
let node_process = require("node:process");
|
|
5
5
|
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createActivityPubRequest, o as version, r as getUserAgent } from "./request-
|
|
1
|
+
import { n as createActivityPubRequest, o as version, r as getUserAgent } from "./request-Cb7B8WAb.mjs";
|
|
2
2
|
import { test } from "node:test";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { deepStrictEqual } from "node:assert";
|
package/package.json
CHANGED
package/src/docloader.test.ts
CHANGED
|
@@ -1621,6 +1621,90 @@ const unsupportedUrls = [
|
|
|
1621
1621
|
}`,
|
|
1622
1622
|
];
|
|
1623
1623
|
|
|
1624
|
+
function isCredentialUrlError(error: unknown): boolean {
|
|
1625
|
+
return error instanceof UrlError && error.reason === "disallowed" &&
|
|
1626
|
+
error.message === "URLs with credentials are not allowed";
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
test("getDocumentLoader() rejects URLs with credentials", async (t) => {
|
|
1630
|
+
const start = "https://8.8.8.8/start";
|
|
1631
|
+
const target = "https://user:pass@8.8.8.8/document";
|
|
1632
|
+
for (const allowPrivateAddress of [false, true]) {
|
|
1633
|
+
const loader = getDocumentLoader({ allowPrivateAddress });
|
|
1634
|
+
for (const path of ["direct", "redirect", "Link", "HTML"]) {
|
|
1635
|
+
await t.step(
|
|
1636
|
+
`${path}, allowPrivateAddress=${allowPrivateAddress}`,
|
|
1637
|
+
async () => {
|
|
1638
|
+
await withFetchStub(
|
|
1639
|
+
(url) => {
|
|
1640
|
+
if (url !== start) return Response.json({});
|
|
1641
|
+
if (path === "redirect") return Response.redirect(target, 302);
|
|
1642
|
+
if (path === "HTML") {
|
|
1643
|
+
return new Response(
|
|
1644
|
+
`<link rel="alternate" type="application/activity+json" href="${target}">`,
|
|
1645
|
+
{ headers: { "Content-Type": "text/html" } },
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
return new Response("", {
|
|
1649
|
+
headers: {
|
|
1650
|
+
"Content-Type": "text/plain",
|
|
1651
|
+
Link:
|
|
1652
|
+
`<${target}>; rel="alternate"; type="application/activity+json"`,
|
|
1653
|
+
},
|
|
1654
|
+
});
|
|
1655
|
+
},
|
|
1656
|
+
async (fetched) => {
|
|
1657
|
+
await rejects(
|
|
1658
|
+
loader(path === "direct" ? target : start),
|
|
1659
|
+
isCredentialUrlError,
|
|
1660
|
+
);
|
|
1661
|
+
deepStrictEqual(fetched, path === "direct" ? [] : [start]);
|
|
1662
|
+
},
|
|
1663
|
+
);
|
|
1664
|
+
},
|
|
1665
|
+
);
|
|
1666
|
+
}
|
|
1667
|
+
for (
|
|
1668
|
+
const url of [
|
|
1669
|
+
"https://user@8.8.8.8/document",
|
|
1670
|
+
"https://:pass@8.8.8.8/document",
|
|
1671
|
+
"https://%75ser:p%40ss@8.8.8.8/document",
|
|
1672
|
+
"http://user:pass@8.8.8.8/document",
|
|
1673
|
+
]
|
|
1674
|
+
) {
|
|
1675
|
+
await t.step(
|
|
1676
|
+
`${url}, allowPrivateAddress=${allowPrivateAddress}`,
|
|
1677
|
+
async () => {
|
|
1678
|
+
await withFetchStub(() => Response.json({}), async (fetched) => {
|
|
1679
|
+
await rejects(loader(url), isCredentialUrlError);
|
|
1680
|
+
deepStrictEqual(fetched, []);
|
|
1681
|
+
});
|
|
1682
|
+
},
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1685
|
+
for (
|
|
1686
|
+
const url of [
|
|
1687
|
+
"https://:@8.8.8.8/document",
|
|
1688
|
+
"https://8.8.8.8/@user?q=@pass",
|
|
1689
|
+
]
|
|
1690
|
+
) {
|
|
1691
|
+
await t.step(
|
|
1692
|
+
`allows ${url}, allowPrivateAddress=${allowPrivateAddress}`,
|
|
1693
|
+
async () => {
|
|
1694
|
+
await withFetchStub(
|
|
1695
|
+
() => Response.json({ done: true }),
|
|
1696
|
+
async (fetched) => {
|
|
1697
|
+
const document = await loader(url);
|
|
1698
|
+
deepStrictEqual(document.document, { done: true });
|
|
1699
|
+
deepStrictEqual(fetched, [new URL(url).href]);
|
|
1700
|
+
},
|
|
1701
|
+
);
|
|
1702
|
+
},
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1624
1708
|
function isUnsupportedProtocolError(error: unknown, url: string): boolean {
|
|
1625
1709
|
return error instanceof UrlError &&
|
|
1626
1710
|
error.message === `Unsupported protocol: ${new URL(url).protocol}` &&
|
package/src/docloader.ts
CHANGED
|
@@ -561,6 +561,11 @@ export function getDocumentLoader(
|
|
|
561
561
|
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
562
562
|
throw new UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
|
|
563
563
|
}
|
|
564
|
+
// When merging forward, keep this guard independent of allowPrivateAddress:
|
|
565
|
+
// private-address opt-in must not permit credentials on any followed URL.
|
|
566
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") {
|
|
567
|
+
throw new UrlError("URLs with credentials are not allowed");
|
|
568
|
+
}
|
|
564
569
|
const currentUrl = parsedUrl.href;
|
|
565
570
|
if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
|
|
566
571
|
logger.debug("Using preloaded context: {url}.", { url: currentUrl });
|