@fedify/fedify 2.2.0-pr.715.28 → 2.2.0-pr.731.34
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_strict_equals-Dmjbg-bA.mjs +41 -0
- package/dist/{builder-BfmqkrIE.mjs → builder-OFkoAQ85.mjs} +3 -3
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/outgoing-jsonld.test.d.mts +2 -0
- package/dist/compat/outgoing-jsonld.test.mjs +189 -0
- package/dist/compat/public-audience.test.d.mts +2 -0
- package/dist/compat/public-audience.test.mjs +178 -0
- package/dist/compat/transformers.test.mjs +3 -3
- package/dist/{context-BGrYMSTk.d.ts → context-BzH2-ajs.d.ts} +22 -0
- package/dist/{context-CMUd4wy0.d.cts → context-DJGagtNd.d.cts} +22 -0
- package/dist/{deno-CjZGHpS5.mjs → deno-BYRjHaeb.mjs} +1 -1
- package/dist/{docloader-Du9_2PyA.mjs → docloader-CuVh02a1.mjs} +2 -2
- package/dist/federation/builder.test.mjs +3 -3
- package/dist/federation/collection.test.mjs +2 -2
- package/dist/federation/handler.test.mjs +8 -8
- package/dist/federation/idempotency.test.mjs +5 -5
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/keycache.test.mjs +3 -3
- package/dist/federation/kv.test.mjs +2 -2
- package/dist/federation/middleware.test.mjs +160 -10
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +2 -2
- package/dist/federation/mod.d.ts +2 -2
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +17 -10
- package/dist/federation/negotiation.test.mjs +3 -3
- package/dist/federation/retry.test.mjs +1 -1
- package/dist/federation/router.test.mjs +2 -2
- package/dist/federation/send.test.mjs +6 -6
- package/dist/federation/webfinger.test.mjs +3 -3
- package/dist/{http-9zjtsC0n.mjs → http-BrVfkREl.mjs} +3 -3
- package/dist/{http-Wm7tvhRa.cjs → http-H-4FzBb3.cjs} +1 -1
- package/dist/{http-C8puyZ4Z.js → http-pZce7PcA.js} +1 -1
- package/dist/{key-CMhIdO1-.mjs → key-cK-YTNKa.mjs} +1 -1
- package/dist/{kv-cache-BTq6qqgJ.js → kv-cache-CjweM0Am.js} +1 -1
- package/dist/{kv-cache-DyRjARFV.cjs → kv-cache-NgzTkTjv.cjs} +1 -1
- package/dist/{ld-BH-6muxq.mjs → ld-SXDkzuUo.mjs} +2 -2
- package/dist/{middleware-CO5RpqOP.mjs → middleware-AqjYcDmu.mjs} +1 -1
- package/dist/{middleware-DBgitte6.mjs → middleware-BMePykoo.mjs} +34 -22
- package/dist/{middleware-D3S_Ctwx.cjs → middleware-CXhCkBG4.cjs} +1 -1
- package/dist/{middleware-C_CRggCg.cjs → middleware-TZHT1gKk.cjs} +20 -9
- package/dist/{middleware-Da-j6H9U.js → middleware-YcQIUUHs.js} +19 -8
- package/dist/{mod-CJXfyw7v.d.ts → mod-2d12ffz3.d.ts} +1 -1
- package/dist/{mod-BcJHeuv1.d.cts → mod-D35TRn09.d.cts} +1 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +2 -2
- package/dist/mod.d.ts +2 -2
- 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 +2 -2
- package/dist/otel/exporter.test.mjs +2 -2
- package/dist/outgoing-jsonld-CNmZLixq.mjs +203 -0
- package/dist/{owner-CE5FwvNR.mjs → owner-Cudh-ej0.mjs} +2 -2
- package/dist/{proof-jHDv7IKD.js → proof-BJrEACyu.js} +425 -41
- package/dist/{proof-D-HuDCQe.cjs → proof-CFERN43j.cjs} +428 -38
- package/dist/{proof-Ds3T1-sE.mjs → proof-DC69vtxY.mjs} +38 -31
- package/dist/public-audience-DYFHzm_c.mjs +192 -0
- package/dist/{send-TicMA6nJ.mjs → send-BBRG1CKC.mjs} +2 -2
- package/dist/sig/accept.test.mjs +1 -1
- package/dist/sig/http.test.mjs +5 -5
- package/dist/sig/key.test.mjs +3 -3
- package/dist/sig/ld.test.mjs +4 -4
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +4 -4
- package/dist/sig/proof.test.mjs +78 -5
- package/dist/{std__assert-Duiq_YC9.mjs → std__assert-CRDpx_HF.mjs} +3 -38
- package/dist/testing/mod.d.mts +22 -0
- package/dist/utils/docloader.test.mjs +4 -4
- 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 +5 -5
- /package/dist/{accept-Dd__NiUL.mjs → accept-CPkZzmGN.mjs} +0 -0
- /package/dist/{activity-listener-Ck3JZ_hR.mjs → activity-listener-ell7W1s9.mjs} +0 -0
- /package/dist/{assert-ddO5KLpe.mjs → assert-DikXweDx.mjs} +0 -0
- /package/dist/{client-DEpOVgY1.mjs → client-D_1QpnWt.mjs} +0 -0
- /package/dist/{collection-BD6-SZ6O.mjs → collection-D-HqUuA2.mjs} +0 -0
- /package/dist/{keycache-CCSwkQcY.mjs → keycache-EGATflN-.mjs} +0 -0
- /package/dist/{keys-BAK-tUlf.mjs → keys-DGu1NFwu.mjs} +0 -0
- /package/dist/{kv-cache-B01V7s3h.mjs → kv-cache-U__xU4qR.mjs} +0 -0
- /package/dist/{kv-tL2TOE9X.mjs → kv-rV3vodCc.mjs} +0 -0
- /package/dist/{negotiation-DnsfFF8I.mjs → negotiation-SQvQgUqe.mjs} +0 -0
- /package/dist/{retry-B_E3V_Dx.mjs → retry-bMXBL97A.mjs} +0 -0
- /package/dist/{types-DCP0WLdt.mjs → types-J53Kw7so.mjs} +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { n as fetchKey, o as validateCryptoKey } from "./key-
|
|
4
|
+
import { n as version, t as name } from "./deno-BYRjHaeb.mjs";
|
|
5
|
+
import { n as fetchKey, o as validateCryptoKey } from "./key-cK-YTNKa.mjs";
|
|
6
|
+
import { n as preloadedOnlyDocumentLoader } from "./public-audience-DYFHzm_c.mjs";
|
|
7
|
+
import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-CNmZLixq.mjs";
|
|
6
8
|
import { Activity, DataIntegrityProof, Multikey, getTypeId } from "@fedify/vocab";
|
|
7
9
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
8
10
|
import { getLogger } from "@logtape/logtape";
|
|
@@ -56,11 +58,12 @@ function hasProofLike(jsonLd) {
|
|
|
56
58
|
async function createProof(object, privateKey, keyId, { contextLoader, context, created } = {}) {
|
|
57
59
|
validateCryptoKey(privateKey, "private");
|
|
58
60
|
if (privateKey.algorithm.name !== "Ed25519") throw new TypeError("Unsupported algorithm: " + privateKey.algorithm.name);
|
|
59
|
-
|
|
61
|
+
let compactMsg = await object.clone({ proofs: [] }).toJsonLd({
|
|
60
62
|
format: "compact",
|
|
61
63
|
contextLoader,
|
|
62
64
|
context
|
|
63
65
|
});
|
|
66
|
+
compactMsg = await normalizeOutgoingActivityJsonLd(compactMsg, contextLoader);
|
|
64
67
|
const msgCanon = serialize(compactMsg);
|
|
65
68
|
const encoder = new TextEncoder();
|
|
66
69
|
const msgBytes = encoder.encode(msgCanon);
|
|
@@ -155,27 +158,22 @@ async function verifyProof(jsonLd, proof, options = {}) {
|
|
|
155
158
|
});
|
|
156
159
|
}
|
|
157
160
|
async function verifyProofInternal(jsonLd, proof, options) {
|
|
158
|
-
if (typeof jsonLd !== "object" || proof.cryptosuite !== "eddsa-jcs-2022" || proof.verificationMethodId == null || proof.proofPurpose !== "assertionMethod" || proof.proofValue == null || proof.created == null) return null;
|
|
161
|
+
if (typeof jsonLd !== "object" || jsonLd == null || Array.isArray(jsonLd) || proof.cryptosuite !== "eddsa-jcs-2022" || proof.verificationMethodId == null || proof.proofPurpose !== "assertionMethod" || proof.proofValue == null || proof.created == null) return null;
|
|
159
162
|
const publicKeyPromise = fetchKey(proof.verificationMethodId, Multikey, options);
|
|
160
|
-
const
|
|
163
|
+
const proofConfig = {
|
|
161
164
|
"@context": jsonLd["@context"],
|
|
162
165
|
type: "DataIntegrityProof",
|
|
163
166
|
cryptosuite: proof.cryptosuite,
|
|
164
167
|
verificationMethod: proof.verificationMethodId.href,
|
|
165
168
|
proofPurpose: proof.proofPurpose,
|
|
166
169
|
created: proof.created.toString()
|
|
167
|
-
}
|
|
170
|
+
};
|
|
168
171
|
const encoder = new TextEncoder();
|
|
169
|
-
const proofBytes = encoder.encode(
|
|
172
|
+
const proofBytes = encoder.encode(serialize(proofConfig));
|
|
170
173
|
const proofDigest = await crypto.subtle.digest("SHA-256", proofBytes);
|
|
171
174
|
const msg = { ...jsonLd };
|
|
172
175
|
if ("proof" in msg) delete msg.proof;
|
|
173
|
-
|
|
174
|
-
const msgBytes = encoder.encode(msgCanon);
|
|
175
|
-
const msgDigest = await crypto.subtle.digest("SHA-256", msgBytes);
|
|
176
|
-
const digest = new Uint8Array(proofDigest.byteLength + msgDigest.byteLength);
|
|
177
|
-
digest.set(new Uint8Array(proofDigest), 0);
|
|
178
|
-
digest.set(new Uint8Array(msgDigest), proofDigest.byteLength);
|
|
176
|
+
if ("https://w3id.org/security#proof" in msg) delete msg["https://w3id.org/security#proof"];
|
|
179
177
|
let fetchedKey;
|
|
180
178
|
try {
|
|
181
179
|
fetchedKey = await publicKeyPromise;
|
|
@@ -204,7 +202,7 @@ async function verifyProofInternal(jsonLd, proof, options) {
|
|
|
204
202
|
return await verifyProof(jsonLd, proof, {
|
|
205
203
|
...options,
|
|
206
204
|
keyCache: {
|
|
207
|
-
get: () => Promise.resolve(
|
|
205
|
+
get: () => Promise.resolve(void 0),
|
|
208
206
|
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
209
207
|
}
|
|
210
208
|
});
|
|
@@ -215,27 +213,36 @@ async function verifyProofInternal(jsonLd, proof, options) {
|
|
|
215
213
|
});
|
|
216
214
|
return null;
|
|
217
215
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
logger.debug("Failed to verify the proof with the fetched key {keyId}:\n{proof}", {
|
|
216
|
+
const digest = new Uint8Array(proofDigest.byteLength + 32);
|
|
217
|
+
digest.set(new Uint8Array(proofDigest), 0);
|
|
218
|
+
const proofValue = proof.proofValue;
|
|
219
|
+
const verifyCandidate = async (candidate) => {
|
|
220
|
+
const msgBytes = encoder.encode(serialize(candidate));
|
|
221
|
+
const msgDigest = await crypto.subtle.digest("SHA-256", msgBytes);
|
|
222
|
+
digest.set(new Uint8Array(msgDigest), proofDigest.byteLength);
|
|
223
|
+
return await crypto.subtle.verify("Ed25519", publicKey.publicKey, proofValue.slice(), digest);
|
|
224
|
+
};
|
|
225
|
+
if (await verifyCandidate(msg)) return publicKey;
|
|
226
|
+
const normalized = await normalizeOutgoingActivityJsonLd(msg, preloadedOnlyDocumentLoader);
|
|
227
|
+
if (normalized !== msg && await verifyCandidate(normalized)) return publicKey;
|
|
228
|
+
if (fetchedKey.cached) {
|
|
229
|
+
logger.debug("Failed to verify the proof with the cached key {keyId}; retrying with the freshly fetched key...", {
|
|
233
230
|
keyId: proof.verificationMethodId.href,
|
|
234
231
|
proof
|
|
235
232
|
});
|
|
236
|
-
return
|
|
233
|
+
return await verifyProof(jsonLd, proof, {
|
|
234
|
+
...options,
|
|
235
|
+
keyCache: {
|
|
236
|
+
get: () => Promise.resolve(void 0),
|
|
237
|
+
set: async (keyId, key) => await options.keyCache?.set(keyId, key)
|
|
238
|
+
}
|
|
239
|
+
});
|
|
237
240
|
}
|
|
238
|
-
|
|
241
|
+
logger.debug("Failed to verify the proof with the fetched key {keyId}:\n{proof}", {
|
|
242
|
+
keyId: proof.verificationMethodId.href,
|
|
243
|
+
proof
|
|
244
|
+
});
|
|
245
|
+
return null;
|
|
239
246
|
}
|
|
240
247
|
/**
|
|
241
248
|
* Verifies the given object. It will verify all the proofs in the object,
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import "@js-temporal/polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { PUBLIC_COLLECTION } from "@fedify/vocab";
|
|
5
|
+
import { preloadedContexts } from "@fedify/vocab-runtime";
|
|
6
|
+
import { getLogger } from "@logtape/logtape";
|
|
7
|
+
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
8
|
+
//#region src/compat/preloaded-context-loader.ts
|
|
9
|
+
/**
|
|
10
|
+
* A restricted JSON-LD document loader that resolves only contexts bundled
|
|
11
|
+
* with Fedify.
|
|
12
|
+
*
|
|
13
|
+
* This is intentionally narrower than `getDocumentLoader()`: normalization
|
|
14
|
+
* helpers are also reached from verification paths that operate on inbound,
|
|
15
|
+
* attacker-controlled JSON-LD, so the default fallback must never fetch
|
|
16
|
+
* attacker-supplied context URLs.
|
|
17
|
+
*/
|
|
18
|
+
const preloadedOnlyDocumentLoader = (url) => {
|
|
19
|
+
if (Object.hasOwn(preloadedContexts, url)) return Promise.resolve({
|
|
20
|
+
contextUrl: null,
|
|
21
|
+
documentUrl: url,
|
|
22
|
+
document: preloadedContexts[url]
|
|
23
|
+
});
|
|
24
|
+
return Promise.reject(/* @__PURE__ */ new Error("Refusing to fetch a non-preloaded JSON-LD context: " + url));
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/compat/public-audience.ts
|
|
28
|
+
const logger = getLogger([
|
|
29
|
+
"fedify",
|
|
30
|
+
"compat",
|
|
31
|
+
"public-audience"
|
|
32
|
+
]);
|
|
33
|
+
const PUBLIC_ADDRESSING_FIELDS = new Set([
|
|
34
|
+
"to",
|
|
35
|
+
"cc",
|
|
36
|
+
"bto",
|
|
37
|
+
"bcc",
|
|
38
|
+
"audience"
|
|
39
|
+
]);
|
|
40
|
+
const AS_CONTEXT_URL = "https://www.w3.org/ns/activitystreams";
|
|
41
|
+
const MAX_TRAVERSAL_DEPTH = 64;
|
|
42
|
+
const KNOWN_SAFE_CONTEXT_URLS = new Set(Object.keys(preloadedContexts));
|
|
43
|
+
function hasPublicCurieInAddressing(value, parentKey, depth = 0) {
|
|
44
|
+
if (typeof value === "string") return parentKey != null && PUBLIC_ADDRESSING_FIELDS.has(parentKey) && (value === "as:Public" || value === "Public");
|
|
45
|
+
if (depth >= MAX_TRAVERSAL_DEPTH) return false;
|
|
46
|
+
if (Array.isArray(value)) return value.some((item) => hasPublicCurieInAddressing(item, parentKey, depth + 1));
|
|
47
|
+
if (typeof value !== "object" || value == null) return false;
|
|
48
|
+
const record = value;
|
|
49
|
+
for (const key of Object.keys(record)) {
|
|
50
|
+
if (key === "@context") continue;
|
|
51
|
+
if (hasPublicCurieInAddressing(record[key], key, depth + 1)) return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
function rewritePublicAudience(value, parentKey, depth = 0) {
|
|
56
|
+
if (typeof value === "string" && parentKey != null && PUBLIC_ADDRESSING_FIELDS.has(parentKey) && (value === "as:Public" || value === "Public")) return PUBLIC_COLLECTION.href;
|
|
57
|
+
if (depth >= MAX_TRAVERSAL_DEPTH) return value;
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
let changed = false;
|
|
60
|
+
const mapped = value.map((item) => {
|
|
61
|
+
const rewritten = rewritePublicAudience(item, parentKey, depth + 1);
|
|
62
|
+
if (rewritten !== item) changed = true;
|
|
63
|
+
return rewritten;
|
|
64
|
+
});
|
|
65
|
+
return changed ? mapped : value;
|
|
66
|
+
}
|
|
67
|
+
if (typeof value !== "object" || value == null) return value;
|
|
68
|
+
const record = value;
|
|
69
|
+
let changed = false;
|
|
70
|
+
const normalized = Object.create(null);
|
|
71
|
+
for (const key of Object.keys(record)) {
|
|
72
|
+
const rewritten = key === "@context" ? record[key] : rewritePublicAudience(record[key], key, depth + 1);
|
|
73
|
+
if (rewritten !== record[key]) changed = true;
|
|
74
|
+
normalized[key] = rewritten;
|
|
75
|
+
}
|
|
76
|
+
return changed ? normalized : value;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Reports whether `value` carries an `@context` property anywhere inside
|
|
80
|
+
* its subtree (not counting the value itself). A nested `@context` can
|
|
81
|
+
* introduce a local term-definition scope that redefines `as:` or `Public`
|
|
82
|
+
* even when the top-level `@context` is safe, so the fast path must defer
|
|
83
|
+
* to the URDNA2015 equivalence check whenever one is present.
|
|
84
|
+
*/
|
|
85
|
+
function hasNestedContext(value, depth = 0) {
|
|
86
|
+
if (depth >= MAX_TRAVERSAL_DEPTH) return true;
|
|
87
|
+
if (Array.isArray(value)) return value.some((item) => hasNestedContext(item, depth + 1));
|
|
88
|
+
if (typeof value !== "object" || value == null) return false;
|
|
89
|
+
const record = value;
|
|
90
|
+
for (const key of Object.keys(record)) {
|
|
91
|
+
if (key === "@context") return true;
|
|
92
|
+
if (hasNestedContext(record[key], depth + 1)) return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Checks whether the `@context` of a JSON-LD document is guaranteed not
|
|
98
|
+
* to redefine the `as:` prefix or the bare `Public` term. Only documents
|
|
99
|
+
* whose `@context` is a string, or an array of strings, drawn from Fedify's
|
|
100
|
+
* preloaded context set AND including the ActivityStreams URL qualify,
|
|
101
|
+
* AND no nested subtree carries its own `@context` that might redefine
|
|
102
|
+
* those terms within a local scope. When all of that holds the rewrite
|
|
103
|
+
* is provably semantics-preserving and the URDNA2015 equivalence check
|
|
104
|
+
* can be skipped. Any other shape (unknown external URLs, inline
|
|
105
|
+
* objects at the top level, nested `@context` blocks) is treated as
|
|
106
|
+
* potentially unsafe.
|
|
107
|
+
*/
|
|
108
|
+
function hasKnownSafeContext(jsonLd) {
|
|
109
|
+
if (typeof jsonLd !== "object" || jsonLd == null) return false;
|
|
110
|
+
const record = jsonLd;
|
|
111
|
+
if (!Object.hasOwn(record, "@context")) return false;
|
|
112
|
+
const ctx = record["@context"];
|
|
113
|
+
const entries = typeof ctx === "string" ? [ctx] : Array.isArray(ctx) ? ctx : null;
|
|
114
|
+
if (entries == null || entries.length === 0) return false;
|
|
115
|
+
let hasAs = false;
|
|
116
|
+
for (const entry of entries) {
|
|
117
|
+
if (typeof entry !== "string") return false;
|
|
118
|
+
if (!KNOWN_SAFE_CONTEXT_URLS.has(entry)) return false;
|
|
119
|
+
if (entry === AS_CONTEXT_URL) hasAs = true;
|
|
120
|
+
}
|
|
121
|
+
if (!hasAs) return false;
|
|
122
|
+
for (const key of Object.keys(record)) {
|
|
123
|
+
if (key === "@context") continue;
|
|
124
|
+
if (hasNestedContext(record[key])) return false;
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Rewrites the compact `as:Public` / `Public` CURIE appearing in activity
|
|
130
|
+
* addressing fields (`to`, `cc`, `bto`, `bcc`, `audience`) to the fully
|
|
131
|
+
* expanded `https://www.w3.org/ns/activitystreams#Public` URI.
|
|
132
|
+
*
|
|
133
|
+
* Several ActivityPub implementations, Lemmy among them, match these
|
|
134
|
+
* fields as plain URLs without running JSON-LD expansion, and silently
|
|
135
|
+
* drop activities whose public addressing appears in CURIE form. This
|
|
136
|
+
* helper works around that gap.
|
|
137
|
+
*
|
|
138
|
+
* For documents whose `@context` is drawn entirely from Fedify's
|
|
139
|
+
* preloaded context set and includes the ActivityStreams URL, the
|
|
140
|
+
* rewrite is applied directly: the content of every preloaded non-AS
|
|
141
|
+
* context is known not to redefine the `as:` prefix or the bare `Public`
|
|
142
|
+
* term, so the semantics are preserved by construction. Any other
|
|
143
|
+
* shape (an inline object, an unknown external URL, and so on) is
|
|
144
|
+
* treated as potentially unsafe and gated on a JSON-LD equivalence
|
|
145
|
+
* check; both forms are canonicalized with URDNA2015 and the resulting
|
|
146
|
+
* N-Quads are compared. When they differ, the original document is
|
|
147
|
+
* returned unchanged. Canonicalization failures also fall back to the
|
|
148
|
+
* original document.
|
|
149
|
+
*
|
|
150
|
+
* When no `contextLoader` is supplied the helper falls back to an
|
|
151
|
+
* internal loader that resolves only the URLs in Fedify's
|
|
152
|
+
* preloaded-contexts set and rejects every other URL without issuing a
|
|
153
|
+
* network request. That behaviour is deliberately narrower than
|
|
154
|
+
* `@fedify/vocab-runtime`'s `getDocumentLoader()`, which after its
|
|
155
|
+
* `validatePublicUrl` check will happily fetch non-preloaded URLs: the
|
|
156
|
+
* helper is reached from verification paths (`verifyProof()` /
|
|
157
|
+
* `verifyObject()`) that operate on inbound, potentially adversarial
|
|
158
|
+
* JSON-LD, and a default loader that fetches attacker-supplied
|
|
159
|
+
* `@context` URLs on the caller's behalf would be an SSRF vector.
|
|
160
|
+
* Canonicalization failures against the restricted loader fall back to
|
|
161
|
+
* the original document, same as any other canonicalization error.
|
|
162
|
+
* Callers that genuinely need the remote-fetch loader (for example
|
|
163
|
+
* applications that sign local JSON-LD against a custom vocabulary)
|
|
164
|
+
* should pass a `contextLoader` explicitly.
|
|
165
|
+
*
|
|
166
|
+
* Must be called before any signing step that canonicalizes the
|
|
167
|
+
* compact form byte-for-byte (for example, Object Integrity Proofs
|
|
168
|
+
* using the `eddsa-jcs-2022` cryptosuite), so the signed payload
|
|
169
|
+
* matches what is sent on the wire.
|
|
170
|
+
*/
|
|
171
|
+
async function normalizePublicAudience(jsonLd, contextLoader) {
|
|
172
|
+
if (!hasPublicCurieInAddressing(jsonLd)) return jsonLd;
|
|
173
|
+
const normalized = rewritePublicAudience(jsonLd);
|
|
174
|
+
if (hasKnownSafeContext(jsonLd)) return normalized;
|
|
175
|
+
const loader = contextLoader ?? preloadedOnlyDocumentLoader;
|
|
176
|
+
try {
|
|
177
|
+
const [before, after] = await Promise.all([jsonld.canonize(jsonLd, {
|
|
178
|
+
format: "application/n-quads",
|
|
179
|
+
documentLoader: loader
|
|
180
|
+
}), jsonld.canonize(normalized, {
|
|
181
|
+
format: "application/n-quads",
|
|
182
|
+
documentLoader: loader
|
|
183
|
+
})]);
|
|
184
|
+
if (before === after) return normalized;
|
|
185
|
+
logger.warn("Expanding the public audience CURIE to its full URI would change the canonical form of the activity; sending the activity as is. This usually means the active JSON-LD context redefines the `as:` prefix or the bare `Public` term.");
|
|
186
|
+
} catch (error) {
|
|
187
|
+
logger.debug("Failed to verify public audience normalization equivalence via JSON-LD canonicalization; sending the activity as is.\n{error}", { error });
|
|
188
|
+
}
|
|
189
|
+
return jsonLd;
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
export { preloadedOnlyDocumentLoader as n, normalizePublicAudience as t };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { n as doubleKnock } from "./http-
|
|
4
|
+
import { n as version, t as name } from "./deno-BYRjHaeb.mjs";
|
|
5
|
+
import { n as doubleKnock } from "./http-BrVfkREl.mjs";
|
|
6
6
|
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
7
7
|
import { getLogger } from "@logtape/logtape";
|
|
8
8
|
//#region src/federation/send.ts
|
package/dist/sig/accept.test.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature, t as formatAcceptSignature } from "../accept-
|
|
4
|
+
import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature, t as formatAcceptSignature } from "../accept-CPkZzmGN.mjs";
|
|
5
5
|
import { test } from "@fedify/fixture";
|
|
6
6
|
import { deepStrictEqual, strictEqual } from "node:assert/strict";
|
|
7
7
|
//#region src/sig/accept.test.ts
|
package/dist/sig/http.test.mjs
CHANGED
|
@@ -3,13 +3,13 @@ import "urlpattern-polyfill";
|
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as esm_default } from "../esm-DVILvP5e.mjs";
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { i as assertExists, t as assertStringIncludes } from "../std__assert-CRDpx_HF.mjs";
|
|
7
7
|
import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
|
|
8
8
|
import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
|
|
9
|
-
import { t as assert } from "../assert-
|
|
10
|
-
import { t as exportJwk } from "../key-
|
|
11
|
-
import { a as parseRfc9421Signature, c as timingSafeEqual, i as formatRfc9421SignatureParameters, l as verifyRequest, n as doubleKnock, o as parseRfc9421SignatureInput, r as formatRfc9421Signature, s as signRequest, t as createRfc9421SignatureBase, u as verifyRequestDetailed } from "../http-
|
|
12
|
-
import { i as rsaPrivateKey2, l as rsaPublicKey5, o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-
|
|
9
|
+
import { t as assert } from "../assert-DikXweDx.mjs";
|
|
10
|
+
import { t as exportJwk } from "../key-cK-YTNKa.mjs";
|
|
11
|
+
import { a as parseRfc9421Signature, c as timingSafeEqual, i as formatRfc9421SignatureParameters, l as verifyRequest, n as doubleKnock, o as parseRfc9421SignatureInput, r as formatRfc9421Signature, s as signRequest, t as createRfc9421SignatureBase, u as verifyRequestDetailed } from "../http-BrVfkREl.mjs";
|
|
12
|
+
import { i as rsaPrivateKey2, l as rsaPublicKey5, o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-DGu1NFwu.mjs";
|
|
13
13
|
import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
14
14
|
import { FetchError, exportSpki } from "@fedify/vocab-runtime";
|
|
15
15
|
import { encodeBase64 } from "byte-encodings/base64";
|
package/dist/sig/key.test.mjs
CHANGED
|
@@ -2,11 +2,11 @@ import "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
5
|
-
import "../std__assert-
|
|
5
|
+
import "../std__assert-CRDpx_HF.mjs";
|
|
6
6
|
import { t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
|
|
7
7
|
import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
|
|
8
|
-
import { a as importJwk, i as generateCryptoKeyPair, n as fetchKey, o as validateCryptoKey, r as fetchKeyDetailed, t as exportJwk } from "../key-
|
|
9
|
-
import { c as rsaPublicKey3, i as rsaPrivateKey2, o as rsaPublicKey1, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-
|
|
8
|
+
import { a as importJwk, i as generateCryptoKeyPair, n as fetchKey, o as validateCryptoKey, r as fetchKeyDetailed, t as exportJwk } from "../key-cK-YTNKa.mjs";
|
|
9
|
+
import { c as rsaPublicKey3, i as rsaPrivateKey2, o as rsaPublicKey1, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DGu1NFwu.mjs";
|
|
10
10
|
import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
11
11
|
import { CryptographicKey, Multikey } from "@fedify/vocab";
|
|
12
12
|
import { FetchError } from "@fedify/vocab-runtime";
|
package/dist/sig/ld.test.mjs
CHANGED
|
@@ -4,10 +4,10 @@ globalThis.addEventListener = () => {};
|
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
5
5
|
import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
|
|
6
6
|
import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
|
|
7
|
-
import { t as assert } from "../assert-
|
|
8
|
-
import { i as generateCryptoKeyPair } from "../key-
|
|
9
|
-
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-
|
|
10
|
-
import { a as signJsonLd, i as hasSignatureLike, n as createSignature, o as verifyJsonLd, r as detachSignature, s as verifySignature, t as attachSignature } from "../ld-
|
|
7
|
+
import { t as assert } from "../assert-DikXweDx.mjs";
|
|
8
|
+
import { i as generateCryptoKeyPair } from "../key-cK-YTNKa.mjs";
|
|
9
|
+
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DGu1NFwu.mjs";
|
|
10
|
+
import { a as signJsonLd, i as hasSignatureLike, n as createSignature, o as verifyJsonLd, r as detachSignature, s as verifySignature, t as attachSignature } from "../ld-SXDkzuUo.mjs";
|
|
11
11
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
12
12
|
import { CryptographicKey } from "@fedify/vocab";
|
|
13
13
|
import { encodeBase64 } from "byte-encodings/base64";
|
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-
|
|
5
|
-
const require_proof = require("../proof-
|
|
4
|
+
const require_http = require("../http-H-4FzBb3.cjs");
|
|
5
|
+
const require_proof = require("../proof-CFERN43j.cjs");
|
|
6
6
|
exports.attachSignature = require_proof.attachSignature;
|
|
7
7
|
exports.createProof = require_proof.createProof;
|
|
8
8
|
exports.createSignature = require_proof.createSignature;
|
package/dist/sig/mod.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
|
-
import { a as verifyRequestDetailed, c as fetchKeyDetailed, f as formatAcceptSignature, h as validateAcceptSignature, i as verifyRequest, l as generateCryptoKeyPair, m as parseAcceptSignature, o as exportJwk, p as fulfillAcceptSignature, r as signRequest, s as fetchKey, u as importJwk } from "../http-
|
|
4
|
-
import { a as verifyProof, c as
|
|
3
|
+
import { a as verifyRequestDetailed, c as fetchKeyDetailed, f as formatAcceptSignature, h as validateAcceptSignature, i as verifyRequest, l as generateCryptoKeyPair, m as parseAcceptSignature, o as exportJwk, p as fulfillAcceptSignature, r as signRequest, s as fetchKey, u as importJwk } from "../http-pZce7PcA.js";
|
|
4
|
+
import { a as verifyProof, c as getKeyOwner, d as detachSignature, f as hasSignatureLike, h as verifySignature, i as verifyObject, l as attachSignature, m as verifyJsonLd, n as hasProofLike, p as signJsonLd, r as signObject, s as doesActorOwnKey, t as createProof, u as createSignature } from "../proof-BJrEACyu.js";
|
|
5
5
|
export { attachSignature, createProof, createSignature, detachSignature, doesActorOwnKey, exportJwk, fetchKey, fetchKeyDetailed, formatAcceptSignature, fulfillAcceptSignature, generateCryptoKeyPair, getKeyOwner, hasProofLike, hasSignatureLike, importJwk, parseAcceptSignature, signJsonLd, signObject, signRequest, validateAcceptSignature, verifyJsonLd, verifyObject, verifyProof, verifyRequest, verifyRequestDetailed, verifySignature };
|
package/dist/sig/owner.test.mjs
CHANGED
|
@@ -2,11 +2,11 @@ import "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
5
|
-
import "../std__assert-
|
|
5
|
+
import "../std__assert-CRDpx_HF.mjs";
|
|
6
6
|
import { n as assertFalse } from "../assert_rejects-B-qJtC9Z.mjs";
|
|
7
|
-
import { t as assert } from "../assert-
|
|
8
|
-
import { o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-
|
|
9
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-
|
|
7
|
+
import { t as assert } from "../assert-DikXweDx.mjs";
|
|
8
|
+
import { o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-DGu1NFwu.mjs";
|
|
9
|
+
import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-Cudh-ej0.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
|
package/dist/sig/proof.test.mjs
CHANGED
|
@@ -2,14 +2,15 @@ import { Temporal } from "@js-temporal/polyfill";
|
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
|
|
5
|
-
import "../std__assert-
|
|
5
|
+
import "../std__assert-CRDpx_HF.mjs";
|
|
6
6
|
import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
|
|
7
7
|
import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
|
|
8
|
-
import { t as assert } from "../assert-
|
|
9
|
-
import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-
|
|
10
|
-
import {
|
|
8
|
+
import { t as assert } from "../assert-DikXweDx.mjs";
|
|
9
|
+
import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DGu1NFwu.mjs";
|
|
10
|
+
import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-CNmZLixq.mjs";
|
|
11
|
+
import { a as verifyProof, i as verifyObject, n as hasProofLike, r as signObject, t as createProof } from "../proof-DC69vtxY.mjs";
|
|
11
12
|
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
12
|
-
import { Create, DataIntegrityProof, Multikey, Note, Place } from "@fedify/vocab";
|
|
13
|
+
import { Create, DataIntegrityProof, Document, Multikey, Note, PUBLIC_COLLECTION, Place } from "@fedify/vocab";
|
|
13
14
|
import { decodeMultibase, importMultibaseKey } from "@fedify/vocab-runtime";
|
|
14
15
|
import { decodeHex } from "byte-encodings/hex";
|
|
15
16
|
//#region src/sig/proof.test.ts
|
|
@@ -151,6 +152,47 @@ test("signObject()", async () => {
|
|
|
151
152
|
created,
|
|
152
153
|
contextLoader: mockDocumentLoader
|
|
153
154
|
}), TypeError, "Unsupported algorithm");
|
|
155
|
+
const signed = await signObject(new Create({
|
|
156
|
+
id: new URL("https://server.example/activities/2"),
|
|
157
|
+
actor: new URL("https://server.example/users/alice"),
|
|
158
|
+
object: new Note({
|
|
159
|
+
id: new URL("https://server.example/objects/2"),
|
|
160
|
+
attribution: new URL("https://server.example/users/alice"),
|
|
161
|
+
content: "Hello public",
|
|
162
|
+
attachments: [new Document({
|
|
163
|
+
mediaType: "image/png",
|
|
164
|
+
url: new URL("https://server.example/objects/2/image.png")
|
|
165
|
+
})]
|
|
166
|
+
}),
|
|
167
|
+
tos: [PUBLIC_COLLECTION]
|
|
168
|
+
}), fep8b32TestVectorPrivateKey, fep8b32TestVectorKeyId, {
|
|
169
|
+
...options,
|
|
170
|
+
created
|
|
171
|
+
});
|
|
172
|
+
const [proof] = await Array.fromAsync(signed.getProofs(options));
|
|
173
|
+
assertInstanceOf(proof, DataIntegrityProof);
|
|
174
|
+
const signedJson = await normalizeOutgoingActivityJsonLd(await signed.toJsonLd(options), mockDocumentLoader);
|
|
175
|
+
assertEquals(signedJson.to, PUBLIC_COLLECTION.href);
|
|
176
|
+
const signedJsonObject = signedJson.object;
|
|
177
|
+
assertEquals(Array.isArray(signedJsonObject.attachment), true);
|
|
178
|
+
const verifyCache = {};
|
|
179
|
+
const verifyOptions = {
|
|
180
|
+
contextLoader: mockDocumentLoader,
|
|
181
|
+
documentLoader: mockDocumentLoader,
|
|
182
|
+
keyCache: {
|
|
183
|
+
get: (keyId) => Promise.resolve(verifyCache[keyId.href]),
|
|
184
|
+
set: (keyId, key) => {
|
|
185
|
+
verifyCache[keyId.href] = key;
|
|
186
|
+
return Promise.resolve();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
assertInstanceOf(await verifyProof(signedJson, proof, verifyOptions), Multikey);
|
|
191
|
+
const signedJsonWithCurie = await signed.toJsonLd(options);
|
|
192
|
+
assertEquals(signedJsonWithCurie.to, "as:Public");
|
|
193
|
+
const signedJsonWithCurieObject = signedJsonWithCurie.object;
|
|
194
|
+
assertEquals(Array.isArray(signedJsonWithCurieObject.attachment), false);
|
|
195
|
+
assertInstanceOf(await verifyProof(signedJsonWithCurie, proof, verifyOptions), Multikey);
|
|
154
196
|
});
|
|
155
197
|
test("hasProofLike()", () => {
|
|
156
198
|
assert(hasProofLike({ proof: {
|
|
@@ -252,6 +294,37 @@ test("verifyProof()", async () => {
|
|
|
252
294
|
}
|
|
253
295
|
}, proof, options), null);
|
|
254
296
|
assertEquals(await verifyProof(jsonLd, proof.clone({ created: Temporal.Now.instant() }), options), null);
|
|
297
|
+
assertEquals(await verifyProof({
|
|
298
|
+
...jsonLd,
|
|
299
|
+
"https://w3id.org/security#proof": {
|
|
300
|
+
"@type": ["https://w3id.org/security#DataIntegrityProof"],
|
|
301
|
+
"https://w3id.org/security#proofValue": [{ "@value": "stale" }]
|
|
302
|
+
}
|
|
303
|
+
}, proof, options), expectedKey);
|
|
304
|
+
assertEquals(await verifyProof([jsonLd], proof, options), null);
|
|
305
|
+
const attackerInput = {
|
|
306
|
+
"@context": ["https://www.w3.org/ns/activitystreams", "https://attacker.example/ctx"],
|
|
307
|
+
id: "https://server.example/activities/attacker",
|
|
308
|
+
type: "Create",
|
|
309
|
+
actor: "https://server.example/users/alice",
|
|
310
|
+
object: {
|
|
311
|
+
id: "https://server.example/objects/attacker",
|
|
312
|
+
type: "Note",
|
|
313
|
+
attributedTo: "https://server.example/users/alice",
|
|
314
|
+
content: "n/a",
|
|
315
|
+
to: "as:Public"
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
const contextLoaderCalls = [];
|
|
319
|
+
assertEquals(await verifyProof(attackerInput, proof, {
|
|
320
|
+
contextLoader: async (url) => {
|
|
321
|
+
contextLoaderCalls.push(url);
|
|
322
|
+
return await mockDocumentLoader(url);
|
|
323
|
+
},
|
|
324
|
+
documentLoader: mockDocumentLoader,
|
|
325
|
+
keyCache: options.keyCache
|
|
326
|
+
}), null);
|
|
327
|
+
assertFalse(contextLoaderCalls.includes("https://attacker.example/ctx"));
|
|
255
328
|
});
|
|
256
329
|
test("verifyObject()", async () => {
|
|
257
330
|
const options = {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import "@js-temporal/polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import {
|
|
4
|
+
import { l as AssertionError, s as format } from "./assert_equals-Ew3jOFa3.mjs";
|
|
5
5
|
import "./assert_rejects-B-qJtC9Z.mjs";
|
|
6
6
|
import "./assert_throws-4NwKEy2q.mjs";
|
|
7
|
+
import "./assert_strict_equals-Dmjbg-bA.mjs";
|
|
7
8
|
//#region ../../node_modules/.pnpm/@jsr+std__assert@0.226.0/node_modules/@jsr/std__assert/assert_exists.js
|
|
8
9
|
/**
|
|
9
10
|
* Make an assertion that actual is not null or undefined.
|
|
@@ -77,42 +78,6 @@ import "./assert_throws-4NwKEy2q.mjs";
|
|
|
77
78
|
throw new AssertionError(msg ?? `Expect ${actualString} > ${expectedString}`);
|
|
78
79
|
}
|
|
79
80
|
//#endregion
|
|
80
|
-
//#region ../../node_modules/.pnpm/@jsr+std__assert@0.226.0/node_modules/@jsr/std__assert/assert_strict_equals.js
|
|
81
|
-
/**
|
|
82
|
-
* Make an assertion that `actual` and `expected` are equal using
|
|
83
|
-
* {@linkcode Object.is} for equality comparison. If not, then throw.
|
|
84
|
-
*
|
|
85
|
-
* @example Usage
|
|
86
|
-
* ```ts no-eval
|
|
87
|
-
* import { assertStrictEquals } from "@std/assert/assert-strict-equals";
|
|
88
|
-
*
|
|
89
|
-
* const a = {};
|
|
90
|
-
* const b = a;
|
|
91
|
-
* assertStrictEquals(a, b); // Doesn't throw
|
|
92
|
-
*
|
|
93
|
-
* const c = {};
|
|
94
|
-
* const d = {};
|
|
95
|
-
* assertStrictEquals(c, d); // Throws
|
|
96
|
-
* ```
|
|
97
|
-
*
|
|
98
|
-
* @typeParam T The type of the expected value.
|
|
99
|
-
* @param actual The actual value to compare.
|
|
100
|
-
* @param expected The expected value to compare.
|
|
101
|
-
* @param msg The optional message to display if the assertion fails.
|
|
102
|
-
*/ function assertStrictEquals(actual, expected, msg) {
|
|
103
|
-
if (Object.is(actual, expected)) return;
|
|
104
|
-
const msgSuffix = msg ? `: ${msg}` : ".";
|
|
105
|
-
let message;
|
|
106
|
-
const actualString = format(actual);
|
|
107
|
-
const expectedString = format(expected);
|
|
108
|
-
if (actualString === expectedString) message = `Values have the same structure but are not reference-equal${msgSuffix}\n\n${red(actualString.split("\n").map((l) => ` ${l}`).join("\n"))}\n`;
|
|
109
|
-
else {
|
|
110
|
-
const stringDiff = typeof actual === "string" && typeof expected === "string";
|
|
111
|
-
message = `Values are not strictly equal${msgSuffix}\n${buildMessage(stringDiff ? diffStr(actual, expected) : diff(actualString.split("\n"), expectedString.split("\n")), { stringDiff }).join("\n")}`;
|
|
112
|
-
}
|
|
113
|
-
throw new AssertionError(message);
|
|
114
|
-
}
|
|
115
|
-
//#endregion
|
|
116
81
|
//#region ../../node_modules/.pnpm/@jsr+std__assert@0.226.0/node_modules/@jsr/std__assert/assert_string_includes.js
|
|
117
82
|
/**
|
|
118
83
|
* Make an assertion that actual includes expected. If not
|
|
@@ -136,4 +101,4 @@ import "./assert_throws-4NwKEy2q.mjs";
|
|
|
136
101
|
}
|
|
137
102
|
}
|
|
138
103
|
//#endregion
|
|
139
|
-
export { assertExists as
|
|
104
|
+
export { assertExists as i, assertGreater as n, assertGreaterOrEqual as r, assertStringIncludes as t };
|
package/dist/testing/mod.d.mts
CHANGED
|
@@ -584,6 +584,15 @@ interface FanoutMessage {
|
|
|
584
584
|
readonly activityType: string;
|
|
585
585
|
readonly collectionSync?: string;
|
|
586
586
|
readonly orderingKey?: string;
|
|
587
|
+
/**
|
|
588
|
+
* Whether to apply outgoing JSON-LD wire-format normalization to queued
|
|
589
|
+
* activities that already carry Object Integrity Proofs.
|
|
590
|
+
*
|
|
591
|
+
* `true` is used for proofs Fedify created before fanout, or when callers
|
|
592
|
+
* explicitly request normalization for locally pre-signed activities.
|
|
593
|
+
* `false`/`undefined` preserves existing proofs as-is.
|
|
594
|
+
*/
|
|
595
|
+
readonly normalizeExistingProofs?: boolean;
|
|
587
596
|
readonly traceContext: Readonly<Record<string, string>>;
|
|
588
597
|
}
|
|
589
598
|
interface OutboxMessage {
|
|
@@ -2215,6 +2224,19 @@ interface SendActivityOptions {
|
|
|
2215
2224
|
* @since 1.5.0
|
|
2216
2225
|
*/
|
|
2217
2226
|
readonly fanout?: "auto" | "skip" | "force";
|
|
2227
|
+
/**
|
|
2228
|
+
* Whether to apply Fedify's outgoing JSON-LD wire-format compatibility fixes
|
|
2229
|
+
* to activities that already carry Object Integrity Proofs.
|
|
2230
|
+
*
|
|
2231
|
+
* By default, Fedify preserves existing proofs byte-for-byte because it
|
|
2232
|
+
* cannot know whether they were created for the normalized outgoing wire
|
|
2233
|
+
* form. Set this to `true` when sending an activity that was pre-signed
|
|
2234
|
+
* locally with `signObject()` or `createProof()`, so the emitted
|
|
2235
|
+
* compact JSON-LD matches the bytes covered by the proof.
|
|
2236
|
+
*
|
|
2237
|
+
* @since 2.2.0
|
|
2238
|
+
*/
|
|
2239
|
+
readonly normalizeExistingProofs?: boolean;
|
|
2218
2240
|
/**
|
|
2219
2241
|
* The base URIs to exclude from the recipients' inboxes. It is useful
|
|
2220
2242
|
* for excluding the recipients having the same shared inbox with the sender.
|