@fedify/fedify 1.4.14 → 1.4.15

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,19 @@
3
3
  Fedify changelog
4
4
  ================
5
5
 
6
+ Version 1.4.15
7
+ --------------
8
+
9
+ Released on August 25, 2025.
10
+
11
+ - Fixed a bug where `verifyRequest()` function threw a `TypeError` when
12
+ verifying HTTP Signatures with `created` or `expires` fields in
13
+ the `Signature` header as defined in draft-cavage-http-signatures-12,
14
+ causing `500 Internal Server Error` responses in inbox handlers.
15
+ Now it correctly handles these fields as unquoted integers according
16
+ to the specification.
17
+
18
+
6
19
  Version 1.4.14
7
20
  --------------
8
21
 
@@ -276,6 +289,19 @@ Released on February 5, 2025.
276
289
  [#195]: https://github.com/fedify-dev/fedify/issues/195
277
290
 
278
291
 
292
+ Version 1.3.22
293
+ --------------
294
+
295
+ Released on August 25, 2025.
296
+
297
+ - Fixed a bug where `verifyRequest()` function threw a `TypeError` when
298
+ verifying HTTP Signatures with `created` or `expires` fields in
299
+ the `Signature` header as defined in draft-cavage-http-signatures-12,
300
+ causing `500 Internal Server Error` responses in inbox handlers.
301
+ Now it correctly handles these fields as unquoted integers according
302
+ to the specification.
303
+
304
+
279
305
  Version 1.3.21
280
306
  --------------
281
307
 
@@ -667,6 +693,19 @@ Released on November 30, 2024.
667
693
  [#193]: https://github.com/fedify-dev/fedify/issues/193
668
694
 
669
695
 
696
+ Version 1.2.25
697
+ --------------
698
+
699
+ Released on August 25, 2025.
700
+
701
+ - Fixed a bug where `verifyRequest()` function threw a `TypeError` when
702
+ verifying HTTP Signatures with `created` or `expires` fields in
703
+ the `Signature` header as defined in draft-cavage-http-signatures-12,
704
+ causing `500 Internal Server Error` responses in inbox handlers.
705
+ Now it correctly handles these fields as unquoted integers according
706
+ to the specification.
707
+
708
+
670
709
  Version 1.2.24
671
710
  --------------
672
711
 
@@ -1091,6 +1130,19 @@ Released on October 31, 2024.
1091
1130
  [#118]: https://github.com/fedify-dev/fedify/issues/118
1092
1131
 
1093
1132
 
1133
+ Version 1.1.25
1134
+ --------------
1135
+
1136
+ Released on August 25, 2025.
1137
+
1138
+ - Fixed a bug where `verifyRequest()` function threw a `TypeError` when
1139
+ verifying HTTP Signatures with `created` or `expires` fields in
1140
+ the `Signature` header as defined in draft-cavage-http-signatures-12,
1141
+ causing `500 Internal Server Error` responses in inbox handlers.
1142
+ Now it correctly handles these fields as unquoted integers according
1143
+ to the specification.
1144
+
1145
+
1094
1146
  Version 1.1.24
1095
1147
  --------------
1096
1148
 
@@ -1556,6 +1608,19 @@ Released on October 20, 2024.
1556
1608
  [#150]: https://github.com/fedify-dev/fedify/issues/150
1557
1609
 
1558
1610
 
1611
+ Version 1.0.28
1612
+ --------------
1613
+
1614
+ Released on August 25, 2025.
1615
+
1616
+ - Fixed a bug where `verifyRequest()` function threw a `TypeError` when
1617
+ verifying HTTP Signatures with `created` or `expires` fields in
1618
+ the `Signature` header as defined in draft-cavage-http-signatures-12,
1619
+ causing `500 Internal Server Error` responses in inbox handlers.
1620
+ Now it correctly handles these fields as unquoted integers according
1621
+ to the specification.
1622
+
1623
+
1559
1624
  Version 1.0.27
1560
1625
  --------------
1561
1626
 
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "@fedify/fedify",
3
- "version": "1.4.14",
3
+ "version": "1.4.15",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./mod.ts",
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:
@@ -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
+ }