@fedify/vocab-runtime 2.4.0-dev.2056 → 2.4.0-dev.2063
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/deno.json +1 -1
- package/dist/internal/portable-dereference.cjs +176 -1
- package/dist/internal/portable-dereference.d.cts +94 -2
- package/dist/internal/portable-dereference.d.ts +94 -2
- package/dist/internal/portable-dereference.js +171 -3
- package/dist/mod.cjs +1 -1
- package/dist/mod.d.cts +2 -2
- package/dist/mod.d.ts +2 -2
- package/dist/mod.js +1 -1
- package/dist/portable--NcyA1R_.d.ts +156 -0
- package/dist/portable-BykOD45s.d.cts +156 -0
- package/dist/tests/{body-wkL-XGhe.mjs → body-BN5x-zDQ.mjs} +1 -1
- package/dist/tests/{body-h9zUR79f.cjs → body-CTGqQhNI.cjs} +1 -1
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/decimal.test.cjs +1 -1
- package/dist/tests/decimal.test.mjs +1 -1
- package/dist/tests/{docloader-DMGssPEX.mjs → docloader-BFxwbqMx.mjs} +2 -2
- package/dist/tests/{docloader-CBLgaVPd.cjs → docloader-JGSpG-3l.cjs} +2 -2
- package/dist/tests/docloader.test.cjs +2 -2
- package/dist/tests/docloader.test.mjs +2 -2
- package/dist/tests/{request-Ytm0xyZH.mjs → request-BT9FBhk6.mjs} +1 -1
- package/dist/tests/{request-ClFs0ePp.cjs → request-jWhPNG9V.cjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/package.json +1 -1
- package/src/internal/portable-dereference.ts +251 -2
- package/src/mod.ts +2 -0
- package/src/portable.ts +132 -8
- package/dist/portable-CduElToy.d.ts +0 -52
- package/dist/portable-DxF2Ut-Z.d.cts +0 -52
package/deno.json
CHANGED
|
@@ -25,6 +25,156 @@ const BASELINE_CONTEXT_URLS = /* @__PURE__ */ new Set([
|
|
|
25
25
|
"https://w3id.org/security/data-integrity/v1"
|
|
26
26
|
]);
|
|
27
27
|
/**
|
|
28
|
+
* The maximum number of links in a referrer chain passed to a
|
|
29
|
+
* {@link PortableObjectVerifier}. Longer chains are cut off and marked as
|
|
30
|
+
* truncated.
|
|
31
|
+
*/
|
|
32
|
+
const MAX_REFERRER_CHAIN_LENGTH = 32;
|
|
33
|
+
const provenances = /* @__PURE__ */ new WeakMap();
|
|
34
|
+
function getObjectId(object) {
|
|
35
|
+
if (object == null || typeof object !== "object" || !("id" in object)) return null;
|
|
36
|
+
const id = object.id;
|
|
37
|
+
return id instanceof URL ? id : null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Checks whether a vocabulary object is part of a chain of portable objects,
|
|
41
|
+
* i.e., whether it has a portable ID or was obtained from a portable object.
|
|
42
|
+
*
|
|
43
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
44
|
+
* part of the public API contract. This is not considered public API for
|
|
45
|
+
* Semantic Versioning decisions.
|
|
46
|
+
*/
|
|
47
|
+
function isInPortableChain(object) {
|
|
48
|
+
if (provenances.has(object)) return true;
|
|
49
|
+
const id = getObjectId(object);
|
|
50
|
+
return id != null && isPortableIri(id);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Checks whether a vocabulary object was accepted without an integrity
|
|
54
|
+
* proof, e.g., as an unsecured portable collection served by a trusted
|
|
55
|
+
* gateway. Objects embedded in such an object must not be trusted because
|
|
56
|
+
* of their origin.
|
|
57
|
+
*
|
|
58
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
59
|
+
* part of the public API contract. This is not considered public API for
|
|
60
|
+
* Semantic Versioning decisions.
|
|
61
|
+
*/
|
|
62
|
+
function isUnsecuredPortableObject(object) {
|
|
63
|
+
return provenances.get(object)?.acceptance === "unsecured";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Checks whether a URL is an FEP-ef61 compatible identifier, i.e., an HTTP(S)
|
|
67
|
+
* URL under a gateway's `/.well-known/apgateway/` path that stands for
|
|
68
|
+
* a portable object. Malformed compatible identifiers count as compatible
|
|
69
|
+
* identifiers too.
|
|
70
|
+
*
|
|
71
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
72
|
+
* part of the public API contract. This is not considered public API for
|
|
73
|
+
* Semantic Versioning decisions.
|
|
74
|
+
*/
|
|
75
|
+
function isCompatibleEf61Iri(url) {
|
|
76
|
+
try {
|
|
77
|
+
return require_url.fromCompatibleEf61Id(url) != null;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error instanceof TypeError) return true;
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Records that a vocabulary object returned by a property accessor was
|
|
85
|
+
* obtained through the given property of a portable object, so that later
|
|
86
|
+
* dereferences from it can tell where it came from. Nothing is recorded if
|
|
87
|
+
* the parent is not in a portable chain, or if the child already has its
|
|
88
|
+
* provenance.
|
|
89
|
+
*
|
|
90
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
91
|
+
* part of the public API contract. This is not considered public API for
|
|
92
|
+
* Semantic Versioning decisions.
|
|
93
|
+
*/
|
|
94
|
+
function recordPortableReferrer(parent, child, property) {
|
|
95
|
+
if (child == null || typeof child !== "object") return;
|
|
96
|
+
if (provenances.has(child) || !isInPortableChain(parent)) return;
|
|
97
|
+
provenances.set(child, { referrer: {
|
|
98
|
+
object: parent,
|
|
99
|
+
property
|
|
100
|
+
} });
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Copies the provenance of a vocabulary object to its clone. The clone
|
|
104
|
+
* keeps where the original came from and the restriction on unsecured
|
|
105
|
+
* objects, but not a positive verification status nor a collection context,
|
|
106
|
+
* since the clone may have different property values.
|
|
107
|
+
*
|
|
108
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
109
|
+
* part of the public API contract. This is not considered public API for
|
|
110
|
+
* Semantic Versioning decisions.
|
|
111
|
+
*/
|
|
112
|
+
function copyPortableProvenance(from, to) {
|
|
113
|
+
const provenance = provenances.get(from);
|
|
114
|
+
if (provenance == null) return;
|
|
115
|
+
provenances.set(to, {
|
|
116
|
+
referrer: provenance.referrer,
|
|
117
|
+
...provenance.acceptance === "unsecured" ? { acceptance: "unsecured" } : {}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Rejects a reference from a portable object that would be dereferenced as
|
|
122
|
+
* an ordinary HTTP(S) object although it stands for a portable object, i.e.,
|
|
123
|
+
* an FEP-ef61 compatible identifier, or a document whose final URL or `@id`
|
|
124
|
+
* is a compatible identifier or a portable IRI. Such a document would skip
|
|
125
|
+
* the portable object policy, so it is rejected regardless of the
|
|
126
|
+
* `crossOrigin` option, except that `crossOrigin: "throw"` makes it throw.
|
|
127
|
+
*
|
|
128
|
+
* @param url The rejected URL, for the log message.
|
|
129
|
+
* @param crossOrigin The `crossOrigin` option of the accessor.
|
|
130
|
+
* @returns Always `null`.
|
|
131
|
+
* @throws {Error} If `crossOrigin` is `"throw"`.
|
|
132
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
133
|
+
* part of the public API contract. This is not considered public API for
|
|
134
|
+
* Semantic Versioning decisions.
|
|
135
|
+
*/
|
|
136
|
+
function rejectPortableChainReference(url, crossOrigin) {
|
|
137
|
+
const message = "Refusing to dereference {url} from a portable object as an ordinary HTTP(S) object, because it stands for an FEP-ef61 portable object that is not verified this way.";
|
|
138
|
+
if (crossOrigin === "throw") throw new Error(message.replace("{url}", url));
|
|
139
|
+
logger.warn(message, { url });
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Logs that an embedded object without `@id` in an object accepted without
|
|
144
|
+
* an integrity proof is dropped, because it cannot be dereferenced and
|
|
145
|
+
* verified on its own.
|
|
146
|
+
*
|
|
147
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
148
|
+
* part of the public API contract. This is not considered public API for
|
|
149
|
+
* Semantic Versioning decisions.
|
|
150
|
+
*/
|
|
151
|
+
function warnUnverifiableEmbeddedObject(parent, property) {
|
|
152
|
+
logger.warn("Dropping an embedded object without @id in the {property} property of {parentId}, because the parent was accepted without an integrity proof and the embedded object cannot be verified on its own.", {
|
|
153
|
+
property,
|
|
154
|
+
parentId: getObjectId(parent)?.href ?? null
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function buildReferrerChain(link, depth = 0) {
|
|
158
|
+
if (link == null) return void 0;
|
|
159
|
+
const provenance = provenances.get(link.object);
|
|
160
|
+
const base = {
|
|
161
|
+
object: link.object,
|
|
162
|
+
id: getObjectId(link.object),
|
|
163
|
+
property: link.property,
|
|
164
|
+
...provenance?.acceptance == null ? {} : { acceptance: provenance.acceptance },
|
|
165
|
+
...provenance?.collectionContext === void 0 ? {} : { collectionContext: provenance.collectionContext }
|
|
166
|
+
};
|
|
167
|
+
if (provenance?.referrer == null) return base;
|
|
168
|
+
if (depth + 1 >= MAX_REFERRER_CHAIN_LENGTH) return {
|
|
169
|
+
...base,
|
|
170
|
+
truncated: true
|
|
171
|
+
};
|
|
172
|
+
return {
|
|
173
|
+
...base,
|
|
174
|
+
referrer: buildReferrerChain(provenance.referrer, depth + 1)
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
28
178
|
* Checks whether a URL is an FEP-ef61 portable ActivityPub IRI.
|
|
29
179
|
*
|
|
30
180
|
* @internal Technically exported for generated vocabulary classes, but not
|
|
@@ -205,6 +355,9 @@ async function dereferencePortableIri(url, options) {
|
|
|
205
355
|
const contextLoader = snapshot.loader;
|
|
206
356
|
const attempts = [];
|
|
207
357
|
const { signal } = options;
|
|
358
|
+
const explicitGateways = options.gateways == null ? void 0 : gateways;
|
|
359
|
+
const gatewayHints = options.gateways == null && gateways.length > 0 ? gateways : void 0;
|
|
360
|
+
const referrer = buildReferrerChain(options.referrer);
|
|
208
361
|
try {
|
|
209
362
|
for (const { url: requestUrl, gateway } of requestUrls) {
|
|
210
363
|
signal?.throwIfAborted();
|
|
@@ -227,12 +380,22 @@ async function dereferencePortableIri(url, options) {
|
|
|
227
380
|
const { document } = remoteDocument;
|
|
228
381
|
try {
|
|
229
382
|
await checkPortableObjectId(document, expectedId, contextLoader);
|
|
383
|
+
let documentUrl;
|
|
384
|
+
try {
|
|
385
|
+
documentUrl = require_url.parseIri(remoteDocument.documentUrl);
|
|
386
|
+
} catch {
|
|
387
|
+
documentUrl = void 0;
|
|
388
|
+
}
|
|
230
389
|
let result;
|
|
231
390
|
try {
|
|
232
391
|
result = await verify(document, {
|
|
233
392
|
documentLoader: options.documentLoader,
|
|
234
393
|
contextLoader,
|
|
235
|
-
tracerProvider: options.tracerProvider
|
|
394
|
+
tracerProvider: options.tracerProvider,
|
|
395
|
+
...documentUrl == null ? {} : { documentUrl },
|
|
396
|
+
...explicitGateways == null ? {} : { gateways: explicitGateways },
|
|
397
|
+
...gatewayHints == null ? {} : { gatewayHints },
|
|
398
|
+
...referrer == null ? {} : { referrer }
|
|
236
399
|
});
|
|
237
400
|
} catch (error) {
|
|
238
401
|
throw new PortableObjectRejection("the proof verifier failed: {error}", { error });
|
|
@@ -243,6 +406,11 @@ async function dereferencePortableIri(url, options) {
|
|
|
243
406
|
baseUrl: url
|
|
244
407
|
});
|
|
245
408
|
signal?.throwIfAborted();
|
|
409
|
+
if (object != null && typeof object === "object") provenances.set(object, {
|
|
410
|
+
acceptance: result.unsecured === true ? "unsecured" : "verified",
|
|
411
|
+
...result.collectionContext === void 0 ? {} : { collectionContext: result.collectionContext },
|
|
412
|
+
...options.referrer == null ? {} : { referrer: options.referrer }
|
|
413
|
+
});
|
|
246
414
|
if (gateway != null) span?.setAttribute("activitypub.gateway", gateway.href);
|
|
247
415
|
return object;
|
|
248
416
|
} catch (error) {
|
|
@@ -310,6 +478,13 @@ async function checkPortableObjectId(document, expectedId, contextLoader) {
|
|
|
310
478
|
}
|
|
311
479
|
//#endregion
|
|
312
480
|
exports.PortableObjectRejectedError = PortableObjectRejectedError;
|
|
481
|
+
exports.copyPortableProvenance = copyPortableProvenance;
|
|
313
482
|
exports.dereferencePortableIri = dereferencePortableIri;
|
|
314
483
|
exports.getPortableGatewayCandidates = getPortableGatewayCandidates;
|
|
484
|
+
exports.isCompatibleEf61Iri = isCompatibleEf61Iri;
|
|
485
|
+
exports.isInPortableChain = isInPortableChain;
|
|
315
486
|
exports.isPortableIri = isPortableIri;
|
|
487
|
+
exports.isUnsecuredPortableObject = isUnsecuredPortableObject;
|
|
488
|
+
exports.recordPortableReferrer = recordPortableReferrer;
|
|
489
|
+
exports.rejectPortableChainReference = rejectPortableChainReference;
|
|
490
|
+
exports.warnUnverifiableEmbeddedObject = warnUnverifiableEmbeddedObject;
|
|
@@ -1,10 +1,101 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
2
|
import { n as DocumentLoader } from "../docloader-Dr8wEkif.cjs";
|
|
3
|
-
import {
|
|
3
|
+
import { r as PortableObjectVerifier } from "../portable-BykOD45s.cjs";
|
|
4
4
|
import { Span, TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
|
|
6
6
|
//#region src/internal/portable-dereference.d.ts
|
|
7
7
|
/**
|
|
8
|
+
* A reference from a vocabulary object's property, which generated
|
|
9
|
+
* accessors pass to {@link dereferencePortableIri}.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
interface PortableReferrerLink {
|
|
14
|
+
readonly object: object;
|
|
15
|
+
readonly property: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Checks whether a vocabulary object is part of a chain of portable objects,
|
|
19
|
+
* i.e., whether it has a portable ID or was obtained from a portable object.
|
|
20
|
+
*
|
|
21
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
22
|
+
* part of the public API contract. This is not considered public API for
|
|
23
|
+
* Semantic Versioning decisions.
|
|
24
|
+
*/
|
|
25
|
+
declare function isInPortableChain(object: object): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Checks whether a vocabulary object was accepted without an integrity
|
|
28
|
+
* proof, e.g., as an unsecured portable collection served by a trusted
|
|
29
|
+
* gateway. Objects embedded in such an object must not be trusted because
|
|
30
|
+
* of their origin.
|
|
31
|
+
*
|
|
32
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
33
|
+
* part of the public API contract. This is not considered public API for
|
|
34
|
+
* Semantic Versioning decisions.
|
|
35
|
+
*/
|
|
36
|
+
declare function isUnsecuredPortableObject(object: object): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Checks whether a URL is an FEP-ef61 compatible identifier, i.e., an HTTP(S)
|
|
39
|
+
* URL under a gateway's `/.well-known/apgateway/` path that stands for
|
|
40
|
+
* a portable object. Malformed compatible identifiers count as compatible
|
|
41
|
+
* identifiers too.
|
|
42
|
+
*
|
|
43
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
44
|
+
* part of the public API contract. This is not considered public API for
|
|
45
|
+
* Semantic Versioning decisions.
|
|
46
|
+
*/
|
|
47
|
+
declare function isCompatibleEf61Iri(url: URL | string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Records that a vocabulary object returned by a property accessor was
|
|
50
|
+
* obtained through the given property of a portable object, so that later
|
|
51
|
+
* dereferences from it can tell where it came from. Nothing is recorded if
|
|
52
|
+
* the parent is not in a portable chain, or if the child already has its
|
|
53
|
+
* provenance.
|
|
54
|
+
*
|
|
55
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
56
|
+
* part of the public API contract. This is not considered public API for
|
|
57
|
+
* Semantic Versioning decisions.
|
|
58
|
+
*/
|
|
59
|
+
declare function recordPortableReferrer(parent: object, child: unknown, property: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* Copies the provenance of a vocabulary object to its clone. The clone
|
|
62
|
+
* keeps where the original came from and the restriction on unsecured
|
|
63
|
+
* objects, but not a positive verification status nor a collection context,
|
|
64
|
+
* since the clone may have different property values.
|
|
65
|
+
*
|
|
66
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
67
|
+
* part of the public API contract. This is not considered public API for
|
|
68
|
+
* Semantic Versioning decisions.
|
|
69
|
+
*/
|
|
70
|
+
declare function copyPortableProvenance(from: object, to: object): void;
|
|
71
|
+
/**
|
|
72
|
+
* Rejects a reference from a portable object that would be dereferenced as
|
|
73
|
+
* an ordinary HTTP(S) object although it stands for a portable object, i.e.,
|
|
74
|
+
* an FEP-ef61 compatible identifier, or a document whose final URL or `@id`
|
|
75
|
+
* is a compatible identifier or a portable IRI. Such a document would skip
|
|
76
|
+
* the portable object policy, so it is rejected regardless of the
|
|
77
|
+
* `crossOrigin` option, except that `crossOrigin: "throw"` makes it throw.
|
|
78
|
+
*
|
|
79
|
+
* @param url The rejected URL, for the log message.
|
|
80
|
+
* @param crossOrigin The `crossOrigin` option of the accessor.
|
|
81
|
+
* @returns Always `null`.
|
|
82
|
+
* @throws {Error} If `crossOrigin` is `"throw"`.
|
|
83
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
84
|
+
* part of the public API contract. This is not considered public API for
|
|
85
|
+
* Semantic Versioning decisions.
|
|
86
|
+
*/
|
|
87
|
+
declare function rejectPortableChainReference(url: string, crossOrigin: "ignore" | "throw" | "trust" | undefined): null;
|
|
88
|
+
/**
|
|
89
|
+
* Logs that an embedded object without `@id` in an object accepted without
|
|
90
|
+
* an integrity proof is dropped, because it cannot be dereferenced and
|
|
91
|
+
* verified on its own.
|
|
92
|
+
*
|
|
93
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
94
|
+
* part of the public API contract. This is not considered public API for
|
|
95
|
+
* Semantic Versioning decisions.
|
|
96
|
+
*/
|
|
97
|
+
declare function warnUnverifiableEmbeddedObject(parent: object, property: string): void;
|
|
98
|
+
/**
|
|
8
99
|
* Checks whether a URL is an FEP-ef61 portable ActivityPub IRI.
|
|
9
100
|
*
|
|
10
101
|
* @internal Technically exported for generated vocabulary classes, but not
|
|
@@ -37,6 +128,7 @@ interface DereferencePortableIriOptions<T> {
|
|
|
37
128
|
tracerProvider: TracerProvider;
|
|
38
129
|
gateways?: readonly (string | URL)[];
|
|
39
130
|
verifyPortableObject?: PortableObjectVerifier;
|
|
131
|
+
referrer?: PortableReferrerLink;
|
|
40
132
|
suppressError?: boolean;
|
|
41
133
|
crossOrigin?: "ignore" | "throw" | "trust";
|
|
42
134
|
/**
|
|
@@ -84,4 +176,4 @@ declare function dereferencePortableIri<T extends {
|
|
|
84
176
|
id: URL | null;
|
|
85
177
|
}>(url: URL, options: DereferencePortableIriOptions<T>): Promise<T | null>;
|
|
86
178
|
//#endregion
|
|
87
|
-
export { DereferencePortableIriOptions, PortableObjectRejectedError, dereferencePortableIri, getPortableGatewayCandidates, isPortableIri };
|
|
179
|
+
export { DereferencePortableIriOptions, PortableObjectRejectedError, PortableReferrerLink, copyPortableProvenance, dereferencePortableIri, getPortableGatewayCandidates, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isUnsecuredPortableObject, recordPortableReferrer, rejectPortableChainReference, warnUnverifiableEmbeddedObject };
|
|
@@ -1,10 +1,101 @@
|
|
|
1
1
|
/// <reference lib="esnext.temporal" />
|
|
2
2
|
import { n as DocumentLoader } from "../docloader-Dr8wEkif.js";
|
|
3
|
-
import {
|
|
3
|
+
import { r as PortableObjectVerifier } from "../portable--NcyA1R_.js";
|
|
4
4
|
import { Span, TracerProvider } from "@opentelemetry/api";
|
|
5
5
|
|
|
6
6
|
//#region src/internal/portable-dereference.d.ts
|
|
7
7
|
/**
|
|
8
|
+
* A reference from a vocabulary object's property, which generated
|
|
9
|
+
* accessors pass to {@link dereferencePortableIri}.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
interface PortableReferrerLink {
|
|
14
|
+
readonly object: object;
|
|
15
|
+
readonly property: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Checks whether a vocabulary object is part of a chain of portable objects,
|
|
19
|
+
* i.e., whether it has a portable ID or was obtained from a portable object.
|
|
20
|
+
*
|
|
21
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
22
|
+
* part of the public API contract. This is not considered public API for
|
|
23
|
+
* Semantic Versioning decisions.
|
|
24
|
+
*/
|
|
25
|
+
declare function isInPortableChain(object: object): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Checks whether a vocabulary object was accepted without an integrity
|
|
28
|
+
* proof, e.g., as an unsecured portable collection served by a trusted
|
|
29
|
+
* gateway. Objects embedded in such an object must not be trusted because
|
|
30
|
+
* of their origin.
|
|
31
|
+
*
|
|
32
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
33
|
+
* part of the public API contract. This is not considered public API for
|
|
34
|
+
* Semantic Versioning decisions.
|
|
35
|
+
*/
|
|
36
|
+
declare function isUnsecuredPortableObject(object: object): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Checks whether a URL is an FEP-ef61 compatible identifier, i.e., an HTTP(S)
|
|
39
|
+
* URL under a gateway's `/.well-known/apgateway/` path that stands for
|
|
40
|
+
* a portable object. Malformed compatible identifiers count as compatible
|
|
41
|
+
* identifiers too.
|
|
42
|
+
*
|
|
43
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
44
|
+
* part of the public API contract. This is not considered public API for
|
|
45
|
+
* Semantic Versioning decisions.
|
|
46
|
+
*/
|
|
47
|
+
declare function isCompatibleEf61Iri(url: URL | string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Records that a vocabulary object returned by a property accessor was
|
|
50
|
+
* obtained through the given property of a portable object, so that later
|
|
51
|
+
* dereferences from it can tell where it came from. Nothing is recorded if
|
|
52
|
+
* the parent is not in a portable chain, or if the child already has its
|
|
53
|
+
* provenance.
|
|
54
|
+
*
|
|
55
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
56
|
+
* part of the public API contract. This is not considered public API for
|
|
57
|
+
* Semantic Versioning decisions.
|
|
58
|
+
*/
|
|
59
|
+
declare function recordPortableReferrer(parent: object, child: unknown, property: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* Copies the provenance of a vocabulary object to its clone. The clone
|
|
62
|
+
* keeps where the original came from and the restriction on unsecured
|
|
63
|
+
* objects, but not a positive verification status nor a collection context,
|
|
64
|
+
* since the clone may have different property values.
|
|
65
|
+
*
|
|
66
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
67
|
+
* part of the public API contract. This is not considered public API for
|
|
68
|
+
* Semantic Versioning decisions.
|
|
69
|
+
*/
|
|
70
|
+
declare function copyPortableProvenance(from: object, to: object): void;
|
|
71
|
+
/**
|
|
72
|
+
* Rejects a reference from a portable object that would be dereferenced as
|
|
73
|
+
* an ordinary HTTP(S) object although it stands for a portable object, i.e.,
|
|
74
|
+
* an FEP-ef61 compatible identifier, or a document whose final URL or `@id`
|
|
75
|
+
* is a compatible identifier or a portable IRI. Such a document would skip
|
|
76
|
+
* the portable object policy, so it is rejected regardless of the
|
|
77
|
+
* `crossOrigin` option, except that `crossOrigin: "throw"` makes it throw.
|
|
78
|
+
*
|
|
79
|
+
* @param url The rejected URL, for the log message.
|
|
80
|
+
* @param crossOrigin The `crossOrigin` option of the accessor.
|
|
81
|
+
* @returns Always `null`.
|
|
82
|
+
* @throws {Error} If `crossOrigin` is `"throw"`.
|
|
83
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
84
|
+
* part of the public API contract. This is not considered public API for
|
|
85
|
+
* Semantic Versioning decisions.
|
|
86
|
+
*/
|
|
87
|
+
declare function rejectPortableChainReference(url: string, crossOrigin: "ignore" | "throw" | "trust" | undefined): null;
|
|
88
|
+
/**
|
|
89
|
+
* Logs that an embedded object without `@id` in an object accepted without
|
|
90
|
+
* an integrity proof is dropped, because it cannot be dereferenced and
|
|
91
|
+
* verified on its own.
|
|
92
|
+
*
|
|
93
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
94
|
+
* part of the public API contract. This is not considered public API for
|
|
95
|
+
* Semantic Versioning decisions.
|
|
96
|
+
*/
|
|
97
|
+
declare function warnUnverifiableEmbeddedObject(parent: object, property: string): void;
|
|
98
|
+
/**
|
|
8
99
|
* Checks whether a URL is an FEP-ef61 portable ActivityPub IRI.
|
|
9
100
|
*
|
|
10
101
|
* @internal Technically exported for generated vocabulary classes, but not
|
|
@@ -37,6 +128,7 @@ interface DereferencePortableIriOptions<T> {
|
|
|
37
128
|
tracerProvider: TracerProvider;
|
|
38
129
|
gateways?: readonly (string | URL)[];
|
|
39
130
|
verifyPortableObject?: PortableObjectVerifier;
|
|
131
|
+
referrer?: PortableReferrerLink;
|
|
40
132
|
suppressError?: boolean;
|
|
41
133
|
crossOrigin?: "ignore" | "throw" | "trust";
|
|
42
134
|
/**
|
|
@@ -84,4 +176,4 @@ declare function dereferencePortableIri<T extends {
|
|
|
84
176
|
id: URL | null;
|
|
85
177
|
}>(url: URL, options: DereferencePortableIriOptions<T>): Promise<T | null>;
|
|
86
178
|
//#endregion
|
|
87
|
-
export { DereferencePortableIriOptions, PortableObjectRejectedError, dereferencePortableIri, getPortableGatewayCandidates, isPortableIri };
|
|
179
|
+
export { DereferencePortableIriOptions, PortableObjectRejectedError, PortableReferrerLink, copyPortableProvenance, dereferencePortableIri, getPortableGatewayCandidates, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isUnsecuredPortableObject, recordPortableReferrer, rejectPortableChainReference, warnUnverifiableEmbeddedObject };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { t as preloadedContexts } from "../contexts-DoZUIW7y.js";
|
|
3
|
-
import { a as formatIri, g as toCompatibleEf61Id, m as parseIri, r as canonicalizePortableUri } from "../url-CEhi4hb4.js";
|
|
3
|
+
import { a as formatIri, g as toCompatibleEf61Id, m as parseIri, o as fromCompatibleEf61Id, r as canonicalizePortableUri } from "../url-CEhi4hb4.js";
|
|
4
4
|
import jsonld_default from "../jsonld.js";
|
|
5
5
|
import { getLogger } from "@logtape/logtape";
|
|
6
6
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
@@ -24,6 +24,156 @@ const BASELINE_CONTEXT_URLS = /* @__PURE__ */ new Set([
|
|
|
24
24
|
"https://w3id.org/security/data-integrity/v1"
|
|
25
25
|
]);
|
|
26
26
|
/**
|
|
27
|
+
* The maximum number of links in a referrer chain passed to a
|
|
28
|
+
* {@link PortableObjectVerifier}. Longer chains are cut off and marked as
|
|
29
|
+
* truncated.
|
|
30
|
+
*/
|
|
31
|
+
const MAX_REFERRER_CHAIN_LENGTH = 32;
|
|
32
|
+
const provenances = /* @__PURE__ */ new WeakMap();
|
|
33
|
+
function getObjectId(object) {
|
|
34
|
+
if (object == null || typeof object !== "object" || !("id" in object)) return null;
|
|
35
|
+
const id = object.id;
|
|
36
|
+
return id instanceof URL ? id : null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Checks whether a vocabulary object is part of a chain of portable objects,
|
|
40
|
+
* i.e., whether it has a portable ID or was obtained from a portable object.
|
|
41
|
+
*
|
|
42
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
43
|
+
* part of the public API contract. This is not considered public API for
|
|
44
|
+
* Semantic Versioning decisions.
|
|
45
|
+
*/
|
|
46
|
+
function isInPortableChain(object) {
|
|
47
|
+
if (provenances.has(object)) return true;
|
|
48
|
+
const id = getObjectId(object);
|
|
49
|
+
return id != null && isPortableIri(id);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Checks whether a vocabulary object was accepted without an integrity
|
|
53
|
+
* proof, e.g., as an unsecured portable collection served by a trusted
|
|
54
|
+
* gateway. Objects embedded in such an object must not be trusted because
|
|
55
|
+
* of their origin.
|
|
56
|
+
*
|
|
57
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
58
|
+
* part of the public API contract. This is not considered public API for
|
|
59
|
+
* Semantic Versioning decisions.
|
|
60
|
+
*/
|
|
61
|
+
function isUnsecuredPortableObject(object) {
|
|
62
|
+
return provenances.get(object)?.acceptance === "unsecured";
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Checks whether a URL is an FEP-ef61 compatible identifier, i.e., an HTTP(S)
|
|
66
|
+
* URL under a gateway's `/.well-known/apgateway/` path that stands for
|
|
67
|
+
* a portable object. Malformed compatible identifiers count as compatible
|
|
68
|
+
* identifiers too.
|
|
69
|
+
*
|
|
70
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
71
|
+
* part of the public API contract. This is not considered public API for
|
|
72
|
+
* Semantic Versioning decisions.
|
|
73
|
+
*/
|
|
74
|
+
function isCompatibleEf61Iri(url) {
|
|
75
|
+
try {
|
|
76
|
+
return fromCompatibleEf61Id(url) != null;
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error instanceof TypeError) return true;
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Records that a vocabulary object returned by a property accessor was
|
|
84
|
+
* obtained through the given property of a portable object, so that later
|
|
85
|
+
* dereferences from it can tell where it came from. Nothing is recorded if
|
|
86
|
+
* the parent is not in a portable chain, or if the child already has its
|
|
87
|
+
* provenance.
|
|
88
|
+
*
|
|
89
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
90
|
+
* part of the public API contract. This is not considered public API for
|
|
91
|
+
* Semantic Versioning decisions.
|
|
92
|
+
*/
|
|
93
|
+
function recordPortableReferrer(parent, child, property) {
|
|
94
|
+
if (child == null || typeof child !== "object") return;
|
|
95
|
+
if (provenances.has(child) || !isInPortableChain(parent)) return;
|
|
96
|
+
provenances.set(child, { referrer: {
|
|
97
|
+
object: parent,
|
|
98
|
+
property
|
|
99
|
+
} });
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Copies the provenance of a vocabulary object to its clone. The clone
|
|
103
|
+
* keeps where the original came from and the restriction on unsecured
|
|
104
|
+
* objects, but not a positive verification status nor a collection context,
|
|
105
|
+
* since the clone may have different property values.
|
|
106
|
+
*
|
|
107
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
108
|
+
* part of the public API contract. This is not considered public API for
|
|
109
|
+
* Semantic Versioning decisions.
|
|
110
|
+
*/
|
|
111
|
+
function copyPortableProvenance(from, to) {
|
|
112
|
+
const provenance = provenances.get(from);
|
|
113
|
+
if (provenance == null) return;
|
|
114
|
+
provenances.set(to, {
|
|
115
|
+
referrer: provenance.referrer,
|
|
116
|
+
...provenance.acceptance === "unsecured" ? { acceptance: "unsecured" } : {}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Rejects a reference from a portable object that would be dereferenced as
|
|
121
|
+
* an ordinary HTTP(S) object although it stands for a portable object, i.e.,
|
|
122
|
+
* an FEP-ef61 compatible identifier, or a document whose final URL or `@id`
|
|
123
|
+
* is a compatible identifier or a portable IRI. Such a document would skip
|
|
124
|
+
* the portable object policy, so it is rejected regardless of the
|
|
125
|
+
* `crossOrigin` option, except that `crossOrigin: "throw"` makes it throw.
|
|
126
|
+
*
|
|
127
|
+
* @param url The rejected URL, for the log message.
|
|
128
|
+
* @param crossOrigin The `crossOrigin` option of the accessor.
|
|
129
|
+
* @returns Always `null`.
|
|
130
|
+
* @throws {Error} If `crossOrigin` is `"throw"`.
|
|
131
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
132
|
+
* part of the public API contract. This is not considered public API for
|
|
133
|
+
* Semantic Versioning decisions.
|
|
134
|
+
*/
|
|
135
|
+
function rejectPortableChainReference(url, crossOrigin) {
|
|
136
|
+
const message = "Refusing to dereference {url} from a portable object as an ordinary HTTP(S) object, because it stands for an FEP-ef61 portable object that is not verified this way.";
|
|
137
|
+
if (crossOrigin === "throw") throw new Error(message.replace("{url}", url));
|
|
138
|
+
logger.warn(message, { url });
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Logs that an embedded object without `@id` in an object accepted without
|
|
143
|
+
* an integrity proof is dropped, because it cannot be dereferenced and
|
|
144
|
+
* verified on its own.
|
|
145
|
+
*
|
|
146
|
+
* @internal Technically exported for generated vocabulary classes, but not
|
|
147
|
+
* part of the public API contract. This is not considered public API for
|
|
148
|
+
* Semantic Versioning decisions.
|
|
149
|
+
*/
|
|
150
|
+
function warnUnverifiableEmbeddedObject(parent, property) {
|
|
151
|
+
logger.warn("Dropping an embedded object without @id in the {property} property of {parentId}, because the parent was accepted without an integrity proof and the embedded object cannot be verified on its own.", {
|
|
152
|
+
property,
|
|
153
|
+
parentId: getObjectId(parent)?.href ?? null
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function buildReferrerChain(link, depth = 0) {
|
|
157
|
+
if (link == null) return void 0;
|
|
158
|
+
const provenance = provenances.get(link.object);
|
|
159
|
+
const base = {
|
|
160
|
+
object: link.object,
|
|
161
|
+
id: getObjectId(link.object),
|
|
162
|
+
property: link.property,
|
|
163
|
+
...provenance?.acceptance == null ? {} : { acceptance: provenance.acceptance },
|
|
164
|
+
...provenance?.collectionContext === void 0 ? {} : { collectionContext: provenance.collectionContext }
|
|
165
|
+
};
|
|
166
|
+
if (provenance?.referrer == null) return base;
|
|
167
|
+
if (depth + 1 >= MAX_REFERRER_CHAIN_LENGTH) return {
|
|
168
|
+
...base,
|
|
169
|
+
truncated: true
|
|
170
|
+
};
|
|
171
|
+
return {
|
|
172
|
+
...base,
|
|
173
|
+
referrer: buildReferrerChain(provenance.referrer, depth + 1)
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
27
177
|
* Checks whether a URL is an FEP-ef61 portable ActivityPub IRI.
|
|
28
178
|
*
|
|
29
179
|
* @internal Technically exported for generated vocabulary classes, but not
|
|
@@ -204,6 +354,9 @@ async function dereferencePortableIri(url, options) {
|
|
|
204
354
|
const contextLoader = snapshot.loader;
|
|
205
355
|
const attempts = [];
|
|
206
356
|
const { signal } = options;
|
|
357
|
+
const explicitGateways = options.gateways == null ? void 0 : gateways;
|
|
358
|
+
const gatewayHints = options.gateways == null && gateways.length > 0 ? gateways : void 0;
|
|
359
|
+
const referrer = buildReferrerChain(options.referrer);
|
|
207
360
|
try {
|
|
208
361
|
for (const { url: requestUrl, gateway } of requestUrls) {
|
|
209
362
|
signal?.throwIfAborted();
|
|
@@ -226,12 +379,22 @@ async function dereferencePortableIri(url, options) {
|
|
|
226
379
|
const { document } = remoteDocument;
|
|
227
380
|
try {
|
|
228
381
|
await checkPortableObjectId(document, expectedId, contextLoader);
|
|
382
|
+
let documentUrl;
|
|
383
|
+
try {
|
|
384
|
+
documentUrl = parseIri(remoteDocument.documentUrl);
|
|
385
|
+
} catch {
|
|
386
|
+
documentUrl = void 0;
|
|
387
|
+
}
|
|
229
388
|
let result;
|
|
230
389
|
try {
|
|
231
390
|
result = await verify(document, {
|
|
232
391
|
documentLoader: options.documentLoader,
|
|
233
392
|
contextLoader,
|
|
234
|
-
tracerProvider: options.tracerProvider
|
|
393
|
+
tracerProvider: options.tracerProvider,
|
|
394
|
+
...documentUrl == null ? {} : { documentUrl },
|
|
395
|
+
...explicitGateways == null ? {} : { gateways: explicitGateways },
|
|
396
|
+
...gatewayHints == null ? {} : { gatewayHints },
|
|
397
|
+
...referrer == null ? {} : { referrer }
|
|
235
398
|
});
|
|
236
399
|
} catch (error) {
|
|
237
400
|
throw new PortableObjectRejection("the proof verifier failed: {error}", { error });
|
|
@@ -242,6 +405,11 @@ async function dereferencePortableIri(url, options) {
|
|
|
242
405
|
baseUrl: url
|
|
243
406
|
});
|
|
244
407
|
signal?.throwIfAborted();
|
|
408
|
+
if (object != null && typeof object === "object") provenances.set(object, {
|
|
409
|
+
acceptance: result.unsecured === true ? "unsecured" : "verified",
|
|
410
|
+
...result.collectionContext === void 0 ? {} : { collectionContext: result.collectionContext },
|
|
411
|
+
...options.referrer == null ? {} : { referrer: options.referrer }
|
|
412
|
+
});
|
|
245
413
|
if (gateway != null) span?.setAttribute("activitypub.gateway", gateway.href);
|
|
246
414
|
return object;
|
|
247
415
|
} catch (error) {
|
|
@@ -308,4 +476,4 @@ async function checkPortableObjectId(document, expectedId, contextLoader) {
|
|
|
308
476
|
if (actualId !== expectedId) throw new PortableObjectRejection("its @id ({objectId}) does not match the requested portable ID", { objectId: id });
|
|
309
477
|
}
|
|
310
478
|
//#endregion
|
|
311
|
-
export { PortableObjectRejectedError, dereferencePortableIri, getPortableGatewayCandidates, isPortableIri };
|
|
479
|
+
export { PortableObjectRejectedError, copyPortableProvenance, dereferencePortableIri, getPortableGatewayCandidates, isCompatibleEf61Iri, isInPortableChain, isPortableIri, isUnsecuredPortableObject, recordPortableReferrer, rejectPortableChainReference, warnUnverifiableEmbeddedObject };
|
package/dist/mod.cjs
CHANGED
|
@@ -16,7 +16,7 @@ let _multiformats_base_x = require("@multiformats/base-x");
|
|
|
16
16
|
_multiformats_base_x = require_rolldown_runtime.__toESM(_multiformats_base_x, 1);
|
|
17
17
|
//#region deno.json
|
|
18
18
|
var name = "@fedify/vocab-runtime";
|
|
19
|
-
var version = "2.4.0-dev.
|
|
19
|
+
var version = "2.4.0-dev.2063+366c0362";
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/request.ts
|
|
22
22
|
/**
|