@fedify/fedify 2.1.29 → 2.1.31

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.
Files changed (86) hide show
  1. package/dist/{builder-Mg2G3qj8.mjs → builder-isMRh_I6.mjs} +3 -3
  2. package/dist/{client-CAM_bQXx.d.cts → client-BIBzqEIp.d.cts} +0 -1
  3. package/dist/{client-CjDMsn4a.mjs → client-BZr7TGyu.mjs} +113 -64
  4. package/dist/{client-CSddvgWN.d.ts → client-jE7oGvRh.d.ts} +0 -1
  5. package/dist/compat/mod.d.cts +1 -1
  6. package/dist/compat/mod.d.ts +1 -1
  7. package/dist/compat/transformers.test.mjs +1 -1
  8. package/dist/{context-DLLaDau8.d.ts → context-C0uGVzgk.d.ts} +28 -2
  9. package/dist/{context-BNwv9Q1v.d.cts → context-CLaLxi49.d.cts} +28 -2
  10. package/dist/{deno-BiYv5RT5.mjs → deno-CiaJqTcu.mjs} +1 -1
  11. package/dist/docloader-DOrwedT7.mjs +124 -0
  12. package/dist/federation/builder.test.mjs +1 -1
  13. package/dist/federation/handler.test.mjs +6 -6
  14. package/dist/federation/idempotency.test.mjs +2 -2
  15. package/dist/federation/inbox.test.mjs +1 -1
  16. package/dist/federation/keycache.test.mjs +18 -7
  17. package/dist/federation/middleware.test.mjs +380 -24
  18. package/dist/federation/mod.cjs +1 -1
  19. package/dist/federation/mod.d.cts +2 -2
  20. package/dist/federation/mod.d.ts +2 -2
  21. package/dist/federation/mod.js +1 -1
  22. package/dist/federation/send.test.mjs +445 -70
  23. package/dist/federation/temporal.test.mjs +1 -1
  24. package/dist/federation/webfinger.test.mjs +1 -1
  25. package/dist/{http-Dl00HXr4.d.ts → http-9IFUxdkp.d.cts} +1 -2
  26. package/dist/{http-C1ga9WQu.mjs → http-A9mP2uM7.mjs} +194 -96
  27. package/dist/{http-Cginz7L0.cjs → http-CRiVlZd5.cjs} +188 -105
  28. package/dist/{http-B3rSb2GQ.js → http-Crml3gei.js} +187 -104
  29. package/dist/{http-DqZhyEso.d.cts → http-SbfezERn.d.ts} +1 -2
  30. package/dist/{inbox-BWzL0xMU.mjs → inbox-KESLudq4.mjs} +1 -1
  31. package/dist/{key-O-ATwIGS.mjs → key-Dg6-DV_O.mjs} +17 -3
  32. package/dist/{keycache-RFlZObU3.mjs → keycache-DahCaG_m.mjs} +1 -1
  33. package/dist/{kv-cache-BdylAE9w.cjs → kv-cache-Cqmm-yQa.cjs} +59 -15
  34. package/dist/{kv-cache-8Kg2CHiD.js → kv-cache-DQcyZL5T.js} +60 -16
  35. package/dist/{ld-C-fCGM-z.mjs → ld-COjDN6Nb.mjs} +2 -2
  36. package/dist/{middleware-CVRFS7PZ.cjs → middleware-BbgR7xOI.cjs} +185 -60
  37. package/dist/{middleware-VvrqPwHL.cjs → middleware-BtDQUCcz.cjs} +1 -1
  38. package/dist/{middleware-C9UCWOqw.mjs → middleware-COFlKeYn.mjs} +79 -39
  39. package/dist/{middleware-CxgFiKI3.mjs → middleware-CuQvczZd.mjs} +1 -1
  40. package/dist/{middleware-41MkquTV.js → middleware-DIW5S_5L.js} +185 -60
  41. package/dist/{mod-DNEgTI8M.d.ts → mod-BRnwh2oy.d.ts} +2 -7
  42. package/dist/{mod-BLDHhuW0.d.ts → mod-C7w-QVUv.d.ts} +1 -1
  43. package/dist/{mod-CWJNbyju.d.ts → mod-CFRocFeQ.d.ts} +1 -1
  44. package/dist/{mod-BmlfRBmU.d.cts → mod-CGUH8Ujp.d.cts} +1 -1
  45. package/dist/{mod-Cn9GJ5ex.d.cts → mod-Co7LEK9X.d.cts} +2 -7
  46. package/dist/{mod-DiuFTYUt.d.cts → mod-LOEOKbYd.d.cts} +1 -1
  47. package/dist/mod.cjs +5 -5
  48. package/dist/mod.d.cts +6 -6
  49. package/dist/mod.d.ts +6 -6
  50. package/dist/mod.js +5 -5
  51. package/dist/nodeinfo/client.test.mjs +337 -19
  52. package/dist/nodeinfo/handler.test.mjs +1 -1
  53. package/dist/nodeinfo/mod.cjs +1 -1
  54. package/dist/nodeinfo/mod.d.cts +1 -1
  55. package/dist/nodeinfo/mod.d.ts +1 -1
  56. package/dist/nodeinfo/mod.js +1 -1
  57. package/dist/otel/exporter.test.mjs +292 -30
  58. package/dist/otel/mod.cjs +36 -30
  59. package/dist/otel/mod.d.cts +13 -0
  60. package/dist/otel/mod.d.ts +13 -0
  61. package/dist/otel/mod.js +36 -30
  62. package/dist/{owner-C8IVASD3.mjs → owner-CFHq61cq.mjs} +2 -2
  63. package/dist/{proof-BbHMAkPG.cjs → proof-0JkSC_pT.cjs} +1 -1
  64. package/dist/{proof-cGfRPTiV.js → proof-DzksJFK7.js} +1 -1
  65. package/dist/{proof-CUdcBgvg.mjs → proof-KSHlbPCG.mjs} +2 -2
  66. package/dist/{send-BoV4dyVO.mjs → send-DaDe2YCb.mjs} +117 -32
  67. package/dist/sig/http.test.mjs +507 -3
  68. package/dist/sig/key.test.mjs +197 -3
  69. package/dist/sig/ld.test.mjs +47 -2
  70. package/dist/sig/mod.cjs +2 -2
  71. package/dist/sig/mod.d.cts +2 -2
  72. package/dist/sig/mod.d.ts +2 -2
  73. package/dist/sig/mod.js +2 -2
  74. package/dist/sig/owner.test.mjs +46 -1
  75. package/dist/sig/proof.test.mjs +52 -1
  76. package/dist/{temporal-BJlnku_d.mjs → temporal-DNGH8o2E.mjs} +1 -1
  77. package/dist/testing/mod.d.mts +26 -1
  78. package/dist/{types-miAYfiGO.js → types-B1_pRFE_.js} +112 -63
  79. package/dist/{types-DRPjmHVV.cjs → types-DOvyymM4.cjs} +111 -62
  80. package/dist/utils/docloader.test.mjs +380 -32
  81. package/dist/utils/mod.cjs +1 -1
  82. package/dist/utils/mod.d.cts +1 -1
  83. package/dist/utils/mod.d.ts +1 -1
  84. package/dist/utils/mod.js +1 -1
  85. package/package.json +7 -7
  86. package/dist/docloader-DrxXBFL8.mjs +0 -80
