@fedify/webfinger 2.3.8 → 2.3.9

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/webfinger",
3
- "version": "2.3.8",
3
+ "version": "2.3.9",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts"
@@ -22,10 +22,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  }) : target, mod));
23
23
  //#endregion
24
24
  let _fedify_fixture = require("@fedify/fixture");
25
- let es_toolkit = require("es-toolkit");
26
- let node_assert_strict = require("node:assert/strict");
27
25
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
28
26
  let _logtape_logtape = require("@logtape/logtape");
27
+ let es_toolkit = require("es-toolkit");
28
+ let node_assert_strict = require("node:assert/strict");
29
+ let node_dns_promises = require("node:dns/promises");
30
+ node_dns_promises = __toESM(node_dns_promises, 1);
29
31
  let _opentelemetry_api = require("@opentelemetry/api");
30
32
  //#endregion
31
33
  //#region ../../node_modules/.pnpm/regexparam@3.0.0/node_modules/regexparam/dist/index.mjs
@@ -1228,7 +1230,7 @@ var esm_default = new class FetchMock {
1228
1230
  //#endregion
1229
1231
  //#region deno.json
1230
1232
  var name = "@fedify/webfinger";
1231
- var version = "2.3.8";
1233
+ var version = "2.3.9";
1232
1234
  //#endregion
1233
1235
  //#region src/body.ts
1234
1236
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -1447,7 +1449,11 @@ async function lookupWebFingerInternal(resource, options = {}) {
1447
1449
  await (0, _fedify_vocab_runtime.validatePublicUrl)(url.href);
1448
1450
  } catch (e) {
1449
1451
  if (e instanceof _fedify_vocab_runtime.UrlError) {
1450
- logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
1452
+ if (e.reason === "dns") logger.debug("DNS lookup failed for {url}", {
1453
+ url: url.href,
1454
+ error: e
1455
+ });
1456
+ else logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
1451
1457
  return {
1452
1458
  resource: null,
1453
1459
  result: "network_error",
@@ -1989,4 +1995,42 @@ async function lookupWebFingerInternal(resource, options = {}) {
1989
1995
  esm_default.hardReset();
1990
1996
  }
1991
1997
  });
1998
+ (0, _fedify_fixture.test)("lookupWebFinger() logs DNS failures as such", async (t) => {
1999
+ if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") return;
2000
+ for (const result of [
2001
+ "throws",
2002
+ "empty",
2003
+ "private"
2004
+ ]) await t.step(result, async () => {
2005
+ const originalLookup = node_dns_promises.default.lookup;
2006
+ node_dns_promises.default.lookup = (() => result === "throws" ? Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")) : Promise.resolve(result === "empty" ? [] : [{
2007
+ address: "127.0.0.1",
2008
+ family: 4
2009
+ }]));
2010
+ const records = [];
2011
+ await (0, _logtape_logtape.configure)({
2012
+ sinks: { buffer: (record) => records.push(record) },
2013
+ loggers: [{
2014
+ category: "fedify",
2015
+ sinks: ["buffer"],
2016
+ lowestLevel: "debug"
2017
+ }, {
2018
+ category: ["logtape", "meta"],
2019
+ sinks: []
2020
+ }],
2021
+ reset: true
2022
+ });
2023
+ try {
2024
+ (0, node_assert_strict.deepStrictEqual)(await lookupWebFinger("acct:alice@dns-failure.invalid"), null);
2025
+ const failures = records.filter((r) => r.rawMessage !== "Fetching WebFinger resource descriptor from {url}...");
2026
+ (0, node_assert_strict.deepStrictEqual)(failures.map((r) => [r.level, r.rawMessage]), [result === "private" ? ["error", "Invalid URL for WebFinger resource descriptor: {error}"] : ["debug", "DNS lookup failed for {url}"]]);
2027
+ const { error } = failures[0].properties;
2028
+ (0, node_assert_strict.ok)(error instanceof _fedify_vocab_runtime.UrlError);
2029
+ (0, node_assert_strict.deepStrictEqual)(error.reason, result === "private" ? "disallowed" : "dns");
2030
+ } finally {
2031
+ await (0, _logtape_logtape.reset)();
2032
+ node_dns_promises.default.lookup = originalLookup;
2033
+ }
2034
+ });
2035
+ });
1992
2036
  //#endregion
@@ -1,8 +1,9 @@
1
1
  import { createTestMeterProvider, test } from "@fedify/fixture";
2
+ import { FetchError, UrlError, getUserAgent, validatePublicUrl } from "@fedify/vocab-runtime";
3
+ import { configure, getLogger, reset } from "@logtape/logtape";
2
4
  import { withTimeout } from "es-toolkit";
3
5
  import { deepStrictEqual, ok } from "node:assert/strict";
