@fedify/fedify 2.4.0-dev.2149 → 2.4.0-dev.2157
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-W2fIPRjv.mjs → builder-CLaS4-yA.mjs} +56 -3
- package/dist/{client-DpnpgjyX.mjs → client-CEnNz8O7.mjs} +7 -3
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-B1QH25S6.mjs → compound-proof-D7K94KPz.mjs} +216 -4
- package/dist/{context-DfSVPcJ8.mjs → context-B2rlkkyJ.mjs} +9 -2
- package/dist/{context-CrDDVrbF.d.cts → context-BMggDR-l.d.cts} +394 -5
- package/dist/{context-B2C8nuN3.d.ts → context-exYu7gDU.d.ts} +394 -5
- package/dist/{deno-yXpYZm4K.mjs → deno-D1gcWWqB.mjs} +1 -1
- package/dist/{dist-BQvArojP.mjs → dist-DJQM4u7i.mjs} +2 -1
- package/dist/docloader-BMqr3hji.mjs +79 -0
- 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 +4 -4
- package/dist/federation/handler.test.mjs +6 -6
- package/dist/federation/hashlink-media.test.mjs +2 -2
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +187 -10
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/mq.test.mjs +98 -69
- package/dist/federation/outgoing-proof.test.mjs +5 -5
- package/dist/federation/portable-actor.test.mjs +3 -3
- package/dist/federation/portable-chain.test.mjs +3 -3
- package/dist/federation/portable-collection.test.d.mts +2 -0
- package/dist/federation/portable-collection.test.mjs +768 -0
- package/dist/federation/portable-gateway-keys.test.mjs +4 -4
- package/dist/federation/portable-inbox.test.mjs +4 -4
- package/dist/federation/portable.test.mjs +4 -4
- package/dist/federation/send.test.mjs +84 -3
- package/dist/federation/tasks/enqueue.test.mjs +3 -3
- package/dist/federation/tasks/tasks.test.mjs +4 -4
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +2 -2
- package/dist/{federation-D4j-ZXUN.cjs → federation-2e8XrN7q.cjs} +700 -70
- package/dist/{http-C_jobbLz.cjs → http-BObQoB0j.cjs} +18 -3
- package/dist/{http-BoH-e8sR.mjs → http-CGcLv8rh.mjs} +9 -5
- package/dist/{http-CBRurnlR.js → http-Dgtt45-K.js} +13 -4
- package/dist/{key-BtO7WCfP.mjs → key-CPM4r6tP.mjs} +2 -2
- package/dist/{kv-cache-E0es0w4R.js → kv-cache-BweyIZTb.js} +35 -9
- package/dist/{kv-cache-BxUqVENn.cjs → kv-cache-D2fnB8qu.cjs} +34 -8
- package/dist/{kv-cache-CMuTXrkC.mjs → kv-cache-bdPqTR1k.mjs} +1 -1
- package/dist/{ld-YYBfDbe_.mjs → ld-lU_ZqzsQ.mjs} +4 -4
- package/dist/{metrics-DMqdDud_.mjs → metrics-B41pRPfd.mjs} +1 -1
- package/dist/{middleware-D6uiL1Tw.js → middleware-BYWyYjmX.js} +702 -72
- package/dist/{middleware-C97UJpkA.mjs → middleware-TrCVULgs.mjs} +1 -1
- package/dist/{middleware-CBGR92fz.mjs → middleware-xdukzV3x.mjs} +432 -81
- package/dist/{mod-C06GoL5Z.d.ts → mod-CnGtADvO.d.ts} +1 -1
- package/dist/{mod-CZetqT5h.d.ts → mod-DaiBrWDW.d.ts} +2 -0
- package/dist/{mod-B7SfEqIP.d.cts → mod-Decmur1U.d.cts} +1 -1
- package/dist/{mod-D4g9ffEn.d.cts → mod-Mt4aO3DX.d.cts} +2 -0
- package/dist/mod.cjs +5 -5
- package/dist/mod.d.cts +5 -5
- package/dist/mod.d.ts +5 -5
- package/dist/mod.js +5 -5
- package/dist/nodeinfo/client.test.mjs +49 -1
- package/dist/nodeinfo/handler.test.mjs +2 -2
- package/dist/nodeinfo/mod.cjs +1 -1
- package/dist/nodeinfo/mod.js +1 -1
- package/dist/{owner-Cn6CFYBy.mjs → owner-D63yPo_i.mjs} +3 -3
- package/dist/{portable-collection-CWgXwOdR.cjs → portable-collection-B8ojoQ50.cjs} +1 -1
- package/dist/{portable-collection-CyhFWEB-.js → portable-collection-C3xEzYri.js} +1 -1
- package/dist/{portable-collection-B3swTTja.mjs → portable-collection-_lZg-osX.mjs} +2 -2
- package/dist/{portable-key-HM5Siym0.mjs → portable-key-D8uWZF7b.mjs} +1 -1
- package/dist/{proof-DWkXfZIU.mjs → proof-C2YwD8a_.mjs} +10 -5
- package/dist/{send-BQDT_8bc.mjs → send-D9SGhxTB.mjs} +18 -4
- 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 +42 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +5 -2
- package/dist/sig/mod.cjs +2 -2
- package/dist/sig/mod.js +2 -2
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.mjs +2 -2
- package/dist/sig/portable-key.test.mjs +6 -6
- package/dist/sig/proof.test.mjs +3 -3
- package/dist/{tasks-dbCShlCb.mjs → tasks-IG3pHfvZ.mjs} +1 -1
- package/dist/{temporal-CFI__wjQ.mjs → temporal-q6FSekcN.mjs} +1 -1
- package/dist/testing/context.test.mjs +1 -1
- package/dist/testing/kv.test.mjs +3 -3
- package/dist/testing/mod.d.mts +393 -4
- package/dist/testing/mod.mjs +4 -4
- package/dist/{testing-otQhu4aD.mjs → testing-C1itIjqW.mjs} +3 -3
- package/dist/{types-C_cNlNVK.js → types-_S2_MeLF.js} +7 -3
- package/dist/{types-Be2zbyeG.cjs → types-poIao40f.cjs} +7 -3
- package/dist/utils/docloader.test.mjs +297 -2
- 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/dist/vocab/cjs.test.mjs +1 -1
- package/package.json +7 -7
- package/dist/docloader-C4h6PL7m.mjs +0 -53
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { n as version, t as name } from "./deno-
|
|
5
|
-
import { c as isPortableId } from "./portable-key-id-Bg259c4Z.mjs";
|
|
4
|
+
import { n as version, t as name } from "./deno-D1gcWWqB.mjs";
|
|
5
|
+
import { c as isPortableId, r as getPortableDid, t as getCanonicalPortableId } from "./portable-key-id-Bg259c4Z.mjs";
|
|
6
6
|
import { t as ActivityListenerSet } from "./activity-listener-C-BSbKI7.mjs";
|
|
7
7
|
import { getLogger } from "@logtape/logtape";
|
|
8
8
|
import { Router, RouterError, assertPath } from "@fedify/uri-template";
|
|
@@ -89,7 +89,7 @@ var FederationBuilderImpl = class {
|
|
|
89
89
|
* specified.
|
|
90
90
|
*/
|
|
91
91
|
async build(options) {
|
|
92
|
-
const { FederationImpl } = await import("./middleware-
|
|
92
|
+
const { FederationImpl } = await import("./middleware-TrCVULgs.mjs");
|
|
93
93
|
const f = new FederationImpl(options);
|
|
94
94
|
const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
|
|
95
95
|
f.router = this.router.clone();
|
|
@@ -166,6 +166,7 @@ var FederationBuilderImpl = class {
|
|
|
166
166
|
else if (!portable && actor.id.href != context.getActorUri(identifier).href) logger.warn("Actor dispatcher returned an actor with an id property that does not match the actor URI. Set the property with Context.getActorUri(identifier).");
|
|
167
167
|
if (actor instanceof Tombstone) return actor;
|
|
168
168
|
if (actor.id != null && isCompatibleEf61Iri(actor.id)) warnCompatibleActorId(actor.id, actor.gateway);
|
|
169
|
+
if (portable) this.#warnPortableCollectionIds(context, identifier, actor);
|
|
169
170
|
if (this.followingCallbacks != null && this.followingCallbacks.dispatcher != null) {
|
|
170
171
|
if (actor.followingId == null) logger.warn("You configured a following collection dispatcher, but the actor does not have a following property. Set the property with Context.getFollowingUri(identifier).");
|
|
171
172
|
else if (!portable && actor.followingId.href != context.getFollowingUri(identifier).href) logger.warn("You configured a following collection dispatcher, but the actor's following property does not match the following collection URI. Set the property with Context.getFollowingUri(identifier).");
|
|
@@ -651,6 +652,10 @@ var FederationBuilderImpl = class {
|
|
|
651
652
|
authorize(predicate) {
|
|
652
653
|
callbacks.authorizePredicate = predicate;
|
|
653
654
|
return setters;
|
|
655
|
+
},
|
|
656
|
+
mapPortableOwner(mapper) {
|
|
657
|
+
callbacks.portableOwnerMapper = mapper;
|
|
658
|
+
return setters;
|
|
654
659
|
}
|
|
655
660
|
};
|
|
656
661
|
return setters;
|
|
@@ -672,6 +677,54 @@ var FederationBuilderImpl = class {
|
|
|
672
677
|
this.outboxPermanentFailureHandler = handler;
|
|
673
678
|
}
|
|
674
679
|
/**
|
|
680
|
+
* Warns if a portable actor's collection properties that are portable IDs
|
|
681
|
+
* or compatible identifiers do not refer to the collections that
|
|
682
|
+
* the registered collection dispatchers serve through the FEP-ef61 gateway
|
|
683
|
+
* endpoint for the actor's DID. Properties with ordinary HTTP(S) URLs are
|
|
684
|
+
* not compared, since a portable actor may keep ordinary collections.
|
|
685
|
+
*/
|
|
686
|
+
#warnPortableCollectionIds(context, identifier, actor) {
|
|
687
|
+
const did = actor.id == null ? null : getPortableDid(actor.id);
|
|
688
|
+
if (did == null) return;
|
|
689
|
+
const logger = getLogger([
|
|
690
|
+
"fedify",
|
|
691
|
+
"federation",
|
|
692
|
+
"actor"
|
|
693
|
+
]);
|
|
694
|
+
const check = (registered, property, value, helper, build) => {
|
|
695
|
+
if (!registered || value == null || !isPortableId(value)) return;
|
|
696
|
+
const actual = getCanonicalPortableId(value);
|
|
697
|
+
if (actual == null) {
|
|
698
|
+
logger.warn("The actor's {property} property, {value}, is a malformed FEP-ef61 portable ID or compatible identifier.", {
|
|
699
|
+
property,
|
|
700
|
+
value: value.href
|
|
701
|
+
});
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
let expected;
|
|
705
|
+
try {
|
|
706
|
+
expected = build();
|
|
707
|
+
} catch (error) {
|
|
708
|
+
if (error instanceof TypeError || error instanceof RouterError) return;
|
|
709
|
+
throw error;
|
|
710
|
+
}
|
|
711
|
+
if (actual === getCanonicalPortableId(expected)) return;
|
|
712
|
+
logger.warn("The portable actor's {property} property, {value}, does not match the portable ID of the collection that the gateway endpoint serves, {expected}. Set the property with Context.{helper}(identifier, did).", {
|
|
713
|
+
property,
|
|
714
|
+
value: value.href,
|
|
715
|
+
expected: expected.href,
|
|
716
|
+
helper
|
|
717
|
+
});
|
|
718
|
+
};
|
|
719
|
+
check(this.outboxCallbacks?.dispatcher != null, "outbox", actor.outboxId, "getPortableOutboxUri", () => context.getPortableOutboxUri(identifier, did));
|
|
720
|
+
check(this.inboxCallbacks?.dispatcher != null || this.router.has("inbox"), "inbox", actor.inboxId, "getPortableInboxUri", () => context.getPortableInboxUri(identifier, did));
|
|
721
|
+
check(this.followingCallbacks?.dispatcher != null, "following", actor.followingId, "getPortableFollowingUri", () => context.getPortableFollowingUri(identifier, did));
|
|
722
|
+
check(this.followersCallbacks?.dispatcher != null, "followers", actor.followersId, "getPortableFollowersUri", () => context.getPortableFollowersUri(identifier, did));
|
|
723
|
+
check(this.likedCallbacks?.dispatcher != null, "liked", actor.likedId, "getPortableLikedUri", () => context.getPortableLikedUri(identifier, did));
|
|
724
|
+
check(this.featuredCallbacks?.dispatcher != null, "featured", actor.featuredId, "getPortableFeaturedUri", () => context.getPortableFeaturedUri(identifier, did));
|
|
725
|
+
check(this.featuredTagsCallbacks?.dispatcher != null, "featuredTags", actor.featuredTagsId, "getPortableFeaturedTagsUri", () => context.getPortableFeaturedTagsUri(identifier, did));
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
675
728
|
* Converts a name (string or symbol) to a unique string identifier.
|
|
676
729
|
* For symbols, generates and caches a UUID if not already present.
|
|
677
730
|
* For strings, returns the string as-is.
|
|
@@ -93,8 +93,8 @@ const DEFAULT_MAX_REDIRECTION = 5;
|
|
|
93
93
|
* bounce the request to an internal address.
|
|
94
94
|
* @param url The URL to fetch.
|
|
95
95
|
* @param headers The request headers.
|
|
96
|
-
* @returns The final response, or `null` if the URL is disallowed
|
|
97
|
-
* are too many redirects.
|
|
96
|
+
* @returns The final response, or `null` if the URL is disallowed, its
|
|
97
|
+
* hostname cannot be resolved, or there are too many redirects.
|
|
98
98
|
*/
|
|
99
99
|
async function fetchPublicUrl(url, headers) {
|
|
100
100
|
let current = url instanceof URL ? url : new URL(url);
|
|
@@ -104,7 +104,11 @@ async function fetchPublicUrl(url, headers) {
|
|
|
104
104
|
await validatePublicUrl(current.href);
|
|
105
105
|
} catch (e) {
|
|
106
106
|
if (e instanceof UrlError) {
|
|
107
|
-
logger.error("
|
|
107
|
+
if (e.reason === "dns") logger.error("DNS lookup failed for {url}: {error}", {
|
|
108
|
+
url: current.href,
|
|
109
|
+
error: e
|
|
110
|
+
});
|
|
111
|
+
else logger.error("Refused to fetch a private or invalid URL {url}: {error}", {
|
|
108
112
|
url: current.href,
|
|
109
113
|
error: e
|
|
110
114
|
});
|
package/dist/compat/mod.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import {
|
|
2
|
+
import { Qt as ActivityTransformer, n as Context } from "../context-BMggDR-l.cjs";
|
|
3
3
|
import { Activity } from "@fedify/vocab";
|
|
4
4
|
|
|
5
5
|
//#region src/compat/transformers.d.ts
|
package/dist/compat/mod.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
|
-
import {
|
|
2
|
+
import { Qt as ActivityTransformer, n as Context } from "../context-exYu7gDU.js";
|
|
3
3
|
import { Activity } from "@fedify/vocab";
|
|
4
4
|
|
|
5
5
|
//#region src/compat/transformers.d.ts
|
|
@@ -5,7 +5,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
|
|
|
5
5
|
import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
|
|
6
6
|
import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
|
|
7
7
|
import { t as assert } from "../assert-D1Eafmd_.mjs";
|
|
8
|
-
import { E as autoIdAssigner, T as actorDehydrator, n as FederationImpl } from "../middleware-
|
|
8
|
+
import { E as autoIdAssigner, T as actorDehydrator, n as FederationImpl } from "../middleware-xdukzV3x.mjs";
|
|
9
9
|
import { Follow, Person } from "@fedify/vocab";
|
|
10
10
|
import { test } from "@fedify/fixture";
|
|
11
11
|
//#region src/compat/transformers.test.ts
|
|
@@ -3,9 +3,9 @@ import "urlpattern-polyfill";
|
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
4
|
import { c as isPortableId, i as getRawCanonicalPortableId, t as getCanonicalPortableId } from "./portable-key-id-Bg259c4Z.mjs";
|
|
5
5
|
import { n as preloadedOnlyDocumentLoader } from "./public-audience--klBIOk1.mjs";
|
|
6
|
-
import { c as getNormalizationContextLoader } from "./ld-
|
|
7
|
-
import {
|
|
8
|
-
import { parseIri } from "@fedify/vocab-runtime";
|
|
6
|
+
import { c as getNormalizationContextLoader } from "./ld-lU_ZqzsQ.mjs";
|
|
7
|
+
import { l as verifyPortableObjectProofPolicy, n as classifyFep2277CoreType, o as verifyMapLocalProof } from "./proof-C2YwD8a_.mjs";
|
|
8
|
+
import { parseIri, preloadedContexts } from "@fedify/vocab-runtime";
|
|
9
9
|
import jsonld from "@fedify/vocab-runtime/jsonld";
|
|
10
10
|
//#region src/sig/compound-proof.ts
|
|
11
11
|
const textEncoder = new TextEncoder();
|
|
@@ -736,5 +736,217 @@ function isSingleStringValue(values, datatype) {
|
|
|
736
736
|
const keys = Object.keys(value);
|
|
737
737
|
return keys.length === 1 || datatype != null && keys.length === 2 && value["@type"] === datatype;
|
|
738
738
|
}
|
|
739
|
+
/**
|
|
740
|
+
* FEP-2277 core types that FEP-ef61 does not require to carry proofs.
|
|
741
|
+
*/
|
|
742
|
+
const UNSECURED_CORE_TYPES = /* @__PURE__ */ new Set([
|
|
743
|
+
"collection",
|
|
744
|
+
"link",
|
|
745
|
+
"verificationMethod",
|
|
746
|
+
"publicKey"
|
|
747
|
+
]);
|
|
748
|
+
/**
|
|
749
|
+
* Applies the FEP-ef61 proof policy, under the map-local compound-proof
|
|
750
|
+
* profile, to every portable object embedded in a document that a gateway is
|
|
751
|
+
* about to serve, e.g., the items of a portable collection page. The root
|
|
752
|
+
* map itself is not checked, as the caller built it.
|
|
753
|
+
*
|
|
754
|
+
* Each map below the root whose direct `id` or `@id` is a portable ID or
|
|
755
|
+
* a compatible identifier is checked independently of the others:
|
|
756
|
+
*
|
|
757
|
+
* - A map with a direct `proof` is a separately secured document: the proof
|
|
758
|
+
* has to be a single map, the map has to carry its own `@context`, and
|
|
759
|
+
* the proof has to verify over the map exactly as it is served, with only
|
|
760
|
+
* its own proof removed, and be made with a key of the DID in the map's
|
|
761
|
+
* ID, as the map-local compound-proof profile requires.
|
|
762
|
+
* - A map without a proof has to be a collection, a link, or a key, which
|
|
763
|
+
* FEP-ef61 does not require to be secured. It is classified under its
|
|
764
|
+
* effective context, i.e., the `@context` values of its ancestors and
|
|
765
|
+
* itself, which therefore may only use the simple, preloaded contexts
|
|
766
|
+
* that the compound-proof policy accepts.
|
|
767
|
+
*
|
|
768
|
+
* Maps that merely refer to a portable object, i.e., have no member other
|
|
769
|
+
* than `id` or `@id`, and maps with non-portable IDs, are neither checked nor
|
|
770
|
+
* verified. Every `@context` in the document, however, has to be simple, and
|
|
771
|
+
* every portable object that the expanded document describes has to be
|
|
772
|
+
* a map found this way, so that no context can hide a portable object from
|
|
773
|
+
* this check, e.g., by aliasing `@id` or redefining `proof`. The input must be a finite, already-materialized JSON tree, such
|
|
774
|
+
* as the compact JSON-LD document that is about to be served.
|
|
775
|
+
*
|
|
776
|
+
* @param json The document to check.
|
|
777
|
+
* @param options The options for verifying the proofs. Proofs whose
|
|
778
|
+
* verification methods are not `did:key` DID URLs may make
|
|
779
|
+
* the document loader fetch DID documents.
|
|
780
|
+
* @returns The result, which names the first refused map, if any.
|
|
781
|
+
* @internal
|
|
782
|
+
*/
|
|
783
|
+
async function verifyServedPortableObjects(json, options = {}) {
|
|
784
|
+
json = JSON.parse(JSON.stringify(json));
|
|
785
|
+
const collected = collectServedPortableMaps(json);
|
|
786
|
+
if (!("maps" in collected)) return collected;
|
|
787
|
+
const maps = collected.maps;
|
|
788
|
+
const discovered = /* @__PURE__ */ new Map();
|
|
789
|
+
const ids = maps.map((candidate) => candidate.id);
|
|
790
|
+
const rootId = isJsonMap(json) ? [json.id, json["@id"]].find(isPortableIdValue) : void 0;
|
|
791
|
+
if (rootId != null) ids.push(rootId);
|
|
792
|
+
for (const id of ids) discovered.set(id, (discovered.get(id) ?? 0) + 1);
|
|
793
|
+
let hidden;
|
|
794
|
+
try {
|
|
795
|
+
hidden = findHiddenPortableObject(await jsonld.expand(json, { documentLoader: preloadedOnlyDocumentLoader }), discovered);
|
|
796
|
+
} catch {
|
|
797
|
+
return {
|
|
798
|
+
verified: false,
|
|
799
|
+
path: "",
|
|
800
|
+
id: "",
|
|
801
|
+
reason: "unsupportedContext"
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
if (hidden != null) return {
|
|
805
|
+
verified: false,
|
|
806
|
+
path: "",
|
|
807
|
+
id: hidden,
|
|
808
|
+
reason: "hiddenPortableObject"
|
|
809
|
+
};
|
|
810
|
+
for (const candidate of maps) {
|
|
811
|
+
const reason = await checkServedPortableMap(candidate, options);
|
|
812
|
+
if (reason != null) return {
|
|
813
|
+
verified: false,
|
|
814
|
+
path: candidate.path,
|
|
815
|
+
id: candidate.id,
|
|
816
|
+
reason
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
return { verified: true };
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Finds a portable object that an expanded JSON-LD document describes, i.e.,
|
|
823
|
+
* a node with a portable `@id` and other members, more times than the given
|
|
824
|
+
* numbers of maps found for its ID.
|
|
825
|
+
*/
|
|
826
|
+
function findHiddenPortableObject(expanded, discovered) {
|
|
827
|
+
const remaining = new Map(discovered);
|
|
828
|
+
const pending = [expanded];
|
|
829
|
+
while (pending.length > 0) {
|
|
830
|
+
const value = pending.pop();
|
|
831
|
+
if (Array.isArray(value)) {
|
|
832
|
+
pending.push(...value);
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
if (!isJsonMap(value)) continue;
|
|
836
|
+
const id = value["@id"];
|
|
837
|
+
if (isPortableIdValue(id) && Object.keys(value).some((key) => key !== "@id")) {
|
|
838
|
+
const count = remaining.get(id) ?? 0;
|
|
839
|
+
if (count < 1) return id;
|
|
840
|
+
remaining.set(id, count - 1);
|
|
841
|
+
}
|
|
842
|
+
pending.push(...Object.values(value));
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Checks whether a context uses only preloaded remote contexts and simple
|
|
847
|
+
* term definitions, so that the context in effect for a map can be told from
|
|
848
|
+
* the contexts of its ancestors.
|
|
849
|
+
*/
|
|
850
|
+
function isCheckableContext(context) {
|
|
851
|
+
return (Array.isArray(context) ? context : [context]).every((value) => typeof value === "string" ? Object.hasOwn(preloadedContexts, value) : !Array.isArray(value) && isSimpleContext(value));
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Checks whether every remote context that a context refers to is preloaded,
|
|
855
|
+
* so that all of its definitions are known.
|
|
856
|
+
*/
|
|
857
|
+
function hasOnlyPreloadedRemoteContexts(context) {
|
|
858
|
+
return (Array.isArray(context) ? context : [context]).every((value) => typeof value === "string" ? Object.hasOwn(preloadedContexts, value) : !Array.isArray(value));
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Checks whether a map or any map below it, outside proof and context values,
|
|
862
|
+
* has a portable `id` or `@id` and other members.
|
|
863
|
+
*/
|
|
864
|
+
function hasLiteralPortableObject(json) {
|
|
865
|
+
return findInJsonMaps(json, (map) => [map.id, map["@id"]].some(isPortableIdValue) && Object.keys(map).some((key) => key !== "id" && key !== "@id") ? true : void 0) ?? false;
|
|
866
|
+
}
|
|
867
|
+
function collectServedPortableMaps(json) {
|
|
868
|
+
const maps = [];
|
|
869
|
+
const seen = /* @__PURE__ */ new Set();
|
|
870
|
+
const pending = [{
|
|
871
|
+
value: json,
|
|
872
|
+
path: "",
|
|
873
|
+
depth: 0,
|
|
874
|
+
contexts: []
|
|
875
|
+
}];
|
|
876
|
+
while (pending.length > 0) {
|
|
877
|
+
const { value, path, depth, contexts } = pending.pop();
|
|
878
|
+
if (typeof value !== "object" || value == null || seen.has(value)) continue;
|
|
879
|
+
seen.add(value);
|
|
880
|
+
if (Array.isArray(value)) {
|
|
881
|
+
for (let index = value.length - 1; index >= 0; index--) pending.push({
|
|
882
|
+
value: value[index],
|
|
883
|
+
path: childPath(path, String(index)),
|
|
884
|
+
depth: depth + 1,
|
|
885
|
+
contexts
|
|
886
|
+
});
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
const map = value;
|
|
890
|
+
const hasContext = Object.hasOwn(map, "@context");
|
|
891
|
+
if (hasContext && !isCheckableContext(map["@context"])) {
|
|
892
|
+
if (hasOnlyPreloadedRemoteContexts(map["@context"]) && !hasLiteralPortableObject(map)) continue;
|
|
893
|
+
const id = [map.id, map["@id"]].find((v) => typeof v === "string");
|
|
894
|
+
return {
|
|
895
|
+
verified: false,
|
|
896
|
+
path,
|
|
897
|
+
id: typeof id === "string" ? id : "",
|
|
898
|
+
reason: "unsupportedContext"
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
const mapContexts = hasContext ? [...contexts, map["@context"]] : contexts;
|
|
902
|
+
const id = [map.id, map["@id"]].find(isPortableIdValue);
|
|
903
|
+
if (depth > 0 && id != null && Object.keys(map).some((key) => key !== "id" && key !== "@id")) maps.push({
|
|
904
|
+
path,
|
|
905
|
+
id,
|
|
906
|
+
map,
|
|
907
|
+
contexts: mapContexts
|
|
908
|
+
});
|
|
909
|
+
const keys = Object.keys(map);
|
|
910
|
+
for (let index = keys.length - 1; index >= 0; index--) {
|
|
911
|
+
const key = keys[index];
|
|
912
|
+
if (key === "proof" || key === "@context") continue;
|
|
913
|
+
pending.push({
|
|
914
|
+
value: map[key],
|
|
915
|
+
path: childPath(path, key),
|
|
916
|
+
depth: depth + 1,
|
|
917
|
+
contexts: mapContexts
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
maps.sort((left, right) => comparePaths(left.path, right.path));
|
|
922
|
+
return { maps };
|
|
923
|
+
}
|
|
924
|
+
async function checkServedPortableMap({ map, contexts }, options) {
|
|
925
|
+
if (Object.hasOwn(map, "proof")) {
|
|
926
|
+
if (!isJsonMap(map.proof)) return "unsupportedProofShape";
|
|
927
|
+
if (!Object.hasOwn(map, "@context")) return "missingContext";
|
|
928
|
+
try {
|
|
929
|
+
const policy = await verifyPortableObjectProofPolicy(map, await verifyMapLocalProof(map, options), options);
|
|
930
|
+
return policy.verified ? null : policy.reason.type;
|
|
931
|
+
} catch (error) {
|
|
932
|
+
if (error instanceof TypeError) return "invalidPortableObject";
|
|
933
|
+
throw error;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
const flattened = contexts.flatMap((context) => Array.isArray(context) ? context : [context]);
|
|
937
|
+
if (flattened.length < 1 || !isSimpleContext(flattened)) return "unsupportedContext";
|
|
938
|
+
let expanded;
|
|
939
|
+
try {
|
|
940
|
+
expanded = await jsonld.expand({
|
|
941
|
+
...map,
|
|
942
|
+
"@context": flattened
|
|
943
|
+
}, { documentLoader: preloadedOnlyDocumentLoader });
|
|
944
|
+
} catch {
|
|
945
|
+
return "unsupportedContext";
|
|
946
|
+
}
|
|
947
|
+
if (expanded.length !== 1 || !isJsonMap(expanded[0])) return "invalidPortableObject";
|
|
948
|
+
const type = classifyFep2277CoreType(expanded[0]);
|
|
949
|
+
return UNSECURED_CORE_TYPES.has(type) ? null : "missingProof";
|
|
950
|
+
}
|
|
739
951
|
//#endregion
|
|
740
|
-
export { verifyCompoundPortableObjectProofs as a, inspectCompoundPortableObjectApplicability as i, discoverCompoundProofDocuments as n, verifyCompoundProofDocuments as o, findUnsupportedCompoundProofShape as r, containsCompoundPortableObject as t };
|
|
952
|
+
export { verifyCompoundPortableObjectProofs as a, inspectCompoundPortableObjectApplicability as i, discoverCompoundProofDocuments as n, verifyCompoundProofDocuments as o, findUnsupportedCompoundProofShape as r, verifyServedPortableObjects as s, containsCompoundPortableObject as t };
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import "temporal-polyfill";
|
|
2
2
|
import "urlpattern-polyfill";
|
|
3
3
|
globalThis.addEventListener = () => {};
|
|
4
|
-
import { t as mockDocumentLoader } from "./dist-
|
|
4
|
+
import { t as mockDocumentLoader } from "./dist-DJQM4u7i.mjs";
|
|
5
5
|
import { RouterError } from "@fedify/uri-template";
|
|
6
6
|
import { lookupObject, traverseCollection } from "@fedify/vocab";
|
|
7
7
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
8
8
|
import { metrics, trace } from "@opentelemetry/api";
|
|
9
9
|
//#region src/testing/context.ts
|
|
10
10
|
function createContext(values) {
|
|
11
|
-
const { federation, url = new URL("http://example.com/"), canonicalOrigin, data, documentLoader, contextLoader, verifyPortableObject, meterProvider, tracerProvider, clone, getNodeInfoUri, getActorUri, getPortableActorUri, getObjectUri, getPortableObjectUri, getPortableInboxUri, getCollectionUri, getOutboxUri, getMediaUploaderUri, getInboxUri, getFollowingUri, getFollowersUri, getLikedUri, getFeaturedUri, getFeaturedTagsUri, parseUri, getActorKeyPairs, getDocumentLoader, lookupObject: lookupObject$1, traverseCollection: traverseCollection$1, lookupNodeInfo, lookupWebFinger: lookupWebFinger$1, sendActivity, routeActivity, enqueueTask, enqueueTaskMany } = values;
|
|
11
|
+
const { federation, url = new URL("http://example.com/"), canonicalOrigin, data, documentLoader, contextLoader, verifyPortableObject, meterProvider, tracerProvider, clone, getNodeInfoUri, getActorUri, getPortableActorUri, getObjectUri, getPortableObjectUri, getPortableInboxUri, getPortableOutboxUri, getPortableFollowingUri, getPortableFollowersUri, getPortableLikedUri, getPortableFeaturedUri, getPortableFeaturedTagsUri, getPortableCollectionUri, getCollectionUri, getOutboxUri, getMediaUploaderUri, getInboxUri, getFollowingUri, getFollowersUri, getLikedUri, getFeaturedUri, getFeaturedTagsUri, parseUri, getActorKeyPairs, getDocumentLoader, lookupObject: lookupObject$1, traverseCollection: traverseCollection$1, lookupNodeInfo, lookupWebFinger: lookupWebFinger$1, sendActivity, routeActivity, enqueueTask, enqueueTaskMany } = values;
|
|
12
12
|
function throwRouterError() {
|
|
13
13
|
throw new RouterError("Not implemented");
|
|
14
14
|
}
|
|
@@ -34,6 +34,13 @@ function createContext(values) {
|
|
|
34
34
|
getObjectUri: getObjectUri ?? throwRouterError,
|
|
35
35
|
getPortableObjectUri: getPortableObjectUri ?? throwRouterError,
|
|
36
36
|
getPortableInboxUri: getPortableInboxUri ?? throwRouterError,
|
|
37
|
+
getPortableOutboxUri: getPortableOutboxUri ?? throwRouterError,
|
|
38
|
+
getPortableFollowingUri: getPortableFollowingUri ?? throwRouterError,
|
|
39
|
+
getPortableFollowersUri: getPortableFollowersUri ?? throwRouterError,
|
|
40
|
+
getPortableLikedUri: getPortableLikedUri ?? throwRouterError,
|
|
41
|
+
getPortableFeaturedUri: getPortableFeaturedUri ?? throwRouterError,
|
|
42
|
+
getPortableFeaturedTagsUri: getPortableFeaturedTagsUri ?? throwRouterError,
|
|
43
|
+
getPortableCollectionUri: getPortableCollectionUri ?? throwRouterError,
|
|
37
44
|
getCollectionUri: getCollectionUri ?? throwRouterError,
|
|
38
45
|
getOutboxUri: getOutboxUri ?? throwRouterError,
|
|
39
46
|
getMediaUploaderUri: getMediaUploaderUri ?? throwRouterError,
|