@@ -3,12 +3,12 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { n as assertStrictEquals } from "../std__assert-DflMZha5.mjs";
6
- import { t as assertThrows } from "../assert_throws-D4DNWrux.mjs";
6
+ import { r as assertFalse, t as assertThrows } from "../assert_throws-D4DNWrux.mjs";
7
7
  import { t as assert } from "../assert-BhQd8QHP.mjs";
8
8
  import { t as assertRejects } from "../assert_rejects-WyYSTDDX.mjs";
9
- import { a as generateCryptoKeyPair, i as fetchKeyDetailed, o as importJwk, r as fetchKey, s as validateCryptoKey, t as exportJwk } from "../key-O-ATwIGS.mjs";
9
+ import { a as generateCryptoKeyPair, i as fetchKeyDetailed, o as importJwk, r as fetchKey, s as validateCryptoKey, t as exportJwk } from "../key-Dg6-DV_O.mjs";
10
10
  import { c as rsaPublicKey3, i as rsaPrivateKey2, o as rsaPublicKey1, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
11
- import { g as wrapContextLoaderForJsonLd } from "../ld-C-fCGM-z.mjs";
11
+ import { g as wrapContextLoaderForJsonLd } from "../ld-COjDN6Nb.mjs";
12
12
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
13
13
  import { CryptographicKey, Multikey, Person } from "@fedify/vocab";
14
14
  import { FetchError, UrlError, getDocumentLoader } from "@fedify/vocab-runtime";
@@ -759,5 +759,199 @@ test("fetchKey() records the controller an actor document establishes", async ()
759
759
  assertEquals(key?.controllerId, new URL(actorId));
760
760
  assert(key?.publicKey != null);
761
761
  });
