@fedify/vocab-runtime 2.0.33-dev.2393 → 2.0.33-dev.2413

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab-runtime",
3
- "version": "2.0.33-dev.2393+8f913f03",
3
+ "version": "2.0.33-dev.2413+bacabf0a",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts",
package/dist/mod.cjs CHANGED
@@ -4504,7 +4504,7 @@ const preloadedContexts = {
4504
4504
  //#endregion
4505
4505
  //#region deno.json
4506
4506
  var name = "@fedify/vocab-runtime";
4507
- var version = "2.0.33-dev.2393+8f913f03";
4507
+ var version = "2.0.33-dev.2413+bacabf0a";
4508
4508
  //#endregion
4509
4509
  //#region src/request.ts
4510
4510
  /**
@@ -5158,7 +5158,9 @@ function getDocumentLoader({ allowPrivateAddress, skipPreloadedContexts, userAge
5158
5158
  const tracer = _opentelemetry_api.trace.getTracerProvider().getTracer(name, version);
5159
5159
  async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
5160
5160
  options?.signal?.throwIfAborted();
5161
- const currentUrl = new URL(url).href;
5161
+ const parsedUrl = new URL(url);
5162
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
5163
+ const currentUrl = parsedUrl.href;
5162
5164
  if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
5163
5165
  logger.debug("Using preloaded context: {url}.", { url: currentUrl });
5164
5166
  return {
package/dist/mod.js CHANGED
@@ -4499,7 +4499,7 @@ const preloadedContexts = {
4499
4499
  //#endregion
4500
4500
  //#region deno.json
4501
4501
  var name = "@fedify/vocab-runtime";
4502
- var version = "2.0.33-dev.2393+8f913f03";
4502
+ var version = "2.0.33-dev.2413+bacabf0a";
4503
4503
  //#endregion
4504
4504
  //#region src/request.ts
4505
4505
  /**
@@ -5153,7 +5153,9 @@ function getDocumentLoader({ allowPrivateAddress, skipPreloadedContexts, userAge
5153
5153
  const tracer = trace.getTracerProvider().getTracer(name, version);
5154
5154
  async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
5155
5155
  options?.signal?.throwIfAborted();
5156
- const currentUrl = new URL(url).href;
5156
+ const parsedUrl = new URL(url);
5157
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
5158
+ const currentUrl = parsedUrl.href;
5157
5159
  if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
5158
5160
  logger.debug("Using preloaded context: {url}.", { url: currentUrl });
5159
5161
  return {
@@ -1,4 +1,4 @@
1
- import { t as FetchError } from "./request-LCQ3ylSC.mjs";
1
+ import { t as FetchError } from "./request-CoIAn9E5.mjs";
2
2
  import { getLogger } from "@logtape/logtape";
3
3
  //#region src/body.ts
4
4
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -1,5 +1,5 @@
1
1
  require("./chunk-Do9eywBl.cjs");
2
- const require_request = require("./request-BdR4y-S5.cjs");
2
+ const require_request = require("./request-DeICVa2R.cjs");
3
3
  let _logtape_logtape = require("@logtape/logtape");
4
4
  //#region src/body.ts
5
5
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -1,5 +1,5 @@
1
1
  require("./chunk-Do9eywBl.cjs");
2
- const require_body = require("./body-C9ze62X7.cjs");
2
+ const require_body = require("./body-XjuAufFE.cjs");
3
3
  let node_assert_strict = require("node:assert/strict");
4
4
  let node_test = require("node:test");
5
5
  let node_zlib = require("node:zlib");
@@ -1,4 +1,4 @@
1
- import { r as readBoundedText, t as BodyTooLargeError } from "./body-ChY9NU2c.mjs";
1
+ import { r as readBoundedText, t as BodyTooLargeError } from "./body-D2UrzLDS.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,6 +1,6 @@
1
1
  const require_chunk = require("./chunk-Do9eywBl.cjs");
2
- const require_request = require("./request-BdR4y-S5.cjs");
3
- const require_body = require("./body-C9ze62X7.cjs");
2
+ const require_request = require("./request-DeICVa2R.cjs");
3
+ const require_body = require("./body-XjuAufFE.cjs");
4
4
  const require_link = require("./link-FguCydMA.cjs");
5
5
  const require_url = require("./url-Qk1svKZU.cjs");
6
6
  let node_zlib = require("node:zlib");
@@ -5824,7 +5824,9 @@ function getDocumentLoader({ allowPrivateAddress, skipPreloadedContexts, userAge
5824
5824
  const tracer = _opentelemetry_api.trace.getTracerProvider().getTracer(require_request.name, require_request.version);
5825
5825
  async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
5826
5826
  options?.signal?.throwIfAborted();
5827
- const currentUrl = new URL(url).href;
5827
+ const parsedUrl = new URL(url);
5828
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new require_url.UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
5829
+ const currentUrl = parsedUrl.href;
5828
5830
  if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
5829
5831
  logger.debug("Using preloaded context: {url}.", { url: currentUrl });
5830
5832
  return {
@@ -6689,4 +6691,52 @@ var jsonld_default = jsonld_dist_jsonld_esm_js.default;
6689
6691
  esm_default.hardReset();
6690
6692
  }
6691
6693
  });
6694
+ const unsupportedUrls = [
6695
+ "ftp://example.com/collection",
6696
+ "file:///collection",
6697
+ `data:application/ld+json,${encodeURIComponent(JSON.stringify({ id: "https://example.com/data" }))}`
6698
+ ];
6699
+ function isUnsupportedProtocolError(error, url) {
6700
+ return error instanceof require_url.UrlError && error.message === `Unsupported protocol: ${new URL(url).protocol}` && error.reason === "disallowed";
6701
+ }
6702
+ async function withFetchStub(respond, callback) {
6703
+ const fetched = [];
6704
+ const originalFetch = globalThis.fetch;
6705
+ globalThis.fetch = (input) => {
6706
+ const url = input instanceof Request ? input.url : String(input);
6707
+ fetched.push(url);
6708
+ return Promise.resolve(respond(url));
6709
+ };
6710
+ try {
6711
+ await callback(fetched);
6712
+ } finally {
6713
+ globalThis.fetch = originalFetch;
6714
+ }
6715
+ }
6716
+ for (const url of unsupportedUrls) {
6717
+ const scheme = new URL(url).protocol;
6718
+ (0, _fedify_fixture.test)(`getDocumentLoader() rejects ${scheme} URLs with private addresses allowed before fetching`, async () => {
6719
+ await withFetchStub(() => Response.json({}), async (fetched) => {
6720
+ await (0, node_assert.rejects)(getDocumentLoader({ allowPrivateAddress: true })(url), (e) => isUnsupportedProtocolError(e, url));
6721
+ (0, node_assert.deepStrictEqual)(fetched, []);
6722
+ });
6723
+ });
6724
+ (0, _fedify_fixture.test)(`getDocumentLoader() rejects redirects to ${scheme} URLs with private addresses allowed`, async () => {
6725
+ const start = "https://example.com/redirect-to-unsupported";
6726
+ await withFetchStub(() => Response.redirect(url, 302), async (fetched) => {
6727
+ await (0, node_assert.rejects)(getDocumentLoader({ allowPrivateAddress: true })(start), (e) => isUnsupportedProtocolError(e, url));
6728
+ (0, node_assert.deepStrictEqual)(fetched, [start]);
6729
+ });
6730
+ });
6731
+ for (const html of [false, true]) (0, _fedify_fixture.test)(`getDocumentLoader() rejects ${html ? "HTML" : "Link header"} alternates to ${scheme} URLs with private addresses allowed`, async () => {
6732
+ const start = "https://example.com/alternate-to-unsupported";
6733
+ await withFetchStub(() => html ? new Response(`<link rel="alternate" type="application/activity+json" href="${url}">`, { headers: { "Content-Type": "text/html" } }) : new Response("", { headers: {
6734
+ "Content-Type": "text/plain",
6735
+ Link: `<${url}>; rel="alternate"; type="application/activity+json"`
6736
+ } }), async (fetched) => {
6737
+ await (0, node_assert.rejects)(getDocumentLoader({ allowPrivateAddress: true })(start), (e) => isUnsupportedProtocolError(e, url));
6738
+ (0, node_assert.deepStrictEqual)(fetched, [start]);
6739
+ });
6740
+ });
6741
+ }
6692
6742
  //#endregion
@@ -1,5 +1,5 @@
1
- import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-LCQ3ylSC.mjs";
2
- import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-ChY9NU2c.mjs";
1
+ import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-CoIAn9E5.mjs";
2
+ import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-D2UrzLDS.mjs";
3
3
  import { t as HttpHeaderLink } from "./link-NUUWCdnK.mjs";
4
4
  import { o as validatePublicUrl, t as UrlError } from "./url-DzlMS01z.mjs";
5
5
  import { gzipSync } from "node:zlib";
@@ -5841,7 +5841,9 @@ function getDocumentLoader({ allowPrivateAddress, skipPreloadedContexts, userAge
5841
5841
  const tracer = trace.getTracerProvider().getTracer(name, version);
5842
5842
  async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
5843
5843
  options?.signal?.throwIfAborted();
5844
- const currentUrl = new URL(url).href;
5844
+ const parsedUrl = new URL(url);
5845
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
5846
+ const currentUrl = parsedUrl.href;
5845
5847
  if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
5846
5848
  logger.debug("Using preloaded context: {url}.", { url: currentUrl });
5847
5849
  return {
@@ -6706,5 +6708,53 @@ test("getDocumentLoader() rejects cancellation before fetching", async () => {
6706
6708
  esm_default.hardReset();
6707
6709
  }
6708
6710
  });
6711
+ const unsupportedUrls = [
6712
+ "ftp://example.com/collection",
6713
+ "file:///collection",
6714
+ `data:application/ld+json,${encodeURIComponent(JSON.stringify({ id: "https://example.com/data" }))}`
6715
+ ];
6716
+ function isUnsupportedProtocolError(error, url) {
6717
+ return error instanceof UrlError && error.message === `Unsupported protocol: ${new URL(url).protocol}` && error.reason === "disallowed";
6718
+ }
6719
+ async function withFetchStub(respond, callback) {
6720
+ const fetched = [];
6721
+ const originalFetch = globalThis.fetch;
6722
+ globalThis.fetch = (input) => {
6723
+ const url = input instanceof Request ? input.url : String(input);
6724
+ fetched.push(url);
6725
+ return Promise.resolve(respond(url));
6726
+ };
6727
+ try {
6728
+ await callback(fetched);
6729
+ } finally {
6730
+ globalThis.fetch = originalFetch;
6731
+ }
6732
+ }
6733
+ for (const url of unsupportedUrls) {
6734
+ const scheme = new URL(url).protocol;
6735
+ test(`getDocumentLoader() rejects ${scheme} URLs with private addresses allowed before fetching`, async () => {
6736
+ await withFetchStub(() => Response.json({}), async (fetched) => {
6737
+ await rejects(getDocumentLoader({ allowPrivateAddress: true })(url), (e) => isUnsupportedProtocolError(e, url));
6738
+ deepStrictEqual(fetched, []);
6739
+ });
6740
+ });
6741
+ test(`getDocumentLoader() rejects redirects to ${scheme} URLs with private addresses allowed`, async () => {
6742
+ const start = "https://example.com/redirect-to-unsupported";
6743
+ await withFetchStub(() => Response.redirect(url, 302), async (fetched) => {
6744
+ await rejects(getDocumentLoader({ allowPrivateAddress: true })(start), (e) => isUnsupportedProtocolError(e, url));
6745
+ deepStrictEqual(fetched, [start]);
6746
+ });
6747
+ });
6748
+ for (const html of [false, true]) test(`getDocumentLoader() rejects ${html ? "HTML" : "Link header"} alternates to ${scheme} URLs with private addresses allowed`, async () => {
6749
+ const start = "https://example.com/alternate-to-unsupported";
6750
+ await withFetchStub(() => html ? new Response(`<link rel="alternate" type="application/activity+json" href="${url}">`, { headers: { "Content-Type": "text/html" } }) : new Response("", { headers: {
6751
+ "Content-Type": "text/plain",
6752
+ Link: `<${url}>; rel="alternate"; type="application/activity+json"`
6753
+ } }), async (fetched) => {
6754
+ await rejects(getDocumentLoader({ allowPrivateAddress: true })(start), (e) => isUnsupportedProtocolError(e, url));
6755
+ deepStrictEqual(fetched, [start]);
6756
+ });
6757
+ });
6758
+ }
6709
6759
  //#endregion
6710
6760
  export {};
@@ -1,7 +1,7 @@
1
1
  import process from "node:process";
2
2
  //#region deno.json
3
3
  var name = "@fedify/vocab-runtime";
4
- var version = "2.0.33-dev.2393+8f913f03";
4
+ var version = "2.0.33-dev.2413+bacabf0a";
5
5
  //#endregion
6
6
  //#region src/request.ts
7
7
  /**
@@ -3,7 +3,7 @@ let node_process = require("node:process");
3
3
  node_process = require_chunk.__toESM(node_process);
4
4
  //#region deno.json
5
5
  var name = "@fedify/vocab-runtime";
6
- var version = "2.0.33-dev.2393+8f913f03";
6
+ var version = "2.0.33-dev.2413+bacabf0a";
7
7
  //#endregion
8
8
  //#region src/request.ts
9
9
  /**
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Do9eywBl.cjs");
2
- const require_request = require("./request-BdR4y-S5.cjs");
2
+ const require_request = require("./request-DeICVa2R.cjs");
3
3
  let node_test = require("node:test");
4
4
  let node_process = require("node:process");
5
5
  node_process = require_chunk.__toESM(node_process);
@@ -1,4 +1,4 @@
1
- import { o as version, r as getUserAgent } from "./request-LCQ3ylSC.mjs";
1
+ import { o as version, r as getUserAgent } from "./request-CoIAn9E5.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/vocab-runtime",
3
- "version": "2.0.33-dev.2393+8f913f03",
3
+ "version": "2.0.33-dev.2413+bacabf0a",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1084,3 +1084,93 @@ test("getDocumentLoader() rejects cancellation before fetching", async () => {
1084
1084
  fetchMock.hardReset();
1085
1085
  }
1086
1086
  });
1087
+
1088
+ const unsupportedUrls = [
1089
+ "ftp://example.com/collection",
1090
+ "file:///collection",
1091
+ `data:application/ld+json,${
1092
+ encodeURIComponent(JSON.stringify({ id: "https://example.com/data" }))
1093
+ }`,
1094
+ ];
1095
+
1096
+ function isUnsupportedProtocolError(error: unknown, url: string): boolean {
1097
+ return error instanceof UrlError &&
1098
+ error.message === `Unsupported protocol: ${new URL(url).protocol}` &&
1099
+ error.reason === "disallowed";
1100
+ }
1101
+
1102
+ async function withFetchStub(
1103
+ respond: (url: string) => Response,
1104
+ callback: (fetched: string[]) => Promise<void>,
1105
+ ): Promise<void> {
1106
+ const fetched: string[] = [];
1107
+ const originalFetch = globalThis.fetch;
1108
+ globalThis.fetch = (input: RequestInfo | URL) => {
1109
+ const url = input instanceof Request ? input.url : String(input);
1110
+ fetched.push(url);
1111
+ return Promise.resolve(respond(url));
1112
+ };
1113
+ try {
1114
+ await callback(fetched);
1115
+ } finally {
1116
+ globalThis.fetch = originalFetch;
1117
+ }
1118
+ }
1119
+
1120
+ for (const url of unsupportedUrls) {
1121
+ const scheme = new URL(url).protocol;
1122
+
1123
+ test(`getDocumentLoader() rejects ${scheme} URLs with private addresses allowed before fetching`, async () => {
1124
+ await withFetchStub(() => Response.json({}), async (fetched) => {
1125
+ await rejects(
1126
+ getDocumentLoader({ allowPrivateAddress: true })(url),
1127
+ (e) => isUnsupportedProtocolError(e, url),
1128
+ );
1129
+ deepStrictEqual(fetched, []);
1130
+ });
1131
+ });
1132
+
1133
+ test(`getDocumentLoader() rejects redirects to ${scheme} URLs with private addresses allowed`, async () => {
1134
+ const start = "https://example.com/redirect-to-unsupported";
1135
+ await withFetchStub(() => Response.redirect(url, 302), async (fetched) => {
1136
+ await rejects(
1137
+ getDocumentLoader({ allowPrivateAddress: true })(start),
1138
+ (e) => isUnsupportedProtocolError(e, url),
1139
+ );
1140
+ deepStrictEqual(fetched, [start]);
1141
+ });
1142
+ });
1143
+
1144
+ for (const html of [false, true]) {
1145
+ test(
1146
+ `getDocumentLoader() rejects ${
1147
+ html ? "HTML" : "Link header"
1148
+ } alternates to ${scheme} URLs with private addresses allowed`,
1149
+ async () => {
1150
+ const start = "https://example.com/alternate-to-unsupported";
1151
+ await withFetchStub(
1152
+ () =>
1153
+ html
1154
+ ? new Response(
1155
+ `<link rel="alternate" type="application/activity+json" href="${url}">`,
1156
+ { headers: { "Content-Type": "text/html" } },
1157
+ )
1158
+ : new Response("", {
1159
+ headers: {
1160
+ "Content-Type": "text/plain",
1161
+ Link:
1162
+ `<${url}>; rel="alternate"; type="application/activity+json"`,
1163
+ },
1164
+ }),
1165
+ async (fetched) => {
1166
+ await rejects(
1167
+ getDocumentLoader({ allowPrivateAddress: true })(start),
1168
+ (e) => isUnsupportedProtocolError(e, url),
1169
+ );
1170
+ deepStrictEqual(fetched, [start]);
1171
+ },
1172
+ );
1173
+ },
1174
+ );
1175
+ }
1176
+ }
package/src/docloader.ts CHANGED
@@ -302,7 +302,11 @@ export function getDocumentLoader(
302
302
  visited = new Set<string>(),
303
303
  ): Promise<RemoteDocument> {
304
304
  options?.signal?.throwIfAborted();
305
- const currentUrl = new URL(url).href;
305
+ const parsedUrl = new URL(url);
306
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
307
+ throw new UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
308
+ }
309
+ const currentUrl = parsedUrl.href;
306
310
  if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
307
311
  logger.debug("Using preloaded context: {url}.", { url: currentUrl });
308
312
  return {