@adcp/sdk 14.0.0-beta.19 → 14.0.0-beta.20
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/bin/adcp.js +154 -6
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/decisioning/runtime/postgres-task-settlement.d.mts +10 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-settlement.d.ts +10 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-settlement.js +21 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-settlement.mjs +21 -0
- package/dist/lib/server/serve.d.mts +11 -0
- package/dist/lib/server/serve.d.ts +11 -0
- package/dist/lib/server/serve.js +35 -5
- package/dist/lib/server/serve.mjs +35 -5
- package/dist/lib/signing/brand-jwks.d.mts +44 -1
- package/dist/lib/signing/brand-jwks.d.ts +44 -1
- package/dist/lib/signing/brand-jwks.js +53 -15
- package/dist/lib/signing/brand-jwks.mjs +51 -14
- package/dist/lib/signing/server.d.mts +1 -1
- package/dist/lib/signing/server.d.ts +1 -1
- package/dist/lib/signing/server.js +2 -0
- package/dist/lib/signing/server.mjs +3 -1
- package/dist/lib/testing/storyboard/runner.js +6 -2
- package/dist/lib/testing/storyboard/runner.mjs +6 -2
- package/dist/lib/testing/storyboard/types.d.mts +6 -0
- package/dist/lib/testing/storyboard/types.d.ts +6 -0
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/storyboard/webhook-assertions.js +6 -1
- package/dist/lib/testing/storyboard/webhook-assertions.mjs +6 -1
- package/dist/lib/testing/storyboard/webhook-receiver.d.mts +15 -0
- package/dist/lib/testing/storyboard/webhook-receiver.d.ts +15 -0
- package/dist/lib/testing/storyboard/webhook-receiver.js +55 -15
- package/dist/lib/testing/storyboard/webhook-receiver.mjs +55 -15
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/llms.txt +1 -1
- package/package.json +1 -1
|
@@ -10,7 +10,13 @@ export type BrandJsonResolverErrorCode = 'invalid_url' | 'invalid_house' | 'redi
|
|
|
10
10
|
*/
|
|
11
11
|
export declare class BrandJsonResolverError extends Error {
|
|
12
12
|
readonly code: BrandJsonResolverErrorCode;
|
|
13
|
-
|
|
13
|
+
readonly cause?: unknown;
|
|
14
|
+
/** HTTP status for `fetch_failed` responses, when a response was received. */
|
|
15
|
+
readonly httpStatus?: number;
|
|
16
|
+
constructor(code: BrandJsonResolverErrorCode, message: string, details?: {
|
|
17
|
+
httpStatus?: number;
|
|
18
|
+
cause?: unknown;
|
|
19
|
+
});
|
|
14
20
|
}
|
|
15
21
|
export interface BrandJsonJwksResolverOptions {
|
|
16
22
|
/** Functional role of the agent whose keys we want to resolve. */
|
|
@@ -99,3 +105,40 @@ export declare class BrandJsonJwksResolver implements JwksResolver {
|
|
|
99
105
|
private refresh;
|
|
100
106
|
private doRefresh;
|
|
101
107
|
}
|
|
108
|
+
export interface FetchedBrandJson {
|
|
109
|
+
status: 'ok' | 'not_modified';
|
|
110
|
+
finalUrl: string;
|
|
111
|
+
data: unknown;
|
|
112
|
+
etag?: string;
|
|
113
|
+
cacheControl?: string;
|
|
114
|
+
}
|
|
115
|
+
export interface FetchBrandJsonOptions {
|
|
116
|
+
/** Entry-point URL. HTTPS and public addresses are required by default. */
|
|
117
|
+
startUrl: string;
|
|
118
|
+
/** ETag sent only to the entry URL for cache revalidation. */
|
|
119
|
+
currentEtag?: string;
|
|
120
|
+
/** Maximum JSON-level `authoritative_location` / `house` hops. Default 3, hard maximum 10. */
|
|
121
|
+
maxRedirects?: number;
|
|
122
|
+
/** Permit HTTP and private addresses for controlled development environments. */
|
|
123
|
+
allowPrivateIp?: boolean;
|
|
124
|
+
/** Whole-request deadline per hop. Default and hard maximum 10 seconds. */
|
|
125
|
+
timeoutMs?: number;
|
|
126
|
+
/** Response-body cap per hop. Default and hard maximum 256 KiB. */
|
|
127
|
+
maxBodyBytes?: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Fetch brand.json from `startUrl`, following `authoritative_location` and
|
|
131
|
+
* `house` string redirect variants up to `maxRedirects` hops. Each hop goes
|
|
132
|
+
* through the SSRF-safe fetch primitive so an attacker-supplied chain can't
|
|
133
|
+
* land on a private address or IMDS. Redirect targets are structurally
|
|
134
|
+
* validated before dispatch — an attacker-controlled brand.json that emits
|
|
135
|
+
* `{"house": "evil.com\\@victim.com"}` or `{"authoritative_location":
|
|
136
|
+
* "http://169.254.169.254/..."}` is rejected at parse time rather than
|
|
137
|
+
* relying on `ssrfSafeFetch` to catch every pathological shape.
|
|
138
|
+
*
|
|
139
|
+
* This low-level function is intentionally stateless. Callers MUST add
|
|
140
|
+
* response caching and a minimum refresh cooldown rather than invoking it on
|
|
141
|
+
* every authorization request. Prefer `BrandJsonJwksResolver` when resolving
|
|
142
|
+
* signing keys; it provides both safeguards.
|
|
143
|
+
*/
|
|
144
|
+
export declare function fetchBrandJson(args: FetchBrandJsonOptions): Promise<FetchedBrandJson>;
|
|
@@ -19,17 +19,23 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var brand_jwks_exports = {};
|
|
20
20
|
__export(brand_jwks_exports, {
|
|
21
21
|
BrandJsonJwksResolver: () => BrandJsonJwksResolver,
|
|
22
|
-
BrandJsonResolverError: () => BrandJsonResolverError
|
|
22
|
+
BrandJsonResolverError: () => BrandJsonResolverError,
|
|
23
|
+
fetchBrandJson: () => fetchBrandJson
|
|
23
24
|
});
|
|
24
25
|
module.exports = __toCommonJS(brand_jwks_exports);
|
|
25
26
|
var import_net = require('../net/index.js');
|
|
26
27
|
var import_jwks_https = require('./jwks-https.js');
|
|
27
28
|
class BrandJsonResolverError extends Error {
|
|
28
29
|
code;
|
|
29
|
-
|
|
30
|
+
cause;
|
|
31
|
+
/** HTTP status for `fetch_failed` responses, when a response was received. */
|
|
32
|
+
httpStatus;
|
|
33
|
+
constructor(code, message, details = {}) {
|
|
30
34
|
super(message);
|
|
31
35
|
this.name = "BrandJsonResolverError";
|
|
32
36
|
this.code = code;
|
|
37
|
+
this.httpStatus = details.httpStatus;
|
|
38
|
+
this.cause = details.cause;
|
|
33
39
|
}
|
|
34
40
|
}
|
|
35
41
|
const DEFAULT_MIN_COOLDOWN_SECONDS = 30;
|
|
@@ -143,10 +149,26 @@ class BrandJsonJwksResolver {
|
|
|
143
149
|
};
|
|
144
150
|
}
|
|
145
151
|
}
|
|
152
|
+
const MAX_BRAND_JSON_TIMEOUT_MS = 1e4;
|
|
153
|
+
const MAX_BRAND_JSON_BODY_BYTES = 262144;
|
|
154
|
+
const MAX_BRAND_JSON_REDIRECTS = 10;
|
|
146
155
|
async function fetchBrandJson(args) {
|
|
156
|
+
const maxRedirects = boundedIntegerOption("maxRedirects", args.maxRedirects ?? DEFAULT_MAX_REDIRECTS, {
|
|
157
|
+
min: 0,
|
|
158
|
+
max: MAX_BRAND_JSON_REDIRECTS
|
|
159
|
+
});
|
|
160
|
+
const timeoutMs = boundedIntegerOption("timeoutMs", args.timeoutMs ?? MAX_BRAND_JSON_TIMEOUT_MS, {
|
|
161
|
+
min: 1,
|
|
162
|
+
max: MAX_BRAND_JSON_TIMEOUT_MS
|
|
163
|
+
});
|
|
164
|
+
const maxBodyBytes = boundedIntegerOption("maxBodyBytes", args.maxBodyBytes ?? MAX_BRAND_JSON_BODY_BYTES, {
|
|
165
|
+
min: 1,
|
|
166
|
+
max: MAX_BRAND_JSON_BODY_BYTES
|
|
167
|
+
});
|
|
168
|
+
const allowPrivateIp = args.allowPrivateIp === true;
|
|
147
169
|
const seen = /* @__PURE__ */ new Set();
|
|
148
|
-
let url = canonicalizeUrl(args.startUrl,
|
|
149
|
-
for (let hop = 0; hop <=
|
|
170
|
+
let url = canonicalizeUrl(args.startUrl, allowPrivateIp);
|
|
171
|
+
for (let hop = 0; hop <= maxRedirects; hop++) {
|
|
150
172
|
if (seen.has(url)) {
|
|
151
173
|
throw new BrandJsonResolverError("redirect_loop", `brand.json redirect loop detected`);
|
|
152
174
|
}
|
|
@@ -155,11 +177,18 @@ async function fetchBrandJson(args) {
|
|
|
155
177
|
accept: "application/json"
|
|
156
178
|
};
|
|
157
179
|
if (hop === 0 && args.currentEtag) headers["if-none-match"] = args.currentEtag;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
180
|
+
let res;
|
|
181
|
+
try {
|
|
182
|
+
res = await (0, import_net.ssrfSafeFetch)(url, {
|
|
183
|
+
method: "GET",
|
|
184
|
+
headers,
|
|
185
|
+
allowPrivateIp,
|
|
186
|
+
timeoutMs,
|
|
187
|
+
maxBodyBytes
|
|
188
|
+
});
|
|
189
|
+
} catch (cause) {
|
|
190
|
+
throw new BrandJsonResolverError("fetch_failed", "Unable to fetch brand.json", { cause });
|
|
191
|
+
}
|
|
163
192
|
if (hop === 0 && res.status === 304) {
|
|
164
193
|
return {
|
|
165
194
|
status: "not_modified",
|
|
@@ -170,7 +199,9 @@ async function fetchBrandJson(args) {
|
|
|
170
199
|
};
|
|
171
200
|
}
|
|
172
201
|
if (res.status !== 200) {
|
|
173
|
-
throw new BrandJsonResolverError("fetch_failed", `brand.json fetch returned HTTP ${res.status}
|
|
202
|
+
throw new BrandJsonResolverError("fetch_failed", `brand.json fetch returned HTTP ${res.status}`, {
|
|
203
|
+
httpStatus: res.status
|
|
204
|
+
});
|
|
174
205
|
}
|
|
175
206
|
const text = Buffer.from(res.body).toString("utf8");
|
|
176
207
|
let parsed;
|
|
@@ -186,20 +217,20 @@ async function fetchBrandJson(args) {
|
|
|
186
217
|
const authoritative = typeof obj.authoritative_location === "string" ? obj.authoritative_location : void 0;
|
|
187
218
|
const house = typeof obj.house === "string" ? obj.house : void 0;
|
|
188
219
|
if (authoritative !== void 0) {
|
|
189
|
-
if (hop ===
|
|
220
|
+
if (hop === maxRedirects) {
|
|
190
221
|
throw new BrandJsonResolverError("redirect_depth_exceeded", `brand.json redirect depth exceeded`);
|
|
191
222
|
}
|
|
192
|
-
url = canonicalizeUrl(authoritative,
|
|
223
|
+
url = canonicalizeUrl(authoritative, allowPrivateIp);
|
|
193
224
|
continue;
|
|
194
225
|
}
|
|
195
226
|
if (house !== void 0) {
|
|
196
227
|
if (!BARE_HOSTNAME.test(house)) {
|
|
197
228
|
throw new BrandJsonResolverError("invalid_house", `brand.json "house" is not a bare hostname`);
|
|
198
229
|
}
|
|
199
|
-
if (hop ===
|
|
230
|
+
if (hop === maxRedirects) {
|
|
200
231
|
throw new BrandJsonResolverError("redirect_depth_exceeded", `brand.json redirect depth exceeded`);
|
|
201
232
|
}
|
|
202
|
-
url = canonicalizeUrl(`https://${house}/.well-known/brand.json`,
|
|
233
|
+
url = canonicalizeUrl(`https://${house}/.well-known/brand.json`, allowPrivateIp);
|
|
203
234
|
continue;
|
|
204
235
|
}
|
|
205
236
|
assertBrandJsonShape(obj);
|
|
@@ -213,6 +244,12 @@ async function fetchBrandJson(args) {
|
|
|
213
244
|
}
|
|
214
245
|
throw new BrandJsonResolverError("redirect_depth_exceeded", `brand.json redirect depth exceeded`);
|
|
215
246
|
}
|
|
247
|
+
function boundedIntegerOption(name, value, bounds) {
|
|
248
|
+
if (!Number.isInteger(value) || value < bounds.min || value > bounds.max) {
|
|
249
|
+
throw new TypeError(`fetchBrandJson: ${name} must be an integer between ${bounds.min} and ${bounds.max}`);
|
|
250
|
+
}
|
|
251
|
+
return value;
|
|
252
|
+
}
|
|
216
253
|
function canonicalizeUrl(raw, allowPrivateIp) {
|
|
217
254
|
let parsed;
|
|
218
255
|
try {
|
|
@@ -346,5 +383,6 @@ function computeLifetime(cacheControl, maxAge) {
|
|
|
346
383
|
// Annotate the CommonJS export names for ESM import in node:
|
|
347
384
|
0 && (module.exports = {
|
|
348
385
|
BrandJsonJwksResolver,
|
|
349
|
-
BrandJsonResolverError
|
|
386
|
+
BrandJsonResolverError,
|
|
387
|
+
fetchBrandJson
|
|
350
388
|
});
|
|
@@ -2,10 +2,15 @@ import { ssrfSafeFetch } from "../net/index.mjs";
|
|
|
2
2
|
import { HttpsJwksResolver } from "./jwks-https.mjs";
|
|
3
3
|
class BrandJsonResolverError extends Error {
|
|
4
4
|
code;
|
|
5
|
-
|
|
5
|
+
cause;
|
|
6
|
+
/** HTTP status for `fetch_failed` responses, when a response was received. */
|
|
7
|
+
httpStatus;
|
|
8
|
+
constructor(code, message, details = {}) {
|
|
6
9
|
super(message);
|
|
7
10
|
this.name = "BrandJsonResolverError";
|
|
8
11
|
this.code = code;
|
|
12
|
+
this.httpStatus = details.httpStatus;
|
|
13
|
+
this.cause = details.cause;
|
|
9
14
|
}
|
|
10
15
|
}
|
|
11
16
|
const DEFAULT_MIN_COOLDOWN_SECONDS = 30;
|
|
@@ -119,10 +124,26 @@ class BrandJsonJwksResolver {
|
|
|
119
124
|
};
|
|
120
125
|
}
|
|
121
126
|
}
|
|
127
|
+
const MAX_BRAND_JSON_TIMEOUT_MS = 1e4;
|
|
128
|
+
const MAX_BRAND_JSON_BODY_BYTES = 262144;
|
|
129
|
+
const MAX_BRAND_JSON_REDIRECTS = 10;
|
|
122
130
|
async function fetchBrandJson(args) {
|
|
131
|
+
const maxRedirects = boundedIntegerOption("maxRedirects", args.maxRedirects ?? DEFAULT_MAX_REDIRECTS, {
|
|
132
|
+
min: 0,
|
|
133
|
+
max: MAX_BRAND_JSON_REDIRECTS
|
|
134
|
+
});
|
|
135
|
+
const timeoutMs = boundedIntegerOption("timeoutMs", args.timeoutMs ?? MAX_BRAND_JSON_TIMEOUT_MS, {
|
|
136
|
+
min: 1,
|
|
137
|
+
max: MAX_BRAND_JSON_TIMEOUT_MS
|
|
138
|
+
});
|
|
139
|
+
const maxBodyBytes = boundedIntegerOption("maxBodyBytes", args.maxBodyBytes ?? MAX_BRAND_JSON_BODY_BYTES, {
|
|
140
|
+
min: 1,
|
|
141
|
+
max: MAX_BRAND_JSON_BODY_BYTES
|
|
142
|
+
});
|
|
143
|
+
const allowPrivateIp = args.allowPrivateIp === true;
|
|
123
144
|
const seen = /* @__PURE__ */ new Set();
|
|
124
|
-
let url = canonicalizeUrl(args.startUrl,
|
|
125
|
-
for (let hop = 0; hop <=
|
|
145
|
+
let url = canonicalizeUrl(args.startUrl, allowPrivateIp);
|
|
146
|
+
for (let hop = 0; hop <= maxRedirects; hop++) {
|
|
126
147
|
if (seen.has(url)) {
|
|
127
148
|
throw new BrandJsonResolverError("redirect_loop", `brand.json redirect loop detected`);
|
|
128
149
|
}
|
|
@@ -131,11 +152,18 @@ async function fetchBrandJson(args) {
|
|
|
131
152
|
accept: "application/json"
|
|
132
153
|
};
|
|
133
154
|
if (hop === 0 && args.currentEtag) headers["if-none-match"] = args.currentEtag;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
155
|
+
let res;
|
|
156
|
+
try {
|
|
157
|
+
res = await ssrfSafeFetch(url, {
|
|
158
|
+
method: "GET",
|
|
159
|
+
headers,
|
|
160
|
+
allowPrivateIp,
|
|
161
|
+
timeoutMs,
|
|
162
|
+
maxBodyBytes
|
|
163
|
+
});
|
|
164
|
+
} catch (cause) {
|
|
165
|
+
throw new BrandJsonResolverError("fetch_failed", "Unable to fetch brand.json", { cause });
|
|
166
|
+
}
|
|
139
167
|
if (hop === 0 && res.status === 304) {
|
|
140
168
|
return {
|
|
141
169
|
status: "not_modified",
|
|
@@ -146,7 +174,9 @@ async function fetchBrandJson(args) {
|
|
|
146
174
|
};
|
|
147
175
|
}
|
|
148
176
|
if (res.status !== 200) {
|
|
149
|
-
throw new BrandJsonResolverError("fetch_failed", `brand.json fetch returned HTTP ${res.status}
|
|
177
|
+
throw new BrandJsonResolverError("fetch_failed", `brand.json fetch returned HTTP ${res.status}`, {
|
|
178
|
+
httpStatus: res.status
|
|
179
|
+
});
|
|
150
180
|
}
|
|
151
181
|
const text = Buffer.from(res.body).toString("utf8");
|
|
152
182
|
let parsed;
|
|
@@ -162,20 +192,20 @@ async function fetchBrandJson(args) {
|
|
|
162
192
|
const authoritative = typeof obj.authoritative_location === "string" ? obj.authoritative_location : void 0;
|
|
163
193
|
const house = typeof obj.house === "string" ? obj.house : void 0;
|
|
164
194
|
if (authoritative !== void 0) {
|
|
165
|
-
if (hop ===
|
|
195
|
+
if (hop === maxRedirects) {
|
|
166
196
|
throw new BrandJsonResolverError("redirect_depth_exceeded", `brand.json redirect depth exceeded`);
|
|
167
197
|
}
|
|
168
|
-
url = canonicalizeUrl(authoritative,
|
|
198
|
+
url = canonicalizeUrl(authoritative, allowPrivateIp);
|
|
169
199
|
continue;
|
|
170
200
|
}
|
|
171
201
|
if (house !== void 0) {
|
|
172
202
|
if (!BARE_HOSTNAME.test(house)) {
|
|
173
203
|
throw new BrandJsonResolverError("invalid_house", `brand.json "house" is not a bare hostname`);
|
|
174
204
|
}
|
|
175
|
-
if (hop ===
|
|
205
|
+
if (hop === maxRedirects) {
|
|
176
206
|
throw new BrandJsonResolverError("redirect_depth_exceeded", `brand.json redirect depth exceeded`);
|
|
177
207
|
}
|
|
178
|
-
url = canonicalizeUrl(`https://${house}/.well-known/brand.json`,
|
|
208
|
+
url = canonicalizeUrl(`https://${house}/.well-known/brand.json`, allowPrivateIp);
|
|
179
209
|
continue;
|
|
180
210
|
}
|
|
181
211
|
assertBrandJsonShape(obj);
|
|
@@ -189,6 +219,12 @@ async function fetchBrandJson(args) {
|
|
|
189
219
|
}
|
|
190
220
|
throw new BrandJsonResolverError("redirect_depth_exceeded", `brand.json redirect depth exceeded`);
|
|
191
221
|
}
|
|
222
|
+
function boundedIntegerOption(name, value, bounds) {
|
|
223
|
+
if (!Number.isInteger(value) || value < bounds.min || value > bounds.max) {
|
|
224
|
+
throw new TypeError(`fetchBrandJson: ${name} must be an integer between ${bounds.min} and ${bounds.max}`);
|
|
225
|
+
}
|
|
226
|
+
return value;
|
|
227
|
+
}
|
|
192
228
|
function canonicalizeUrl(raw, allowPrivateIp) {
|
|
193
229
|
let parsed;
|
|
194
230
|
try {
|
|
@@ -321,5 +357,6 @@ function computeLifetime(cacheControl, maxAge) {
|
|
|
321
357
|
}
|
|
322
358
|
export {
|
|
323
359
|
BrandJsonJwksResolver,
|
|
324
|
-
BrandJsonResolverError
|
|
360
|
+
BrandJsonResolverError,
|
|
361
|
+
fetchBrandJson
|
|
325
362
|
};
|
|
@@ -15,7 +15,7 @@ export { jwkToPublicKey, verifySignature } from './crypto.mjs';
|
|
|
15
15
|
export { RequestSignatureError, type RequestSignatureErrorCode, ResponseSignatureError, type ResponseSignatureErrorCode, WebhookSignatureError, type WebhookSignatureErrorCode, } from './errors.mjs';
|
|
16
16
|
export { StaticJwksResolver, type JwksResolution, type JwksResolver } from './jwks.mjs';
|
|
17
17
|
export { HttpsJwksResolver, type HttpsJwksResolverOptions } from './jwks-https.mjs';
|
|
18
|
-
export { BrandJsonJwksResolver, BrandJsonResolverError, type BrandAgentType, type BrandJsonJwksResolverOptions, type BrandJsonResolverErrorCode, } from './brand-jwks.mjs';
|
|
18
|
+
export { BrandJsonJwksResolver, BrandJsonResolverError, fetchBrandJson, type BrandAgentType, type FetchedBrandJson, type FetchBrandJsonOptions, type BrandJsonJwksResolverOptions, type BrandJsonResolverErrorCode, } from './brand-jwks.mjs';
|
|
19
19
|
export { parseSignature, parseSignatureInput, type ParsedSignature, type ParsedSignatureInput } from './parser.mjs';
|
|
20
20
|
export { InMemoryReplayStore, type InMemoryReplayStoreOptions, type ReplayInsertResult, type ReplayStore, } from './replay.mjs';
|
|
21
21
|
export { PostgresReplayStore, REPLAY_CACHE_MIGRATION, getReplayStoreMigration, sweepExpiredReplays, type PostgresReplayStoreOptions, type SweepExpiredReplaysOptions, } from './postgres-replay-store.mjs';
|
|
@@ -15,7 +15,7 @@ export { jwkToPublicKey, verifySignature } from './crypto';
|
|
|
15
15
|
export { RequestSignatureError, type RequestSignatureErrorCode, ResponseSignatureError, type ResponseSignatureErrorCode, WebhookSignatureError, type WebhookSignatureErrorCode, } from './errors';
|
|
16
16
|
export { StaticJwksResolver, type JwksResolution, type JwksResolver } from './jwks';
|
|
17
17
|
export { HttpsJwksResolver, type HttpsJwksResolverOptions } from './jwks-https';
|
|
18
|
-
export { BrandJsonJwksResolver, BrandJsonResolverError, type BrandAgentType, type BrandJsonJwksResolverOptions, type BrandJsonResolverErrorCode, } from './brand-jwks';
|
|
18
|
+
export { BrandJsonJwksResolver, BrandJsonResolverError, fetchBrandJson, type BrandAgentType, type FetchedBrandJson, type FetchBrandJsonOptions, type BrandJsonJwksResolverOptions, type BrandJsonResolverErrorCode, } from './brand-jwks';
|
|
19
19
|
export { parseSignature, parseSignatureInput, type ParsedSignature, type ParsedSignatureInput } from './parser';
|
|
20
20
|
export { InMemoryReplayStore, type InMemoryReplayStoreOptions, type ReplayInsertResult, type ReplayStore, } from './replay';
|
|
21
21
|
export { PostgresReplayStore, REPLAY_CACHE_MIGRATION, getReplayStoreMigration, sweepExpiredReplays, type PostgresReplayStoreOptions, type SweepExpiredReplaysOptions, } from './postgres-replay-store';
|
|
@@ -54,6 +54,7 @@ __export(server_exports, {
|
|
|
54
54
|
createAgentJwksSet: () => import_agent_resolver.createAgentJwksSet,
|
|
55
55
|
createExpressVerifier: () => import_middleware.createExpressVerifier,
|
|
56
56
|
createWebhookVerifier: () => import_webhook_verifier.createWebhookVerifier,
|
|
57
|
+
fetchBrandJson: () => import_brand_jwks.fetchBrandJson,
|
|
57
58
|
finalizeResponseSignature: () => import_signer.finalizeResponseSignature,
|
|
58
59
|
formatSignatureParams: () => import_canonicalize.formatSignatureParams,
|
|
59
60
|
getAgentJwks: () => import_agent_resolver.getAgentJwks,
|
|
@@ -138,6 +139,7 @@ var import_agent_resolver = require('./agent-resolver/index.js');
|
|
|
138
139
|
createAgentJwksSet,
|
|
139
140
|
createExpressVerifier,
|
|
140
141
|
createWebhookVerifier,
|
|
142
|
+
fetchBrandJson,
|
|
141
143
|
finalizeResponseSignature,
|
|
142
144
|
formatSignatureParams,
|
|
143
145
|
getAgentJwks,
|
|
@@ -23,7 +23,8 @@ import { StaticJwksResolver } from "./jwks.mjs";
|
|
|
23
23
|
import { HttpsJwksResolver } from "./jwks-https.mjs";
|
|
24
24
|
import {
|
|
25
25
|
BrandJsonJwksResolver,
|
|
26
|
-
BrandJsonResolverError
|
|
26
|
+
BrandJsonResolverError,
|
|
27
|
+
fetchBrandJson
|
|
27
28
|
} from "./brand-jwks.mjs";
|
|
28
29
|
import { parseSignature, parseSignatureInput } from "./parser.mjs";
|
|
29
30
|
import {
|
|
@@ -112,6 +113,7 @@ export {
|
|
|
112
113
|
createAgentJwksSet,
|
|
113
114
|
createExpressVerifier,
|
|
114
115
|
createWebhookVerifier,
|
|
116
|
+
fetchBrandJson,
|
|
115
117
|
finalizeResponseSignature,
|
|
116
118
|
formatSignatureParams,
|
|
117
119
|
getAgentJwks,
|
|
@@ -1529,7 +1529,9 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1529
1529
|
...options.webhook_receiver.mode && { mode: options.webhook_receiver.mode },
|
|
1530
1530
|
...options.webhook_receiver.host !== void 0 && { host: options.webhook_receiver.host },
|
|
1531
1531
|
...options.webhook_receiver.port !== void 0 && { port: options.webhook_receiver.port },
|
|
1532
|
-
...options.webhook_receiver.public_url !== void 0 && { public_url: options.webhook_receiver.public_url }
|
|
1532
|
+
...options.webhook_receiver.public_url !== void 0 && { public_url: options.webhook_receiver.public_url },
|
|
1533
|
+
allowHttp: options.allow_http === true,
|
|
1534
|
+
...options.webhook_receiver.tls !== void 0 && { tls: options.webhook_receiver.tls }
|
|
1533
1535
|
}) : void 0;
|
|
1534
1536
|
const runnerVars = (0, import_context.createRunnerVariables)({
|
|
1535
1537
|
...webhookReceiver && { webhookBase: webhookReceiver.base_url }
|
|
@@ -2602,7 +2604,9 @@ async function runStoryboardStepBody(agentUrl, storyboard, stepId, options) {
|
|
|
2602
2604
|
...options.webhook_receiver.port !== void 0 && { port: options.webhook_receiver.port },
|
|
2603
2605
|
...options.webhook_receiver.public_url !== void 0 && {
|
|
2604
2606
|
public_url: options.webhook_receiver.public_url
|
|
2605
|
-
}
|
|
2607
|
+
},
|
|
2608
|
+
allowHttp: options.allow_http === true,
|
|
2609
|
+
...options.webhook_receiver.tls !== void 0 && { tls: options.webhook_receiver.tls }
|
|
2606
2610
|
}) : void 0);
|
|
2607
2611
|
const ownsWebhookReceiver = !injectedReceiver && !!webhookReceiver;
|
|
2608
2612
|
const runnerVars = (0, import_context.createRunnerVariables)({
|
|
@@ -1546,7 +1546,9 @@ async function executeStoryboardPass(agentUrls, storyboard, options, dispatchOff
|
|
|
1546
1546
|
...options.webhook_receiver.mode && { mode: options.webhook_receiver.mode },
|
|
1547
1547
|
...options.webhook_receiver.host !== void 0 && { host: options.webhook_receiver.host },
|
|
1548
1548
|
...options.webhook_receiver.port !== void 0 && { port: options.webhook_receiver.port },
|
|
1549
|
-
...options.webhook_receiver.public_url !== void 0 && { public_url: options.webhook_receiver.public_url }
|
|
1549
|
+
...options.webhook_receiver.public_url !== void 0 && { public_url: options.webhook_receiver.public_url },
|
|
1550
|
+
allowHttp: options.allow_http === true,
|
|
1551
|
+
...options.webhook_receiver.tls !== void 0 && { tls: options.webhook_receiver.tls }
|
|
1550
1552
|
}) : void 0;
|
|
1551
1553
|
const runnerVars = createRunnerVariables({
|
|
1552
1554
|
...webhookReceiver && { webhookBase: webhookReceiver.base_url }
|
|
@@ -2619,7 +2621,9 @@ async function runStoryboardStepBody(agentUrl, storyboard, stepId, options) {
|
|
|
2619
2621
|
...options.webhook_receiver.port !== void 0 && { port: options.webhook_receiver.port },
|
|
2620
2622
|
...options.webhook_receiver.public_url !== void 0 && {
|
|
2621
2623
|
public_url: options.webhook_receiver.public_url
|
|
2622
|
-
}
|
|
2624
|
+
},
|
|
2625
|
+
allowHttp: options.allow_http === true,
|
|
2626
|
+
...options.webhook_receiver.tls !== void 0 && { tls: options.webhook_receiver.tls }
|
|
2623
2627
|
}) : void 0);
|
|
2624
2628
|
const ownsWebhookReceiver = !injectedReceiver && !!webhookReceiver;
|
|
2625
2629
|
const runnerVars = createRunnerVariables({
|
|
@@ -1718,6 +1718,12 @@ export interface StoryboardRunOptions extends TestOptions {
|
|
|
1718
1718
|
* validate reachability.
|
|
1719
1719
|
*/
|
|
1720
1720
|
public_url?: string;
|
|
1721
|
+
/** TLS material for direct HTTPS serving; omit behind a TLS-terminating proxy. */
|
|
1722
|
+
tls?: {
|
|
1723
|
+
cert: string | Buffer;
|
|
1724
|
+
key: string | Buffer;
|
|
1725
|
+
passphrase?: string;
|
|
1726
|
+
};
|
|
1721
1727
|
};
|
|
1722
1728
|
/**
|
|
1723
1729
|
* Target receiver for `replay_webhook_vector` storyboards. This is separate
|
|
@@ -1718,6 +1718,12 @@ export interface StoryboardRunOptions extends TestOptions {
|
|
|
1718
1718
|
* validate reachability.
|
|
1719
1719
|
*/
|
|
1720
1720
|
public_url?: string;
|
|
1721
|
+
/** TLS material for direct HTTPS serving; omit behind a TLS-terminating proxy. */
|
|
1722
|
+
tls?: {
|
|
1723
|
+
cert: string | Buffer;
|
|
1724
|
+
key: string | Buffer;
|
|
1725
|
+
passphrase?: string;
|
|
1726
|
+
};
|
|
1721
1727
|
};
|
|
1722
1728
|
/**
|
|
1723
1729
|
* Target receiver for `replay_webhook_vector` storyboards. This is separate
|
|
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
|
|
|
197
197
|
*/
|
|
198
198
|
export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
|
|
199
199
|
/** Capability semver emitted in run summaries and used for advisory expiry. */
|
|
200
|
-
export declare const RUNNER_CAPABILITY_VERSION = "14.0.0-beta.
|
|
200
|
+
export declare const RUNNER_CAPABILITY_VERSION = "14.0.0-beta.20";
|
|
201
201
|
/** True when a failed validation contributes to the owning step's grade. */
|
|
202
202
|
export declare function validationFailsStep(result: ValidationResult): boolean;
|
|
203
203
|
/**
|
|
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
|
|
|
197
197
|
*/
|
|
198
198
|
export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
|
|
199
199
|
/** Capability semver emitted in run summaries and used for advisory expiry. */
|
|
200
|
-
export declare const RUNNER_CAPABILITY_VERSION = "14.0.0-beta.
|
|
200
|
+
export declare const RUNNER_CAPABILITY_VERSION = "14.0.0-beta.20";
|
|
201
201
|
/** True when a failed validation contributes to the owning step's grade. */
|
|
202
202
|
export declare function validationFailsStep(result: ValidationResult): boolean;
|
|
203
203
|
/**
|
|
@@ -64,7 +64,12 @@ const DEFAULT_RETRY_COUNT = 3;
|
|
|
64
64
|
const DEFAULT_RETRY_HTTP_STATUS = 503;
|
|
65
65
|
const DEFAULT_MIN_DELIVERIES = 2;
|
|
66
66
|
function webhookTimeoutDetail(receiver, base) {
|
|
67
|
-
|
|
67
|
+
if (receiver.all().length > 0 || receiver.challenges().length > 0) return base;
|
|
68
|
+
const bindHost = receiver.bind_host ?? "the configured local address";
|
|
69
|
+
if (receiver.mode === "loopback_mock") {
|
|
70
|
+
return `${base} No request reached the receiver bound to ${bindHost}; an out-of-process agent must use webhook_receiver.mode="proxy_url".`;
|
|
71
|
+
}
|
|
72
|
+
return `${base} No request reached the receiver bound to ${bindHost}; verify that the advertised callback URL routes to this container and bind address.`;
|
|
68
73
|
}
|
|
69
74
|
const DEFAULT_WEBHOOK_SIGNING_TAG = "adcp/webhook-signing/v1";
|
|
70
75
|
const MAX_RETRY_REPLAY_COUNT = 10;
|
|
@@ -39,7 +39,12 @@ const DEFAULT_RETRY_COUNT = 3;
|
|
|
39
39
|
const DEFAULT_RETRY_HTTP_STATUS = 503;
|
|
40
40
|
const DEFAULT_MIN_DELIVERIES = 2;
|
|
41
41
|
function webhookTimeoutDetail(receiver, base) {
|
|
42
|
-
|
|
42
|
+
if (receiver.all().length > 0 || receiver.challenges().length > 0) return base;
|
|
43
|
+
const bindHost = receiver.bind_host ?? "the configured local address";
|
|
44
|
+
if (receiver.mode === "loopback_mock") {
|
|
45
|
+
return `${base} No request reached the receiver bound to ${bindHost}; an out-of-process agent must use webhook_receiver.mode="proxy_url".`;
|
|
46
|
+
}
|
|
47
|
+
return `${base} No request reached the receiver bound to ${bindHost}; verify that the advertised callback URL routes to this container and bind address.`;
|
|
43
48
|
}
|
|
44
49
|
const DEFAULT_WEBHOOK_SIGNING_TAG = "adcp/webhook-signing/v1";
|
|
45
50
|
const MAX_RETRY_REPLAY_COUNT = 10;
|
|
@@ -80,6 +80,8 @@ export interface WebhookReceiver {
|
|
|
80
80
|
readonly base_url: string;
|
|
81
81
|
/** Whether the receiver is in loopback-mock or proxy-url mode. */
|
|
82
82
|
readonly mode: 'loopback_mock' | 'proxy_url';
|
|
83
|
+
/** Local address the listener is bound to. */
|
|
84
|
+
readonly bind_host?: string;
|
|
83
85
|
/** All webhooks captured so far, in arrival order. */
|
|
84
86
|
all(): CapturedWebhook[];
|
|
85
87
|
/** Schema-valid proof-of-control challenges, kept separate from event deliveries. */
|
|
@@ -114,5 +116,18 @@ export interface CreateWebhookReceiverOptions {
|
|
|
114
116
|
port?: number;
|
|
115
117
|
/** Public URL to advertise when `mode: 'proxy_url'`. */
|
|
116
118
|
public_url?: string;
|
|
119
|
+
/**
|
|
120
|
+
* Permit an HTTP public URL for controlled local-development topologies.
|
|
121
|
+
*/
|
|
122
|
+
allowHttp?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* TLS material for serving HTTPS directly. Omit when HTTPS terminates at a
|
|
125
|
+
* tunnel or reverse proxy in front of this listener.
|
|
126
|
+
*/
|
|
127
|
+
tls?: {
|
|
128
|
+
cert: string | Buffer;
|
|
129
|
+
key: string | Buffer;
|
|
130
|
+
passphrase?: string;
|
|
131
|
+
};
|
|
117
132
|
}
|
|
118
133
|
export declare function createWebhookReceiver(options?: CreateWebhookReceiverOptions): Promise<WebhookReceiver>;
|
|
@@ -80,6 +80,8 @@ export interface WebhookReceiver {
|
|
|
80
80
|
readonly base_url: string;
|
|
81
81
|
/** Whether the receiver is in loopback-mock or proxy-url mode. */
|
|
82
82
|
readonly mode: 'loopback_mock' | 'proxy_url';
|
|
83
|
+
/** Local address the listener is bound to. */
|
|
84
|
+
readonly bind_host?: string;
|
|
83
85
|
/** All webhooks captured so far, in arrival order. */
|
|
84
86
|
all(): CapturedWebhook[];
|
|
85
87
|
/** Schema-valid proof-of-control challenges, kept separate from event deliveries. */
|
|
@@ -114,5 +116,18 @@ export interface CreateWebhookReceiverOptions {
|
|
|
114
116
|
port?: number;
|
|
115
117
|
/** Public URL to advertise when `mode: 'proxy_url'`. */
|
|
116
118
|
public_url?: string;
|
|
119
|
+
/**
|
|
120
|
+
* Permit an HTTP public URL for controlled local-development topologies.
|
|
121
|
+
*/
|
|
122
|
+
allowHttp?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* TLS material for serving HTTPS directly. Omit when HTTPS terminates at a
|
|
125
|
+
* tunnel or reverse proxy in front of this listener.
|
|
126
|
+
*/
|
|
127
|
+
tls?: {
|
|
128
|
+
cert: string | Buffer;
|
|
129
|
+
key: string | Buffer;
|
|
130
|
+
passphrase?: string;
|
|
131
|
+
};
|
|
117
132
|
}
|
|
118
133
|
export declare function createWebhookReceiver(options?: CreateWebhookReceiverOptions): Promise<WebhookReceiver>;
|