762
+ test("fetchKey() rejects a key whose owner redirects to another actor", async () => {
763
+ const keyId = "https://evil.example/keys/1";
764
+ const victim = "https://victim.example/users/alice";
765
+ const mallory = "https://evil.example/users/mallory";
766
+ const { publicKeyPem } = await rsaPublicKey3.toJsonLd({ contextLoader: mockDocumentLoader });
767
+ const cache = {};
768
+ assertEquals(await fetchKey(keyId, CryptographicKey, {
769
+ documentLoader(resource) {
770
+ if (resource === keyId) return Promise.resolve({
771
+ contextUrl: null,
772
+ documentUrl: resource,
773
+ document: {
774
+ "@context": "https://w3id.org/security/v1",
775
+ id: keyId,
776
+ type: "CryptographicKey",
777
+ owner: victim,
778
+ publicKeyPem
779
+ }
780
+ });
781
+ if (resource === victim || resource === mallory) return Promise.resolve({
782
+ contextUrl: null,
783
+ documentUrl: mallory,
784
+ document: {
785
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
786
+ id: mallory,
787
+ type: "Person",
788
+ publicKey: keyId
789
+ }
790
+ });
791
+ return mockDocumentLoader(resource);
792
+ },
793
+ contextLoader: mockDocumentLoader,
794
+ keyCache: {
795
+ get(keyId) {
796
+ return Promise.resolve(cache[keyId.href]);
797
+ },
798
+ set(keyId, key) {
799
+ cache[keyId.href] = key;
800
+ return Promise.resolve();
801
+ }
802
+ }
803
+ }), {
804
+ key: null,
805
+ cached: false
806
+ });
807
+ assertEquals(cache, { [keyId]: null });
808
+ });
809
+ test("fetchKey() rejects a key whose owner resolves to another actor on the same origin", async () => {
810
+ const keyId = "https://victim.example/keys/bob";
811
+ const victim = "https://victim.example/users/alice";
812
+ const bob = "https://victim.example/users/bob";
813
+ const { publicKeyPem } = await rsaPublicKey3.toJsonLd({ contextLoader: mockDocumentLoader });
814
+ assertEquals(await fetchKey(keyId, CryptographicKey, {
815
+ documentLoader(resource) {
816
+ if (resource === keyId) return Promise.resolve({
817
+ contextUrl: null,
818
+ documentUrl: resource,
819
+ document: {
820
+ "@context": "https://w3id.org/security/v1",
821
+ id: keyId,
822
+ type: "CryptographicKey",
823
+ owner: victim,
824
+ publicKeyPem
825
+ }
826
+ });
827
+ if (resource === victim || resource === bob) return Promise.resolve({
828
+ contextUrl: null,
829
+ documentUrl: resource,
830
+ document: {
831
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
832
+ id: bob,
833
+ type: "Person",
834
+ publicKey: keyId
835
+ }
836
+ });
837
+ return mockDocumentLoader(resource);
838
+ },
839
+ contextLoader: mockDocumentLoader
840
+ }), {
841
+ key: null,
842
+ cached: false
843
+ });
844
+ });
845
+ test("fetchKey() rejects a Multikey whose controller redirects to another actor", async () => {
846
+ const keyId = "https://evil.example/keys/ed25519";
847
+ const victim = "https://victim.example/users/alice";
848
+ const mallory = "https://evil.example/users/mallory";
849
+ const multikeyDocument = await ed25519Multikey.toJsonLd({ contextLoader: mockDocumentLoader });
850
+ assertEquals(await fetchKey(keyId, Multikey, {
851
+ documentLoader(resource) {
852
+ if (resource === keyId) return Promise.resolve({
853
+ contextUrl: null,
854
+ documentUrl: resource,
855
+ document: {
856
+ ...multikeyDocument,
857
+ id: keyId,
858
+ controller: victim
859
+ }
860
+ });
861
+ if (resource === victim || resource === mallory) return Promise.resolve({
862
+ contextUrl: null,
863
+ documentUrl: mallory,
864
+ document: {
865
+ "@context": [
866
+ "https://www.w3.org/ns/activitystreams",
867
+ "https://w3id.org/security/v1",
868
+ "https://w3id.org/security/multikey/v1",
869
+ "https://w3id.org/security/data-integrity/v1",
870
+ "https://www.w3.org/ns/did/v1"
871
+ ],
872
+ id: mallory,
873
+ type: "Person",
874
+ assertionMethod: [keyId]
875
+ }
876
+ });
877
+ return mockDocumentLoader(resource);
878
+ },
879
+ contextLoader: mockDocumentLoader
880
+ }), {
881
+ key: null,
882
+ cached: false
883
+ });
884
+ });
885
+ test("fetchKey() accepts an owner whose document URL differs from its id", async () => {
886
+ const keyId = "https://victim.example/keys/alice";
887
+ const alice = "https://victim.example/users/alice";
888
+ const { publicKeyPem } = await rsaPublicKey3.toJsonLd({ contextLoader: mockDocumentLoader });
889
+ const { key, cached } = await fetchKey(keyId, CryptographicKey, {
890
+ documentLoader(resource) {
891
+ if (resource === keyId) return Promise.resolve({
892
+ contextUrl: null,
893
+ documentUrl: resource,
894
+ document: {
895
+ "@context": "https://w3id.org/security/v1",
896
+ id: keyId,
897
+ type: "CryptographicKey",
898
+ owner: alice,
899
+ publicKeyPem
900
+ }
901
+ });
902
+ if (resource === alice) return Promise.resolve({
903
+ contextUrl: null,
904
+ documentUrl: `${alice}.json`,
905
+ document: {
906
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
907
+ id: alice,
908
+ type: "Person",
909
+ publicKey: keyId
910
+ }
911
+ });
912
+ return mockDocumentLoader(resource);
913
+ },
914
+ contextLoader: mockDocumentLoader
915
+ });
916
+ assertFalse(cached);
917
+ assertEquals(key?.id, new URL(keyId));
918
+ assertEquals(key?.ownerId, new URL(alice));
919
+ });
920
+ test("fetchKey() rejects an owner reached through a normalization redirect", async () => {
921
+ const keyId = "https://victim.example/keys/alice";
922
+ const canonical = "https://victim.example/users/alice";
923
+ const nonCanonical = "https://victim.example/users/Alice";
924
+ const { publicKeyPem } = await rsaPublicKey3.toJsonLd({ contextLoader: mockDocumentLoader });
925
+ assertEquals(await fetchKey(keyId, CryptographicKey, {
926
+ documentLoader(resource) {
927
+ if (resource === keyId) return Promise.resolve({
928
+ contextUrl: null,
929
+ documentUrl: resource,
930
+ document: {
931
+ "@context": "https://w3id.org/security/v1",
932
+ id: keyId,
933
+ type: "CryptographicKey",
934
+ owner: nonCanonical,
935
+ publicKeyPem
936
+ }
937
+ });
938
+ if (resource === canonical || resource === nonCanonical) return Promise.resolve({
939
+ contextUrl: null,
940
+ documentUrl: canonical,
941
+ document: {
942
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
943
+ id: canonical,
944
+ type: "Person",
945
+ publicKey: keyId
946
+ }
947
+ });
948
+ return mockDocumentLoader(resource);
949
+ },
950
+ contextLoader: mockDocumentLoader
951
+ }), {
952
+ key: null,
953
+ cached: false
954
+ });
955
+ });
762
956
  //#endregion
