@adcp/sdk 14.0.0-rc.39 → 14.0.0-rc.40

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.
Files changed (81) hide show
  1. package/README.md +2 -0
  2. package/dist/lib/core/AgentClient.d.mts +4 -3
  3. package/dist/lib/core/AgentClient.d.ts +4 -3
  4. package/dist/lib/core/AsyncHandler.d.mts +3 -0
  5. package/dist/lib/core/AsyncHandler.d.ts +3 -0
  6. package/dist/lib/core/AsyncHandler.js +3 -0
  7. package/dist/lib/core/AsyncHandler.mjs +3 -0
  8. package/dist/lib/core/SingleAgentClient.d.mts +6 -1
  9. package/dist/lib/core/SingleAgentClient.d.ts +6 -1
  10. package/dist/lib/core/SingleAgentClient.js +57 -17
  11. package/dist/lib/core/SingleAgentClient.mjs +59 -17
  12. package/dist/lib/discovery/adagents-redirects.d.mts +5 -1
  13. package/dist/lib/discovery/adagents-redirects.d.ts +5 -1
  14. package/dist/lib/discovery/adagents-redirects.js +10 -3
  15. package/dist/lib/discovery/adagents-redirects.mjs +10 -3
  16. package/dist/lib/discovery/types.d.mts +42 -0
  17. package/dist/lib/discovery/types.d.ts +42 -0
  18. package/dist/lib/index.d.mts +3 -1
  19. package/dist/lib/index.d.ts +3 -1
  20. package/dist/lib/index.js +3 -1
  21. package/dist/lib/index.mjs +1 -0
  22. package/dist/lib/net/ssrf-fetch.js +47 -21
  23. package/dist/lib/net/ssrf-fetch.mjs +47 -21
  24. package/dist/lib/registry/index.d.mts +3 -0
  25. package/dist/lib/registry/index.d.ts +3 -0
  26. package/dist/lib/registry/index.js +8 -0
  27. package/dist/lib/registry/index.mjs +8 -0
  28. package/dist/lib/registry/types.d.mts +2 -1
  29. package/dist/lib/registry/types.d.ts +2 -1
  30. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  31. package/dist/lib/supply-path/evaluate.d.mts +17 -0
  32. package/dist/lib/supply-path/evaluate.d.ts +17 -0
  33. package/dist/lib/supply-path/evaluate.js +436 -0
  34. package/dist/lib/supply-path/evaluate.mjs +407 -0
  35. package/dist/lib/supply-path/fetch-evidence.d.mts +36 -0
  36. package/dist/lib/supply-path/fetch-evidence.d.ts +36 -0
  37. package/dist/lib/supply-path/fetch-evidence.js +293 -0
  38. package/dist/lib/supply-path/fetch-evidence.mjs +268 -0
  39. package/dist/lib/supply-path/index.d.mts +6 -0
  40. package/dist/lib/supply-path/index.d.ts +6 -0
  41. package/dist/lib/supply-path/index.js +49 -0
  42. package/dist/lib/supply-path/index.mjs +23 -0
  43. package/dist/lib/supply-path/products.d.mts +41 -0
  44. package/dist/lib/supply-path/products.d.ts +41 -0
  45. package/dist/lib/supply-path/products.js +228 -0
  46. package/dist/lib/supply-path/products.mjs +204 -0
  47. package/dist/lib/supply-path/revocations.d.mts +55 -0
  48. package/dist/lib/supply-path/revocations.d.ts +55 -0
  49. package/dist/lib/supply-path/revocations.js +127 -0
  50. package/dist/lib/supply-path/revocations.mjs +99 -0
  51. package/dist/lib/supply-path/types.d.mts +168 -0
  52. package/dist/lib/supply-path/types.d.ts +168 -0
  53. package/dist/lib/supply-path/types.js +16 -0
  54. package/dist/lib/supply-path/types.mjs +0 -0
  55. package/dist/lib/supply-path/validation.d.mts +10 -0
  56. package/dist/lib/supply-path/validation.d.ts +10 -0
  57. package/dist/lib/supply-path/validation.js +152 -0
  58. package/dist/lib/supply-path/validation.mjs +122 -0
  59. package/dist/lib/supply-path/verify.d.mts +25 -0
  60. package/dist/lib/supply-path/verify.d.ts +25 -0
  61. package/dist/lib/supply-path/verify.js +218 -0
  62. package/dist/lib/supply-path/verify.mjs +203 -0
  63. package/dist/lib/testing/storyboard/request-signing/grader.js +19 -9
  64. package/dist/lib/testing/storyboard/request-signing/grader.mjs +19 -9
  65. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  66. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  67. package/dist/lib/v2/projection/creative-delivery.d.mts +1 -1
  68. package/dist/lib/v2/projection/creative-delivery.d.ts +1 -1
  69. package/dist/lib/validation/schema-loader.js +7 -5
  70. package/dist/lib/validation/schema-loader.mjs +7 -5
  71. package/dist/lib/version.d.mts +3 -3
  72. package/dist/lib/version.d.ts +3 -3
  73. package/dist/lib/version.js +3 -3
  74. package/dist/lib/version.mjs +3 -3
  75. package/docs/TYPE-SUMMARY.md +2 -2
  76. package/docs/guides/SUPPLY-PATH-VERIFICATION.md +101 -0
  77. package/docs/llms.txt +2 -2
  78. package/docs/migration-14.x-rc-worksheet.md +4 -4
  79. package/examples/README.md +2 -0
  80. package/examples/supply-path-verification.ts +29 -0
  81. package/package.json +2 -1
