@fedify/fedify 0.12.0-dev.268 → 0.12.0-dev.273

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/CHANGES.md CHANGED
@@ -54,6 +54,15 @@ To be released.
54
54
  - The last parameter of `Federation.sendActivity()` method is no longer
55
55
  optional. Also, it now takes the required `contextData` option.
56
56
 
57
+ - Fixed a SSRF vulnerability in the built-in document loader.
58
+ [[CVE-2024-39687]]
59
+
60
+ - The `fetchDocumentLoader()` function now throws an error when the given
61
+ URL is not an HTTP or HTTPS URL or refers to a private network address.
62
+ - The `getAuthenticatedDocumentLoader()` function now returns a document
63
+ loader that throws an error when the given URL is not an HTTP or HTTPS
64
+ URL or refers to a private network address.
65
+
57
66
  - Added more log messages using the [LogTape] library. Currently the below
58
67
  logger categories are used:
59
68
 
@@ -65,6 +74,21 @@ To be released.
65
74
  [#85]: https://github.com/dahlia/fedify/issues/85
66
75
 
67
76
 
77
+ Version 0.11.1
78
+ --------------
79
+
80
+ Released on July 5, 2024.
81
+
82
+ - Fixed a SSRF vulnerability in the built-in document loader.
83
+ [[CVE-2024-39687]]
84
+
85
+ - The `fetchDocumentLoader()` function now throws an error when the given
86
+ URL is not an HTTP or HTTPS URL or refers to a private network address.
87
+ - The `getAuthenticatedDocumentLoader()` function now returns a document
88
+ loader that throws an error when the given URL is not an HTTP or HTTPS
89
+ URL or refers to a private network address.
90
+
91
+
68
92
  Version 0.11.0
69
93
  --------------
70
94
 
@@ -248,6 +272,21 @@ Released on June 29, 2024.
248
272
  [#80]: https://github.com/dahlia/fedify/pull/80
249
273
 
250
274
 
275
+ Version 0.10.1
276
+ --------------
277
+
278
+ Released on July 5, 2024.
279
+
280
+ - Fixed a SSRF vulnerability in the built-in document loader.
281
+ [[CVE-2024-39687]]
282
+
283
+ - The `fetchDocumentLoader()` function now throws an error when the given
284
+ URL is not an HTTP or HTTPS URL or refers to a private network address.
285
+ - The `getAuthenticatedDocumentLoader()` function now returns a document
286
+ loader that throws an error when the given URL is not an HTTP or HTTPS
287
+ URL or refers to a private network address.
288
+
289
+
251
290
  Version 0.10.0
252
291
  --------------
253
292
 
@@ -409,6 +448,23 @@ is now distributed under the [MIT License] to encourage wider adoption.
409
448
  [x-forwarded-fetch]: https://github.com/dahlia/x-forwarded-fetch
410
449
 
411
450
 
451
+ Version 0.9.2
452
+ -------------
453
+
454
+ Released on July 5, 2024.
455
+
456
+ - Fixed a SSRF vulnerability in the built-in document loader.
457
+ [[CVE-2024-39687]]
458
+
459
+ - The `fetchDocumentLoader()` function now throws an error when the given
460
+ URL is not an HTTP or HTTPS URL or refers to a private network address.
461
+ - The `getAuthenticatedDocumentLoader()` function now returns a document
462
+ loader that throws an error when the given URL is not an HTTP or HTTPS
463
+ URL or refers to a private network address.
464
+
465
+ [CVE-2024-39687]: https://github.com/dahlia/fedify/security/advisories/GHSA-p9cg-vqcc-grcx
466
+
467
+
412
468
  Version 0.9.1
413
469
  -------------
414
470
 
@@ -3,6 +3,7 @@ import { getLogger } from "@logtape/logtape";
3
3
  import { signRequest } from "../sig/http.js";
4
4
  import { validateCryptoKey } from "../sig/key.js";
5
5
  import preloadedContexts from "./contexts.js";
6
+ import { validatePublicUrl } from "./url.js";
6
7
  const logger = getLogger(["fedify", "runtime", "docloader"]);
7
8
  /**
8
9
  * Error thrown when fetching a JSON-LD document failed.
@@ -82,6 +83,7 @@ export async function fetchDocumentLoader(url) {
82
83
  documentUrl: url,
83
84
  };
84
85
  }
86
+ await validatePublicUrl(url);
85
87
  const request = createRequest(url);
86
88
  logRequest(request);
87
89
  const response = await fetch(request, {
@@ -110,6 +112,7 @@ export async function fetchDocumentLoader(url) {
110
112
  export function getAuthenticatedDocumentLoader(identity) {
111
113
  validateCryptoKey(identity.privateKey);
112
114
  async function load(url) {
115
+ await validatePublicUrl(url);
113
116
  let request = createRequest(url);
114
117
  request = await signRequest(request, identity.privateKey, identity.keyId);
115
118
  logRequest(request);
@@ -0,0 +1,75 @@
1
+ import * as dntShim from "../_dnt.shims.js";
2
+ import { lookup } from "node:dns/promises";
3
+ import { isIP } from "node:net";
4
+ export class UrlError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "UrlError";
8
+ }
9
+ }
10
+ /**
11
+ * Validates a URL to prevent SSRF attacks.
12
+ */
13
+ export async function validatePublicUrl(url) {
14
+ const parsed = new URL(url);
15
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
16
+ throw new UrlError(`Unsupported protocol: ${parsed.protocol}`);
17
+ }
18
+ let hostname = parsed.hostname;
19
+ if (hostname.startsWith("[") && hostname.endsWith("]")) {
20
+ hostname = hostname.substring(1, hostname.length - 2);
21
+ }
22
+ if (hostname === "localhost") {
23
+ throw new UrlError("Localhost is not allowed");
24
+ }
25
+ if ("Deno" in dntShim.dntGlobalThis && !isIP(hostname)) {
26
+ // If the `net` permission is not granted, we can't resolve the hostname.
27
+ // However, we can safely assume that it cannot gain access to private
28
+ // resources.
29
+ const netPermission = await dntShim.Deno.permissions.query({ name: "net" });
30
+ if (netPermission.state !== "granted")
31
+ return;
32
+ }
33
+ const { address, family } = await lookup(hostname);
34
+ if (family === 4 && !isValidPublicIPv4Address(address) ||
35
+ family === 6 && !isValidPublicIPv6Address(address) ||
36
+ family < 4 || family === 5 || family > 6) {
37
+ throw new UrlError(`Invalid or private address: ${address}`);
38
+ }
39
+ }
40
+ export function isValidPublicIPv4Address(address) {
41
+ const parts = address.split(".");
42
+ const first = parseInt(parts[0]);
43
+ if (first === 0 || first === 10 || first === 127)
44
+ return false;
45
+ const second = parseInt(parts[1]);
46
+ if (first === 169 && second === 254)
47
+ return false;
48
+ if (first === 172 && second >= 16 && second <= 31)
49
+ return false;
50
+ if (first === 192 && second === 168)
51
+ return false;
52
+ return true;
53
+ }
54
+ export function isValidPublicIPv6Address(address) {
55
+ address = expandIPv6Address(address);
56
+ if (address.at(4) !== ":")
57
+ return false;
58
+ const firstWord = parseInt(address.substring(0, 4), 16);
59
+ return !((firstWord >= 0xfc00 && firstWord <= 0xfdff) || // ULA
60
+ (firstWord >= 0xfe80 && firstWord <= 0xfebf) || // Link-local
61
+ firstWord === 0 || firstWord >= 0xff00 // Multicast
62
+ );
63
+ }
64
+ export function expandIPv6Address(address) {
65
+ address = address.toLowerCase();
66
+ if (address === "::")
67
+ return "0000:0000:0000:0000:0000:0000:0000:0000";
68
+ if (address.startsWith("::"))
69
+ address = "0000" + address;
70
+ if (address.endsWith("::"))
71
+ address = address + "0000";
72
+ address = address.replace("::", ":0000".repeat(8 - (address.match(/:/g) || []).length) + ":");
73
+ const parts = address.split(":");
74
+ return parts.map((part) => part.padStart(4, "0")).join(":");
75
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/fedify",
3
- "version": "0.12.0-dev.268+99d302f3",
3
+ "version": "0.12.0-dev.273+75036491",
4
4
  "description": "An ActivityPub server framework",
5
5
  "keywords": [
6
6
  "ActivityPub",
@@ -1 +1 @@
1
- {"version":3,"file":"docloader.d.ts","sourceRoot":"","sources":["../../src/runtime/docloader.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;AAE5C,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAO1D;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;AAEtE;;;;;;;;GAQG;AACH,MAAM,MAAM,kCAAkC,GAAG,CAC/C,QAAQ,EAAE;IAAE,KAAK,EAAE,GAAG,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC,SAAS,CAAA;CAAE,KACpD,cAAc,CAAC;AAEpB;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC;;OAEG;IACH,GAAG,EAAE,GAAG,CAAC;IAET;;;;;OAKG;gBACS,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAKhD;AAwDD;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,cAAc,CAAC,CAyBzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE;IAAE,KAAK,EAAE,GAAG,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC,SAAS,CAAA;CAAE,GACtD,cAAc,CAsBhB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,MAAM,EAAE,cAAc,CAAC;IAEvB;;OAEG;IACH,EAAE,EAAE,OAAO,CAAC;IAEZ;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC;IAEf;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;CAC1E;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,iBAAiB,GAC/C,cAAc,CA2ChB"}
1
+ {"version":3,"file":"docloader.d.ts","sourceRoot":"","sources":["../../src/runtime/docloader.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;AAE5C,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAQ1D;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;AAEtE;;;;;;;;GAQG;AACH,MAAM,MAAM,kCAAkC,GAAG,CAC/C,QAAQ,EAAE;IAAE,KAAK,EAAE,GAAG,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC,SAAS,CAAA;CAAE,KACpD,cAAc,CAAC;AAEpB;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC;;OAEG;IACH,GAAG,EAAE,GAAG,CAAC;IAET;;;;;OAKG;gBACS,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAKhD;AAwDD;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,cAAc,CAAC,CA0BzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE;IAAE,KAAK,EAAE,GAAG,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC,SAAS,CAAA;CAAE,GACtD,cAAc,CAuBhB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,MAAM,EAAE,cAAc,CAAC;IAEvB;;OAEG;IACH,EAAE,EAAE,OAAO,CAAC;IAEZ;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC;IAEf;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,CAAC,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;CAC1E;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,iBAAiB,GAC/C,cAAc,CA2ChB"}
@@ -0,0 +1,11 @@
1
+ export declare class UrlError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ /**
5
+ * Validates a URL to prevent SSRF attacks.
6
+ */
7
+ export declare function validatePublicUrl(url: string): Promise<void>;
8
+ export declare function isValidPublicIPv4Address(address: string): boolean;
9
+ export declare function isValidPublicIPv6Address(address: string): boolean;
10
+ export declare function expandIPv6Address(address: string): string;
11
+ //# sourceMappingURL=url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/runtime/url.ts"],"names":[],"mappings":"AAIA,qBAAa,QAAS,SAAQ,KAAK;gBACrB,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BlE;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CASjE;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,WASvD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAWzD"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.test.d.ts","sourceRoot":"","sources":["../../src/runtime/url.test.ts"],"names":[],"mappings":"AAAA,OAAO,2BAA2B,CAAC"}