763
957
  export {};
@@ -5,9 +5,9 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { r as assertFalse, t as assertThrows } from "../assert_throws-D4DNWrux.mjs";
6
6
  import { n as assertInstanceOf, t as assert } from "../assert-BhQd8QHP.mjs";
7
7
  import { t as assertRejects } from "../assert_rejects-WyYSTDDX.mjs";
8
- import { a as generateCryptoKeyPair } from "../key-O-ATwIGS.mjs";
8
+ import { a as generateCryptoKeyPair } from "../key-Dg6-DV_O.mjs";
9
9
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
10
- import { a as compactJsonLd, d as isInvalidUrlTypeError, f as signJsonLd, h as verifySignature, i as attachSignature, m as verifyJsonLd, n as UnsafeJsonLdError, o as createSignature, s as detachSignature } from "../ld-C-fCGM-z.mjs";
10
+ import { a as compactJsonLd, d as isInvalidUrlTypeError, f as signJsonLd, h as verifySignature, i as attachSignature, m as verifyJsonLd, n as UnsafeJsonLdError, o as createSignature, s as detachSignature } from "../ld-COjDN6Nb.mjs";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
12
  import { CryptographicKey } from "@fedify/vocab";
13
13
  import { encodeBase64 } from "byte-encodings/base64";
@@ -818,5 +818,50 @@ test("verifyJsonLd() rejects a key that claims a forged owner", async () => {
818
818
  contextLoader: mockDocumentLoader
819
819
  }));
820
820
  });
821
+ test("verifyJsonLd() rejects a key whose owner redirects to another actor", async () => {
822
+ const keyId = new URL("https://evil.example/keys/1");
823
+ const victim = "https://victim.example/users/alice";
824
+ const mallory = "https://evil.example/users/mallory";
825
+ const { publicKeyPem } = await rsaPublicKey3.toJsonLd({ contextLoader: mockDocumentLoader });
826
+ const signed = await signJsonLd({
827
+ "@context": "https://www.w3.org/ns/activitystreams",
828
+ id: "https://victim.example/activities/forged",
829
+ type: "Create",
830
+ actor: victim,
831
+ object: {
832
+ type: "Note",
833
+ content: "forged"
834
+ }
835
+ }, rsaPrivateKey3, keyId, { contextLoader: mockDocumentLoader });
836
+ const options = {
837
+ documentLoader(resource) {
838
+ if (resource === keyId.href) return Promise.resolve({
839
+ contextUrl: null,
840
+ documentUrl: resource,
841
+ document: {
842
+ "@context": "https://w3id.org/security/v1",
843
+ id: keyId.href,
844
+ type: "CryptographicKey",
845
+ owner: victim,
846
+ publicKeyPem
847
+ }
848
+ });
849
+ if (resource === victim || resource === mallory) return Promise.resolve({
850
+ contextUrl: null,
851
+ documentUrl: mallory,
852
+ document: {
853
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
854
+ id: mallory,
855
+ type: "Person",
856
+ publicKey: keyId.href
857
+ }
858
+ });
859
+ return mockDocumentLoader(resource);
860
+ },
861
+ contextLoader: mockDocumentLoader
862
+ };
863
+ assertEquals(await verifySignature(signed, options), null);
864
+ assertFalse(await verifyJsonLd(signed, options));
865
+ });
821
866
  //#endregion
822
867
  export {};
package/dist/sig/mod.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
- const require_http = require("../http-Cginz7L0.cjs");
5
- const require_proof = require("../proof-BbHMAkPG.cjs");
4
+ const require_http = require("../http-CRiVlZd5.cjs");
5
+ const require_proof = require("../proof-0JkSC_pT.cjs");
6
6
  exports.attachSignature = require_proof.attachSignature;
7
7
  exports.createProof = require_proof.createProof;