4
- import { FetchError, UrlError, getUserAgent, validatePublicUrl } from "@fedify/vocab-runtime";
5
- import { getLogger } from "@logtape/logtape";
6
+ import dns from "node:dns/promises";
6
7
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
7
8
  //#region \0rolldown/runtime.js
8
9
  var __create = Object.create;
@@ -1227,7 +1228,7 @@ var esm_default = new class FetchMock {
1227
1228
  //#endregion
1228
1229
  //#region deno.json
1229
1230
  var name = "@fedify/webfinger";
1230
- var version = "2.3.8";
1231
+ var version = "2.3.9";
1231
1232
  //#endregion
1232
1233
  //#region src/body.ts
1233
1234
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -1446,7 +1447,11 @@ async function lookupWebFingerInternal(resource, options = {}) {
1446
1447
  await validatePublicUrl(url.href);
1447
1448
  } catch (e) {
1448
1449
  if (e instanceof UrlError) {
1449
- logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
1450
+ if (e.reason === "dns") logger.debug("DNS lookup failed for {url}", {
1451
+ url: url.href,
1452
+ error: e
1453
+ });
1454
+ else logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
1450
1455
  return {
1451
1456
  resource: null,
1452
1457
  result: "network_error",
@@ -1988,5 +1993,43 @@ test("lookupWebFinger() bounds resource descriptors", async () => {
1988
1993
  esm_default.hardReset();
1989
1994
  }
1990
1995
  });
1996
+ test("lookupWebFinger() logs DNS failures as such", async (t) => {
1997
+ if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") return;
1998
+ for (const result of [
1999
+ "throws",
2000
+ "empty",
2001
+ "private"
2002
+ ]) await t.step(result, async () => {
2003
+ const originalLookup = dns.lookup;
2004
+ dns.lookup = (() => result === "throws" ? Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")) : Promise.resolve(result === "empty" ? [] : [{
2005
+ address: "127.0.0.1",
2006
+ family: 4
2007
+ }]));
2008
+ const records = [];
2009
+ await configure({
2010
+ sinks: { buffer: (record) => records.push(record) },
2011
+ loggers: [{
2012
+ category: "fedify",
2013
+ sinks: ["buffer"],
2014
+ lowestLevel: "debug"
2015
+ }, {
2016
+ category: ["logtape", "meta"],
2017
+ sinks: []
2018
+ }],
2019
+ reset: true
2020
+ });
2021
+ try {
2022
+ deepStrictEqual(await lookupWebFinger("acct:alice@dns-failure.invalid"), null);
2023
+ const failures = records.filter((r) => r.rawMessage !== "Fetching WebFinger resource descriptor from {url}...");
2024
+ deepStrictEqual(failures.map((r) => [r.level, r.rawMessage]), [result === "private" ? ["error", "Invalid URL for WebFinger resource descriptor: {error}"] : ["debug", "DNS lookup failed for {url}"]]);
2025
+ const { error } = failures[0].properties;
2026
+ ok(error instanceof UrlError);
2027
+ deepStrictEqual(error.reason, result === "private" ? "disallowed" : "dns");
2028
+ } finally {
2029
+ await reset();
2030
+ dns.lookup = originalLookup;
2031
+ }
2032
+ });
2033
+ });
1991
2034
  //#endregion
1992
2035
  export {};
package/dist/mod.cjs CHANGED
@@ -4,7 +4,7 @@ let _logtape_logtape = require("@logtape/logtape");
4
4
  let _opentelemetry_api = require("@opentelemetry/api");
5
5
  //#region deno.json
6
6
  var name = "@fedify/webfinger";
7
- var version = "2.3.8";
7
+ var version = "2.3.9";
8
8
  //#endregion
9
9
  //#region src/body.ts
