@fedify/fedify 2.4.0-dev.2010 → 2.4.0-dev.2030
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/dist/{assert-CebeUf0C.mjs → assert-D1Eafmd_.mjs} +1 -1
- package/dist/{assert_equals-CBlRfA-_.mjs → assert_equals-Z6IcEbWU.mjs} +2 -28
- package/dist/{assert_exists-DtzkXC47.mjs → assert_exists-cffSpJ7X.mjs} +1 -1
- package/dist/{assert_false-3pLLLsO4.mjs → assert_false-CPdRnc3E.mjs} +1 -1
- package/dist/{assert_greater_or_equal-BApOTh1h.mjs → assert_greater_or_equal-BlafOF4-.mjs} +2 -1
- package/dist/{assert_instance_of-g3WcyYNR.mjs → assert_instance_of-mrXWWNY-.mjs} +1 -1
- package/dist/{assert_not_equals-BN7yDMWe.mjs → assert_not_equals-CpPK2t89.mjs} +2 -1
- package/dist/{assert_rejects-wFbSPn1O.mjs → assert_rejects-CTptdYck.mjs} +2 -2
- package/dist/{assert_strict_equals-Bpjl4Psw.mjs → assert_strict_equals-CYvBmkpC.mjs} +2 -1
- package/dist/{assert_throws-BJ5wuvR2.mjs → assert_throws-BYITOvPO.mjs} +2 -1
- package/dist/assertion_error-B-cx0fFA.mjs +31 -0
- package/dist/{builder-asm96yV1.mjs → builder-BYJ5otJa.mjs} +2 -2
- package/dist/compat/outgoing-jsonld.test.mjs +71 -3
- package/dist/compat/public-audience.test.mjs +3 -3
- package/dist/compat/secured-document.test.d.mts +2 -0
- package/dist/compat/secured-document.test.mjs +58 -0
- package/dist/compat/transformers.test.mjs +4 -4
- package/dist/{compound-proof-7UIAueLV.mjs → compound-proof-BIvUoJxm.mjs} +74 -2
- package/dist/{deno-Ckq9TwCv.mjs → deno-Vj_9wePq.mjs} +1 -1
- package/dist/{docloader-DUKEPG-O.mjs → docloader-qUudHZVH.mjs} +2 -2
- package/dist/federation/builder.test.mjs +5 -5
- package/dist/federation/circuit-breaker.test.mjs +3 -3
- package/dist/federation/collection.test.mjs +2 -2
- package/dist/federation/compound-fanout.test.d.mts +2 -0
- package/dist/federation/compound-fanout.test.mjs +228 -0
- package/dist/federation/handler-compound-proof.test.mjs +5 -5
- package/dist/federation/handler.test.mjs +11 -11
- package/dist/federation/idempotency.test.mjs +4 -4
- package/dist/federation/inbox.test.mjs +2 -2
- package/dist/federation/keycache.test.mjs +3 -3
- package/dist/federation/kv.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +4 -4
- package/dist/federation/middleware.test.mjs +17 -17
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +7 -6
- package/dist/federation/negotiation.test.mjs +3 -3
- package/dist/federation/outgoing-proof.test.d.mts +2 -0
- package/dist/federation/outgoing-proof.test.mjs +396 -0
- package/dist/federation/retry.test.mjs +3 -2
- package/dist/federation/router.test.mjs +5 -5
- package/dist/federation/send.test.mjs +11 -11
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +4 -4
- package/dist/federation/webfinger.test.mjs +4 -4
- package/dist/{federation-CgbvMauV.cjs → federation-CTnaljmy.cjs} +203 -37
- package/dist/{http-DQBfmkrS.cjs → http-B5F1JMh0.cjs} +1 -1
- package/dist/{http-DQA2JOEJ.js → http-By8tnmEr.js} +1 -1
- package/dist/{http-DZaAutqg.mjs → http-D4WPcJUe.mjs} +3 -3
- package/dist/{key-zxzx01Ec.mjs → key-B_Zc5Jbd.mjs} +2 -2
- package/dist/{kv-cache-sy8OMNT8.cjs → kv-cache-8K70pjtX.cjs} +1 -1
- package/dist/{kv-cache-CbrUX90H.mjs → kv-cache-BXdT7pJQ.mjs} +1 -1
- package/dist/{kv-cache-BBoePTOJ.js → kv-cache-BswMKQjC.js} +1 -1
- package/dist/{ld-CD_TN0e8.mjs → ld-B16KUSIQ.mjs} +3 -3
- package/dist/{metrics-B47Cm5gK.mjs → metrics-DpQqUAmh.mjs} +1 -1
- package/dist/{middleware-U_Gannqi.mjs → middleware-BL5HFFVU.mjs} +1 -1
- package/dist/{middleware-pHwDTAnq.mjs → middleware-C7_hqDnl.mjs} +143 -49
- package/dist/{middleware-U0OIuqwD.js → middleware-Cxto6Bq2.js} +204 -38
- package/dist/{mod-UDwMjuHM.d.ts → mod-CNRAxXEb.d.ts} +1 -5
- package/dist/{mod-CbKDOnCt.d.cts → mod-CVQFWefn.d.cts} +1 -5
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +1 -1
- package/dist/mod.d.ts +1 -1
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/client.test.mjs +2 -2
- package/dist/nodeinfo/handler.test.mjs +3 -3
- package/dist/nodeinfo/types.test.mjs +3 -3
- package/dist/otel/exporter.test.mjs +2 -2
- package/dist/{outgoing-jsonld-B5Ne7JoL.mjs → outgoing-jsonld-4IJqPYos.mjs} +11 -9
- package/dist/{owner-u3IJRhVx.mjs → owner-BIOIhZoM.mjs} +2 -2
- package/dist/{owner-BNKC3s6G.js → owner-D-E0bT2O.js} +127 -35
- package/dist/{owner--mNatWct.cjs → owner-WUYke16i.cjs} +126 -34
- package/dist/{proof-D3te0SP-.mjs → proof-BVxJu5vv.mjs} +80 -23
- package/dist/{public-audience-Cc2YJ3ip.mjs → public-audience--klBIOk1.mjs} +13 -9
- package/dist/secured-document-CxzZZ9nL.mjs +35 -0
- package/dist/{send-CDr0Zivk.mjs → send-Cr5FJ7iu.mjs} +3 -3
- package/dist/sig/compound-proof-discovery.test.mjs +3 -3
- package/dist/sig/compound-proof-producer.test.d.mts +2 -0
- package/dist/sig/compound-proof-producer.test.mjs +107 -0
- package/dist/sig/compound-proof-verification.test.mjs +5 -5
- package/dist/sig/compound-proof.test.mjs +145 -58
- package/dist/sig/http.test.mjs +11 -10
- package/dist/sig/key.test.mjs +6 -6
- package/dist/sig/ld.test.mjs +9 -9
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +1 -1
- package/dist/sig/mod.d.ts +1 -1
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +5 -5
- package/dist/sig/proof.test.mjs +9 -9
- package/dist/std__assert-DvMDRPes.mjs +8 -0
- package/dist/{temporal-DKhVF_Uk.mjs → temporal-CCf0lZV3.mjs} +1 -1
- package/dist/testing/kv.test.mjs +2 -2
- package/dist/utils/docloader.test.mjs +5 -5
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +6 -6
- package/dist/std__assert-CxFT0MNt.mjs +0 -8
package/dist/mod.js
CHANGED
|
@@ -3,11 +3,11 @@ import { URLPattern } from "urlpattern-polyfill";
|
|
|
3
3
|
import "./rolldown-runtime-BC-Eo5Mj.js";
|
|
4
4
|
import { n as autoIdAssigner, r as getDefaultActivityTransformers, t as actorDehydrator } from "./transformers-DAtXl_Kc.js";
|
|
5
5
|
import "./compat/mod.js";
|
|
6
|
-
import { F as validateAcceptSignature, M as formatAcceptSignature, N as fulfillAcceptSignature, P as parseAcceptSignature, a as verifyRequestDetailed, c as fetchKey, d as importJwk, i as verifyRequest, l as fetchKeyDetailed, o as exportJwk, r as signRequest, u as generateCryptoKeyPair } from "./http-
|
|
7
|
-
import { a as respondWithObject, c as digest, d as parseCircuitBreakerKvState, f as createFederationBuilder, i as SendActivityError, l as CircuitBreaker, n as handleWebFinger, o as respondWithObjectIfAcceptable, r as createExponentialBackoffPolicy, s as buildCollectionSynchronizationHeader, t as createFederation, u as normalizeCircuitBreakerOptions } from "./middleware-
|
|
6
|
+
import { F as validateAcceptSignature, M as formatAcceptSignature, N as fulfillAcceptSignature, P as parseAcceptSignature, a as verifyRequestDetailed, c as fetchKey, d as importJwk, i as verifyRequest, l as fetchKeyDetailed, o as exportJwk, r as signRequest, u as generateCryptoKeyPair } from "./http-By8tnmEr.js";
|
|
7
|
+
import { a as respondWithObject, c as digest, d as parseCircuitBreakerKvState, f as createFederationBuilder, i as SendActivityError, l as CircuitBreaker, n as handleWebFinger, o as respondWithObjectIfAcceptable, r as createExponentialBackoffPolicy, s as buildCollectionSynchronizationHeader, t as createFederation, u as normalizeCircuitBreakerOptions } from "./middleware-Cxto6Bq2.js";
|
|
8
8
|
import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "./types-C_cNlNVK.js";
|
|
9
|
-
import { S as signJsonLd, T as verifySignature, a as signObject, c as verifyPortableObjectProof, g as detachSignature, h as createSignature, i as hasProofLike, n as getKeyOwner, p as attachSignature, r as createProof, s as verifyObject, t as doesActorOwnKey, u as verifyProof, w as verifyJsonLd, y as hasSignatureLike } from "./owner-
|
|
10
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-
|
|
9
|
+
import { S as signJsonLd, T as verifySignature, a as signObject, c as verifyPortableObjectProof, g as detachSignature, h as createSignature, i as hasProofLike, n as getKeyOwner, p as attachSignature, r as createProof, s as verifyObject, t as doesActorOwnKey, u as verifyProof, w as verifyJsonLd, y as hasSignatureLike } from "./owner-D-E0bT2O.js";
|
|
10
|
+
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-BswMKQjC.js";
|
|
11
11
|
import { InProcessMessageQueue, MemoryKvStore, ParallelMessageQueue, Router, RouterError } from "./federation/mod.js";
|
|
12
12
|
import "./nodeinfo/mod.js";
|
|
13
13
|
import "./runtime/mod.js";
|
|
@@ -2,8 +2,8 @@ import "temporal-polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
|
|
5
|
-
import { t as assertEquals } from "../assert_equals-
|
|
6
|
-
import "../std__assert-
|
|
5
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
6
|
+
import "../std__assert-DvMDRPes.mjs";
|
|
7
7
|
import { a as parseProtocol, c as parseUsage, i as parseOutboundService, n as parseInboundService, o as parseServices, r as parseNodeInfo, s as parseSoftware, t as getNodeInfo } from "../client-DpnpgjyX.mjs";
|
|
8
8
|
import { test } from "@fedify/fixture";
|
|
9
9
|
//#region src/nodeinfo/client.test.ts
|
|
@@ -3,9 +3,9 @@ import "urlpattern-polyfill";
|
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { r as createRequestContext } from "../context-CbS35fLr.mjs";
|
|
5
5
|
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
6
|
-
import { t as assertEquals } from "../assert_equals-
|
|
7
|
-
import "../std__assert-
|
|
8
|
-
import { _ as handleNodeInfo, o as createFederation, v as handleNodeInfoJrd } from "../middleware-
|
|
6
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
7
|
+
import "../std__assert-DvMDRPes.mjs";
|
|
8
|
+
import { _ as handleNodeInfo, o as createFederation, v as handleNodeInfoJrd } from "../middleware-C7_hqDnl.mjs";
|
|
9
9
|
import { test } from "@fedify/fixture";
|
|
10
10
|
//#region src/nodeinfo/handler.test.ts
|
|
11
11
|
test("handleNodeInfo()", async () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { t as assertEquals } from "../assert_equals-
|
|
5
|
-
import { t as assertThrows } from "../assert_throws-
|
|
6
|
-
import "../std__assert-
|
|
4
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
5
|
+
import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
|
|
6
|
+
import "../std__assert-DvMDRPes.mjs";
|
|
7
7
|
import { t as nodeInfoToJson } from "../types-DE_LV1JB.mjs";
|
|
8
8
|
import { test } from "@fedify/fixture";
|
|
9
9
|
//#region src/nodeinfo/types.test.ts
|
|
@@ -2,8 +2,8 @@ import { Temporal } from "temporal-polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
5
|
-
import { t as assertEquals } from "../assert_equals-
|
|
6
|
-
import "../std__assert-
|
|
5
|
+
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
6
|
+
import "../std__assert-DvMDRPes.mjs";
|
|
7
7
|
import { getLogger } from "@logtape/logtape";
|
|
8
8
|
import { ExportResultCode } from "@opentelemetry/core";
|
|
9
9
|
import { SpanKind, SpanStatusCode, TraceFlags } from "@opentelemetry/api";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as preloadedOnlyDocumentLoader, t as normalizePublicAudience } from "./public-audience
|
|
4
|
+
import { n as preloadedOnlyDocumentLoader, t as normalizePublicAudience } from "./public-audience--klBIOk1.mjs";
|
|
5
|
+
import { t as isSelfContainedSecuredDocument } from "./secured-document-CxzZZ9nL.mjs";
|
|
5
6
|
import { getLogger } from "@logtape/logtape";
|
|
6
7
|
import { preloadedContexts } from "@fedify/vocab-runtime";
|
|
7
8
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
@@ -64,13 +65,14 @@ function getKnownSafeContextUrls() {
|
|
|
64
65
|
/**
|
|
65
66
|
* Wraps scalar ActivityStreams attachment properties in arrays.
|
|
66
67
|
*/
|
|
67
|
-
function wrapScalarAttachments(jsonLd, depth = 0) {
|
|
68
|
+
function wrapScalarAttachments(jsonLd, preserveSecured, depth = 0) {
|
|
68
69
|
if (depth >= MAX_TRAVERSAL_DEPTH) return jsonLd;
|
|
70
|
+
if (preserveSecured && depth > 0 && isSelfContainedSecuredDocument(jsonLd)) return jsonLd;
|
|
69
71
|
if (Array.isArray(jsonLd)) {
|
|
70
72
|
let normalized = null;
|
|
71
73
|
for (let i = 0; i < jsonLd.length; i++) {
|
|
72
74
|
const item = jsonLd[i];
|
|
73
|
-
const next = wrapScalarAttachments(item, depth + 1);
|
|
75
|
+
const next = wrapScalarAttachments(item, preserveSecured, depth + 1);
|
|
74
76
|
if (normalized == null && next !== item) normalized = jsonLd.slice(0, i);
|
|
75
77
|
if (normalized != null) normalized[i] = next;
|
|
76
78
|
}
|
|
@@ -83,7 +85,7 @@ function wrapScalarAttachments(jsonLd, depth = 0) {
|
|
|
83
85
|
for (let i = 0; i < keys.length; i++) {
|
|
84
86
|
const key = keys[i];
|
|
85
87
|
const value = record[key];
|
|
86
|
-
const next = key === "@context" || key === "@value" && isJsonLdValueObject(jsonLd) ? value : wrapScalarAttachments(value, depth + 1);
|
|
88
|
+
const next = key === "@context" || key === "@value" && isJsonLdValueObject(jsonLd) ? value : wrapScalarAttachments(value, preserveSecured, depth + 1);
|
|
87
89
|
const output = ATTACHMENT_FIELDS.has(key) && next != null && !Array.isArray(next) && !isJsonLdListObject(next) ? [next] : next;
|
|
88
90
|
if (normalized == null && output !== value) {
|
|
89
91
|
const cloned = Object.create(null);
|
|
@@ -166,8 +168,8 @@ function getLogSafeJsonLdMetadata(jsonLd) {
|
|
|
166
168
|
* the semantic-preservation check to succeed; otherwise canonicalization
|
|
167
169
|
* failures leave the original document unchanged.
|
|
168
170
|
*/
|
|
169
|
-
async function normalizeAttachmentArrays(jsonLd, contextLoader) {
|
|
170
|
-
const normalized = wrapScalarAttachments(jsonLd);
|
|
171
|
+
async function normalizeAttachmentArrays(jsonLd, contextLoader, options = {}) {
|
|
172
|
+
const normalized = wrapScalarAttachments(jsonLd, options.preserveNestedSecuredDocuments ?? false);
|
|
171
173
|
if (normalized === jsonLd) return jsonLd;
|
|
172
174
|
if (exceedsTraversalDepth(jsonLd)) {
|
|
173
175
|
logger.debug("Skipping attachment array normalization because the JSON-LD document exceeds the safe traversal depth; leaving it unchanged.");
|
|
@@ -195,9 +197,9 @@ async function normalizeAttachmentArrays(jsonLd, contextLoader) {
|
|
|
195
197
|
* to locally generated outgoing activities before they are signed, enqueued,
|
|
196
198
|
* or sent.
|
|
197
199
|
*/
|
|
198
|
-
async function normalizeOutgoingActivityJsonLd(jsonLd, contextLoader) {
|
|
199
|
-
jsonLd = await normalizePublicAudience(jsonLd, contextLoader);
|
|
200
|
-
return await normalizeAttachmentArrays(jsonLd, contextLoader);
|
|
200
|
+
async function normalizeOutgoingActivityJsonLd(jsonLd, contextLoader, options = {}) {
|
|
201
|
+
jsonLd = await normalizePublicAudience(jsonLd, contextLoader, options);
|
|
202
|
+
return await normalizeAttachmentArrays(jsonLd, contextLoader, options);
|
|
201
203
|
}
|
|
202
204
|
//#endregion
|
|
203
205
|
export { normalizeAttachmentArrays as n, normalizeOutgoingActivityJsonLd as r, isPreloadedContextAttachmentSafe as t };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { c as verifyKeyOwnership, n as fetchActorDocument, t as exportJwk } from "./key-
|
|
4
|
+
import { n as version, t as name } from "./deno-Vj_9wePq.mjs";
|
|
5
|
+
import { c as verifyKeyOwnership, n as fetchActorDocument, t as exportJwk } from "./key-B_Zc5Jbd.mjs";
|
|
6
6
|
import { getLogger } from "@logtape/logtape";
|
|
7
7
|
import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
|
|
8
8
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { I as name, L as version, c as fetchKey, f as validateCryptoKey, h as getFederationMetrics, m as getDurationMs, o as exportJwk, p as verifyKeyOwnership, s as fetchActorDocument, y as measureSignatureKeyFetch } from "./http-
|
|
3
|
+
import { I as name, L as version, c as fetchKey, f as validateCryptoKey, h as getFederationMetrics, m as getDurationMs, o as exportJwk, p as verifyKeyOwnership, s as fetchActorDocument, y as measureSignatureKeyFetch } from "./http-By8tnmEr.js";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
5
|
import { Activity, CryptographicKey, DataIntegrityProof, Multikey, Object as Object$1, PUBLIC_COLLECTION, getTypeId, isActor } from "@fedify/vocab";
|
|
6
6
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
7
7
|
import { encodeHex } from "byte-encodings/hex";
|
|
8
|
-
import { formatIri, getDocumentLoader, getFe34Origin, haveSameFe34Origin, parseIri, preloadedContexts } from "@fedify/vocab-runtime";
|
|
8
|
+
import { encodeMultibase, formatIri, getDocumentLoader, getFe34Origin, haveSameFe34Origin, parseIri, preloadedContexts } from "@fedify/vocab-runtime";
|
|
9
|
+
import { isPlainJsonTree, retainSignedRepresentation } from "@fedify/vocab-runtime/internal/signed-representation";
|
|
9
10
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
10
11
|
import serialize from "json-canon";
|
|
11
12
|
import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
|
|
@@ -28,6 +29,37 @@ const preloadedOnlyDocumentLoader = (url) => {
|
|
|
28
29
|
return Promise.reject(/* @__PURE__ */ new Error("Refusing to fetch a non-preloaded JSON-LD context: " + url));
|
|
29
30
|
};
|
|
30
31
|
//#endregion
|
|
32
|
+
//#region src/compat/secured-document.ts
|
|
33
|
+
function isJsonMap$1(value) {
|
|
34
|
+
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
35
|
+
}
|
|
36
|
+
function isNonEmptyString(value) {
|
|
37
|
+
return typeof value === "string" && value.length > 0;
|
|
38
|
+
}
|
|
39
|
+
function hasDataIntegrityProofType(value) {
|
|
40
|
+
return (Array.isArray(value) ? value : [value]).some((type) => type === "DataIntegrityProof" || type === "https://w3id.org/security#DataIntegrityProof");
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Checks whether a JSON value is a complete, self-contained secured document:
|
|
44
|
+
* a map with its own `@context` and exactly one direct, map-valued `proof`
|
|
45
|
+
* that carries every member `eddsa-jcs-2022` proof generation produces.
|
|
46
|
+
*
|
|
47
|
+
* This is deliberately stricter than {@link hasProofLike}, which exists to
|
|
48
|
+
* decide whether a document is worth verifying. Here a false positive would
|
|
49
|
+
* silently suppress a wire-compatibility fix on an ordinary document, so a
|
|
50
|
+
* partial or malformed proof does not qualify.
|
|
51
|
+
*
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
function isSelfContainedSecuredDocument(value) {
|
|
55
|
+
if (!isJsonMap$1(value)) return false;
|
|
56
|
+
if (!Object.hasOwn(value, "@context") || value["@context"] == null) return false;
|
|
57
|
+
if (!Object.hasOwn(value, "proof")) return false;
|
|
58
|
+
const proof = value.proof;
|
|
59
|
+
if (!isJsonMap$1(proof)) return false;
|
|
60
|
+
return hasDataIntegrityProofType(proof.type) && isNonEmptyString(proof.cryptosuite) && isNonEmptyString(proof.created) && isNonEmptyString(proof.verificationMethod) && isNonEmptyString(proof.proofPurpose) && isNonEmptyString(proof.proofValue);
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
31
63
|
//#region src/compat/public-audience.ts
|
|
32
64
|
const logger$4 = getLogger([
|
|
33
65
|
"fedify",
|
|
@@ -44,37 +76,39 @@ const PUBLIC_ADDRESSING_FIELDS = /* @__PURE__ */ new Set([
|
|
|
44
76
|
const AS_CONTEXT_URL$1 = "https://www.w3.org/ns/activitystreams";
|
|
45
77
|
const MAX_TRAVERSAL_DEPTH$1 = 64;
|
|
46
78
|
const KNOWN_SAFE_CONTEXT_URLS$1 = new Set(Object.keys(preloadedContexts));
|
|
47
|
-
function hasPublicCurieInAddressing(value, parentKey, depth = 0) {
|
|
79
|
+
function hasPublicCurieInAddressing(value, preserveSecured, parentKey, depth = 0) {
|
|
48
80
|
if (typeof value === "string") return parentKey != null && PUBLIC_ADDRESSING_FIELDS.has(parentKey) && (value === "as:Public" || value === "Public");
|
|
49
81
|
if (depth >= MAX_TRAVERSAL_DEPTH$1) return false;
|
|
50
|
-
if (Array.isArray(value)) return value.some((item) => hasPublicCurieInAddressing(item, parentKey, depth + 1));
|
|
82
|
+
if (Array.isArray(value)) return value.some((item) => hasPublicCurieInAddressing(item, preserveSecured, parentKey, depth + 1));
|
|
51
83
|
if (typeof value !== "object" || value == null) return false;
|
|
84
|
+
if (preserveSecured && depth > 0 && isSelfContainedSecuredDocument(value)) return false;
|
|
52
85
|
const record = value;
|
|
53
86
|
for (const key of Object.keys(record)) {
|
|
54
87
|
if (record.type === "Follow" && key === "object" && (record[key] === "as:Public" || record[key] === "Public")) return true;
|
|
55
88
|
if (key === "@context") continue;
|
|
56
|
-
if (hasPublicCurieInAddressing(record[key], key, depth + 1)) return true;
|
|
89
|
+
if (hasPublicCurieInAddressing(record[key], preserveSecured, key, depth + 1)) return true;
|
|
57
90
|
}
|
|
58
91
|
return false;
|
|
59
92
|
}
|
|
60
|
-
function rewritePublicAudience(value, parentKey, depth = 0) {
|
|
93
|
+
function rewritePublicAudience(value, preserveSecured, parentKey, depth = 0) {
|
|
61
94
|
if (typeof value === "string" && parentKey != null && PUBLIC_ADDRESSING_FIELDS.has(parentKey) && (value === "as:Public" || value === "Public")) return PUBLIC_COLLECTION.href;
|
|
62
95
|
if (depth >= MAX_TRAVERSAL_DEPTH$1) return value;
|
|
63
96
|
if (Array.isArray(value)) {
|
|
64
97
|
let changed = false;
|
|
65
98
|
const mapped = value.map((item) => {
|
|
66
|
-
const rewritten = rewritePublicAudience(item, parentKey, depth + 1);
|
|
99
|
+
const rewritten = rewritePublicAudience(item, preserveSecured, parentKey, depth + 1);
|
|
67
100
|
if (rewritten !== item) changed = true;
|
|
68
101
|
return rewritten;
|
|
69
102
|
});
|
|
70
103
|
return changed ? mapped : value;
|
|
71
104
|
}
|
|
72
105
|
if (typeof value !== "object" || value == null) return value;
|
|
106
|
+
if (preserveSecured && depth > 0 && isSelfContainedSecuredDocument(value)) return value;
|
|
73
107
|
const record = value;
|
|
74
108
|
let changed = false;
|
|
75
109
|
const normalized = Object.create(null);
|
|
76
110
|
for (const key of Object.keys(record)) {
|
|
77
|
-
const rewritten = key === "@context" ? record[key] : record.type === "Follow" && key === "object" && (record[key] === "as:Public" || record[key] === "Public") ? PUBLIC_COLLECTION.href : rewritePublicAudience(record[key], key, depth + 1);
|
|
111
|
+
const rewritten = key === "@context" ? record[key] : record.type === "Follow" && key === "object" && (record[key] === "as:Public" || record[key] === "Public") ? PUBLIC_COLLECTION.href : rewritePublicAudience(record[key], preserveSecured, key, depth + 1);
|
|
78
112
|
if (rewritten !== record[key]) changed = true;
|
|
79
113
|
normalized[key] = rewritten;
|
|
80
114
|
}
|
|
@@ -173,9 +207,10 @@ function hasKnownSafeContext$1(jsonLd) {
|
|
|
173
207
|
* using the `eddsa-jcs-2022` cryptosuite), so the signed payload
|
|
174
208
|
* matches what is sent on the wire.
|
|
175
209
|
*/
|
|
176
|
-
async function normalizePublicAudience(jsonLd, contextLoader) {
|
|
177
|
-
|
|
178
|
-
|
|
210
|
+
async function normalizePublicAudience(jsonLd, contextLoader, options = {}) {
|
|
211
|
+
const preserveSecured = options.preserveNestedSecuredDocuments ?? false;
|
|
212
|
+
if (!hasPublicCurieInAddressing(jsonLd, preserveSecured)) return jsonLd;
|
|
213
|
+
const normalized = rewritePublicAudience(jsonLd, preserveSecured);
|
|
179
214
|
if (hasKnownSafeContext$1(jsonLd)) return normalized;
|
|
180
215
|
const loader = contextLoader ?? preloadedOnlyDocumentLoader;
|
|
181
216
|
try {
|
|
@@ -253,13 +288,14 @@ function getKnownSafeContextUrls() {
|
|
|
253
288
|
/**
|
|
254
289
|
* Wraps scalar ActivityStreams attachment properties in arrays.
|
|
255
290
|
*/
|
|
256
|
-
function wrapScalarAttachments(jsonLd, depth = 0) {
|
|
291
|
+
function wrapScalarAttachments(jsonLd, preserveSecured, depth = 0) {
|
|
257
292
|
if (depth >= MAX_TRAVERSAL_DEPTH) return jsonLd;
|
|
293
|
+
if (preserveSecured && depth > 0 && isSelfContainedSecuredDocument(jsonLd)) return jsonLd;
|
|
258
294
|
if (Array.isArray(jsonLd)) {
|
|
259
295
|
let normalized = null;
|
|
260
296
|
for (let i = 0; i < jsonLd.length; i++) {
|
|
261
297
|
const item = jsonLd[i];
|
|
262
|
-
const next = wrapScalarAttachments(item, depth + 1);
|
|
298
|
+
const next = wrapScalarAttachments(item, preserveSecured, depth + 1);
|
|
263
299
|
if (normalized == null && next !== item) normalized = jsonLd.slice(0, i);
|
|
264
300
|
if (normalized != null) normalized[i] = next;
|
|
265
301
|
}
|
|
@@ -272,7 +308,7 @@ function wrapScalarAttachments(jsonLd, depth = 0) {
|
|
|
272
308
|
for (let i = 0; i < keys.length; i++) {
|
|
273
309
|
const key = keys[i];
|
|
274
310
|
const value = record[key];
|
|
275
|
-
const next = key === "@context" || key === "@value" && isJsonLdValueObject(jsonLd) ? value : wrapScalarAttachments(value, depth + 1);
|
|
311
|
+
const next = key === "@context" || key === "@value" && isJsonLdValueObject(jsonLd) ? value : wrapScalarAttachments(value, preserveSecured, depth + 1);
|
|
276
312
|
const output = ATTACHMENT_FIELDS.has(key) && next != null && !Array.isArray(next) && !isJsonLdListObject(next) ? [next] : next;
|
|
277
313
|
if (normalized == null && output !== value) {
|
|
278
314
|
const cloned = Object.create(null);
|
|
@@ -355,8 +391,8 @@ function getLogSafeJsonLdMetadata(jsonLd) {
|
|
|
355
391
|
* the semantic-preservation check to succeed; otherwise canonicalization
|
|
356
392
|
* failures leave the original document unchanged.
|
|
357
393
|
*/
|
|
358
|
-
async function normalizeAttachmentArrays(jsonLd, contextLoader) {
|
|
359
|
-
const normalized = wrapScalarAttachments(jsonLd);
|
|
394
|
+
async function normalizeAttachmentArrays(jsonLd, contextLoader, options = {}) {
|
|
395
|
+
const normalized = wrapScalarAttachments(jsonLd, options.preserveNestedSecuredDocuments ?? false);
|
|
360
396
|
if (normalized === jsonLd) return jsonLd;
|
|
361
397
|
if (exceedsTraversalDepth(jsonLd)) {
|
|
362
398
|
logger$3.debug("Skipping attachment array normalization because the JSON-LD document exceeds the safe traversal depth; leaving it unchanged.");
|
|
@@ -384,9 +420,9 @@ async function normalizeAttachmentArrays(jsonLd, contextLoader) {
|
|
|
384
420
|
* to locally generated outgoing activities before they are signed, enqueued,
|
|
385
421
|
* or sent.
|
|
386
422
|
*/
|
|
387
|
-
async function normalizeOutgoingActivityJsonLd(jsonLd, contextLoader) {
|
|
388
|
-
jsonLd = await normalizePublicAudience(jsonLd, contextLoader);
|
|
389
|
-
return await normalizeAttachmentArrays(jsonLd, contextLoader);
|
|
423
|
+
async function normalizeOutgoingActivityJsonLd(jsonLd, contextLoader, options = {}) {
|
|
424
|
+
jsonLd = await normalizePublicAudience(jsonLd, contextLoader, options);
|
|
425
|
+
return await normalizeAttachmentArrays(jsonLd, contextLoader, options);
|
|
390
426
|
}
|
|
391
427
|
//#endregion
|
|
392
428
|
//#region src/sig/ld.ts
|
|
@@ -1055,17 +1091,49 @@ function hasProofLike(jsonLd) {
|
|
|
1055
1091
|
};
|
|
1056
1092
|
return Array.isArray(proof) ? proof.some(isProofLike) : isProofLike(proof);
|
|
1057
1093
|
}
|
|
1094
|
+
function isJsonMap(value) {
|
|
1095
|
+
return typeof value === "object" && value != null && !Array.isArray(value);
|
|
1096
|
+
}
|
|
1058
1097
|
/**
|
|
1059
|
-
*
|
|
1060
|
-
*
|
|
1061
|
-
*
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1064
|
-
* @
|
|
1065
|
-
*
|
|
1066
|
-
*
|
|
1098
|
+
* Assembles the secured JSON document from the bytes that were just signed.
|
|
1099
|
+
*
|
|
1100
|
+
* The document is not re-derived from the vocabulary object: it is
|
|
1101
|
+
* `compactMsg`, the value whose JCS form was hashed, with the serialized
|
|
1102
|
+
* proof added. Every part of it is then checked against the digests and the
|
|
1103
|
+
* signature that {@link createProofInternal} produced, so a document that is
|
|
1104
|
+
* returned verifies under the map-local compound-proof profile by
|
|
1105
|
+
* construction. Anything that does not check out yields `null`, and the
|
|
1106
|
+
* caller simply does not retain a representation.
|
|
1067
1107
|
*/
|
|
1068
|
-
async function
|
|
1108
|
+
async function captureSecuredDocument(proof, compactMsg, msgCanon, proofCanon, proofValue, contextLoader) {
|
|
1109
|
+
if (!isJsonMap(compactMsg)) return null;
|
|
1110
|
+
const documentContext = compactMsg["@context"];
|
|
1111
|
+
if (documentContext == null) return null;
|
|
1112
|
+
if (globalThis.Object.hasOwn(compactMsg, "proof")) return null;
|
|
1113
|
+
try {
|
|
1114
|
+
const proofJson = await proof.toJsonLd({
|
|
1115
|
+
format: "compact",
|
|
1116
|
+
contextLoader,
|
|
1117
|
+
context: documentContext
|
|
1118
|
+
});
|
|
1119
|
+
if (!isJsonMap(proofJson)) return null;
|
|
1120
|
+
const { proofValue: serializedProofValue, ...proofConfiguration } = proofJson;
|
|
1121
|
+
if (serializedProofValue !== proofValue) return null;
|
|
1122
|
+
if (serialize(proofConfiguration) !== proofCanon) return null;
|
|
1123
|
+
const securedDocument = {
|
|
1124
|
+
...structuredClone(compactMsg),
|
|
1125
|
+
proof: structuredClone(proofJson)
|
|
1126
|
+
};
|
|
1127
|
+
const { proof: _embeddedProof, ...unsecuredDocument } = securedDocument;
|
|
1128
|
+
if (serialize(unsecuredDocument) !== msgCanon) return null;
|
|
1129
|
+
if (!isPlainJsonTree(securedDocument)) return null;
|
|
1130
|
+
return securedDocument;
|
|
1131
|
+
} catch (error) {
|
|
1132
|
+
logger$1.debug("Failed to capture the secured JSON document for a created proof; the signed object will not preserve its representation when it is embedded in another object.\n{error}", { error });
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
async function createProofInternal(object, privateKey, keyId, { contextLoader, context, created } = {}, capture = false) {
|
|
1069
1137
|
validateCryptoKey(privateKey, "private");
|
|
1070
1138
|
if (privateKey.algorithm.name !== "Ed25519") throw new TypeError("Unsupported algorithm: " + privateKey.algorithm.name);
|
|
1071
1139
|
let compactMsg = await object.clone({ proofs: [] }).toJsonLd({
|
|
@@ -1073,7 +1141,7 @@ async function createProof(object, privateKey, keyId, { contextLoader, context,
|
|
|
1073
1141
|
contextLoader,
|
|
1074
1142
|
context
|
|
1075
1143
|
});
|
|
1076
|
-
compactMsg = await normalizeOutgoingActivityJsonLd(compactMsg, contextLoader);
|
|
1144
|
+
compactMsg = await normalizeOutgoingActivityJsonLd(compactMsg, contextLoader, { preserveNestedSecuredDocuments: true });
|
|
1077
1145
|
const msgCanon = serialize(compactMsg);
|
|
1078
1146
|
const encoder = new TextEncoder();
|
|
1079
1147
|
const msgBytes = encoder.encode(msgCanon);
|
|
@@ -1092,14 +1160,32 @@ async function createProof(object, privateKey, keyId, { contextLoader, context,
|
|
|
1092
1160
|
const digest = new Uint8Array(proofDigest.byteLength + msgDigest.byteLength);
|
|
1093
1161
|
digest.set(new Uint8Array(proofDigest), 0);
|
|
1094
1162
|
digest.set(new Uint8Array(msgDigest), proofDigest.byteLength);
|
|
1095
|
-
const sig = await crypto.subtle.sign("Ed25519", privateKey, digest);
|
|
1096
|
-
|
|
1163
|
+
const sig = new Uint8Array(await crypto.subtle.sign("Ed25519", privateKey, digest));
|
|
1164
|
+
const proof = new DataIntegrityProof({
|
|
1097
1165
|
cryptosuite: "eddsa-jcs-2022",
|
|
1098
1166
|
verificationMethod: keyId,
|
|
1099
1167
|
proofPurpose: "assertionMethod",
|
|
1100
|
-
created
|
|
1101
|
-
proofValue:
|
|
1168
|
+
created,
|
|
1169
|
+
proofValue: sig
|
|
1102
1170
|
});
|
|
1171
|
+
return {
|
|
1172
|
+
proof,
|
|
1173
|
+
securedDocument: capture ? await captureSecuredDocument(proof, compactMsg, msgCanon, proofCanon, new TextDecoder().decode(encodeMultibase("base58btc", sig)), contextLoader) : null
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Creates a proof for the given object.
|
|
1178
|
+
* @param object The object to create a proof for.
|
|
1179
|
+
* @param privateKey The private key to sign the proof with.
|
|
1180
|
+
* @param keyId The key ID to use in the proof. It will be used by the verifier.
|
|
1181
|
+
* @param options Additional options. See also {@link CreateProofOptions}.
|
|
1182
|
+
* @returns The created proof.
|
|
1183
|
+
* @throws {TypeError} If the private key is invalid or unsupported.
|
|
1184
|
+
* @since 0.10.0
|
|
1185
|
+
*/
|
|
1186
|
+
async function createProof(object, privateKey, keyId, options = {}) {
|
|
1187
|
+
const { proof } = await createProofInternal(object, privateKey, keyId, options);
|
|
1188
|
+
return proof;
|
|
1103
1189
|
}
|
|
1104
1190
|
/**
|
|
1105
1191
|
* Signs the given object with the private key and returns the signed object.
|
|
@@ -1117,13 +1203,19 @@ async function signObject(object, privateKey, keyId, options = {}) {
|
|
|
1117
1203
|
if (object.id != null) span.setAttribute("activitypub.object.id", object.id.href);
|
|
1118
1204
|
const existingProofs = [];
|
|
1119
1205
|
for await (const proof of object.getProofs(options)) existingProofs.push(proof);
|
|
1120
|
-
const proof = await
|
|
1206
|
+
const { proof, securedDocument } = await createProofInternal(object, privateKey, keyId, options, existingProofs.length < 1);
|
|
1121
1207
|
if (span.isRecording()) {
|
|
1122
1208
|
if (proof.cryptosuite != null) span.setAttribute("object_integrity_proofs.cryptosuite", proof.cryptosuite);
|
|
1123
1209
|
if (proof.verificationMethodId != null) span.setAttribute("object_integrity_proofs.key_id", proof.verificationMethodId.href);
|
|
1124
1210
|
if (proof.proofValue != null) span.setAttribute("object_integrity_proofs.signature", encodeHex(proof.proofValue));
|
|
1125
1211
|
}
|
|
1126
|
-
|
|
1212
|
+
const signed = object.clone({ proofs: [...existingProofs, proof] });
|
|
1213
|
+
if (securedDocument != null) retainSignedRepresentation(signed, securedDocument);
|
|
1214
|
+
else if (existingProofs.length > 0) logger$1.debug("The object {objectId} already had {proofCount} proof(s), so its signed representation is not retained for embedding; the map-local compound-proof profile accepts exactly one direct proof per map.", {
|
|
1215
|
+
objectId: object.id?.href,
|
|
1216
|
+
proofCount: existingProofs.length
|
|
1217
|
+
});
|
|
1218
|
+
return signed;
|
|
1127
1219
|
} catch (error) {
|
|
1128
1220
|
span.setStatus({
|
|
1129
1221
|
code: SpanStatusCode.ERROR,
|