8
8
  exports.createSignature = require_proof.createSignature;
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { C as exportJwk, D as importJwk, E as generateCryptoKeyPair, S as KeyCache, T as fetchKeyDetailed, _ as validateAcceptSignature, a as VerifyRequestDetailedResult, b as FetchKeyOptions, c as signRequest, d as AcceptSignatureMember, f as AcceptSignatureParameters, g as parseAcceptSignature, h as fulfillAcceptSignature, i as SignRequestOptions, l as verifyRequest, m as formatAcceptSignature, n as HttpMessageSignaturesSpecDeterminer, o as VerifyRequestFailureReason, p as FulfillAcceptSignatureResult, r as Rfc9421SignRequestOptions, s as VerifyRequestOptions, t as HttpMessageSignaturesSpec, u as verifyRequestDetailed, v as FetchKeyDetailedResult, w as fetchKey, x as FetchKeyResult, y as FetchKeyErrorResult } from "../http-DqZhyEso.cjs";
2
+ import { C as exportJwk, D as importJwk, E as generateCryptoKeyPair, S as KeyCache, T as fetchKeyDetailed, _ as validateAcceptSignature, a as VerifyRequestDetailedResult, b as FetchKeyOptions, c as signRequest, d as AcceptSignatureMember, f as AcceptSignatureParameters, g as parseAcceptSignature, h as fulfillAcceptSignature, i as SignRequestOptions, l as verifyRequest, m as formatAcceptSignature, n as HttpMessageSignaturesSpecDeterminer, o as VerifyRequestFailureReason, p as FulfillAcceptSignatureResult, r as Rfc9421SignRequestOptions, s as VerifyRequestOptions, t as HttpMessageSignaturesSpec, u as verifyRequestDetailed, v as FetchKeyDetailedResult, w as fetchKey, x as FetchKeyResult, y as FetchKeyErrorResult } from "../http-9IFUxdkp.cjs";
3
3
  import { i as getKeyOwner, n as GetKeyOwnerOptions, r as doesActorOwnKey, t as DoesActorOwnKeyOptions } from "../owner-B3hNQCG8.cjs";
4
- import { _ as verifyJsonLd, a as createProof, c as verifyProof, d as VerifyJsonLdOptions, f as VerifySignatureOptions, g as signJsonLd, h as detachSignature, i as VerifyProofOptions, l as CreateSignatureOptions, m as createSignature, n as SignObjectOptions, o as signObject, p as attachSignature, r as VerifyObjectOptions, s as verifyObject, t as CreateProofOptions, u as SignJsonLdOptions, v as verifySignature } from "../mod-DiuFTYUt.cjs";
4
+ import { _ as verifyJsonLd, a as createProof, c as verifyProof, d as VerifyJsonLdOptions, f as VerifySignatureOptions, g as signJsonLd, h as detachSignature, i as VerifyProofOptions, l as CreateSignatureOptions, m as createSignature, n as SignObjectOptions, o as signObject, p as attachSignature, r as VerifyObjectOptions, s as verifyObject, t as CreateProofOptions, u as SignJsonLdOptions, v as verifySignature } from "../mod-LOEOKbYd.cjs";
5
5
  export { AcceptSignatureMember, AcceptSignatureParameters, CreateProofOptions, CreateSignatureOptions, DoesActorOwnKeyOptions, FetchKeyDetailedResult, FetchKeyErrorResult, FetchKeyOptions, FetchKeyResult, FulfillAcceptSignatureResult, GetKeyOwnerOptions, HttpMessageSignaturesSpec, HttpMessageSignaturesSpecDeterminer, KeyCache, Rfc9421SignRequestOptions, SignJsonLdOptions, SignObjectOptions, SignRequestOptions, VerifyJsonLdOptions, VerifyObjectOptions, VerifyProofOptions, VerifyRequestDetailedResult, VerifyRequestFailureReason, VerifyRequestOptions, VerifySignatureOptions, attachSignature, createProof, createSignature, detachSignature, doesActorOwnKey, exportJwk, fetchKey, fetchKeyDetailed, formatAcceptSignature, fulfillAcceptSignature, generateCryptoKeyPair, getKeyOwner, importJwk, parseAcceptSignature, signJsonLd, signObject, signRequest, validateAcceptSignature, verifyJsonLd, verifyObject, verifyProof, verifyRequest, verifyRequestDetailed, verifySignature };