10
10
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -223,7 +223,11 @@ async function lookupWebFingerInternal(resource, options = {}) {
223
223
  await (0, _fedify_vocab_runtime.validatePublicUrl)(url.href);
224
224
  } catch (e) {
225
225
  if (e instanceof _fedify_vocab_runtime.UrlError) {
226
- logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
226
+ if (e.reason === "dns") logger.debug("DNS lookup failed for {url}", {
227
+ url: url.href,
228
+ error: e
229
+ });
230
+ else logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
227
231
  return {
228
232
  resource: null,
229
233
  result: "network_error",
package/dist/mod.js CHANGED
@@ -3,7 +3,7 @@ import { getLogger } from "@logtape/logtape";
3
3
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
4
4
  //#region deno.json
5
5
  var name = "@fedify/webfinger";
6
- var version = "2.3.8";
6
+ var version = "2.3.9";
7
7
  //#endregion
8
8
  //#region src/body.ts
9
9
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -222,7 +222,11 @@ async function lookupWebFingerInternal(resource, options = {}) {
222
222
  await validatePublicUrl(url.href);
223
223
  } catch (e) {
224
224
  if (e instanceof UrlError) {
225
- logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
225
+ if (e.reason === "dns") logger.debug("DNS lookup failed for {url}", {
226
+ url: url.href,
227
+ error: e
228
+ });
229
+ else logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
226
230
  return {
227
231
  resource: null,
228
232
  result: "network_error",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/webfinger",
3
- "version": "2.3.8",
3
+ "version": "2.3.9",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -60,7 +60,7 @@
60
60
  "@logtape/logtape": "^2.2.0",
61
61
  "@opentelemetry/api": "^1.9.1",
62
62
  "es-toolkit": "1.46.1",
63
- "@fedify/vocab-runtime": "2.3.8"
63
+ "@fedify/vocab-runtime": "2.3.9"
64
64
  },
65
65
  "scripts": {
66
66
  "build:self": "tsdown",
@@ -1,7 +1,10 @@
1
1
  import { createTestMeterProvider, test } from "@fedify/fixture";
2
+ import { UrlError } from "@fedify/vocab-runtime";
3
+ import { configure, type LogRecord, reset } from "@logtape/logtape";
2
4
  import { withTimeout } from "es-toolkit";
3
5
  import fetchMock from "fetch-mock";
4
6
  import { deepStrictEqual, ok } from "node:assert/strict";
7
+ import dns from "node:dns/promises";
5
8
  import type { ResourceDescriptor } from "./jrd.ts";
6
9
  import { lookupWebFinger } from "./lookup.ts";
7
10
 
@@ -922,3 +925,67 @@ test("lookupWebFinger() bounds resource descriptors", async () => {
922
925
  fetchMock.hardReset();
923
926
  }
924
927
  });
928
+
929
+ test("lookupWebFinger() logs DNS failures as such", async (t) => {
930
+ // The validator skips DNS when Deno has no network permission. Checked
931
+ // here rather than with top-level await, which the CommonJS build rejects.
932
+ if (
933
+ "Deno" in globalThis &&
934
+ (await Deno.permissions.query({ name: "net" })).state !== "granted"
935
+ ) return;
936
+ for (const result of ["throws", "empty", "private"] as const) {
937
+ await t.step(result, async () => {
938
+ // Stubbing works only because vocab-runtime's url.ts uses the default
939
+ // node:dns/promises import; see the FIXME there.
940
+ const originalLookup = dns.lookup;
941
+ dns.lookup = (() =>
942
+ result === "throws"
943
+ ? Promise.reject(new Error("Resolver unavailable"))
944
+ : Promise.resolve(
945
+ result === "empty" ? [] : [{ address: "127.0.0.1", family: 4 }],
946
+ )) as typeof dns.lookup;
947
+ const records: LogRecord[] = [];
948
+ await configure({
949
+ sinks: {
950
+ buffer: (record) =>
951
+ records.push(record),
952
+ },
953
+ loggers: [
954
+ { category: "fedify", sinks: ["buffer"], lowestLevel: "debug" },
955
+ { category: ["logtape", "meta"], sinks: [] },
956
+ ],
957
+ reset: true,
958
+ });
959
+ try {
960
+ deepStrictEqual(
961
+ await lookupWebFinger("acct:alice@dns-failure.invalid"),
962
+ null,
963
+ );
964
+ const failures = records.filter((r) =>
965
+ r.rawMessage !==
966
+ "Fetching WebFinger resource descriptor from {url}..."
967
+ );
968
+ deepStrictEqual(
969
+ failures.map((r) => [r.level, r.rawMessage]),
970
+ [
971
+ result === "private"
972
+ ? [
973
+ "error",
974
+ "Invalid URL for WebFinger resource descriptor: {error}",
975
+ ]
976
+ : ["debug", "DNS lookup failed for {url}"],
977
+ ],
978
+ );
979
+ const { error } = failures[0].properties;
980
+ ok(error instanceof UrlError);
981
+ deepStrictEqual(
982
+ error.reason,
983
+ result === "private" ? "disallowed" : "dns",
984
+ );
985
+ } finally {
986
+ await reset();
987
+ dns.lookup = originalLookup;
988
+ }
989
+ });
990
+ }
991
+ });
package/src/lookup.ts CHANGED
@@ -322,10 +322,17 @@ async function lookupWebFingerInternal(
322
322
  await validatePublicUrl(url.href);
323
323
  } catch (e) {
324
324
  if (e instanceof UrlError) {
325
- logger.error(
326
- "Invalid URL for WebFinger resource descriptor: {error}",
327
- { error: e },
328
- );
325
+ if (e.reason === "dns") {
326
+ logger.debug("DNS lookup failed for {url}", {
327
+ url: url.href,
328
+ error: e,
329
+ });
330
+ } else {
331
+ logger.error(
332
+ "Invalid URL for WebFinger resource descriptor: {error}",
333
+ { error: e },
334
+ );
335
+ }
329
336
  return { resource: null, result: "network_error", remoteHost };
330
337
  }
331
338
  throw e;