@fedify/vocab-runtime 2.2.18 → 2.2.19

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.2.18",
3
+ "version": "2.2.19",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts",
package/dist/mod.cjs CHANGED
@@ -4593,7 +4593,7 @@ const preloadedContexts = {
4593
4593
  //#endregion
4594
4594
  //#region deno.json
4595
4595
  var name = "@fedify/vocab-runtime";
4596
- var version = "2.2.18";
4596
+ var version = "2.2.19";
4597
4597
  //#endregion
4598
4598
  //#region src/request.ts
4599
4599
  /**
@@ -5256,6 +5256,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
5256
5256
  options?.signal?.throwIfAborted();
5257
5257
  const parsedUrl = new URL(url);
5258
5258
  if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
5259
+ if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new UrlError("URLs with credentials are not allowed");
5259
5260
  const currentUrl = parsedUrl.href;
5260
5261
  if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
5261
5262
  logger.debug("Using preloaded context: {url}.", { url: currentUrl });
package/dist/mod.js CHANGED
@@ -4588,7 +4588,7 @@ const preloadedContexts = {
4588
4588
  //#endregion
4589
4589
  //#region deno.json
4590
4590
  var name = "@fedify/vocab-runtime";
4591
- var version = "2.2.18";
4591
+ var version = "2.2.19";
4592
4592
  //#endregion
4593
4593
  //#region src/request.ts
4594
4594
  /**
@@ -5251,6 +5251,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
5251
5251
  options?.signal?.throwIfAborted();
5252
5252
  const parsedUrl = new URL(url);
5253
5253
  if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
5254
+ if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new UrlError("URLs with credentials are not allowed");
5254
5255
  const currentUrl = parsedUrl.href;
5255
5256
  if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
5256
5257
  logger.debug("Using preloaded context: {url}.", { url: currentUrl });
@@ -1,4 +1,4 @@
1
- import { t as FetchError } from "./request-BE7XIrGw.mjs";
1
+ import { t as FetchError } from "./request-yCis9VCB.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-BngWcP0t.cjs");
2
+ const require_request = require("./request-DGtx5MkG.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-CaJBg0tr.cjs");
2
+ const require_body = require("./body-DhbRZ6Ps.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-CbvYxP3V.mjs";
1
+ import { r as readBoundedText, t as BodyTooLargeError } from "./body-BOnyCa1y.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,5 +1,5 @@
1
1
  require("./chunk-Do9eywBl.cjs");
2
- require("./docloader-k2T5kUlV.cjs");
2
+ require("./docloader-BhSiTajy.cjs");
3
3
  require("./url-vZTgkIZg.cjs");
4
4
  require("./key-BarKGLQz.cjs");
5
5
  require("./multibase-8zhYPa7o.cjs");
@@ -1,4 +1,4 @@
1
- import "./docloader-jQMjHIRI.mjs";
1
+ import "./docloader-BjyFsnAA.mjs";
2
2
  import "./url-3fyJuqC0.mjs";
3
3
  import "./key-BDIsC6hV.mjs";
4
4
  import "./multibase-Ds75BvXs.mjs";
@@ -1,6 +1,6 @@
1
1
  require("./chunk-Do9eywBl.cjs");
2
- const require_request = require("./request-BngWcP0t.cjs");
3
- const require_body = require("./body-CaJBg0tr.cjs");
2
+ const require_request = require("./request-DGtx5MkG.cjs");
3
+ const require_body = require("./body-DhbRZ6Ps.cjs");
4
4
  const require_link = require("./link-qDTmjPw_.cjs");
5
5
  const require_url = require("./url-vZTgkIZg.cjs");
6
6
  let _logtape_logtape = require("@logtape/logtape");
@@ -4707,6 +4707,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
4707
4707
  options?.signal?.throwIfAborted();
4708
4708
  const parsedUrl = new URL(url);
4709
4709
  if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new require_url.UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
4710
+ if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new require_url.UrlError("URLs with credentials are not allowed");
4710
4711
  const currentUrl = parsedUrl.href;
4711
4712
  if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
4712
4713
  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-BE7XIrGw.mjs";
2
- import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-CbvYxP3V.mjs";
1
+ import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-yCis9VCB.mjs";
2
+ import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-BOnyCa1y.mjs";
3
3
  import { t as HttpHeaderLink } from "./link-CYszIegt.mjs";
4
4
  import { o as validatePublicUrl, t as UrlError } from "./url-3fyJuqC0.mjs";
5
5
  import { getLogger } from "@logtape/logtape";
@@ -4706,6 +4706,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
4706
4706
  options?.signal?.throwIfAborted();
4707
4707
  const parsedUrl = new URL(url);
4708
4708
  if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
4709
+ if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new UrlError("URLs with credentials are not allowed");
4709
4710
  const currentUrl = parsedUrl.href;
4710
4711
  if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
4711
4712
  logger.debug("Using preloaded context: {url}.", { url: currentUrl });
@@ -1,6 +1,6 @@
1
1
  const require_chunk = require("./chunk-Do9eywBl.cjs");
2
- const require_request = require("./request-BngWcP0t.cjs");
3
- const require_docloader = require("./docloader-k2T5kUlV.cjs");
2
+ const require_request = require("./request-DGtx5MkG.cjs");
3
+ const require_docloader = require("./docloader-BhSiTajy.cjs");
4
4
  const require_url = require("./url-vZTgkIZg.cjs");
5
5
  let node_zlib = require("node:zlib");
6
6
  let _logtape_logtape = require("@logtape/logtape");
@@ -2014,6 +2014,52 @@ const unsupportedUrls = [
2014
2014
  "file:///collection",
2015
2015
  `data:application/ld+json,${encodeURIComponent(JSON.stringify({ id: "https://example.com/data" }))}`
2016
2016
  ];
2017
+ function isCredentialUrlError(error) {
2018
+ return error instanceof require_url.UrlError && error.reason === "disallowed" && error.message === "URLs with credentials are not allowed";
2019
+ }
2020
+ (0, _fedify_fixture.test)("getDocumentLoader() rejects URLs with credentials", async (t) => {
2021
+ const start = "https://8.8.8.8/start";
2022
+ const target = "https://user:pass@8.8.8.8/document";
2023
+ for (const allowPrivateAddress of [false, true]) {
2024
+ const loader = require_docloader.getDocumentLoader({ allowPrivateAddress });
2025
+ for (const path of [
2026
+ "direct",
2027
+ "redirect",
2028
+ "Link",
2029
+ "HTML"
2030
+ ]) await t.step(`${path}, allowPrivateAddress=${allowPrivateAddress}`, async () => {
2031
+ await withFetchStub((url) => {
2032
+ if (url !== start) return Response.json({});
2033
+ if (path === "redirect") return Response.redirect(target, 302);
2034
+ if (path === "HTML") return new Response(`<link rel="alternate" type="application/activity+json" href="${target}">`, { headers: { "Content-Type": "text/html" } });
2035
+ return new Response("", { headers: {
2036
+ "Content-Type": "text/plain",
2037
+ Link: `<${target}>; rel="alternate"; type="application/activity+json"`
2038
+ } });
2039
+ }, async (fetched) => {
2040
+ await (0, node_assert.rejects)(loader(path === "direct" ? target : start), isCredentialUrlError);
2041
+ (0, node_assert.deepStrictEqual)(fetched, path === "direct" ? [] : [start]);
2042
+ });
2043
+ });
2044
+ for (const url of [
2045
+ "https://user@8.8.8.8/document",
2046
+ "https://:pass@8.8.8.8/document",
2047
+ "https://%75ser:p%40ss@8.8.8.8/document",
2048
+ "http://user:pass@8.8.8.8/document"
2049
+ ]) await t.step(`${url}, allowPrivateAddress=${allowPrivateAddress}`, async () => {
2050
+ await withFetchStub(() => Response.json({}), async (fetched) => {
2051
+ await (0, node_assert.rejects)(loader(url), isCredentialUrlError);
2052
+ (0, node_assert.deepStrictEqual)(fetched, []);
2053
+ });
2054
+ });
2055
+ 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 () => {
2056
+ await withFetchStub(() => Response.json({ done: true }), async (fetched) => {
2057
+ (0, node_assert.deepStrictEqual)((await loader(url)).document, { done: true });
2058
+ (0, node_assert.deepStrictEqual)(fetched, [new URL(url).href]);
2059
+ });
2060
+ });
2061
+ }
2062
+ });
2017
2063
  function isUnsupportedProtocolError(error, url) {
2018
2064
  return error instanceof require_url.UrlError && error.message === `Unsupported protocol: ${new URL(url).protocol}` && error.reason === "disallowed";
2019
2065
  }
@@ -1,5 +1,5 @@
1
- import { t as FetchError } from "./request-BE7XIrGw.mjs";
2
- import { i as cid_v1_default, n as getRemoteDocument, r as preloadedContexts, t as getDocumentLoader } from "./docloader-jQMjHIRI.mjs";
1
+ import { t as FetchError } from "./request-yCis9VCB.mjs";
2
+ import { i as cid_v1_default, n as getRemoteDocument, r as preloadedContexts, t as getDocumentLoader } from "./docloader-BjyFsnAA.mjs";
3
3
  import { t as UrlError } from "./url-3fyJuqC0.mjs";
4
4
  import { gzipSync } from "node:zlib";
5
5
  import { configure, reset } from "@logtape/logtape";
@@ -2031,6 +2031,52 @@ const unsupportedUrls = [
2031
2031
  "file:///collection",
2032
2032
  `data:application/ld+json,${encodeURIComponent(JSON.stringify({ id: "https://example.com/data" }))}`
2033
2033
  ];
2034
+ function isCredentialUrlError(error) {
2035
+ return error instanceof UrlError && error.reason === "disallowed" && error.message === "URLs with credentials are not allowed";
2036
+ }
2037
+ test("getDocumentLoader() rejects URLs with credentials", async (t) => {
2038
+ const start = "https://8.8.8.8/start";
2039
+ const target = "https://user:pass@8.8.8.8/document";
2040
+ for (const allowPrivateAddress of [false, true]) {
2041
+ const loader = getDocumentLoader({ allowPrivateAddress });
2042
+ for (const path of [
2043
+ "direct",
2044
+ "redirect",
2045
+ "Link",
2046
+ "HTML"
2047
+ ]) await t.step(`${path}, allowPrivateAddress=${allowPrivateAddress}`, async () => {
2048
+ await withFetchStub((url) => {
2049
+ if (url !== start) return Response.json({});
2050
+ if (path === "redirect") return Response.redirect(target, 302);
2051
+ if (path === "HTML") return new Response(`<link rel="alternate" type="application/activity+json" href="${target}">`, { headers: { "Content-Type": "text/html" } });
2052
+ return new Response("", { headers: {
2053
+ "Content-Type": "text/plain",
2054
+ Link: `<${target}>; rel="alternate"; type="application/activity+json"`
2055
+ } });
2056
+ }, async (fetched) => {
2057
+ await rejects(loader(path === "direct" ? target : start), isCredentialUrlError);
2058
+ deepStrictEqual(fetched, path === "direct" ? [] : [start]);
2059
+ });
2060
+ });
2061
+ for (const url of [
2062
+ "https://user@8.8.8.8/document",
2063
+ "https://:pass@8.8.8.8/document",
2064
+ "https://%75ser:p%40ss@8.8.8.8/document",
2065
+ "http://user:pass@8.8.8.8/document"
2066
+ ]) await t.step(`${url}, allowPrivateAddress=${allowPrivateAddress}`, async () => {
2067
+ await withFetchStub(() => Response.json({}), async (fetched) => {
2068
+ await rejects(loader(url), isCredentialUrlError);
2069
+ deepStrictEqual(fetched, []);
2070
+ });
2071
+ });
2072
+ 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 () => {
2073
+ await withFetchStub(() => Response.json({ done: true }), async (fetched) => {
2074
+ deepStrictEqual((await loader(url)).document, { done: true });
2075
+ deepStrictEqual(fetched, [new URL(url).href]);
2076
+ });
2077
+ });
2078
+ }
2079
+ });
2034
2080
  function isUnsupportedProtocolError(error, url) {
2035
2081
  return error instanceof UrlError && error.message === `Unsupported protocol: ${new URL(url).protocol}` && error.reason === "disallowed";
2036
2082
  }
@@ -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.2.18";
6
+ var version = "2.2.19";
7
7
  //#endregion
8
8
  //#region src/request.ts
9
9
  /**
@@ -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.2.18";
4
+ var version = "2.2.19";
5
5
  //#endregion
6
6
  //#region src/request.ts
7
7
  /**
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Do9eywBl.cjs");
2
- const require_request = require("./request-BngWcP0t.cjs");
2
+ const require_request = require("./request-DGtx5MkG.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-BE7XIrGw.mjs";
1
+ import { o as version, r as getUserAgent } from "./request-yCis9VCB.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.2.18",
3
+ "version": "2.2.19",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1112,6 +1112,90 @@ const unsupportedUrls = [
1112
1112
  }`,
1113
1113
  ];
1114
1114
 
1115
+ function isCredentialUrlError(error: unknown): boolean {
1116
+ return error instanceof UrlError && error.reason === "disallowed" &&
1117
+ error.message === "URLs with credentials are not allowed";
1118
+ }
1119
+
1120
+ test("getDocumentLoader() rejects URLs with credentials", async (t) => {
1121
+ const start = "https://8.8.8.8/start";
1122
+ const target = "https://user:pass@8.8.8.8/document";
1123
+ for (const allowPrivateAddress of [false, true]) {
1124
+ const loader = getDocumentLoader({ allowPrivateAddress });
1125
+ for (const path of ["direct", "redirect", "Link", "HTML"]) {
1126
+ await t.step(
1127
+ `${path}, allowPrivateAddress=${allowPrivateAddress}`,
1128
+ async () => {
1129
+ await withFetchStub(
1130
+ (url) => {
1131
+ if (url !== start) return Response.json({});
1132
+ if (path === "redirect") return Response.redirect(target, 302);
1133
+ if (path === "HTML") {
1134
+ return new Response(
1135
+ `<link rel="alternate" type="application/activity+json" href="${target}">`,
1136
+ { headers: { "Content-Type": "text/html" } },
1137
+ );
1138
+ }
1139
+ return new Response("", {
1140
+ headers: {
1141
+ "Content-Type": "text/plain",
1142
+ Link:
1143
+ `<${target}>; rel="alternate"; type="application/activity+json"`,
1144
+ },
1145
+ });
1146
+ },
1147
+ async (fetched) => {
1148
+ await rejects(
1149
+ loader(path === "direct" ? target : start),
1150
+ isCredentialUrlError,
1151
+ );
1152
+ deepStrictEqual(fetched, path === "direct" ? [] : [start]);
1153
+ },
1154
+ );
1155
+ },
1156
+ );
1157
+ }
1158
+ for (
1159
+ const url of [
1160
+ "https://user@8.8.8.8/document",
1161
+ "https://:pass@8.8.8.8/document",
1162
+ "https://%75ser:p%40ss@8.8.8.8/document",
1163
+ "http://user:pass@8.8.8.8/document",
1164
+ ]
1165
+ ) {
1166
+ await t.step(
1167
+ `${url}, allowPrivateAddress=${allowPrivateAddress}`,
1168
+ async () => {
1169
+ await withFetchStub(() => Response.json({}), async (fetched) => {
1170
+ await rejects(loader(url), isCredentialUrlError);
1171
+ deepStrictEqual(fetched, []);
1172
+ });
1173
+ },
1174
+ );
1175
+ }
1176
+ for (
1177
+ const url of [
1178
+ "https://:@8.8.8.8/document",
1179
+ "https://8.8.8.8/@user?q=@pass",
1180
+ ]
1181
+ ) {
1182
+ await t.step(
1183
+ `allows ${url}, allowPrivateAddress=${allowPrivateAddress}`,
1184
+ async () => {
1185
+ await withFetchStub(
1186
+ () => Response.json({ done: true }),
1187
+ async (fetched) => {
1188
+ const document = await loader(url);
1189
+ deepStrictEqual(document.document, { done: true });
1190
+ deepStrictEqual(fetched, [new URL(url).href]);
1191
+ },
1192
+ );
1193
+ },
1194
+ );
1195
+ }
1196
+ }
1197
+ });
1198
+
1115
1199
  function isUnsupportedProtocolError(error: unknown, url: string): boolean {
1116
1200
  return error instanceof UrlError &&
1117
1201
  error.message === `Unsupported protocol: ${new URL(url).protocol}` &&
package/src/docloader.ts CHANGED
@@ -315,6 +315,11 @@ export function getDocumentLoader(
315
315
  if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
316
316
  throw new UrlError(`Unsupported protocol: ${parsedUrl.protocol}`);
317
317
  }
318
+ // When merging forward, keep this guard independent of allowPrivateAddress:
319
+ // private-address opt-in must not permit credentials on any followed URL.
320
+ if (parsedUrl.username !== "" || parsedUrl.password !== "") {
321
+ throw new UrlError("URLs with credentials are not allowed");
322
+ }
318
323
  const currentUrl = parsedUrl.href;
319
324
  if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
320
325
  logger.debug("Using preloaded context: {url}.", { url: currentUrl });