@adcp/sdk 12.0.0 → 12.0.2
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/content-standards-adapter.mjs +0 -6
- package/dist/lib/adapters/content-standards-adapter.mjs.map +1 -1
- package/dist/lib/adapters/derived-account-store.mjs +0 -6
- package/dist/lib/adapters/derived-account-store.mjs.map +1 -1
- package/dist/lib/adapters/governance-adapter.mjs +0 -6
- package/dist/lib/adapters/governance-adapter.mjs.map +1 -1
- package/dist/lib/adapters/implicit-account-store.mjs +0 -6
- package/dist/lib/adapters/implicit-account-store.mjs.map +1 -1
- package/dist/lib/adapters/index.mjs +0 -6
- package/dist/lib/adapters/index.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/create_media_buy.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/create_media_buy.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/get_products.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/get_products.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/index.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/index.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/list_creative_formats.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/list_creative_formats.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/preview_creative.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/preview_creative.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/sync_creatives.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/sync_creatives.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/types.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/update_media_buy.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/update_media_buy.mjs.map +1 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.mjs +0 -6
- package/dist/lib/adapters/oauth-passthrough-resolver.mjs.map +1 -1
- package/dist/lib/adapters/property-list-adapter.mjs +0 -6
- package/dist/lib/adapters/property-list-adapter.mjs.map +1 -1
- package/dist/lib/adapters/roster-account-store.mjs +0 -6
- package/dist/lib/adapters/roster-account-store.mjs.map +1 -1
- package/dist/lib/adapters/si-session-manager.mjs +0 -6
- package/dist/lib/adapters/si-session-manager.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/brand-fields.mjs +0 -6
- package/dist/lib/adapters/version/3.0/brand-fields.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/get-products.mjs +0 -6
- package/dist/lib/adapters/version/3.0/get-products.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/index.mjs +0 -6
- package/dist/lib/adapters/version/3.0/index.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/sync-accounts.mjs +0 -6
- package/dist/lib/adapters/version/3.0/sync-accounts.mjs.map +1 -1
- package/dist/lib/adapters/version/index.mjs +0 -6
- package/dist/lib/adapters/version/index.mjs.map +1 -1
- package/dist/lib/adapters/version/types.mjs +0 -6
- package/dist/lib/advanced.mjs +0 -6
- package/dist/lib/advanced.mjs.map +1 -1
- package/dist/lib/agents/index.generated.mjs +0 -6
- package/dist/lib/agents/index.generated.mjs.map +1 -1
- package/dist/lib/auth/index.mjs +0 -6
- package/dist/lib/auth/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs +0 -6
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs.map +1 -1
- package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs +0 -6
- package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs.map +1 -1
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs +0 -6
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs.map +1 -1
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs +0 -6
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.mjs +0 -6
- package/dist/lib/auth/oauth/authorization-required.mjs.map +1 -1
- package/dist/lib/auth/oauth/diagnose.mjs +0 -6
- package/dist/lib/auth/oauth/diagnose.mjs.map +1 -1
- package/dist/lib/auth/oauth/diagnostics.mjs +0 -6
- package/dist/lib/auth/oauth/diagnostics.mjs.map +1 -1
- package/dist/lib/auth/oauth/discovery.mjs +0 -6
- package/dist/lib/auth/oauth/discovery.mjs.map +1 -1
- package/dist/lib/auth/oauth/file-storage.mjs +0 -6
- package/dist/lib/auth/oauth/file-storage.mjs.map +1 -1
- package/dist/lib/auth/oauth/index.mjs +0 -6
- package/dist/lib/auth/oauth/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/secret-resolver.mjs +0 -6
- package/dist/lib/auth/oauth/secret-resolver.mjs.map +1 -1
- package/dist/lib/auth/oauth/storage-registry.mjs +0 -6
- package/dist/lib/auth/oauth/storage-registry.mjs.map +1 -1
- package/dist/lib/auth/oauth/types.mjs +0 -6
- package/dist/lib/auth/oauth/types.mjs.map +1 -1
- package/dist/lib/auth/oauth/web-flow.mjs +0 -6
- package/dist/lib/auth/oauth/web-flow.mjs.map +1 -1
- package/dist/lib/brand/index.mjs +0 -6
- package/dist/lib/brand/index.mjs.map +1 -1
- package/dist/lib/canonical-references/index.mjs +6 -7
- package/dist/lib/canonical-references/index.mjs.map +1 -1
- package/dist/lib/client/index.mjs +0 -6
- package/dist/lib/client/index.mjs.map +1 -1
- package/dist/lib/compliance/index.mjs +0 -6
- package/dist/lib/compliance/index.mjs.map +1 -1
- package/dist/lib/compliance-fixtures/index.mjs +0 -6
- package/dist/lib/compliance-fixtures/index.mjs.map +1 -1
- package/dist/lib/compliance-fixtures/test-authorization-server.mjs +0 -6
- package/dist/lib/compliance-fixtures/test-authorization-server.mjs.map +1 -1
- package/dist/lib/conformance/index.mjs +0 -6
- package/dist/lib/conformance/index.mjs.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs +0 -6
- package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
- package/dist/lib/conformance/invariants/uniformErrorComparator.mjs +0 -6
- package/dist/lib/conformance/invariants/uniformErrorComparator.mjs.map +1 -1
- package/dist/lib/conformance/oracle.mjs +0 -6
- package/dist/lib/conformance/oracle.mjs.map +1 -1
- package/dist/lib/conformance/runConformance.mjs +0 -6
- package/dist/lib/conformance/runConformance.mjs.map +1 -1
- package/dist/lib/conformance/runners.mjs +0 -6
- package/dist/lib/conformance/runners.mjs.map +1 -1
- package/dist/lib/conformance/schemaArbitrary.mjs +0 -6
- package/dist/lib/conformance/schemaArbitrary.mjs.map +1 -1
- package/dist/lib/conformance/schemaLoader.mjs +5 -6
- package/dist/lib/conformance/schemaLoader.mjs.map +1 -1
- package/dist/lib/conformance/seeder.mjs +0 -6
- package/dist/lib/conformance/seeder.mjs.map +1 -1
- package/dist/lib/conformance/types.mjs +0 -6
- package/dist/lib/conformance/types.mjs.map +1 -1
- package/dist/lib/conformance/webhook.mjs +0 -6
- package/dist/lib/conformance/webhook.mjs.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.mjs +0 -6
- package/dist/lib/core/ADCPMultiAgentClient.mjs.map +1 -1
- package/dist/lib/core/AgentClient.mjs +0 -6
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/AsyncHandler.mjs +0 -6
- package/dist/lib/core/AsyncHandler.mjs.map +1 -1
- package/dist/lib/core/ConfigurationManager.mjs +0 -6
- package/dist/lib/core/ConfigurationManager.mjs.map +1 -1
- package/dist/lib/core/ConversationTypes.mjs +0 -6
- package/dist/lib/core/CreativeAgentClient.mjs +0 -6
- package/dist/lib/core/CreativeAgentClient.mjs.map +1 -1
- package/dist/lib/core/GovernanceMiddleware.mjs +0 -6
- package/dist/lib/core/GovernanceMiddleware.mjs.map +1 -1
- package/dist/lib/core/GovernanceTypes.mjs +0 -6
- package/dist/lib/core/GovernanceTypes.mjs.map +1 -1
- package/dist/lib/core/ProtocolResponseParser.mjs +0 -6
- package/dist/lib/core/ProtocolResponseParser.mjs.map +1 -1
- package/dist/lib/core/ResponseValidator.mjs +0 -6
- package/dist/lib/core/ResponseValidator.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +0 -6
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskEventTypes.mjs +0 -6
- package/dist/lib/core/TaskEventTypes.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +0 -6
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/core/match.mjs +0 -6
- package/dist/lib/core/match.mjs.map +1 -1
- package/dist/lib/core/task-status.mjs +0 -6
- package/dist/lib/core/task-status.mjs.map +1 -1
- package/dist/lib/core/webhook-url.mjs +0 -6
- package/dist/lib/core/webhook-url.mjs.map +1 -1
- package/dist/lib/discovery/adagents-redirects.mjs +0 -6
- package/dist/lib/discovery/adagents-redirects.mjs.map +1 -1
- package/dist/lib/discovery/agent-directory.mjs +0 -6
- package/dist/lib/discovery/agent-directory.mjs.map +1 -1
- package/dist/lib/discovery/inline-publisher-properties.mjs +0 -6
- package/dist/lib/discovery/inline-publisher-properties.mjs.map +1 -1
- package/dist/lib/discovery/network-consistency-checker.mjs +0 -6
- package/dist/lib/discovery/network-consistency-checker.mjs.map +1 -1
- package/dist/lib/discovery/property-crawler.mjs +0 -6
- package/dist/lib/discovery/property-crawler.mjs.map +1 -1
- package/dist/lib/discovery/property-index.mjs +0 -6
- package/dist/lib/discovery/property-index.mjs.map +1 -1
- package/dist/lib/discovery/publisher-property-selector.mjs +0 -6
- package/dist/lib/discovery/publisher-property-selector.mjs.map +1 -1
- package/dist/lib/discovery/resolve-agent-properties.mjs +0 -6
- package/dist/lib/discovery/resolve-agent-properties.mjs.map +1 -1
- package/dist/lib/discovery/types.mjs +0 -6
- package/dist/lib/discovery/validate-adagents.mjs +0 -6
- package/dist/lib/discovery/validate-adagents.mjs.map +1 -1
- package/dist/lib/enums.mjs +0 -6
- package/dist/lib/enums.mjs.map +1 -1
- package/dist/lib/errors/index.mjs +0 -6
- package/dist/lib/errors/index.mjs.map +1 -1
- package/dist/lib/express-mcp/index.mjs +0 -6
- package/dist/lib/express-mcp/index.mjs.map +1 -1
- package/dist/lib/governance/index.mjs +0 -6
- package/dist/lib/governance/index.mjs.map +1 -1
- package/dist/lib/handlers/types.mjs +0 -6
- package/dist/lib/handlers/types.mjs.map +1 -1
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +0 -6
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/media-buy/available-actions.mjs +0 -6
- package/dist/lib/media-buy/available-actions.mjs.map +1 -1
- package/dist/lib/media-buy/index.mjs +0 -6
- package/dist/lib/media-buy/index.mjs.map +1 -1
- package/dist/lib/media-buy/preflight.mjs +0 -6
- package/dist/lib/media-buy/preflight.mjs.map +1 -1
- package/dist/lib/media-buy/property-policy.mjs +0 -6
- package/dist/lib/media-buy/property-policy.mjs.map +1 -1
- package/dist/lib/media-buy/types.mjs +0 -6
- package/dist/lib/media-buy/types.mjs.map +1 -1
- package/dist/lib/media-buy/update-fields.generated.mjs +0 -6
- package/dist/lib/media-buy/update-fields.generated.mjs.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/seed-data.mjs +0 -6
- package/dist/lib/mock-server/creative-ad-server/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/server.mjs +0 -6
- package/dist/lib/mock-server/creative-ad-server/server.mjs.map +1 -1
- package/dist/lib/mock-server/creative-template/seed-data.mjs +0 -6
- package/dist/lib/mock-server/creative-template/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/creative-template/server.mjs +0 -6
- package/dist/lib/mock-server/creative-template/server.mjs.map +1 -1
- package/dist/lib/mock-server/index.mjs +0 -6
- package/dist/lib/mock-server/index.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/recipe.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/recipe.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/server.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/server.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/recipe.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/recipe.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/server.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/server.mjs.map +1 -1
- package/dist/lib/mock-server/sales-social/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-social/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-social/server.mjs +0 -6
- package/dist/lib/mock-server/sales-social/server.mjs.map +1 -1
- package/dist/lib/mock-server/scenario.mjs +0 -6
- package/dist/lib/mock-server/scenario.mjs.map +1 -1
- package/dist/lib/mock-server/signal-marketplace/seed-data.mjs +0 -6
- package/dist/lib/mock-server/signal-marketplace/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/signal-marketplace/server.mjs +0 -6
- package/dist/lib/mock-server/signal-marketplace/server.mjs.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/server.mjs +0 -6
- package/dist/lib/mock-server/sponsored-intelligence/server.mjs.map +1 -1
- package/dist/lib/net/address-guards.mjs +0 -6
- package/dist/lib/net/address-guards.mjs.map +1 -1
- package/dist/lib/net/index.mjs +0 -6
- package/dist/lib/net/index.mjs.map +1 -1
- package/dist/lib/net/ssrf-fetch.mjs +0 -6
- package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
- package/dist/lib/observability/index.mjs +0 -6
- package/dist/lib/observability/index.mjs.map +1 -1
- package/dist/lib/observability/tracing.mjs +6 -7
- package/dist/lib/observability/tracing.mjs.map +1 -1
- package/dist/lib/protocols/a2a.mjs +0 -6
- package/dist/lib/protocols/a2a.mjs.map +1 -1
- package/dist/lib/protocols/abort.mjs +0 -6
- package/dist/lib/protocols/abort.mjs.map +1 -1
- package/dist/lib/protocols/index.mjs +0 -6
- package/dist/lib/protocols/index.mjs.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +0 -6
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/protocols/mcp-tasks.mjs +0 -6
- package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
- package/dist/lib/protocols/mcp.mjs +0 -6
- package/dist/lib/protocols/mcp.mjs.map +1 -1
- package/dist/lib/protocols/rawResponseCapture.mjs +0 -6
- package/dist/lib/protocols/rawResponseCapture.mjs.map +1 -1
- package/dist/lib/protocols/responseSizeLimit.mjs +0 -6
- package/dist/lib/protocols/responseSizeLimit.mjs.map +1 -1
- package/dist/lib/protocols/transportDiagnostics.mjs +0 -6
- package/dist/lib/protocols/transportDiagnostics.mjs.map +1 -1
- package/dist/lib/registry/cursor-store.mjs +0 -6
- package/dist/lib/registry/cursor-store.mjs.map +1 -1
- package/dist/lib/registry/feed-stream.mjs +0 -6
- package/dist/lib/registry/feed-stream.mjs.map +1 -1
- package/dist/lib/registry/index.mjs +0 -6
- package/dist/lib/registry/index.mjs.map +1 -1
- package/dist/lib/registry/property-registry.mjs +0 -6
- package/dist/lib/registry/property-registry.mjs.map +1 -1
- package/dist/lib/registry/sync.mjs +0 -6
- package/dist/lib/registry/sync.mjs.map +1 -1
- package/dist/lib/registry/types.generated.mjs +0 -6
- package/dist/lib/registry/types.mjs +0 -6
- package/dist/lib/schemas/index.mjs +0 -6
- package/dist/lib/schemas/index.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/a2a-adapter.mjs +0 -6
- package/dist/lib/server/a2a-adapter.mjs.map +1 -1
- package/dist/lib/server/account-mode.mjs +0 -6
- package/dist/lib/server/account-mode.mjs.map +1 -1
- package/dist/lib/server/adcp-server.mjs +0 -6
- package/dist/lib/server/adcp-server.mjs.map +1 -1
- package/dist/lib/server/auth-introspection.mjs +0 -6
- package/dist/lib/server/auth-introspection.mjs.map +1 -1
- package/dist/lib/server/auth-signature.mjs +0 -6
- package/dist/lib/server/auth-signature.mjs.map +1 -1
- package/dist/lib/server/auth.mjs +0 -6
- package/dist/lib/server/auth.mjs.map +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +13 -0
- package/dist/lib/server/create-adcp-server.d.ts +13 -0
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +3 -2
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/create-adcp-server.mjs +3 -8
- package/dist/lib/server/create-adcp-server.mjs.map +1 -1
- package/dist/lib/server/credential-policy.mjs +0 -6
- package/dist/lib/server/credential-policy.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/memory.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/memory.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/pg.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/pg.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/redis.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/redis.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/index.mjs +0 -6
- package/dist/lib/server/ctx-metadata/index.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/store.mjs +0 -6
- package/dist/lib/server/ctx-metadata/store.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/wire-shape.mjs +0 -6
- package/dist/lib/server/ctx-metadata/wire-shape.mjs.map +1 -1
- package/dist/lib/server/decisioning/account.mjs +0 -6
- package/dist/lib/server/decisioning/account.mjs.map +1 -1
- package/dist/lib/server/decisioning/admin-router.mjs +0 -6
- package/dist/lib/server/decisioning/admin-router.mjs.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/assembly-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/async-outcome.mjs +0 -6
- package/dist/lib/server/decisioning/async-outcome.mjs.map +1 -1
- package/dist/lib/server/decisioning/buyer-agent.mjs +0 -6
- package/dist/lib/server/decisioning/buyer-agent.mjs.map +1 -1
- package/dist/lib/server/decisioning/capabilities.mjs +0 -6
- package/dist/lib/server/decisioning/capabilities.mjs.map +1 -1
- package/dist/lib/server/decisioning/compose.mjs +0 -6
- package/dist/lib/server/decisioning/compose.mjs.map +1 -1
- package/dist/lib/server/decisioning/context.mjs +0 -6
- package/dist/lib/server/decisioning/errors-typed.mjs +0 -6
- package/dist/lib/server/decisioning/errors-typed.mjs.map +1 -1
- package/dist/lib/server/decisioning/helpers.mjs +0 -6
- package/dist/lib/server/decisioning/helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/index.mjs +0 -6
- package/dist/lib/server/decisioning/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/list-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/manifest-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/pagination.mjs +0 -6
- package/dist/lib/server/decisioning/platform-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/platform-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/platform.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/dispatch.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/dispatch.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/store.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/store.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/types.mjs.map +1 -1
- package/dist/lib/server/decisioning/resolve-presets.mjs +0 -6
- package/dist/lib/server/decisioning/resolve-presets.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/entity-hydration.generated.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/entity-hydration.generated.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/observed-modes.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/observed-modes.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/task-registry.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/task-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/to-context.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/to-context.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/validate-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/specialisms/audiences.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/brand-rights.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/campaign-governance.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/content-standards.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/creative.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/lists.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/sales.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/signals.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.mjs +0 -6
- package/dist/lib/server/decisioning/start-time.mjs +0 -6
- package/dist/lib/server/decisioning/start-time.mjs.map +1 -1
- package/dist/lib/server/decisioning/status-changes.mjs +0 -6
- package/dist/lib/server/decisioning/status-changes.mjs.map +1 -1
- package/dist/lib/server/decisioning/status-mappers.mjs +0 -6
- package/dist/lib/server/decisioning/status-mappers.mjs.map +1 -1
- package/dist/lib/server/decisioning/tenant-registry.mjs +0 -6
- package/dist/lib/server/decisioning/tenant-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/tenant-store.mjs +0 -6
- package/dist/lib/server/decisioning/tenant-store.mjs.map +1 -1
- package/dist/lib/server/decisioning/validate-specialisms.mjs +0 -6
- package/dist/lib/server/decisioning/validate-specialisms.mjs.map +1 -1
- package/dist/lib/server/dynamic-registry.mjs +0 -6
- package/dist/lib/server/dynamic-registry.mjs.map +1 -1
- package/dist/lib/server/envelope-allowlist.mjs +0 -6
- package/dist/lib/server/envelope-allowlist.mjs.map +1 -1
- package/dist/lib/server/error-arm-tools.mjs +5 -6
- package/dist/lib/server/error-arm-tools.mjs.map +1 -1
- package/dist/lib/server/errors.mjs +0 -6
- package/dist/lib/server/errors.mjs.map +1 -1
- package/dist/lib/server/example-tld-guard.mjs +0 -6
- package/dist/lib/server/example-tld-guard.mjs.map +1 -1
- package/dist/lib/server/express-adapter.mjs +0 -6
- package/dist/lib/server/express-adapter.mjs.map +1 -1
- package/dist/lib/server/governance.mjs +0 -6
- package/dist/lib/server/governance.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/lazy.mjs +0 -6
- package/dist/lib/server/idempotency/backends/lazy.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/memory.mjs +0 -6
- package/dist/lib/server/idempotency/backends/memory.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/pg.mjs +0 -6
- package/dist/lib/server/idempotency/backends/pg.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/redis.mjs +0 -6
- package/dist/lib/server/idempotency/backends/redis.mjs.map +1 -1
- package/dist/lib/server/idempotency/index.mjs +0 -6
- package/dist/lib/server/idempotency/index.mjs.map +1 -1
- package/dist/lib/server/idempotency/store.mjs +0 -6
- package/dist/lib/server/idempotency/store.mjs.map +1 -1
- package/dist/lib/server/index.d.mts +1 -1
- package/dist/lib/server/index.d.ts +1 -1
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/index.mjs +0 -6
- package/dist/lib/server/index.mjs.map +1 -1
- package/dist/lib/server/legacy/v5/index.d.mts +1 -1
- package/dist/lib/server/legacy/v5/index.d.ts +1 -1
- package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
- package/dist/lib/server/legacy/v5/index.js.map +1 -1
- package/dist/lib/server/legacy/v5/index.mjs +0 -6
- package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
- package/dist/lib/server/mcp-modern-server.mjs +0 -6
- package/dist/lib/server/mcp-modern-server.mjs.map +1 -1
- package/dist/lib/server/media-buy-actions.mjs +0 -6
- package/dist/lib/server/media-buy-actions.mjs.map +1 -1
- package/dist/lib/server/media-buy-helpers.mjs +0 -6
- package/dist/lib/server/media-buy-helpers.mjs.map +1 -1
- package/dist/lib/server/media-buy-store.mjs +0 -6
- package/dist/lib/server/media-buy-store.mjs.map +1 -1
- package/dist/lib/server/normalize-errors.mjs +0 -6
- package/dist/lib/server/normalize-errors.mjs.map +1 -1
- package/dist/lib/server/operational-platform.mjs +0 -6
- package/dist/lib/server/operational-platform.mjs.map +1 -1
- package/dist/lib/server/pick-safe-details.mjs +0 -6
- package/dist/lib/server/pick-safe-details.mjs.map +1 -1
- package/dist/lib/server/pin-and-bind-fetch.mjs +0 -6
- package/dist/lib/server/pin-and-bind-fetch.mjs.map +1 -1
- package/dist/lib/server/postgres-state-store.mjs +0 -6
- package/dist/lib/server/postgres-state-store.mjs.map +1 -1
- package/dist/lib/server/postgres-task-store.mjs +0 -6
- package/dist/lib/server/postgres-task-store.mjs.map +1 -1
- package/dist/lib/server/product-defaults.mjs +0 -6
- package/dist/lib/server/product-defaults.mjs.map +1 -1
- package/dist/lib/server/redact.mjs +0 -6
- package/dist/lib/server/redact.mjs.map +1 -1
- package/dist/lib/server/responses.mjs +0 -6
- package/dist/lib/server/responses.mjs.map +1 -1
- package/dist/lib/server/serve.mjs +0 -6
- package/dist/lib/server/serve.mjs.map +1 -1
- package/dist/lib/server/socket-mode/conformance-client.mjs +0 -6
- package/dist/lib/server/socket-mode/conformance-client.mjs.map +1 -1
- package/dist/lib/server/socket-mode/index.mjs +0 -6
- package/dist/lib/server/socket-mode/index.mjs.map +1 -1
- package/dist/lib/server/socket-mode/ws-transport.mjs +0 -6
- package/dist/lib/server/socket-mode/ws-transport.mjs.map +1 -1
- package/dist/lib/server/state-machine.mjs +0 -6
- package/dist/lib/server/state-machine.mjs.map +1 -1
- package/dist/lib/server/state-store.mjs +0 -6
- package/dist/lib/server/state-store.mjs.map +1 -1
- package/dist/lib/server/structured-serialize.mjs +0 -6
- package/dist/lib/server/structured-serialize.mjs.map +1 -1
- package/dist/lib/server/targeting-helpers.mjs +0 -6
- package/dist/lib/server/targeting-helpers.mjs.map +1 -1
- package/dist/lib/server/tasks.mjs +0 -6
- package/dist/lib/server/tasks.mjs.map +1 -1
- package/dist/lib/server/test-controller-bridge.mjs +0 -6
- package/dist/lib/server/test-controller-bridge.mjs.map +1 -1
- package/dist/lib/server/test-controller.mjs +0 -6
- package/dist/lib/server/test-controller.mjs.map +1 -1
- package/dist/lib/server/upstream-helpers.mjs +0 -6
- package/dist/lib/server/upstream-helpers.mjs.map +1 -1
- package/dist/lib/server/webhook-emitter.mjs +0 -6
- package/dist/lib/server/webhook-emitter.mjs.map +1 -1
- package/dist/lib/server/wire-safe.mjs +0 -6
- package/dist/lib/server/wire-safe.mjs.map +1 -1
- package/dist/lib/server/wire-spec-fields.generated.mjs +0 -6
- package/dist/lib/server/wire-spec-fields.generated.mjs.map +1 -1
- package/dist/lib/server/wrap-envelope.mjs +0 -6
- package/dist/lib/server/wrap-envelope.mjs.map +1 -1
- package/dist/lib/signing/agent-context.mjs +0 -6
- package/dist/lib/signing/agent-context.mjs.map +1 -1
- package/dist/lib/signing/agent-fetch.mjs +0 -6
- package/dist/lib/signing/agent-fetch.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/canonicalize.mjs +0 -6
- package/dist/lib/signing/agent-resolver/canonicalize.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/capabilities-types.mjs +0 -6
- package/dist/lib/signing/agent-resolver/capabilities-types.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/consistency.mjs +0 -6
- package/dist/lib/signing/agent-resolver/consistency.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/errors.mjs +0 -6
- package/dist/lib/signing/agent-resolver/errors.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/etld.mjs +0 -6
- package/dist/lib/signing/agent-resolver/etld.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +0 -6
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/index.mjs +0 -6
- package/dist/lib/signing/agent-resolver/index.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/jwks-set.mjs +0 -6
- package/dist/lib/signing/agent-resolver/jwks-set.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs +0 -6
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/select-agent.mjs +0 -6
- package/dist/lib/signing/agent-resolver/select-agent.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/strict-json.mjs +0 -6
- package/dist/lib/signing/agent-resolver/strict-json.mjs.map +1 -1
- package/dist/lib/signing/brand-jwks.mjs +0 -6
- package/dist/lib/signing/brand-jwks.mjs.map +1 -1
- package/dist/lib/signing/canonicalize.mjs +0 -6
- package/dist/lib/signing/canonicalize.mjs.map +1 -1
- package/dist/lib/signing/capability-cache.mjs +0 -6
- package/dist/lib/signing/capability-cache.mjs.map +1 -1
- package/dist/lib/signing/capability-priming.mjs +0 -6
- package/dist/lib/signing/capability-priming.mjs.map +1 -1
- package/dist/lib/signing/client.mjs +0 -6
- package/dist/lib/signing/client.mjs.map +1 -1
- package/dist/lib/signing/content-digest.mjs +0 -6
- package/dist/lib/signing/content-digest.mjs.map +1 -1
- package/dist/lib/signing/crypto.mjs +0 -6
- package/dist/lib/signing/crypto.mjs.map +1 -1
- package/dist/lib/signing/ecdsa-encoding.mjs +0 -6
- package/dist/lib/signing/ecdsa-encoding.mjs.map +1 -1
- package/dist/lib/signing/errors.mjs +0 -6
- package/dist/lib/signing/errors.mjs.map +1 -1
- package/dist/lib/signing/fetch-async.mjs +0 -6
- package/dist/lib/signing/fetch-async.mjs.map +1 -1
- package/dist/lib/signing/fetch.mjs +0 -6
- package/dist/lib/signing/fetch.mjs.map +1 -1
- package/dist/lib/signing/index.mjs +0 -6
- package/dist/lib/signing/index.mjs.map +1 -1
- package/dist/lib/signing/jwks-helpers.mjs +0 -6
- package/dist/lib/signing/jwks-helpers.mjs.map +1 -1
- package/dist/lib/signing/jwks-https.mjs +0 -6
- package/dist/lib/signing/jwks-https.mjs.map +1 -1
- package/dist/lib/signing/jwks.mjs +0 -6
- package/dist/lib/signing/jwks.mjs.map +1 -1
- package/dist/lib/signing/middleware.mjs +0 -6
- package/dist/lib/signing/middleware.mjs.map +1 -1
- package/dist/lib/signing/parser.mjs +0 -6
- package/dist/lib/signing/parser.mjs.map +1 -1
- package/dist/lib/signing/postgres-replay-store.mjs +0 -6
- package/dist/lib/signing/postgres-replay-store.mjs.map +1 -1
- package/dist/lib/signing/protocol-response.mjs +0 -6
- package/dist/lib/signing/protocol-response.mjs.map +1 -1
- package/dist/lib/signing/provider.mjs +0 -6
- package/dist/lib/signing/redis-replay-store.mjs +0 -6
- package/dist/lib/signing/redis-replay-store.mjs.map +1 -1
- package/dist/lib/signing/replay.mjs +0 -6
- package/dist/lib/signing/replay.mjs.map +1 -1
- package/dist/lib/signing/request-context.mjs +0 -6
- package/dist/lib/signing/request-context.mjs.map +1 -1
- package/dist/lib/signing/revocation-https.mjs +0 -6
- package/dist/lib/signing/revocation-https.mjs.map +1 -1
- package/dist/lib/signing/revocation.mjs +0 -6
- package/dist/lib/signing/revocation.mjs.map +1 -1
- package/dist/lib/signing/server.mjs +0 -6
- package/dist/lib/signing/server.mjs.map +1 -1
- package/dist/lib/signing/signer-async.mjs +0 -6
- package/dist/lib/signing/signer-async.mjs.map +1 -1
- package/dist/lib/signing/signer.mjs +0 -6
- package/dist/lib/signing/signer.mjs.map +1 -1
- package/dist/lib/signing/testing.mjs +0 -6
- package/dist/lib/signing/testing.mjs.map +1 -1
- package/dist/lib/signing/types.mjs +0 -6
- package/dist/lib/signing/types.mjs.map +1 -1
- package/dist/lib/signing/verifier.mjs +0 -6
- package/dist/lib/signing/verifier.mjs.map +1 -1
- package/dist/lib/signing/webhook-auth-detection.mjs +0 -6
- package/dist/lib/signing/webhook-auth-detection.mjs.map +1 -1
- package/dist/lib/signing/webhook-verifier.mjs +0 -6
- package/dist/lib/signing/webhook-verifier.mjs.map +1 -1
- package/dist/lib/storage/MemoryStorage.mjs +0 -6
- package/dist/lib/storage/MemoryStorage.mjs.map +1 -1
- package/dist/lib/storage/interfaces.mjs +0 -6
- package/dist/lib/substitution/encoder/SubstitutionEncoder.mjs +0 -6
- package/dist/lib/substitution/encoder/SubstitutionEncoder.mjs.map +1 -1
- package/dist/lib/substitution/encoder/index.mjs +0 -6
- package/dist/lib/substitution/encoder/index.mjs.map +1 -1
- package/dist/lib/substitution/index.mjs +0 -6
- package/dist/lib/substitution/index.mjs.map +1 -1
- package/dist/lib/substitution/observer/SubstitutionObserver.mjs +0 -6
- package/dist/lib/substitution/observer/SubstitutionObserver.mjs.map +1 -1
- package/dist/lib/substitution/observer/alignment.mjs +0 -6
- package/dist/lib/substitution/observer/alignment.mjs.map +1 -1
- package/dist/lib/substitution/observer/assertions.mjs +0 -6
- package/dist/lib/substitution/observer/assertions.mjs.map +1 -1
- package/dist/lib/substitution/observer/html-parser.mjs +0 -6
- package/dist/lib/substitution/observer/html-parser.mjs.map +1 -1
- package/dist/lib/substitution/observer/index.mjs +0 -6
- package/dist/lib/substitution/observer/index.mjs.map +1 -1
- package/dist/lib/substitution/observer/ssrf.mjs +0 -6
- package/dist/lib/substitution/observer/ssrf.mjs.map +1 -1
- package/dist/lib/substitution/rfc3986.mjs +0 -6
- package/dist/lib/substitution/rfc3986.mjs.map +1 -1
- package/dist/lib/substitution/translate.mjs +0 -6
- package/dist/lib/substitution/translate.mjs.map +1 -1
- package/dist/lib/substitution/types.mjs +0 -6
- package/dist/lib/substitution/vectors.mjs +0 -6
- package/dist/lib/substitution/vectors.mjs.map +1 -1
- package/dist/lib/testing/agent-tester.mjs +0 -6
- package/dist/lib/testing/agent-tester.mjs.map +1 -1
- package/dist/lib/testing/client.mjs +0 -6
- package/dist/lib/testing/client.mjs.map +1 -1
- package/dist/lib/testing/compliance/briefs.mjs +0 -6
- package/dist/lib/testing/compliance/briefs.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +0 -6
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/compliance/index.mjs +0 -6
- package/dist/lib/testing/compliance/index.mjs.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.mjs +0 -6
- package/dist/lib/testing/compliance/spec-conformance.mjs.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.mjs +0 -6
- package/dist/lib/testing/compliance/storyboard-tracks.mjs.map +1 -1
- package/dist/lib/testing/compliance/summary.mjs +0 -6
- package/dist/lib/testing/compliance/summary.mjs.map +1 -1
- package/dist/lib/testing/compliance/types.mjs +0 -6
- package/dist/lib/testing/comply-controller.mjs +0 -6
- package/dist/lib/testing/comply-controller.mjs.map +1 -1
- package/dist/lib/testing/controller-assertions.mjs +0 -6
- package/dist/lib/testing/controller-assertions.mjs.map +1 -1
- package/dist/lib/testing/formatter.mjs +0 -6
- package/dist/lib/testing/formatter.mjs.map +1 -1
- package/dist/lib/testing/index.mjs +0 -6
- package/dist/lib/testing/index.mjs.map +1 -1
- package/dist/lib/testing/local-agent-runner.mjs +0 -6
- package/dist/lib/testing/local-agent-runner.mjs.map +1 -1
- package/dist/lib/testing/orchestrator.mjs +0 -6
- package/dist/lib/testing/orchestrator.mjs.map +1 -1
- package/dist/lib/testing/personas/index.mjs +0 -6
- package/dist/lib/testing/personas/index.mjs.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.mjs +0 -6
- package/dist/lib/testing/scenarios/brand-rights.mjs.map +1 -1
- package/dist/lib/testing/scenarios/capabilities.mjs +0 -6
- package/dist/lib/testing/scenarios/capabilities.mjs.map +1 -1
- package/dist/lib/testing/scenarios/creative.mjs +0 -6
- package/dist/lib/testing/scenarios/creative.mjs.map +1 -1
- package/dist/lib/testing/scenarios/deterministic.mjs +0 -6
- package/dist/lib/testing/scenarios/deterministic.mjs.map +1 -1
- package/dist/lib/testing/scenarios/discovery.mjs +0 -6
- package/dist/lib/testing/scenarios/discovery.mjs.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.mjs +0 -6
- package/dist/lib/testing/scenarios/edge-cases.mjs.map +1 -1
- package/dist/lib/testing/scenarios/error-compliance.mjs +0 -6
- package/dist/lib/testing/scenarios/error-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/governance.mjs +0 -6
- package/dist/lib/testing/scenarios/governance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/health.mjs +0 -6
- package/dist/lib/testing/scenarios/health.mjs.map +1 -1
- package/dist/lib/testing/scenarios/index.mjs +0 -6
- package/dist/lib/testing/scenarios/index.mjs.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.mjs +0 -6
- package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs +0 -6
- package/dist/lib/testing/scenarios/schema-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/signals.mjs +0 -6
- package/dist/lib/testing/scenarios/signals.mjs.map +1 -1
- package/dist/lib/testing/scenarios/sponsored-intelligence.mjs +0 -6
- package/dist/lib/testing/scenarios/sponsored-intelligence.mjs.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.mjs +0 -6
- package/dist/lib/testing/scenarios/trusted-match.mjs.map +1 -1
- package/dist/lib/testing/seed-merge.mjs +0 -6
- package/dist/lib/testing/seed-merge.mjs.map +1 -1
- package/dist/lib/testing/storyboard/agent-routing.mjs +0 -6
- package/dist/lib/testing/storyboard/agent-routing.mjs.map +1 -1
- package/dist/lib/testing/storyboard/assertions.mjs +0 -6
- package/dist/lib/testing/storyboard/assertions.mjs.map +1 -1
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs +0 -6
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs.map +1 -1
- package/dist/lib/testing/storyboard/compliance.mjs +5 -6
- package/dist/lib/testing/storyboard/compliance.mjs.map +1 -1
- package/dist/lib/testing/storyboard/context.mjs +0 -6
- package/dist/lib/testing/storyboard/context.mjs.map +1 -1
- package/dist/lib/testing/storyboard/default-invariants.mjs +0 -6
- package/dist/lib/testing/storyboard/default-invariants.mjs.map +1 -1
- package/dist/lib/testing/storyboard/index.mjs +0 -6
- package/dist/lib/testing/storyboard/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/junit.mjs +0 -6
- package/dist/lib/testing/storyboard/junit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/loader.mjs +0 -6
- package/dist/lib/testing/storyboard/loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/parallel-dispatch.mjs +0 -6
- package/dist/lib/testing/storyboard/parallel-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/path.mjs +0 -6
- package/dist/lib/testing/storyboard/path.mjs.map +1 -1
- package/dist/lib/testing/storyboard/probes.mjs +0 -6
- package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
- package/dist/lib/testing/storyboard/rate-limit-trip.mjs +0 -6
- package/dist/lib/testing/storyboard/rate-limit-trip.mjs.map +1 -1
- package/dist/lib/testing/storyboard/rejection-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/rejection-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-builder.mjs +0 -6
- package/dist/lib/testing/storyboard/request-builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/grader.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/index.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/probe.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/synthesize.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/synthesize.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/test-kit.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/test-kit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/types.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +0 -6
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/sandbox-entities.mjs +0 -6
- package/dist/lib/testing/storyboard/sandbox-entities.mjs.map +1 -1
- package/dist/lib/testing/storyboard/seeding.mjs +0 -6
- package/dist/lib/testing/storyboard/seeding.mjs.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs +0 -6
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/index.mjs +0 -6
- package/dist/lib/testing/storyboard/signer-grader/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/types.mjs +0 -6
- package/dist/lib/testing/storyboard/strict-validation-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/strict-validation-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/task-map.mjs +0 -6
- package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
- package/dist/lib/testing/storyboard/test-kit.mjs +0 -6
- package/dist/lib/testing/storyboard/test-kit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/types.mjs +0 -6
- package/dist/lib/testing/storyboard/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/validations.mjs +0 -6
- package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
- package/dist/lib/testing/storyboard/webhook-assertions.mjs +0 -6
- package/dist/lib/testing/storyboard/webhook-assertions.mjs.map +1 -1
- package/dist/lib/testing/storyboard/webhook-receiver.mjs +0 -6
- package/dist/lib/testing/storyboard/webhook-receiver.mjs.map +1 -1
- package/dist/lib/testing/stubs/governance-agent-stub.mjs +0 -6
- package/dist/lib/testing/stubs/governance-agent-stub.mjs.map +1 -1
- package/dist/lib/testing/stubs/index.mjs +0 -6
- package/dist/lib/testing/stubs/index.mjs.map +1 -1
- package/dist/lib/testing/test-controller.mjs +0 -6
- package/dist/lib/testing/test-controller.mjs.map +1 -1
- package/dist/lib/testing/test-helpers.mjs +0 -6
- package/dist/lib/testing/test-helpers.mjs.map +1 -1
- package/dist/lib/testing/types.mjs +0 -6
- package/dist/lib/types/adcp.mjs +0 -6
- package/dist/lib/types/adcp.mjs.map +1 -1
- package/dist/lib/types/asset-instances.mjs +0 -6
- package/dist/lib/types/compat.mjs +0 -6
- package/dist/lib/types/compat.mjs.map +1 -1
- package/dist/lib/types/core.generated.mjs +0 -6
- package/dist/lib/types/enums.generated.mjs +0 -6
- package/dist/lib/types/enums.generated.mjs.map +1 -1
- package/dist/lib/types/error-codes.mjs +0 -6
- package/dist/lib/types/error-codes.mjs.map +1 -1
- package/dist/lib/types/error-details.aliases.mjs +0 -6
- package/dist/lib/types/error-details.aliases.mjs.map +1 -1
- package/dist/lib/types/format-asset-slots.mjs +0 -6
- package/dist/lib/types/forward-compat-error-codes.mjs +0 -6
- package/dist/lib/types/forward-compat-error-codes.mjs.map +1 -1
- package/dist/lib/types/index.mjs +0 -6
- package/dist/lib/types/index.mjs.map +1 -1
- package/dist/lib/types/inline-enums.aliases.mjs +0 -6
- package/dist/lib/types/inline-enums.aliases.mjs.map +1 -1
- package/dist/lib/types/inline-enums.generated.mjs +0 -6
- package/dist/lib/types/inline-enums.generated.mjs.map +1 -1
- package/dist/lib/types/manifest.generated.mjs +0 -6
- package/dist/lib/types/manifest.generated.mjs.map +1 -1
- package/dist/lib/types/schemas.generated.mjs +0 -6
- package/dist/lib/types/schemas.generated.mjs.map +1 -1
- package/dist/lib/types/server-payload-aliases.mjs +0 -6
- package/dist/lib/types/server-payload.mjs +0 -6
- package/dist/lib/types/sync-rows.mjs +0 -6
- package/dist/lib/types/tools.generated.mjs +0 -6
- package/dist/lib/types/v2-5/index.mjs +0 -6
- package/dist/lib/types/v2-5/index.mjs.map +1 -1
- package/dist/lib/types/v2-5/tools.generated.mjs +0 -6
- package/dist/lib/types/v3-1-beta/index.mjs +0 -6
- package/dist/lib/types/v3-1-beta/index.mjs.map +1 -1
- package/dist/lib/types/v3-1-beta/tools.generated.mjs +0 -6
- package/dist/lib/types/wellknown-schemas.generated.mjs +0 -6
- package/dist/lib/types/wellknown-schemas.generated.mjs.map +1 -1
- package/dist/lib/upstream-recorder/constants.mjs +0 -6
- package/dist/lib/upstream-recorder/constants.mjs.map +1 -1
- package/dist/lib/upstream-recorder/index.mjs +0 -6
- package/dist/lib/upstream-recorder/index.mjs.map +1 -1
- package/dist/lib/upstream-recorder/recorder.mjs +0 -6
- package/dist/lib/upstream-recorder/recorder.mjs.map +1 -1
- package/dist/lib/upstream-recorder/types.mjs +0 -6
- package/dist/lib/upstream-recorder/types.mjs.map +1 -1
- package/dist/lib/utils/a2a-artifacts.mjs +0 -6
- package/dist/lib/utils/a2a-artifacts.mjs.map +1 -1
- package/dist/lib/utils/a2a-discovery.mjs +0 -6
- package/dist/lib/utils/a2a-discovery.mjs.map +1 -1
- package/dist/lib/utils/activation-key-builders.mjs +0 -6
- package/dist/lib/utils/activation-key-builders.mjs.map +1 -1
- package/dist/lib/utils/adcp-version-config.mjs +0 -6
- package/dist/lib/utils/adcp-version-config.mjs.map +1 -1
- package/dist/lib/utils/asset-builders.mjs +0 -6
- package/dist/lib/utils/asset-builders.mjs.map +1 -1
- package/dist/lib/utils/build-creative-return-builders.mjs +0 -6
- package/dist/lib/utils/build-creative-return-builders.mjs.map +1 -1
- package/dist/lib/utils/buyer-retry-policy.mjs +0 -6
- package/dist/lib/utils/buyer-retry-policy.mjs.map +1 -1
- package/dist/lib/utils/capabilities.mjs +0 -6
- package/dist/lib/utils/capabilities.mjs.map +1 -1
- package/dist/lib/utils/capability-rollups.mjs +0 -6
- package/dist/lib/utils/capability-rollups.mjs.map +1 -1
- package/dist/lib/utils/creative-adapter.mjs +0 -6
- package/dist/lib/utils/creative-adapter.mjs.map +1 -1
- package/dist/lib/utils/creative-delivery.mjs +0 -6
- package/dist/lib/utils/creative-delivery.mjs.map +1 -1
- package/dist/lib/utils/deprecation.mjs +0 -6
- package/dist/lib/utils/deprecation.mjs.map +1 -1
- package/dist/lib/utils/envelope-status-compat.mjs +0 -6
- package/dist/lib/utils/envelope-status-compat.mjs.map +1 -1
- package/dist/lib/utils/error-extraction.mjs +0 -6
- package/dist/lib/utils/error-extraction.mjs.map +1 -1
- package/dist/lib/utils/extract-result.mjs +0 -6
- package/dist/lib/utils/extract-result.mjs.map +1 -1
- package/dist/lib/utils/format-asset-slot-builders.mjs +0 -6
- package/dist/lib/utils/format-asset-slot-builders.mjs.map +1 -1
- package/dist/lib/utils/format-assets.mjs +0 -6
- package/dist/lib/utils/format-assets.mjs.map +1 -1
- package/dist/lib/utils/format-render-builders.mjs +0 -6
- package/dist/lib/utils/format-render-builders.mjs.map +1 -1
- package/dist/lib/utils/format-renders.mjs +0 -6
- package/dist/lib/utils/format-renders.mjs.map +1 -1
- package/dist/lib/utils/get-products-cache-scope.mjs +0 -6
- package/dist/lib/utils/get-products-cache-scope.mjs.map +1 -1
- package/dist/lib/utils/glob.mjs +0 -6
- package/dist/lib/utils/glob.mjs.map +1 -1
- package/dist/lib/utils/global-async-local-storage.mjs +0 -6
- package/dist/lib/utils/global-async-local-storage.mjs.map +1 -1
- package/dist/lib/utils/idempotency.mjs +0 -6
- package/dist/lib/utils/idempotency.mjs.map +1 -1
- package/dist/lib/utils/index.mjs +0 -6
- package/dist/lib/utils/index.mjs.map +1 -1
- package/dist/lib/utils/jcs.mjs +0 -6
- package/dist/lib/utils/jcs.mjs.map +1 -1
- package/dist/lib/utils/json-depth.mjs +0 -6
- package/dist/lib/utils/json-depth.mjs.map +1 -1
- package/dist/lib/utils/logger.mjs +0 -6
- package/dist/lib/utils/logger.mjs.map +1 -1
- package/dist/lib/utils/media-buy-delivery-notification-builders.mjs +0 -6
- package/dist/lib/utils/media-buy-delivery-notification-builders.mjs.map +1 -1
- package/dist/lib/utils/media-buy-status.mjs +0 -6
- package/dist/lib/utils/media-buy-status.mjs.map +1 -1
- package/dist/lib/utils/pagination.mjs +0 -6
- package/dist/lib/utils/pagination.mjs.map +1 -1
- package/dist/lib/utils/preview-creative-builders.mjs +0 -6
- package/dist/lib/utils/preview-creative-builders.mjs.map +1 -1
- package/dist/lib/utils/preview-normalizer.mjs +0 -6
- package/dist/lib/utils/preview-normalizer.mjs.map +1 -1
- package/dist/lib/utils/preview-utils.mjs +0 -6
- package/dist/lib/utils/preview-utils.mjs.map +1 -1
- package/dist/lib/utils/pricing-adapter.mjs +0 -6
- package/dist/lib/utils/pricing-adapter.mjs.map +1 -1
- package/dist/lib/utils/probe-policy.mjs +0 -6
- package/dist/lib/utils/probe-policy.mjs.map +1 -1
- package/dist/lib/utils/protocol-detection.mjs +0 -6
- package/dist/lib/utils/protocol-detection.mjs.map +1 -1
- package/dist/lib/utils/redact-secrets.mjs +0 -6
- package/dist/lib/utils/redact-secrets.mjs.map +1 -1
- package/dist/lib/utils/redis-default-prefix-warn.mjs +0 -6
- package/dist/lib/utils/redis-default-prefix-warn.mjs.map +1 -1
- package/dist/lib/utils/render-builders.mjs +0 -6
- package/dist/lib/utils/render-builders.mjs.map +1 -1
- package/dist/lib/utils/request-normalizer.mjs +0 -6
- package/dist/lib/utils/request-normalizer.mjs.map +1 -1
- package/dist/lib/utils/response-schemas.mjs +0 -6
- package/dist/lib/utils/response-schemas.mjs.map +1 -1
- package/dist/lib/utils/response-unwrapper.mjs +0 -6
- package/dist/lib/utils/response-unwrapper.mjs.map +1 -1
- package/dist/lib/utils/retry.mjs +0 -6
- package/dist/lib/utils/retry.mjs.map +1 -1
- package/dist/lib/utils/signal-discovery-helpers.mjs +0 -6
- package/dist/lib/utils/signal-discovery-helpers.mjs.map +1 -1
- package/dist/lib/utils/signal-id-builders.mjs +0 -6
- package/dist/lib/utils/signal-id-builders.mjs.map +1 -1
- package/dist/lib/utils/sync-creatives-adapter.mjs +0 -6
- package/dist/lib/utils/sync-creatives-adapter.mjs.map +1 -1
- package/dist/lib/utils/task-state.mjs +0 -6
- package/dist/lib/utils/task-state.mjs.map +1 -1
- package/dist/lib/utils/tool-request-schemas.mjs +0 -6
- package/dist/lib/utils/tool-request-schemas.mjs.map +1 -1
- package/dist/lib/utils/typeGuards.mjs +0 -6
- package/dist/lib/utils/typeGuards.mjs.map +1 -1
- package/dist/lib/utils/union-errors.mjs +0 -6
- package/dist/lib/utils/union-errors.mjs.map +1 -1
- package/dist/lib/utils/validate-user-agent.mjs +0 -6
- package/dist/lib/utils/validate-user-agent.mjs.map +1 -1
- package/dist/lib/v2/format-schema/fetch.mjs +0 -6
- package/dist/lib/v2/format-schema/fetch.mjs.map +1 -1
- package/dist/lib/v2/format-schema/index.mjs +0 -6
- package/dist/lib/v2/format-schema/index.mjs.map +1 -1
- package/dist/lib/v2/format-schema/regex-safety.mjs +0 -6
- package/dist/lib/v2/format-schema/regex-safety.mjs.map +1 -1
- package/dist/lib/v2/format-schema/resolver.mjs +7 -8
- package/dist/lib/v2/format-schema/resolver.mjs.map +1 -1
- package/dist/lib/v2/format-schema/sandbox-refs.mjs +0 -6
- package/dist/lib/v2/format-schema/sandbox-refs.mjs.map +1 -1
- package/dist/lib/v2/projection/augment-response.mjs +0 -6
- package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
- package/dist/lib/v2/projection/builders.mjs +0 -6
- package/dist/lib/v2/projection/builders.mjs.map +1 -1
- package/dist/lib/v2/projection/cache-versions.mjs +0 -6
- package/dist/lib/v2/projection/cache-versions.mjs.map +1 -1
- package/dist/lib/v2/projection/canonical-properties.mjs +5 -6
- package/dist/lib/v2/projection/canonical-properties.mjs.map +1 -1
- package/dist/lib/v2/projection/catalog.mjs +5 -6
- package/dist/lib/v2/projection/catalog.mjs.map +1 -1
- package/dist/lib/v2/projection/constants.mjs +0 -6
- package/dist/lib/v2/projection/constants.mjs.map +1 -1
- package/dist/lib/v2/projection/index.mjs +0 -6
- package/dist/lib/v2/projection/index.mjs.map +1 -1
- package/dist/lib/v2/projection/registry.mjs +5 -6
- package/dist/lib/v2/projection/registry.mjs.map +1 -1
- package/dist/lib/v2/projection/types.mjs +0 -6
- package/dist/lib/v2/projection/v1-to-v2.mjs +0 -6
- package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.mjs +0 -6
- package/dist/lib/v2/projection/v2-to-v1.mjs.map +1 -1
- package/dist/lib/v2/projection/write-side.mjs +0 -6
- package/dist/lib/v2/projection/write-side.mjs.map +1 -1
- package/dist/lib/v2/publisher-catalog/formats.mjs +0 -6
- package/dist/lib/v2/publisher-catalog/formats.mjs.map +1 -1
- package/dist/lib/v2/publisher-catalog/index.mjs +0 -6
- package/dist/lib/v2/publisher-catalog/index.mjs.map +1 -1
- package/dist/lib/validation/client-hooks.mjs +0 -6
- package/dist/lib/validation/client-hooks.mjs.map +1 -1
- package/dist/lib/validation/hints.mjs +0 -6
- package/dist/lib/validation/hints.mjs.map +1 -1
- package/dist/lib/validation/index.mjs +0 -6
- package/dist/lib/validation/index.mjs.map +1 -1
- package/dist/lib/validation/schema-errors.mjs +0 -6
- package/dist/lib/validation/schema-errors.mjs.map +1 -1
- package/dist/lib/validation/schema-loader.mjs +5 -6
- package/dist/lib/validation/schema-loader.mjs.map +1 -1
- package/dist/lib/validation/schema-validator.mjs +0 -6
- package/dist/lib/validation/schema-validator.mjs.map +1 -1
- package/dist/lib/validation/sync-creatives.mjs +0 -6
- package/dist/lib/validation/sync-creatives.mjs.map +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.js.map +1 -1
- package/dist/lib/version.mjs +3 -9
- package/dist/lib/version.mjs.map +1 -1
- package/dist/lib/webhooks/index.mjs +0 -6
- package/dist/lib/webhooks/index.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/index.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/index.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/sync.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/sync.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/types.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/webhook-notification.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/webhook-notification.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/canonicalize.ts"],"sourcesContent":["/**\n * Origin canonicalization for the brand_json_url verifier algorithm.\n * `security.mdx` §\"Quickstart: implement a `brand_json_url`-based verifier\"\n * step 3 mandates: ASCII-lowercase the host, then convert to IDNA-2008\n * A-label (Punycode) form before byte-equality. A non-canonical comparison\n * (raw `Example.COM` vs `example.com`, or U-label vs A-label) silently\n * rejects legitimate traffic.\n *\n * Node's WHATWG URL parser already performs IDNA-2008 A-label conversion\n * and ASCII-lowercases the host on construction (`new URL('https://Bücher.example/').hostname`\n * returns `xn--bcher-kva.example`). We delegate to it rather than vendoring\n * an IDNA implementation — the rule is \"use the platform's IDNA-2008\", not\n * \"ship our own\".\n */\n\nexport class CanonicalizeError extends Error {\n constructor(\n message: string,\n readonly meta: { input: string }\n ) {\n super(message);\n this.name = 'CanonicalizeError';\n }\n}\n\n/**\n * Canonicalize a hostname (no scheme, no port, no path). Throws on inputs\n * that the URL parser rejects.\n */\nexport function canonicalizeHost(host: string): string {\n if (typeof host !== 'string' || host.length === 0) {\n throw new CanonicalizeError('Empty host', { input: host });\n }\n try {\n const u = new URL(`https://${host}/`);\n return u.hostname;\n } catch {\n throw new CanonicalizeError(`Invalid host: ${host}`, { input: host });\n }\n}\n\n/**\n * Canonicalize the origin component (scheme + host) of a URL or origin string.\n * Accepts either a full URL (`https://Example.COM/path`) or a bare origin\n * (`https://Example.COM`). Output is `https://example.com` form — scheme +\n * canonical host, no port when default, no path/query.\n *\n * Used for the step-7 `identity.key_origins` consistency check: the verifier\n * compares the canonicalized host of the resolved `jwks_uri` against the\n * canonicalized host of the declared `key_origins.{purpose}` value.\n */\nexport function canonicalizeOrigin(originOrUrl: string): string {\n if (typeof originOrUrl !== 'string' || originOrUrl.length === 0) {\n throw new CanonicalizeError('Empty origin', { input: originOrUrl });\n }\n let parsed: URL;\n try {\n parsed = new URL(originOrUrl);\n } catch {\n throw new CanonicalizeError(`Invalid origin: ${originOrUrl}`, { input: originOrUrl });\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new CanonicalizeError(`Unsupported scheme: ${parsed.protocol}`, { input: originOrUrl });\n }\n const port = parsed.port ? `:${parsed.port}` : '';\n return `${parsed.protocol}//${parsed.hostname}${port}`;\n}\n\n/**\n * Compare two origins (scheme + host + port) for byte-equality after\n * canonicalization. Wrong scheme, wrong port, or wrong host all return false.\n */\nexport function originsEqual(a: string, b: string): boolean {\n try {\n return canonicalizeOrigin(a) === canonicalizeOrigin(b);\n } catch {\n return false;\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/canonicalize.ts"],"sourcesContent":["/**\n * Origin canonicalization for the brand_json_url verifier algorithm.\n * `security.mdx` §\"Quickstart: implement a `brand_json_url`-based verifier\"\n * step 3 mandates: ASCII-lowercase the host, then convert to IDNA-2008\n * A-label (Punycode) form before byte-equality. A non-canonical comparison\n * (raw `Example.COM` vs `example.com`, or U-label vs A-label) silently\n * rejects legitimate traffic.\n *\n * Node's WHATWG URL parser already performs IDNA-2008 A-label conversion\n * and ASCII-lowercases the host on construction (`new URL('https://Bücher.example/').hostname`\n * returns `xn--bcher-kva.example`). We delegate to it rather than vendoring\n * an IDNA implementation — the rule is \"use the platform's IDNA-2008\", not\n * \"ship our own\".\n */\n\nexport class CanonicalizeError extends Error {\n constructor(\n message: string,\n readonly meta: { input: string }\n ) {\n super(message);\n this.name = 'CanonicalizeError';\n }\n}\n\n/**\n * Canonicalize a hostname (no scheme, no port, no path). Throws on inputs\n * that the URL parser rejects.\n */\nexport function canonicalizeHost(host: string): string {\n if (typeof host !== 'string' || host.length === 0) {\n throw new CanonicalizeError('Empty host', { input: host });\n }\n try {\n const u = new URL(`https://${host}/`);\n return u.hostname;\n } catch {\n throw new CanonicalizeError(`Invalid host: ${host}`, { input: host });\n }\n}\n\n/**\n * Canonicalize the origin component (scheme + host) of a URL or origin string.\n * Accepts either a full URL (`https://Example.COM/path`) or a bare origin\n * (`https://Example.COM`). Output is `https://example.com` form — scheme +\n * canonical host, no port when default, no path/query.\n *\n * Used for the step-7 `identity.key_origins` consistency check: the verifier\n * compares the canonicalized host of the resolved `jwks_uri` against the\n * canonicalized host of the declared `key_origins.{purpose}` value.\n */\nexport function canonicalizeOrigin(originOrUrl: string): string {\n if (typeof originOrUrl !== 'string' || originOrUrl.length === 0) {\n throw new CanonicalizeError('Empty origin', { input: originOrUrl });\n }\n let parsed: URL;\n try {\n parsed = new URL(originOrUrl);\n } catch {\n throw new CanonicalizeError(`Invalid origin: ${originOrUrl}`, { input: originOrUrl });\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new CanonicalizeError(`Unsupported scheme: ${parsed.protocol}`, { input: originOrUrl });\n }\n const port = parsed.port ? `:${parsed.port}` : '';\n return `${parsed.protocol}//${parsed.hostname}${port}`;\n}\n\n/**\n * Compare two origins (scheme + host + port) for byte-equality after\n * canonicalization. Wrong scheme, wrong port, or wrong host all return false.\n */\nexport function originsEqual(a: string, b: string): boolean {\n try {\n return canonicalizeOrigin(a) === canonicalizeOrigin(b);\n } catch {\n return false;\n }\n}\n"],"mappings":"AAeO,MAAM,0BAA0B,MAAM;AAAA,EAC3C,YACE,SACS,MACT;AACA,UAAM,OAAO;AAFJ;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EAJW;AAKb;AAMO,SAAS,iBAAiB,MAAsB;AACrD,MAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG;AACjD,UAAM,IAAI,kBAAkB,cAAc,EAAE,OAAO,KAAK,CAAC;AAAA,EAC3D;AACA,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,WAAW,IAAI,GAAG;AACpC,WAAO,EAAE;AAAA,EACX,QAAQ;AACN,UAAM,IAAI,kBAAkB,iBAAiB,IAAI,IAAI,EAAE,OAAO,KAAK,CAAC;AAAA,EACtE;AACF;AAYO,SAAS,mBAAmB,aAA6B;AAC9D,MAAI,OAAO,gBAAgB,YAAY,YAAY,WAAW,GAAG;AAC/D,UAAM,IAAI,kBAAkB,gBAAgB,EAAE,OAAO,YAAY,CAAC;AAAA,EACpE;AACA,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,WAAW;AAAA,EAC9B,QAAQ;AACN,UAAM,IAAI,kBAAkB,mBAAmB,WAAW,IAAI,EAAE,OAAO,YAAY,CAAC;AAAA,EACtF;AACA,MAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAS;AAC/D,UAAM,IAAI,kBAAkB,uBAAuB,OAAO,QAAQ,IAAI,EAAE,OAAO,YAAY,CAAC;AAAA,EAC9F;AACA,QAAM,OAAO,OAAO,OAAO,IAAI,OAAO,IAAI,KAAK;AAC/C,SAAO,GAAG,OAAO,QAAQ,KAAK,OAAO,QAAQ,GAAG,IAAI;AACtD;AAMO,SAAS,aAAa,GAAW,GAAoB;AAC1D,MAAI;AACF,WAAO,mBAAmB,CAAC,MAAM,mBAAmB,CAAC;AAAA,EACvD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
function readIdentityPosture(caps) {
|
|
8
2
|
if (!caps || typeof caps !== "object") return void 0;
|
|
9
3
|
const identity = caps.identity;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/capabilities-types.ts"],"sourcesContent":["/**\n * Forward-compat narrow access for `identity.brand_json_url` and the\n * surrounding fields from a `get_adcp_capabilities` response. The field\n * lands in the spec as a typed property in 3.1's additive minor (PR\n * adcontextprotocol/adcp#3690); 3.0.5 already accepts it on the wire by\n * relaxing `identity.additionalProperties` to `true`, but the generated\n * TS surface won't include it until codegen runs against a release that\n * declares it as a typed property. This file bridges the gap on both\n * sides:\n *\n * - **Verifier side**: `readBrandJsonUrl(caps)` peels the field out of\n * an unknown payload without forcing the caller into `as any`.\n * - **Publisher side**: TS sellers constructing their capabilities\n * response in 3.0.5+ can spread `IdentityPosture` over the generated\n * `identity` shape to set `brand_json_url` without losing types on\n * the rest of the block:\n *\n * ```ts\n * import type { IdentityPosture } from '@adcp/sdk/signing/server';\n * const identity: IdentityPosture = {\n * per_principal_key_isolation: true,\n * key_origins: { request_signing: 'https://keys.example.com' },\n * brand_json_url: 'https://example.com/.well-known/brand.json',\n * };\n * return { adcp: {...}, identity, ... };\n * ```\n *\n * This file goes away — replaced by the generated type — when the SDK's\n * schema pin moves to a release that includes the field as a typed\n * property.\n */\n\nimport type { GetAdCPCapabilitiesResponse } from '../../types/tools.generated';\n\nexport type IdentityKeyOriginPurpose = 'governance_signing' | 'request_signing' | 'webhook_signing' | 'tmp_signing';\n\nexport interface IdentityKeyOrigins {\n governance_signing?: string;\n request_signing?: string;\n webhook_signing?: string;\n tmp_signing?: string;\n}\n\nexport interface IdentityPosture {\n per_principal_key_isolation?: boolean;\n key_origins?: IdentityKeyOrigins;\n compromise_notification?: { emits?: boolean; accepts?: boolean };\n brand_json_url?: string;\n}\n\nexport type CapabilitiesWithBrandJsonUrl = GetAdCPCapabilitiesResponse & {\n identity?: IdentityPosture;\n};\n\nexport function readIdentityPosture(caps: unknown): IdentityPosture | undefined {\n if (!caps || typeof caps !== 'object') return undefined;\n const identity = (caps as { identity?: unknown }).identity;\n if (!identity || typeof identity !== 'object') return undefined;\n return identity as IdentityPosture;\n}\n\nexport function readBrandJsonUrl(caps: unknown): string | undefined {\n const identity = readIdentityPosture(caps);\n const value = identity?.brand_json_url;\n return typeof value === 'string' ? value : undefined;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/capabilities-types.ts"],"sourcesContent":["/**\n * Forward-compat narrow access for `identity.brand_json_url` and the\n * surrounding fields from a `get_adcp_capabilities` response. The field\n * lands in the spec as a typed property in 3.1's additive minor (PR\n * adcontextprotocol/adcp#3690); 3.0.5 already accepts it on the wire by\n * relaxing `identity.additionalProperties` to `true`, but the generated\n * TS surface won't include it until codegen runs against a release that\n * declares it as a typed property. This file bridges the gap on both\n * sides:\n *\n * - **Verifier side**: `readBrandJsonUrl(caps)` peels the field out of\n * an unknown payload without forcing the caller into `as any`.\n * - **Publisher side**: TS sellers constructing their capabilities\n * response in 3.0.5+ can spread `IdentityPosture` over the generated\n * `identity` shape to set `brand_json_url` without losing types on\n * the rest of the block:\n *\n * ```ts\n * import type { IdentityPosture } from '@adcp/sdk/signing/server';\n * const identity: IdentityPosture = {\n * per_principal_key_isolation: true,\n * key_origins: { request_signing: 'https://keys.example.com' },\n * brand_json_url: 'https://example.com/.well-known/brand.json',\n * };\n * return { adcp: {...}, identity, ... };\n * ```\n *\n * This file goes away — replaced by the generated type — when the SDK's\n * schema pin moves to a release that includes the field as a typed\n * property.\n */\n\nimport type { GetAdCPCapabilitiesResponse } from '../../types/tools.generated';\n\nexport type IdentityKeyOriginPurpose = 'governance_signing' | 'request_signing' | 'webhook_signing' | 'tmp_signing';\n\nexport interface IdentityKeyOrigins {\n governance_signing?: string;\n request_signing?: string;\n webhook_signing?: string;\n tmp_signing?: string;\n}\n\nexport interface IdentityPosture {\n per_principal_key_isolation?: boolean;\n key_origins?: IdentityKeyOrigins;\n compromise_notification?: { emits?: boolean; accepts?: boolean };\n brand_json_url?: string;\n}\n\nexport type CapabilitiesWithBrandJsonUrl = GetAdCPCapabilitiesResponse & {\n identity?: IdentityPosture;\n};\n\nexport function readIdentityPosture(caps: unknown): IdentityPosture | undefined {\n if (!caps || typeof caps !== 'object') return undefined;\n const identity = (caps as { identity?: unknown }).identity;\n if (!identity || typeof identity !== 'object') return undefined;\n return identity as IdentityPosture;\n}\n\nexport function readBrandJsonUrl(caps: unknown): string | undefined {\n const identity = readIdentityPosture(caps);\n const value = identity?.brand_json_url;\n return typeof value === 'string' ? value : undefined;\n}\n"],"mappings":"AAsDO,SAAS,oBAAoB,MAA4C;AAC9E,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,QAAM,WAAY,KAAgC;AAClD,MAAI,CAAC,YAAY,OAAO,aAAa,SAAU,QAAO;AACtD,SAAO;AACT;AAEO,SAAS,iBAAiB,MAAmC;AAClE,QAAM,WAAW,oBAAoB,IAAI;AACzC,QAAM,QAAQ,UAAU;AACxB,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { canonicalizeOrigin } from "./canonicalize.mjs";
|
|
8
2
|
function checkOriginConsistency(input) {
|
|
9
3
|
if (input.publisherPinned) return { ok: true };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/consistency.ts"],"sourcesContent":["/**\n * Step 7 of the brand_json_url discovery algorithm — `identity.key_origins`\n * consistency check (security.mdx §\"Discovering an agent's signing keys via\n * `brand_json_url`\" step 7). Two distinct rejections:\n *\n * `request_signature_key_origin_mismatch` — the resolved `jwks_uri` host\n * does not match the operator's declared `identity.key_origins.{purpose}`\n * value. Skipped only for the specific (agent, purpose, role) tuple\n * whose JWKS source was a publisher `adagents.json signing_keys` pin —\n * operator-side use of the same purpose is still checked.\n *\n * `request_signature_key_origin_missing` — the agent's capabilities\n * declare a signing posture for a purpose (e.g. `request_signing.supported_for`\n * non-empty, `webhook_signing.supported === true`) but the corresponding\n * `identity.key_origins.{purpose}` entry is absent.\n *\n * Both checks are pure: the caller supplies the inputs (which purposes are\n * declared, the declared origins, the resolved jwksUri, whether the resolution\n * came from a publisher pin), the function returns a typed result. No I/O.\n */\n\nimport { canonicalizeOrigin } from './canonicalize';\nimport type { IdentityKeyOrigins, IdentityKeyOriginPurpose, IdentityPosture } from './capabilities-types';\n\nexport type ConsistencyErrorCode = 'key_origin_mismatch' | 'key_origin_missing';\n\nexport interface KeyOriginMismatch {\n ok: false;\n code: 'key_origin_mismatch';\n purpose: IdentityKeyOriginPurpose;\n expected_origin: string;\n actual_origin: string;\n}\n\nexport interface KeyOriginMissing {\n ok: false;\n code: 'key_origin_missing';\n purpose: IdentityKeyOriginPurpose;\n posture: 'request_signing' | 'webhook_signing' | 'governance_signing' | 'tmp_signing';\n}\n\nexport type ConsistencyResult = { ok: true } | KeyOriginMismatch | KeyOriginMissing;\n\n/**\n * Inputs for the per-purpose origin check. The caller is responsible for\n * deciding whether `publisherPinned` is true — the carve-out applies only\n * to (agent, webhook-signing, sell-side) tuples whose JWKS came from an\n * `adagents.json signing_keys` pin. For request-signing, governance-signing,\n * TMP-signing, and buyer-side webhook receivers, `publisherPinned` MUST\n * always be false.\n */\nexport interface OriginCheckInput {\n purpose: IdentityKeyOriginPurpose;\n declaredOrigin: string | undefined;\n resolvedJwksUri: string;\n publisherPinned: boolean;\n}\n\n/**\n * Check origin consistency for a single (purpose, jwksUri) pair against the\n * declared `identity.key_origins.{purpose}` value. Returns `{ ok: true }`\n * when the check passes or is skipped (publisher pin); a typed mismatch\n * result on failure. `key_origin_missing` is NOT raised here — that is a\n * separate check (`checkRequiredOrigins`) because it depends on which\n * purposes the agent has declared signing for.\n */\nexport function checkOriginConsistency(input: OriginCheckInput): ConsistencyResult {\n if (input.publisherPinned) return { ok: true };\n if (!input.declaredOrigin) return { ok: true };\n let expected: string;\n let actual: string;\n try {\n expected = canonicalizeOrigin(input.declaredOrigin);\n actual = canonicalizeOrigin(input.resolvedJwksUri);\n } catch {\n return {\n ok: false,\n code: 'key_origin_mismatch',\n purpose: input.purpose,\n expected_origin: input.declaredOrigin,\n actual_origin: input.resolvedJwksUri,\n };\n }\n if (expected !== actual) {\n return {\n ok: false,\n code: 'key_origin_mismatch',\n purpose: input.purpose,\n expected_origin: expected,\n actual_origin: actual,\n };\n }\n return { ok: true };\n}\n\n/**\n * Determine which signing purposes the capabilities response declares.\n * Any of the following signal a declared purpose, per security.mdx\n * §\"Discovering an agent's signing keys via `brand_json_url`\" step 2:\n *\n * - `request_signing.supported_for` non-empty → `request_signing`\n * - `request_signing.required_for` non-empty → `request_signing`\n * - `webhook_signing.supported === true` → `webhook_signing`\n * - any field present under `identity.key_origins` → that purpose\n *\n * Governance-signing and TMP-signing declarations are agent-protocol-specific\n * and not reliably detectable from a generic capabilities walk; callers may\n * pass `extraDeclaredPurposes` for purposes they have detected externally.\n */\nexport function declaredSigningPurposes(\n capabilities: unknown,\n extraDeclaredPurposes: readonly IdentityKeyOriginPurpose[] = []\n): Set<IdentityKeyOriginPurpose> {\n const out = new Set<IdentityKeyOriginPurpose>(extraDeclaredPurposes);\n if (!capabilities || typeof capabilities !== 'object') return out;\n const caps = capabilities as Record<string, unknown>;\n const requestSigning = caps.request_signing as { supported_for?: unknown; required_for?: unknown } | undefined;\n if (Array.isArray(requestSigning?.supported_for) && requestSigning.supported_for.length > 0) {\n out.add('request_signing');\n }\n if (Array.isArray(requestSigning?.required_for) && requestSigning.required_for.length > 0) {\n out.add('request_signing');\n }\n const webhookSigning = caps.webhook_signing as { supported?: unknown } | undefined;\n if (webhookSigning?.supported === true) {\n out.add('webhook_signing');\n }\n const identity = caps.identity as IdentityPosture | undefined;\n if (identity?.key_origins) {\n for (const purpose of Object.keys(identity.key_origins) as IdentityKeyOriginPurpose[]) {\n if (identity.key_origins[purpose] !== undefined) out.add(purpose);\n }\n }\n return out;\n}\n\n/**\n * For every purpose the agent has declared signing for, confirm an\n * `identity.key_origins.{purpose}` entry is present. Returns the set of\n * missing purposes; the caller maps each onto `request_signature_key_origin_missing`\n * with `posture` set to the corresponding signing field name.\n */\nexport function checkRequiredOrigins(\n declaredPurposes: ReadonlySet<IdentityKeyOriginPurpose>,\n keyOrigins: IdentityKeyOrigins | undefined\n): KeyOriginMissing[] {\n const missing: KeyOriginMissing[] = [];\n for (const purpose of declaredPurposes) {\n const value = keyOrigins?.[purpose];\n if (typeof value !== 'string' || value.length === 0) {\n missing.push({ ok: false, code: 'key_origin_missing', purpose, posture: purpose });\n }\n }\n return missing;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/consistency.ts"],"sourcesContent":["/**\n * Step 7 of the brand_json_url discovery algorithm — `identity.key_origins`\n * consistency check (security.mdx §\"Discovering an agent's signing keys via\n * `brand_json_url`\" step 7). Two distinct rejections:\n *\n * `request_signature_key_origin_mismatch` — the resolved `jwks_uri` host\n * does not match the operator's declared `identity.key_origins.{purpose}`\n * value. Skipped only for the specific (agent, purpose, role) tuple\n * whose JWKS source was a publisher `adagents.json signing_keys` pin —\n * operator-side use of the same purpose is still checked.\n *\n * `request_signature_key_origin_missing` — the agent's capabilities\n * declare a signing posture for a purpose (e.g. `request_signing.supported_for`\n * non-empty, `webhook_signing.supported === true`) but the corresponding\n * `identity.key_origins.{purpose}` entry is absent.\n *\n * Both checks are pure: the caller supplies the inputs (which purposes are\n * declared, the declared origins, the resolved jwksUri, whether the resolution\n * came from a publisher pin), the function returns a typed result. No I/O.\n */\n\nimport { canonicalizeOrigin } from './canonicalize';\nimport type { IdentityKeyOrigins, IdentityKeyOriginPurpose, IdentityPosture } from './capabilities-types';\n\nexport type ConsistencyErrorCode = 'key_origin_mismatch' | 'key_origin_missing';\n\nexport interface KeyOriginMismatch {\n ok: false;\n code: 'key_origin_mismatch';\n purpose: IdentityKeyOriginPurpose;\n expected_origin: string;\n actual_origin: string;\n}\n\nexport interface KeyOriginMissing {\n ok: false;\n code: 'key_origin_missing';\n purpose: IdentityKeyOriginPurpose;\n posture: 'request_signing' | 'webhook_signing' | 'governance_signing' | 'tmp_signing';\n}\n\nexport type ConsistencyResult = { ok: true } | KeyOriginMismatch | KeyOriginMissing;\n\n/**\n * Inputs for the per-purpose origin check. The caller is responsible for\n * deciding whether `publisherPinned` is true — the carve-out applies only\n * to (agent, webhook-signing, sell-side) tuples whose JWKS came from an\n * `adagents.json signing_keys` pin. For request-signing, governance-signing,\n * TMP-signing, and buyer-side webhook receivers, `publisherPinned` MUST\n * always be false.\n */\nexport interface OriginCheckInput {\n purpose: IdentityKeyOriginPurpose;\n declaredOrigin: string | undefined;\n resolvedJwksUri: string;\n publisherPinned: boolean;\n}\n\n/**\n * Check origin consistency for a single (purpose, jwksUri) pair against the\n * declared `identity.key_origins.{purpose}` value. Returns `{ ok: true }`\n * when the check passes or is skipped (publisher pin); a typed mismatch\n * result on failure. `key_origin_missing` is NOT raised here — that is a\n * separate check (`checkRequiredOrigins`) because it depends on which\n * purposes the agent has declared signing for.\n */\nexport function checkOriginConsistency(input: OriginCheckInput): ConsistencyResult {\n if (input.publisherPinned) return { ok: true };\n if (!input.declaredOrigin) return { ok: true };\n let expected: string;\n let actual: string;\n try {\n expected = canonicalizeOrigin(input.declaredOrigin);\n actual = canonicalizeOrigin(input.resolvedJwksUri);\n } catch {\n return {\n ok: false,\n code: 'key_origin_mismatch',\n purpose: input.purpose,\n expected_origin: input.declaredOrigin,\n actual_origin: input.resolvedJwksUri,\n };\n }\n if (expected !== actual) {\n return {\n ok: false,\n code: 'key_origin_mismatch',\n purpose: input.purpose,\n expected_origin: expected,\n actual_origin: actual,\n };\n }\n return { ok: true };\n}\n\n/**\n * Determine which signing purposes the capabilities response declares.\n * Any of the following signal a declared purpose, per security.mdx\n * §\"Discovering an agent's signing keys via `brand_json_url`\" step 2:\n *\n * - `request_signing.supported_for` non-empty → `request_signing`\n * - `request_signing.required_for` non-empty → `request_signing`\n * - `webhook_signing.supported === true` → `webhook_signing`\n * - any field present under `identity.key_origins` → that purpose\n *\n * Governance-signing and TMP-signing declarations are agent-protocol-specific\n * and not reliably detectable from a generic capabilities walk; callers may\n * pass `extraDeclaredPurposes` for purposes they have detected externally.\n */\nexport function declaredSigningPurposes(\n capabilities: unknown,\n extraDeclaredPurposes: readonly IdentityKeyOriginPurpose[] = []\n): Set<IdentityKeyOriginPurpose> {\n const out = new Set<IdentityKeyOriginPurpose>(extraDeclaredPurposes);\n if (!capabilities || typeof capabilities !== 'object') return out;\n const caps = capabilities as Record<string, unknown>;\n const requestSigning = caps.request_signing as { supported_for?: unknown; required_for?: unknown } | undefined;\n if (Array.isArray(requestSigning?.supported_for) && requestSigning.supported_for.length > 0) {\n out.add('request_signing');\n }\n if (Array.isArray(requestSigning?.required_for) && requestSigning.required_for.length > 0) {\n out.add('request_signing');\n }\n const webhookSigning = caps.webhook_signing as { supported?: unknown } | undefined;\n if (webhookSigning?.supported === true) {\n out.add('webhook_signing');\n }\n const identity = caps.identity as IdentityPosture | undefined;\n if (identity?.key_origins) {\n for (const purpose of Object.keys(identity.key_origins) as IdentityKeyOriginPurpose[]) {\n if (identity.key_origins[purpose] !== undefined) out.add(purpose);\n }\n }\n return out;\n}\n\n/**\n * For every purpose the agent has declared signing for, confirm an\n * `identity.key_origins.{purpose}` entry is present. Returns the set of\n * missing purposes; the caller maps each onto `request_signature_key_origin_missing`\n * with `posture` set to the corresponding signing field name.\n */\nexport function checkRequiredOrigins(\n declaredPurposes: ReadonlySet<IdentityKeyOriginPurpose>,\n keyOrigins: IdentityKeyOrigins | undefined\n): KeyOriginMissing[] {\n const missing: KeyOriginMissing[] = [];\n for (const purpose of declaredPurposes) {\n const value = keyOrigins?.[purpose];\n if (typeof value !== 'string' || value.length === 0) {\n missing.push({ ok: false, code: 'key_origin_missing', purpose, posture: purpose });\n }\n }\n return missing;\n}\n"],"mappings":"AAqBA,SAAS,0BAA0B;AA6C5B,SAAS,uBAAuB,OAA4C;AACjF,MAAI,MAAM,gBAAiB,QAAO,EAAE,IAAI,KAAK;AAC7C,MAAI,CAAC,MAAM,eAAgB,QAAO,EAAE,IAAI,KAAK;AAC7C,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,eAAW,mBAAmB,MAAM,cAAc;AAClD,aAAS,mBAAmB,MAAM,eAAe;AAAA,EACnD,QAAQ;AACN,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,SAAS,MAAM;AAAA,MACf,iBAAiB,MAAM;AAAA,MACvB,eAAe,MAAM;AAAA,IACvB;AAAA,EACF;AACA,MAAI,aAAa,QAAQ;AACvB,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,SAAS,MAAM;AAAA,MACf,iBAAiB;AAAA,MACjB,eAAe;AAAA,IACjB;AAAA,EACF;AACA,SAAO,EAAE,IAAI,KAAK;AACpB;AAgBO,SAAS,wBACd,cACA,wBAA6D,CAAC,GAC/B;AAC/B,QAAM,MAAM,IAAI,IAA8B,qBAAqB;AACnE,MAAI,CAAC,gBAAgB,OAAO,iBAAiB,SAAU,QAAO;AAC9D,QAAM,OAAO;AACb,QAAM,iBAAiB,KAAK;AAC5B,MAAI,MAAM,QAAQ,gBAAgB,aAAa,KAAK,eAAe,cAAc,SAAS,GAAG;AAC3F,QAAI,IAAI,iBAAiB;AAAA,EAC3B;AACA,MAAI,MAAM,QAAQ,gBAAgB,YAAY,KAAK,eAAe,aAAa,SAAS,GAAG;AACzF,QAAI,IAAI,iBAAiB;AAAA,EAC3B;AACA,QAAM,iBAAiB,KAAK;AAC5B,MAAI,gBAAgB,cAAc,MAAM;AACtC,QAAI,IAAI,iBAAiB;AAAA,EAC3B;AACA,QAAM,WAAW,KAAK;AACtB,MAAI,UAAU,aAAa;AACzB,eAAW,WAAW,OAAO,KAAK,SAAS,WAAW,GAAiC;AACrF,UAAI,SAAS,YAAY,OAAO,MAAM,OAAW,KAAI,IAAI,OAAO;AAAA,IAClE;AAAA,EACF;AACA,SAAO;AACT;AAQO,SAAS,qBACd,kBACA,YACoB;AACpB,QAAM,UAA8B,CAAC;AACrC,aAAW,WAAW,kBAAkB;AACtC,UAAM,QAAQ,aAAa,OAAO;AAClC,QAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG;AACnD,cAAQ,KAAK,EAAE,IAAI,OAAO,MAAM,sBAAsB,SAAS,SAAS,QAAQ,CAAC;AAAA,IACnF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
const ATTACKER_INFLUENCED = /* @__PURE__ */ Symbol.for("adcp.attacker-influenced");
|
|
8
2
|
class AgentResolverError extends Error {
|
|
9
3
|
code;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/errors.ts"],"sourcesContent":["/**\n * Typed errors surfaced by `resolveAgent`. Codes mirror the\n * `request_signature_*` taxonomy from security.mdx §\"Discovering an\n * agent's signing keys via `brand_json_url`\" so a verifier consuming the\n * resolver's output can short-circuit to the same wire-rejection code\n * without rewriting the mapping.\n *\n * Detail-field discipline:\n * - Counterparty-influenceable strings (`agent_url`, `brand_json_url`,\n * `matched_entries`, `parse_error`, etc.) MUST be HTML-escaped before\n * rendering in admin UIs. The `attackerInfluenced` symbol marks fields\n * that came from the wire so downstream renderers can detect them.\n * - Internal network topology (resolved IPs, DNS state) MUST NOT be\n * copied onto detail fields. If `ssrfSafeFetch` throws, the resolver\n * translates the `SsrfRefusedError` into a `request_signature_*_unreachable`\n * code with `dns_error` set to the error class name only — never the\n * `address` or `hostname` fields the underlying error carries.\n */\n\nexport const ATTACKER_INFLUENCED = Symbol.for('adcp.attacker-influenced');\n\nexport type AgentResolverErrorCode =\n | 'request_signature_brand_json_url_missing'\n | 'request_signature_capabilities_unreachable'\n | 'request_signature_brand_json_unreachable'\n | 'request_signature_brand_json_malformed'\n | 'request_signature_brand_origin_mismatch'\n | 'request_signature_agent_not_in_brand_json'\n | 'request_signature_brand_json_ambiguous'\n | 'request_signature_key_origin_mismatch'\n | 'request_signature_key_origin_missing'\n /**\n * SDK-side codes — not in the spec's `request_signature_*` rejection\n * table but distinct conditions a verifier needs to surface separately.\n * The spec hands JWKS-fetch failures off to the verifier checklist\n * (`request_signature_key_unknown`), but that code only applies once a\n * `kid` lookup has been attempted; the bootstrap chain needs a code\n * before we have a kid in hand. Likewise, an alg-allowlist rejection at\n * import time is a distinct trust failure, not a fetch failure.\n */\n | 'request_signature_jwks_unreachable'\n | 'request_signature_jwks_alg_disallowed';\n\nexport interface AgentResolverErrorDetail {\n agent_url?: string;\n brand_json_url?: string;\n jwks_uri?: string;\n agent_etld1?: string;\n brand_json_url_etld1?: string;\n http_status?: number;\n /**\n * Coarse classification of a transport error class — `'fetch_failed'`,\n * `'timeout'`, `'dns_error'`, `'ssrf_refused'`. NEVER includes a resolved\n * IP, hostname-to-address mapping, or any other internal-topology hint.\n */\n dns_error?: string;\n last_attempt_at?: number;\n parse_error?: string;\n matched_count?: number;\n matched_entries?: ReadonlyArray<{ url: string; jwks_uri?: string }>;\n purpose?: string;\n expected_origin?: string;\n actual_origin?: string;\n posture?: string;\n}\n\nexport class AgentResolverError extends Error {\n readonly code: AgentResolverErrorCode;\n readonly detail: AgentResolverErrorDetail;\n readonly [ATTACKER_INFLUENCED]: ReadonlyArray<keyof AgentResolverErrorDetail>;\n\n constructor(\n code: AgentResolverErrorCode,\n message: string,\n detail: AgentResolverErrorDetail = {},\n attackerInfluencedFields: ReadonlyArray<keyof AgentResolverErrorDetail> = []\n ) {\n super(message);\n this.name = 'AgentResolverError';\n this.code = code;\n this.detail = detail;\n this[ATTACKER_INFLUENCED] = attackerInfluencedFields;\n }\n}\n\n/**\n * Field names on `AgentResolverError.detail` that carry counterparty-controlled\n * strings. Renderers (admin UIs, log aggregators) MUST HTML-escape these\n * before display.\n */\nexport function attackerInfluencedFields(err: AgentResolverError): ReadonlyArray<keyof AgentResolverErrorDetail> {\n return err[ATTACKER_INFLUENCED];\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/errors.ts"],"sourcesContent":["/**\n * Typed errors surfaced by `resolveAgent`. Codes mirror the\n * `request_signature_*` taxonomy from security.mdx §\"Discovering an\n * agent's signing keys via `brand_json_url`\" so a verifier consuming the\n * resolver's output can short-circuit to the same wire-rejection code\n * without rewriting the mapping.\n *\n * Detail-field discipline:\n * - Counterparty-influenceable strings (`agent_url`, `brand_json_url`,\n * `matched_entries`, `parse_error`, etc.) MUST be HTML-escaped before\n * rendering in admin UIs. The `attackerInfluenced` symbol marks fields\n * that came from the wire so downstream renderers can detect them.\n * - Internal network topology (resolved IPs, DNS state) MUST NOT be\n * copied onto detail fields. If `ssrfSafeFetch` throws, the resolver\n * translates the `SsrfRefusedError` into a `request_signature_*_unreachable`\n * code with `dns_error` set to the error class name only — never the\n * `address` or `hostname` fields the underlying error carries.\n */\n\nexport const ATTACKER_INFLUENCED = Symbol.for('adcp.attacker-influenced');\n\nexport type AgentResolverErrorCode =\n | 'request_signature_brand_json_url_missing'\n | 'request_signature_capabilities_unreachable'\n | 'request_signature_brand_json_unreachable'\n | 'request_signature_brand_json_malformed'\n | 'request_signature_brand_origin_mismatch'\n | 'request_signature_agent_not_in_brand_json'\n | 'request_signature_brand_json_ambiguous'\n | 'request_signature_key_origin_mismatch'\n | 'request_signature_key_origin_missing'\n /**\n * SDK-side codes — not in the spec's `request_signature_*` rejection\n * table but distinct conditions a verifier needs to surface separately.\n * The spec hands JWKS-fetch failures off to the verifier checklist\n * (`request_signature_key_unknown`), but that code only applies once a\n * `kid` lookup has been attempted; the bootstrap chain needs a code\n * before we have a kid in hand. Likewise, an alg-allowlist rejection at\n * import time is a distinct trust failure, not a fetch failure.\n */\n | 'request_signature_jwks_unreachable'\n | 'request_signature_jwks_alg_disallowed';\n\nexport interface AgentResolverErrorDetail {\n agent_url?: string;\n brand_json_url?: string;\n jwks_uri?: string;\n agent_etld1?: string;\n brand_json_url_etld1?: string;\n http_status?: number;\n /**\n * Coarse classification of a transport error class — `'fetch_failed'`,\n * `'timeout'`, `'dns_error'`, `'ssrf_refused'`. NEVER includes a resolved\n * IP, hostname-to-address mapping, or any other internal-topology hint.\n */\n dns_error?: string;\n last_attempt_at?: number;\n parse_error?: string;\n matched_count?: number;\n matched_entries?: ReadonlyArray<{ url: string; jwks_uri?: string }>;\n purpose?: string;\n expected_origin?: string;\n actual_origin?: string;\n posture?: string;\n}\n\nexport class AgentResolverError extends Error {\n readonly code: AgentResolverErrorCode;\n readonly detail: AgentResolverErrorDetail;\n readonly [ATTACKER_INFLUENCED]: ReadonlyArray<keyof AgentResolverErrorDetail>;\n\n constructor(\n code: AgentResolverErrorCode,\n message: string,\n detail: AgentResolverErrorDetail = {},\n attackerInfluencedFields: ReadonlyArray<keyof AgentResolverErrorDetail> = []\n ) {\n super(message);\n this.name = 'AgentResolverError';\n this.code = code;\n this.detail = detail;\n this[ATTACKER_INFLUENCED] = attackerInfluencedFields;\n }\n}\n\n/**\n * Field names on `AgentResolverError.detail` that carry counterparty-controlled\n * strings. Renderers (admin UIs, log aggregators) MUST HTML-escape these\n * before display.\n */\nexport function attackerInfluencedFields(err: AgentResolverError): ReadonlyArray<keyof AgentResolverErrorDetail> {\n return err[ATTACKER_INFLUENCED];\n}\n"],"mappings":"AAmBO,MAAM,sBAAsB,uBAAO,IAAI,0BAA0B;AA+CjE,MAAM,2BAA2B,MAAM;AAAA,EACnC;AAAA,EACA;AAAA,EACT,CAAU,mBAAmB;AAAA,EAE7B,YACE,MACA,SACA,SAAmC,CAAC,GACpCA,4BAA0E,CAAC,GAC3E;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,mBAAmB,IAAIA;AAAA,EAC9B;AACF;AAOO,SAAS,yBAAyB,KAAwE;AAC/G,SAAO,IAAI,mBAAmB;AAChC;","names":["attackerInfluencedFields"]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { parse as parseTld } from "tldts";
|
|
8
2
|
import { canonicalizeHost } from "./canonicalize.mjs";
|
|
9
3
|
const PSL_SNAPSHOT_VERSION = "tldts@7";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/etld.ts"],"sourcesContent":["/**\n * eTLD+1 computation against a pinned, dated PSL snapshot. Backed by\n * `tldts`, which ships its PSL bundled into the published package — pinning\n * the dependency in `package.json` pins the snapshot. Runtime PSL fetches are\n * never performed: a runtime fetch creates both a denial-of-service oracle\n * (PSL host outage stalls verification) and a non-deterministic eTLD+1\n * across deployments running on different snapshot ages\n * (security.mdx §\"Quickstart: implement a `brand_json_url`-based verifier\"\n * step 3).\n *\n * Both ICANN and PRIVATE PSL sections are in scope: platforms like\n * `vercel.app`, `pages.dev`, `github.io` MUST be treated as suffixes so an\n * attacker on `attacker.vercel.app` cannot pretend to share an eTLD+1 with\n * a legitimate publisher on `legit.vercel.app`.\n */\nimport { parse as parseTld } from 'tldts';\n\nimport { canonicalizeHost } from './canonicalize';\n\nexport const PSL_SNAPSHOT_VERSION = 'tldts@7';\n\nexport class EtldComputationError extends Error {\n constructor(\n message: string,\n readonly meta: { hostOrUrl?: string }\n ) {\n super(message);\n this.name = 'EtldComputationError';\n }\n}\n\n/**\n * Compute eTLD+1 for a hostname or URL. Caller should pass either a bare\n * hostname (`buyer.example.com`) or a full URL (`https://buyer.example.com/mcp`).\n *\n * Returns the registrable domain in canonical form (ASCII-lowercased, IDNA-2008\n * A-label). Throws when the input has no PSL match — typically because the host\n * is an IP literal or a single-label name.\n */\nexport function eTldPlusOne(hostOrUrl: string): string {\n const host = canonicalizeHost(extractHost(hostOrUrl));\n const result = parseTld(host, { allowPrivateDomains: true });\n if (result.isIp) {\n throw new EtldComputationError(`Cannot compute eTLD+1 for IP literal`, { hostOrUrl });\n }\n if (!result.domain) {\n throw new EtldComputationError(`No PSL match for hostname`, { hostOrUrl });\n }\n return canonicalizeHost(result.domain);\n}\n\n/**\n * Convenience: do two hostnames share an eTLD+1? Returns false when either\n * input has no PSL match (rather than throwing) — callers comparing two\n * hostnames typically want a non-match boolean, not an exception.\n */\nexport function sameEtldPlusOne(a: string, b: string): boolean {\n let aEtld: string;\n let bEtld: string;\n try {\n aEtld = eTldPlusOne(a);\n bEtld = eTldPlusOne(b);\n } catch {\n return false;\n }\n return aEtld === bEtld;\n}\n\nfunction extractHost(hostOrUrl: string): string {\n if (hostOrUrl.includes('://')) {\n return new URL(hostOrUrl).hostname;\n }\n return hostOrUrl;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/etld.ts"],"sourcesContent":["/**\n * eTLD+1 computation against a pinned, dated PSL snapshot. Backed by\n * `tldts`, which ships its PSL bundled into the published package — pinning\n * the dependency in `package.json` pins the snapshot. Runtime PSL fetches are\n * never performed: a runtime fetch creates both a denial-of-service oracle\n * (PSL host outage stalls verification) and a non-deterministic eTLD+1\n * across deployments running on different snapshot ages\n * (security.mdx §\"Quickstart: implement a `brand_json_url`-based verifier\"\n * step 3).\n *\n * Both ICANN and PRIVATE PSL sections are in scope: platforms like\n * `vercel.app`, `pages.dev`, `github.io` MUST be treated as suffixes so an\n * attacker on `attacker.vercel.app` cannot pretend to share an eTLD+1 with\n * a legitimate publisher on `legit.vercel.app`.\n */\nimport { parse as parseTld } from 'tldts';\n\nimport { canonicalizeHost } from './canonicalize';\n\nexport const PSL_SNAPSHOT_VERSION = 'tldts@7';\n\nexport class EtldComputationError extends Error {\n constructor(\n message: string,\n readonly meta: { hostOrUrl?: string }\n ) {\n super(message);\n this.name = 'EtldComputationError';\n }\n}\n\n/**\n * Compute eTLD+1 for a hostname or URL. Caller should pass either a bare\n * hostname (`buyer.example.com`) or a full URL (`https://buyer.example.com/mcp`).\n *\n * Returns the registrable domain in canonical form (ASCII-lowercased, IDNA-2008\n * A-label). Throws when the input has no PSL match — typically because the host\n * is an IP literal or a single-label name.\n */\nexport function eTldPlusOne(hostOrUrl: string): string {\n const host = canonicalizeHost(extractHost(hostOrUrl));\n const result = parseTld(host, { allowPrivateDomains: true });\n if (result.isIp) {\n throw new EtldComputationError(`Cannot compute eTLD+1 for IP literal`, { hostOrUrl });\n }\n if (!result.domain) {\n throw new EtldComputationError(`No PSL match for hostname`, { hostOrUrl });\n }\n return canonicalizeHost(result.domain);\n}\n\n/**\n * Convenience: do two hostnames share an eTLD+1? Returns false when either\n * input has no PSL match (rather than throwing) — callers comparing two\n * hostnames typically want a non-match boolean, not an exception.\n */\nexport function sameEtldPlusOne(a: string, b: string): boolean {\n let aEtld: string;\n let bEtld: string;\n try {\n aEtld = eTldPlusOne(a);\n bEtld = eTldPlusOne(b);\n } catch {\n return false;\n }\n return aEtld === bEtld;\n}\n\nfunction extractHost(hostOrUrl: string): string {\n if (hostOrUrl.includes('://')) {\n return new URL(hostOrUrl).hostname;\n }\n return hostOrUrl;\n}\n"],"mappings":"AAeA,SAAS,SAAS,gBAAgB;AAElC,SAAS,wBAAwB;AAE1B,MAAM,uBAAuB;AAE7B,MAAM,6BAA6B,MAAM;AAAA,EAC9C,YACE,SACS,MACT;AACA,UAAM,OAAO;AAFJ;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EAJW;AAKb;AAUO,SAAS,YAAY,WAA2B;AACrD,QAAM,OAAO,iBAAiB,YAAY,SAAS,CAAC;AACpD,QAAM,SAAS,SAAS,MAAM,EAAE,qBAAqB,KAAK,CAAC;AAC3D,MAAI,OAAO,MAAM;AACf,UAAM,IAAI,qBAAqB,wCAAwC,EAAE,UAAU,CAAC;AAAA,EACtF;AACA,MAAI,CAAC,OAAO,QAAQ;AAClB,UAAM,IAAI,qBAAqB,6BAA6B,EAAE,UAAU,CAAC;AAAA,EAC3E;AACA,SAAO,iBAAiB,OAAO,MAAM;AACvC;AAOO,SAAS,gBAAgB,GAAW,GAAoB;AAC7D,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,YAAQ,YAAY,CAAC;AACrB,YAAQ,YAAY,CAAC;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,UAAU;AACnB;AAEA,SAAS,YAAY,WAA2B;AAC9C,MAAI,UAAU,SAAS,KAAK,GAAG;AAC7B,WAAO,IAAI,IAAI,SAAS,EAAE;AAAA,EAC5B;AACA,SAAO;AACT;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { ssrfSafeFetch, SsrfRefusedError } from "../../net/index.mjs";
|
|
8
2
|
import { parseStrictJson, StrictJsonError } from "./strict-json.mjs";
|
|
9
3
|
const MAX_CAPABILITIES_BYTES = 65536;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/fetch-helpers.ts"],"sourcesContent":["/**\n * SSRF-safe HTTPS fetches for the brand_json_url discovery chain. Wraps\n * `ssrfSafeFetch` with the spec's body caps and the strict-JSON parser.\n * No redirects, no caching here — that's the resolver's responsibility\n * one layer up.\n *\n * Error translation lives here too: `SsrfRefusedError`'s `address` /\n * `hostname` fields MUST NOT propagate onto `AgentResolverError.detail`\n * (internal-topology leak). We surface the `code` only.\n */\n\nimport { ssrfSafeFetch, SsrfRefusedError, type SsrfFetchOptions } from '../../net';\n\nimport { parseStrictJson, StrictJsonError } from './strict-json';\n\nexport const MAX_CAPABILITIES_BYTES = 65_536;\nexport const MAX_BRAND_JSON_BYTES = 262_144;\nexport const MAX_JWKS_BYTES = 65_536;\nexport const DEFAULT_CONNECT_TIMEOUT_MS = 5_000;\nexport const DEFAULT_TOTAL_TIMEOUT_MS = 10_000;\n\nexport type FetchKind = 'capabilities' | 'brand.json' | 'jwks';\n\nexport class SafeFetchError extends Error {\n readonly kind: FetchKind;\n readonly httpStatus: number | undefined;\n readonly transport: 'fetch_failed' | 'timeout' | 'dns_error' | 'ssrf_refused' | 'body_cap';\n constructor(kind: FetchKind, transport: SafeFetchError['transport'], message: string, httpStatus?: number) {\n super(message);\n this.name = 'SafeFetchError';\n this.kind = kind;\n this.transport = transport;\n this.httpStatus = httpStatus;\n }\n}\n\nexport interface SafeFetchJsonResult {\n body: unknown;\n status: number;\n headers: Record<string, string>;\n fetchedAt: number;\n}\n\n/**\n * Fetch a JSON document under the spec's SSRF rules with a hard body cap and\n * the strict-JSON parser. Network errors, body-cap overruns, and JSON parse\n * errors are translated to a typed `SafeFetchError` so the resolver can map\n * each onto a `request_signature_*` code without inspecting the underlying\n * cause.\n */\nexport async function safeFetchJson(\n url: string,\n kind: FetchKind,\n options: { allowPrivateIp?: boolean; timeoutMs?: number; maxBodyBytes: number }\n): Promise<SafeFetchJsonResult> {\n const fetchOpts: SsrfFetchOptions = {\n method: 'GET',\n headers: { accept: 'application/json' },\n allowPrivateIp: options.allowPrivateIp === true,\n timeoutMs: options.timeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS,\n maxBodyBytes: options.maxBodyBytes,\n };\n\n let res;\n try {\n res = await ssrfSafeFetch(url, fetchOpts);\n } catch (err) {\n if (err instanceof SsrfRefusedError) {\n throw new SafeFetchError(kind, 'ssrf_refused', `${kind} fetch refused: ${err.code}`);\n }\n const message = err instanceof Error ? err.message : String(err);\n if (/abort|timed?\\s*out/i.test(message)) {\n throw new SafeFetchError(kind, 'timeout', `${kind} fetch timed out`);\n }\n throw new SafeFetchError(kind, 'fetch_failed', `${kind} fetch failed`);\n }\n\n if (res.status < 200 || res.status >= 300) {\n throw new SafeFetchError(kind, 'fetch_failed', `${kind} fetch returned HTTP ${res.status}`, res.status);\n }\n\n let parsed: unknown;\n try {\n const text = Buffer.from(res.body).toString('utf8');\n parsed = parseStrictJson(text);\n } catch (err) {\n if (err instanceof StrictJsonError) {\n throw new SafeFetchError(kind, 'fetch_failed', `${kind} body failed strict-JSON parse: ${err.code}`);\n }\n throw new SafeFetchError(kind, 'fetch_failed', `${kind} body parse failed`);\n }\n\n return {\n body: parsed,\n status: res.status,\n headers: res.headers,\n fetchedAt: Math.floor(Date.now() / 1000),\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/fetch-helpers.ts"],"sourcesContent":["/**\n * SSRF-safe HTTPS fetches for the brand_json_url discovery chain. Wraps\n * `ssrfSafeFetch` with the spec's body caps and the strict-JSON parser.\n * No redirects, no caching here — that's the resolver's responsibility\n * one layer up.\n *\n * Error translation lives here too: `SsrfRefusedError`'s `address` /\n * `hostname` fields MUST NOT propagate onto `AgentResolverError.detail`\n * (internal-topology leak). We surface the `code` only.\n */\n\nimport { ssrfSafeFetch, SsrfRefusedError, type SsrfFetchOptions } from '../../net';\n\nimport { parseStrictJson, StrictJsonError } from './strict-json';\n\nexport const MAX_CAPABILITIES_BYTES = 65_536;\nexport const MAX_BRAND_JSON_BYTES = 262_144;\nexport const MAX_JWKS_BYTES = 65_536;\nexport const DEFAULT_CONNECT_TIMEOUT_MS = 5_000;\nexport const DEFAULT_TOTAL_TIMEOUT_MS = 10_000;\n\nexport type FetchKind = 'capabilities' | 'brand.json' | 'jwks';\n\nexport class SafeFetchError extends Error {\n readonly kind: FetchKind;\n readonly httpStatus: number | undefined;\n readonly transport: 'fetch_failed' | 'timeout' | 'dns_error' | 'ssrf_refused' | 'body_cap';\n constructor(kind: FetchKind, transport: SafeFetchError['transport'], message: string, httpStatus?: number) {\n super(message);\n this.name = 'SafeFetchError';\n this.kind = kind;\n this.transport = transport;\n this.httpStatus = httpStatus;\n }\n}\n\nexport interface SafeFetchJsonResult {\n body: unknown;\n status: number;\n headers: Record<string, string>;\n fetchedAt: number;\n}\n\n/**\n * Fetch a JSON document under the spec's SSRF rules with a hard body cap and\n * the strict-JSON parser. Network errors, body-cap overruns, and JSON parse\n * errors are translated to a typed `SafeFetchError` so the resolver can map\n * each onto a `request_signature_*` code without inspecting the underlying\n * cause.\n */\nexport async function safeFetchJson(\n url: string,\n kind: FetchKind,\n options: { allowPrivateIp?: boolean; timeoutMs?: number; maxBodyBytes: number }\n): Promise<SafeFetchJsonResult> {\n const fetchOpts: SsrfFetchOptions = {\n method: 'GET',\n headers: { accept: 'application/json' },\n allowPrivateIp: options.allowPrivateIp === true,\n timeoutMs: options.timeoutMs ?? DEFAULT_TOTAL_TIMEOUT_MS,\n maxBodyBytes: options.maxBodyBytes,\n };\n\n let res;\n try {\n res = await ssrfSafeFetch(url, fetchOpts);\n } catch (err) {\n if (err instanceof SsrfRefusedError) {\n throw new SafeFetchError(kind, 'ssrf_refused', `${kind} fetch refused: ${err.code}`);\n }\n const message = err instanceof Error ? err.message : String(err);\n if (/abort|timed?\\s*out/i.test(message)) {\n throw new SafeFetchError(kind, 'timeout', `${kind} fetch timed out`);\n }\n throw new SafeFetchError(kind, 'fetch_failed', `${kind} fetch failed`);\n }\n\n if (res.status < 200 || res.status >= 300) {\n throw new SafeFetchError(kind, 'fetch_failed', `${kind} fetch returned HTTP ${res.status}`, res.status);\n }\n\n let parsed: unknown;\n try {\n const text = Buffer.from(res.body).toString('utf8');\n parsed = parseStrictJson(text);\n } catch (err) {\n if (err instanceof StrictJsonError) {\n throw new SafeFetchError(kind, 'fetch_failed', `${kind} body failed strict-JSON parse: ${err.code}`);\n }\n throw new SafeFetchError(kind, 'fetch_failed', `${kind} body parse failed`);\n }\n\n return {\n body: parsed,\n status: res.status,\n headers: res.headers,\n fetchedAt: Math.floor(Date.now() / 1000),\n };\n}\n"],"mappings":"AAWA,SAAS,eAAe,wBAA+C;AAEvE,SAAS,iBAAiB,uBAAuB;AAE1C,MAAM,yBAAyB;AAC/B,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB;AACvB,MAAM,6BAA6B;AACnC,MAAM,2BAA2B;AAIjC,MAAM,uBAAuB,MAAM;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACT,YAAY,MAAiB,WAAwC,SAAiB,YAAqB;AACzG,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,aAAa;AAAA,EACpB;AACF;AAgBA,eAAsB,cACpB,KACA,MACA,SAC8B;AAC9B,QAAM,YAA8B;AAAA,IAClC,QAAQ;AAAA,IACR,SAAS,EAAE,QAAQ,mBAAmB;AAAA,IACtC,gBAAgB,QAAQ,mBAAmB;AAAA,IAC3C,WAAW,QAAQ,aAAa;AAAA,IAChC,cAAc,QAAQ;AAAA,EACxB;AAEA,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,cAAc,KAAK,SAAS;AAAA,EAC1C,SAAS,KAAK;AACZ,QAAI,eAAe,kBAAkB;AACnC,YAAM,IAAI,eAAe,MAAM,gBAAgB,GAAG,IAAI,mBAAmB,IAAI,IAAI,EAAE;AAAA,IACrF;AACA,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,QAAI,sBAAsB,KAAK,OAAO,GAAG;AACvC,YAAM,IAAI,eAAe,MAAM,WAAW,GAAG,IAAI,kBAAkB;AAAA,IACrE;AACA,UAAM,IAAI,eAAe,MAAM,gBAAgB,GAAG,IAAI,eAAe;AAAA,EACvE;AAEA,MAAI,IAAI,SAAS,OAAO,IAAI,UAAU,KAAK;AACzC,UAAM,IAAI,eAAe,MAAM,gBAAgB,GAAG,IAAI,wBAAwB,IAAI,MAAM,IAAI,IAAI,MAAM;AAAA,EACxG;AAEA,MAAI;AACJ,MAAI;AACF,UAAM,OAAO,OAAO,KAAK,IAAI,IAAI,EAAE,SAAS,MAAM;AAClD,aAAS,gBAAgB,IAAI;AAAA,EAC/B,SAAS,KAAK;AACZ,QAAI,eAAe,iBAAiB;AAClC,YAAM,IAAI,eAAe,MAAM,gBAAgB,GAAG,IAAI,mCAAmC,IAAI,IAAI,EAAE;AAAA,IACrG;AACA,UAAM,IAAI,eAAe,MAAM,gBAAgB,GAAG,IAAI,oBAAoB;AAAA,EAC5E;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,IAAI;AAAA,IACZ,SAAS,IAAI;AAAA,IACb,WAAW,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,EACzC;AACF;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { resolveAgent } from "./resolve-agent.mjs";
|
|
8
2
|
import { getAgentJwks, createAgentJwksSet } from "./jwks-set.mjs";
|
|
9
3
|
import {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/index.ts"],"sourcesContent":["/**\n * Brand-JSON-URL agent resolver — bootstraps from an agent URL to its\n * signing keys via the 8-step algorithm in `security.mdx` §\"Discovering\n * an agent's signing keys via `brand_json_url`\".\n *\n * Public exports for `@adcp/client`:\n *\n * - `resolveAgent(url, opts)` — full chain + per-step trace\n * - `getAgentJwks(url, opts)` — JWKS-only fast path (stage 4)\n * - `createAgentJwksSet(url, opts)` — JOSE-compatible JWTVerifyGetKey factory (stage 4)\n * - `AgentResolverError` — typed error with `request_signature_*` codes\n * - `attackerInfluencedFields` — names of `detail` fields admin UIs MUST escape\n *\n * Pure-function primitives (`eTldPlusOne`, `canonicalizeOrigin`,\n * `selectAgentByUrl`, etc.) are exported under `agent-resolver/primitives`\n * for callers building bespoke verification flows on top of this module.\n */\n\nexport { resolveAgent } from './resolve-agent';\nexport type {\n AgentResolution,\n AgentProtocol,\n FetchCapabilitiesFn,\n ResolveAgentOptions,\n TraceStep,\n} from './resolve-agent';\nexport { getAgentJwks, createAgentJwksSet } from './jwks-set';\nexport type { AgentJwksResult, GetAgentJwksOptions, CreateAgentJwksSetOptions } from './jwks-set';\nexport {\n AgentResolverError,\n ATTACKER_INFLUENCED,\n attackerInfluencedFields,\n type AgentResolverErrorCode,\n type AgentResolverErrorDetail,\n} from './errors';\nexport type { AgentEntry } from './select-agent';\nexport type {\n CapabilitiesWithBrandJsonUrl,\n IdentityKeyOriginPurpose,\n IdentityKeyOrigins,\n IdentityPosture,\n} from './capabilities-types';\nexport { readBrandJsonUrl, readIdentityPosture } from './capabilities-types';\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/index.ts"],"sourcesContent":["/**\n * Brand-JSON-URL agent resolver — bootstraps from an agent URL to its\n * signing keys via the 8-step algorithm in `security.mdx` §\"Discovering\n * an agent's signing keys via `brand_json_url`\".\n *\n * Public exports for `@adcp/client`:\n *\n * - `resolveAgent(url, opts)` — full chain + per-step trace\n * - `getAgentJwks(url, opts)` — JWKS-only fast path (stage 4)\n * - `createAgentJwksSet(url, opts)` — JOSE-compatible JWTVerifyGetKey factory (stage 4)\n * - `AgentResolverError` — typed error with `request_signature_*` codes\n * - `attackerInfluencedFields` — names of `detail` fields admin UIs MUST escape\n *\n * Pure-function primitives (`eTldPlusOne`, `canonicalizeOrigin`,\n * `selectAgentByUrl`, etc.) are exported under `agent-resolver/primitives`\n * for callers building bespoke verification flows on top of this module.\n */\n\nexport { resolveAgent } from './resolve-agent';\nexport type {\n AgentResolution,\n AgentProtocol,\n FetchCapabilitiesFn,\n ResolveAgentOptions,\n TraceStep,\n} from './resolve-agent';\nexport { getAgentJwks, createAgentJwksSet } from './jwks-set';\nexport type { AgentJwksResult, GetAgentJwksOptions, CreateAgentJwksSetOptions } from './jwks-set';\nexport {\n AgentResolverError,\n ATTACKER_INFLUENCED,\n attackerInfluencedFields,\n type AgentResolverErrorCode,\n type AgentResolverErrorDetail,\n} from './errors';\nexport type { AgentEntry } from './select-agent';\nexport type {\n CapabilitiesWithBrandJsonUrl,\n IdentityKeyOriginPurpose,\n IdentityKeyOrigins,\n IdentityPosture,\n} from './capabilities-types';\nexport { readBrandJsonUrl, readIdentityPosture } from './capabilities-types';\n"],"mappings":"AAkBA,SAAS,oBAAoB;AAQ7B,SAAS,cAAc,0BAA0B;AAEjD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAQP,SAAS,kBAAkB,2BAA2B;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { createLocalJWKSet } from "jose";
|
|
8
2
|
import { AgentResolverError } from "./errors.mjs";
|
|
9
3
|
import { resolveAgent } from "./resolve-agent.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/jwks-set.ts"],"sourcesContent":["/**\n * Convenience surfaces over `resolveAgent`:\n *\n * - `getAgentJwks(url, opts)` — JWKS-only fast path. Skips the trace\n * assembly the full resolver returns; callers who only need keys for\n * a verifier don't have to filter the larger record.\n *\n * - `createAgentJwksSet(url, opts)` — returns a `JWTVerifyGetKey`-shaped\n * async function compatible with `jose.jwtVerify`'s `getKey` parameter.\n * Caches the resolved JWKS per `cacheMaxAgeSeconds`; refetches on a\n * `kid` miss subject to the spec's 30-second cooldown\n * (security.mdx §\"Verifier checklist (requests)\" step 7's refetch\n * guidance). Enforces the caller-supplied algorithm allowlist at JWKS\n * import time AND on the verify call — defense-in-depth against the\n * \"alg: HS256\" confusion vector. The allowlist is REQUIRED; there is\n * no library default.\n */\n\nimport { createLocalJWKSet, type JWK, type JWTVerifyGetKey } from 'jose';\n\nimport { AgentResolverError } from './errors';\nimport { resolveAgent, type AgentResolution, type ResolveAgentOptions } from './resolve-agent';\n\nexport type GetAgentJwksOptions = Pick<\n ResolveAgentOptions,\n 'protocol' | 'fetchCapabilities' | 'allowPrivateIp' | 'timeoutMs' | 'now'\n>;\n\nexport interface AgentJwksResult {\n agentUrl: string;\n brandJsonUrl: string;\n jwksUri: string;\n jwks: { keys: ReadonlyArray<Record<string, unknown>> };\n cacheControl?: string;\n fetchedAt: number;\n}\n\n/**\n * Fast path: returns the JWKS plus the URIs that produced it. Skips the\n * trace + freshness aggregate the full `resolveAgent` returns. Errors\n * propagate as `AgentResolverError` with the same `request_signature_*`\n * codes — the JWKS fast path is not a separate trust chain.\n */\nexport async function getAgentJwks(agentUrl: string, options: GetAgentJwksOptions = {}): Promise<AgentJwksResult> {\n const resolution = await resolveAgent(agentUrl, options);\n return {\n agentUrl: resolution.agentUrl,\n brandJsonUrl: resolution.brandJsonUrl,\n jwksUri: resolution.jwksUri,\n jwks: resolution.jwks,\n ...(resolution.jwksCacheControl !== undefined && { cacheControl: resolution.jwksCacheControl }),\n fetchedAt: resolution.freshness.jwksFetchedAt,\n };\n}\n\nexport interface CreateAgentJwksSetOptions extends Pick<\n ResolveAgentOptions,\n 'protocol' | 'fetchCapabilities' | 'allowPrivateIp' | 'timeoutMs' | 'now'\n> {\n /**\n * Algorithm allowlist enforced at JWKS-import time AND surfaced to\n * `jose.jwtVerify`'s `algorithms` option. Required — there is no default.\n * The classic confusion vector is `alg: HS256` against a public-key JWK,\n * which jose's verifier would reject — but a library that pre-imports\n * keys without an allowlist still leaves the door open to JWKs whose\n * declared `alg` doesn't match the verifier's expectation. Requiring the\n * caller to name the algorithms closes the door at every layer.\n *\n * Use a tight set: `[\"EdDSA\"]` for Ed25519-only deployments;\n * `[\"EdDSA\", \"ES256\"]` for the AdCP request-signing default pair.\n */\n allowedAlgs: readonly string[];\n /**\n * Max age in seconds before a forced refetch of the JWKS. Default 300s\n * (5 minutes). Bound this above by your operator's revocation polling\n * interval — a longer cache mask key rotation; a shorter cache costs\n * extra fetches without buying you anything.\n */\n cacheMaxAgeSeconds?: number;\n /**\n * Minimum interval between JWKS refetches when a `kid` miss triggers a\n * refresh. Default 30s — matches the spec's cooldown rule for the\n * verifier checklist's step-7 refetch.\n */\n kidMissCooldownSeconds?: number;\n}\n\nconst DEFAULT_CACHE_MAX_AGE_SECONDS = 300;\nconst DEFAULT_KID_MISS_COOLDOWN_SECONDS = 30;\n\ninterface CachedJwks {\n resolution: AgentResolution;\n fetchedAt: number;\n /** `createLocalJWKSet` output for the cached JWKS. */\n getKey: JWTVerifyGetKey;\n}\n\n/**\n * Build a `JWTVerifyGetKey` that runs the brand_json_url discovery chain on\n * first use, caches the resolved JWKS, refetches on cache expiry, and\n * refetches once on a `kid` miss subject to a 30s cooldown. Imports each\n * JWK and rejects any whose `alg` (when declared) is outside `allowedAlgs`.\n *\n * The returned function is compatible with `jose.jwtVerify` — pass it as\n * `getKey` and pair with `algorithms: opts.allowedAlgs` on the verify call.\n */\nexport function createAgentJwksSet(agentUrl: string, options: CreateAgentJwksSetOptions): JWTVerifyGetKey {\n if (!options.allowedAlgs || options.allowedAlgs.length === 0) {\n throw new TypeError(\n `createAgentJwksSet requires non-empty allowedAlgs — refusing to verify against an open algorithm set`\n );\n }\n const allowedAlgs = new Set(options.allowedAlgs);\n const cacheMaxAge = options.cacheMaxAgeSeconds ?? DEFAULT_CACHE_MAX_AGE_SECONDS;\n const kidCooldown = options.kidMissCooldownSeconds ?? DEFAULT_KID_MISS_COOLDOWN_SECONDS;\n const now = options.now ?? (() => Math.floor(Date.now() / 1000));\n\n let cache: CachedJwks | undefined;\n let lastRefetchAt = 0;\n let inFlight: Promise<CachedJwks> | undefined;\n\n const refresh = async (): Promise<CachedJwks> => {\n if (inFlight) return inFlight;\n inFlight = (async () => {\n const resolution = await resolveAgent(agentUrl, options);\n assertAllJwksAllowed(resolution.jwks, allowedAlgs, agentUrl);\n const filtered = filterJwksToAllowedAlgs(resolution.jwks, allowedAlgs);\n const getKey = createLocalJWKSet({ keys: filtered as unknown as JWK[] });\n cache = { resolution, fetchedAt: now(), getKey };\n lastRefetchAt = cache.fetchedAt;\n return cache;\n })().finally(() => {\n inFlight = undefined;\n });\n return inFlight;\n };\n\n const ensureFresh = async (): Promise<CachedJwks> => {\n if (!cache || now() - cache.fetchedAt >= cacheMaxAge) {\n return refresh();\n }\n return cache;\n };\n\n const getKey: JWTVerifyGetKey = async (protectedHeader, token) => {\n const fresh = await ensureFresh();\n try {\n return await fresh.getKey(protectedHeader, token);\n } catch (err) {\n // Likely cause: kid miss (key rotated). Subject to the cooldown,\n // refetch once and retry — same logic the spec's verifier checklist\n // applies between the agent-URL preamble and the kid resolution.\n if (now() - lastRefetchAt < kidCooldown) throw err;\n const refreshed = await refresh();\n return refreshed.getKey(protectedHeader, token);\n }\n };\n\n return getKey;\n}\n\n/**\n * Reject every JWK whose declared `alg` is outside the allowlist. This is\n * the import-time half of the defense-in-depth pair against alg-confusion\n * attacks; the verify-time half is the caller passing `algorithms` to\n * `jose.jwtVerify`. Malformed-JWK detection is left to `createLocalJWKSet`\n * + `jose.jwtVerify` at verify time — running an async smoke-import here\n * would be fire-and-forget and swallow rejections.\n */\nfunction assertAllJwksAllowed(\n jwks: { keys: ReadonlyArray<Record<string, unknown>> },\n allowedAlgs: ReadonlySet<string>,\n agentUrl: string\n): void {\n for (const jwk of jwks.keys) {\n const declared = jwk.alg;\n if (typeof declared === 'string' && !allowedAlgs.has(declared)) {\n throw new AgentResolverError(\n 'request_signature_jwks_alg_disallowed',\n `Agent JWKS contains key with alg=${declared} not in allowedAlgs`,\n { agent_url: agentUrl },\n ['agent_url']\n );\n }\n }\n}\n\nfunction filterJwksToAllowedAlgs(\n jwks: { keys: ReadonlyArray<Record<string, unknown>> },\n allowedAlgs: ReadonlySet<string>\n): ReadonlyArray<Record<string, unknown>> {\n // Keep keys without a declared `alg` (jose still gates by the verifier's\n // `algorithms` option) and keys whose declared `alg` is in the allowlist.\n return jwks.keys.filter(jwk => typeof jwk.alg !== 'string' || allowedAlgs.has(jwk.alg));\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/jwks-set.ts"],"sourcesContent":["/**\n * Convenience surfaces over `resolveAgent`:\n *\n * - `getAgentJwks(url, opts)` — JWKS-only fast path. Skips the trace\n * assembly the full resolver returns; callers who only need keys for\n * a verifier don't have to filter the larger record.\n *\n * - `createAgentJwksSet(url, opts)` — returns a `JWTVerifyGetKey`-shaped\n * async function compatible with `jose.jwtVerify`'s `getKey` parameter.\n * Caches the resolved JWKS per `cacheMaxAgeSeconds`; refetches on a\n * `kid` miss subject to the spec's 30-second cooldown\n * (security.mdx §\"Verifier checklist (requests)\" step 7's refetch\n * guidance). Enforces the caller-supplied algorithm allowlist at JWKS\n * import time AND on the verify call — defense-in-depth against the\n * \"alg: HS256\" confusion vector. The allowlist is REQUIRED; there is\n * no library default.\n */\n\nimport { createLocalJWKSet, type JWK, type JWTVerifyGetKey } from 'jose';\n\nimport { AgentResolverError } from './errors';\nimport { resolveAgent, type AgentResolution, type ResolveAgentOptions } from './resolve-agent';\n\nexport type GetAgentJwksOptions = Pick<\n ResolveAgentOptions,\n 'protocol' | 'fetchCapabilities' | 'allowPrivateIp' | 'timeoutMs' | 'now'\n>;\n\nexport interface AgentJwksResult {\n agentUrl: string;\n brandJsonUrl: string;\n jwksUri: string;\n jwks: { keys: ReadonlyArray<Record<string, unknown>> };\n cacheControl?: string;\n fetchedAt: number;\n}\n\n/**\n * Fast path: returns the JWKS plus the URIs that produced it. Skips the\n * trace + freshness aggregate the full `resolveAgent` returns. Errors\n * propagate as `AgentResolverError` with the same `request_signature_*`\n * codes — the JWKS fast path is not a separate trust chain.\n */\nexport async function getAgentJwks(agentUrl: string, options: GetAgentJwksOptions = {}): Promise<AgentJwksResult> {\n const resolution = await resolveAgent(agentUrl, options);\n return {\n agentUrl: resolution.agentUrl,\n brandJsonUrl: resolution.brandJsonUrl,\n jwksUri: resolution.jwksUri,\n jwks: resolution.jwks,\n ...(resolution.jwksCacheControl !== undefined && { cacheControl: resolution.jwksCacheControl }),\n fetchedAt: resolution.freshness.jwksFetchedAt,\n };\n}\n\nexport interface CreateAgentJwksSetOptions extends Pick<\n ResolveAgentOptions,\n 'protocol' | 'fetchCapabilities' | 'allowPrivateIp' | 'timeoutMs' | 'now'\n> {\n /**\n * Algorithm allowlist enforced at JWKS-import time AND surfaced to\n * `jose.jwtVerify`'s `algorithms` option. Required — there is no default.\n * The classic confusion vector is `alg: HS256` against a public-key JWK,\n * which jose's verifier would reject — but a library that pre-imports\n * keys without an allowlist still leaves the door open to JWKs whose\n * declared `alg` doesn't match the verifier's expectation. Requiring the\n * caller to name the algorithms closes the door at every layer.\n *\n * Use a tight set: `[\"EdDSA\"]` for Ed25519-only deployments;\n * `[\"EdDSA\", \"ES256\"]` for the AdCP request-signing default pair.\n */\n allowedAlgs: readonly string[];\n /**\n * Max age in seconds before a forced refetch of the JWKS. Default 300s\n * (5 minutes). Bound this above by your operator's revocation polling\n * interval — a longer cache mask key rotation; a shorter cache costs\n * extra fetches without buying you anything.\n */\n cacheMaxAgeSeconds?: number;\n /**\n * Minimum interval between JWKS refetches when a `kid` miss triggers a\n * refresh. Default 30s — matches the spec's cooldown rule for the\n * verifier checklist's step-7 refetch.\n */\n kidMissCooldownSeconds?: number;\n}\n\nconst DEFAULT_CACHE_MAX_AGE_SECONDS = 300;\nconst DEFAULT_KID_MISS_COOLDOWN_SECONDS = 30;\n\ninterface CachedJwks {\n resolution: AgentResolution;\n fetchedAt: number;\n /** `createLocalJWKSet` output for the cached JWKS. */\n getKey: JWTVerifyGetKey;\n}\n\n/**\n * Build a `JWTVerifyGetKey` that runs the brand_json_url discovery chain on\n * first use, caches the resolved JWKS, refetches on cache expiry, and\n * refetches once on a `kid` miss subject to a 30s cooldown. Imports each\n * JWK and rejects any whose `alg` (when declared) is outside `allowedAlgs`.\n *\n * The returned function is compatible with `jose.jwtVerify` — pass it as\n * `getKey` and pair with `algorithms: opts.allowedAlgs` on the verify call.\n */\nexport function createAgentJwksSet(agentUrl: string, options: CreateAgentJwksSetOptions): JWTVerifyGetKey {\n if (!options.allowedAlgs || options.allowedAlgs.length === 0) {\n throw new TypeError(\n `createAgentJwksSet requires non-empty allowedAlgs — refusing to verify against an open algorithm set`\n );\n }\n const allowedAlgs = new Set(options.allowedAlgs);\n const cacheMaxAge = options.cacheMaxAgeSeconds ?? DEFAULT_CACHE_MAX_AGE_SECONDS;\n const kidCooldown = options.kidMissCooldownSeconds ?? DEFAULT_KID_MISS_COOLDOWN_SECONDS;\n const now = options.now ?? (() => Math.floor(Date.now() / 1000));\n\n let cache: CachedJwks | undefined;\n let lastRefetchAt = 0;\n let inFlight: Promise<CachedJwks> | undefined;\n\n const refresh = async (): Promise<CachedJwks> => {\n if (inFlight) return inFlight;\n inFlight = (async () => {\n const resolution = await resolveAgent(agentUrl, options);\n assertAllJwksAllowed(resolution.jwks, allowedAlgs, agentUrl);\n const filtered = filterJwksToAllowedAlgs(resolution.jwks, allowedAlgs);\n const getKey = createLocalJWKSet({ keys: filtered as unknown as JWK[] });\n cache = { resolution, fetchedAt: now(), getKey };\n lastRefetchAt = cache.fetchedAt;\n return cache;\n })().finally(() => {\n inFlight = undefined;\n });\n return inFlight;\n };\n\n const ensureFresh = async (): Promise<CachedJwks> => {\n if (!cache || now() - cache.fetchedAt >= cacheMaxAge) {\n return refresh();\n }\n return cache;\n };\n\n const getKey: JWTVerifyGetKey = async (protectedHeader, token) => {\n const fresh = await ensureFresh();\n try {\n return await fresh.getKey(protectedHeader, token);\n } catch (err) {\n // Likely cause: kid miss (key rotated). Subject to the cooldown,\n // refetch once and retry — same logic the spec's verifier checklist\n // applies between the agent-URL preamble and the kid resolution.\n if (now() - lastRefetchAt < kidCooldown) throw err;\n const refreshed = await refresh();\n return refreshed.getKey(protectedHeader, token);\n }\n };\n\n return getKey;\n}\n\n/**\n * Reject every JWK whose declared `alg` is outside the allowlist. This is\n * the import-time half of the defense-in-depth pair against alg-confusion\n * attacks; the verify-time half is the caller passing `algorithms` to\n * `jose.jwtVerify`. Malformed-JWK detection is left to `createLocalJWKSet`\n * + `jose.jwtVerify` at verify time — running an async smoke-import here\n * would be fire-and-forget and swallow rejections.\n */\nfunction assertAllJwksAllowed(\n jwks: { keys: ReadonlyArray<Record<string, unknown>> },\n allowedAlgs: ReadonlySet<string>,\n agentUrl: string\n): void {\n for (const jwk of jwks.keys) {\n const declared = jwk.alg;\n if (typeof declared === 'string' && !allowedAlgs.has(declared)) {\n throw new AgentResolverError(\n 'request_signature_jwks_alg_disallowed',\n `Agent JWKS contains key with alg=${declared} not in allowedAlgs`,\n { agent_url: agentUrl },\n ['agent_url']\n );\n }\n }\n}\n\nfunction filterJwksToAllowedAlgs(\n jwks: { keys: ReadonlyArray<Record<string, unknown>> },\n allowedAlgs: ReadonlySet<string>\n): ReadonlyArray<Record<string, unknown>> {\n // Keep keys without a declared `alg` (jose still gates by the verifier's\n // `algorithms` option) and keys whose declared `alg` is in the allowlist.\n return jwks.keys.filter(jwk => typeof jwk.alg !== 'string' || allowedAlgs.has(jwk.alg));\n}\n"],"mappings":"AAkBA,SAAS,yBAAyD;AAElE,SAAS,0BAA0B;AACnC,SAAS,oBAAoE;AAsB7E,eAAsB,aAAa,UAAkB,UAA+B,CAAC,GAA6B;AAChH,QAAM,aAAa,MAAM,aAAa,UAAU,OAAO;AACvD,SAAO;AAAA,IACL,UAAU,WAAW;AAAA,IACrB,cAAc,WAAW;AAAA,IACzB,SAAS,WAAW;AAAA,IACpB,MAAM,WAAW;AAAA,IACjB,GAAI,WAAW,qBAAqB,UAAa,EAAE,cAAc,WAAW,iBAAiB;AAAA,IAC7F,WAAW,WAAW,UAAU;AAAA,EAClC;AACF;AAkCA,MAAM,gCAAgC;AACtC,MAAM,oCAAoC;AAkBnC,SAAS,mBAAmB,UAAkB,SAAqD;AACxG,MAAI,CAAC,QAAQ,eAAe,QAAQ,YAAY,WAAW,GAAG;AAC5D,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,cAAc,IAAI,IAAI,QAAQ,WAAW;AAC/C,QAAM,cAAc,QAAQ,sBAAsB;AAClD,QAAM,cAAc,QAAQ,0BAA0B;AACtD,QAAM,MAAM,QAAQ,QAAQ,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAE9D,MAAI;AACJ,MAAI,gBAAgB;AACpB,MAAI;AAEJ,QAAM,UAAU,YAAiC;AAC/C,QAAI,SAAU,QAAO;AACrB,gBAAY,YAAY;AACtB,YAAM,aAAa,MAAM,aAAa,UAAU,OAAO;AACvD,2BAAqB,WAAW,MAAM,aAAa,QAAQ;AAC3D,YAAM,WAAW,wBAAwB,WAAW,MAAM,WAAW;AACrE,YAAMA,UAAS,kBAAkB,EAAE,MAAM,SAA6B,CAAC;AACvE,cAAQ,EAAE,YAAY,WAAW,IAAI,GAAG,QAAAA,QAAO;AAC/C,sBAAgB,MAAM;AACtB,aAAO;AAAA,IACT,GAAG,EAAE,QAAQ,MAAM;AACjB,iBAAW;AAAA,IACb,CAAC;AACD,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,YAAiC;AACnD,QAAI,CAAC,SAAS,IAAI,IAAI,MAAM,aAAa,aAAa;AACpD,aAAO,QAAQ;AAAA,IACjB;AACA,WAAO;AAAA,EACT;AAEA,QAAM,SAA0B,OAAO,iBAAiB,UAAU;AAChE,UAAM,QAAQ,MAAM,YAAY;AAChC,QAAI;AACF,aAAO,MAAM,MAAM,OAAO,iBAAiB,KAAK;AAAA,IAClD,SAAS,KAAK;AAIZ,UAAI,IAAI,IAAI,gBAAgB,YAAa,OAAM;AAC/C,YAAM,YAAY,MAAM,QAAQ;AAChC,aAAO,UAAU,OAAO,iBAAiB,KAAK;AAAA,IAChD;AAAA,EACF;AAEA,SAAO;AACT;AAUA,SAAS,qBACP,MACA,aACA,UACM;AACN,aAAW,OAAO,KAAK,MAAM;AAC3B,UAAM,WAAW,IAAI;AACrB,QAAI,OAAO,aAAa,YAAY,CAAC,YAAY,IAAI,QAAQ,GAAG;AAC9D,YAAM,IAAI;AAAA,QACR;AAAA,QACA,oCAAoC,QAAQ;AAAA,QAC5C,EAAE,WAAW,SAAS;AAAA,QACtB,CAAC,WAAW;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,wBACP,MACA,aACwC;AAGxC,SAAO,KAAK,KAAK,OAAO,SAAO,OAAO,IAAI,QAAQ,YAAY,YAAY,IAAI,IAAI,GAAG,CAAC;AACxF;","names":["getKey"]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { createA2AClient, createMCPClient } from "../../protocols/index.mjs";
|
|
8
2
|
import { readBrandJsonUrl, readIdentityPosture } from "./capabilities-types.mjs";
|
|
9
3
|
import { canonicalizeOrigin } from "./canonicalize.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/resolve-agent.ts"],"sourcesContent":["/**\n * `resolveAgent` — implementation of the 8-step brand_json_url discovery\n * algorithm defined in `security.mdx` §\"Discovering an agent's signing\n * keys via `brand_json_url`\". Given an agent URL `A` and (optionally)\n * the protocol it speaks, returns the resolved brand.json document, the\n * matched `agents[]` entry, the JWKS URI, the JWKS itself, and a per-step\n * trace.\n *\n * Composition with existing pieces:\n * - Step 1 (capabilities fetch) goes through `ProtocolClient` via the\n * `createMCPClient` / `createA2AClient` factories — never a raw HTTP\n * GET on the agent URL. The spec is explicit: the agent URL is the\n * protocol endpoint, not a JSON capabilities document.\n * - Steps 4 / 8 (brand.json / JWKS fetches) go through `ssrfSafeFetch`\n * with `maxRedirects: 0` (the spec forbids redirects on the\n * bootstrap fetch — the `authoritative_location` carve-out is scoped\n * to webhook receivers and MUST NOT be inherited here) and the\n * strict-JSON parser (duplicate-key + prototype-property rejection).\n *\n * Error mapping:\n * - All wire-shape rejections surface as `AgentResolverError` with a\n * `request_signature_*` code matching the spec's rejection-code table.\n * - Counterparty-controlled detail fields (`brand_json_url`,\n * `matched_entries`, `parse_error`) are marked\n * `attackerInfluencedFields` so admin-UI renderers know to escape.\n * - SSRF refusals translate to a coarse `dns_error` classification —\n * never the resolved IP or hostname-to-address mapping the underlying\n * `SsrfRefusedError` carries.\n */\n\nimport { createA2AClient, createMCPClient } from '../../protocols';\n\nimport type { IdentityKeyOriginPurpose, IdentityPosture } from './capabilities-types';\nimport { readBrandJsonUrl, readIdentityPosture } from './capabilities-types';\nimport { canonicalizeOrigin } from './canonicalize';\nimport {\n checkOriginConsistency,\n checkRequiredOrigins,\n declaredSigningPurposes,\n type ConsistencyResult,\n} from './consistency';\nimport { AgentResolverError, type AgentResolverErrorDetail } from './errors';\nimport { eTldPlusOne, sameEtldPlusOne } from './etld';\nimport { MAX_BRAND_JSON_BYTES, MAX_JWKS_BYTES, safeFetchJson, SafeFetchError } from './fetch-helpers';\nimport { unwrapProtocolResponse } from '../protocol-response';\nimport { type AgentEntry, selectAgentByUrl, AgentSelectorError } from './select-agent';\n\nexport type AgentProtocol = 'mcp' | 'a2a';\n\nexport interface FetchCapabilitiesFn {\n (agentUrl: string): Promise<unknown>;\n}\n\nexport interface ResolveAgentOptions {\n /** Default `'mcp'`. Ignored when `fetchCapabilities` is supplied. */\n protocol?: AgentProtocol;\n /**\n * Override the capabilities-fetch step entirely. Tests pass a fake; production\n * callers who already hold a configured protocol client can wire it through\n * here rather than letting the resolver build a fresh transport.\n */\n fetchCapabilities?: FetchCapabilitiesFn;\n /**\n * Allow `http://` and private/loopback targets. Default false.\n *\n * Refused outside `{NODE_ENV=test, NODE_ENV=development}` unless the\n * adopter sets `ADCP_RESOLVER_ALLOW_PRIVATE_IP=1` as an explicit ops\n * acknowledgment — a security-critical entry point should fail closed\n * when the carve-out gets wired from a misconfigured env var. Matches\n * the project pattern around `createAdcpServer`'s in-memory-state and\n * `tenant-registry`'s NODE_ENV allowlist.\n */\n allowPrivateIp?: boolean;\n /**\n * Body caps for the brand.json + JWKS fetches. Default: brand.json 256 KiB,\n * JWKS 64 KiB — the budgets recommended by `security.mdx` §\"Quickstart\".\n * The capabilities-fetch cap is enforced upstream by `ProtocolClient`'s\n * own response-size limit; it is not configurable here.\n */\n bodyCaps?: { brandJsonBytes?: number; jwksBytes?: number };\n /** Total per-fetch timeout in ms. Default 10_000. */\n timeoutMs?: number;\n /**\n * Purposes the caller knows the agent is signing for but that aren't\n * inferable from a generic capabilities walk (governance-signing,\n * TMP-signing). Joined with the auto-detected set before the\n * `key_origins` missing check.\n */\n declaredPurposes?: readonly IdentityKeyOriginPurpose[];\n /**\n * Sell-side webhook publisher pin marker — when `webhook_signing` is\n * `true`, the step-7 origin-consistency check is skipped for the\n * webhook-signing purpose only (operator-side webhook-signing remains\n * checked). Buyer-side / receive-side verifiers MUST leave this unset.\n */\n publisherPinned?: { webhook_signing?: boolean };\n /** Override the current time (epoch seconds) — for deterministic tests. */\n now?: () => number;\n}\n\nexport interface TraceStep {\n step: number;\n name: string;\n ok: boolean;\n fetchedAt?: number;\n ageSeconds?: number;\n url?: string;\n detail?: Record<string, unknown> | AgentResolverErrorDetail;\n}\n\nexport interface AgentResolution {\n agentUrl: string;\n brandJsonUrl: string;\n agentEntry: AgentEntry;\n jwksUri: string;\n jwks: { keys: ReadonlyArray<Record<string, unknown>> };\n identityPosture: IdentityPosture | undefined;\n consistency:\n | { ok: true }\n | {\n ok: false;\n results: ReadonlyArray<ConsistencyResult>;\n };\n freshness: {\n capabilitiesFetchedAt: number;\n brandJsonFetchedAt: number;\n jwksFetchedAt: number;\n };\n /** `Cache-Control` header from the JWKS fetch, when the response carried one. */\n jwksCacheControl?: string;\n trace: ReadonlyArray<TraceStep>;\n}\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport async function resolveAgent(agentUrl: string, options: ResolveAgentOptions = {}): Promise<AgentResolution> {\n const trace: TraceStep[] = [];\n const now = options.now ?? (() => Math.floor(Date.now() / 1000));\n const allowPrivateIp = checkAllowPrivateIp(options.allowPrivateIp === true);\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const caps = options.bodyCaps ?? {};\n const brandJsonCap = caps.brandJsonBytes ?? MAX_BRAND_JSON_BYTES;\n const jwksCap = caps.jwksBytes ?? MAX_JWKS_BYTES;\n\n // ─── Step 1: fetch capabilities at the protocol layer ─────────────────\n const fetchCapabilities = options.fetchCapabilities ?? defaultFetchCapabilities(agentUrl, options.protocol ?? 'mcp');\n let capabilitiesPayload: unknown;\n let capabilitiesFetchedAt: number;\n try {\n const raw = await fetchCapabilities(agentUrl);\n capabilitiesPayload = unwrapProtocolResponse(raw);\n capabilitiesFetchedAt = now();\n pushTrace(trace, {\n step: 1,\n name: 'fetch_capabilities',\n ok: true,\n fetchedAt: capabilitiesFetchedAt,\n url: agentUrl,\n });\n } catch (err) {\n const detail: AgentResolverErrorDetail = {\n agent_url: agentUrl,\n dns_error: err instanceof SafeFetchError ? err.transport : 'fetch_failed',\n last_attempt_at: now(),\n };\n pushTrace(trace, { step: 1, name: 'fetch_capabilities', ok: false, url: agentUrl, detail });\n throw new AgentResolverError(\n 'request_signature_capabilities_unreachable',\n `Capabilities fetch failed for ${agentUrl}`,\n detail,\n ['agent_url']\n );\n }\n\n // ─── Step 2: read identity.brand_json_url ─────────────────────────────\n const brandJsonUrl = readBrandJsonUrl(capabilitiesPayload);\n const identityPosture = readIdentityPosture(capabilitiesPayload);\n // Spec mandates `https://`; dev/test deployments setting `allowPrivateIp`\n // are also allowed `http://` (matches the parallel carve-out in\n // `ssrfSafeFetch` and `BrandJsonJwksResolver` for loopback testing).\n const acceptsScheme = (url: string | undefined): url is string => {\n if (!url) return false;\n if (url.startsWith('https://')) return true;\n if (allowPrivateIp && url.startsWith('http://')) return true;\n return false;\n };\n if (!acceptsScheme(brandJsonUrl)) {\n const detail: AgentResolverErrorDetail = { agent_url: agentUrl };\n pushTrace(trace, { step: 2, name: 'read_brand_json_url', ok: false, detail });\n throw new AgentResolverError(\n 'request_signature_brand_json_url_missing',\n `identity.brand_json_url is absent or not https on ${agentUrl}`,\n detail,\n ['agent_url']\n );\n }\n pushTrace(trace, { step: 2, name: 'read_brand_json_url', ok: true, detail: { brand_json_url: brandJsonUrl } });\n\n // ─── Step 3: eTLD+1 origin binding ────────────────────────────────────\n // IP-literal hosts have no PSL match; under `allowPrivateIp` (dev/test)\n // we fall back to bare hostname equality so loopback fixtures work without\n // standing up a public-name server. Production callers who don't set\n // `allowPrivateIp` will hit `request_signature_brand_origin_mismatch` on\n // an IP literal — which is the right answer: an agent advertising an IP\n // brand_json_url has no business being trusted at the wire.\n let agentEtld1: string;\n let brandEtld1: string;\n let sameOrigin: boolean;\n try {\n agentEtld1 = eTldPlusOne(agentUrl);\n brandEtld1 = eTldPlusOne(brandJsonUrl);\n sameOrigin = sameEtldPlusOne(agentUrl, brandJsonUrl);\n } catch {\n if (allowPrivateIp) {\n const agentHost = new URL(agentUrl).hostname;\n const brandHost = new URL(brandJsonUrl).hostname;\n agentEtld1 = agentHost;\n brandEtld1 = brandHost;\n sameOrigin = agentHost === brandHost;\n } else {\n const detail: AgentResolverErrorDetail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };\n pushTrace(trace, { step: 3, name: 'etld1_binding', ok: false, detail });\n throw new AgentResolverError(\n 'request_signature_brand_origin_mismatch',\n `Cannot compute eTLD+1 for agent or brand.json host`,\n detail,\n ['agent_url', 'brand_json_url']\n );\n }\n }\n\n // ─── Step 4: fetch brand.json (no redirects, strict JSON) ─────────────\n let brandJson: unknown;\n let brandJsonFetchedAt: number;\n try {\n const fetched = await safeFetchJson(brandJsonUrl, 'brand.json', {\n allowPrivateIp,\n timeoutMs,\n maxBodyBytes: brandJsonCap,\n });\n brandJson = fetched.body;\n brandJsonFetchedAt = fetched.fetchedAt;\n pushTrace(trace, {\n step: 4,\n name: 'fetch_brand_json',\n ok: true,\n fetchedAt: brandJsonFetchedAt,\n url: brandJsonUrl,\n });\n } catch (err) {\n if (err instanceof SafeFetchError && /strict-JSON parse/.test(err.message)) {\n const detail: AgentResolverErrorDetail = {\n brand_json_url: brandJsonUrl,\n parse_error: err.message,\n last_attempt_at: now(),\n };\n pushTrace(trace, { step: 4, name: 'fetch_brand_json', ok: false, url: brandJsonUrl, detail });\n throw new AgentResolverError(\n 'request_signature_brand_json_malformed',\n `brand.json failed strict-JSON parse`,\n detail,\n ['brand_json_url', 'parse_error']\n );\n }\n const transport = err instanceof SafeFetchError ? err.transport : 'fetch_failed';\n const detail: AgentResolverErrorDetail = {\n brand_json_url: brandJsonUrl,\n dns_error: transport,\n last_attempt_at: now(),\n ...(err instanceof SafeFetchError && err.httpStatus !== undefined && { http_status: err.httpStatus }),\n };\n pushTrace(trace, { step: 4, name: 'fetch_brand_json', ok: false, url: brandJsonUrl, detail });\n throw new AgentResolverError('request_signature_brand_json_unreachable', `brand.json fetch failed`, detail, [\n 'brand_json_url',\n ]);\n }\n\n // Now run step 3's authorized_operators delegation check against the body.\n if (!sameOrigin) {\n const delegated = isAuthorizedOperator(brandJson, agentEtld1);\n if (!delegated) {\n const detail: AgentResolverErrorDetail = {\n agent_url: agentUrl,\n agent_etld1: agentEtld1,\n brand_json_url_etld1: brandEtld1,\n };\n pushTrace(trace, { step: 3, name: 'etld1_binding', ok: false, detail });\n throw new AgentResolverError(\n 'request_signature_brand_origin_mismatch',\n `Agent eTLD+1 ${agentEtld1} not delegated by brand.json authorized_operators[]`,\n detail,\n ['agent_url']\n );\n }\n pushTrace(trace, {\n step: 3,\n name: 'etld1_binding',\n ok: true,\n detail: { agent_etld1: agentEtld1, brand_json_url_etld1: brandEtld1, delegated_via_authorized_operators: true },\n });\n } else {\n pushTrace(trace, {\n step: 3,\n name: 'etld1_binding',\n ok: true,\n detail: { agent_etld1: agentEtld1, brand_json_url_etld1: brandEtld1 },\n });\n }\n\n // ─── Step 5: byte-equal agents[] selection ────────────────────────────\n let agentEntry: AgentEntry;\n try {\n agentEntry = selectAgentByUrl(brandJson, agentUrl);\n pushTrace(trace, { step: 5, name: 'select_agent', ok: true, detail: { url: agentEntry.url } });\n } catch (err) {\n if (err instanceof AgentSelectorError) {\n const detail: AgentResolverErrorDetail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };\n const attacker: Array<keyof AgentResolverErrorDetail> = ['agent_url', 'brand_json_url'];\n let code: 'request_signature_agent_not_in_brand_json' | 'request_signature_brand_json_ambiguous';\n if (err.code === 'agent_not_in_brand_json') {\n code = 'request_signature_agent_not_in_brand_json';\n } else {\n code = 'request_signature_brand_json_ambiguous';\n if (err.detail.matched_count !== undefined) detail.matched_count = err.detail.matched_count;\n if (err.detail.matched_entries !== undefined) {\n detail.matched_entries = err.detail.matched_entries.map(e => ({\n url: e.url,\n ...(e.jwks_uri !== undefined && { jwks_uri: e.jwks_uri }),\n }));\n attacker.push('matched_entries');\n }\n }\n pushTrace(trace, { step: 5, name: 'select_agent', ok: false, detail });\n throw new AgentResolverError(code, err.message, detail, attacker);\n }\n throw err;\n }\n\n // ─── Step 6: resolve jwks_uri ─────────────────────────────────────────\n const declaredJwksUri = agentEntry.jwks_uri;\n const jwksUriFromEntry =\n typeof declaredJwksUri === 'string' && acceptsScheme(declaredJwksUri) ? declaredJwksUri : undefined;\n const jwksUri = jwksUriFromEntry ?? `${originOf(agentUrl)}/.well-known/jwks.json`;\n pushTrace(trace, { step: 6, name: 'resolve_jwks_uri', ok: true, detail: { jwks_uri: jwksUri } });\n\n // ─── Step 7: identity.key_origins consistency ─────────────────────────\n const consistencyResults = runConsistencyChecks({\n capabilitiesPayload,\n identityPosture,\n jwksUri,\n publisherPinnedWebhookSigning: options.publisherPinned?.webhook_signing === true,\n extraDeclaredPurposes: options.declaredPurposes ?? [],\n });\n\n const failedConsistency = consistencyResults.filter(r => r.ok === false);\n if (failedConsistency.length > 0) {\n const first = failedConsistency[0]!;\n const detail: AgentResolverErrorDetail = { purpose: first.purpose };\n if (first.code === 'key_origin_mismatch') {\n detail.expected_origin = first.expected_origin;\n detail.actual_origin = first.actual_origin;\n pushTrace(trace, { step: 7, name: 'key_origins_consistency', ok: false, detail });\n throw new AgentResolverError(\n 'request_signature_key_origin_mismatch',\n `identity.key_origins.${first.purpose} mismatch`,\n detail\n );\n }\n detail.posture = first.posture;\n pushTrace(trace, { step: 7, name: 'key_origins_consistency', ok: false, detail });\n throw new AgentResolverError(\n 'request_signature_key_origin_missing',\n `identity.key_origins.${first.purpose} declaration missing`,\n detail\n );\n }\n pushTrace(trace, { step: 7, name: 'key_origins_consistency', ok: true });\n\n // ─── Step 8 (preamble): fetch JWKS ────────────────────────────────────\n let jwks: { keys: ReadonlyArray<Record<string, unknown>> };\n let jwksFetchedAt: number;\n let jwksCacheControl: string | undefined;\n try {\n const fetched = await safeFetchJson(jwksUri, 'jwks', {\n allowPrivateIp,\n timeoutMs,\n maxBodyBytes: jwksCap,\n });\n if (\n !fetched.body ||\n typeof fetched.body !== 'object' ||\n !Array.isArray((fetched.body as { keys?: unknown }).keys)\n ) {\n throw new SafeFetchError('jwks', 'fetch_failed', 'JWKS document has no keys[] array');\n }\n jwks = fetched.body as { keys: ReadonlyArray<Record<string, unknown>> };\n jwksFetchedAt = fetched.fetchedAt;\n if (typeof fetched.headers['cache-control'] === 'string') {\n jwksCacheControl = fetched.headers['cache-control'];\n }\n pushTrace(trace, { step: 8, name: 'fetch_jwks', ok: true, fetchedAt: jwksFetchedAt, url: jwksUri });\n } catch (err) {\n const transport = err instanceof SafeFetchError ? err.transport : 'fetch_failed';\n const detail: AgentResolverErrorDetail = {\n jwks_uri: jwksUri,\n dns_error: transport,\n last_attempt_at: now(),\n ...(err instanceof SafeFetchError && err.httpStatus !== undefined && { http_status: err.httpStatus }),\n };\n pushTrace(trace, { step: 8, name: 'fetch_jwks', ok: false, url: jwksUri, detail });\n // The spec hands step 8 off to the verifier checklist, where the\n // canonical \"JWKS unreachable\" code (`request_signature_key_unknown`)\n // only applies once a kid lookup has been attempted. The bootstrap\n // chain needs a code before we have a kid, so we emit the SDK-side\n // `request_signature_jwks_unreachable`. Operators triaging the\n // rejection see `detail.jwks_uri`, not `detail.brand_json_url`.\n throw new AgentResolverError('request_signature_jwks_unreachable', `JWKS fetch failed`, detail, ['jwks_uri']);\n }\n\n return {\n agentUrl,\n brandJsonUrl,\n agentEntry,\n jwksUri,\n jwks,\n identityPosture,\n consistency: { ok: true },\n freshness: { capabilitiesFetchedAt, brandJsonFetchedAt, jwksFetchedAt },\n ...(jwksCacheControl !== undefined && { jwksCacheControl }),\n trace: trace.map(annotateAge(now())),\n };\n}\n\n/**\n * Refuse `allowPrivateIp: true` outside `{NODE_ENV=test, NODE_ENV=development}`\n * unless the adopter set `ADCP_RESOLVER_ALLOW_PRIVATE_IP=1`. The flag lifts\n * the spec's HTTPS-only / public-IP-only constraint to enable loopback test\n * fixtures; in production it would silently widen the SSRF surface, so we\n * close the door at the public API rather than relying on caller discipline.\n */\nfunction checkAllowPrivateIp(requested: boolean): boolean {\n if (!requested) return false;\n const env = process.env.NODE_ENV;\n if (env === 'test' || env === 'development') return true;\n if (process.env.ADCP_RESOLVER_ALLOW_PRIVATE_IP === '1') return true;\n throw new Error(\n 'resolveAgent: allowPrivateIp=true refused outside {NODE_ENV=test, NODE_ENV=development}. ' +\n 'Set NODE_ENV appropriately for dev/test, or set ADCP_RESOLVER_ALLOW_PRIVATE_IP=1 as an ' +\n 'explicit ops acknowledgment if you genuinely need private-IP discovery in this process.'\n );\n}\n\nfunction defaultFetchCapabilities(agentUrl: string, protocol: AgentProtocol): FetchCapabilitiesFn {\n return async () => {\n const client = protocol === 'a2a' ? createA2AClient(agentUrl) : createMCPClient(agentUrl);\n return client.callTool('get_adcp_capabilities', {});\n };\n}\n\nfunction isAuthorizedOperator(brandJson: unknown, agentEtld1: string): boolean {\n if (!brandJson || typeof brandJson !== 'object') return false;\n const operators = (brandJson as { authorized_operators?: unknown }).authorized_operators;\n if (!Array.isArray(operators)) return false;\n for (const op of operators) {\n if (op && typeof op === 'object') {\n const domain = (op as { domain?: unknown }).domain;\n if (typeof domain === 'string') {\n try {\n if (eTldPlusOne(domain) === agentEtld1) return true;\n } catch {\n continue;\n }\n }\n }\n // Some brand.json shapes carry a bare-string entry rather than `{ domain }`.\n if (typeof op === 'string') {\n try {\n if (eTldPlusOne(op) === agentEtld1) return true;\n } catch {\n continue;\n }\n }\n }\n return false;\n}\n\nfunction originOf(url: string): string {\n return canonicalizeOrigin(url);\n}\n\nfunction pushTrace(trace: TraceStep[], step: TraceStep): void {\n trace.push(step);\n}\n\nfunction annotateAge(now: number): (step: TraceStep) => TraceStep {\n return step => (step.fetchedAt !== undefined ? { ...step, ageSeconds: Math.max(0, now - step.fetchedAt) } : step);\n}\n\ninterface ConsistencyArgs {\n capabilitiesPayload: unknown;\n identityPosture: IdentityPosture | undefined;\n jwksUri: string;\n publisherPinnedWebhookSigning: boolean;\n extraDeclaredPurposes: readonly IdentityKeyOriginPurpose[];\n}\n\nfunction runConsistencyChecks(args: ConsistencyArgs): ConsistencyResult[] {\n const results: ConsistencyResult[] = [];\n const declared = declaredSigningPurposes(args.capabilitiesPayload, args.extraDeclaredPurposes);\n\n // Missing-origin check first — independent of jwksUri origin matching.\n for (const missing of checkRequiredOrigins(declared, args.identityPosture?.key_origins)) {\n results.push(missing);\n }\n\n // Origin-match check: walk every declared origin and compare against jwksUri\n // host. Skip publisher-pinned webhook-signing only.\n const keyOrigins = args.identityPosture?.key_origins;\n if (keyOrigins) {\n for (const purpose of Object.keys(keyOrigins) as IdentityKeyOriginPurpose[]) {\n const declaredOrigin = keyOrigins[purpose];\n if (!declaredOrigin) continue;\n const result = checkOriginConsistency({\n purpose,\n declaredOrigin,\n resolvedJwksUri: args.jwksUri,\n publisherPinned: purpose === 'webhook_signing' && args.publisherPinnedWebhookSigning,\n });\n if (result.ok === false) results.push(result);\n }\n }\n return results;\n}\n"],"mappings":";;;;;;AA8BA,SAAS,iBAAiB,uBAAuB;AAGjD,SAAS,kBAAkB,2BAA2B;AACtD,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,0BAAyD;AAClE,SAAS,aAAa,uBAAuB;AAC7C,SAAS,sBAAsB,gBAAgB,eAAe,sBAAsB;AACpF,SAAS,8BAA8B;AACvC,SAA0B,kBAAkB,0BAA0B;AAwFtE,MAAM,qBAAqB;AAE3B,eAAsB,aAAa,UAAkB,UAA+B,CAAC,GAA6B;AAChH,QAAM,QAAqB,CAAC;AAC5B,QAAM,MAAM,QAAQ,QAAQ,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAC9D,QAAM,iBAAiB,oBAAoB,QAAQ,mBAAmB,IAAI;AAC1E,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,OAAO,QAAQ,YAAY,CAAC;AAClC,QAAM,eAAe,KAAK,kBAAkB;AAC5C,QAAM,UAAU,KAAK,aAAa;AAGlC,QAAM,oBAAoB,QAAQ,qBAAqB,yBAAyB,UAAU,QAAQ,YAAY,KAAK;AACnH,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,kBAAkB,QAAQ;AAC5C,0BAAsB,uBAAuB,GAAG;AAChD,4BAAwB,IAAI;AAC5B,cAAU,OAAO;AAAA,MACf,MAAM;AAAA,MACN,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,KAAK;AAAA,IACP,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,UAAM,SAAmC;AAAA,MACvC,WAAW;AAAA,MACX,WAAW,eAAe,iBAAiB,IAAI,YAAY;AAAA,MAC3D,iBAAiB,IAAI;AAAA,IACvB;AACA,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,sBAAsB,IAAI,OAAO,KAAK,UAAU,OAAO,CAAC;AAC1F,UAAM,IAAI;AAAA,MACR;AAAA,MACA,iCAAiC,QAAQ;AAAA,MACzC;AAAA,MACA,CAAC,WAAW;AAAA,IACd;AAAA,EACF;AAGA,QAAM,eAAe,iBAAiB,mBAAmB;AACzD,QAAM,kBAAkB,oBAAoB,mBAAmB;AAI/D,QAAM,gBAAgB,CAAC,QAA2C;AAChE,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI,IAAI,WAAW,UAAU,EAAG,QAAO;AACvC,QAAI,kBAAkB,IAAI,WAAW,SAAS,EAAG,QAAO;AACxD,WAAO;AAAA,EACT;AACA,MAAI,CAAC,cAAc,YAAY,GAAG;AAChC,UAAM,SAAmC,EAAE,WAAW,SAAS;AAC/D,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,uBAAuB,IAAI,OAAO,OAAO,CAAC;AAC5E,UAAM,IAAI;AAAA,MACR;AAAA,MACA,qDAAqD,QAAQ;AAAA,MAC7D;AAAA,MACA,CAAC,WAAW;AAAA,IACd;AAAA,EACF;AACA,YAAU,OAAO,EAAE,MAAM,GAAG,MAAM,uBAAuB,IAAI,MAAM,QAAQ,EAAE,gBAAgB,aAAa,EAAE,CAAC;AAS7G,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,iBAAa,YAAY,QAAQ;AACjC,iBAAa,YAAY,YAAY;AACrC,iBAAa,gBAAgB,UAAU,YAAY;AAAA,EACrD,QAAQ;AACN,QAAI,gBAAgB;AAClB,YAAM,YAAY,IAAI,IAAI,QAAQ,EAAE;AACpC,YAAM,YAAY,IAAI,IAAI,YAAY,EAAE;AACxC,mBAAa;AACb,mBAAa;AACb,mBAAa,cAAc;AAAA,IAC7B,OAAO;AACL,YAAM,SAAmC,EAAE,WAAW,UAAU,gBAAgB,aAAa;AAC7F,gBAAU,OAAO,EAAE,MAAM,GAAG,MAAM,iBAAiB,IAAI,OAAO,OAAO,CAAC;AACtE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA,CAAC,aAAa,gBAAgB;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AAGA,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,MAAM,cAAc,cAAc,cAAc;AAAA,MAC9D;AAAA,MACA;AAAA,MACA,cAAc;AAAA,IAChB,CAAC;AACD,gBAAY,QAAQ;AACpB,yBAAqB,QAAQ;AAC7B,cAAU,OAAO;AAAA,MACf,MAAM;AAAA,MACN,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,KAAK;AAAA,IACP,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,eAAe,kBAAkB,oBAAoB,KAAK,IAAI,OAAO,GAAG;AAC1E,YAAMA,UAAmC;AAAA,QACvC,gBAAgB;AAAA,QAChB,aAAa,IAAI;AAAA,QACjB,iBAAiB,IAAI;AAAA,MACvB;AACA,gBAAU,OAAO,EAAE,MAAM,GAAG,MAAM,oBAAoB,IAAI,OAAO,KAAK,cAAc,QAAAA,QAAO,CAAC;AAC5F,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACAA;AAAA,QACA,CAAC,kBAAkB,aAAa;AAAA,MAClC;AAAA,IACF;AACA,UAAM,YAAY,eAAe,iBAAiB,IAAI,YAAY;AAClE,UAAM,SAAmC;AAAA,MACvC,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,iBAAiB,IAAI;AAAA,MACrB,GAAI,eAAe,kBAAkB,IAAI,eAAe,UAAa,EAAE,aAAa,IAAI,WAAW;AAAA,IACrG;AACA,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,oBAAoB,IAAI,OAAO,KAAK,cAAc,OAAO,CAAC;AAC5F,UAAM,IAAI,mBAAmB,4CAA4C,2BAA2B,QAAQ;AAAA,MAC1G;AAAA,IACF,CAAC;AAAA,EACH;AAGA,MAAI,CAAC,YAAY;AACf,UAAM,YAAY,qBAAqB,WAAW,UAAU;AAC5D,QAAI,CAAC,WAAW;AACd,YAAM,SAAmC;AAAA,QACvC,WAAW;AAAA,QACX,aAAa;AAAA,QACb,sBAAsB;AAAA,MACxB;AACA,gBAAU,OAAO,EAAE,MAAM,GAAG,MAAM,iBAAiB,IAAI,OAAO,OAAO,CAAC;AACtE,YAAM,IAAI;AAAA,QACR;AAAA,QACA,gBAAgB,UAAU;AAAA,QAC1B;AAAA,QACA,CAAC,WAAW;AAAA,MACd;AAAA,IACF;AACA,cAAU,OAAO;AAAA,MACf,MAAM;AAAA,MACN,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,QAAQ,EAAE,aAAa,YAAY,sBAAsB,YAAY,oCAAoC,KAAK;AAAA,IAChH,CAAC;AAAA,EACH,OAAO;AACL,cAAU,OAAO;AAAA,MACf,MAAM;AAAA,MACN,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,QAAQ,EAAE,aAAa,YAAY,sBAAsB,WAAW;AAAA,IACtE,CAAC;AAAA,EACH;AAGA,MAAI;AACJ,MAAI;AACF,iBAAa,iBAAiB,WAAW,QAAQ;AACjD,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,gBAAgB,IAAI,MAAM,QAAQ,EAAE,KAAK,WAAW,IAAI,EAAE,CAAC;AAAA,EAC/F,SAAS,KAAK;AACZ,QAAI,eAAe,oBAAoB;AACrC,YAAM,SAAmC,EAAE,WAAW,UAAU,gBAAgB,aAAa;AAC7F,YAAM,WAAkD,CAAC,aAAa,gBAAgB;AACtF,UAAI;AACJ,UAAI,IAAI,SAAS,2BAA2B;AAC1C,eAAO;AAAA,MACT,OAAO;AACL,eAAO;AACP,YAAI,IAAI,OAAO,kBAAkB,OAAW,QAAO,gBAAgB,IAAI,OAAO;AAC9E,YAAI,IAAI,OAAO,oBAAoB,QAAW;AAC5C,iBAAO,kBAAkB,IAAI,OAAO,gBAAgB,IAAI,QAAM;AAAA,YAC5D,KAAK,EAAE;AAAA,YACP,GAAI,EAAE,aAAa,UAAa,EAAE,UAAU,EAAE,SAAS;AAAA,UACzD,EAAE;AACF,mBAAS,KAAK,iBAAiB;AAAA,QACjC;AAAA,MACF;AACA,gBAAU,OAAO,EAAE,MAAM,GAAG,MAAM,gBAAgB,IAAI,OAAO,OAAO,CAAC;AACrE,YAAM,IAAI,mBAAmB,MAAM,IAAI,SAAS,QAAQ,QAAQ;AAAA,IAClE;AACA,UAAM;AAAA,EACR;AAGA,QAAM,kBAAkB,WAAW;AACnC,QAAM,mBACJ,OAAO,oBAAoB,YAAY,cAAc,eAAe,IAAI,kBAAkB;AAC5F,QAAM,UAAU,oBAAoB,GAAG,SAAS,QAAQ,CAAC;AACzD,YAAU,OAAO,EAAE,MAAM,GAAG,MAAM,oBAAoB,IAAI,MAAM,QAAQ,EAAE,UAAU,QAAQ,EAAE,CAAC;AAG/F,QAAM,qBAAqB,qBAAqB;AAAA,IAC9C;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,QAAQ,iBAAiB,oBAAoB;AAAA,IAC5E,uBAAuB,QAAQ,oBAAoB,CAAC;AAAA,EACtD,CAAC;AAED,QAAM,oBAAoB,mBAAmB,OAAO,OAAK,EAAE,OAAO,KAAK;AACvE,MAAI,kBAAkB,SAAS,GAAG;AAChC,UAAM,QAAQ,kBAAkB,CAAC;AACjC,UAAM,SAAmC,EAAE,SAAS,MAAM,QAAQ;AAClE,QAAI,MAAM,SAAS,uBAAuB;AACxC,aAAO,kBAAkB,MAAM;AAC/B,aAAO,gBAAgB,MAAM;AAC7B,gBAAU,OAAO,EAAE,MAAM,GAAG,MAAM,2BAA2B,IAAI,OAAO,OAAO,CAAC;AAChF,YAAM,IAAI;AAAA,QACR;AAAA,QACA,wBAAwB,MAAM,OAAO;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AACA,WAAO,UAAU,MAAM;AACvB,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,2BAA2B,IAAI,OAAO,OAAO,CAAC;AAChF,UAAM,IAAI;AAAA,MACR;AAAA,MACA,wBAAwB,MAAM,OAAO;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACA,YAAU,OAAO,EAAE,MAAM,GAAG,MAAM,2BAA2B,IAAI,KAAK,CAAC;AAGvE,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,MAAM,cAAc,SAAS,QAAQ;AAAA,MACnD;AAAA,MACA;AAAA,MACA,cAAc;AAAA,IAChB,CAAC;AACD,QACE,CAAC,QAAQ,QACT,OAAO,QAAQ,SAAS,YACxB,CAAC,MAAM,QAAS,QAAQ,KAA4B,IAAI,GACxD;AACA,YAAM,IAAI,eAAe,QAAQ,gBAAgB,mCAAmC;AAAA,IACtF;AACA,WAAO,QAAQ;AACf,oBAAgB,QAAQ;AACxB,QAAI,OAAO,QAAQ,QAAQ,eAAe,MAAM,UAAU;AACxD,yBAAmB,QAAQ,QAAQ,eAAe;AAAA,IACpD;AACA,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,cAAc,IAAI,MAAM,WAAW,eAAe,KAAK,QAAQ,CAAC;AAAA,EACpG,SAAS,KAAK;AACZ,UAAM,YAAY,eAAe,iBAAiB,IAAI,YAAY;AAClE,UAAM,SAAmC;AAAA,MACvC,UAAU;AAAA,MACV,WAAW;AAAA,MACX,iBAAiB,IAAI;AAAA,MACrB,GAAI,eAAe,kBAAkB,IAAI,eAAe,UAAa,EAAE,aAAa,IAAI,WAAW;AAAA,IACrG;AACA,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,cAAc,IAAI,OAAO,KAAK,SAAS,OAAO,CAAC;AAOjF,UAAM,IAAI,mBAAmB,sCAAsC,qBAAqB,QAAQ,CAAC,UAAU,CAAC;AAAA,EAC9G;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,EAAE,IAAI,KAAK;AAAA,IACxB,WAAW,EAAE,uBAAuB,oBAAoB,cAAc;AAAA,IACtE,GAAI,qBAAqB,UAAa,EAAE,iBAAiB;AAAA,IACzD,OAAO,MAAM,IAAI,YAAY,IAAI,CAAC,CAAC;AAAA,EACrC;AACF;AASA,SAAS,oBAAoB,WAA6B;AACxD,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,QAAQ,UAAU,QAAQ,cAAe,QAAO;AACpD,MAAI,QAAQ,IAAI,mCAAmC,IAAK,QAAO;AAC/D,QAAM,IAAI;AAAA,IACR;AAAA,EAGF;AACF;AAEA,SAAS,yBAAyB,UAAkB,UAA8C;AAChG,SAAO,YAAY;AACjB,UAAM,SAAS,aAAa,QAAQ,gBAAgB,QAAQ,IAAI,gBAAgB,QAAQ;AACxF,WAAO,OAAO,SAAS,yBAAyB,CAAC,CAAC;AAAA,EACpD;AACF;AAEA,SAAS,qBAAqB,WAAoB,YAA6B;AAC7E,MAAI,CAAC,aAAa,OAAO,cAAc,SAAU,QAAO;AACxD,QAAM,YAAa,UAAiD;AACpE,MAAI,CAAC,MAAM,QAAQ,SAAS,EAAG,QAAO;AACtC,aAAW,MAAM,WAAW;AAC1B,QAAI,MAAM,OAAO,OAAO,UAAU;AAChC,YAAM,SAAU,GAA4B;AAC5C,UAAI,OAAO,WAAW,UAAU;AAC9B,YAAI;AACF,cAAI,YAAY,MAAM,MAAM,WAAY,QAAO;AAAA,QACjD,QAAQ;AACN;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,OAAO,UAAU;AAC1B,UAAI;AACF,YAAI,YAAY,EAAE,MAAM,WAAY,QAAO;AAAA,MAC7C,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,SAAS,KAAqB;AACrC,SAAO,mBAAmB,GAAG;AAC/B;AAEA,SAAS,UAAU,OAAoB,MAAuB;AAC5D,QAAM,KAAK,IAAI;AACjB;AAEA,SAAS,YAAY,KAA6C;AAChE,SAAO,UAAS,KAAK,cAAc,SAAY,EAAE,GAAG,MAAM,YAAY,KAAK,IAAI,GAAG,MAAM,KAAK,SAAS,EAAE,IAAI;AAC9G;AAUA,SAAS,qBAAqB,MAA4C;AACxE,QAAM,UAA+B,CAAC;AACtC,QAAM,WAAW,wBAAwB,KAAK,qBAAqB,KAAK,qBAAqB;AAG7F,aAAW,WAAW,qBAAqB,UAAU,KAAK,iBAAiB,WAAW,GAAG;AACvF,YAAQ,KAAK,OAAO;AAAA,EACtB;AAIA,QAAM,aAAa,KAAK,iBAAiB;AACzC,MAAI,YAAY;AACd,eAAW,WAAW,OAAO,KAAK,UAAU,GAAiC;AAC3E,YAAM,iBAAiB,WAAW,OAAO;AACzC,UAAI,CAAC,eAAgB;AACrB,YAAM,SAAS,uBAAuB;AAAA,QACpC;AAAA,QACA;AAAA,QACA,iBAAiB,KAAK;AAAA,QACtB,iBAAiB,YAAY,qBAAqB,KAAK;AAAA,MACzD,CAAC;AACD,UAAI,OAAO,OAAO,MAAO,SAAQ,KAAK,MAAM;AAAA,IAC9C;AAAA,EACF;AACA,SAAO;AACT;","names":["detail"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/resolve-agent.ts"],"sourcesContent":["/**\n * `resolveAgent` — implementation of the 8-step brand_json_url discovery\n * algorithm defined in `security.mdx` §\"Discovering an agent's signing\n * keys via `brand_json_url`\". Given an agent URL `A` and (optionally)\n * the protocol it speaks, returns the resolved brand.json document, the\n * matched `agents[]` entry, the JWKS URI, the JWKS itself, and a per-step\n * trace.\n *\n * Composition with existing pieces:\n * - Step 1 (capabilities fetch) goes through `ProtocolClient` via the\n * `createMCPClient` / `createA2AClient` factories — never a raw HTTP\n * GET on the agent URL. The spec is explicit: the agent URL is the\n * protocol endpoint, not a JSON capabilities document.\n * - Steps 4 / 8 (brand.json / JWKS fetches) go through `ssrfSafeFetch`\n * with `maxRedirects: 0` (the spec forbids redirects on the\n * bootstrap fetch — the `authoritative_location` carve-out is scoped\n * to webhook receivers and MUST NOT be inherited here) and the\n * strict-JSON parser (duplicate-key + prototype-property rejection).\n *\n * Error mapping:\n * - All wire-shape rejections surface as `AgentResolverError` with a\n * `request_signature_*` code matching the spec's rejection-code table.\n * - Counterparty-controlled detail fields (`brand_json_url`,\n * `matched_entries`, `parse_error`) are marked\n * `attackerInfluencedFields` so admin-UI renderers know to escape.\n * - SSRF refusals translate to a coarse `dns_error` classification —\n * never the resolved IP or hostname-to-address mapping the underlying\n * `SsrfRefusedError` carries.\n */\n\nimport { createA2AClient, createMCPClient } from '../../protocols';\n\nimport type { IdentityKeyOriginPurpose, IdentityPosture } from './capabilities-types';\nimport { readBrandJsonUrl, readIdentityPosture } from './capabilities-types';\nimport { canonicalizeOrigin } from './canonicalize';\nimport {\n checkOriginConsistency,\n checkRequiredOrigins,\n declaredSigningPurposes,\n type ConsistencyResult,\n} from './consistency';\nimport { AgentResolverError, type AgentResolverErrorDetail } from './errors';\nimport { eTldPlusOne, sameEtldPlusOne } from './etld';\nimport { MAX_BRAND_JSON_BYTES, MAX_JWKS_BYTES, safeFetchJson, SafeFetchError } from './fetch-helpers';\nimport { unwrapProtocolResponse } from '../protocol-response';\nimport { type AgentEntry, selectAgentByUrl, AgentSelectorError } from './select-agent';\n\nexport type AgentProtocol = 'mcp' | 'a2a';\n\nexport interface FetchCapabilitiesFn {\n (agentUrl: string): Promise<unknown>;\n}\n\nexport interface ResolveAgentOptions {\n /** Default `'mcp'`. Ignored when `fetchCapabilities` is supplied. */\n protocol?: AgentProtocol;\n /**\n * Override the capabilities-fetch step entirely. Tests pass a fake; production\n * callers who already hold a configured protocol client can wire it through\n * here rather than letting the resolver build a fresh transport.\n */\n fetchCapabilities?: FetchCapabilitiesFn;\n /**\n * Allow `http://` and private/loopback targets. Default false.\n *\n * Refused outside `{NODE_ENV=test, NODE_ENV=development}` unless the\n * adopter sets `ADCP_RESOLVER_ALLOW_PRIVATE_IP=1` as an explicit ops\n * acknowledgment — a security-critical entry point should fail closed\n * when the carve-out gets wired from a misconfigured env var. Matches\n * the project pattern around `createAdcpServer`'s in-memory-state and\n * `tenant-registry`'s NODE_ENV allowlist.\n */\n allowPrivateIp?: boolean;\n /**\n * Body caps for the brand.json + JWKS fetches. Default: brand.json 256 KiB,\n * JWKS 64 KiB — the budgets recommended by `security.mdx` §\"Quickstart\".\n * The capabilities-fetch cap is enforced upstream by `ProtocolClient`'s\n * own response-size limit; it is not configurable here.\n */\n bodyCaps?: { brandJsonBytes?: number; jwksBytes?: number };\n /** Total per-fetch timeout in ms. Default 10_000. */\n timeoutMs?: number;\n /**\n * Purposes the caller knows the agent is signing for but that aren't\n * inferable from a generic capabilities walk (governance-signing,\n * TMP-signing). Joined with the auto-detected set before the\n * `key_origins` missing check.\n */\n declaredPurposes?: readonly IdentityKeyOriginPurpose[];\n /**\n * Sell-side webhook publisher pin marker — when `webhook_signing` is\n * `true`, the step-7 origin-consistency check is skipped for the\n * webhook-signing purpose only (operator-side webhook-signing remains\n * checked). Buyer-side / receive-side verifiers MUST leave this unset.\n */\n publisherPinned?: { webhook_signing?: boolean };\n /** Override the current time (epoch seconds) — for deterministic tests. */\n now?: () => number;\n}\n\nexport interface TraceStep {\n step: number;\n name: string;\n ok: boolean;\n fetchedAt?: number;\n ageSeconds?: number;\n url?: string;\n detail?: Record<string, unknown> | AgentResolverErrorDetail;\n}\n\nexport interface AgentResolution {\n agentUrl: string;\n brandJsonUrl: string;\n agentEntry: AgentEntry;\n jwksUri: string;\n jwks: { keys: ReadonlyArray<Record<string, unknown>> };\n identityPosture: IdentityPosture | undefined;\n consistency:\n | { ok: true }\n | {\n ok: false;\n results: ReadonlyArray<ConsistencyResult>;\n };\n freshness: {\n capabilitiesFetchedAt: number;\n brandJsonFetchedAt: number;\n jwksFetchedAt: number;\n };\n /** `Cache-Control` header from the JWKS fetch, when the response carried one. */\n jwksCacheControl?: string;\n trace: ReadonlyArray<TraceStep>;\n}\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport async function resolveAgent(agentUrl: string, options: ResolveAgentOptions = {}): Promise<AgentResolution> {\n const trace: TraceStep[] = [];\n const now = options.now ?? (() => Math.floor(Date.now() / 1000));\n const allowPrivateIp = checkAllowPrivateIp(options.allowPrivateIp === true);\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const caps = options.bodyCaps ?? {};\n const brandJsonCap = caps.brandJsonBytes ?? MAX_BRAND_JSON_BYTES;\n const jwksCap = caps.jwksBytes ?? MAX_JWKS_BYTES;\n\n // ─── Step 1: fetch capabilities at the protocol layer ─────────────────\n const fetchCapabilities = options.fetchCapabilities ?? defaultFetchCapabilities(agentUrl, options.protocol ?? 'mcp');\n let capabilitiesPayload: unknown;\n let capabilitiesFetchedAt: number;\n try {\n const raw = await fetchCapabilities(agentUrl);\n capabilitiesPayload = unwrapProtocolResponse(raw);\n capabilitiesFetchedAt = now();\n pushTrace(trace, {\n step: 1,\n name: 'fetch_capabilities',\n ok: true,\n fetchedAt: capabilitiesFetchedAt,\n url: agentUrl,\n });\n } catch (err) {\n const detail: AgentResolverErrorDetail = {\n agent_url: agentUrl,\n dns_error: err instanceof SafeFetchError ? err.transport : 'fetch_failed',\n last_attempt_at: now(),\n };\n pushTrace(trace, { step: 1, name: 'fetch_capabilities', ok: false, url: agentUrl, detail });\n throw new AgentResolverError(\n 'request_signature_capabilities_unreachable',\n `Capabilities fetch failed for ${agentUrl}`,\n detail,\n ['agent_url']\n );\n }\n\n // ─── Step 2: read identity.brand_json_url ─────────────────────────────\n const brandJsonUrl = readBrandJsonUrl(capabilitiesPayload);\n const identityPosture = readIdentityPosture(capabilitiesPayload);\n // Spec mandates `https://`; dev/test deployments setting `allowPrivateIp`\n // are also allowed `http://` (matches the parallel carve-out in\n // `ssrfSafeFetch` and `BrandJsonJwksResolver` for loopback testing).\n const acceptsScheme = (url: string | undefined): url is string => {\n if (!url) return false;\n if (url.startsWith('https://')) return true;\n if (allowPrivateIp && url.startsWith('http://')) return true;\n return false;\n };\n if (!acceptsScheme(brandJsonUrl)) {\n const detail: AgentResolverErrorDetail = { agent_url: agentUrl };\n pushTrace(trace, { step: 2, name: 'read_brand_json_url', ok: false, detail });\n throw new AgentResolverError(\n 'request_signature_brand_json_url_missing',\n `identity.brand_json_url is absent or not https on ${agentUrl}`,\n detail,\n ['agent_url']\n );\n }\n pushTrace(trace, { step: 2, name: 'read_brand_json_url', ok: true, detail: { brand_json_url: brandJsonUrl } });\n\n // ─── Step 3: eTLD+1 origin binding ────────────────────────────────────\n // IP-literal hosts have no PSL match; under `allowPrivateIp` (dev/test)\n // we fall back to bare hostname equality so loopback fixtures work without\n // standing up a public-name server. Production callers who don't set\n // `allowPrivateIp` will hit `request_signature_brand_origin_mismatch` on\n // an IP literal — which is the right answer: an agent advertising an IP\n // brand_json_url has no business being trusted at the wire.\n let agentEtld1: string;\n let brandEtld1: string;\n let sameOrigin: boolean;\n try {\n agentEtld1 = eTldPlusOne(agentUrl);\n brandEtld1 = eTldPlusOne(brandJsonUrl);\n sameOrigin = sameEtldPlusOne(agentUrl, brandJsonUrl);\n } catch {\n if (allowPrivateIp) {\n const agentHost = new URL(agentUrl).hostname;\n const brandHost = new URL(brandJsonUrl).hostname;\n agentEtld1 = agentHost;\n brandEtld1 = brandHost;\n sameOrigin = agentHost === brandHost;\n } else {\n const detail: AgentResolverErrorDetail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };\n pushTrace(trace, { step: 3, name: 'etld1_binding', ok: false, detail });\n throw new AgentResolverError(\n 'request_signature_brand_origin_mismatch',\n `Cannot compute eTLD+1 for agent or brand.json host`,\n detail,\n ['agent_url', 'brand_json_url']\n );\n }\n }\n\n // ─── Step 4: fetch brand.json (no redirects, strict JSON) ─────────────\n let brandJson: unknown;\n let brandJsonFetchedAt: number;\n try {\n const fetched = await safeFetchJson(brandJsonUrl, 'brand.json', {\n allowPrivateIp,\n timeoutMs,\n maxBodyBytes: brandJsonCap,\n });\n brandJson = fetched.body;\n brandJsonFetchedAt = fetched.fetchedAt;\n pushTrace(trace, {\n step: 4,\n name: 'fetch_brand_json',\n ok: true,\n fetchedAt: brandJsonFetchedAt,\n url: brandJsonUrl,\n });\n } catch (err) {\n if (err instanceof SafeFetchError && /strict-JSON parse/.test(err.message)) {\n const detail: AgentResolverErrorDetail = {\n brand_json_url: brandJsonUrl,\n parse_error: err.message,\n last_attempt_at: now(),\n };\n pushTrace(trace, { step: 4, name: 'fetch_brand_json', ok: false, url: brandJsonUrl, detail });\n throw new AgentResolverError(\n 'request_signature_brand_json_malformed',\n `brand.json failed strict-JSON parse`,\n detail,\n ['brand_json_url', 'parse_error']\n );\n }\n const transport = err instanceof SafeFetchError ? err.transport : 'fetch_failed';\n const detail: AgentResolverErrorDetail = {\n brand_json_url: brandJsonUrl,\n dns_error: transport,\n last_attempt_at: now(),\n ...(err instanceof SafeFetchError && err.httpStatus !== undefined && { http_status: err.httpStatus }),\n };\n pushTrace(trace, { step: 4, name: 'fetch_brand_json', ok: false, url: brandJsonUrl, detail });\n throw new AgentResolverError('request_signature_brand_json_unreachable', `brand.json fetch failed`, detail, [\n 'brand_json_url',\n ]);\n }\n\n // Now run step 3's authorized_operators delegation check against the body.\n if (!sameOrigin) {\n const delegated = isAuthorizedOperator(brandJson, agentEtld1);\n if (!delegated) {\n const detail: AgentResolverErrorDetail = {\n agent_url: agentUrl,\n agent_etld1: agentEtld1,\n brand_json_url_etld1: brandEtld1,\n };\n pushTrace(trace, { step: 3, name: 'etld1_binding', ok: false, detail });\n throw new AgentResolverError(\n 'request_signature_brand_origin_mismatch',\n `Agent eTLD+1 ${agentEtld1} not delegated by brand.json authorized_operators[]`,\n detail,\n ['agent_url']\n );\n }\n pushTrace(trace, {\n step: 3,\n name: 'etld1_binding',\n ok: true,\n detail: { agent_etld1: agentEtld1, brand_json_url_etld1: brandEtld1, delegated_via_authorized_operators: true },\n });\n } else {\n pushTrace(trace, {\n step: 3,\n name: 'etld1_binding',\n ok: true,\n detail: { agent_etld1: agentEtld1, brand_json_url_etld1: brandEtld1 },\n });\n }\n\n // ─── Step 5: byte-equal agents[] selection ────────────────────────────\n let agentEntry: AgentEntry;\n try {\n agentEntry = selectAgentByUrl(brandJson, agentUrl);\n pushTrace(trace, { step: 5, name: 'select_agent', ok: true, detail: { url: agentEntry.url } });\n } catch (err) {\n if (err instanceof AgentSelectorError) {\n const detail: AgentResolverErrorDetail = { agent_url: agentUrl, brand_json_url: brandJsonUrl };\n const attacker: Array<keyof AgentResolverErrorDetail> = ['agent_url', 'brand_json_url'];\n let code: 'request_signature_agent_not_in_brand_json' | 'request_signature_brand_json_ambiguous';\n if (err.code === 'agent_not_in_brand_json') {\n code = 'request_signature_agent_not_in_brand_json';\n } else {\n code = 'request_signature_brand_json_ambiguous';\n if (err.detail.matched_count !== undefined) detail.matched_count = err.detail.matched_count;\n if (err.detail.matched_entries !== undefined) {\n detail.matched_entries = err.detail.matched_entries.map(e => ({\n url: e.url,\n ...(e.jwks_uri !== undefined && { jwks_uri: e.jwks_uri }),\n }));\n attacker.push('matched_entries');\n }\n }\n pushTrace(trace, { step: 5, name: 'select_agent', ok: false, detail });\n throw new AgentResolverError(code, err.message, detail, attacker);\n }\n throw err;\n }\n\n // ─── Step 6: resolve jwks_uri ─────────────────────────────────────────\n const declaredJwksUri = agentEntry.jwks_uri;\n const jwksUriFromEntry =\n typeof declaredJwksUri === 'string' && acceptsScheme(declaredJwksUri) ? declaredJwksUri : undefined;\n const jwksUri = jwksUriFromEntry ?? `${originOf(agentUrl)}/.well-known/jwks.json`;\n pushTrace(trace, { step: 6, name: 'resolve_jwks_uri', ok: true, detail: { jwks_uri: jwksUri } });\n\n // ─── Step 7: identity.key_origins consistency ─────────────────────────\n const consistencyResults = runConsistencyChecks({\n capabilitiesPayload,\n identityPosture,\n jwksUri,\n publisherPinnedWebhookSigning: options.publisherPinned?.webhook_signing === true,\n extraDeclaredPurposes: options.declaredPurposes ?? [],\n });\n\n const failedConsistency = consistencyResults.filter(r => r.ok === false);\n if (failedConsistency.length > 0) {\n const first = failedConsistency[0]!;\n const detail: AgentResolverErrorDetail = { purpose: first.purpose };\n if (first.code === 'key_origin_mismatch') {\n detail.expected_origin = first.expected_origin;\n detail.actual_origin = first.actual_origin;\n pushTrace(trace, { step: 7, name: 'key_origins_consistency', ok: false, detail });\n throw new AgentResolverError(\n 'request_signature_key_origin_mismatch',\n `identity.key_origins.${first.purpose} mismatch`,\n detail\n );\n }\n detail.posture = first.posture;\n pushTrace(trace, { step: 7, name: 'key_origins_consistency', ok: false, detail });\n throw new AgentResolverError(\n 'request_signature_key_origin_missing',\n `identity.key_origins.${first.purpose} declaration missing`,\n detail\n );\n }\n pushTrace(trace, { step: 7, name: 'key_origins_consistency', ok: true });\n\n // ─── Step 8 (preamble): fetch JWKS ────────────────────────────────────\n let jwks: { keys: ReadonlyArray<Record<string, unknown>> };\n let jwksFetchedAt: number;\n let jwksCacheControl: string | undefined;\n try {\n const fetched = await safeFetchJson(jwksUri, 'jwks', {\n allowPrivateIp,\n timeoutMs,\n maxBodyBytes: jwksCap,\n });\n if (\n !fetched.body ||\n typeof fetched.body !== 'object' ||\n !Array.isArray((fetched.body as { keys?: unknown }).keys)\n ) {\n throw new SafeFetchError('jwks', 'fetch_failed', 'JWKS document has no keys[] array');\n }\n jwks = fetched.body as { keys: ReadonlyArray<Record<string, unknown>> };\n jwksFetchedAt = fetched.fetchedAt;\n if (typeof fetched.headers['cache-control'] === 'string') {\n jwksCacheControl = fetched.headers['cache-control'];\n }\n pushTrace(trace, { step: 8, name: 'fetch_jwks', ok: true, fetchedAt: jwksFetchedAt, url: jwksUri });\n } catch (err) {\n const transport = err instanceof SafeFetchError ? err.transport : 'fetch_failed';\n const detail: AgentResolverErrorDetail = {\n jwks_uri: jwksUri,\n dns_error: transport,\n last_attempt_at: now(),\n ...(err instanceof SafeFetchError && err.httpStatus !== undefined && { http_status: err.httpStatus }),\n };\n pushTrace(trace, { step: 8, name: 'fetch_jwks', ok: false, url: jwksUri, detail });\n // The spec hands step 8 off to the verifier checklist, where the\n // canonical \"JWKS unreachable\" code (`request_signature_key_unknown`)\n // only applies once a kid lookup has been attempted. The bootstrap\n // chain needs a code before we have a kid, so we emit the SDK-side\n // `request_signature_jwks_unreachable`. Operators triaging the\n // rejection see `detail.jwks_uri`, not `detail.brand_json_url`.\n throw new AgentResolverError('request_signature_jwks_unreachable', `JWKS fetch failed`, detail, ['jwks_uri']);\n }\n\n return {\n agentUrl,\n brandJsonUrl,\n agentEntry,\n jwksUri,\n jwks,\n identityPosture,\n consistency: { ok: true },\n freshness: { capabilitiesFetchedAt, brandJsonFetchedAt, jwksFetchedAt },\n ...(jwksCacheControl !== undefined && { jwksCacheControl }),\n trace: trace.map(annotateAge(now())),\n };\n}\n\n/**\n * Refuse `allowPrivateIp: true` outside `{NODE_ENV=test, NODE_ENV=development}`\n * unless the adopter set `ADCP_RESOLVER_ALLOW_PRIVATE_IP=1`. The flag lifts\n * the spec's HTTPS-only / public-IP-only constraint to enable loopback test\n * fixtures; in production it would silently widen the SSRF surface, so we\n * close the door at the public API rather than relying on caller discipline.\n */\nfunction checkAllowPrivateIp(requested: boolean): boolean {\n if (!requested) return false;\n const env = process.env.NODE_ENV;\n if (env === 'test' || env === 'development') return true;\n if (process.env.ADCP_RESOLVER_ALLOW_PRIVATE_IP === '1') return true;\n throw new Error(\n 'resolveAgent: allowPrivateIp=true refused outside {NODE_ENV=test, NODE_ENV=development}. ' +\n 'Set NODE_ENV appropriately for dev/test, or set ADCP_RESOLVER_ALLOW_PRIVATE_IP=1 as an ' +\n 'explicit ops acknowledgment if you genuinely need private-IP discovery in this process.'\n );\n}\n\nfunction defaultFetchCapabilities(agentUrl: string, protocol: AgentProtocol): FetchCapabilitiesFn {\n return async () => {\n const client = protocol === 'a2a' ? createA2AClient(agentUrl) : createMCPClient(agentUrl);\n return client.callTool('get_adcp_capabilities', {});\n };\n}\n\nfunction isAuthorizedOperator(brandJson: unknown, agentEtld1: string): boolean {\n if (!brandJson || typeof brandJson !== 'object') return false;\n const operators = (brandJson as { authorized_operators?: unknown }).authorized_operators;\n if (!Array.isArray(operators)) return false;\n for (const op of operators) {\n if (op && typeof op === 'object') {\n const domain = (op as { domain?: unknown }).domain;\n if (typeof domain === 'string') {\n try {\n if (eTldPlusOne(domain) === agentEtld1) return true;\n } catch {\n continue;\n }\n }\n }\n // Some brand.json shapes carry a bare-string entry rather than `{ domain }`.\n if (typeof op === 'string') {\n try {\n if (eTldPlusOne(op) === agentEtld1) return true;\n } catch {\n continue;\n }\n }\n }\n return false;\n}\n\nfunction originOf(url: string): string {\n return canonicalizeOrigin(url);\n}\n\nfunction pushTrace(trace: TraceStep[], step: TraceStep): void {\n trace.push(step);\n}\n\nfunction annotateAge(now: number): (step: TraceStep) => TraceStep {\n return step => (step.fetchedAt !== undefined ? { ...step, ageSeconds: Math.max(0, now - step.fetchedAt) } : step);\n}\n\ninterface ConsistencyArgs {\n capabilitiesPayload: unknown;\n identityPosture: IdentityPosture | undefined;\n jwksUri: string;\n publisherPinnedWebhookSigning: boolean;\n extraDeclaredPurposes: readonly IdentityKeyOriginPurpose[];\n}\n\nfunction runConsistencyChecks(args: ConsistencyArgs): ConsistencyResult[] {\n const results: ConsistencyResult[] = [];\n const declared = declaredSigningPurposes(args.capabilitiesPayload, args.extraDeclaredPurposes);\n\n // Missing-origin check first — independent of jwksUri origin matching.\n for (const missing of checkRequiredOrigins(declared, args.identityPosture?.key_origins)) {\n results.push(missing);\n }\n\n // Origin-match check: walk every declared origin and compare against jwksUri\n // host. Skip publisher-pinned webhook-signing only.\n const keyOrigins = args.identityPosture?.key_origins;\n if (keyOrigins) {\n for (const purpose of Object.keys(keyOrigins) as IdentityKeyOriginPurpose[]) {\n const declaredOrigin = keyOrigins[purpose];\n if (!declaredOrigin) continue;\n const result = checkOriginConsistency({\n purpose,\n declaredOrigin,\n resolvedJwksUri: args.jwksUri,\n publisherPinned: purpose === 'webhook_signing' && args.publisherPinnedWebhookSigning,\n });\n if (result.ok === false) results.push(result);\n }\n }\n return results;\n}\n"],"mappings":"AA8BA,SAAS,iBAAiB,uBAAuB;AAGjD,SAAS,kBAAkB,2BAA2B;AACtD,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,0BAAyD;AAClE,SAAS,aAAa,uBAAuB;AAC7C,SAAS,sBAAsB,gBAAgB,eAAe,sBAAsB;AACpF,SAAS,8BAA8B;AACvC,SAA0B,kBAAkB,0BAA0B;AAwFtE,MAAM,qBAAqB;AAE3B,eAAsB,aAAa,UAAkB,UAA+B,CAAC,GAA6B;AAChH,QAAM,QAAqB,CAAC;AAC5B,QAAM,MAAM,QAAQ,QAAQ,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAC9D,QAAM,iBAAiB,oBAAoB,QAAQ,mBAAmB,IAAI;AAC1E,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,OAAO,QAAQ,YAAY,CAAC;AAClC,QAAM,eAAe,KAAK,kBAAkB;AAC5C,QAAM,UAAU,KAAK,aAAa;AAGlC,QAAM,oBAAoB,QAAQ,qBAAqB,yBAAyB,UAAU,QAAQ,YAAY,KAAK;AACnH,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,kBAAkB,QAAQ;AAC5C,0BAAsB,uBAAuB,GAAG;AAChD,4BAAwB,IAAI;AAC5B,cAAU,OAAO;AAAA,MACf,MAAM;AAAA,MACN,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,KAAK;AAAA,IACP,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,UAAM,SAAmC;AAAA,MACvC,WAAW;AAAA,MACX,WAAW,eAAe,iBAAiB,IAAI,YAAY;AAAA,MAC3D,iBAAiB,IAAI;AAAA,IACvB;AACA,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,sBAAsB,IAAI,OAAO,KAAK,UAAU,OAAO,CAAC;AAC1F,UAAM,IAAI;AAAA,MACR;AAAA,MACA,iCAAiC,QAAQ;AAAA,MACzC;AAAA,MACA,CAAC,WAAW;AAAA,IACd;AAAA,EACF;AAGA,QAAM,eAAe,iBAAiB,mBAAmB;AACzD,QAAM,kBAAkB,oBAAoB,mBAAmB;AAI/D,QAAM,gBAAgB,CAAC,QAA2C;AAChE,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI,IAAI,WAAW,UAAU,EAAG,QAAO;AACvC,QAAI,kBAAkB,IAAI,WAAW,SAAS,EAAG,QAAO;AACxD,WAAO;AAAA,EACT;AACA,MAAI,CAAC,cAAc,YAAY,GAAG;AAChC,UAAM,SAAmC,EAAE,WAAW,SAAS;AAC/D,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,uBAAuB,IAAI,OAAO,OAAO,CAAC;AAC5E,UAAM,IAAI;AAAA,MACR;AAAA,MACA,qDAAqD,QAAQ;AAAA,MAC7D;AAAA,MACA,CAAC,WAAW;AAAA,IACd;AAAA,EACF;AACA,YAAU,OAAO,EAAE,MAAM,GAAG,MAAM,uBAAuB,IAAI,MAAM,QAAQ,EAAE,gBAAgB,aAAa,EAAE,CAAC;AAS7G,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,iBAAa,YAAY,QAAQ;AACjC,iBAAa,YAAY,YAAY;AACrC,iBAAa,gBAAgB,UAAU,YAAY;AAAA,EACrD,QAAQ;AACN,QAAI,gBAAgB;AAClB,YAAM,YAAY,IAAI,IAAI,QAAQ,EAAE;AACpC,YAAM,YAAY,IAAI,IAAI,YAAY,EAAE;AACxC,mBAAa;AACb,mBAAa;AACb,mBAAa,cAAc;AAAA,IAC7B,OAAO;AACL,YAAM,SAAmC,EAAE,WAAW,UAAU,gBAAgB,aAAa;AAC7F,gBAAU,OAAO,EAAE,MAAM,GAAG,MAAM,iBAAiB,IAAI,OAAO,OAAO,CAAC;AACtE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA,CAAC,aAAa,gBAAgB;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AAGA,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,MAAM,cAAc,cAAc,cAAc;AAAA,MAC9D;AAAA,MACA;AAAA,MACA,cAAc;AAAA,IAChB,CAAC;AACD,gBAAY,QAAQ;AACpB,yBAAqB,QAAQ;AAC7B,cAAU,OAAO;AAAA,MACf,MAAM;AAAA,MACN,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,KAAK;AAAA,IACP,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,eAAe,kBAAkB,oBAAoB,KAAK,IAAI,OAAO,GAAG;AAC1E,YAAMA,UAAmC;AAAA,QACvC,gBAAgB;AAAA,QAChB,aAAa,IAAI;AAAA,QACjB,iBAAiB,IAAI;AAAA,MACvB;AACA,gBAAU,OAAO,EAAE,MAAM,GAAG,MAAM,oBAAoB,IAAI,OAAO,KAAK,cAAc,QAAAA,QAAO,CAAC;AAC5F,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACAA;AAAA,QACA,CAAC,kBAAkB,aAAa;AAAA,MAClC;AAAA,IACF;AACA,UAAM,YAAY,eAAe,iBAAiB,IAAI,YAAY;AAClE,UAAM,SAAmC;AAAA,MACvC,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,iBAAiB,IAAI;AAAA,MACrB,GAAI,eAAe,kBAAkB,IAAI,eAAe,UAAa,EAAE,aAAa,IAAI,WAAW;AAAA,IACrG;AACA,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,oBAAoB,IAAI,OAAO,KAAK,cAAc,OAAO,CAAC;AAC5F,UAAM,IAAI,mBAAmB,4CAA4C,2BAA2B,QAAQ;AAAA,MAC1G;AAAA,IACF,CAAC;AAAA,EACH;AAGA,MAAI,CAAC,YAAY;AACf,UAAM,YAAY,qBAAqB,WAAW,UAAU;AAC5D,QAAI,CAAC,WAAW;AACd,YAAM,SAAmC;AAAA,QACvC,WAAW;AAAA,QACX,aAAa;AAAA,QACb,sBAAsB;AAAA,MACxB;AACA,gBAAU,OAAO,EAAE,MAAM,GAAG,MAAM,iBAAiB,IAAI,OAAO,OAAO,CAAC;AACtE,YAAM,IAAI;AAAA,QACR;AAAA,QACA,gBAAgB,UAAU;AAAA,QAC1B;AAAA,QACA,CAAC,WAAW;AAAA,MACd;AAAA,IACF;AACA,cAAU,OAAO;AAAA,MACf,MAAM;AAAA,MACN,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,QAAQ,EAAE,aAAa,YAAY,sBAAsB,YAAY,oCAAoC,KAAK;AAAA,IAChH,CAAC;AAAA,EACH,OAAO;AACL,cAAU,OAAO;AAAA,MACf,MAAM;AAAA,MACN,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,QAAQ,EAAE,aAAa,YAAY,sBAAsB,WAAW;AAAA,IACtE,CAAC;AAAA,EACH;AAGA,MAAI;AACJ,MAAI;AACF,iBAAa,iBAAiB,WAAW,QAAQ;AACjD,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,gBAAgB,IAAI,MAAM,QAAQ,EAAE,KAAK,WAAW,IAAI,EAAE,CAAC;AAAA,EAC/F,SAAS,KAAK;AACZ,QAAI,eAAe,oBAAoB;AACrC,YAAM,SAAmC,EAAE,WAAW,UAAU,gBAAgB,aAAa;AAC7F,YAAM,WAAkD,CAAC,aAAa,gBAAgB;AACtF,UAAI;AACJ,UAAI,IAAI,SAAS,2BAA2B;AAC1C,eAAO;AAAA,MACT,OAAO;AACL,eAAO;AACP,YAAI,IAAI,OAAO,kBAAkB,OAAW,QAAO,gBAAgB,IAAI,OAAO;AAC9E,YAAI,IAAI,OAAO,oBAAoB,QAAW;AAC5C,iBAAO,kBAAkB,IAAI,OAAO,gBAAgB,IAAI,QAAM;AAAA,YAC5D,KAAK,EAAE;AAAA,YACP,GAAI,EAAE,aAAa,UAAa,EAAE,UAAU,EAAE,SAAS;AAAA,UACzD,EAAE;AACF,mBAAS,KAAK,iBAAiB;AAAA,QACjC;AAAA,MACF;AACA,gBAAU,OAAO,EAAE,MAAM,GAAG,MAAM,gBAAgB,IAAI,OAAO,OAAO,CAAC;AACrE,YAAM,IAAI,mBAAmB,MAAM,IAAI,SAAS,QAAQ,QAAQ;AAAA,IAClE;AACA,UAAM;AAAA,EACR;AAGA,QAAM,kBAAkB,WAAW;AACnC,QAAM,mBACJ,OAAO,oBAAoB,YAAY,cAAc,eAAe,IAAI,kBAAkB;AAC5F,QAAM,UAAU,oBAAoB,GAAG,SAAS,QAAQ,CAAC;AACzD,YAAU,OAAO,EAAE,MAAM,GAAG,MAAM,oBAAoB,IAAI,MAAM,QAAQ,EAAE,UAAU,QAAQ,EAAE,CAAC;AAG/F,QAAM,qBAAqB,qBAAqB;AAAA,IAC9C;AAAA,IACA;AAAA,IACA;AAAA,IACA,+BAA+B,QAAQ,iBAAiB,oBAAoB;AAAA,IAC5E,uBAAuB,QAAQ,oBAAoB,CAAC;AAAA,EACtD,CAAC;AAED,QAAM,oBAAoB,mBAAmB,OAAO,OAAK,EAAE,OAAO,KAAK;AACvE,MAAI,kBAAkB,SAAS,GAAG;AAChC,UAAM,QAAQ,kBAAkB,CAAC;AACjC,UAAM,SAAmC,EAAE,SAAS,MAAM,QAAQ;AAClE,QAAI,MAAM,SAAS,uBAAuB;AACxC,aAAO,kBAAkB,MAAM;AAC/B,aAAO,gBAAgB,MAAM;AAC7B,gBAAU,OAAO,EAAE,MAAM,GAAG,MAAM,2BAA2B,IAAI,OAAO,OAAO,CAAC;AAChF,YAAM,IAAI;AAAA,QACR;AAAA,QACA,wBAAwB,MAAM,OAAO;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AACA,WAAO,UAAU,MAAM;AACvB,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,2BAA2B,IAAI,OAAO,OAAO,CAAC;AAChF,UAAM,IAAI;AAAA,MACR;AAAA,MACA,wBAAwB,MAAM,OAAO;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACA,YAAU,OAAO,EAAE,MAAM,GAAG,MAAM,2BAA2B,IAAI,KAAK,CAAC;AAGvE,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,UAAM,UAAU,MAAM,cAAc,SAAS,QAAQ;AAAA,MACnD;AAAA,MACA;AAAA,MACA,cAAc;AAAA,IAChB,CAAC;AACD,QACE,CAAC,QAAQ,QACT,OAAO,QAAQ,SAAS,YACxB,CAAC,MAAM,QAAS,QAAQ,KAA4B,IAAI,GACxD;AACA,YAAM,IAAI,eAAe,QAAQ,gBAAgB,mCAAmC;AAAA,IACtF;AACA,WAAO,QAAQ;AACf,oBAAgB,QAAQ;AACxB,QAAI,OAAO,QAAQ,QAAQ,eAAe,MAAM,UAAU;AACxD,yBAAmB,QAAQ,QAAQ,eAAe;AAAA,IACpD;AACA,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,cAAc,IAAI,MAAM,WAAW,eAAe,KAAK,QAAQ,CAAC;AAAA,EACpG,SAAS,KAAK;AACZ,UAAM,YAAY,eAAe,iBAAiB,IAAI,YAAY;AAClE,UAAM,SAAmC;AAAA,MACvC,UAAU;AAAA,MACV,WAAW;AAAA,MACX,iBAAiB,IAAI;AAAA,MACrB,GAAI,eAAe,kBAAkB,IAAI,eAAe,UAAa,EAAE,aAAa,IAAI,WAAW;AAAA,IACrG;AACA,cAAU,OAAO,EAAE,MAAM,GAAG,MAAM,cAAc,IAAI,OAAO,KAAK,SAAS,OAAO,CAAC;AAOjF,UAAM,IAAI,mBAAmB,sCAAsC,qBAAqB,QAAQ,CAAC,UAAU,CAAC;AAAA,EAC9G;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,EAAE,IAAI,KAAK;AAAA,IACxB,WAAW,EAAE,uBAAuB,oBAAoB,cAAc;AAAA,IACtE,GAAI,qBAAqB,UAAa,EAAE,iBAAiB;AAAA,IACzD,OAAO,MAAM,IAAI,YAAY,IAAI,CAAC,CAAC;AAAA,EACrC;AACF;AASA,SAAS,oBAAoB,WAA6B;AACxD,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,QAAQ,UAAU,QAAQ,cAAe,QAAO;AACpD,MAAI,QAAQ,IAAI,mCAAmC,IAAK,QAAO;AAC/D,QAAM,IAAI;AAAA,IACR;AAAA,EAGF;AACF;AAEA,SAAS,yBAAyB,UAAkB,UAA8C;AAChG,SAAO,YAAY;AACjB,UAAM,SAAS,aAAa,QAAQ,gBAAgB,QAAQ,IAAI,gBAAgB,QAAQ;AACxF,WAAO,OAAO,SAAS,yBAAyB,CAAC,CAAC;AAAA,EACpD;AACF;AAEA,SAAS,qBAAqB,WAAoB,YAA6B;AAC7E,MAAI,CAAC,aAAa,OAAO,cAAc,SAAU,QAAO;AACxD,QAAM,YAAa,UAAiD;AACpE,MAAI,CAAC,MAAM,QAAQ,SAAS,EAAG,QAAO;AACtC,aAAW,MAAM,WAAW;AAC1B,QAAI,MAAM,OAAO,OAAO,UAAU;AAChC,YAAM,SAAU,GAA4B;AAC5C,UAAI,OAAO,WAAW,UAAU;AAC9B,YAAI;AACF,cAAI,YAAY,MAAM,MAAM,WAAY,QAAO;AAAA,QACjD,QAAQ;AACN;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,OAAO,UAAU;AAC1B,UAAI;AACF,YAAI,YAAY,EAAE,MAAM,WAAY,QAAO;AAAA,MAC7C,QAAQ;AACN;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,SAAS,KAAqB;AACrC,SAAO,mBAAmB,GAAG;AAC/B;AAEA,SAAS,UAAU,OAAoB,MAAuB;AAC5D,QAAM,KAAK,IAAI;AACjB;AAEA,SAAS,YAAY,KAA6C;AAChE,SAAO,UAAS,KAAK,cAAc,SAAY,EAAE,GAAG,MAAM,YAAY,KAAK,IAAI,GAAG,MAAM,KAAK,SAAS,EAAE,IAAI;AAC9G;AAUA,SAAS,qBAAqB,MAA4C;AACxE,QAAM,UAA+B,CAAC;AACtC,QAAM,WAAW,wBAAwB,KAAK,qBAAqB,KAAK,qBAAqB;AAG7F,aAAW,WAAW,qBAAqB,UAAU,KAAK,iBAAiB,WAAW,GAAG;AACvF,YAAQ,KAAK,OAAO;AAAA,EACtB;AAIA,QAAM,aAAa,KAAK,iBAAiB;AACzC,MAAI,YAAY;AACd,eAAW,WAAW,OAAO,KAAK,UAAU,GAAiC;AAC3E,YAAM,iBAAiB,WAAW,OAAO;AACzC,UAAI,CAAC,eAAgB;AACrB,YAAM,SAAS,uBAAuB;AAAA,QACpC;AAAA,QACA;AAAA,QACA,iBAAiB,KAAK;AAAA,QACtB,iBAAiB,YAAY,qBAAqB,KAAK;AAAA,MACzD,CAAC;AACD,UAAI,OAAO,OAAO,MAAO,SAAQ,KAAK,MAAM;AAAA,IAC9C;AAAA,EACF;AACA,SAAO;AACT;","names":["detail"]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
class AgentSelectorError extends Error {
|
|
8
2
|
code;
|
|
9
3
|
detail;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/select-agent.ts"],"sourcesContent":["/**\n * Step 5 of the brand_json_url discovery algorithm: locate the brand.json\n * `agents[]` entry whose `url` byte-equals the agent URL the verifier is\n * resolving. The byte-equal rule is deliberate (security.mdx §\"Discovering\n * an agent's signing keys via `brand_json_url`\" step 5) — no canonicalization,\n * because the most common operator misconfiguration is a trailing-slash or\n * scheme mismatch (`https://x.com/mcp` vs `https://x.com/mcp/`), and silently\n * canonicalizing past that hides the bug. The match must be exact.\n *\n * Both flat (`agents[]` at top level) and house-portfolio\n * (`house.agents[]` + `brands[].agents[]`) brand.json shapes are supported,\n * because either may carry the entry that matches the agent URL.\n */\n\nexport type AgentSelectorErrorCode = 'agent_not_in_brand_json' | 'brand_json_ambiguous';\n\nexport interface AgentEntry {\n url: string;\n jwks_uri?: string;\n /** Other fields preserved verbatim — the resolver passes the entry to the caller. */\n [key: string]: unknown;\n}\n\nexport class AgentSelectorError extends Error {\n readonly code: AgentSelectorErrorCode;\n readonly detail: { agent_url: string; matched_count?: number; matched_entries?: AgentEntry[] };\n constructor(\n code: AgentSelectorErrorCode,\n message: string,\n detail: { agent_url: string; matched_count?: number; matched_entries?: AgentEntry[] }\n ) {\n super(message);\n this.name = 'AgentSelectorError';\n this.code = code;\n this.detail = detail;\n }\n}\n\n/**\n * Walk every `agents[]` array reachable from the brand.json document and\n * collect entries with a string `url`. Entries without a string `url` are\n * silently skipped — they cannot match an agent URL, and rejecting the\n * whole document here would let a single malformed entry deny verification\n * for every agent the document declares.\n */\nexport function collectAgentEntries(brandJson: unknown): AgentEntry[] {\n if (!brandJson || typeof brandJson !== 'object') return [];\n const entries: AgentEntry[] = [];\n const obj = brandJson as Record<string, unknown>;\n pushAgentArray(obj.agents, entries);\n const house = obj.house;\n if (house && typeof house === 'object') {\n pushAgentArray((house as Record<string, unknown>).agents, entries);\n }\n if (Array.isArray(obj.brands)) {\n for (const brand of obj.brands) {\n if (brand && typeof brand === 'object') {\n pushAgentArray((brand as Record<string, unknown>).agents, entries);\n }\n }\n }\n return entries;\n}\n\nfunction pushAgentArray(value: unknown, out: AgentEntry[]): void {\n if (!Array.isArray(value)) return;\n for (const entry of value) {\n if (entry && typeof entry === 'object') {\n const e = entry as Record<string, unknown>;\n if (typeof e.url === 'string') {\n out.push(e as AgentEntry);\n }\n }\n }\n}\n\n/**\n * Find the unique `agents[]` entry whose `url` byte-equals `agentUrl`.\n *\n * - Returns the matched entry on a unique hit.\n * - Throws `AgentSelectorError('agent_not_in_brand_json')` on zero matches.\n * - Throws `AgentSelectorError('brand_json_ambiguous')` on multiple matches.\n * `matched_count` and `matched_entries` are populated on the error so the\n * caller can map them onto `request_signature_brand_json_ambiguous` detail\n * fields. `matched_entries` reflects counterparty-controlled state and is\n * marked attacker-influenceable in the resolver's error mapping.\n */\nexport function selectAgentByUrl(brandJson: unknown, agentUrl: string): AgentEntry {\n const entries = collectAgentEntries(brandJson);\n const matches = entries.filter(e => e.url === agentUrl);\n if (matches.length === 0) {\n throw new AgentSelectorError(\n 'agent_not_in_brand_json',\n `No brand.json agent entry has url byte-equal to ${agentUrl}`,\n { agent_url: agentUrl }\n );\n }\n if (matches.length > 1) {\n throw new AgentSelectorError('brand_json_ambiguous', `Multiple brand.json agent entries match ${agentUrl}`, {\n agent_url: agentUrl,\n matched_count: matches.length,\n matched_entries: matches,\n });\n }\n return matches[0]!;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/select-agent.ts"],"sourcesContent":["/**\n * Step 5 of the brand_json_url discovery algorithm: locate the brand.json\n * `agents[]` entry whose `url` byte-equals the agent URL the verifier is\n * resolving. The byte-equal rule is deliberate (security.mdx §\"Discovering\n * an agent's signing keys via `brand_json_url`\" step 5) — no canonicalization,\n * because the most common operator misconfiguration is a trailing-slash or\n * scheme mismatch (`https://x.com/mcp` vs `https://x.com/mcp/`), and silently\n * canonicalizing past that hides the bug. The match must be exact.\n *\n * Both flat (`agents[]` at top level) and house-portfolio\n * (`house.agents[]` + `brands[].agents[]`) brand.json shapes are supported,\n * because either may carry the entry that matches the agent URL.\n */\n\nexport type AgentSelectorErrorCode = 'agent_not_in_brand_json' | 'brand_json_ambiguous';\n\nexport interface AgentEntry {\n url: string;\n jwks_uri?: string;\n /** Other fields preserved verbatim — the resolver passes the entry to the caller. */\n [key: string]: unknown;\n}\n\nexport class AgentSelectorError extends Error {\n readonly code: AgentSelectorErrorCode;\n readonly detail: { agent_url: string; matched_count?: number; matched_entries?: AgentEntry[] };\n constructor(\n code: AgentSelectorErrorCode,\n message: string,\n detail: { agent_url: string; matched_count?: number; matched_entries?: AgentEntry[] }\n ) {\n super(message);\n this.name = 'AgentSelectorError';\n this.code = code;\n this.detail = detail;\n }\n}\n\n/**\n * Walk every `agents[]` array reachable from the brand.json document and\n * collect entries with a string `url`. Entries without a string `url` are\n * silently skipped — they cannot match an agent URL, and rejecting the\n * whole document here would let a single malformed entry deny verification\n * for every agent the document declares.\n */\nexport function collectAgentEntries(brandJson: unknown): AgentEntry[] {\n if (!brandJson || typeof brandJson !== 'object') return [];\n const entries: AgentEntry[] = [];\n const obj = brandJson as Record<string, unknown>;\n pushAgentArray(obj.agents, entries);\n const house = obj.house;\n if (house && typeof house === 'object') {\n pushAgentArray((house as Record<string, unknown>).agents, entries);\n }\n if (Array.isArray(obj.brands)) {\n for (const brand of obj.brands) {\n if (brand && typeof brand === 'object') {\n pushAgentArray((brand as Record<string, unknown>).agents, entries);\n }\n }\n }\n return entries;\n}\n\nfunction pushAgentArray(value: unknown, out: AgentEntry[]): void {\n if (!Array.isArray(value)) return;\n for (const entry of value) {\n if (entry && typeof entry === 'object') {\n const e = entry as Record<string, unknown>;\n if (typeof e.url === 'string') {\n out.push(e as AgentEntry);\n }\n }\n }\n}\n\n/**\n * Find the unique `agents[]` entry whose `url` byte-equals `agentUrl`.\n *\n * - Returns the matched entry on a unique hit.\n * - Throws `AgentSelectorError('agent_not_in_brand_json')` on zero matches.\n * - Throws `AgentSelectorError('brand_json_ambiguous')` on multiple matches.\n * `matched_count` and `matched_entries` are populated on the error so the\n * caller can map them onto `request_signature_brand_json_ambiguous` detail\n * fields. `matched_entries` reflects counterparty-controlled state and is\n * marked attacker-influenceable in the resolver's error mapping.\n */\nexport function selectAgentByUrl(brandJson: unknown, agentUrl: string): AgentEntry {\n const entries = collectAgentEntries(brandJson);\n const matches = entries.filter(e => e.url === agentUrl);\n if (matches.length === 0) {\n throw new AgentSelectorError(\n 'agent_not_in_brand_json',\n `No brand.json agent entry has url byte-equal to ${agentUrl}`,\n { agent_url: agentUrl }\n );\n }\n if (matches.length > 1) {\n throw new AgentSelectorError('brand_json_ambiguous', `Multiple brand.json agent entries match ${agentUrl}`, {\n agent_url: agentUrl,\n matched_count: matches.length,\n matched_entries: matches,\n });\n }\n return matches[0]!;\n}\n"],"mappings":"AAuBO,MAAM,2BAA2B,MAAM;AAAA,EACnC;AAAA,EACA;AAAA,EACT,YACE,MACA,SACA,QACA;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;AASO,SAAS,oBAAoB,WAAkC;AACpE,MAAI,CAAC,aAAa,OAAO,cAAc,SAAU,QAAO,CAAC;AACzD,QAAM,UAAwB,CAAC;AAC/B,QAAM,MAAM;AACZ,iBAAe,IAAI,QAAQ,OAAO;AAClC,QAAM,QAAQ,IAAI;AAClB,MAAI,SAAS,OAAO,UAAU,UAAU;AACtC,mBAAgB,MAAkC,QAAQ,OAAO;AAAA,EACnE;AACA,MAAI,MAAM,QAAQ,IAAI,MAAM,GAAG;AAC7B,eAAW,SAAS,IAAI,QAAQ;AAC9B,UAAI,SAAS,OAAO,UAAU,UAAU;AACtC,uBAAgB,MAAkC,QAAQ,OAAO;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,eAAe,OAAgB,KAAyB;AAC/D,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG;AAC3B,aAAW,SAAS,OAAO;AACzB,QAAI,SAAS,OAAO,UAAU,UAAU;AACtC,YAAM,IAAI;AACV,UAAI,OAAO,EAAE,QAAQ,UAAU;AAC7B,YAAI,KAAK,CAAe;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACF;AAaO,SAAS,iBAAiB,WAAoB,UAA8B;AACjF,QAAM,UAAU,oBAAoB,SAAS;AAC7C,QAAM,UAAU,QAAQ,OAAO,OAAK,EAAE,QAAQ,QAAQ;AACtD,MAAI,QAAQ,WAAW,GAAG;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,MACA,mDAAmD,QAAQ;AAAA,MAC3D,EAAE,WAAW,SAAS;AAAA,IACxB;AAAA,EACF;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI,mBAAmB,wBAAwB,2CAA2C,QAAQ,IAAI;AAAA,MAC1G,WAAW;AAAA,MACX,eAAe,QAAQ;AAAA,MACvB,iBAAiB;AAAA,IACnB,CAAC;AAAA,EACH;AACA,SAAO,QAAQ,CAAC;AAClB;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import sjp from "secure-json-parse";
|
|
8
2
|
class StrictJsonError extends Error {
|
|
9
3
|
code;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/strict-json.ts"],"sourcesContent":["/**\n * Strict JSON parser for the brand_json_url discovery chain. Two hardening\n * passes the spec mandates that the platform `JSON.parse` does not provide:\n *\n * 1. **Duplicate-key rejection.** `JSON.parse('{\"a\":1,\"a\":2}')` silently\n * keeps the last value. The brand_json_url verifier algorithm\n * (security.mdx step 4) requires duplicate-key detection because the\n * same trust-root document MUST NOT parse to two different shapes\n * across verifiers — that is the parser-differential vector that\n * step 14 of the verifier checklist closes on the request body, and\n * the same closure applies on the brand.json bootstrap fetch.\n *\n * 2. **Prototype-pollution rejection.** Backed by `secure-json-parse` —\n * `__proto__` and `constructor` keys raise rather than mutating the\n * prototype chain.\n *\n * Body cap is enforced by the caller (the SSRF fetch wrapper). This module\n * works against an already-bounded UTF-8 string. Errors are typed so the\n * resolver can surface `request_signature_brand_json_malformed` directly.\n */\n\nimport sjp from 'secure-json-parse';\n\nexport type StrictJsonErrorCode = 'invalid_json' | 'duplicate_key' | 'forbidden_prototype_property';\n\nexport class StrictJsonError extends Error {\n readonly code: StrictJsonErrorCode;\n readonly detail: { offset?: number; key?: string };\n constructor(code: StrictJsonErrorCode, message: string, detail: { offset?: number; key?: string } = {}) {\n super(message);\n this.name = 'StrictJsonError';\n this.code = code;\n this.detail = detail;\n }\n}\n\n/**\n * Parse JSON text under the strict rules above. Returns the parsed value on\n * success; throws `StrictJsonError` on any reject path.\n *\n * Implementation: first scan the source for duplicate keys at any object\n * scope, then hand the source to `secure-json-parse` for prototype-property\n * rejection and the actual structural parse. Two passes is fine for the\n * 256 KiB brand.json budget — both are linear, the constant factor is small,\n * and we never trust the tokenizer to produce the value (it's only a guard).\n */\nexport function parseStrictJson(text: string): unknown {\n rejectDuplicateKeys(text);\n try {\n return sjp.parse(text, null, { protoAction: 'error', constructorAction: 'error' });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes('forbidden prototype property')) {\n throw new StrictJsonError('forbidden_prototype_property', message);\n }\n throw new StrictJsonError('invalid_json', message);\n }\n}\n\n/**\n * Walk the source character-by-character and reject when any object scope\n * declares the same key twice. Comments are not legal JSON, so a `//` or\n * `/*` inside a string is fine and outside a string would already be a\n * parse error (caught downstream). This routine only needs to track:\n * - whether we are inside a string (with escape handling),\n * - the stack of open object scopes (each owning a `Set<string>`),\n * - whether the next quoted-string at object scope is the key half of\n * a key-value pair.\n */\nfunction rejectDuplicateKeys(text: string): void {\n type Scope = { kind: 'object'; keys: Set<string> } | { kind: 'array' };\n const stack: Scope[] = [];\n /** True when at an object scope and the next string token is a key. */\n let expectingKey = false;\n let i = 0;\n const len = text.length;\n\n while (i < len) {\n const ch = text[i]!;\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') {\n i++;\n continue;\n }\n if (ch === '{') {\n stack.push({ kind: 'object', keys: new Set<string>() });\n expectingKey = true;\n i++;\n continue;\n }\n if (ch === '}') {\n stack.pop();\n expectingKey = false;\n i++;\n continue;\n }\n if (ch === '[') {\n stack.push({ kind: 'array' });\n expectingKey = false;\n i++;\n continue;\n }\n if (ch === ']') {\n stack.pop();\n expectingKey = false;\n i++;\n continue;\n }\n if (ch === ',') {\n const top = stack[stack.length - 1];\n expectingKey = top?.kind === 'object';\n i++;\n continue;\n }\n if (ch === ':') {\n expectingKey = false;\n i++;\n continue;\n }\n if (ch === '\"') {\n const { value, end } = readString(text, i);\n if (expectingKey) {\n const top = stack[stack.length - 1];\n if (top?.kind === 'object') {\n if (top.keys.has(value)) {\n throw new StrictJsonError('duplicate_key', `Duplicate key \"${value}\" at offset ${i}`, {\n offset: i,\n key: value,\n });\n }\n top.keys.add(value);\n }\n }\n i = end;\n continue;\n }\n // Any other character is part of a primitive (number, true/false/null);\n // skip until we hit whitespace, structural punctuation, or end of input.\n while (i < len) {\n const c = text[i]!;\n if (c === ',' || c === '}' || c === ']' || c === ' ' || c === '\\t' || c === '\\n' || c === '\\r') break;\n i++;\n }\n }\n}\n\nfunction readString(text: string, start: number): { value: string; end: number } {\n // text[start] is the opening quote.\n let i = start + 1;\n let out = '';\n const len = text.length;\n while (i < len) {\n const ch = text[i]!;\n if (ch === '\"') {\n return { value: out, end: i + 1 };\n }\n // RFC 8259 §7: unescaped control characters (U+0000–U+001F) are forbidden\n // inside JSON strings. The platform `JSON.parse` would reject them; the\n // tokenizer must reject them too so its accepted dialect tracks JSON's\n // (otherwise an attacker who can inject a literal control char into a\n // brand.json key smuggles a key that the tokenizer dedupes but the\n // parser rejects, decoupling our two passes).\n if (ch.charCodeAt(0) < 0x20) {\n throw new StrictJsonError('invalid_json', `Unescaped control character at offset ${i} (RFC 8259 §7)`, {\n offset: i,\n });\n }\n if (ch === '\\\\') {\n const next = text[i + 1];\n if (next === undefined) {\n throw new StrictJsonError('invalid_json', `Unterminated escape at offset ${i}`, { offset: i });\n }\n if (next === '\"' || next === '\\\\' || next === '/') {\n out += next;\n i += 2;\n continue;\n }\n if (next === 'b') {\n out += '\\b';\n i += 2;\n continue;\n }\n if (next === 'f') {\n out += '\\f';\n i += 2;\n continue;\n }\n if (next === 'n') {\n out += '\\n';\n i += 2;\n continue;\n }\n if (next === 'r') {\n out += '\\r';\n i += 2;\n continue;\n }\n if (next === 't') {\n out += '\\t';\n i += 2;\n continue;\n }\n if (next === 'u') {\n const hex = text.slice(i + 2, i + 6);\n if (hex.length !== 4 || /[^0-9a-fA-F]/.test(hex)) {\n throw new StrictJsonError('invalid_json', `Invalid unicode escape at offset ${i}`, { offset: i });\n }\n out += String.fromCharCode(parseInt(hex, 16));\n i += 6;\n continue;\n }\n throw new StrictJsonError('invalid_json', `Invalid escape \\\\${next} at offset ${i}`, { offset: i });\n }\n out += ch;\n i++;\n }\n throw new StrictJsonError('invalid_json', `Unterminated string starting at offset ${start}`, { offset: start });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/signing/agent-resolver/strict-json.ts"],"sourcesContent":["/**\n * Strict JSON parser for the brand_json_url discovery chain. Two hardening\n * passes the spec mandates that the platform `JSON.parse` does not provide:\n *\n * 1. **Duplicate-key rejection.** `JSON.parse('{\"a\":1,\"a\":2}')` silently\n * keeps the last value. The brand_json_url verifier algorithm\n * (security.mdx step 4) requires duplicate-key detection because the\n * same trust-root document MUST NOT parse to two different shapes\n * across verifiers — that is the parser-differential vector that\n * step 14 of the verifier checklist closes on the request body, and\n * the same closure applies on the brand.json bootstrap fetch.\n *\n * 2. **Prototype-pollution rejection.** Backed by `secure-json-parse` —\n * `__proto__` and `constructor` keys raise rather than mutating the\n * prototype chain.\n *\n * Body cap is enforced by the caller (the SSRF fetch wrapper). This module\n * works against an already-bounded UTF-8 string. Errors are typed so the\n * resolver can surface `request_signature_brand_json_malformed` directly.\n */\n\nimport sjp from 'secure-json-parse';\n\nexport type StrictJsonErrorCode = 'invalid_json' | 'duplicate_key' | 'forbidden_prototype_property';\n\nexport class StrictJsonError extends Error {\n readonly code: StrictJsonErrorCode;\n readonly detail: { offset?: number; key?: string };\n constructor(code: StrictJsonErrorCode, message: string, detail: { offset?: number; key?: string } = {}) {\n super(message);\n this.name = 'StrictJsonError';\n this.code = code;\n this.detail = detail;\n }\n}\n\n/**\n * Parse JSON text under the strict rules above. Returns the parsed value on\n * success; throws `StrictJsonError` on any reject path.\n *\n * Implementation: first scan the source for duplicate keys at any object\n * scope, then hand the source to `secure-json-parse` for prototype-property\n * rejection and the actual structural parse. Two passes is fine for the\n * 256 KiB brand.json budget — both are linear, the constant factor is small,\n * and we never trust the tokenizer to produce the value (it's only a guard).\n */\nexport function parseStrictJson(text: string): unknown {\n rejectDuplicateKeys(text);\n try {\n return sjp.parse(text, null, { protoAction: 'error', constructorAction: 'error' });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes('forbidden prototype property')) {\n throw new StrictJsonError('forbidden_prototype_property', message);\n }\n throw new StrictJsonError('invalid_json', message);\n }\n}\n\n/**\n * Walk the source character-by-character and reject when any object scope\n * declares the same key twice. Comments are not legal JSON, so a `//` or\n * `/*` inside a string is fine and outside a string would already be a\n * parse error (caught downstream). This routine only needs to track:\n * - whether we are inside a string (with escape handling),\n * - the stack of open object scopes (each owning a `Set<string>`),\n * - whether the next quoted-string at object scope is the key half of\n * a key-value pair.\n */\nfunction rejectDuplicateKeys(text: string): void {\n type Scope = { kind: 'object'; keys: Set<string> } | { kind: 'array' };\n const stack: Scope[] = [];\n /** True when at an object scope and the next string token is a key. */\n let expectingKey = false;\n let i = 0;\n const len = text.length;\n\n while (i < len) {\n const ch = text[i]!;\n if (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') {\n i++;\n continue;\n }\n if (ch === '{') {\n stack.push({ kind: 'object', keys: new Set<string>() });\n expectingKey = true;\n i++;\n continue;\n }\n if (ch === '}') {\n stack.pop();\n expectingKey = false;\n i++;\n continue;\n }\n if (ch === '[') {\n stack.push({ kind: 'array' });\n expectingKey = false;\n i++;\n continue;\n }\n if (ch === ']') {\n stack.pop();\n expectingKey = false;\n i++;\n continue;\n }\n if (ch === ',') {\n const top = stack[stack.length - 1];\n expectingKey = top?.kind === 'object';\n i++;\n continue;\n }\n if (ch === ':') {\n expectingKey = false;\n i++;\n continue;\n }\n if (ch === '\"') {\n const { value, end } = readString(text, i);\n if (expectingKey) {\n const top = stack[stack.length - 1];\n if (top?.kind === 'object') {\n if (top.keys.has(value)) {\n throw new StrictJsonError('duplicate_key', `Duplicate key \"${value}\" at offset ${i}`, {\n offset: i,\n key: value,\n });\n }\n top.keys.add(value);\n }\n }\n i = end;\n continue;\n }\n // Any other character is part of a primitive (number, true/false/null);\n // skip until we hit whitespace, structural punctuation, or end of input.\n while (i < len) {\n const c = text[i]!;\n if (c === ',' || c === '}' || c === ']' || c === ' ' || c === '\\t' || c === '\\n' || c === '\\r') break;\n i++;\n }\n }\n}\n\nfunction readString(text: string, start: number): { value: string; end: number } {\n // text[start] is the opening quote.\n let i = start + 1;\n let out = '';\n const len = text.length;\n while (i < len) {\n const ch = text[i]!;\n if (ch === '\"') {\n return { value: out, end: i + 1 };\n }\n // RFC 8259 §7: unescaped control characters (U+0000–U+001F) are forbidden\n // inside JSON strings. The platform `JSON.parse` would reject them; the\n // tokenizer must reject them too so its accepted dialect tracks JSON's\n // (otherwise an attacker who can inject a literal control char into a\n // brand.json key smuggles a key that the tokenizer dedupes but the\n // parser rejects, decoupling our two passes).\n if (ch.charCodeAt(0) < 0x20) {\n throw new StrictJsonError('invalid_json', `Unescaped control character at offset ${i} (RFC 8259 §7)`, {\n offset: i,\n });\n }\n if (ch === '\\\\') {\n const next = text[i + 1];\n if (next === undefined) {\n throw new StrictJsonError('invalid_json', `Unterminated escape at offset ${i}`, { offset: i });\n }\n if (next === '\"' || next === '\\\\' || next === '/') {\n out += next;\n i += 2;\n continue;\n }\n if (next === 'b') {\n out += '\\b';\n i += 2;\n continue;\n }\n if (next === 'f') {\n out += '\\f';\n i += 2;\n continue;\n }\n if (next === 'n') {\n out += '\\n';\n i += 2;\n continue;\n }\n if (next === 'r') {\n out += '\\r';\n i += 2;\n continue;\n }\n if (next === 't') {\n out += '\\t';\n i += 2;\n continue;\n }\n if (next === 'u') {\n const hex = text.slice(i + 2, i + 6);\n if (hex.length !== 4 || /[^0-9a-fA-F]/.test(hex)) {\n throw new StrictJsonError('invalid_json', `Invalid unicode escape at offset ${i}`, { offset: i });\n }\n out += String.fromCharCode(parseInt(hex, 16));\n i += 6;\n continue;\n }\n throw new StrictJsonError('invalid_json', `Invalid escape \\\\${next} at offset ${i}`, { offset: i });\n }\n out += ch;\n i++;\n }\n throw new StrictJsonError('invalid_json', `Unterminated string starting at offset ${start}`, { offset: start });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { ssrfSafeFetch } from "../net/index.mjs";
|
|
8
2
|
import { HttpsJwksResolver } from "./jwks-https.mjs";
|
|
9
3
|
class BrandJsonResolverError extends Error {
|