package/dist/sig/mod.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { C as exportJwk, D as importJwk, E as generateCryptoKeyPair, S as KeyCache, T as fetchKeyDetailed, _ as validateAcceptSignature, a as VerifyRequestDetailedResult, b as FetchKeyOptions, c as signRequest, d as AcceptSignatureMember, f as AcceptSignatureParameters, g as parseAcceptSignature, h as fulfillAcceptSignature, i as SignRequestOptions, l as verifyRequest, m as formatAcceptSignature, n as HttpMessageSignaturesSpecDeterminer, o as VerifyRequestFailureReason, p as FulfillAcceptSignatureResult, r as Rfc9421SignRequestOptions, s as VerifyRequestOptions, t as HttpMessageSignaturesSpec, u as verifyRequestDetailed, v as FetchKeyDetailedResult, w as fetchKey, x as FetchKeyResult, y as FetchKeyErrorResult } from "../http-Dl00HXr4.js";
2
+ import { C as exportJwk, D as importJwk, E as generateCryptoKeyPair, S as KeyCache, T as fetchKeyDetailed, _ as validateAcceptSignature, a as VerifyRequestDetailedResult, b as FetchKeyOptions, c as signRequest, d as AcceptSignatureMember, f as AcceptSignatureParameters, g as parseAcceptSignature, h as fulfillAcceptSignature, i as SignRequestOptions, l as verifyRequest, m as formatAcceptSignature, n as HttpMessageSignaturesSpecDeterminer, o as VerifyRequestFailureReason, p as FulfillAcceptSignatureResult, r as Rfc9421SignRequestOptions, s as VerifyRequestOptions, t as HttpMessageSignaturesSpec, u as verifyRequestDetailed, v as FetchKeyDetailedResult, w as fetchKey, x as FetchKeyResult, y as FetchKeyErrorResult } from "../http-SbfezERn.js";
3
3
  import { i as getKeyOwner, n as GetKeyOwnerOptions, r as doesActorOwnKey, t as DoesActorOwnKeyOptions } from "../owner-CiBPcC6F.js";
4
- import { _ as verifyJsonLd, a as createProof, c as verifyProof, d as VerifyJsonLdOptions, f as VerifySignatureOptions, g as signJsonLd, h as detachSignature, i as VerifyProofOptions, l as CreateSignatureOptions, m as createSignature, n as SignObjectOptions, o as signObject, p as attachSignature, r as VerifyObjectOptions, s as verifyObject, t as CreateProofOptions, u as SignJsonLdOptions, v as verifySignature } from "../mod-BLDHhuW0.js";
4
+ import { _ as verifyJsonLd, a as createProof, c as verifyProof, d as VerifyJsonLdOptions, f as VerifySignatureOptions, g as signJsonLd, h as detachSignature, i as VerifyProofOptions, l as CreateSignatureOptions, m as createSignature, n as SignObjectOptions, o as signObject, p as attachSignature, r as VerifyObjectOptions, s as verifyObject, t as CreateProofOptions, u as SignJsonLdOptions, v as verifySignature } from "../mod-C7w-QVUv.js";
5
5
  export { AcceptSignatureMember, AcceptSignatureParameters, CreateProofOptions, CreateSignatureOptions, DoesActorOwnKeyOptions, FetchKeyDetailedResult, FetchKeyErrorResult, FetchKeyOptions, FetchKeyResult, FulfillAcceptSignatureResult, GetKeyOwnerOptions, HttpMessageSignaturesSpec, HttpMessageSignaturesSpecDeterminer, KeyCache, Rfc9421SignRequestOptions, SignJsonLdOptions, SignObjectOptions, SignRequestOptions, VerifyJsonLdOptions, VerifyObjectOptions, VerifyProofOptions, VerifyRequestDetailedResult, VerifyRequestFailureReason, VerifyRequestOptions, VerifySignatureOptions, attachSignature, createProof, createSignature, detachSignature, doesActorOwnKey, exportJwk, fetchKey, fetchKeyDetailed, formatAcceptSignature, fulfillAcceptSignature, generateCryptoKeyPair, getKeyOwner, importJwk, parseAcceptSignature, signJsonLd, signObject, signRequest, validateAcceptSignature, verifyJsonLd, verifyObject, verifyProof, verifyRequest, verifyRequestDetailed, verifySignature };
package/dist/sig/mod.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { _ as validateAcceptSignature, a as verifyRequestDetailed, c as fetchKey, d as importJwk, g as parseAcceptSignature, h as fulfillAcceptSignature, i as verifyRequest, l as fetchKeyDetailed, m as formatAcceptSignature, o as exportJwk, r as signRequest, u as generateCryptoKeyPair } from "../http-B3rSb2GQ.js";
4
- import { _ as signJsonLd, a as doesActorOwnKey, b as verifySignature, d as createSignature, f as detachSignature, i as verifyProof, l as attachSignature, n as signObject, o as getKeyOwner, r as verifyObject, t as createProof, y as verifyJsonLd } from "../proof-cGfRPTiV.js";
3
+ import { _ as validateAcceptSignature, a as verifyRequestDetailed, c as fetchKey, d as importJwk, g as parseAcceptSignature, h as fulfillAcceptSignature, i as verifyRequest, l as fetchKeyDetailed, m as formatAcceptSignature, o as exportJwk, r as signRequest, u as generateCryptoKeyPair } from "../http-Crml3gei.js";
4
+ import { _ as signJsonLd, a as doesActorOwnKey, b as verifySignature, d as createSignature, f as detachSignature, i as verifyProof, l as attachSignature, n as signObject, o as getKeyOwner, r as verifyObject, t as createProof, y as verifyJsonLd } from "../proof-DzksJFK7.js";
5
5
  export { attachSignature, createProof, createSignature, detachSignature, doesActorOwnKey, exportJwk, fetchKey, fetchKeyDetailed, formatAcceptSignature, fulfillAcceptSignature, generateCryptoKeyPair, getKeyOwner, importJwk, parseAcceptSignature, signJsonLd, signObject, signRequest, validateAcceptSignature, verifyJsonLd, verifyObject, verifyProof, verifyRequest, verifyRequestDetailed, verifySignature };
