@fedify/fedify 2.3.0-dev.1110 → 2.3.0-dev.1119
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_rejects-B-qJtC9Z.mjs → assert_rejects-DQP-q39h.mjs} +27 -2
- package/dist/{builder-B-Y6fwSu.mjs → builder-Ond_h57y.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.mjs +1 -1
- package/dist/compat/public-audience.test.mjs +1 -1
- package/dist/compat/transformers.test.mjs +2 -2
- package/dist/{context-C0C_sRha.d.cts → context-Ch-ZLyTQ.d.cts} +1 -1
- package/dist/{context-Dqgt8saU.d.ts → context-cSUMk2da.d.ts} +1 -1
- package/dist/{deno-hqC7tKJn.mjs → deno-DVsHS7rA.mjs} +1 -1
- package/dist/{docloader-BOEuuXkX.mjs → docloader-WsWfKaE5.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 -7
- package/dist/federation/idempotency.test.mjs +5 -5
- package/dist/federation/inbox.test.mjs +1 -1
- package/dist/federation/keycache.test.mjs +1 -1
- package/dist/federation/kv.test.mjs +2 -2
- package/dist/federation/metrics.test.d.mts +2 -0
- package/dist/federation/metrics.test.mjs +107 -0
- package/dist/federation/middleware.test.mjs +390 -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 +2 -2
- package/dist/federation/negotiation.test.mjs +2 -2
- package/dist/federation/router.test.mjs +2 -2
- package/dist/federation/send.test.mjs +11 -11
- package/dist/federation/webfinger.test.mjs +3 -3
- package/dist/{getMachineId-bsd-etIyxDet.mjs → getMachineId-bsd-BY01PL1n.mjs} +1 -1
- package/dist/{getMachineId-darwin-D23zTf4g.mjs → getMachineId-darwin-Dr1gkBkp.mjs} +1 -1
- package/dist/{getMachineId-win-Dpap6v5i.mjs → getMachineId-win-QEYwcJiy.mjs} +1 -1
- package/dist/{http-O8MYWwk8.js → http-CouJSFVK.js} +461 -37
- package/dist/{http-DV0il3vk.cjs → http-CubOB9wq.cjs} +513 -35
- package/dist/{http-BDZeS5om.d.ts → http-D6LP89UO.d.ts} +7 -1
- package/dist/{http-C87EWkO0.d.cts → http-D6aw3j2U.d.cts} +7 -1
- package/dist/{http-BLopFpvC.mjs → http-DUV8ysti.mjs} +86 -37
- package/dist/{key-DW1EVmtP.mjs → key-BoWaYRHm.mjs} +1 -1
- package/dist/{kv-cache-C3NWWiTg.js → kv-cache-DBNpsneh.js} +1 -1
- package/dist/{kv-cache-Dya-TWMe.cjs → kv-cache-Dz31ATUT.cjs} +1 -1
- package/dist/{ld-BNkk2Yal.mjs → ld-B5K1mSuG.mjs} +60 -9
- package/dist/{send-hokVCPu6.mjs → metrics-C4attqv0.mjs} +124 -224
- package/dist/{middleware-D6FbOjuK.mjs → middleware-BDKFRjue.mjs} +1 -1
- package/dist/{middleware-DUWeXjZR.cjs → middleware-CmsDtIHI.cjs} +75 -309
- package/dist/{middleware-CjzI3aYo.js → middleware-Dtjz-hSk.js} +46 -280
- package/dist/{middleware-DA2WTBr4.mjs → middleware-t0jC8I99.mjs} +59 -34
- package/dist/{mod-DXY9JF28.d.cts → mod-B-Lin9Sy.d.ts} +25 -2
- package/dist/{mod-DHO9lk3D.d.ts → mod-BDhgfjP7.d.cts} +25 -2
- package/dist/{mod-B0rWmfW5.d.cts → mod-BR_BB0bh.d.cts} +1 -1
- package/dist/{mod-Dx3-hqyo.d.ts → mod-C6E8rkcz.d.ts} +1 -1
- package/dist/{mod-BhU_H1I_.d.ts → mod-DLrRb0dx.d.ts} +1 -1
- package/dist/{mod-CLPnQPsv.d.cts → mod-P9tE2WmM.d.cts} +1 -1
- package/dist/mod.cjs +4 -4
- package/dist/mod.d.cts +5 -5
- package/dist/mod.d.ts +5 -5
- 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-BgFLCJQ_.mjs → outgoing-jsonld-BNL8AC14.mjs} +1 -1
- package/dist/{owner-jvJAtR5O.mjs → owner-hDxI0ufu.mjs} +2 -2
- package/dist/{proof-BD92WeqV.cjs → proof-BUWfVr6Q.cjs} +78 -11
- package/dist/{proof-mfmHH9j0.mjs → proof-DhVuz4bc.mjs} +25 -7
- package/dist/{proof-5kT7OUPV.js → proof-n60t8o9P.js} +78 -11
- package/dist/send-BPhyR5Oo.mjs +225 -0
- package/dist/sig/accept.test.mjs +1 -1
- package/dist/sig/http.test.mjs +212 -6
- package/dist/sig/key.test.mjs +4 -4
- package/dist/sig/ld.test.mjs +138 -5
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +4 -4
- package/dist/sig/proof.test.mjs +167 -6
- package/dist/{std__assert-CRDpx_HF.mjs → std__assert-BTEgfoJo.mjs} +2 -27
- 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.d.cts +1 -1
- package/dist/utils/mod.d.ts +1 -1
- package/dist/utils/mod.js +1 -1
- package/package.json +5 -5
- /package/dist/{accept-CceiKpCy.mjs → accept-CgDcxvjV.mjs} +0 -0
- /package/dist/{activity-listener-tztVvlNb.mjs → activity-listener-BeTGV3wc.mjs} +0 -0
- /package/dist/{client-B_A6mfn3.mjs → client-Bneh_DYR.mjs} +0 -0
- /package/dist/{collection-CA3V5zyK.mjs → collection-Cc3DVAhE.mjs} +0 -0
- /package/dist/{execAsync-DCBrgFiV.mjs → execAsync-Dxb7rNf3.mjs} +0 -0
- /package/dist/{getMachineId-linux-ObI47Hql.mjs → getMachineId-linux-Bbhofx-s.mjs} +0 -0
- /package/dist/{getMachineId-unsupported-Ddu-PFeh.mjs → getMachineId-unsupported-dIOte2Ct.mjs} +0 -0
- /package/dist/{keys-C3kae-6B.mjs → keys-CSYsOMFG.mjs} +0 -0
- /package/dist/{kv-x2IvBUyq.mjs → kv-QHE0oeM3.mjs} +0 -0
- /package/dist/{kv-cache-CiiNwT6W.mjs → kv-cache-DihufyAQ.mjs} +0 -0
- /package/dist/{public-audience-N3pyOx2p.mjs → public-audience-c9zmYKgA.mjs} +0 -0
- /package/dist/{types-BFowWFTT.mjs → types-D09GN0uZ.mjs} +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import "@js-temporal/polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { n as version, t as name } from "./deno-DVsHS7rA.mjs";
|
|
5
|
+
import { n as getFederationMetrics, t as getDurationMs } from "./metrics-C4attqv0.mjs";
|
|
6
|
+
import { n as doubleKnock } from "./http-DUV8ysti.mjs";
|
|
7
|
+
import { getLogger } from "@logtape/logtape";
|
|
8
|
+
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
9
|
+
//#region src/federation/send.ts
|
|
10
|
+
/**
|
|
11
|
+
* Extracts the inbox URLs from recipients.
|
|
12
|
+
* @param parameters The parameters to extract the inboxes.
|
|
13
|
+
* See also {@link ExtractInboxesParameters}.
|
|
14
|
+
* @returns The inboxes as a map of inbox URL to actor URIs.
|
|
15
|
+
*/
|
|
16
|
+
function extractInboxes({ recipients, preferSharedInbox, excludeBaseUris }) {
|
|
17
|
+
const inboxes = {};
|
|
18
|
+
for (const recipient of recipients) {
|
|
19
|
+
let inbox;
|
|
20
|
+
let sharedInbox = false;
|
|
21
|
+
if (preferSharedInbox && recipient.endpoints?.sharedInbox != null) {
|
|
22
|
+
inbox = recipient.endpoints.sharedInbox;
|
|
23
|
+
sharedInbox = true;
|
|
24
|
+
} else inbox = recipient.inboxId;
|
|
25
|
+
if (inbox != null && recipient.id != null) {
|
|
26
|
+
if (excludeBaseUris != null && excludeBaseUris.some((u) => u.origin === inbox?.origin)) continue;
|
|
27
|
+
inboxes[inbox.href] ??= {
|
|
28
|
+
actorIds: /* @__PURE__ */ new Set(),
|
|
29
|
+
sharedInbox
|
|
30
|
+
};
|
|
31
|
+
inboxes[inbox.href].actorIds.add(recipient.id.href);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return inboxes;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Sends an {@link Activity} to an inbox.
|
|
38
|
+
*
|
|
39
|
+
* @param parameters The parameters for sending the activity.
|
|
40
|
+
* See also {@link SendActivityParameters}.
|
|
41
|
+
* @throws {Error} If the activity fails to send.
|
|
42
|
+
*/
|
|
43
|
+
function sendActivity(options) {
|
|
44
|
+
const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
|
|
45
|
+
return tracerProvider.getTracer(name, version).startActiveSpan("activitypub.send_activity", {
|
|
46
|
+
kind: SpanKind.CLIENT,
|
|
47
|
+
attributes: { "activitypub.shared_inbox": options.sharedInbox ?? false }
|
|
48
|
+
}, async (span) => {
|
|
49
|
+
if (options.activityId != null) span.setAttribute("activitypub.activity.id", options.activityId);
|
|
50
|
+
if (options.activityType != null) span.setAttribute("activitypub.activity.type", options.activityType);
|
|
51
|
+
try {
|
|
52
|
+
await sendActivityInternal({
|
|
53
|
+
...options,
|
|
54
|
+
tracerProvider
|
|
55
|
+
}, span);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
span.setStatus({
|
|
58
|
+
code: SpanStatusCode.ERROR,
|
|
59
|
+
message: String(e)
|
|
60
|
+
});
|
|
61
|
+
throw e;
|
|
62
|
+
} finally {
|
|
63
|
+
span.end();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const MAX_ERROR_RESPONSE_BODY_BYTES = 1024;
|
|
68
|
+
function getActivityActorId(activity) {
|
|
69
|
+
if (!isRecord(activity)) return void 0;
|
|
70
|
+
return getIdValue(activity.actor);
|
|
71
|
+
}
|
|
72
|
+
function getIdValue(value) {
|
|
73
|
+
if (typeof value === "string" && value !== "") return value;
|
|
74
|
+
if (value instanceof URL) return value.href;
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
for (const item of value) {
|
|
77
|
+
const id = getIdValue(item);
|
|
78
|
+
if (id != null) return id;
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (isRecord(value)) return getIdValue(value.id);
|
|
83
|
+
}
|
|
84
|
+
function isRecord(value) {
|
|
85
|
+
return typeof value === "object" && value != null;
|
|
86
|
+
}
|
|
87
|
+
async function readLimitedResponseBody(response, maxBytes) {
|
|
88
|
+
if (response.body == null) return "";
|
|
89
|
+
const reader = response.body.getReader();
|
|
90
|
+
const decoder = new TextDecoder();
|
|
91
|
+
const chunks = [];
|
|
92
|
+
let totalBytes = 0;
|
|
93
|
+
let truncated = false;
|
|
94
|
+
try {
|
|
95
|
+
while (true) {
|
|
96
|
+
const { done, value } = await reader.read();
|
|
97
|
+
if (done) break;
|
|
98
|
+
if (totalBytes + value.length > maxBytes) {
|
|
99
|
+
const remaining = maxBytes - totalBytes;
|
|
100
|
+
if (remaining > 0) chunks.push(decoder.decode(value.slice(0, remaining), { stream: true }));
|
|
101
|
+
truncated = true;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
chunks.push(decoder.decode(value, { stream: true }));
|
|
105
|
+
totalBytes += value.length;
|
|
106
|
+
}
|
|
107
|
+
} finally {
|
|
108
|
+
reader.releaseLock();
|
|
109
|
+
}
|
|
110
|
+
let result = chunks.join("");
|
|
111
|
+
if (truncated) result += "… (truncated)";
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
async function sendActivityInternal({ activity, activityId, activityType, keys, inbox, headers, specDeterminer, meterProvider, tracerProvider }, span) {
|
|
115
|
+
const logger = getLogger([
|
|
116
|
+
"fedify",
|
|
117
|
+
"federation",
|
|
118
|
+
"outbox"
|
|
119
|
+
]);
|
|
120
|
+
const federationMetrics = getFederationMetrics(meterProvider);
|
|
121
|
+
const started = performance.now();
|
|
122
|
+
let deliverySuccess = false;
|
|
123
|
+
headers = new Headers(headers);
|
|
124
|
+
headers.set("Content-Type", "application/activity+json");
|
|
125
|
+
const request = new Request(inbox, {
|
|
126
|
+
method: "POST",
|
|
127
|
+
headers,
|
|
128
|
+
body: JSON.stringify(activity)
|
|
129
|
+
});
|
|
130
|
+
let rsaKey = null;
|
|
131
|
+
for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
|
|
132
|
+
rsaKey = key;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
if (rsaKey == null) logger.warn("No supported key found to sign the request to {inbox}. The request will be sent without a signature. In order to sign the request, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
|
|
136
|
+
inbox: inbox.href,
|
|
137
|
+
keys: keys.map((pair) => ({
|
|
138
|
+
keyId: pair.keyId.href,
|
|
139
|
+
privateKey: pair.privateKey
|
|
140
|
+
}))
|
|
141
|
+
});
|
|
142
|
+
let response;
|
|
143
|
+
try {
|
|
144
|
+
response = rsaKey == null ? await fetch(request) : await doubleKnock(request, rsaKey, {
|
|
145
|
+
tracerProvider,
|
|
146
|
+
specDeterminer
|
|
147
|
+
});
|
|
148
|
+
} catch (error) {
|
|
149
|
+
logger.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
|
|
150
|
+
activityId,
|
|
151
|
+
inbox: inbox.href,
|
|
152
|
+
error
|
|
153
|
+
});
|
|
154
|
+
federationMetrics.recordDelivery(inbox, getDurationMs(started), false, activityType);
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
let error;
|
|
160
|
+
try {
|
|
161
|
+
error = await readLimitedResponseBody(response, MAX_ERROR_RESPONSE_BODY_BYTES);
|
|
162
|
+
} catch (_) {
|
|
163
|
+
error = "";
|
|
164
|
+
}
|
|
165
|
+
logger.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
|
|
166
|
+
activityId,
|
|
167
|
+
inbox: inbox.href,
|
|
168
|
+
status: response.status,
|
|
169
|
+
statusText: response.statusText,
|
|
170
|
+
error
|
|
171
|
+
});
|
|
172
|
+
throw new SendActivityError(inbox, response.status, `Failed to send activity ${activityId} to ${inbox.href} (${response.status} ${response.statusText}):\n${error}`, error);
|
|
173
|
+
}
|
|
174
|
+
deliverySuccess = true;
|
|
175
|
+
const eventAttributes = {
|
|
176
|
+
"activitypub.inbox.url": inbox.href,
|
|
177
|
+
"activitypub.activity.id": activityId ?? ""
|
|
178
|
+
};
|
|
179
|
+
if (activityType != null) eventAttributes["activitypub.activity.type"] = activityType;
|
|
180
|
+
const actorId = getActivityActorId(activity);
|
|
181
|
+
if (actorId != null) eventAttributes["activitypub.actor.id"] = actorId;
|
|
182
|
+
span.addEvent("activitypub.activity.sent", eventAttributes);
|
|
183
|
+
} finally {
|
|
184
|
+
federationMetrics.recordDelivery(inbox, getDurationMs(started), deliverySuccess, activityType);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* An error that is thrown when an activity fails to send to a remote inbox.
|
|
189
|
+
* It contains structured information about the failure, including the HTTP
|
|
190
|
+
* status code, the inbox URL, and the response body.
|
|
191
|
+
* @since 2.0.0
|
|
192
|
+
*/
|
|
193
|
+
var SendActivityError = class extends Error {
|
|
194
|
+
/**
|
|
195
|
+
* The inbox URL that the activity was being sent to.
|
|
196
|
+
*/
|
|
197
|
+
inbox;
|
|
198
|
+
/**
|
|
199
|
+
* The HTTP status code returned by the inbox.
|
|
200
|
+
*/
|
|
201
|
+
statusCode;
|
|
202
|
+
/**
|
|
203
|
+
* The response body from the inbox, if any. Note that this may be
|
|
204
|
+
* truncated to a maximum of 1 KiB to prevent excessive memory consumption
|
|
205
|
+
* when remote servers return large error pages (e.g., Cloudflare error pages).
|
|
206
|
+
* If truncated, the string will end with `"… (truncated)"`.
|
|
207
|
+
*/
|
|
208
|
+
responseBody;
|
|
209
|
+
/**
|
|
210
|
+
* Creates a new {@link SendActivityError}.
|
|
211
|
+
* @param inbox The inbox URL.
|
|
212
|
+
* @param statusCode The HTTP status code.
|
|
213
|
+
* @param message The error message.
|
|
214
|
+
* @param responseBody The response body.
|
|
215
|
+
*/
|
|
216
|
+
constructor(inbox, statusCode, message, responseBody) {
|
|
217
|
+
super(message);
|
|
218
|
+
this.name = "SendActivityError";
|
|
219
|
+
this.inbox = inbox;
|
|
220
|
+
this.statusCode = statusCode;
|
|
221
|
+
this.responseBody = responseBody;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
//#endregion
|
|
225
|
+
export { extractInboxes as n, sendActivity as r, SendActivityError as t };
|
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-CgDcxvjV.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
|
@@ -2,15 +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 {
|
|
6
|
-
import { n as assertFalse, t as assertRejects } from "../assert_rejects-
|
|
5
|
+
import { r as assertExists, t as assertStringIncludes } from "../std__assert-BTEgfoJo.mjs";
|
|
6
|
+
import { n as assertGreaterOrEqual, r as assertFalse, t as assertRejects } from "../assert_rejects-DQP-q39h.mjs";
|
|
7
7
|
import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
|
|
8
8
|
import { t as assert } from "../assert-DikXweDx.mjs";
|
|
9
9
|
import { t as esm_default } from "../esm-sdtqOUPu.mjs";
|
|
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-
|
|
13
|
-
import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
10
|
+
import { t as exportJwk } from "../key-BoWaYRHm.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-DUV8ysti.mjs";
|
|
12
|
+
import { i as rsaPrivateKey2, l as rsaPublicKey5, o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-CSYsOMFG.mjs";
|
|
13
|
+
import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
14
14
|
import { FetchError, exportSpki } from "@fedify/vocab-runtime";
|
|
15
15
|
import { encodeBase64 } from "byte-encodings/base64";
|
|
16
16
|
//#region src/sig/http.test.ts
|
|
@@ -230,6 +230,212 @@ test("verifyRequestDetailed() records failure details on span", async () => {
|
|
|
230
230
|
assertEquals(span.attributes["http_signatures.key_id"], keyId.href);
|
|
231
231
|
assertEquals(span.attributes["http_signatures.key_fetch_status"], 410);
|
|
232
232
|
});
|
|
233
|
+
test("verifyRequestDetailed() records verification duration metric", async (t) => {
|
|
234
|
+
const buildSignedRequest = () => signRequest(new Request("https://example.com/inbox", {
|
|
235
|
+
method: "POST",
|
|
236
|
+
headers: {
|
|
237
|
+
"Content-Type": "application/activity+json",
|
|
238
|
+
accept: "application/ld+json"
|
|
239
|
+
},
|
|
240
|
+
body: JSON.stringify({
|
|
241
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
242
|
+
type: "Create",
|
|
243
|
+
actor: "https://example.com/key2"
|
|
244
|
+
})
|
|
245
|
+
}), rsaPrivateKey2, new URL("https://example.com/key2"));
|
|
246
|
+
await t.step("verified path emits one measurement", async () => {
|
|
247
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
248
|
+
assert((await verifyRequestDetailed(await buildSignedRequest(), {
|
|
249
|
+
contextLoader: mockDocumentLoader,
|
|
250
|
+
documentLoader: mockDocumentLoader,
|
|
251
|
+
meterProvider
|
|
252
|
+
})).verified);
|
|
253
|
+
const measurements = recorder.getMeasurements("activitypub.signature.verification.duration");
|
|
254
|
+
assertEquals(measurements.length, 1);
|
|
255
|
+
const measurement = measurements[0];
|
|
256
|
+
assertEquals(measurement.type, "histogram");
|
|
257
|
+
assertGreaterOrEqual(measurement.value, 0);
|
|
258
|
+
assertEquals(measurement.attributes["activitypub.signature.kind"], "http");
|
|
259
|
+
assertEquals(measurement.attributes["activitypub.signature.result"], "verified");
|
|
260
|
+
assertEquals(measurement.attributes["http_signatures.algorithm"], "rsa-sha256");
|
|
261
|
+
assertFalse("http_signatures.failure_reason" in measurement.attributes);
|
|
262
|
+
});
|
|
263
|
+
await t.step("missing signature is recorded as result=missing", async () => {
|
|
264
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
265
|
+
const result = await verifyRequestDetailed(new Request("https://example.com/inbox", {
|
|
266
|
+
method: "POST",
|
|
267
|
+
headers: { "Content-Type": "application/activity+json" },
|
|
268
|
+
body: "{}"
|
|
269
|
+
}), {
|
|
270
|
+
contextLoader: mockDocumentLoader,
|
|
271
|
+
documentLoader: mockDocumentLoader,
|
|
272
|
+
meterProvider
|
|
273
|
+
});
|
|
274
|
+
assertFalse(result.verified);
|
|
275
|
+
assertEquals(result.reason.type, "noSignature");
|
|
276
|
+
const measurements = recorder.getMeasurements("activitypub.signature.verification.duration");
|
|
277
|
+
assertEquals(measurements.length, 1);
|
|
278
|
+
assertEquals(measurements[0].attributes["activitypub.signature.kind"], "http");
|
|
279
|
+
assertEquals(measurements[0].attributes["activitypub.signature.result"], "missing");
|
|
280
|
+
assertFalse("http_signatures.failure_reason" in measurements[0].attributes);
|
|
281
|
+
});
|
|
282
|
+
await t.step("invalid signature is recorded as result=rejected with failure_reason", async () => {
|
|
283
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
284
|
+
const result = await verifyRequestDetailed(new Request("https://example.com/", {
|
|
285
|
+
method: "POST",
|
|
286
|
+
headers: {
|
|
287
|
+
Date: "Tue, 05 Mar 2024 07:49:44 GMT",
|
|
288
|
+
Digest: "sha-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
|
|
289
|
+
Signature: "keyId=\"https://example.com/key2\",headers=\"(request-target) date digest\",signature=\"AAAA\""
|
|
290
|
+
},
|
|
291
|
+
body: ""
|
|
292
|
+
}), {
|
|
293
|
+
documentLoader: mockDocumentLoader,
|
|
294
|
+
contextLoader: mockDocumentLoader,
|
|
295
|
+
meterProvider
|
|
296
|
+
});
|
|
297
|
+
assertFalse(result.verified);
|
|
298
|
+
assertEquals(result.reason.type, "invalidSignature");
|
|
299
|
+
const measurements = recorder.getMeasurements("activitypub.signature.verification.duration");
|
|
300
|
+
assertEquals(measurements.length, 1);
|
|
301
|
+
assertEquals(measurements[0].attributes["activitypub.signature.kind"], "http");
|
|
302
|
+
assertEquals(measurements[0].attributes["activitypub.signature.result"], "rejected");
|
|
303
|
+
assertEquals(measurements[0].attributes["http_signatures.failure_reason"], "invalidSignature");
|
|
304
|
+
});
|
|
305
|
+
await t.step("key fetch failure is recorded as result=rejected with failure_reason=keyFetchError", async () => {
|
|
306
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
307
|
+
const keyId = new URL("https://gone.example/actors/alice#main-key");
|
|
308
|
+
const result = await verifyRequestDetailed(await signRequest(new Request("https://example.com/inbox", {
|
|
309
|
+
method: "POST",
|
|
310
|
+
headers: {
|
|
311
|
+
"Content-Type": "application/activity+json",
|
|
312
|
+
accept: "application/ld+json"
|
|
313
|
+
},
|
|
314
|
+
body: JSON.stringify({
|
|
315
|
+
"@context": "https://www.w3.org/ns/activitystreams",
|
|
316
|
+
type: "Create",
|
|
317
|
+
actor: "https://gone.example/actors/alice"
|
|
318
|
+
})
|
|
319
|
+
}), rsaPrivateKey2, keyId), {
|
|
320
|
+
contextLoader: mockDocumentLoader,
|
|
321
|
+
documentLoader(url) {
|
|
322
|
+
if (url === keyId.href) throw new FetchError(keyId, `HTTP 410: ${keyId.href}`, new Response(null, { status: 410 }));
|
|
323
|
+
return mockDocumentLoader(url);
|
|
324
|
+
},
|
|
325
|
+
meterProvider
|
|
326
|
+
});
|
|
327
|
+
assertFalse(result.verified);
|
|
328
|
+
assertEquals(result.reason.type, "keyFetchError");
|
|
329
|
+
const measurements = recorder.getMeasurements("activitypub.signature.verification.duration");
|
|
330
|
+
assertEquals(measurements.length, 1);
|
|
331
|
+
assertEquals(measurements[0].attributes["activitypub.signature.result"], "rejected");
|
|
332
|
+
assertEquals(measurements[0].attributes["http_signatures.failure_reason"], "keyFetchError");
|
|
333
|
+
});
|
|
334
|
+
await t.step("verifyRequest() wrapper emits exactly one measurement, not two", async () => {
|
|
335
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
336
|
+
assertExists(await verifyRequest(await buildSignedRequest(), {
|
|
337
|
+
contextLoader: mockDocumentLoader,
|
|
338
|
+
documentLoader: mockDocumentLoader,
|
|
339
|
+
meterProvider
|
|
340
|
+
}));
|
|
341
|
+
assertEquals(recorder.getMeasurements("activitypub.signature.verification.duration").length, 1);
|
|
342
|
+
});
|
|
343
|
+
await t.step("cached-key retry emits one measurement, not two", async () => {
|
|
344
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
345
|
+
const cache = { "https://example.com/key2": rsaPublicKey1 };
|
|
346
|
+
assertExists(await verifyRequest(await buildSignedRequest(), {
|
|
347
|
+
contextLoader: mockDocumentLoader,
|
|
348
|
+
documentLoader: mockDocumentLoader,
|
|
349
|
+
meterProvider,
|
|
350
|
+
keyCache: {
|
|
351
|
+
get(keyId) {
|
|
352
|
+
return Promise.resolve(cache[keyId.href]);
|
|
353
|
+
},
|
|
354
|
+
set(keyId, k) {
|
|
355
|
+
cache[keyId.href] = k;
|
|
356
|
+
return Promise.resolve();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}));
|
|
360
|
+
assertEquals(recorder.getMeasurements("activitypub.signature.verification.duration").length, 1);
|
|
361
|
+
});
|
|
362
|
+
await t.step("key fetch records result=fetched on a cold cache", async () => {
|
|
363
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
364
|
+
assertExists(await verifyRequest(await buildSignedRequest(), {
|
|
365
|
+
contextLoader: mockDocumentLoader,
|
|
366
|
+
documentLoader: mockDocumentLoader,
|
|
367
|
+
meterProvider
|
|
368
|
+
}));
|
|
369
|
+
const measurements = recorder.getMeasurements("activitypub.signature.key_fetch.duration");
|
|
370
|
+
assertEquals(measurements.length, 1);
|
|
371
|
+
assertEquals(measurements[0].type, "histogram");
|
|
372
|
+
assertGreaterOrEqual(measurements[0].value, 0);
|
|
373
|
+
assertEquals(measurements[0].attributes["activitypub.signature.kind"], "http");
|
|
374
|
+
assertEquals(measurements[0].attributes["activitypub.signature.key_fetch.result"], "fetched");
|
|
375
|
+
});
|
|
376
|
+
await t.step("key fetch records result=hit when served from the key cache", async () => {
|
|
377
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
378
|
+
const cache = { "https://example.com/key2": rsaPublicKey2 };
|
|
379
|
+
assertExists(await verifyRequest(await buildSignedRequest(), {
|
|
380
|
+
contextLoader: mockDocumentLoader,
|
|
381
|
+
documentLoader: mockDocumentLoader,
|
|
382
|
+
meterProvider,
|
|
383
|
+
keyCache: {
|
|
384
|
+
get(keyId) {
|
|
385
|
+
return Promise.resolve(cache[keyId.href]);
|
|
386
|
+
},
|
|
387
|
+
set(keyId, k) {
|
|
388
|
+
cache[keyId.href] = k;
|
|
389
|
+
return Promise.resolve();
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}));
|
|
393
|
+
const measurements = recorder.getMeasurements("activitypub.signature.key_fetch.duration");
|
|
394
|
+
assertEquals(measurements.length, 1);
|
|
395
|
+
assertEquals(measurements[0].attributes["activitypub.signature.key_fetch.result"], "hit");
|
|
396
|
+
});
|
|
397
|
+
await t.step("key fetch records result=error when the remote key returns HTTP 410", async () => {
|
|
398
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
399
|
+
const keyId = new URL("https://gone.example/actors/alice#main-key");
|
|
400
|
+
assertFalse((await verifyRequestDetailed(await signRequest(new Request("https://example.com/inbox", {
|
|
401
|
+
method: "POST",
|
|
402
|
+
headers: {
|
|
403
|
+
"Content-Type": "application/activity+json",
|
|
404
|
+
accept: "application/ld+json"
|
|
405
|
+
},
|
|
406
|
+
body: "{}"
|
|
407
|
+
}), rsaPrivateKey2, keyId), {
|
|
408
|
+
contextLoader: mockDocumentLoader,
|
|
409
|
+
documentLoader(url) {
|
|
410
|
+
if (url === keyId.href) throw new FetchError(keyId, `HTTP 410: ${keyId.href}`, new Response(null, { status: 410 }));
|
|
411
|
+
return mockDocumentLoader(url);
|
|
412
|
+
},
|
|
413
|
+
meterProvider
|
|
414
|
+
})).verified);
|
|
415
|
+
const measurements = recorder.getMeasurements("activitypub.signature.key_fetch.duration");
|
|
416
|
+
assertEquals(measurements.length, 1);
|
|
417
|
+
assertEquals(measurements[0].attributes["activitypub.signature.key_fetch.result"], "error");
|
|
418
|
+
});
|
|
419
|
+
await t.step("draft-cavage with unknown algorithm omits the algorithm metric attribute", async () => {
|
|
420
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
421
|
+
assertFalse((await verifyRequestDetailed(new Request("https://example.com/", {
|
|
422
|
+
method: "POST",
|
|
423
|
+
headers: {
|
|
424
|
+
Date: "Tue, 05 Mar 2024 07:49:44 GMT",
|
|
425
|
+
Digest: "sha-256=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
|
|
426
|
+
Signature: "keyId=\"https://example.com/key2\",algorithm=\"x-attacker-supplied\",headers=\"(request-target) date digest\",signature=\"AAAA\""
|
|
427
|
+
},
|
|
428
|
+
body: ""
|
|
429
|
+
}), {
|
|
430
|
+
documentLoader: mockDocumentLoader,
|
|
431
|
+
contextLoader: mockDocumentLoader,
|
|
432
|
+
meterProvider
|
|
433
|
+
})).verified);
|
|
434
|
+
const measurements = recorder.getMeasurements("activitypub.signature.verification.duration");
|
|
435
|
+
assertEquals(measurements.length, 1);
|
|
436
|
+
assertFalse("http_signatures.algorithm" in measurements[0].attributes);
|
|
437
|
+
});
|
|
438
|
+
});
|
|
233
439
|
test("signRequest() and verifyRequest() [rfc9421] implementation", async () => {
|
|
234
440
|
const currentTimestamp = 1709626184;
|
|
235
441
|
const currentTime = Temporal.Instant.from("2024-03-05T08:09:44Z");
|
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-
|
|
6
|
-
import { t as assertRejects } from "../assert_rejects-
|
|
5
|
+
import "../std__assert-BTEgfoJo.mjs";
|
|
6
|
+
import { t as assertRejects } from "../assert_rejects-DQP-q39h.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-BoWaYRHm.mjs";
|
|
9
|
+
import { c as rsaPublicKey3, i as rsaPrivateKey2, o as rsaPublicKey1, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CSYsOMFG.mjs";
|
|
10
10
|
import { CryptographicKey, Multikey } from "@fedify/vocab";
|
|
11
11
|
import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
12
12
|
import { FetchError } from "@fedify/vocab-runtime";
|
package/dist/sig/ld.test.mjs
CHANGED
|
@@ -2,14 +2,14 @@ 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 { n as assertFalse, t as assertRejects } from "../assert_rejects-
|
|
5
|
+
import { n as assertGreaterOrEqual, r as assertFalse, t as assertRejects } from "../assert_rejects-DQP-q39h.mjs";
|
|
6
6
|
import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
|
|
7
7
|
import { t as assert } from "../assert-DikXweDx.mjs";
|
|
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-
|
|
8
|
+
import { i as generateCryptoKeyPair } from "../key-BoWaYRHm.mjs";
|
|
9
|
+
import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CSYsOMFG.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-B5K1mSuG.mjs";
|
|
11
11
|
import { CryptographicKey } from "@fedify/vocab";
|
|
12
|
-
import { mockDocumentLoader, test } from "@fedify/fixture";
|
|
12
|
+
import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
|
|
13
13
|
import { encodeBase64 } from "byte-encodings/base64";
|
|
14
14
|
//#region src/sig/ld.test.ts
|
|
15
15
|
test("attachSignature()", () => {
|
|
@@ -249,5 +249,138 @@ test("verifyJsonLd()", async () => {
|
|
|
249
249
|
contextLoader: mockDocumentLoader
|
|
250
250
|
}));
|
|
251
251
|
});
|
|
252
|
+
test("verifyJsonLd() records verification duration metric", async (t) => {
|
|
253
|
+
await t.step("verified path records result=verified with bounded type", async () => {
|
|
254
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
255
|
+
assert(await verifyJsonLd(testVector, {
|
|
256
|
+
documentLoader: mockDocumentLoader,
|
|
257
|
+
contextLoader: mockDocumentLoader,
|
|
258
|
+
meterProvider
|
|
259
|
+
}));
|
|
260
|
+
const measurements = recorder.getMeasurements("activitypub.signature.verification.duration");
|
|
261
|
+
assertEquals(measurements.length, 1);
|
|
262
|
+
const m = measurements[0];
|
|
263
|
+
assertEquals(m.type, "histogram");
|
|
264
|
+
assertGreaterOrEqual(m.value, 0);
|
|
265
|
+
assertEquals(m.attributes["activitypub.signature.kind"], "linked_data");
|
|
266
|
+
assertEquals(m.attributes["activitypub.signature.result"], "verified");
|
|
267
|
+
assertEquals(m.attributes["ld_signatures.type"], "RsaSignature2017");
|
|
268
|
+
});
|
|
269
|
+
await t.step("missing signature records result=missing without type", async () => {
|
|
270
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
271
|
+
assertFalse(await verifyJsonLd(document, {
|
|
272
|
+
documentLoader: mockDocumentLoader,
|
|
273
|
+
contextLoader: mockDocumentLoader,
|
|
274
|
+
meterProvider
|
|
275
|
+
}));
|
|
276
|
+
const measurements = recorder.getMeasurements("activitypub.signature.verification.duration");
|
|
277
|
+
assertEquals(measurements.length, 1);
|
|
278
|
+
assertEquals(measurements[0].attributes["activitypub.signature.kind"], "linked_data");
|
|
279
|
+
assertEquals(measurements[0].attributes["activitypub.signature.result"], "missing");
|
|
280
|
+
assertFalse("ld_signatures.type" in measurements[0].attributes);
|
|
281
|
+
});
|
|
282
|
+
await t.step("invalid signature records result=rejected", async () => {
|
|
283
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
284
|
+
assertFalse(await verifyJsonLd({
|
|
285
|
+
...testVector,
|
|
286
|
+
signature: {
|
|
287
|
+
...testVector.signature,
|
|
288
|
+
signatureValue: encodeBase64(new Uint8Array([
|
|
289
|
+
1,
|
|
290
|
+
2,
|
|
291
|
+
3,
|
|
292
|
+
4
|
|
293
|
+
]))
|
|
294
|
+
}
|
|
295
|
+
}, {
|
|
296
|
+
documentLoader: mockDocumentLoader,
|
|
297
|
+
contextLoader: mockDocumentLoader,
|
|
298
|
+
meterProvider
|
|
299
|
+
}));
|
|
300
|
+
const measurements = recorder.getMeasurements("activitypub.signature.verification.duration");
|
|
301
|
+
assertEquals(measurements.length, 1);
|
|
302
|
+
assertEquals(measurements[0].attributes["activitypub.signature.result"], "rejected");
|
|
303
|
+
assertEquals(measurements[0].attributes["ld_signatures.type"], "RsaSignature2017");
|
|
304
|
+
});
|
|
305
|
+
await t.step("malformed (null) signature property records result=rejected, not missing", async () => {
|
|
306
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
307
|
+
assertFalse(await verifyJsonLd({
|
|
308
|
+
...document,
|
|
309
|
+
signature: null
|
|
310
|
+
}, {
|
|
311
|
+
documentLoader: mockDocumentLoader,
|
|
312
|
+
contextLoader: mockDocumentLoader,
|
|
313
|
+
meterProvider
|
|
314
|
+
}));
|
|
315
|
+
const measurements = recorder.getMeasurements("activitypub.signature.verification.duration");
|
|
316
|
+
assertEquals(measurements.length, 1);
|
|
317
|
+
assertEquals(measurements[0].attributes["activitypub.signature.result"], "rejected");
|
|
318
|
+
assertFalse("ld_signatures.type" in measurements[0].attributes);
|
|
319
|
+
});
|
|
320
|
+
await t.step("key fetch records result=fetched on a cold cache", async () => {
|
|
321
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
322
|
+
assert(await verifyJsonLd(testVector, {
|
|
323
|
+
documentLoader: mockDocumentLoader,
|
|
324
|
+
contextLoader: mockDocumentLoader,
|
|
325
|
+
meterProvider
|
|
326
|
+
}));
|
|
327
|
+
const measurements = recorder.getMeasurements("activitypub.signature.key_fetch.duration");
|
|
328
|
+
assertEquals(measurements.length, 1);
|
|
329
|
+
assertGreaterOrEqual(measurements[0].value, 0);
|
|
330
|
+
assertEquals(measurements[0].attributes["activitypub.signature.kind"], "linked_data");
|
|
331
|
+
assertEquals(measurements[0].attributes["activitypub.signature.key_fetch.result"], "fetched");
|
|
332
|
+
});
|
|
333
|
+
await t.step("missing signature emits no key_fetch measurement", async () => {
|
|
334
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
335
|
+
assertFalse(await verifyJsonLd(document, {
|
|
336
|
+
documentLoader: mockDocumentLoader,
|
|
337
|
+
contextLoader: mockDocumentLoader,
|
|
338
|
+
meterProvider
|
|
339
|
+
}));
|
|
340
|
+
assertEquals(recorder.getMeasurements("activitypub.signature.key_fetch.duration").length, 0);
|
|
341
|
+
});
|
|
342
|
+
await t.step("cached-key retry emits two key_fetch measurements: hit then fetched", async () => {
|
|
343
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
344
|
+
assert(await verifyJsonLd(testVector, {
|
|
345
|
+
documentLoader: mockDocumentLoader,
|
|
346
|
+
contextLoader: mockDocumentLoader,
|
|
347
|
+
meterProvider,
|
|
348
|
+
keyCache: {
|
|
349
|
+
async get(keyId) {
|
|
350
|
+
return new CryptographicKey({
|
|
351
|
+
id: keyId,
|
|
352
|
+
owner: new URL("https://activitypub.academy/users/brauca_darradiul"),
|
|
353
|
+
publicKey: (await generateCryptoKeyPair("RSASSA-PKCS1-v1_5")).publicKey
|
|
354
|
+
});
|
|
355
|
+
},
|
|
356
|
+
set(_keyId, _key) {
|
|
357
|
+
return Promise.resolve();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}));
|
|
361
|
+
const measurements = recorder.getMeasurements("activitypub.signature.key_fetch.duration");
|
|
362
|
+
assertEquals(measurements.length, 2);
|
|
363
|
+
assertEquals(measurements[0].attributes["activitypub.signature.key_fetch.result"], "hit");
|
|
364
|
+
assertEquals(measurements[1].attributes["activitypub.signature.key_fetch.result"], "fetched");
|
|
365
|
+
});
|
|
366
|
+
await t.step("unknown signature type omits the ld_signatures.type metric attribute", async () => {
|
|
367
|
+
const [meterProvider, recorder] = createTestMeterProvider();
|
|
368
|
+
assertFalse(await verifyJsonLd({
|
|
369
|
+
...testVector,
|
|
370
|
+
signature: {
|
|
371
|
+
...testVector.signature,
|
|
372
|
+
type: "MadeUpSignature9999"
|
|
373
|
+
}
|
|
374
|
+
}, {
|
|
375
|
+
documentLoader: mockDocumentLoader,
|
|
376
|
+
contextLoader: mockDocumentLoader,
|
|
377
|
+
meterProvider
|
|
378
|
+
}));
|
|
379
|
+
const measurements = recorder.getMeasurements("activitypub.signature.verification.duration");
|
|
380
|
+
assertEquals(measurements.length, 1);
|
|
381
|
+
assertEquals(measurements[0].attributes["activitypub.signature.result"], "rejected");
|
|
382
|
+
assertFalse("ld_signatures.type" in measurements[0].attributes);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
252
385
|
//#endregion
|
|
253
386
|
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-
|
|
5
|
-
const require_proof = require("../proof-
|
|
4
|
+
const require_http = require("../http-CubOB9wq.cjs");
|
|
5
|
+
const require_proof = require("../proof-BUWfVr6Q.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.d.cts
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-
|
|
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-D6aw3j2U.cjs";
|
|
3
3
|
import { i as getKeyOwner, n as GetKeyOwnerOptions, r as doesActorOwnKey, t as DoesActorOwnKeyOptions } from "../owner-DEvZuyOE.cjs";
|
|
4
|
-
import { _ as hasSignatureLike, a as createProof, b as verifySignature, c as verifyObject, d as SignJsonLdOptions, f as VerifyJsonLdOptions, g as detachSignature, h as createSignature, i as VerifyProofOptions, l as verifyProof, m as attachSignature, n as SignObjectOptions, o as hasProofLike, p as VerifySignatureOptions, r as VerifyObjectOptions, s as signObject, t as CreateProofOptions, u as CreateSignatureOptions, v as signJsonLd, y as verifyJsonLd } from "../mod-
|
|
4
|
+
import { _ as hasSignatureLike, a as createProof, b as verifySignature, c as verifyObject, d as SignJsonLdOptions, f as VerifyJsonLdOptions, g as detachSignature, h as createSignature, i as VerifyProofOptions, l as verifyProof, m as attachSignature, n as SignObjectOptions, o as hasProofLike, p as VerifySignatureOptions, r as VerifyObjectOptions, s as signObject, t as CreateProofOptions, u as CreateSignatureOptions, v as signJsonLd, y as verifyJsonLd } from "../mod-BDhgfjP7.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, hasProofLike, hasSignatureLike, importJwk, parseAcceptSignature, signJsonLd, signObject, signRequest, validateAcceptSignature, verifyJsonLd, verifyObject, verifyProof, verifyRequest, verifyRequestDetailed, verifySignature };
|