@adcp/sdk 14.0.0 → 14.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/adapters/implicit-account-store.d.mts +12 -7
- package/dist/lib/adapters/implicit-account-store.d.ts +12 -7
- package/dist/lib/adapters/implicit-account-store.js +69 -15
- package/dist/lib/adapters/implicit-account-store.mjs +69 -15
- package/dist/lib/core/AgentClient.d.mts +1 -0
- package/dist/lib/core/AgentClient.d.ts +1 -0
- package/dist/lib/core/AgentClient.js +3 -0
- package/dist/lib/core/AgentClient.mjs +3 -0
- package/dist/lib/core/SingleAgentClient.d.mts +31 -1
- package/dist/lib/core/SingleAgentClient.d.ts +31 -1
- package/dist/lib/core/SingleAgentClient.js +355 -35
- package/dist/lib/core/SingleAgentClient.mjs +365 -37
- package/dist/lib/core/TaskExecutor.js +2 -1
- package/dist/lib/core/TaskExecutor.mjs +2 -1
- package/dist/lib/core/account-key.d.mts +3 -0
- package/dist/lib/core/account-key.d.ts +3 -0
- package/dist/lib/core/account-key.js +41 -0
- package/dist/lib/core/account-key.mjs +17 -0
- package/dist/lib/core/account-resolution.d.mts +2 -0
- package/dist/lib/core/account-resolution.d.ts +2 -0
- package/dist/lib/core/buyer-account-registry.d.mts +65 -0
- package/dist/lib/core/buyer-account-registry.d.ts +65 -0
- package/dist/lib/core/buyer-account-registry.js +518 -0
- package/dist/lib/core/buyer-account-registry.mjs +494 -0
- package/dist/lib/core/product-cache.d.mts +18 -0
- package/dist/lib/core/product-cache.d.ts +18 -0
- package/dist/lib/core/product-cache.js +137 -0
- package/dist/lib/core/product-cache.mjs +112 -0
- package/dist/lib/errors/index.d.mts +40 -1
- package/dist/lib/errors/index.d.ts +40 -1
- package/dist/lib/errors/index.js +69 -3
- package/dist/lib/errors/index.mjs +64 -3
- package/dist/lib/governance/authorization.d.mts +17 -1
- package/dist/lib/governance/authorization.d.ts +17 -1
- package/dist/lib/governance/authorization.js +55 -7
- package/dist/lib/governance/authorization.mjs +59 -7
- package/dist/lib/governance/index.d.mts +2 -2
- package/dist/lib/governance/index.d.ts +2 -2
- package/dist/lib/governance/index.js +2 -0
- package/dist/lib/governance/index.mjs +3 -1
- package/dist/lib/index.d.mts +5 -3
- package/dist/lib/index.d.ts +5 -3
- package/dist/lib/index.js +20 -0
- package/dist/lib/index.mjs +19 -0
- package/dist/lib/protocols/a2a.js +9 -1
- package/dist/lib/protocols/a2a.mjs +9 -1
- package/dist/lib/protocols/index.js +9 -2
- package/dist/lib/protocols/index.mjs +9 -2
- package/dist/lib/protocols/mcp-modern.js +2 -1
- package/dist/lib/protocols/mcp-modern.mjs +2 -1
- package/dist/lib/protocols/mcp.js +5 -2
- package/dist/lib/protocols/mcp.mjs +5 -2
- package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
- package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
- package/dist/lib/protocols/rawResponseCapture.js +41 -29
- package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
- package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
- package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
- package/dist/lib/protocols/signedRequestRejection.js +209 -0
- package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
- package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
- package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
- package/dist/lib/protocols/transportDiagnostics.js +2 -0
- package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
- package/dist/lib/registry/types.generated.d.mts +112 -45
- package/dist/lib/registry/types.generated.d.ts +112 -45
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/account-provisioning.d.mts +2 -0
- package/dist/lib/server/account-provisioning.d.ts +2 -0
- package/dist/lib/server/account-provisioning.js +30 -0
- package/dist/lib/server/account-provisioning.mjs +6 -0
- package/dist/lib/server/account-reference-warnings.d.mts +12 -0
- package/dist/lib/server/account-reference-warnings.d.ts +12 -0
- package/dist/lib/server/account-reference-warnings.js +48 -0
- package/dist/lib/server/account-reference-warnings.mjs +23 -0
- package/dist/lib/server/auth-signature.js +1 -0
- package/dist/lib/server/auth-signature.mjs +1 -0
- package/dist/lib/server/create-adcp-server.d.mts +34 -0
- package/dist/lib/server/create-adcp-server.d.ts +34 -0
- package/dist/lib/server/create-adcp-server.js +225 -14
- package/dist/lib/server/create-adcp-server.mjs +225 -14
- package/dist/lib/server/decisioning/account.d.mts +2 -0
- package/dist/lib/server/decisioning/account.d.ts +2 -0
- package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
- package/dist/lib/server/index.d.mts +2 -2
- package/dist/lib/server/index.d.ts +2 -2
- package/dist/lib/server/index.js +2 -0
- package/dist/lib/server/index.mjs +3 -1
- package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
- package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
- package/dist/lib/signing/agent-resolver/consistency.js +0 -1
- package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
- package/dist/lib/signing/agent-resolver/errors.d.mts +1 -1
- package/dist/lib/signing/agent-resolver/errors.d.ts +1 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +2 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +2 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.js +2 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +2 -1
- package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
- package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
- package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
- package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
- package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
- package/dist/lib/signing/agent-resolver/resolve-agent.js +101 -132
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs +102 -133
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
- package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
- package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
- package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
- package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
- package/dist/lib/signing/brand-jwks.d.mts +27 -75
- package/dist/lib/signing/brand-jwks.d.ts +27 -75
- package/dist/lib/signing/brand-jwks.js +112 -182
- package/dist/lib/signing/brand-jwks.mjs +112 -182
- package/dist/lib/signing/errors.d.mts +3 -1
- package/dist/lib/signing/errors.d.ts +3 -1
- package/dist/lib/signing/errors.js +4 -1
- package/dist/lib/signing/errors.mjs +4 -1
- package/dist/lib/signing/jwks-https.d.mts +7 -0
- package/dist/lib/signing/jwks-https.d.ts +7 -0
- package/dist/lib/signing/jwks-https.js +31 -8
- package/dist/lib/signing/jwks-https.mjs +31 -8
- package/dist/lib/signing/jwks.d.mts +8 -0
- package/dist/lib/signing/jwks.d.ts +8 -0
- package/dist/lib/signing/middleware.js +2 -1
- package/dist/lib/signing/middleware.mjs +2 -1
- package/dist/lib/signing/publisher-pins.d.mts +11 -0
- package/dist/lib/signing/publisher-pins.d.ts +11 -0
- package/dist/lib/signing/publisher-pins.js +125 -0
- package/dist/lib/signing/publisher-pins.mjs +101 -0
- package/dist/lib/signing/server.d.mts +1 -0
- package/dist/lib/signing/server.d.ts +1 -0
- package/dist/lib/signing/types.d.mts +5 -0
- package/dist/lib/signing/types.d.ts +5 -0
- package/dist/lib/signing/verifier.js +49 -4
- package/dist/lib/signing/verifier.mjs +49 -4
- package/dist/lib/signing/webhook-verifier.d.mts +7 -2
- package/dist/lib/signing/webhook-verifier.d.ts +7 -2
- package/dist/lib/signing/webhook-verifier.js +42 -2
- package/dist/lib/signing/webhook-verifier.mjs +43 -3
- package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
- package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
- package/dist/lib/testing/storyboard/account-policy.js +35 -0
- package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
- package/dist/lib/testing/storyboard/context.js +6 -0
- package/dist/lib/testing/storyboard/context.mjs +6 -0
- package/dist/lib/testing/storyboard/request-builder.js +12 -2
- package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
- package/dist/lib/testing/storyboard/runner.js +3 -2
- package/dist/lib/testing/storyboard/runner.mjs +3 -2
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- 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/dist/lib/wholesale-feed-sync/sync.d.mts +1 -0
- package/dist/lib/wholesale-feed-sync/sync.d.ts +1 -0
- package/dist/lib/wholesale-feed-sync/sync.js +92 -21
- package/dist/lib/wholesale-feed-sync/sync.mjs +92 -21
- package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
- package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/BUILD-AN-AGENT.md +2 -2
- package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
- package/docs/guides/FIRST-CALL-TO-A-SELLER.md +104 -0
- package/docs/guides/SIGNING-GUIDE.md +16 -7
- package/docs/guides/account-resolution.md +86 -0
- package/docs/llms.txt +3 -2
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-4.x-to-5.x.md +1 -0
- package/docs/migration-agent-resolution-3.3.md +123 -0
- package/docs/recipes/verifying-inbound-webhooks.md +56 -15
- package/package.json +2 -2
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const AUTHORIZED_OPERATOR_SCOPES = /* @__PURE__ */ new Set([
|
|
2
|
+
"all",
|
|
3
|
+
"media_buying",
|
|
4
|
+
"creative_generation",
|
|
5
|
+
"rights_clearance",
|
|
6
|
+
"governance",
|
|
7
|
+
"measurement",
|
|
8
|
+
"agent_operations"
|
|
9
|
+
]);
|
|
10
|
+
const AUTHORIZED_OPERATOR_DOMAIN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/;
|
|
11
|
+
function checkDelegatedOperatorAuthorization(brandJson, agentEtld1, now, options) {
|
|
12
|
+
if (!brandJson || typeof brandJson !== "object") return void 0;
|
|
13
|
+
const operators = brandJson.authorized_operators;
|
|
14
|
+
if (!Array.isArray(operators)) return void 0;
|
|
15
|
+
let latestValidUntil;
|
|
16
|
+
let matched = false;
|
|
17
|
+
for (const op of operators) {
|
|
18
|
+
if (!op || typeof op !== "object" || Array.isArray(op)) continue;
|
|
19
|
+
const candidate = op;
|
|
20
|
+
const domain = candidate.domain;
|
|
21
|
+
const brands = candidate.brands;
|
|
22
|
+
if (typeof domain !== "string" || !AUTHORIZED_OPERATOR_DOMAIN.test(domain) || !isValidBrandGrant(brands)) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (domain !== agentEtld1) continue;
|
|
26
|
+
const scopes = candidate.scopes;
|
|
27
|
+
if (scopes !== void 0 && !isValidScopeGrant(scopes)) continue;
|
|
28
|
+
const countries = candidate.countries;
|
|
29
|
+
if (countries !== void 0 && !isValidCountryGrant(countries)) continue;
|
|
30
|
+
const validFrom = parseOptionalRfc3339(candidate.valid_from);
|
|
31
|
+
const validUntil = parseOptionalRfc3339(candidate.valid_until);
|
|
32
|
+
if (validFrom === null || validUntil === null) continue;
|
|
33
|
+
if (validFrom !== void 0 && validUntil !== void 0 && validFrom >= validUntil) continue;
|
|
34
|
+
if (validFrom !== void 0 && now < validFrom) continue;
|
|
35
|
+
if (validUntil !== void 0 && now >= validUntil) continue;
|
|
36
|
+
if (!matchesGrant(brands, options.requiredOperatorBrand, "*")) continue;
|
|
37
|
+
if (scopes !== void 0 && !matchesGrant(scopes, options.requiredOperatorScope, "all")) continue;
|
|
38
|
+
if (countries !== void 0 && !matchesGrant(countries, options.requiredOperatorCountry)) continue;
|
|
39
|
+
matched = true;
|
|
40
|
+
if (validUntil === void 0) return {};
|
|
41
|
+
latestValidUntil = Math.max(latestValidUntil ?? Number.NEGATIVE_INFINITY, validUntil);
|
|
42
|
+
}
|
|
43
|
+
return matched ? { validUntil: latestValidUntil } : void 0;
|
|
44
|
+
}
|
|
45
|
+
function isValidBrandGrant(value) {
|
|
46
|
+
return Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && (item === "*" || /^[a-z0-9_]+$/.test(item)));
|
|
47
|
+
}
|
|
48
|
+
function isValidScopeGrant(value) {
|
|
49
|
+
return Array.isArray(value) && value.length > 0 && new Set(value).size === value.length && value.every((item) => typeof item === "string" && AUTHORIZED_OPERATOR_SCOPES.has(item));
|
|
50
|
+
}
|
|
51
|
+
function isValidCountryGrant(value) {
|
|
52
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string" && /^[A-Z]{2}$/.test(item));
|
|
53
|
+
}
|
|
54
|
+
function matchesGrant(grants, required, wildcard) {
|
|
55
|
+
if (wildcard !== void 0 && grants.includes(wildcard)) return true;
|
|
56
|
+
return required !== void 0 && grants.includes(required);
|
|
57
|
+
}
|
|
58
|
+
function parseOptionalRfc3339(value) {
|
|
59
|
+
if (value === void 0) return void 0;
|
|
60
|
+
if (typeof value !== "string") return null;
|
|
61
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?([Zz]|([+-])(\d{2}):(\d{2}))$/.exec(
|
|
62
|
+
value
|
|
63
|
+
);
|
|
64
|
+
if (!match) return null;
|
|
65
|
+
const year = Number(match[1]);
|
|
66
|
+
const month = Number(match[2]);
|
|
67
|
+
const day = Number(match[3]);
|
|
68
|
+
const hour = Number(match[4]);
|
|
69
|
+
const minute = Number(match[5]);
|
|
70
|
+
const second = Number(match[6]);
|
|
71
|
+
const offsetHour = match[10] === void 0 ? 0 : Number(match[10]);
|
|
72
|
+
const offsetMinute = match[11] === void 0 ? 0 : Number(match[11]);
|
|
73
|
+
if (month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month) || hour > 23 || minute > 59 || second > 59 || offsetHour > 23 || offsetMinute > 59) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const parsed = Date.parse(value) / 1e3;
|
|
77
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
78
|
+
}
|
|
79
|
+
function daysInMonth(year, month) {
|
|
80
|
+
return new Date(Date.UTC(year, month, 0)).getUTCDate();
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
checkDelegatedOperatorAuthorization
|
|
84
|
+
};
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* `SsrfRefusedError` carries.
|
|
29
29
|
*/
|
|
30
30
|
import type { IdentityKeyOriginPurpose, IdentityPosture } from './capabilities-types.mjs';
|
|
31
|
+
import type { SsrfDnsLookup } from '../../net/index.mjs';
|
|
31
32
|
import { type ConsistencyResult } from './consistency.mjs';
|
|
32
33
|
import { type AgentResolverErrorDetail } from './errors.mjs';
|
|
33
34
|
import { type AgentEntry } from './select-agent.mjs';
|
|
@@ -48,6 +49,13 @@ export interface FetchCapabilitiesFn {
|
|
|
48
49
|
export interface ResolveAgentOptions {
|
|
49
50
|
/** Default `'mcp'`. Ignored when `fetchCapabilities` is supplied. */
|
|
50
51
|
protocol?: AgentProtocol;
|
|
52
|
+
/** URL matching is mandatory; these filters may only narrow it. */
|
|
53
|
+
expectedBrandJsonUrl?: string;
|
|
54
|
+
agentType?: string;
|
|
55
|
+
agentId?: string;
|
|
56
|
+
/** Enable the 3.x webhook-only fallback when brand_json_url is absent. */
|
|
57
|
+
legacyWebhookFallback?: boolean;
|
|
58
|
+
lookup?: SsrfDnsLookup;
|
|
51
59
|
/**
|
|
52
60
|
* Override the capabilities-fetch step entirely. Tests pass a fake; production
|
|
53
61
|
* callers who already hold a configured protocol client can wire it through
|
|
@@ -85,10 +93,7 @@ export interface ResolveAgentOptions {
|
|
|
85
93
|
*/
|
|
86
94
|
declaredPurposes?: readonly IdentityKeyOriginPurpose[];
|
|
87
95
|
/**
|
|
88
|
-
*
|
|
89
|
-
* `true`, the step-7 origin-consistency check is skipped for the
|
|
90
|
-
* webhook-signing purpose only (operator-side webhook-signing remains
|
|
91
|
-
* checked). Buyer-side / receive-side verifiers MUST leave this unset.
|
|
96
|
+
* @deprecated Publisher pins never skip origin consistency. Ignored.
|
|
92
97
|
*/
|
|
93
98
|
publisherPinned?: {
|
|
94
99
|
webhook_signing?: boolean;
|
|
@@ -126,6 +131,11 @@ export interface TraceStep {
|
|
|
126
131
|
export interface AgentResolution {
|
|
127
132
|
agentUrl: string;
|
|
128
133
|
brandJsonUrl: string;
|
|
134
|
+
/** Exact selected operator record; use it for signed-buyer governance. */
|
|
135
|
+
brandJson?: unknown;
|
|
136
|
+
brandJsonCacheControl?: string;
|
|
137
|
+
/** True only on the explicitly enabled 3.x webhook compatibility path. */
|
|
138
|
+
legacyWebhookFallback?: boolean;
|
|
129
139
|
agentEntry: AgentEntry;
|
|
130
140
|
jwksUri: string;
|
|
131
141
|
jwks: {
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* `SsrfRefusedError` carries.
|
|
29
29
|
*/
|
|
30
30
|
import type { IdentityKeyOriginPurpose, IdentityPosture } from './capabilities-types';
|
|
31
|
+
import type { SsrfDnsLookup } from '../../net';
|
|
31
32
|
import { type ConsistencyResult } from './consistency';
|
|
32
33
|
import { type AgentResolverErrorDetail } from './errors';
|
|
33
34
|
import { type AgentEntry } from './select-agent';
|
|
@@ -48,6 +49,13 @@ export interface FetchCapabilitiesFn {
|
|
|
48
49
|
export interface ResolveAgentOptions {
|
|
49
50
|
/** Default `'mcp'`. Ignored when `fetchCapabilities` is supplied. */
|
|
50
51
|
protocol?: AgentProtocol;
|
|
52
|
+
/** URL matching is mandatory; these filters may only narrow it. */
|
|
53
|
+
expectedBrandJsonUrl?: string;
|
|
54
|
+
agentType?: string;
|
|
55
|
+
agentId?: string;
|
|
56
|
+
/** Enable the 3.x webhook-only fallback when brand_json_url is absent. */
|
|
57
|
+
legacyWebhookFallback?: boolean;
|
|
58
|
+
lookup?: SsrfDnsLookup;
|
|
51
59
|
/**
|
|
52
60
|
* Override the capabilities-fetch step entirely. Tests pass a fake; production
|
|
53
61
|
* callers who already hold a configured protocol client can wire it through
|
|
@@ -85,10 +93,7 @@ export interface ResolveAgentOptions {
|
|
|
85
93
|
*/
|
|
86
94
|
declaredPurposes?: readonly IdentityKeyOriginPurpose[];
|
|
87
95
|
/**
|
|
88
|
-
*
|
|
89
|
-
* `true`, the step-7 origin-consistency check is skipped for the
|
|
90
|
-
* webhook-signing purpose only (operator-side webhook-signing remains
|
|
91
|
-
* checked). Buyer-side / receive-side verifiers MUST leave this unset.
|
|
96
|
+
* @deprecated Publisher pins never skip origin consistency. Ignored.
|
|
92
97
|
*/
|
|
93
98
|
publisherPinned?: {
|
|
94
99
|
webhook_signing?: boolean;
|
|
@@ -126,6 +131,11 @@ export interface TraceStep {
|
|
|
126
131
|
export interface AgentResolution {
|
|
127
132
|
agentUrl: string;
|
|
128
133
|
brandJsonUrl: string;
|
|
134
|
+
/** Exact selected operator record; use it for signed-buyer governance. */
|
|
135
|
+
brandJson?: unknown;
|
|
136
|
+
brandJsonCacheControl?: string;
|
|
137
|
+
/** True only on the explicitly enabled 3.x webhook compatibility path. */
|
|
138
|
+
legacyWebhookFallback?: boolean;
|
|
129
139
|
agentEntry: AgentEntry;
|
|
130
140
|
jwksUri: string;
|
|
131
141
|
jwks: {
|
|
@@ -25,7 +25,8 @@ var import_tldts = require("tldts");
|
|
|
25
25
|
var import_protocols = require('../../protocols/index.js');
|
|
26
26
|
var import_domain = require('../../brand/domain.js');
|
|
27
27
|
var import_capabilities_types = require('./capabilities-types.js');
|
|
28
|
-
var
|
|
28
|
+
var import_operator_authorization = require('./operator-authorization.js');
|
|
29
|
+
var import_legacy_brand = require('./legacy-brand.js');
|
|
29
30
|
var import_consistency = require('./consistency.js');
|
|
30
31
|
var import_errors = require('./errors.js');
|
|
31
32
|
var import_etld = require('./etld.js');
|
|
@@ -69,7 +70,10 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
69
70
|
["agent_url"]
|
|
70
71
|
);
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
+
let brandJsonUrl = (0, import_capabilities_types.readBrandJsonUrl)(capabilitiesPayload);
|
|
74
|
+
const identity = capabilitiesPayload?.identity;
|
|
75
|
+
const legacyFallback = options.legacyWebhookFallback === true && identity?.brand_json_url === void 0 && permitsLegacyWebhookDiscovery(capabilitiesPayload);
|
|
76
|
+
if (legacyFallback) brandJsonUrl = `${new URL(agentUrl).origin}/.well-known/brand.json`;
|
|
73
77
|
const identityPosture = (0, import_capabilities_types.readIdentityPosture)(capabilitiesPayload);
|
|
74
78
|
const acceptsScheme = (url) => {
|
|
75
79
|
if (!url) return false;
|
|
@@ -87,24 +91,43 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
87
91
|
["agent_url"]
|
|
88
92
|
);
|
|
89
93
|
}
|
|
94
|
+
if (!legacyFallback && options.expectedBrandJsonUrl !== void 0 && (0, import_select_agent.canonicalAgentUrl)(brandJsonUrl) !== (0, import_select_agent.canonicalAgentUrl)(options.expectedBrandJsonUrl)) {
|
|
95
|
+
throw new import_errors.AgentResolverError(
|
|
96
|
+
"request_signature_brand_origin_mismatch",
|
|
97
|
+
"Cached operator mapping disagrees with identity.brand_json_url",
|
|
98
|
+
{ agent_url: agentUrl },
|
|
99
|
+
["agent_url"]
|
|
100
|
+
);
|
|
101
|
+
}
|
|
90
102
|
pushTrace(trace, { step: 2, name: "read_brand_json_url", ok: true, detail: { brand_json_url: brandJsonUrl } });
|
|
91
|
-
let agentEtld1;
|
|
92
|
-
let brandEtld1;
|
|
93
|
-
let sameOrigin;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
let agentEtld1 = "";
|
|
104
|
+
let brandEtld1 = "";
|
|
105
|
+
let sameOrigin = true;
|
|
106
|
+
if (!legacyFallback) {
|
|
107
|
+
try {
|
|
108
|
+
agentEtld1 = (0, import_etld.eTldPlusOne)(agentUrl);
|
|
109
|
+
brandEtld1 = (0, import_etld.eTldPlusOne)(brandJsonUrl);
|
|
110
|
+
sameOrigin = (0, import_etld.sameEtldPlusOne)(agentUrl, brandJsonUrl);
|
|
111
|
+
} catch {
|
|
112
|
+
if (allowPrivateIp) {
|
|
113
|
+
const agentHost = new URL(agentUrl).hostname;
|
|
114
|
+
const brandHost = new URL(brandJsonUrl).hostname;
|
|
115
|
+
const agentIsExplicitDevelopmentHost = agentHost === "localhost" || (0, import_domain.isDevelopmentBrandDomain)(agentHost) || (0, import_tldts.parse)(agentHost, { extractHostname: false }).isIp;
|
|
116
|
+
const brandIsExplicitDevelopmentHost = brandHost === "localhost" || (0, import_domain.isDevelopmentBrandDomain)(brandHost) || (0, import_tldts.parse)(brandHost, { extractHostname: false }).isIp;
|
|
117
|
+
if (agentIsExplicitDevelopmentHost && brandIsExplicitDevelopmentHost) {
|
|
118
|
+
agentEtld1 = agentHost;
|
|
119
|
+
brandEtld1 = brandHost;
|
|
120
|
+
sameOrigin = agentHost === brandHost;
|
|
121
|
+
} else {
|
|
122
|
+
const detail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };
|
|
123
|
+
pushTrace(trace, { step: 3, name: "etld1_binding", ok: false, detail });
|
|
124
|
+
throw new import_errors.AgentResolverError(
|
|
125
|
+
"request_signature_brand_origin_mismatch",
|
|
126
|
+
`Cannot compute eTLD+1 for agent or brand.json host`,
|
|
127
|
+
detail,
|
|
128
|
+
["agent_url", "brand_json_url"]
|
|
129
|
+
);
|
|
130
|
+
}
|
|
108
131
|
} else {
|
|
109
132
|
const detail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };
|
|
110
133
|
pushTrace(trace, { step: 3, name: "etld1_binding", ok: false, detail });
|
|
@@ -115,25 +138,24 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
115
138
|
["agent_url", "brand_json_url"]
|
|
116
139
|
);
|
|
117
140
|
}
|
|
118
|
-
} else {
|
|
119
|
-
const detail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };
|
|
120
|
-
pushTrace(trace, { step: 3, name: "etld1_binding", ok: false, detail });
|
|
121
|
-
throw new import_errors.AgentResolverError(
|
|
122
|
-
"request_signature_brand_origin_mismatch",
|
|
123
|
-
`Cannot compute eTLD+1 for agent or brand.json host`,
|
|
124
|
-
detail,
|
|
125
|
-
["agent_url", "brand_json_url"]
|
|
126
|
-
);
|
|
127
141
|
}
|
|
128
142
|
}
|
|
129
143
|
let brandJson;
|
|
130
144
|
let brandJsonFetchedAt;
|
|
145
|
+
let brandJsonCacheControl;
|
|
131
146
|
try {
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
})
|
|
147
|
+
const fetchOptions = { allowPrivateIp, timeoutMs, maxBodyBytes: brandJsonCap, lookup: options.lookup };
|
|
148
|
+
const fetched = legacyFallback ? await (0, import_legacy_brand.fetchLegacyBrandJson)(agentUrl, fetchOptions) : await (0, import_fetch_helpers.safeFetchJson)(brandJsonUrl, "brand.json", fetchOptions);
|
|
149
|
+
if (legacyFallback && "url" in fetched) brandJsonUrl = fetched.url;
|
|
150
|
+
brandJsonCacheControl = fetched.headers["cache-control"];
|
|
151
|
+
if (legacyFallback && options.expectedBrandJsonUrl !== void 0 && (0, import_select_agent.canonicalAgentUrl)(brandJsonUrl) !== (0, import_select_agent.canonicalAgentUrl)(options.expectedBrandJsonUrl) && (0, import_select_agent.canonicalAgentUrl)(`${new URL(agentUrl).origin}/.well-known/brand.json`) !== (0, import_select_agent.canonicalAgentUrl)(options.expectedBrandJsonUrl)) {
|
|
152
|
+
throw new import_errors.AgentResolverError(
|
|
153
|
+
"request_signature_brand_origin_mismatch",
|
|
154
|
+
"Cached mapping disagrees with legacy discovery",
|
|
155
|
+
{ agent_url: agentUrl },
|
|
156
|
+
["agent_url"]
|
|
157
|
+
);
|
|
158
|
+
}
|
|
137
159
|
brandJson = fetched.body;
|
|
138
160
|
brandJsonFetchedAt = fetched.fetchedAt;
|
|
139
161
|
pushTrace(trace, {
|
|
@@ -144,6 +166,7 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
144
166
|
url: brandJsonUrl
|
|
145
167
|
});
|
|
146
168
|
} catch (err) {
|
|
169
|
+
if (err instanceof import_errors.AgentResolverError) throw err;
|
|
147
170
|
if (err instanceof import_fetch_helpers.SafeFetchError && /strict-JSON parse/.test(err.message)) {
|
|
148
171
|
const detail2 = {
|
|
149
172
|
brand_json_url: brandJsonUrl,
|
|
@@ -187,7 +210,17 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
187
210
|
["agent_url"]
|
|
188
211
|
);
|
|
189
212
|
}
|
|
190
|
-
|
|
213
|
+
if (options.requiredOperatorBrand !== void 0 || options.requiredOperatorScope !== void 0 || options.requiredOperatorCountry !== void 0) {
|
|
214
|
+
const authorization = (0, import_operator_authorization.checkDelegatedOperatorAuthorization)(brandJson, agentEtld1, now(), options);
|
|
215
|
+
if (!authorization)
|
|
216
|
+
throw new import_errors.AgentResolverError(
|
|
217
|
+
"request_signature_brand_origin_mismatch",
|
|
218
|
+
"Delegated operator does not satisfy the receiver account authorization policy",
|
|
219
|
+
{ agent_url: agentUrl },
|
|
220
|
+
["agent_url"]
|
|
221
|
+
);
|
|
222
|
+
operatorAuthorizationValidUntil = authorization.validUntil;
|
|
223
|
+
}
|
|
191
224
|
pushTrace(trace, {
|
|
192
225
|
step: 3,
|
|
193
226
|
name: "etld1_binding",
|
|
@@ -204,7 +237,7 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
204
237
|
}
|
|
205
238
|
let agentEntry;
|
|
206
239
|
try {
|
|
207
|
-
agentEntry = (0, import_select_agent.selectAgentByUrl)(brandJson, agentUrl);
|
|
240
|
+
agentEntry = (0, import_select_agent.selectAgentByUrl)(brandJson, agentUrl, options);
|
|
208
241
|
pushTrace(trace, { step: 5, name: "select_agent", ok: true, detail: { url: agentEntry.url } });
|
|
209
242
|
} catch (err) {
|
|
210
243
|
if (err instanceof import_select_agent.AgentSelectorError) {
|
|
@@ -227,17 +260,19 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
227
260
|
pushTrace(trace, { step: 5, name: "select_agent", ok: false, detail });
|
|
228
261
|
throw new import_errors.AgentResolverError(code, err.message, detail, attacker);
|
|
229
262
|
}
|
|
230
|
-
throw
|
|
263
|
+
throw new import_errors.AgentResolverError(
|
|
264
|
+
"request_signature_brand_json_malformed",
|
|
265
|
+
"Matched agent has an invalid URL or JWKS source",
|
|
266
|
+
{ brand_json_url: brandJsonUrl },
|
|
267
|
+
["brand_json_url"]
|
|
268
|
+
);
|
|
231
269
|
}
|
|
232
|
-
const
|
|
233
|
-
const jwksUriFromEntry = typeof declaredJwksUri === "string" && acceptsScheme(declaredJwksUri) ? declaredJwksUri : void 0;
|
|
234
|
-
const jwksUri = jwksUriFromEntry ?? `${originOf(agentUrl)}/.well-known/jwks.json`;
|
|
270
|
+
const jwksUri = (0, import_select_agent.agentJwksUri)(agentEntry);
|
|
235
271
|
pushTrace(trace, { step: 6, name: "resolve_jwks_uri", ok: true, detail: { jwks_uri: jwksUri } });
|
|
236
|
-
const consistencyResults = runConsistencyChecks({
|
|
272
|
+
const consistencyResults = legacyFallback ? [] : runConsistencyChecks({
|
|
237
273
|
capabilitiesPayload,
|
|
238
274
|
identityPosture,
|
|
239
275
|
jwksUri,
|
|
240
|
-
publisherPinnedWebhookSigning: options.publisherPinned?.webhook_signing === true,
|
|
241
276
|
extraDeclaredPurposes: options.declaredPurposes ?? []
|
|
242
277
|
});
|
|
243
278
|
const failedConsistency = consistencyResults.filter((r) => r.ok === false);
|
|
@@ -270,7 +305,8 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
270
305
|
const fetched = await (0, import_fetch_helpers.safeFetchJson)(jwksUri, "jwks", {
|
|
271
306
|
allowPrivateIp,
|
|
272
307
|
timeoutMs,
|
|
273
|
-
maxBodyBytes: jwksCap
|
|
308
|
+
maxBodyBytes: jwksCap,
|
|
309
|
+
lookup: options.lookup
|
|
274
310
|
});
|
|
275
311
|
if (!fetched.body || typeof fetched.body !== "object" || !Array.isArray(fetched.body.keys)) {
|
|
276
312
|
throw new import_fetch_helpers.SafeFetchError("jwks", "fetch_failed", "JWKS document has no keys[] array");
|
|
@@ -307,8 +343,11 @@ async function resolveAgent(agentUrl, options = {}) {
|
|
|
307
343
|
);
|
|
308
344
|
}
|
|
309
345
|
return {
|
|
310
|
-
agentUrl,
|
|
346
|
+
agentUrl: (0, import_select_agent.canonicalAgentUrl)(agentEntry.url),
|
|
311
347
|
brandJsonUrl,
|
|
348
|
+
brandJson,
|
|
349
|
+
...legacyFallback && { legacyWebhookFallback: true },
|
|
350
|
+
...brandJsonCacheControl !== void 0 && { brandJsonCacheControl },
|
|
312
351
|
agentEntry,
|
|
313
352
|
jwksUri,
|
|
314
353
|
jwks,
|
|
@@ -335,95 +374,14 @@ function defaultFetchCapabilities(agentUrl, protocol) {
|
|
|
335
374
|
return client.callTool("get_adcp_capabilities", {});
|
|
336
375
|
};
|
|
337
376
|
}
|
|
338
|
-
|
|
339
|
-
"all",
|
|
340
|
-
"media_buying",
|
|
341
|
-
"creative_generation",
|
|
342
|
-
"rights_clearance",
|
|
343
|
-
"governance",
|
|
344
|
-
"measurement",
|
|
345
|
-
"agent_operations"
|
|
346
|
-
]);
|
|
347
|
-
const AUTHORIZED_OPERATOR_DOMAIN = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/;
|
|
348
|
-
function findAuthorizedOperator(brandJson, agentEtld1, now, options) {
|
|
377
|
+
function findAuthorizedOperator(brandJson, agentEtld1, _now, _options) {
|
|
349
378
|
if (!brandJson || typeof brandJson !== "object") return void 0;
|
|
350
|
-
const
|
|
351
|
-
if (!Array.isArray(
|
|
352
|
-
|
|
353
|
-
let matched = false;
|
|
354
|
-
for (const op of operators) {
|
|
355
|
-
if (!op || typeof op !== "object" || Array.isArray(op)) continue;
|
|
356
|
-
const candidate = op;
|
|
357
|
-
const domain = candidate.domain;
|
|
358
|
-
const brands = candidate.brands;
|
|
359
|
-
if (typeof domain !== "string" || !AUTHORIZED_OPERATOR_DOMAIN.test(domain) || !isValidBrandGrant(brands)) {
|
|
360
|
-
continue;
|
|
361
|
-
}
|
|
362
|
-
try {
|
|
363
|
-
if ((0, import_etld.eTldPlusOne)(domain) !== agentEtld1) continue;
|
|
364
|
-
} catch {
|
|
365
|
-
if (!(options.allowPrivateIp && (0, import_domain.isDevelopmentBrandDomain)(domain) && domain === agentEtld1)) {
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
const scopes = candidate.scopes;
|
|
370
|
-
if (scopes !== void 0 && !isValidScopeGrant(scopes)) continue;
|
|
371
|
-
const countries = candidate.countries;
|
|
372
|
-
if (countries !== void 0 && !isValidCountryGrant(countries)) continue;
|
|
373
|
-
const validFrom = parseOptionalRfc3339(candidate.valid_from);
|
|
374
|
-
const validUntil = parseOptionalRfc3339(candidate.valid_until);
|
|
375
|
-
if (validFrom === null || validUntil === null) continue;
|
|
376
|
-
if (validFrom !== void 0 && validUntil !== void 0 && validFrom >= validUntil) continue;
|
|
377
|
-
if (validFrom !== void 0 && now < validFrom) continue;
|
|
378
|
-
if (validUntil !== void 0 && now >= validUntil) continue;
|
|
379
|
-
if (!matchesGrant(brands, options.requiredOperatorBrand, "*")) continue;
|
|
380
|
-
if (scopes !== void 0 && !matchesGrant(scopes, options.requiredOperatorScope, "all")) continue;
|
|
381
|
-
if (countries !== void 0 && !matchesGrant(countries, options.requiredOperatorCountry)) continue;
|
|
382
|
-
matched = true;
|
|
383
|
-
if (validUntil === void 0) return {};
|
|
384
|
-
latestValidUntil = Math.max(latestValidUntil ?? Number.NEGATIVE_INFINITY, validUntil);
|
|
379
|
+
const obj = brandJson;
|
|
380
|
+
if (!obj.house || typeof obj.house !== "object" || Array.isArray(obj.house) || !Array.isArray(obj.brands)) {
|
|
381
|
+
return void 0;
|
|
385
382
|
}
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
function isValidBrandGrant(value) {
|
|
389
|
-
return Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && (item === "*" || /^[a-z0-9_]+$/.test(item)));
|
|
390
|
-
}
|
|
391
|
-
function isValidScopeGrant(value) {
|
|
392
|
-
return Array.isArray(value) && value.length > 0 && new Set(value).size === value.length && value.every((item) => typeof item === "string" && AUTHORIZED_OPERATOR_SCOPES.has(item));
|
|
393
|
-
}
|
|
394
|
-
function isValidCountryGrant(value) {
|
|
395
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string" && /^[A-Z]{2}$/.test(item));
|
|
396
|
-
}
|
|
397
|
-
function matchesGrant(grants, required, wildcard) {
|
|
398
|
-
if (wildcard !== void 0 && grants.includes(wildcard)) return true;
|
|
399
|
-
return required !== void 0 && grants.includes(required);
|
|
400
|
-
}
|
|
401
|
-
function parseOptionalRfc3339(value) {
|
|
402
|
-
if (value === void 0) return void 0;
|
|
403
|
-
if (typeof value !== "string") return null;
|
|
404
|
-
const match = /^(\d{4})-(\d{2})-(\d{2})[Tt](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?([Zz]|([+-])(\d{2}):(\d{2}))$/.exec(
|
|
405
|
-
value
|
|
406
|
-
);
|
|
407
|
-
if (!match) return null;
|
|
408
|
-
const year = Number(match[1]);
|
|
409
|
-
const month = Number(match[2]);
|
|
410
|
-
const day = Number(match[3]);
|
|
411
|
-
const hour = Number(match[4]);
|
|
412
|
-
const minute = Number(match[5]);
|
|
413
|
-
const second = Number(match[6]);
|
|
414
|
-
const offsetHour = match[10] === void 0 ? 0 : Number(match[10]);
|
|
415
|
-
const offsetMinute = match[11] === void 0 ? 0 : Number(match[11]);
|
|
416
|
-
if (month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month) || hour > 23 || minute > 59 || second > 59 || offsetHour > 23 || offsetMinute > 59) {
|
|
417
|
-
return null;
|
|
418
|
-
}
|
|
419
|
-
const parsed = Date.parse(value) / 1e3;
|
|
420
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
421
|
-
}
|
|
422
|
-
function daysInMonth(year, month) {
|
|
423
|
-
return new Date(Date.UTC(year, month, 0)).getUTCDate();
|
|
424
|
-
}
|
|
425
|
-
function originOf(url) {
|
|
426
|
-
return (0, import_canonicalize.canonicalizeOrigin)(url);
|
|
383
|
+
if (!Array.isArray(obj.authorized_operators)) return void 0;
|
|
384
|
+
return obj.authorized_operators.some((op) => op && typeof op === "object" && op.domain === agentEtld1) ? {} : void 0;
|
|
427
385
|
}
|
|
428
386
|
function pushTrace(trace, step) {
|
|
429
387
|
trace.push(step);
|
|
@@ -445,14 +403,25 @@ function runConsistencyChecks(args) {
|
|
|
445
403
|
const result = (0, import_consistency.checkOriginConsistency)({
|
|
446
404
|
purpose,
|
|
447
405
|
declaredOrigin,
|
|
448
|
-
resolvedJwksUri: args.jwksUri
|
|
449
|
-
publisherPinned: purpose === "webhook_signing" && args.publisherPinnedWebhookSigning
|
|
406
|
+
resolvedJwksUri: args.jwksUri
|
|
450
407
|
});
|
|
451
408
|
if (result.ok === false) results.push(result);
|
|
452
409
|
}
|
|
453
410
|
}
|
|
454
411
|
return results;
|
|
455
412
|
}
|
|
413
|
+
function permitsLegacyWebhookDiscovery(payload) {
|
|
414
|
+
if (!payload || typeof payload !== "object") return true;
|
|
415
|
+
const caps = payload;
|
|
416
|
+
const major = (value) => typeof value === "string" ? Number(/^(\d+)\./.exec(value)?.[1]) : value;
|
|
417
|
+
if (Number(major(caps.adcp_version)) >= 4 || Array.isArray(caps.adcp?.supported_versions) && caps.adcp.supported_versions.some((v) => Number(major(v)) >= 4) || Array.isArray(caps.adcp?.major_versions) && caps.adcp.major_versions.some((v) => typeof v === "number" && v >= 4))
|
|
418
|
+
return false;
|
|
419
|
+
if (caps.adcp_version !== void 0) return typeof caps.adcp_version === "string" && /^3\./.test(caps.adcp_version);
|
|
420
|
+
if (Array.isArray(caps.adcp?.supported_versions))
|
|
421
|
+
return caps.adcp.supported_versions.some((v) => typeof v === "string" && /^3\./.test(v));
|
|
422
|
+
if (Array.isArray(caps.adcp?.major_versions)) return caps.adcp.major_versions.includes(3);
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
456
425
|
// Annotate the CommonJS export names for ESM import in node:
|
|
457
426
|
0 && (module.exports = {
|
|
458
427
|
resolveAgent
|