@fedify/fedify 2.4.2 → 2.4.4
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/{builder-9ovCSqLp.mjs → builder-DdY0E0w9.mjs} +2 -2
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-G2J_bU0u.mjs → compound-proof-fxkyiXYM.mjs} +2 -2
- package/dist/{deno-BZ4z-lJJ.mjs → deno-CO1X_K_q.mjs} +1 -1
- package/dist/{docloader-DUXXI-7G.mjs → docloader-DFB_TNVp.mjs} +6 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +3 -3
- package/dist/federation/handler-compound-proof.test.mjs +5 -5
- package/dist/federation/handler.test.mjs +258 -5
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/inbox-report.test.mjs +5 -5
- package/dist/federation/inbox.test.mjs +43 -0
- package/dist/federation/keycache.test.mjs +20 -9
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +173 -11
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +4 -4
- package/dist/federation/portable-actor.test.mjs +3 -3
- package/dist/federation/portable-chain.test.mjs +3 -3
- package/dist/federation/portable-collection.test.mjs +4 -4
- package/dist/federation/portable-delivery.test.mjs +2 -2
- package/dist/federation/portable-gateway-keys.test.mjs +5 -5
- package/dist/federation/portable-inbox.test.mjs +4 -4
- package/dist/federation/portable-parse-uri.test.mjs +1 -1
- package/dist/federation/portable-route-activity.test.mjs +2 -2
- package/dist/federation/portable.test.mjs +3 -3
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{federation-DpT8bDM6.cjs → federation-Bf4FEork.cjs} +122 -21
- package/dist/{http-Qq7jR2rC.js → http-D_AGUmSq.js} +17 -3
- package/dist/{http-B5w_qcIg.mjs → http-DkLKT8Vp.mjs} +4 -4
- package/dist/{http-x1satM4y.cjs → http-g5f1D2tm.cjs} +17 -3
- package/dist/inbox-Di1-EKqa.mjs +188 -0
- package/dist/{keycache-D-MNltOk.mjs → keycache-N2opu4-3.mjs} +1 -1
- package/dist/{kv-cache-BgQMAKIg.mjs → kv-cache-BxfOpUP9.mjs} +1 -1
- package/dist/{kv-cache-B0SZfzFb.js → kv-cache-C0AsMZ4U.js} +5 -1
- package/dist/{kv-cache-D4throaQ.cjs → kv-cache-HsXADkl9.cjs} +5 -1
- package/dist/{ld-Dkkyk65e.mjs → ld-Cy86r-Ty.mjs} +18 -4
- package/dist/{metrics-CRv6_IBg.mjs → metrics-BguhdXt-.mjs} +1 -1
- package/dist/{middleware-BqefXH6B.js → middleware-BOBF7req.js} +122 -21
- package/dist/{middleware-DFtNaBcH.mjs → middleware-BYojyPi4.mjs} +1 -1
- package/dist/{middleware-C-swet0z.mjs → middleware-C_gtR3fV.mjs} +104 -180
- package/dist/mod.cjs +4 -4
- package/dist/mod.js +4 -4
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-BHUhedVc.mjs → owner-Dc-Lg_Xn.mjs} +3 -3
- package/dist/{portable-collection-DL-1Wy1s.cjs → portable-collection-Bl2UkqcO.cjs} +1 -1
- package/dist/{portable-collection-CfIc190c.js → portable-collection-CjdP7fQ4.js} +1 -1
- package/dist/{portable-collection-DONEPTXV.mjs → portable-collection-DSKqMgvr.mjs} +2 -2
- package/dist/{portable-key-Cdw3vfl-.mjs → portable-key-ClXZ25LT.mjs} +1 -1
- package/dist/{proof-DuSE1ABD.mjs → proof-kJtQ49gr.mjs} +3 -3
- package/dist/{send-D9oTjA6A.mjs → send-D6hcL5qf.mjs} +3 -3
- package/dist/sig/compound-proof-discovery.test.mjs +1 -1
- package/dist/sig/compound-proof-producer.test.mjs +1 -1
- package/dist/sig/compound-proof-verification.test.mjs +2 -2
- package/dist/sig/compound-proof.test.mjs +2 -2
- package/dist/sig/http.test.mjs +3 -3
- package/dist/sig/key.test.mjs +196 -1
- package/dist/sig/ld.test.mjs +46 -1
- package/dist/sig/mitra-receive-interop.test.mjs +4 -4
- package/dist/sig/mitra-send-interop.test.mjs +3 -3
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +46 -1
- package/dist/sig/portable-collection.test.mjs +1 -1
- package/dist/sig/portable-interop.test.mjs +3 -3
- package/dist/sig/portable-key.test.mjs +6 -6
- package/dist/sig/proof.test.mjs +54 -3
- package/dist/sig/tootik-request-interop.test.mjs +4 -4
- package/dist/sig/tootik-send-interop.test.mjs +3 -3
- package/dist/sig/verification.test.mjs +3 -3
- package/dist/{temporal-iyAxBq0I.mjs → temporal-C8WqyM34.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/utils/docloader.test.mjs +176 -37
- 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
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-Dmm3t92W.cjs");
|
|
3
3
|
const require_transformers = require("./transformers-BalCrSsr.cjs");
|
|
4
|
-
const require_http = require("./http-
|
|
4
|
+
const require_http = require("./http-g5f1D2tm.cjs");
|
|
5
5
|
const require_types = require("./types-poIao40f.cjs");
|
|
6
|
-
const require_portable_collection = require("./portable-collection-
|
|
7
|
-
const require_kv_cache = require("./kv-cache-
|
|
6
|
+
const require_portable_collection = require("./portable-collection-Bl2UkqcO.cjs");
|
|
7
|
+
const require_kv_cache = require("./kv-cache-HsXADkl9.cjs");
|
|
8
8
|
let _logtape_logtape = require("@logtape/logtape");
|
|
9
9
|
let _fedify_uri_template = require("@fedify/uri-template");
|
|
10
10
|
let _fedify_vocab = require("@fedify/vocab");
|
|
@@ -2779,12 +2779,41 @@ async function isInAudience(object, actor, options = {}) {
|
|
|
2779
2779
|
//#endregion
|
|
2780
2780
|
//#region src/federation/inbox.ts
|
|
2781
2781
|
const Temporal$10 = require_http.Temporal;
|
|
2782
|
+
/**
|
|
2783
|
+
* Checks whether an activity's ID and all of its actor IDs have the same
|
|
2784
|
+
* [FEP-fe34] origin. Activities without an ID have no origin to authenticate.
|
|
2785
|
+
*
|
|
2786
|
+
* Origins are compared by `getAuthenticationOrigin()`, so a portable activity
|
|
2787
|
+
* and its portable actors, including compatible identifiers on any gateway,
|
|
2788
|
+
* are compared by their DIDs. An ID that has no supported origin, such as an
|
|
2789
|
+
* opaque URN, never matches.
|
|
2790
|
+
*
|
|
2791
|
+
* The caller is responsible for rejecting a missing primary actor ID before
|
|
2792
|
+
* relying on this check.
|
|
2793
|
+
*
|
|
2794
|
+
* [FEP-fe34]: https://w3id.org/fep/fe34
|
|
2795
|
+
* @internal
|
|
2796
|
+
*/
|
|
2797
|
+
function actorsHaveSameOrigin(activity) {
|
|
2798
|
+
if (activity.id == null) return true;
|
|
2799
|
+
const origin = require_http.getAuthenticationOrigin(activity.id);
|
|
2800
|
+
return origin != null && activity.actorIds.every((actor) => require_http.getAuthenticationOrigin(actor) === origin);
|
|
2801
|
+
}
|
|
2782
2802
|
async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
2783
2803
|
const logger = (0, _logtape_logtape.getLogger)([
|
|
2784
2804
|
"fedify",
|
|
2785
2805
|
"federation",
|
|
2786
2806
|
"inbox"
|
|
2787
2807
|
]);
|
|
2808
|
+
if (activity.actorId == null) {
|
|
2809
|
+
logger.error("Missing actor.", { activity: json });
|
|
2810
|
+
span.setStatus({
|
|
2811
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
2812
|
+
message: "Missing actor."
|
|
2813
|
+
});
|
|
2814
|
+
require_http.recordInboxActivity(meterProvider, "rejected", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
2815
|
+
return "missingActor";
|
|
2816
|
+
}
|
|
2788
2817
|
let cacheKey = null;
|
|
2789
2818
|
if (activity.id != null) {
|
|
2790
2819
|
const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, (0, _fedify_vocab.getTypeId)(activity).href);
|
|
@@ -2820,15 +2849,6 @@ async function routeActivity({ context: ctx, json, originalJson, normalizedActiv
|
|
|
2820
2849
|
return "alreadyProcessed";
|
|
2821
2850
|
}
|
|
2822
2851
|
}
|
|
2823
|
-
if (activity.actorId == null) {
|
|
2824
|
-
logger.error("Missing actor.", { activity: json });
|
|
2825
|
-
span.setStatus({
|
|
2826
|
-
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
2827
|
-
message: "Missing actor."
|
|
2828
|
-
});
|
|
2829
|
-
require_http.recordInboxActivity(meterProvider, "rejected", (0, _fedify_vocab.getTypeId)(activity).href);
|
|
2830
|
-
return "missingActor";
|
|
2831
|
-
}
|
|
2832
2852
|
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
|
2833
2853
|
if (queue != null) {
|
|
2834
2854
|
const carrier = {};
|
|
@@ -2938,7 +2958,7 @@ async function routeActivity({ context: ctx, json, originalJson, normalizedActiv
|
|
|
2938
2958
|
//#endregion
|
|
2939
2959
|
//#region src/federation/keycache.ts
|
|
2940
2960
|
const Temporal$9 = require_http.Temporal;
|
|
2941
|
-
const KEY_CACHE_GENERATION = "
|
|
2961
|
+
const KEY_CACHE_GENERATION = "3";
|
|
2942
2962
|
const COMPATIBLE_KEY_SEGMENT = "__compatible";
|
|
2943
2963
|
const MAX_OWNER_BYTES = 32 * 1024;
|
|
2944
2964
|
function isCompatibleKeyEntry(value) {
|
|
@@ -4746,13 +4766,13 @@ function recordCollectionMetrics(meterProvider, base, result, options = {}) {
|
|
|
4746
4766
|
* @param parameters The parameters for handling the collection.
|
|
4747
4767
|
* @returns A promise that resolves to an HTTP response.
|
|
4748
4768
|
*/
|
|
4749
|
-
async function handleCollection(request, { name: name$
|
|
4750
|
-
const spanName = name$
|
|
4769
|
+
async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, portable, tracerProvider, meterProvider, onUnauthorized, onNotFound }) {
|
|
4770
|
+
const spanName = name$1.trim().replace(/\s+/g, "_");
|
|
4751
4771
|
tracerProvider = tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
|
|
4752
4772
|
const tracer = tracerProvider.getTracer(require_http.name, require_http.version);
|
|
4753
4773
|
const cursor = new URL(request.url).searchParams.get("cursor");
|
|
4754
4774
|
const metricBase = {
|
|
4755
|
-
kind: getCollectionMetricKind(name$
|
|
4775
|
+
kind: getCollectionMetricKind(name$1),
|
|
4756
4776
|
page: cursor != null,
|
|
4757
4777
|
dispatcher: "built_in"
|
|
4758
4778
|
};
|
|
@@ -4798,7 +4818,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
4798
4818
|
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
4799
4819
|
return await onNotFound(request);
|
|
4800
4820
|
}
|
|
4801
|
-
const items = filterCollectionItems(page.items, name$
|
|
4821
|
+
const items = filterCollectionItems(page.items, name$1, filterPredicate);
|
|
4802
4822
|
itemCount = items.length;
|
|
4803
4823
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
4804
4824
|
return items;
|
|
@@ -4840,7 +4860,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
4840
4860
|
} else {
|
|
4841
4861
|
const uri = new URL(portable == null ? baseUri : pageBase);
|
|
4842
4862
|
uri.searchParams.set("cursor", cursor);
|
|
4843
|
-
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$
|
|
4863
|
+
const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
|
|
4844
4864
|
kind: _opentelemetry_api.SpanKind.SERVER,
|
|
4845
4865
|
attributes: {
|
|
4846
4866
|
"activitypub.collection.id": uri.href,
|
|
@@ -4856,7 +4876,7 @@ async function handleCollection(request, { name: name$2, identifier, uriGetter,
|
|
|
4856
4876
|
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
4857
4877
|
return await onNotFound(request);
|
|
4858
4878
|
}
|
|
4859
|
-
const items = filterCollectionItems(page.items, name$
|
|
4879
|
+
const items = filterCollectionItems(page.items, name$1, filterPredicate);
|
|
4860
4880
|
itemCount = items.length;
|
|
4861
4881
|
span.setAttribute("fedify.collection.items", itemCount);
|
|
4862
4882
|
return {
|
|
@@ -6005,11 +6025,49 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
6005
6025
|
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
6006
6026
|
}
|
|
6007
6027
|
}
|
|
6028
|
+
if (!skipSignatureVerification && activity.id != null && !actorsHaveSameOrigin(activity)) {
|
|
6029
|
+
observation.authentication = {
|
|
6030
|
+
status: "rejected",
|
|
6031
|
+
reason: { type: "verificationFailed" }
|
|
6032
|
+
};
|
|
6033
|
+
logger.error("The activity ({activityId}) and its actors do not have the same origin.", {
|
|
6034
|
+
activity: json,
|
|
6035
|
+
recipient,
|
|
6036
|
+
activityId: activity.id.href,
|
|
6037
|
+
actorIds: activity.actorIds.map((actor) => actor.href)
|
|
6038
|
+
});
|
|
6039
|
+
span.setStatus({
|
|
6040
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
6041
|
+
message: `The activity (${activity.id.href}) and its actors do not have the same origin.`
|
|
6042
|
+
});
|
|
6043
|
+
return new Response("The activity and its actors do not have the same origin.", {
|
|
6044
|
+
status: 401,
|
|
6045
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6046
|
+
});
|
|
6047
|
+
}
|
|
6008
6048
|
observation.authentication = skipSignatureVerification ? { status: "skipped" } : {
|
|
6009
6049
|
status: "verified",
|
|
6010
6050
|
attempts: observation.verification.attempts.filter((attempt) => attempt.status === "verified")
|
|
6011
6051
|
};
|
|
6012
6052
|
observation.stage = "dispatch";
|
|
6053
|
+
if (activity.actorId == null) {
|
|
6054
|
+
observation.result = {
|
|
6055
|
+
disposition: "rejected",
|
|
6056
|
+
reason: "missingActor"
|
|
6057
|
+
};
|
|
6058
|
+
logger.error("Missing actor.", {
|
|
6059
|
+
activity: json,
|
|
6060
|
+
recipient
|
|
6061
|
+
});
|
|
6062
|
+
span.setStatus({
|
|
6063
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
6064
|
+
message: "Missing actor."
|
|
6065
|
+
});
|
|
6066
|
+
return new Response("Missing actor.", {
|
|
6067
|
+
status: 400,
|
|
6068
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
6069
|
+
});
|
|
6070
|
+
}
|
|
6013
6071
|
const routeResult = await routeActivity({
|
|
6014
6072
|
context: ctx,
|
|
6015
6073
|
json,
|
|
@@ -6230,8 +6288,8 @@ var CustomCollectionHandler = class {
|
|
|
6230
6288
|
* @param CollectionPage The CollectionPage constructor.
|
|
6231
6289
|
* @param filterPredicate Optional filter predicate for items.
|
|
6232
6290
|
*/
|
|
6233
|
-
constructor(name$
|
|
6234
|
-
this.name = name$
|
|
6291
|
+
constructor(name$2, values, context, callbacks, tracerProvider = _opentelemetry_api.trace.getTracerProvider(), meterProvider, Collection, CollectionPage, filterPredicate, portable) {
|
|
6292
|
+
this.name = name$2;
|
|
6235
6293
|
this.values = values;
|
|
6236
6294
|
this.context = context;
|
|
6237
6295
|
this.callbacks = callbacks;
|
|
@@ -9357,6 +9415,35 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
9357
9415
|
span.setAttribute("activitypub.activity.id", activity.id.href);
|
|
9358
9416
|
listenerSpan.setAttribute("activitypub.activity.id", activity.id.href);
|
|
9359
9417
|
}
|
|
9418
|
+
if (activity.actorId == null) {
|
|
9419
|
+
logger.error("Missing actor in queued incoming activity.", {
|
|
9420
|
+
activityId: activity.id?.href,
|
|
9421
|
+
activity: message.activity,
|
|
9422
|
+
recipient: message.identifier
|
|
9423
|
+
});
|
|
9424
|
+
listenerSpan.setStatus({
|
|
9425
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
9426
|
+
message: "Missing actor."
|
|
9427
|
+
});
|
|
9428
|
+
require_http.recordInboxActivity(this.meterProvider, "rejected", activityType);
|
|
9429
|
+
listenerSpan.end();
|
|
9430
|
+
return;
|
|
9431
|
+
}
|
|
9432
|
+
if (!this.skipSignatureVerification && activity.id != null && !actorsHaveSameOrigin(activity)) {
|
|
9433
|
+
logger.error("The queued activity ({activityId}) and its actors do not have the same origin.", {
|
|
9434
|
+
activityId: activity.id.href,
|
|
9435
|
+
actorIds: activity.actorIds.map((actor) => actor.href),
|
|
9436
|
+
activity: message.activity,
|
|
9437
|
+
recipient: message.identifier
|
|
9438
|
+
});
|
|
9439
|
+
listenerSpan.setStatus({
|
|
9440
|
+
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
9441
|
+
message: `The activity (${activity.id.href}) and its actors do not have the same origin.`
|
|
9442
|
+
});
|
|
9443
|
+
require_http.recordInboxActivity(this.meterProvider, "rejected", activityType);
|
|
9444
|
+
listenerSpan.end();
|
|
9445
|
+
return;
|
|
9446
|
+
}
|
|
9360
9447
|
cacheKey = activity.id == null ? null : [
|
|
9361
9448
|
...this.kvPrefixes.activityIdempotence,
|
|
9362
9449
|
context.origin,
|
|
@@ -11433,6 +11520,20 @@ var ContextImpl = class ContextImpl {
|
|
|
11433
11520
|
recipient,
|
|
11434
11521
|
activity: json
|
|
11435
11522
|
});
|
|
11523
|
+
if (activity.actorId == null) {
|
|
11524
|
+
logger.debug("Activity is missing an actor; unable to verify.", {
|
|
11525
|
+
recipient,
|
|
11526
|
+
activity: json
|
|
11527
|
+
});
|
|
11528
|
+
return false;
|
|
11529
|
+
}
|
|
11530
|
+
if (activity.id != null && !actorsHaveSameOrigin(activity)) {
|
|
11531
|
+
logger.debug("Activity has actors from different origins; unable to verify.", {
|
|
11532
|
+
recipient,
|
|
11533
|
+
activity: json
|
|
11534
|
+
});
|
|
11535
|
+
return false;
|
|
11536
|
+
}
|
|
11436
11537
|
const routeResult = await routeActivity({
|
|
11437
11538
|
context: this,
|
|
11438
11539
|
json,
|
|
@@ -15,7 +15,7 @@ import serialize from "json-canon";
|
|
|
15
15
|
import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
|
|
16
16
|
//#region deno.json
|
|
17
17
|
var name = "@fedify/fedify";
|
|
18
|
-
var version = "2.4.
|
|
18
|
+
var version = "2.4.4";
|
|
19
19
|
//#endregion
|
|
20
20
|
//#region src/sig/portable-key-id.ts
|
|
21
21
|
const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
|
|
@@ -1570,16 +1570,23 @@ async function importJwk(jwk, type) {
|
|
|
1570
1570
|
/**
|
|
1571
1571
|
* Fetches the document that the given actor URI dereferences to, and returns
|
|
1572
1572
|
* the actor only if the host that served the document is authoritative for
|
|
1573
|
-
* the id the document claims.
|
|
1573
|
+
* the id the document claims, and that id is the actor that was asked for.
|
|
1574
1574
|
*
|
|
1575
1575
|
* Only the origin that serves an actor id can speak for it. Without that
|
|
1576
1576
|
* rule any host could serve a document describing somebody else's actor—and
|
|
1577
1577
|
* listing its own keys as that actor's.
|
|
1578
1578
|
*
|
|
1579
|
+
* The returned actor always has exactly the requested `actorId` as its `id`.
|
|
1580
|
+
* A document loader follows redirects and alternate document links, so the
|
|
1581
|
+
* document an actor id leads to may describe a different actor altogether,
|
|
1582
|
+
* on the same origin or on another; accepting it would attribute that other
|
|
1583
|
+
* actor's keys to the requested one. See GHSA-8863-8468-7hq7.
|
|
1584
|
+
*
|
|
1579
1585
|
* @param actorId The URI of the actor to fetch.
|
|
1580
1586
|
* @param options Options for fetching the document.
|
|
1581
1587
|
* @returns The actor, or `null` if the document cannot be fetched, is not an
|
|
1582
|
-
* actor,
|
|
1588
|
+
* actor, belongs to another origin, or describes an actor other
|
|
1589
|
+
* than the requested one.
|
|
1583
1590
|
* @internal
|
|
1584
1591
|
*/
|
|
1585
1592
|
async function fetchActorDocument(actorId, options = {}) {
|
|
@@ -1645,6 +1652,13 @@ async function fetchActorDocumentInternal(actorId, options, propagateContextFetc
|
|
|
1645
1652
|
});
|
|
1646
1653
|
return null;
|
|
1647
1654
|
}
|
|
1655
|
+
if (object.id.href !== actorId.href) {
|
|
1656
|
+
logger.debug("The document fetched for the actor {actorId} is the actor {documentActorId} instead; refusing to treat it as the requested actor's document.", {
|
|
1657
|
+
actorId: actorId.href,
|
|
1658
|
+
documentActorId: object.id.href
|
|
1659
|
+
});
|
|
1660
|
+
return null;
|
|
1661
|
+
}
|
|
1648
1662
|
return object;
|
|
1649
1663
|
}
|
|
1650
1664
|
/**
|
|
@@ -3,12 +3,12 @@ import "urlpattern-polyfill";
|
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { m as parseKeyIdString } from "./portable-key-id-Ca30-hOa.mjs";
|
|
5
5
|
import { a as verificationObservation, i as triedKey, n as observeCheck, t as observeAttempt } from "./verification-Dpfcw8ml.mjs";
|
|
6
|
-
import { n as version, t as name } from "./deno-
|
|
7
|
-
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-
|
|
6
|
+
import { n as version, t as name } from "./deno-CO1X_K_q.mjs";
|
|
7
|
+
import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-BguhdXt-.mjs";
|
|
8
8
|
import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-CPVqki78.mjs";
|
|
9
9
|
import { a as promoteKeyOwnerEvidence } from "./key-owner-evidence-APB6NpSk.mjs";
|
|
10
|
-
import { S as fetchKeyDetailed, T as validateCryptoKey, v as bypassKeyCacheReads } from "./ld-
|
|
11
|
-
import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-
|
|
10
|
+
import { S as fetchKeyDetailed, T as validateCryptoKey, v as bypassKeyCacheReads } from "./ld-Cy86r-Ty.mjs";
|
|
11
|
+
import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-ClXZ25LT.mjs";
|
|
12
12
|
import { getLogger } from "@logtape/logtape";
|
|
13
13
|
import { CryptographicKey } from "@fedify/vocab";
|
|
14
14
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
@@ -17,7 +17,7 @@ json_canon = require_rolldown_runtime.__toESM(json_canon, 1);
|
|
|
17
17
|
let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
18
18
|
//#region deno.json
|
|
19
19
|
var name = "@fedify/fedify";
|
|
20
|
-
var version = "2.4.
|
|
20
|
+
var version = "2.4.4";
|
|
21
21
|
//#endregion
|
|
22
22
|
//#region src/sig/portable-key-id.ts
|
|
23
23
|
const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
|
|
@@ -5138,16 +5138,23 @@ async function importJwk(jwk, type) {
|
|
|
5138
5138
|
/**
|
|
5139
5139
|
* Fetches the document that the given actor URI dereferences to, and returns
|
|
5140
5140
|
* the actor only if the host that served the document is authoritative for
|
|
5141
|
-
* the id the document claims.
|
|
5141
|
+
* the id the document claims, and that id is the actor that was asked for.
|
|
5142
5142
|
*
|
|
5143
5143
|
* Only the origin that serves an actor id can speak for it. Without that
|
|
5144
5144
|
* rule any host could serve a document describing somebody else's actor—and
|
|
5145
5145
|
* listing its own keys as that actor's.
|
|
5146
5146
|
*
|
|
5147
|
+
* The returned actor always has exactly the requested `actorId` as its `id`.
|
|
5148
|
+
* A document loader follows redirects and alternate document links, so the
|
|
5149
|
+
* document an actor id leads to may describe a different actor altogether,
|
|
5150
|
+
* on the same origin or on another; accepting it would attribute that other
|
|
5151
|
+
* actor's keys to the requested one. See GHSA-8863-8468-7hq7.
|
|
5152
|
+
*
|
|
5147
5153
|
* @param actorId The URI of the actor to fetch.
|
|
5148
5154
|
* @param options Options for fetching the document.
|
|
5149
5155
|
* @returns The actor, or `null` if the document cannot be fetched, is not an
|
|
5150
|
-
* actor,
|
|
5156
|
+
* actor, belongs to another origin, or describes an actor other
|
|
5157
|
+
* than the requested one.
|
|
5151
5158
|
* @internal
|
|
5152
5159
|
*/
|
|
5153
5160
|
async function fetchActorDocument(actorId, options = {}) {
|
|
@@ -5213,6 +5220,13 @@ async function fetchActorDocumentInternal(actorId, options, propagateContextFetc
|
|
|
5213
5220
|
});
|
|
5214
5221
|
return null;
|
|
5215
5222
|
}
|
|
5223
|
+
if (object.id.href !== actorId.href) {
|
|
5224
|
+
logger.debug("The document fetched for the actor {actorId} is the actor {documentActorId} instead; refusing to treat it as the requested actor's document.", {
|
|
5225
|
+
actorId: actorId.href,
|
|
5226
|
+
documentActorId: object.id.href
|
|
5227
|
+
});
|
|
5228
|
+
return null;
|
|
5229
|
+
}
|
|
5216
5230
|
return object;
|
|
5217
5231
|
}
|
|
5218
5232
|
/**
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Temporal } from "temporal-polyfill";
|
|
2
|
+
import "urlpattern-polyfill";
|
|
3
|
+
globalThis.addEventListener = () => {};
|
|
4
|
+
import { t as getAuthenticationOrigin } from "./portable-key-id-Ca30-hOa.mjs";
|
|
5
|
+
import { n as version, t as name } from "./deno-CO1X_K_q.mjs";
|
|
6
|
+
import { g as recordInboxActivity, n as getDurationMs, r as getFederationMetrics } from "./metrics-BguhdXt-.mjs";
|
|
7
|
+
import { getLogger } from "@logtape/logtape";
|
|
8
|
+
import { getTypeId } from "@fedify/vocab";
|
|
9
|
+
import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
|
|
10
|
+
//#region src/federation/inbox.ts
|
|
11
|
+
/**
|
|
12
|
+
* Checks whether an activity's ID and all of its actor IDs have the same
|
|
13
|
+
* [FEP-fe34] origin. Activities without an ID have no origin to authenticate.
|
|
14
|
+
*
|
|
15
|
+
* Origins are compared by `getAuthenticationOrigin()`, so a portable activity
|
|
16
|
+
* and its portable actors, including compatible identifiers on any gateway,
|
|
17
|
+
* are compared by their DIDs. An ID that has no supported origin, such as an
|
|
18
|
+
* opaque URN, never matches.
|
|
19
|
+
*
|
|
20
|
+
* The caller is responsible for rejecting a missing primary actor ID before
|
|
21
|
+
* relying on this check.
|
|
22
|
+
*
|
|
23
|
+
* [FEP-fe34]: https://w3id.org/fep/fe34
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
function actorsHaveSameOrigin(activity) {
|
|
27
|
+
if (activity.id == null) return true;
|
|
28
|
+
const origin = getAuthenticationOrigin(activity.id);
|
|
29
|
+
return origin != null && activity.actorIds.every((actor) => getAuthenticationOrigin(actor) === origin);
|
|
30
|
+
}
|
|
31
|
+
async function routeActivity({ context: ctx, json, originalJson, normalizedActivity, ldSignatureVerified, activity, recipient, inboxListeners, inboxContextFactory, listenerInboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, meterProvider, tracerProvider, idempotencyStrategy }) {
|
|
32
|
+
const logger = getLogger([
|
|
33
|
+
"fedify",
|
|
34
|
+
"federation",
|
|
35
|
+
"inbox"
|
|
36
|
+
]);
|
|
37
|
+
if (activity.actorId == null) {
|
|
38
|
+
logger.error("Missing actor.", { activity: json });
|
|
39
|
+
span.setStatus({
|
|
40
|
+
code: SpanStatusCode.ERROR,
|
|
41
|
+
message: "Missing actor."
|
|
42
|
+
});
|
|
43
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
44
|
+
return "missingActor";
|
|
45
|
+
}
|
|
46
|
+
let cacheKey = null;
|
|
47
|
+
if (activity.id != null) {
|
|
48
|
+
const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, getTypeId(activity).href);
|
|
49
|
+
const strategy = idempotencyStrategy ?? "per-inbox";
|
|
50
|
+
let keyString;
|
|
51
|
+
if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
|
|
52
|
+
else switch (strategy) {
|
|
53
|
+
case "global":
|
|
54
|
+
keyString = activity.id.href;
|
|
55
|
+
break;
|
|
56
|
+
case "per-origin":
|
|
57
|
+
keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
58
|
+
break;
|
|
59
|
+
case "per-inbox":
|
|
60
|
+
keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
|
|
61
|
+
break;
|
|
62
|
+
default: keyString = `${ctx.origin}\n${activity.id.href}`;
|
|
63
|
+
}
|
|
64
|
+
if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
|
|
65
|
+
}
|
|
66
|
+
if (cacheKey != null) {
|
|
67
|
+
if (await kv.get(cacheKey) === true) {
|
|
68
|
+
logger.debug("Activity {activityId} has already been processed.", {
|
|
69
|
+
activityId: activity.id?.href,
|
|
70
|
+
activity: json,
|
|
71
|
+
recipient
|
|
72
|
+
});
|
|
73
|
+
span.setStatus({
|
|
74
|
+
code: SpanStatusCode.UNSET,
|
|
75
|
+
message: `Activity ${activity.id?.href} has already been processed.`
|
|
76
|
+
});
|
|
77
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
78
|
+
return "alreadyProcessed";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
span.setAttribute("activitypub.actor.id", activity.actorId.href);
|
|
82
|
+
if (queue != null) {
|
|
83
|
+
const carrier = {};
|
|
84
|
+
propagation.inject(context.active(), carrier);
|
|
85
|
+
try {
|
|
86
|
+
await queue.enqueue({
|
|
87
|
+
type: "inbox",
|
|
88
|
+
id: crypto.randomUUID(),
|
|
89
|
+
baseUrl: ctx.origin,
|
|
90
|
+
activity: originalJson ?? json,
|
|
91
|
+
...normalizedActivity == null ? {} : { normalizedActivity },
|
|
92
|
+
...ldSignatureVerified == null ? {} : { ldSignatureVerified },
|
|
93
|
+
identifier: recipient,
|
|
94
|
+
attempt: 0,
|
|
95
|
+
started: (/* @__PURE__ */ new Date()).toISOString(),
|
|
96
|
+
traceContext: carrier
|
|
97
|
+
});
|
|
98
|
+
} catch (error) {
|
|
99
|
+
logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
|
|
100
|
+
error,
|
|
101
|
+
activityId: activity.id?.href,
|
|
102
|
+
activity: json,
|
|
103
|
+
recipient
|
|
104
|
+
});
|
|
105
|
+
span.setStatus({
|
|
106
|
+
code: SpanStatusCode.ERROR,
|
|
107
|
+
message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
|
|
108
|
+
});
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
getFederationMetrics(meterProvider).recordQueueTaskEnqueued({
|
|
112
|
+
role: "inbox",
|
|
113
|
+
queue,
|
|
114
|
+
activityType: getTypeId(activity).href
|
|
115
|
+
}, 0);
|
|
116
|
+
recordInboxActivity(meterProvider, "queued", getTypeId(activity).href);
|
|
117
|
+
logger.info("Activity {activityId} is enqueued.", {
|
|
118
|
+
activityId: activity.id?.href,
|
|
119
|
+
activity: json,
|
|
120
|
+
recipient
|
|
121
|
+
});
|
|
122
|
+
return "enqueued";
|
|
123
|
+
}
|
|
124
|
+
tracerProvider = tracerProvider ?? trace.getTracerProvider();
|
|
125
|
+
return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span) => {
|
|
126
|
+
const dispatched = inboxListeners?.dispatchWithClass(activity);
|
|
127
|
+
if (dispatched == null) {
|
|
128
|
+
logger.error("Unsupported activity type:\n{activity}", {
|
|
129
|
+
activity: json,
|
|
130
|
+
recipient
|
|
131
|
+
});
|
|
132
|
+
span.setStatus({
|
|
133
|
+
code: SpanStatusCode.UNSET,
|
|
134
|
+
message: `Unsupported activity type: ${getTypeId(activity).href}`
|
|
135
|
+
});
|
|
136
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
137
|
+
span.end();
|
|
138
|
+
return "unsupportedActivity";
|
|
139
|
+
}
|
|
140
|
+
const { class: cls, listener } = dispatched;
|
|
141
|
+
span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
|
|
142
|
+
try {
|
|
143
|
+
const activityType = getTypeId(activity).href;
|
|
144
|
+
const started = performance.now();
|
|
145
|
+
try {
|
|
146
|
+
const contextFactory = listenerInboxContextFactory ?? inboxContextFactory;
|
|
147
|
+
await listener(contextFactory(recipient, contextFactory === inboxContextFactory ? json : originalJson ?? json, activity.id?.href, activityType), activity);
|
|
148
|
+
} finally {
|
|
149
|
+
getFederationMetrics(meterProvider).recordInboxProcessingDuration(activityType, getDurationMs(started));
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
try {
|
|
153
|
+
await inboxErrorHandler?.(ctx, error);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
|
|
156
|
+
error,
|
|
157
|
+
activityId: activity.id?.href,
|
|
158
|
+
activity: json,
|
|
159
|
+
recipient
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
|
|
163
|
+
error,
|
|
164
|
+
activityId: activity.id?.href,
|
|
165
|
+
activity: json,
|
|
166
|
+
recipient
|
|
167
|
+
});
|
|
168
|
+
span.setStatus({
|
|
169
|
+
code: SpanStatusCode.ERROR,
|
|
170
|
+
message: String(error)
|
|
171
|
+
});
|
|
172
|
+
recordInboxActivity(meterProvider, "rejected", getTypeId(activity).href);
|
|
173
|
+
span.end();
|
|
174
|
+
return "error";
|
|
175
|
+
}
|
|
176
|
+
recordInboxActivity(meterProvider, "processed", getTypeId(activity).href);
|
|
177
|
+
if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
|
|
178
|
+
logger.info("Activity {activityId} has been processed.", {
|
|
179
|
+
activityId: activity.id?.href,
|
|
180
|
+
activity: json,
|
|
181
|
+
recipient
|
|
182
|
+
});
|
|
183
|
+
span.end();
|
|
184
|
+
return "success";
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
188
|
+
export { routeActivity as n, actorsHaveSameOrigin as t };
|
|
@@ -5,7 +5,7 @@ import { t as MAX_PORTABLE_KEY_TTL } from "./key-owner-evidence-APB6NpSk.mjs";
|
|
|
5
5
|
import { CryptographicKey, Multikey } from "@fedify/vocab";
|
|
6
6
|
import { FetchError, UrlError } from "@fedify/vocab-runtime";
|
|
7
7
|
//#region src/federation/keycache.ts
|
|
8
|
-
const KEY_CACHE_GENERATION = "
|
|
8
|
+
const KEY_CACHE_GENERATION = "3";
|
|
9
9
|
const COMPATIBLE_KEY_SEGMENT = "__compatible";
|
|
10
10
|
const MAX_OWNER_BYTES = 32 * 1024;
|
|
11
11
|
function isCompatibleKeyEntry(value) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { p as recordDocumentCache } from "./metrics-
|
|
4
|
+
import { p as recordDocumentCache } from "./metrics-BguhdXt-.mjs";
|
|
5
5
|
import { getLogger } from "@logtape/logtape";
|
|
6
6
|
import { preloadedContexts } from "@fedify/vocab-runtime";
|
|
7
7
|
//#region src/utils/kv-cache.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Temporal } from "temporal-polyfill";
|
|
2
2
|
import { URLPattern } from "urlpattern-polyfill";
|
|
3
|
-
import { G as validateCryptoKey, ot as recordDocumentCache, t as doubleKnock } from "./http-
|
|
3
|
+
import { G as validateCryptoKey, ot as recordDocumentCache, t as doubleKnock } from "./http-D_AGUmSq.js";
|
|
4
4
|
import { getLogger } from "@logtape/logtape";
|
|
5
5
|
import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, resolveDocumentLoaderTimeout, validatePublicUrl, withDocumentLoaderTimeout } from "@fedify/vocab-runtime";
|
|
6
6
|
import { curry } from "es-toolkit";
|
|
@@ -62,6 +62,10 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
|
|
|
62
62
|
}, options);
|
|
63
63
|
}
|
|
64
64
|
async function validateUrl(url, options) {
|
|
65
|
+
const parsedUrl = new URL(url);
|
|
66
|
+
const protocol = parsedUrl.protocol;
|
|
67
|
+
if (protocol !== "http:" && protocol !== "https:") throw new UrlError(`Unsupported protocol: ${protocol}`);
|
|
68
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new UrlError("URLs with credentials are not allowed");
|
|
65
69
|
if (!allowPrivateAddress) {
|
|
66
70
|
try {
|
|
67
71
|
await validatePublicUrl(url);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { URLPattern } = require("urlpattern-polyfill");
|
|
2
|
-
const require_http = require("./http-
|
|
2
|
+
const require_http = require("./http-g5f1D2tm.cjs");
|
|
3
3
|
let _logtape_logtape = require("@logtape/logtape");
|
|
4
4
|
let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
|
|
5
5
|
let es_toolkit = require("es-toolkit");
|
|
@@ -61,6 +61,10 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedi
|
|
|
61
61
|
}, options);
|
|
62
62
|
}
|
|
63
63
|
async function validateUrl(url, options) {
|
|
64
|
+
const parsedUrl = new URL(url);
|
|
65
|
+
const protocol = parsedUrl.protocol;
|
|
66
|
+
if (protocol !== "http:" && protocol !== "https:") throw new _fedify_vocab_runtime.UrlError(`Unsupported protocol: ${protocol}`);
|
|
67
|
+
if (parsedUrl.username !== "" || parsedUrl.password !== "") throw new _fedify_vocab_runtime.UrlError("URLs with credentials are not allowed");
|
|
64
68
|
if (!allowPrivateAddress) {
|
|
65
69
|
try {
|
|
66
70
|
await (0, _fedify_vocab_runtime.validatePublicUrl)(url);
|
|
@@ -3,8 +3,8 @@ import "urlpattern-polyfill";
|
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { c as isPortableActorDocument, d as isPortableUri, l as isPortableId, m as parseKeyIdString, s as isCompatibleKeyId } from "./portable-key-id-Ca30-hOa.mjs";
|
|
5
5
|
import { a as verificationObservation, i as triedKey, n as observeCheck, t as observeAttempt } from "./verification-Dpfcw8ml.mjs";
|
|
6
|
-
import { n as version, t as name } from "./deno-
|
|
7
|
-
import { _ as recordKeyLookup, n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch, t as classifyFetchError } from "./metrics-
|
|
6
|
+
import { n as version, t as name } from "./deno-CO1X_K_q.mjs";
|
|
7
|
+
import { _ as recordKeyLookup, n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch, t as classifyFetchError } from "./metrics-BguhdXt-.mjs";
|
|
8
8
|
import { n as attachKeyOwnerEvidence, t as MAX_PORTABLE_KEY_TTL } from "./key-owner-evidence-APB6NpSk.mjs";
|
|
9
9
|
import { getLogger } from "@logtape/logtape";
|
|
10
10
|
import { Activity, CryptographicKey, Multikey, Object as Object$1, getTypeId, isActor } from "@fedify/vocab";
|
|
@@ -154,16 +154,23 @@ async function importJwk(jwk, type) {
|
|
|
154
154
|
/**
|
|
155
155
|
* Fetches the document that the given actor URI dereferences to, and returns
|
|
156
156
|
* the actor only if the host that served the document is authoritative for
|
|
157
|
-
* the id the document claims.
|
|
157
|
+
* the id the document claims, and that id is the actor that was asked for.
|
|
158
158
|
*
|
|
159
159
|
* Only the origin that serves an actor id can speak for it. Without that
|
|
160
160
|
* rule any host could serve a document describing somebody else's actor—and
|
|
161
161
|
* listing its own keys as that actor's.
|
|
162
162
|
*
|
|
163
|
+
* The returned actor always has exactly the requested `actorId` as its `id`.
|
|
164
|
+
* A document loader follows redirects and alternate document links, so the
|
|
165
|
+
* document an actor id leads to may describe a different actor altogether,
|
|
166
|
+
* on the same origin or on another; accepting it would attribute that other
|
|
167
|
+
* actor's keys to the requested one. See GHSA-8863-8468-7hq7.
|
|
168
|
+
*
|
|
163
169
|
* @param actorId The URI of the actor to fetch.
|
|
164
170
|
* @param options Options for fetching the document.
|
|
165
171
|
* @returns The actor, or `null` if the document cannot be fetched, is not an
|
|
166
|
-
* actor,
|
|
172
|
+
* actor, belongs to another origin, or describes an actor other
|
|
173
|
+
* than the requested one.
|
|
167
174
|
* @internal
|
|
168
175
|
*/
|
|
169
176
|
async function fetchActorDocument(actorId, options = {}) {
|
|
@@ -229,6 +236,13 @@ async function fetchActorDocumentInternal(actorId, options, propagateContextFetc
|
|
|
229
236
|
});
|
|
230
237
|
return null;
|
|
231
238
|
}
|
|
239
|
+
if (object.id.href !== actorId.href) {
|
|
240
|
+
logger.debug("The document fetched for the actor {actorId} is the actor {documentActorId} instead; refusing to treat it as the requested actor's document.", {
|
|
241
|
+
actorId: actorId.href,
|
|
242
|
+
documentActorId: object.id.href
|
|
243
|
+
});
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
232
246
|
return object;
|
|
233
247
|
}
|
|
234
248
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
4
|
+
import { n as version, t as name } from "./deno-CO1X_K_q.mjs";
|
|
5
5
|
import { metrics } from "@opentelemetry/api";
|
|
6
6
|
import { FetchError } from "@fedify/vocab-runtime";
|
|
7
7
|
//#region src/federation/metrics.ts
|