@@ -0,0 +1,293 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var fetch_evidence_exports = {};
20
+ __export(fetch_evidence_exports, {
21
+ SupplyPathEvidenceSession: () => SupplyPathEvidenceSession,
22
+ boundedOption: () => boundedOption
23
+ });
24
+ module.exports = __toCommonJS(fetch_evidence_exports);
25
+ var import_revocations = require('./revocations.js');
26
+ var import_abort = require('../protocols/abort.js');
27
+ var import_node_crypto = require("node:crypto");
28
+ var import_secure_json_parse = require("secure-json-parse");
29
+ var import_ssrf_fetch = require('../net/ssrf-fetch.js');
30
+ var import_adagents_redirects = require('../discovery/adagents-redirects.js');
31
+ var import_revocations2 = require('./revocations.js');
32
+ var import_validation = require('./validation.js');
33
+ function boundedOption(value, fallback, maximum, name) {
34
+ if (value === void 0) return fallback;
35
+ if (!Number.isSafeInteger(value) || value <= 0 || value > maximum)
36
+ throw new TypeError(`${name} must be an integer between 1 and ${maximum}`);
37
+ return value;
38
+ }
39
+ class SupplyPathEvidenceSession {
40
+ constructor(options) {
41
+ this.options = options;
42
+ this.timeoutMs = boundedOption(options.timeoutMs, 15e3, 6e4, "timeoutMs");
43
+ this.deadlineAt = Date.now() + this.timeoutMs;
44
+ this.maxBodyBytes = boundedOption(options.maxBodyBytes, 256 * 1024, 20 * 1024 * 1024, "maxBodyBytes");
45
+ this.signal = this.controller.signal;
46
+ this.abort = () => this.controller.abort(options.signal?.reason);
47
+ options.signal?.addEventListener("abort", this.abort, { once: true });
48
+ if (options.signal?.aborted) this.abort();
49
+ this.timer = setTimeout(
50
+ () => this.controller.abort(new Error("Supply-path verification deadline exceeded")),
51
+ this.timeoutMs
52
+ );
53
+ }
54
+ options;
55
+ evidence = [];
56
+ crossOriginAuthorities = /* @__PURE__ */ new Set();
57
+ observedRevocations = /* @__PURE__ */ new Map();
58
+ revocations = /* @__PURE__ */ new Map();
59
+ signal;
60
+ maxBodyBytes;
61
+ timeoutMs;
62
+ deadlineAt;
63
+ timer;
64
+ controller = new AbortController();
65
+ abort;
66
+ responses = /* @__PURE__ */ new Map();
67
+ provenance = /* @__PURE__ */ new WeakMap();
68
+ observedBytes = 0;
69
+ documents = /* @__PURE__ */ new Map();
70
+ close() {
71
+ clearTimeout(this.timer);
72
+ this.controller.abort(new Error("Supply-path evidence session closed"));
73
+ this.options.signal?.removeEventListener("abort", this.abort);
74
+ }
75
+ /** Enforce the absolute deadline even while synchronous evidence evaluation blocks timer delivery. */
76
+ assertActive() {
77
+ this.signal.throwIfAborted();
78
+ if (Date.now() >= this.deadlineAt) {
79
+ this.controller.abort(new Error("Supply-path verification deadline exceeded"));
80
+ this.signal.throwIfAborted();
81
+ }
82
+ }
83
+ adagents(publisher) {
84
+ let pending = this.documents.get(publisher);
85
+ if (!pending) {
86
+ pending = this.readAndRemember(publisher);
87
+ this.documents.set(publisher, pending);
88
+ }
89
+ return pending;
90
+ }
91
+ read(publisher, kind, url, pointer = false) {
92
+ const key = JSON.stringify([publisher, kind, url, pointer]);
93
+ let pending = this.responses.get(key);
94
+ if (!pending) {
95
+ pending = this.fetchEvidence(publisher, kind, url, pointer);
96
+ this.responses.set(key, pending);
97
+ }
98
+ return pending;
99
+ }
100
+ async fetchEvidence(publisher, kind, url, pointer = false) {
101
+ this.assertActive();
102
+ try {
103
+ if (this.observedBytes >= 64 * 1024 * 1024) throw new Error("evidence_budget_exceeded");
104
+ const parsed = new URL(url);
105
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.hash || parsed.port && parsed.port !== "443") {
106
+ throw new Error("invalid_evidence_url");
107
+ }
108
+ const response = await (0, import_adagents_redirects.ssrfSafeFetchAdAgents)(
109
+ url,
110
+ {
111
+ timeoutMs: this.timeoutMs,
112
+ maxBodyBytes: this.maxBodyBytes,
113
+ signal: this.signal,
114
+ trustedFetchFn: this.options.trustedFetchFn,
115
+ headers: { Accept: kind === "adagents" ? "application/json" : "text/plain", "Cache-Control": "no-cache" }
116
+ },
117
+ pointer ? { mode: "none" } : { mode: "same-origin", originUrl: url, maxRedirects: 3 },
118
+ (result) => {
119
+ this.observedBytes += result.body.byteLength;
120
+ const evidence = {
121
+ kind,
122
+ publisher_domain: publisher,
123
+ requested_url: url,
124
+ resolved_url: result.url,
125
+ fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
126
+ status: result.status,
127
+ byte_length: result.body.byteLength,
128
+ sha256: (0, import_node_crypto.createHash)("sha256").update(result.body).digest("hex"),
129
+ connection_pinned: result.connectionPinned,
130
+ ...result.status >= 300 && result.status < 400 && result.headers.location ? { delegated_to: result.headers.location } : {},
131
+ ...this.options.retainEvidenceBodies ? { body_base64: Buffer.from(result.body).toString("base64") } : {}
132
+ };
133
+ this.evidence.push(evidence);
134
+ this.provenance.set(result, evidence);
135
+ if (this.observedBytes > 64 * 1024 * 1024) throw new Error("evidence_budget_exceeded");
136
+ }
137
+ );
138
+ if (response.status === 404 && kind !== "adagents") return { ...response, body: new Uint8Array() };
139
+ if (response.status !== 200) return null;
140
+ const contentType = response.headers["content-type"]?.split(";")[0]?.trim().toLowerCase();
141
+ const validType = kind === "adagents" ? contentType === "application/json" || contentType?.endsWith("+json") : contentType === "text/plain";
142
+ if (!validType) throw new Error("invalid_content_type");
143
+ return response;
144
+ } catch (error) {
145
+ this.signal.throwIfAborted();
146
+ const rawCauseCode = (0, import_validation.record)(error) && typeof error.code === "string" ? error.code.toUpperCase() : void 0;
147
+ const causeCode = rawCauseCode && /^(?:E[A-Z0-9_]{2,40}|CERT_[A-Z0-9_]{2,40})$/.test(rawCauseCode) ? rawCauseCode : void 0;
148
+ const code = error instanceof import_ssrf_fetch.SsrfRefusedError || error instanceof import_adagents_redirects.AdAgentsRedirectRefusedError ? error.code : error instanceof Error && ["invalid_evidence_url", "invalid_content_type", "evidence_budget_exceeded"].includes(error.message) ? error.message : "fetch_failed";
149
+ this.evidence.push({
150
+ kind,
151
+ publisher_domain: publisher,
152
+ requested_url: url,
153
+ fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
154
+ error: code,
155
+ ...causeCode ? { cause_code: causeCode } : {}
156
+ });
157
+ if (error instanceof import_ssrf_fetch.SsrfRefusedError && !import_ssrf_fetch.SSRF_TRANSIENT_CODES.has(error.code)) throw error;
158
+ return null;
159
+ }
160
+ }
161
+ async parseManifest(publisher, response) {
162
+ this.assertActive();
163
+ let document;
164
+ try {
165
+ document = (0, import_secure_json_parse.parse)(new TextDecoder("utf-8", { fatal: true }).decode(response.body));
166
+ if (!(0, import_validation.record)(document)) throw new Error("not_object");
167
+ } catch {
168
+ this.documentError(publisher, response, "invalid_document");
169
+ return null;
170
+ }
171
+ this.assertActive();
172
+ const denials = (0, import_revocations2.parseRevocations)(document.revoked_publisher_domains);
173
+ if (denials === null) {
174
+ this.documentError(publisher, response, "invalid_revocations");
175
+ throw new TypeError(
176
+ `Invalid revoked_publisher_domains in adagents.json for ${publisher}; verification refused, correct the publisher's revocation declaration`
177
+ );
178
+ }
179
+ this.observedRevocations.set(publisher, [...this.observedRevocations.get(publisher) ?? [], ...denials]);
180
+ if (denials.length) await this.rememberRevocations(publisher);
181
+ return document;
182
+ }
183
+ documentError(publisher, response, error) {
184
+ this.evidence.push({
185
+ kind: "adagents",
186
+ publisher_domain: publisher,
187
+ requested_url: response.url,
188
+ fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
189
+ error
190
+ });
191
+ }
192
+ async readAdagents(publisher) {
193
+ const url = `https://${publisher}/.well-known/adagents.json`;
194
+ const initial = await this.read(publisher, "adagents", url);
195
+ if (!initial) return null;
196
+ let authorityLocation = url;
197
+ let documentResponse = initial;
198
+ let manifest = await this.parseManifest(publisher, initial);
199
+ if (!manifest) return null;
200
+ if (manifest.authoritative_location !== void 0 || manifest.superseded_by !== void 0) {
201
+ const target = manifest.authoritative_location ?? manifest.superseded_by;
202
+ if (typeof target !== "string" || target.length > 8192 || manifest.authoritative_location !== void 0 && (manifest.authorized_agents !== void 0 || manifest.superseded_by !== void 0)) {
203
+ this.documentError(publisher, initial, "ambiguous_authoritative_pointer");
204
+ return null;
205
+ }
206
+ const evidence = this.provenance.get(initial);
207
+ if (evidence) evidence.delegated_to = target;
208
+ let location;
209
+ try {
210
+ location = new URL(target);
211
+ } catch {
212
+ this.documentError(publisher, initial, "invalid_authoritative_location");
213
+ return null;
214
+ }
215
+ if (location.protocol !== "https:" || location.username || location.password || location.href.includes("#") || location.port && location.port !== "443") {
216
+ this.documentError(publisher, initial, "invalid_authoritative_location");
217
+ return null;
218
+ }
219
+ authorityLocation = location.href;
220
+ await this.checkAuthority(publisher, authorityLocation, "check");
221
+ const response = await this.read(publisher, "adagents", target, true);
222
+ if (!response) return null;
223
+ documentResponse = response;
224
+ manifest = await this.parseManifest(publisher, response);
225
+ if (!manifest) return null;
226
+ if (manifest.authoritative_location !== void 0 || manifest.superseded_by !== void 0) {
227
+ this.documentError(publisher, response, "chained_authoritative_pointer");
228
+ return null;
229
+ }
230
+ } else {
231
+ await this.checkAuthority(publisher, authorityLocation, "check");
232
+ }
233
+ if (!Array.isArray(manifest.authorized_agents) || !manifest.authorized_agents.every((entry) => (0, import_validation.record)(entry) && (0, import_validation.agentIdentity)(entry.url))) {
234
+ this.documentError(publisher, documentResponse, "malformed_authorized_agents");
235
+ return null;
236
+ }
237
+ await this.checkAuthority(publisher, authorityLocation, "observe");
238
+ if (new URL(authorityLocation).origin !== new URL(url).origin) this.crossOriginAuthorities.add(publisher);
239
+ return manifest;
240
+ }
241
+ async checkAuthority(publisher, location, operation) {
242
+ let accepted;
243
+ try {
244
+ accepted = await (0, import_abort.withAbortSignal)(
245
+ [this.signal],
246
+ void 0,
247
+ () => (this.options.authorityStore ?? import_revocations.defaultSupplyPathAuthorities)[operation](publisher, location)
248
+ );
249
+ } catch (cause) {
250
+ this.signal.throwIfAborted();
251
+ throw new Error(
252
+ `Supply-path authority storage failed for ${publisher}; inspect error.cause and restore store availability or capacity without discarding existing pins`,
253
+ { cause }
254
+ );
255
+ }
256
+ this.signal.throwIfAborted();
257
+ if (!accepted)
258
+ throw new TypeError(
259
+ `Authoritative location changed for ${publisher}; independently confirm the publisher migration before updating its pin`
260
+ );
261
+ }
262
+ async readAndRemember(publisher) {
263
+ const manifest = await this.readAdagents(publisher);
264
+ const effective = await this.rememberRevocations(publisher);
265
+ return manifest ? { ...manifest, revoked_publisher_domains: effective } : null;
266
+ }
267
+ async rememberRevocations(publisher) {
268
+ const observed = this.observedRevocations.get(publisher) ?? [];
269
+ let held;
270
+ try {
271
+ held = await (0, import_abort.withAbortSignal)(
272
+ [this.signal],
273
+ void 0,
274
+ () => (this.options.revocationStore ?? import_revocations2.defaultSupplyPathRevocations).observe(publisher, observed)
275
+ );
276
+ } catch (cause) {
277
+ this.signal.throwIfAborted();
278
+ throw new Error(
279
+ `Supply-path revocation storage failed for ${publisher}; inspect error.cause and restore store availability or capacity without discarding held revocations`,
280
+ { cause }
281
+ );
282
+ }
283
+ this.signal.throwIfAborted();
284
+ const effective = [...new Map([...held, ...observed].map((entry) => [entry.publisher_domain, entry])).values()];
285
+ this.revocations.set(publisher, effective);
286
+ return effective;
287
+ }
288
+ }
289
+ // Annotate the CommonJS export names for ESM import in node:
290
+ 0 && (module.exports = {
291
+ SupplyPathEvidenceSession,
292
+ boundedOption
293
+ });
@@ -0,0 +1,268 @@
1
+ import { defaultSupplyPathAuthorities } from "./revocations.mjs";
2
+ import { withAbortSignal } from "../protocols/abort.mjs";
3
+ import { createHash } from "node:crypto";
4
+ import { parse } from "secure-json-parse";
5
+ import { SsrfRefusedError, SSRF_TRANSIENT_CODES } from "../net/ssrf-fetch.mjs";
6
+ import { ssrfSafeFetchAdAgents, AdAgentsRedirectRefusedError } from "../discovery/adagents-redirects.mjs";
7
+ import { defaultSupplyPathRevocations, parseRevocations } from "./revocations.mjs";
8
+ import { agentIdentity, record } from "./validation.mjs";
9
+ function boundedOption(value, fallback, maximum, name) {
10
+ if (value === void 0) return fallback;
11
+ if (!Number.isSafeInteger(value) || value <= 0 || value > maximum)
12
+ throw new TypeError(`${name} must be an integer between 1 and ${maximum}`);
13
+ return value;
14
+ }
15
+ class SupplyPathEvidenceSession {
16
+ constructor(options) {
17
+ this.options = options;
18
+ this.timeoutMs = boundedOption(options.timeoutMs, 15e3, 6e4, "timeoutMs");
19
+ this.deadlineAt = Date.now() + this.timeoutMs;
20
+ this.maxBodyBytes = boundedOption(options.maxBodyBytes, 256 * 1024, 20 * 1024 * 1024, "maxBodyBytes");
21
+ this.signal = this.controller.signal;
22
+ this.abort = () => this.controller.abort(options.signal?.reason);
23
+ options.signal?.addEventListener("abort", this.abort, { once: true });
24
+ if (options.signal?.aborted) this.abort();
25
+ this.timer = setTimeout(
26
+ () => this.controller.abort(new Error("Supply-path verification deadline exceeded")),
27
+ this.timeoutMs
28
+ );
29
+ }
30
+ options;
31
+ evidence = [];
32
+ crossOriginAuthorities = /* @__PURE__ */ new Set();
33
+ observedRevocations = /* @__PURE__ */ new Map();
34
+ revocations = /* @__PURE__ */ new Map();
35
+ signal;
36
+ maxBodyBytes;
37
+ timeoutMs;
38
+ deadlineAt;
39
+ timer;
40
+ controller = new AbortController();
41
+ abort;
42
+ responses = /* @__PURE__ */ new Map();
43
+ provenance = /* @__PURE__ */ new WeakMap();
44
+ observedBytes = 0;
45
+ documents = /* @__PURE__ */ new Map();
46
+ close() {
47
+ clearTimeout(this.timer);
48
+ this.controller.abort(new Error("Supply-path evidence session closed"));
49
+ this.options.signal?.removeEventListener("abort", this.abort);
50
+ }
51
+ /** Enforce the absolute deadline even while synchronous evidence evaluation blocks timer delivery. */
52
+ assertActive() {
53
+ this.signal.throwIfAborted();
54
+ if (Date.now() >= this.deadlineAt) {
55
+ this.controller.abort(new Error("Supply-path verification deadline exceeded"));
56
+ this.signal.throwIfAborted();
57
+ }
58
+ }
59
+ adagents(publisher) {
60
+ let pending = this.documents.get(publisher);
61
+ if (!pending) {
62
+ pending = this.readAndRemember(publisher);
63
+ this.documents.set(publisher, pending);
64
+ }
65
+ return pending;
66
+ }
67
+ read(publisher, kind, url, pointer = false) {
68
+ const key = JSON.stringify([publisher, kind, url, pointer]);
69
+ let pending = this.responses.get(key);
70
+ if (!pending) {
71
+ pending = this.fetchEvidence(publisher, kind, url, pointer);
72
+ this.responses.set(key, pending);
73
+ }
74
+ return pending;
75
+ }
76
+ async fetchEvidence(publisher, kind, url, pointer = false) {
77
+ this.assertActive();
78
+ try {
79
+ if (this.observedBytes >= 64 * 1024 * 1024) throw new Error("evidence_budget_exceeded");
80
+ const parsed = new URL(url);
81
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.hash || parsed.port && parsed.port !== "443") {
82
+ throw new Error("invalid_evidence_url");
83
+ }
84
+ const response = await ssrfSafeFetchAdAgents(
85
+ url,
86
+ {
87
+ timeoutMs: this.timeoutMs,
88
+ maxBodyBytes: this.maxBodyBytes,
89
+ signal: this.signal,
90
+ trustedFetchFn: this.options.trustedFetchFn,
91
+ headers: { Accept: kind === "adagents" ? "application/json" : "text/plain", "Cache-Control": "no-cache" }
92
+ },
93
+ pointer ? { mode: "none" } : { mode: "same-origin", originUrl: url, maxRedirects: 3 },
94
+ (result) => {
95
+ this.observedBytes += result.body.byteLength;
96
+ const evidence = {
97
+ kind,
98
+ publisher_domain: publisher,
99
+ requested_url: url,
100
+ resolved_url: result.url,
101
+ fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
102
+ status: result.status,
103
+ byte_length: result.body.byteLength,
104
+ sha256: createHash("sha256").update(result.body).digest("hex"),
105
+ connection_pinned: result.connectionPinned,
106
+ ...result.status >= 300 && result.status < 400 && result.headers.location ? { delegated_to: result.headers.location } : {},
107
+ ...this.options.retainEvidenceBodies ? { body_base64: Buffer.from(result.body).toString("base64") } : {}
108
+ };
109
+ this.evidence.push(evidence);
110
+ this.provenance.set(result, evidence);
111
+ if (this.observedBytes > 64 * 1024 * 1024) throw new Error("evidence_budget_exceeded");
112
+ }
113
+ );
114
+ if (response.status === 404 && kind !== "adagents") return { ...response, body: new Uint8Array() };
115
+ if (response.status !== 200) return null;
116
+ const contentType = response.headers["content-type"]?.split(";")[0]?.trim().toLowerCase();
117
+ const validType = kind === "adagents" ? contentType === "application/json" || contentType?.endsWith("+json") : contentType === "text/plain";
118
+ if (!validType) throw new Error("invalid_content_type");
119
+ return response;
120
+ } catch (error) {
121
+ this.signal.throwIfAborted();
122
+ const rawCauseCode = record(error) && typeof error.code === "string" ? error.code.toUpperCase() : void 0;
123
+ const causeCode = rawCauseCode && /^(?:E[A-Z0-9_]{2,40}|CERT_[A-Z0-9_]{2,40})$/.test(rawCauseCode) ? rawCauseCode : void 0;
124
+ const code = error instanceof SsrfRefusedError || error instanceof AdAgentsRedirectRefusedError ? error.code : error instanceof Error && ["invalid_evidence_url", "invalid_content_type", "evidence_budget_exceeded"].includes(error.message) ? error.message : "fetch_failed";
125
+ this.evidence.push({
126
+ kind,
127
+ publisher_domain: publisher,
128
+ requested_url: url,
129
+ fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
130
+ error: code,
131
+ ...causeCode ? { cause_code: causeCode } : {}
132
+ });
133
+ if (error instanceof SsrfRefusedError && !SSRF_TRANSIENT_CODES.has(error.code)) throw error;
134
+ return null;
135
+ }
136
+ }
137
+ async parseManifest(publisher, response) {
138
+ this.assertActive();
139
+ let document;
140
+ try {
141
+ document = parse(new TextDecoder("utf-8", { fatal: true }).decode(response.body));
142
+ if (!record(document)) throw new Error("not_object");
143
+ } catch {
144
+ this.documentError(publisher, response, "invalid_document");
145
+ return null;
146
+ }
147
+ this.assertActive();
148
+ const denials = parseRevocations(document.revoked_publisher_domains);
149
+ if (denials === null) {
150
+ this.documentError(publisher, response, "invalid_revocations");
151
+ throw new TypeError(
152
+ `Invalid revoked_publisher_domains in adagents.json for ${publisher}; verification refused, correct the publisher's revocation declaration`
153
+ );
154
+ }
155
+ this.observedRevocations.set(publisher, [...this.observedRevocations.get(publisher) ?? [], ...denials]);
156
+ if (denials.length) await this.rememberRevocations(publisher);
157
+ return document;
158
+ }
159
+ documentError(publisher, response, error) {
160
+ this.evidence.push({
161
+ kind: "adagents",
162
+ publisher_domain: publisher,
163
+ requested_url: response.url,
164
+ fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
165
+ error
166
+ });
167
+ }
168
+ async readAdagents(publisher) {
169
+ const url = `https://${publisher}/.well-known/adagents.json`;
170
+ const initial = await this.read(publisher, "adagents", url);
171
+ if (!initial) return null;
172
+ let authorityLocation = url;
173
+ let documentResponse = initial;
174
+ let manifest = await this.parseManifest(publisher, initial);
175
+ if (!manifest) return null;
176
+ if (manifest.authoritative_location !== void 0 || manifest.superseded_by !== void 0) {
177
+ const target = manifest.authoritative_location ?? manifest.superseded_by;
178
+ if (typeof target !== "string" || target.length > 8192 || manifest.authoritative_location !== void 0 && (manifest.authorized_agents !== void 0 || manifest.superseded_by !== void 0)) {
179
+ this.documentError(publisher, initial, "ambiguous_authoritative_pointer");
180
+ return null;
181
+ }
182
+ const evidence = this.provenance.get(initial);
183
+ if (evidence) evidence.delegated_to = target;
184
+ let location;
185
+ try {
186
+ location = new URL(target);
187
+ } catch {
188
+ this.documentError(publisher, initial, "invalid_authoritative_location");
189
+ return null;
190
+ }
191
+ if (location.protocol !== "https:" || location.username || location.password || location.href.includes("#") || location.port && location.port !== "443") {
192
+ this.documentError(publisher, initial, "invalid_authoritative_location");
193
+ return null;
194
+ }
195
+ authorityLocation = location.href;
196
+ await this.checkAuthority(publisher, authorityLocation, "check");
197
+ const response = await this.read(publisher, "adagents", target, true);
198
+ if (!response) return null;
199
+ documentResponse = response;
200
+ manifest = await this.parseManifest(publisher, response);
201
+ if (!manifest) return null;
202
+ if (manifest.authoritative_location !== void 0 || manifest.superseded_by !== void 0) {
203
+ this.documentError(publisher, response, "chained_authoritative_pointer");
204
+ return null;
205
+ }
206
+ } else {
207
+ await this.checkAuthority(publisher, authorityLocation, "check");
208
+ }
209
+ if (!Array.isArray(manifest.authorized_agents) || !manifest.authorized_agents.every((entry) => record(entry) && agentIdentity(entry.url))) {
210
+ this.documentError(publisher, documentResponse, "malformed_authorized_agents");
211
+ return null;
212
+ }
213
+ await this.checkAuthority(publisher, authorityLocation, "observe");
214
+ if (new URL(authorityLocation).origin !== new URL(url).origin) this.crossOriginAuthorities.add(publisher);
215
+ return manifest;
216
+ }
217
+ async checkAuthority(publisher, location, operation) {
218
+ let accepted;
219
+ try {
220
+ accepted = await withAbortSignal(
221
+ [this.signal],
222
+ void 0,
223
+ () => (this.options.authorityStore ?? defaultSupplyPathAuthorities)[operation](publisher, location)
224
+ );
225
+ } catch (cause) {
226
+ this.signal.throwIfAborted();
227
+ throw new Error(
228
+ `Supply-path authority storage failed for ${publisher}; inspect error.cause and restore store availability or capacity without discarding existing pins`,
229
+ { cause }
230
+ );
231
+ }
232
+ this.signal.throwIfAborted();
233
+ if (!accepted)
234
+ throw new TypeError(
235
+ `Authoritative location changed for ${publisher}; independently confirm the publisher migration before updating its pin`
236
+ );
237
+ }
238
+ async readAndRemember(publisher) {
239
+ const manifest = await this.readAdagents(publisher);
240
+ const effective = await this.rememberRevocations(publisher);
241
+ return manifest ? { ...manifest, revoked_publisher_domains: effective } : null;
242
+ }
243
+ async rememberRevocations(publisher) {
244
+ const observed = this.observedRevocations.get(publisher) ?? [];
245
+ let held;
246
+ try {
247
+ held = await withAbortSignal(
248
+ [this.signal],
249
+ void 0,
250
+ () => (this.options.revocationStore ?? defaultSupplyPathRevocations).observe(publisher, observed)
251
+ );
252
+ } catch (cause) {
253
+ this.signal.throwIfAborted();
254
+ throw new Error(
255
+ `Supply-path revocation storage failed for ${publisher}; inspect error.cause and restore store availability or capacity without discarding held revocations`,
256
+ { cause }
257
+ );
258
+ }
259
+ this.signal.throwIfAborted();
260
+ const effective = [...new Map([...held, ...observed].map((entry) => [entry.publisher_domain, entry])).values()];
261
+ this.revocations.set(publisher, effective);
262
+ return effective;
263
+ }
264
+ }
265
+ export {
266
+ SupplyPathEvidenceSession,
267
+ boundedOption
268
+ };
@@ -0,0 +1,6 @@
1
+ export * from './types.mjs';
2
+ export { verifySupplyPath } from './verify.mjs';
3
+ export { evaluateSupplyPath, parseInventoryPartnerDomains, SUPPLY_PATH_STATES } from './evaluate.mjs';
4
+ export { annotateProductsSupplyPaths, type AnnotatedSupplyPathProduct, type ListProductsResponseWithSupplyPath, type ProductSupplyPathAnnotation, type ProductSupplyPathOptions, } from './products.mjs';
5
+ export { InMemorySupplyPathRevocationStore, type SupplyPathRevocationStore, type SupplyPathRevocation, } from './revocations.mjs';
6
+ export { defaultSupplyPathAuthorities, InMemorySupplyPathAuthorityStore, type SupplyPathAuthorityStore, } from './revocations.mjs';
@@ -0,0 +1,6 @@
1
+ export * from './types';
2
+ export { verifySupplyPath } from './verify';
3
+ export { evaluateSupplyPath, parseInventoryPartnerDomains, SUPPLY_PATH_STATES } from './evaluate';
4
+ export { annotateProductsSupplyPaths, type AnnotatedSupplyPathProduct, type ListProductsResponseWithSupplyPath, type ProductSupplyPathAnnotation, type ProductSupplyPathOptions, } from './products';
5
+ export { InMemorySupplyPathRevocationStore, type SupplyPathRevocationStore, type SupplyPathRevocation, } from './revocations';
6
+ export { defaultSupplyPathAuthorities, InMemorySupplyPathAuthorityStore, type SupplyPathAuthorityStore, } from './revocations';
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var supply_path_exports = {};
21
+ __export(supply_path_exports, {
22
+ InMemorySupplyPathAuthorityStore: () => import_revocations2.InMemorySupplyPathAuthorityStore,
23
+ InMemorySupplyPathRevocationStore: () => import_revocations.InMemorySupplyPathRevocationStore,
24
+ SUPPLY_PATH_STATES: () => import_evaluate.SUPPLY_PATH_STATES,
25
+ annotateProductsSupplyPaths: () => import_products.annotateProductsSupplyPaths,
26
+ defaultSupplyPathAuthorities: () => import_revocations2.defaultSupplyPathAuthorities,
27
+ evaluateSupplyPath: () => import_evaluate.evaluateSupplyPath,
28
+ parseInventoryPartnerDomains: () => import_evaluate.parseInventoryPartnerDomains,
29
+ verifySupplyPath: () => import_verify.verifySupplyPath
30
+ });
31
+ module.exports = __toCommonJS(supply_path_exports);
32
+ __reExport(supply_path_exports, require('./types.js'), module.exports);
33
+ var import_verify = require('./verify.js');
34
+ var import_evaluate = require('./evaluate.js');
35
+ var import_products = require('./products.js');
36
+ var import_revocations = require('./revocations.js');
37
+ var import_revocations2 = require('./revocations.js');
38
+ // Annotate the CommonJS export names for ESM import in node:
39
+ 0 && (module.exports = {
40
+ InMemorySupplyPathAuthorityStore,
41
+ InMemorySupplyPathRevocationStore,
42
+ SUPPLY_PATH_STATES,
43
+ annotateProductsSupplyPaths,
44
+ defaultSupplyPathAuthorities,
45
+ evaluateSupplyPath,
46
+ parseInventoryPartnerDomains,
47
+ verifySupplyPath,
48
+ ...require('./types.js')
49
+ });
@@ -0,0 +1,23 @@
1
+ export * from "./types.mjs";
2
+ import { verifySupplyPath } from "./verify.mjs";
3
+ import { evaluateSupplyPath, parseInventoryPartnerDomains, SUPPLY_PATH_STATES } from "./evaluate.mjs";
4
+ import {
5
+ annotateProductsSupplyPaths
6
+ } from "./products.mjs";
7
+ import {
8
+ InMemorySupplyPathRevocationStore
9
+ } from "./revocations.mjs";
10
+ import {
11
+ defaultSupplyPathAuthorities,
12
+ InMemorySupplyPathAuthorityStore
13
+ } from "./revocations.mjs";
14
+ export {
15
+ InMemorySupplyPathAuthorityStore,
16
+ InMemorySupplyPathRevocationStore,
17
+ SUPPLY_PATH_STATES,
18
+ annotateProductsSupplyPaths,
19
+ defaultSupplyPathAuthorities,
20
+ evaluateSupplyPath,
21
+ parseInventoryPartnerDomains,
22
+ verifySupplyPath
23
+ };