@@ -6,7 +6,7 @@ import "../std__assert-DflMZha5.mjs";
6
6
  import { r as assertFalse } from "../assert_throws-D4DNWrux.mjs";
7
7
  import { t as assert } from "../assert-BhQd8QHP.mjs";
8
8
  import { o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-CtZLJq76.mjs";
9
- import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-C8IVASD3.mjs";
9
+ import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-CFHq61cq.mjs";
10
10
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
11
11
  import { Create, CryptographicKey, lookupObject } from "@fedify/vocab";
12
12
  //#region src/sig/owner.test.ts
@@ -153,5 +153,50 @@ test("doesActorOwnKey() matches a key that leaves its id implicit", async () =>
153
153
  publicKey: rsaPublicKey2.publicKey
154
154
  }), options));
155
155
  });
156
+ test("doesActorOwnKey() rejects an actor ID that redirects to the key's owner", async () => {
157
+ const keyId = "https://evil.example/keys/1";
158
+ const victim = "https://victim.example/users/alice";
159
+ const mallory = "https://evil.example/users/mallory";
160
+ const { publicKeyPem } = await rsaPublicKey1.toJsonLd({ contextLoader: mockDocumentLoader });
161
+ const serveKeyOwnedBy = (owner) => ({
162
+ documentLoader(resource) {
163
+ if (resource === keyId) return Promise.resolve({
164
+ contextUrl: null,
165
+ documentUrl: resource,
166
+ document: {
167
+ "@context": "https://w3id.org/security/v1",
168
+ id: keyId,
169
+ type: "CryptographicKey",
170
+ owner,
171
+ publicKeyPem
172
+ }
173
+ });
174
+ if (resource === victim || resource === mallory) return Promise.resolve({
175
+ contextUrl: null,
176
+ documentUrl: mallory,
177
+ document: {
178
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1"],
179
+ id: mallory,
180
+ type: "Person",
181
+ publicKey: keyId
182
+ }
183
+ });
184
+ return mockDocumentLoader(resource);
185
+ },
186
+ contextLoader: mockDocumentLoader
187
+ });
188
+ const activity = new Create({ actor: new URL(victim) });
189
+ assertFalse(await doesActorOwnKey(activity, new CryptographicKey({
190
+ id: new URL(keyId),
191
+ owner: new URL(mallory),
192
+ publicKey: rsaPublicKey1.publicKey
193
+ }), serveKeyOwnedBy(mallory)));
194
+ assertFalse(await doesActorOwnKey(activity, new CryptographicKey({
195
+ id: new URL(keyId),
196
+ owner: new URL(victim),
197
+ publicKey: rsaPublicKey1.publicKey
198
+ }), serveKeyOwnedBy(victim)));
199
+ assertEquals(await getKeyOwner(new URL(keyId), serveKeyOwnedBy(victim)), null);
200
+ });
156
201
  //#endregion
157
202
  export {};
@@ -6,7 +6,7 @@ import "../std__assert-DflMZha5.mjs";
6
6
  import { n as assertInstanceOf } from "../assert-BhQd8QHP.mjs";
7
7
  import { t as assertRejects } from "../assert_rejects-WyYSTDDX.mjs";
8
8
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CtZLJq76.mjs";
9
- import { i as verifyProof, n as signObject, r as verifyObject, t as createProof } from "../proof-CUdcBgvg.mjs";
9
+ import { i as verifyProof, n as signObject, r as verifyObject, t as createProof } from "../proof-KSHlbPCG.mjs";
10
10
  import { mockDocumentLoader, test } from "@fedify/fixture";
11
11
  import { Create, DataIntegrityProof, Multikey, Note, Place } from "@fedify/vocab";
12
12
  import { decodeMultibase, importMultibaseKey } from "@fedify/vocab-runtime";
@@ -322,5 +322,56 @@ test("verifyObject() rejects a key that claims a forged controller", async () =>
322
322
  contextLoader: mockDocumentLoader
323
323
  }), null);
324
324
  });
325
+ test("verifyObject() rejects a key whose controller redirects to another actor", async () => {
326
+ const keyId = new URL("https://evil.example/keys/ed25519");
327
+ const victim = "https://victim.example/users/alice";
328
+ const mallory = "https://evil.example/users/mallory";
329
+ const multikeyDocument = await ed25519Multikey.toJsonLd({ contextLoader: mockDocumentLoader });
330
+ const signOptions = {
331
+ format: "compact",
332
+ contextLoader: mockDocumentLoader,
333
+ documentLoader: mockDocumentLoader,
334
+ context: ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"]
335
+ };
336
+ assertEquals(await verifyObject(Create, await (await signObject(new Create({
337
+ id: new URL("https://victim.example/activities/forged"),
338
+ actor: new URL(victim),
339
+ object: new Note({
340
+ id: new URL("https://victim.example/notes/forged"),
341
+ attribution: new URL(victim),
342
+ content: "forged"
343
+ })
344
+ }), ed25519PrivateKey, keyId, signOptions)).toJsonLd(signOptions), {
345
+ documentLoader(resource) {
346
+ if (resource === keyId.href) return Promise.resolve({
347
+ contextUrl: null,
348
+ documentUrl: resource,
349
+ document: {
350
+ ...multikeyDocument,
351
+ id: keyId.href,
352
+ controller: victim
353
+ }
354
+ });
355
+ if (resource === victim || resource === mallory) return Promise.resolve({
356
+ contextUrl: null,
357
+ documentUrl: mallory,
358
+ document: {
359
+ "@context": [
360
+ "https://www.w3.org/ns/activitystreams",
361
+ "https://w3id.org/security/v1",
362
+ "https://w3id.org/security/multikey/v1",
363
+ "https://w3id.org/security/data-integrity/v1",
364
+ "https://www.w3.org/ns/did/v1"
365
+ ],
366
+ id: mallory,
367
+ type: "Person",
368
+ assertionMethod: [keyId.href]
369
+ }
370
+ });
371
+ return mockDocumentLoader(resource);
372
+ },
373
+ contextLoader: mockDocumentLoader
374
+ }), null);
375
+ });
325
376
  //#endregion
