@fedify/fedify 1.5.6 → 1.5.8

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
@@ -3,6 +3,30 @@
3
3
  Fedify changelog
4
4
  ================
5
5
 
6
+ Version 1.5.8
7
+ -------------
8
+
9
+ Released on September 17, 2025.
10
+
11
+ - Added a temporary workaround for invalid AT Protocol URIs from BridgyFed.
12
+ URIs like `at://did:plc:...` that violate RFC 3986 URI syntax are now
13
+ automatically URL-encoded to `at://did%3Aplc%3A...` to prevent parsing
14
+ failures when processing bridged Bluesky content. [[#436]]
15
+
16
+
17
+ Version 1.5.7
18
+ -------------
19
+
20
+ Released on August 25, 2025.
21
+
22
+ - Fixed a bug where `verifyRequest()` function threw a `TypeError` when
23
+ verifying HTTP Signatures with `created` or `expires` fields in
24
+ the `Signature` header as defined in draft-cavage-http-signatures-12,
25
+ causing `500 Internal Server Error` responses in inbox handlers.
26
+ Now it correctly handles these fields as unquoted integers according
27
+ to the specification.
28
+
29
+
6
30
  Version 1.5.6
7
31
  -------------
8
32
 
@@ -201,6 +225,30 @@ Released on March 28, 2025.
201
225
  [multibase]: https://github.com/multiformats/js-multibase
202
226
 
203
227
 
228
+ Version 1.4.16
229
+ --------------
230
+
231
+ Released on September 17, 2025.
232
+
233
+ - Added a temporary workaround for invalid AT Protocol URIs from BridgyFed.
234
+ URIs like `at://did:plc:...` that violate RFC 3986 URI syntax are now
235
+ automatically URL-encoded to `at://did%3Aplc%3A...` to prevent parsing
236
+ failures when processing bridged Bluesky content. [[#436]]
237
+
238
+
239
+ Version 1.4.15
240
+ --------------
241
+
242
+ Released on August 25, 2025.
243
+
244
+ - Fixed a bug where `verifyRequest()` function threw a `TypeError` when
245
+ verifying HTTP Signatures with `created` or `expires` fields in
246
+ the `Signature` header as defined in draft-cavage-http-signatures-12,
247
+ causing `500 Internal Server Error` responses in inbox handlers.
248
+ Now it correctly handles these fields as unquoted integers according
249
+ to the specification.
250
+
251
+
204
252
  Version 1.4.14
205
253
  --------------
206
254
 
@@ -474,6 +522,30 @@ Released on February 5, 2025.
474
522
  [#195]: https://github.com/fedify-dev/fedify/issues/195
475
523
 
476
524
 
525
+ Version 1.3.23
526
+ --------------
527
+
528
+ Released on September 17, 2025.
529
+
530
+ - Added a temporary workaround for invalid AT Protocol URIs from BridgyFed.
531
+ URIs like `at://did:plc:...` that violate RFC 3986 URI syntax are now
532
+ automatically URL-encoded to `at://did%3Aplc%3A...` to prevent parsing
533
+ failures when processing bridged Bluesky content. [[#436]]
534
+
535
+
536
+ Version 1.3.22
537
+ --------------
538
+
539
+ Released on August 25, 2025.
540
+
541
+ - Fixed a bug where `verifyRequest()` function threw a `TypeError` when
542
+ verifying HTTP Signatures with `created` or `expires` fields in
543
+ the `Signature` header as defined in draft-cavage-http-signatures-12,
544
+ causing `500 Internal Server Error` responses in inbox handlers.
545
+ Now it correctly handles these fields as unquoted integers according
546
+ to the specification.
547
+
548
+
477
549
  Version 1.3.21
478
550
  --------------
479
551
 
@@ -865,6 +937,30 @@ Released on November 30, 2024.
865
937
  [#193]: https://github.com/fedify-dev/fedify/issues/193
866
938
 
867
939
 
940
+ Version 1.2.26
941
+ --------------
942
+
943
+ Released on September 17, 2025.
944
+
945
+ - Added a temporary workaround for invalid AT Protocol URIs from BridgyFed.
946
+ URIs like `at://did:plc:...` that violate RFC 3986 URI syntax are now
947
+ automatically URL-encoded to `at://did%3Aplc%3A...` to prevent parsing
948
+ failures when processing bridged Bluesky content. [[#436]]
949
+
950
+
951
+ Version 1.2.25
952
+ --------------
953
+
954
+ Released on August 25, 2025.
955
+
956
+ - Fixed a bug where `verifyRequest()` function threw a `TypeError` when
957
+ verifying HTTP Signatures with `created` or `expires` fields in
958
+ the `Signature` header as defined in draft-cavage-http-signatures-12,
959
+ causing `500 Internal Server Error` responses in inbox handlers.
960
+ Now it correctly handles these fields as unquoted integers according
961
+ to the specification.
962
+
963
+
868
964
  Version 1.2.24
869
965
  --------------
870
966
 
@@ -1289,6 +1385,30 @@ Released on October 31, 2024.
1289
1385
  [#118]: https://github.com/fedify-dev/fedify/issues/118
1290
1386
 
1291
1387
 
1388
+ Version 1.1.26
1389
+ --------------
1390
+
1391
+ Released on September 17, 2025.
1392
+
1393
+ - Added a temporary workaround for invalid AT Protocol URIs from BridgyFed.
1394
+ URIs like `at://did:plc:...` that violate RFC 3986 URI syntax are now
1395
+ automatically URL-encoded to `at://did%3Aplc%3A...` to prevent parsing
1396
+ failures when processing bridged Bluesky content. [[#436]]
1397
+
1398
+
1399
+ Version 1.1.25
1400
+ --------------
1401
+
1402
+ Released on August 25, 2025.
1403
+
1404
+ - Fixed a bug where `verifyRequest()` function threw a `TypeError` when
1405
+ verifying HTTP Signatures with `created` or `expires` fields in
1406
+ the `Signature` header as defined in draft-cavage-http-signatures-12,
1407
+ causing `500 Internal Server Error` responses in inbox handlers.
1408
+ Now it correctly handles these fields as unquoted integers according
1409
+ to the specification.
1410
+
1411
+
1292
1412
  Version 1.1.24
1293
1413
  --------------
1294
1414
 
@@ -1754,6 +1874,32 @@ Released on October 20, 2024.
1754
1874
  [#150]: https://github.com/fedify-dev/fedify/issues/150
1755
1875
 
1756
1876
 
1877
+ Version 1.0.29
1878
+ --------------
1879
+
1880
+ Released on September 17, 2025.
1881
+
1882
+ - Added a temporary workaround for invalid AT Protocol URIs from BridgyFed.
1883
+ URIs like `at://did:plc:...` that violate RFC 3986 URI syntax are now
1884
+ automatically URL-encoded to `at://did%3Aplc%3A...` to prevent parsing
1885
+ failures when processing bridged Bluesky content. [[#436]]
1886
+
1887
+ [#436]: https://github.com/fedify-dev/fedify/issues/436
1888
+
1889
+
1890
+ Version 1.0.28
1891
+ --------------
1892
+
1893
+ Released on August 25, 2025.
1894
+
1895
+ - Fixed a bug where `verifyRequest()` function threw a `TypeError` when
1896
+ verifying HTTP Signatures with `created` or `expires` fields in
1897
+ the `Signature` header as defined in draft-cavage-http-signatures-12,
1898
+ causing `500 Internal Server Error` responses in inbox handlers.
1899
+ Now it correctly handles these fields as unquoted integers according
1900
+ to the specification.
1901
+
1902
+
1757
1903
  Version 1.0.27
1758
1904
  --------------
1759
1905
 
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "@fedify/fedify",
3
- "version": "1.5.6",
3
+ "version": "1.5.8",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./mod.ts",
@@ -94,11 +94,14 @@ export async function importMultibaseKey(key) {
94
94
  format: "der",
95
95
  type: "pkcs1",
96
96
  });
97
- const spki = keyObject.export({ type: "spki", format: "der" }).buffer;
97
+ const exported = keyObject.export({ type: "spki", format: "der" }).buffer;
98
+ const spki = exported instanceof Uint8Array
99
+ ? exported
100
+ : new Uint8Array(exported);
98
101
  return await dntShim.crypto.subtle.importKey("spki", new Uint8Array(spki), { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, true, ["verify"]);
99
102
  }
100
103
  else if (code === 0xed) { // ed25519-pub
101
- return await dntShim.crypto.subtle.importKey("raw", content, "Ed25519", true, ["verify"]);
104
+ return await dntShim.crypto.subtle.importKey("raw", content.slice(), "Ed25519", true, ["verify"]);
102
105
  }
103
106
  else {
104
107
  throw new TypeError("Unsupported key type: 0x" + code.toString(16));
package/esm/sig/http.js CHANGED
@@ -221,7 +221,7 @@ async function verifyRequestInternal(request, span, { documentLoader, contextLoa
221
221
  return null;
222
222
  }
223
223
  }
224
- const sigValues = Object.fromEntries(sigHeader.split(",").map((pair) => pair.match(/^\s*([A-Za-z]+)="([^"]*)"\s*$/)).filter((m) => m != null).map((m) => m.slice(1, 3)));
224
+ const sigValues = Object.fromEntries(sigHeader.split(",").map((pair) => pair.match(/^\s*([A-Za-z]+)=(?:"([^"]*)"|(\d+))\s*$/)).filter((m) => m != null).map((m) => [m[1], m[2] ?? m[3]]));
225
225
  if (!("keyId" in sigValues)) {
226
226
  logger.debug("Failed to verify; no keyId field found in the Signature header.", { signature: sigHeader });
227
227
  return null;
@@ -234,6 +234,41 @@ async function verifyRequestInternal(request, span, { documentLoader, contextLoa
234
234
  logger.debug("Failed to verify; no signature field found in the Signature header.", { signature: sigHeader });
235
235
  return null;
236
236
  }
237
+ if ("expires" in sigValues) {
238
+ const expiresSeconds = parseInt(sigValues.expires);
239
+ if (!Number.isInteger(expiresSeconds)) {
240
+ logger.debug("Failed to verify; invalid expires field in the Signature header: {expires}.", { expires: sigValues.expires, signature: sigHeader });
241
+ return null;
242
+ }
243
+ const expires = dntShim.Temporal.Instant.fromEpochMilliseconds(expiresSeconds * 1000);
244
+ if (dntShim.Temporal.Instant.compare(now, expires) > 0) {
245
+ logger.debug("Failed to verify; signature expired at {expires} (now: {now}).", {
246
+ expires: expires.toString(),
247
+ now: now.toString(),
248
+ signature: sigHeader,
249
+ });
250
+ return null;
251
+ }
252
+ }
253
+ if ("created" in sigValues) {
254
+ const createdSeconds = parseInt(sigValues.created);
255
+ if (!Number.isInteger(createdSeconds)) {
256
+ logger.debug("Failed to verify; invalid created field in the Signature header: {created}.", { created: sigValues.created, signature: sigHeader });
257
+ return null;
258
+ }
259
+ if (timeWindow !== false) {
260
+ const created = dntShim.Temporal.Instant.fromEpochMilliseconds(createdSeconds * 1000);
261
+ const tw = timeWindow ?? { minutes: 1 };
262
+ if (dntShim.Temporal.Instant.compare(created, now.add(tw)) > 0) {
263
+ logger.debug("Failed to verify; created is too far in the future.", { created: created.toString(), now: now.toString() });
264
+ return null;
265
+ }
266
+ else if (dntShim.Temporal.Instant.compare(created, now.subtract(tw)) < 0) {
267
+ logger.debug("Failed to verify; created is too far in the past.", { created: created.toString(), now: now.toString() });
268
+ return null;
269
+ }
270
+ }
271
+ }
237
272
  const { keyId, headers, signature } = sigValues;
238
273
  span?.setAttribute("http_signatures.key_id", keyId);
239
274
  if ("algorithm" in sigValues) {
@@ -259,11 +294,15 @@ async function verifyRequestInternal(request, span, { documentLoader, contextLoa
259
294
  return null;
260
295
  }
261
296
  const message = headerNames.map((name) => `${name}: ` +
262
- (name == "(request-target)"
297
+ (name === "(request-target)"
263
298
  ? `${request.method.toLowerCase()} ${new URL(request.url).pathname}`
264
- : name == "host"
265
- ? request.headers.get("host") ?? new URL(request.url).host
266
- : request.headers.get(name))).join("\n");
299
+ : name === "(created)"
300
+ ? (sigValues.created ?? "")
301
+ : name === "(expires)"
302
+ ? (sigValues.expires ?? "")
303
+ : name === "host"
304
+ ? request.headers.get("host") ?? new URL(request.url).host
305
+ : request.headers.get(name))).join("\n");
267
306
  const sig = decodeBase64(signature);
268
307
  span?.setAttribute("http_signatures.signature", encodeHex(sig));
269
308
  // TODO: support other than RSASSA-PKCS1-v1_5:
package/esm/sig/ld.js CHANGED
@@ -184,7 +184,7 @@ export async function verifySignature(jsonLd, options = {}) {
184
184
  const encoder = new TextEncoder();
185
185
  const message = sigOptsHash + docHash;
186
186
  const messageBytes = encoder.encode(message);
187
- const verified = await dntShim.crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, signature, messageBytes);
187
+ const verified = await dntShim.crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, signature.slice(), messageBytes);
188
188
  if (verified)
189
189
  return key;
190
190
  if (cached) {
@@ -200,7 +200,7 @@ export async function verifySignature(jsonLd, options = {}) {
200
200
  });
201
201
  if (key == null)
202
202
  return null;
203
- const verified = await dntShim.crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, signature, messageBytes);
203
+ const verified = await dntShim.crypto.subtle.verify("RSASSA-PKCS1-v1_5", key.publicKey, signature.slice(), messageBytes);
204
204
  return verified ? key : null;
205
205
  }
206
206
  logger.debug("Failed to verify with the fetched key {keyId}; " +
package/esm/sig/proof.js CHANGED
@@ -209,7 +209,7 @@ async function verifyProofInternal(jsonLd, proof, options) {
209
209
  "Ed25519 key:\n{keyId}", { proof, keyId: proof.verificationMethodId.href });
210
210
  return null;
211
211
  }
212
- const verified = await dntShim.crypto.subtle.verify("Ed25519", publicKey.publicKey, proof.proofValue, digest);
212
+ const verified = await dntShim.crypto.subtle.verify("Ed25519", publicKey.publicKey, proof.proofValue.slice(), digest);
213
213
  if (!verified) {
214
214
  if (fetchedKey.cached) {
215
215
  logger.debug("Failed to verify the proof with the cached key {keyId}; retrying " +
@@ -0,0 +1,24 @@
1
+ {
2
+ "@context": [
3
+ "https://www.w3.org/ns/activitystreams",
4
+ "https://w3id.org/security/v1"
5
+ ],
6
+ "id": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd",
7
+ "type": "Person",
8
+ "preferredUsername": "hongminhee",
9
+ "name": "洪兔",
10
+ "inbox": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd/inbox",
11
+ "outbox": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd/outbox",
12
+ "publicKey": {
13
+ "id": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd#main-key",
14
+ "owner": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd",
15
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAowJfOzpA/nAYyL0bVDTm\niCAOlhFCIBnqwk1jvGrbkDhMzxlsgyoDqUSlmcJdKaPwu24YdFajDtJIgto27Ju7\nIC3hB7OFchnZ4JZrdYFo7CJABOzK58o12sdmmkCdY5hXWf1604E+mzyIdBAJ1FFJ\nL8vP07VEUsZ7yo9x0iVNg7HpCOK+y6BqI2GHS2dq9qkqQEIhC2TKHXn/RQVXwYB6\nG+YQmVUtcsbCVKdcWyTKhItLRGnepu3BqBSbieLxV27B1O9NFSoPu8xiBUnYwMoe\nsUQCE5tGcqxc75HzcVCbq7PqVqHZ1NW9RYssaSUqi4FYcjXxQrR08DrAl8rR4eXT\n4QIDAQAB\n-----END PUBLIC KEY-----\n"
16
+ },
17
+ "endpoints": {
18
+ "type": "as:Endpoints",
19
+ "sharedInbox": "https://oeee.cafe/inbox"
20
+ },
21
+ "followers": "https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd/followers",
22
+ "manuallyApprovesFollowers": false,
23
+ "url": "https://oeee.cafe/@hongminhee"
24
+ }