326
377
  export {};
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { c as getNormalizationContextLoader } from "./ld-C-fCGM-z.mjs";
4
+ import { c as getNormalizationContextLoader } from "./ld-COjDN6Nb.mjs";
5
5
  import jsonld from "@fedify/vocab-runtime/jsonld";
6
6
  //#region src/federation/temporal.ts
7
7
  function isPlainObject(value) {
@@ -5,7 +5,6 @@ import { Activity, Actor, Collection, CryptographicKey, Hashtag, Link, LookupObj
5
5
  import { Link as Link$1, LookupWebFingerOptions, ResourceDescriptor } from "@fedify/webfinger";
6
6
  import { TracerProvider } from "@opentelemetry/api";
7
7
  import { DocumentLoader, GetUserAgentOptions } from "@fedify/vocab-runtime";
8
-
9
8
  //#region src/nodeinfo/types.d.ts
10
9
  /**
11
10
  * The type of the result of parsing JSON.
@@ -1801,6 +1800,11 @@ interface RequestContext<TContextData> extends Context<TContextData> {
1801
1800
  * Gets the public key of the sender, if any exists and it is verified.
1802
1801
  * Otherwise, `null` is returned.
1803
1802
  *
1803
+ * Successful results are reused for this request regardless of the options.
1804
+ * Pending checks and `null` results are reused for the same effective
1805
+ * document and context loaders, including failed lookups. Rejected checks
1806
+ * can be retried. Shared work uses the first caller's tracer provider.
1807
+ *
1804
1808
  * This can be used for implementing [authorized fetch] (also known as
1805
1809
  * secure mode) in ActivityPub.
1806
1810
  *
@@ -1814,6 +1818,11 @@ interface RequestContext<TContextData> extends Context<TContextData> {
1814
1818
  * Gets the public key of the sender, if any exists and it is verified.
1815
1819
  * Otherwise, `null` is returned.
1816
1820
  *
1821
+ * Successful results are reused for this request regardless of the options.
1822
+ * Pending checks and `null` results are reused for the same effective
1823
+ * document and context loaders, including failed lookups. Rejected checks
1824
+ * can be retried. Shared work uses the first caller's tracer provider.
1825
+ *
1817
1826
  * This can be used for implementing [authorized fetch] (also known as
1818
1827
  * secure mode) in ActivityPub.
1819
1828
  *
@@ -1833,6 +1842,14 @@ interface RequestContext<TContextData> extends Context<TContextData> {
1833
1842
  * Gets the owner of the signed key, if any exists and it is verified.
1834
1843
  * Otherwise, `null` is returned.
1835
1844
  *
1845
+ * Verified keys and owners are reused for this request regardless of the
1846
+ * options. Pending checks are shared for the same effective document and
1847
+ * context loaders. A `null` result from an owner lookup after key
1848
+ * verification is retained for those loaders. A `null` result caused by
1849
+ * an unverified key is not retained, allowing an owner lookup after another
1850
+ * loader verifies the key. Rejected checks can be retried. Shared work
1851
+ * uses the first caller's tracer provider.
1852
+ *
1836
1853
  * This can be used for implementing [authorized fetch] (also known as
1837
1854
  * secure mode) in ActivityPub.
1838
1855
  *
@@ -1847,6 +1864,14 @@ interface RequestContext<TContextData> extends Context<TContextData> {
1847
1864
  * Gets the owner of the signed key, if any exists and it is verified.
1848
1865
  * Otherwise, `null` is returned.
1849
1866
  *
1867
+ * Verified keys and owners are reused for this request regardless of the
1868
+ * options. Pending checks are shared for the same effective document and
1869
+ * context loaders. A `null` result from an owner lookup after key
1870
+ * verification is retained for those loaders. A `null` result caused by
1871
+ * an unverified key is not retained, allowing an owner lookup after another
1872
+ * loader verifies the key. Rejected checks can be retried. Shared work
1873
+ * uses the first caller's tracer provider.
1874
+ *
1850
1875
  * This can be used for implementing [authorized fetch] (also known as
1851
1876
  * secure mode) in ActivityPub.
1852
1877
  *