@adcp/sdk 12.0.0 → 12.0.1
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.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.mjs +0 -6
- 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.mjs +0 -6
- package/dist/lib/server/index.mjs.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/verifier.ts"],"sourcesContent":["import { parseDictionary } from 'structured-headers';\nimport {\n buildSignatureBase,\n canonicalTargetUri,\n getHeaderValue,\n rejectNonAsciiHost,\n type RequestLike,\n} from './canonicalize';\nimport { contentDigestMatches } from './content-digest';\nimport { RequestSignatureError } from './errors';\nimport { parseSignature, parseSignatureInput, type ParsedSignatureInput } from './parser';\nimport { jwkToPublicKey, verifySignature } from './crypto';\nimport type { JwksResolver } from './jwks';\nimport type { ReplayStore } from './replay';\nimport type { RevocationStore } from './revocation';\nimport { containsWebhookAuthentication, WEBHOOK_AUTH_TRAVERSAL_DEPTH } from './webhook-auth-detection';\nimport {\n ALLOWED_ALGS,\n CLOCK_SKEW_TOLERANCE_SECONDS,\n MANDATORY_COMPONENTS,\n MAX_SIGNATURE_WINDOW_SECONDS,\n REQUEST_SIGNING_TAG,\n type VerifierCapability,\n type VerifyResult,\n} from './types';\n\nexport interface VerifyRequestOptions {\n capability: VerifierCapability;\n jwks: JwksResolver;\n replayStore: ReplayStore;\n revocationStore: RevocationStore;\n now?: () => number;\n /**\n * The AdCP operation being requested, used to consult\n * `capability.required_for` when an unsigned request arrives. When omitted,\n * the verifier treats the operation as \"not in any required_for list\" and\n * returns an unsigned result rather than rejecting — callers in\n * always-verify mode (where every request is signed) can leave this blank.\n */\n operation?: string;\n agentUrlForKeyid?: (keyid: string) => string | undefined;\n}\n\nexport async function verifyRequestSignature(\n request: RequestLike,\n options: VerifyRequestOptions\n): Promise<VerifyResult> {\n const now = options.now ? options.now() : Math.floor(Date.now() / 1000);\n const sigInputHeader = getHeaderValue(request.headers, 'Signature-Input');\n const sigHeader = getHeaderValue(request.headers, 'Signature');\n\n // Pre-check: both headers present or both absent.\n if (!sigInputHeader && !sigHeader) {\n const operation = options.operation;\n // Precedence is intentionally fail-specific: AdCP tool required_for,\n // raw JSON-RPC protocol methods, then payload-driven webhook-auth\n // elevation. The specific signed-only contract should win before the\n // generic body scan runs.\n if (operation && options.capability.required_for.includes(operation)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Operation \"${operation}\" requires a signed request`\n );\n }\n const protocolMethodsRequiredFor = options.capability.protocol_methods_required_for ?? [];\n if (protocolMethodsRequiredFor.length > 0 && exceedsUnsignedBodyInspectionCap(request.body)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Unsigned request body exceeds the protocol method inspection cap while protocol methods require signing'\n );\n }\n const protocolMethods = jsonRpcProtocolMethods(request.body);\n const requiredProtocolMethod = protocolMethods.find(method => protocolMethodsRequiredFor.includes(method));\n if (requiredProtocolMethod) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Protocol method \"${requiredProtocolMethod}\" requires a signed request`\n );\n }\n // Payload-driven elevation: any request carrying webhook receiver\n // credentials (task, reporting, artifact, or revocation callbacks) MUST\n // be RFC 9421 signed, regardless of whether the operation appears in\n // `required_for`\n // (#webhook-security downgrade-resistance). A bearer-only channel\n // would otherwise let an attacker who captured a token register or\n // update webhook credentials and redirect callbacks to a hostile URL.\n if (carriesWebhookAuthentication(request)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Requests carrying webhook authentication must be RFC 9421 signed'\n );\n }\n return { status: 'unsigned', verified_at: now };\n }\n if (!sigInputHeader || !sigHeader) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Signature and Signature-Input headers must be present as a pair'\n );\n }\n\n // Step 1: parse.\n const parsedInput = parseSignatureInput(sigInputHeader);\n const parsedSig = parseSignature(sigHeader, parsedInput.label);\n rejectNonAsciiHost(request.url);\n validateSingleValuedCoveredHeaders(parsedInput.components, request);\n\n // Step 2: required params present.\n requireParams(parsedInput);\n\n // Step 3: tag match.\n if (parsedInput.params.tag !== REQUEST_SIGNING_TAG) {\n throw new RequestSignatureError(\n 'request_signature_tag_invalid',\n 3,\n `Signature tag must be \"${REQUEST_SIGNING_TAG}\"; got \"${parsedInput.params.tag}\"`\n );\n }\n\n // Step 4: alg allowlist.\n if (!ALLOWED_ALGS.has(parsedInput.params.alg)) {\n throw new RequestSignatureError(\n 'request_signature_alg_not_allowed',\n 4,\n `Signature alg \"${parsedInput.params.alg}\" is not in the AdCP allowlist`\n );\n }\n\n // Step 5: window valid.\n validateWindow(parsedInput.params.created, parsedInput.params.expires, now);\n\n // Step 6: covered components.\n validateCoveredComponents(parsedInput.components, options.capability, request);\n\n // Step 7: resolve keyid.\n const jwk = await options.jwks.resolve(parsedInput.params.keyid);\n if (!jwk) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `No JWK found for keyid \"${parsedInput.params.keyid}\"`\n );\n }\n if (jwk.kid !== parsedInput.params.keyid) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `JWKS resolver returned a JWK whose kid \"${jwk.kid}\" does not match requested keyid \"${parsedInput.params.keyid}\"`\n );\n }\n\n // Step 8: key purpose.\n if (jwk.adcp_use !== 'request-signing' || !jwk.key_ops?.includes('verify')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" is not scoped for request-signing verification`\n );\n }\n validateJwkParameterConsistency(jwk, parsedInput.params.alg);\n\n // Step 9: revocation (runs BEFORE crypto to prevent amplification attacks).\n if (await options.revocationStore.isRevoked(jwk.kid)) {\n throw new RequestSignatureError('request_signature_key_revoked', 9, `JWK \"${jwk.kid}\" is revoked`);\n }\n\n // Replay cache is scoped by `(keyid, @target-uri)` per adcp#2460 — a\n // signature captured on one endpoint (e.g. /create_media_buy) MUST NOT\n // count against the replay budget for a different endpoint under the\n // same keyid. Canonicalize once and reuse for both pre-check and commit.\n const replayScope = canonicalTargetUri(request.url);\n\n // Step 12 pre-checks (rate-abuse cap + replay hit) run before crypto so a\n // compromised-key cache cap or a replayed nonce short-circuits an expensive\n // Ed25519/ECDSA verify. The committing insert happens after step 11.\n if (await options.replayStore.isCapHit(jwk.kid, replayScope, now)) {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n if (await options.replayStore.has(jwk.kid, replayScope, parsedInput.params.nonce, now)) {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n\n // Step 10: cryptographic verify. Use the received Signature-Input value\n // verbatim as the @signature-params line so a peer emitting params in a\n // different legal order still produces a byte-identical base.\n const base = buildSignatureBase(\n parsedInput.components,\n request,\n parsedInput.params,\n parsedInput.signatureParamsValue\n );\n const publicKey = jwkToPublicKey(jwk);\n const valid = verifySignature(parsedInput.params.alg, publicKey, Buffer.from(base, 'utf8'), parsedSig.bytes);\n if (!valid) {\n throw new RequestSignatureError(\n 'request_signature_invalid',\n 10,\n 'Cryptographic verification of signature base failed'\n );\n }\n\n // Step 11: content-digest match (only if covered).\n if (parsedInput.components.includes('content-digest')) {\n const digestHeader = getHeaderValue(request.headers, 'Content-Digest');\n if (!digestHeader || !contentDigestMatches(digestHeader, request.body ?? '')) {\n throw new RequestSignatureError(\n 'request_signature_digest_mismatch',\n 11,\n 'Content-Digest header does not match recomputed body hash'\n );\n }\n }\n\n // Step 12: commit the (keyid, nonce) into the replay cache now that all\n // prior checks have passed. Floor the TTL at one max-window + skew so a\n // signer minting tiny validity windows can't replay outside the configured\n // replay horizon, and so cross-replica eventual consistency has headroom.\n const remaining = parsedInput.params.expires - now + CLOCK_SKEW_TOLERANCE_SECONDS;\n const ttl = Math.max(remaining, MAX_SIGNATURE_WINDOW_SECONDS + CLOCK_SKEW_TOLERANCE_SECONDS);\n const insertResult = await options.replayStore.insert(jwk.kid, replayScope, parsedInput.params.nonce, ttl, now);\n if (insertResult === 'replayed') {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n if (insertResult === 'rate_abuse') {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n\n const agent_url = options.agentUrlForKeyid?.(jwk.kid);\n return { status: 'verified', keyid: jwk.kid, agent_url, verified_at: now };\n}\n\nfunction jsonRpcProtocolMethods(body: string | undefined): string[] {\n if (!body) return [];\n if (exceedsUnsignedBodyInspectionCap(body)) return [];\n try {\n const parsed = JSON.parse(body) as unknown;\n const messages = Array.isArray(parsed) ? parsed : [parsed];\n return messages.flatMap(message =>\n message !== null && typeof message === 'object' && typeof (message as { method?: unknown }).method === 'string'\n ? [(message as { method: string }).method]\n : []\n );\n } catch {\n return [];\n }\n}\n\nfunction requireParams(parsed: ParsedSignatureInput): void {\n const required: Array<keyof ParsedSignatureInput['params']> = ['created', 'expires', 'nonce', 'keyid', 'alg', 'tag'];\n const missing = required.filter(k => parsed.params[k] === undefined);\n if (missing.length) {\n throw new RequestSignatureError(\n 'request_signature_params_incomplete',\n 2,\n `Signature-Input missing required parameter(s): ${missing.join(', ')}`\n );\n }\n}\n\nfunction validateWindow(created: number, expires: number, now: number): void {\n if (expires <= created) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature expires must be strictly greater than created'\n );\n }\n if (expires - created > MAX_SIGNATURE_WINDOW_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n `Signature window exceeds ${MAX_SIGNATURE_WINDOW_SECONDS}s maximum`\n );\n }\n if (now > expires + CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError('request_signature_window_invalid', 5, 'Signature is expired');\n }\n if (now < created - CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature created is in the future beyond skew tolerance'\n );\n }\n}\n\n/**\n * Reject JWKs whose alg/kty/crv are mutually inconsistent OR don't match\n * the sig-params alg. Three checks in one place:\n *\n * 1. JWK MUST declare `alg`. A missing alg lets a signer substitute one\n * algorithm family for another (e.g. EdDSA sig-params against a P-256\n * JWK) and defer failure to step 10's crypto verify — the wrong step\n * and the wrong error code.\n * 2. JWK `alg` MUST match the sig-params `alg` after AdCP→JOSE mapping\n * (`ed25519`↔`EdDSA`, `ecdsa-p256-sha256`↔`ES256`). Prevents sig-params\n * alg-downgrade against a JWK that was published for a stronger alg.\n * 3. JWK kty/crv MUST be consistent with the declared alg per RFC 8037\n * (EdDSA→OKP/Ed25519|Ed448) and RFC 7518 (ES256→EC/P-256).\n */\nfunction validateJwkParameterConsistency(\n jwk: { kid: string; kty?: string; crv?: string; alg?: string },\n sigParamsAlg: string\n): void {\n if (!jwk.alg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" does not declare an alg; cannot bind to sig-params alg=\"${sigParamsAlg}\"`\n );\n }\n const expectedJoseAlg = SIG_PARAMS_ALG_TO_JOSE[sigParamsAlg];\n if (expectedJoseAlg && jwk.alg !== expectedJoseAlg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=${jwk.alg} but the request is signed with alg=\"${sigParamsAlg}\" (JOSE ${expectedJoseAlg})`\n );\n }\n if (jwk.alg === 'EdDSA') {\n if (jwk.kty !== 'OKP' || (jwk.crv !== 'Ed25519' && jwk.crv !== 'Ed448')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=EdDSA but kty/crv (${jwk.kty}/${jwk.crv}) is not a valid Edwards-curve pair`\n );\n }\n return;\n }\n if (jwk.alg === 'ES256') {\n if (jwk.kty !== 'EC' || jwk.crv !== 'P-256') {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=ES256 but kty/crv (${jwk.kty}/${jwk.crv}) is not EC/P-256`\n );\n }\n }\n}\n\nconst SIG_PARAMS_ALG_TO_JOSE: Record<string, string> = {\n ed25519: 'EdDSA',\n 'ecdsa-p256-sha256': 'ES256',\n};\n\n/**\n * Covered components referencing single-valued HTTP header fields\n * (Content-Type per RFC 9110, Content-Digest per RFC 9530) MUST NOT arrive\n * as a comma-joined multi-value. A proxy or buggy client emitting the field\n * twice produces a concatenated value whose meaning relative to the\n * signature base is undefined — reject at parse rather than pick one.\n */\nfunction validateSingleValuedCoveredHeaders(components: string[], request: RequestLike): void {\n if (components.includes('content-type')) {\n const value = getHeaderValue(request.headers, 'Content-Type');\n if (value !== undefined && containsTopLevelComma(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Type header covered by signature must be single-valued; multiple field values are ambiguous'\n );\n }\n }\n if (components.includes('content-digest')) {\n const value = getHeaderValue(request.headers, 'Content-Digest');\n if (value !== undefined) {\n try {\n const dict = parseDictionary(value);\n const seen = new Map<string, number>();\n for (const key of dict.keys()) {\n seen.set(key, (seen.get(key) ?? 0) + 1);\n }\n for (const [key, count] of seen) {\n if (count > 1) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n `Content-Digest declares the \"${key}\" algorithm more than once; duplicate members are ambiguous`\n );\n }\n }\n } catch (e) {\n if (e instanceof RequestSignatureError) throw e;\n }\n // The structured-headers library deduplicates dictionary keys silently.\n // Detect the multi-value case at the raw-string level too so the\n // specific \"two sha-256 members\" shape is caught.\n if (containsDuplicateDictKey(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Digest declares the same algorithm more than once; duplicate members are ambiguous'\n );\n }\n }\n }\n}\n\nfunction containsTopLevelComma(value: string): boolean {\n let inQuoted = false;\n for (let i = 0; i < value.length; i++) {\n const ch = value[i]!;\n if (ch === '\\\\' && i + 1 < value.length && inQuoted) {\n i++;\n continue;\n }\n if (ch === '\"') {\n inQuoted = !inQuoted;\n continue;\n }\n if (ch === ',' && !inQuoted) return true;\n }\n return false;\n}\n\nfunction containsDuplicateDictKey(value: string): boolean {\n const keys: string[] = [];\n let i = 0;\n const len = value.length;\n let atEntryStart = true;\n while (i < len) {\n if (atEntryStart) {\n while (i < len && (value[i] === ' ' || value[i] === '\\t')) i++;\n const start = i;\n while (i < len && /[A-Za-z0-9_*-]/.test(value[i]!)) i++;\n if (i > start) keys.push(value.slice(start, i).toLowerCase());\n atEntryStart = false;\n continue;\n }\n const ch = value[i]!;\n if (ch === '\"') {\n i++;\n while (i < len) {\n if (value[i] === '\\\\' && i + 1 < len) {\n i += 2;\n continue;\n }\n if (value[i] === '\"') {\n i++;\n break;\n }\n i++;\n }\n continue;\n }\n if (ch === ':') {\n i++;\n while (i < len && value[i] !== ':') i++;\n if (i < len) i++;\n continue;\n }\n if (ch === ',') {\n atEntryStart = true;\n i++;\n continue;\n }\n i++;\n }\n const seen = new Set<string>();\n for (const key of keys) {\n if (seen.has(key)) return true;\n seen.add(key);\n }\n return false;\n}\n\n/**\n * Upper bound on request body size we will JSON.parse and walk on the\n * unsigned branch. Real AdCP `update_media_buy` / `create_media_buy` payloads\n * are O(kilobytes); 1 MB is ~1000x that. Over this, require signing rather\n * than spend unbounded pre-crypto CPU on attacker-shaped input — a 1 MB\n * nested-array body would otherwise let an unauthenticated caller force\n * deep recursion on every request.\n */\nconst MAX_UNSIGNED_BODY_INSPECTION_BYTES = 1_048_576;\n/**\n * Upper bound on recursion depth in `containsWebhookAuthentication`. Real\n * AdCP payloads reach depth ~5-8; 64 is generous without leaving room for\n * a pathological `{\"a\":{\"a\":{...}}}` to burn the stack.\n */\nfunction exceedsUnsignedBodyInspectionCap(body: string | undefined): boolean {\n return typeof body === 'string' && body.length > MAX_UNSIGNED_BODY_INSPECTION_BYTES;\n}\n\n/**\n * Scan a JSON request body for a non-empty\n * webhook authentication object — anywhere in the tree, including inside\n * arrays of pending config updates. MCP uses `push_notification_config`;\n * A2A uses `pushNotificationConfig`. Runs only on the\n * unsigned branch; returning `true` triggers the webhook-security downgrade\n * rejection in {@link verifyRequestSignature}.\n *\n * The check is Content-Type independent: an attacker can't evade by\n * labeling a JSON body `text/plain`. A body that fails to parse as JSON\n * returns false so legitimate non-JSON transports (SSE posts, form uploads)\n * aren't blocked; signed-request enforcement for those remains\n * `required_for`-driven. A body over {@link MAX_UNSIGNED_BODY_INSPECTION_BYTES}\n * returns `true` — we can't prove absence of webhook auth within our DoS\n * budget, and oversized unsigned bodies are outside normal AdCP operation.\n */\nfunction carriesWebhookAuthentication(request: RequestLike): boolean {\n const body = request.body;\n if (!body) return false;\n if (exceedsUnsignedBodyInspectionCap(body)) return true;\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return false;\n }\n return containsWebhookAuthentication(parsed, WEBHOOK_AUTH_TRAVERSAL_DEPTH);\n}\n\nfunction validateCoveredComponents(components: string[], capability: VerifierCapability, request: RequestLike): void {\n for (const mandatory of MANDATORY_COMPONENTS) {\n if (!components.includes(mandatory)) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n `Covered components must include \"${mandatory}\"`\n );\n }\n }\n const hasBody = (request.body ?? '').length > 0;\n if (hasBody && !components.includes('content-type')) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Covered components must include \"content-type\" when a request body is present'\n );\n }\n const includesDigest = components.includes('content-digest');\n if (capability.covers_content_digest === 'required' && !includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Verifier requires content-digest coverage'\n );\n }\n if (capability.covers_content_digest === 'forbidden' && includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_unexpected',\n 6,\n 'Verifier forbids content-digest coverage'\n );\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/signing/verifier.ts"],"sourcesContent":["import { parseDictionary } from 'structured-headers';\nimport {\n buildSignatureBase,\n canonicalTargetUri,\n getHeaderValue,\n rejectNonAsciiHost,\n type RequestLike,\n} from './canonicalize';\nimport { contentDigestMatches } from './content-digest';\nimport { RequestSignatureError } from './errors';\nimport { parseSignature, parseSignatureInput, type ParsedSignatureInput } from './parser';\nimport { jwkToPublicKey, verifySignature } from './crypto';\nimport type { JwksResolver } from './jwks';\nimport type { ReplayStore } from './replay';\nimport type { RevocationStore } from './revocation';\nimport { containsWebhookAuthentication, WEBHOOK_AUTH_TRAVERSAL_DEPTH } from './webhook-auth-detection';\nimport {\n ALLOWED_ALGS,\n CLOCK_SKEW_TOLERANCE_SECONDS,\n MANDATORY_COMPONENTS,\n MAX_SIGNATURE_WINDOW_SECONDS,\n REQUEST_SIGNING_TAG,\n type VerifierCapability,\n type VerifyResult,\n} from './types';\n\nexport interface VerifyRequestOptions {\n capability: VerifierCapability;\n jwks: JwksResolver;\n replayStore: ReplayStore;\n revocationStore: RevocationStore;\n now?: () => number;\n /**\n * The AdCP operation being requested, used to consult\n * `capability.required_for` when an unsigned request arrives. When omitted,\n * the verifier treats the operation as \"not in any required_for list\" and\n * returns an unsigned result rather than rejecting — callers in\n * always-verify mode (where every request is signed) can leave this blank.\n */\n operation?: string;\n agentUrlForKeyid?: (keyid: string) => string | undefined;\n}\n\nexport async function verifyRequestSignature(\n request: RequestLike,\n options: VerifyRequestOptions\n): Promise<VerifyResult> {\n const now = options.now ? options.now() : Math.floor(Date.now() / 1000);\n const sigInputHeader = getHeaderValue(request.headers, 'Signature-Input');\n const sigHeader = getHeaderValue(request.headers, 'Signature');\n\n // Pre-check: both headers present or both absent.\n if (!sigInputHeader && !sigHeader) {\n const operation = options.operation;\n // Precedence is intentionally fail-specific: AdCP tool required_for,\n // raw JSON-RPC protocol methods, then payload-driven webhook-auth\n // elevation. The specific signed-only contract should win before the\n // generic body scan runs.\n if (operation && options.capability.required_for.includes(operation)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Operation \"${operation}\" requires a signed request`\n );\n }\n const protocolMethodsRequiredFor = options.capability.protocol_methods_required_for ?? [];\n if (protocolMethodsRequiredFor.length > 0 && exceedsUnsignedBodyInspectionCap(request.body)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Unsigned request body exceeds the protocol method inspection cap while protocol methods require signing'\n );\n }\n const protocolMethods = jsonRpcProtocolMethods(request.body);\n const requiredProtocolMethod = protocolMethods.find(method => protocolMethodsRequiredFor.includes(method));\n if (requiredProtocolMethod) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Protocol method \"${requiredProtocolMethod}\" requires a signed request`\n );\n }\n // Payload-driven elevation: any request carrying webhook receiver\n // credentials (task, reporting, artifact, or revocation callbacks) MUST\n // be RFC 9421 signed, regardless of whether the operation appears in\n // `required_for`\n // (#webhook-security downgrade-resistance). A bearer-only channel\n // would otherwise let an attacker who captured a token register or\n // update webhook credentials and redirect callbacks to a hostile URL.\n if (carriesWebhookAuthentication(request)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Requests carrying webhook authentication must be RFC 9421 signed'\n );\n }\n return { status: 'unsigned', verified_at: now };\n }\n if (!sigInputHeader || !sigHeader) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Signature and Signature-Input headers must be present as a pair'\n );\n }\n\n // Step 1: parse.\n const parsedInput = parseSignatureInput(sigInputHeader);\n const parsedSig = parseSignature(sigHeader, parsedInput.label);\n rejectNonAsciiHost(request.url);\n validateSingleValuedCoveredHeaders(parsedInput.components, request);\n\n // Step 2: required params present.\n requireParams(parsedInput);\n\n // Step 3: tag match.\n if (parsedInput.params.tag !== REQUEST_SIGNING_TAG) {\n throw new RequestSignatureError(\n 'request_signature_tag_invalid',\n 3,\n `Signature tag must be \"${REQUEST_SIGNING_TAG}\"; got \"${parsedInput.params.tag}\"`\n );\n }\n\n // Step 4: alg allowlist.\n if (!ALLOWED_ALGS.has(parsedInput.params.alg)) {\n throw new RequestSignatureError(\n 'request_signature_alg_not_allowed',\n 4,\n `Signature alg \"${parsedInput.params.alg}\" is not in the AdCP allowlist`\n );\n }\n\n // Step 5: window valid.\n validateWindow(parsedInput.params.created, parsedInput.params.expires, now);\n\n // Step 6: covered components.\n validateCoveredComponents(parsedInput.components, options.capability, request);\n\n // Step 7: resolve keyid.\n const jwk = await options.jwks.resolve(parsedInput.params.keyid);\n if (!jwk) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `No JWK found for keyid \"${parsedInput.params.keyid}\"`\n );\n }\n if (jwk.kid !== parsedInput.params.keyid) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `JWKS resolver returned a JWK whose kid \"${jwk.kid}\" does not match requested keyid \"${parsedInput.params.keyid}\"`\n );\n }\n\n // Step 8: key purpose.\n if (jwk.adcp_use !== 'request-signing' || !jwk.key_ops?.includes('verify')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" is not scoped for request-signing verification`\n );\n }\n validateJwkParameterConsistency(jwk, parsedInput.params.alg);\n\n // Step 9: revocation (runs BEFORE crypto to prevent amplification attacks).\n if (await options.revocationStore.isRevoked(jwk.kid)) {\n throw new RequestSignatureError('request_signature_key_revoked', 9, `JWK \"${jwk.kid}\" is revoked`);\n }\n\n // Replay cache is scoped by `(keyid, @target-uri)` per adcp#2460 — a\n // signature captured on one endpoint (e.g. /create_media_buy) MUST NOT\n // count against the replay budget for a different endpoint under the\n // same keyid. Canonicalize once and reuse for both pre-check and commit.\n const replayScope = canonicalTargetUri(request.url);\n\n // Step 12 pre-checks (rate-abuse cap + replay hit) run before crypto so a\n // compromised-key cache cap or a replayed nonce short-circuits an expensive\n // Ed25519/ECDSA verify. The committing insert happens after step 11.\n if (await options.replayStore.isCapHit(jwk.kid, replayScope, now)) {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n if (await options.replayStore.has(jwk.kid, replayScope, parsedInput.params.nonce, now)) {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n\n // Step 10: cryptographic verify. Use the received Signature-Input value\n // verbatim as the @signature-params line so a peer emitting params in a\n // different legal order still produces a byte-identical base.\n const base = buildSignatureBase(\n parsedInput.components,\n request,\n parsedInput.params,\n parsedInput.signatureParamsValue\n );\n const publicKey = jwkToPublicKey(jwk);\n const valid = verifySignature(parsedInput.params.alg, publicKey, Buffer.from(base, 'utf8'), parsedSig.bytes);\n if (!valid) {\n throw new RequestSignatureError(\n 'request_signature_invalid',\n 10,\n 'Cryptographic verification of signature base failed'\n );\n }\n\n // Step 11: content-digest match (only if covered).\n if (parsedInput.components.includes('content-digest')) {\n const digestHeader = getHeaderValue(request.headers, 'Content-Digest');\n if (!digestHeader || !contentDigestMatches(digestHeader, request.body ?? '')) {\n throw new RequestSignatureError(\n 'request_signature_digest_mismatch',\n 11,\n 'Content-Digest header does not match recomputed body hash'\n );\n }\n }\n\n // Step 12: commit the (keyid, nonce) into the replay cache now that all\n // prior checks have passed. Floor the TTL at one max-window + skew so a\n // signer minting tiny validity windows can't replay outside the configured\n // replay horizon, and so cross-replica eventual consistency has headroom.\n const remaining = parsedInput.params.expires - now + CLOCK_SKEW_TOLERANCE_SECONDS;\n const ttl = Math.max(remaining, MAX_SIGNATURE_WINDOW_SECONDS + CLOCK_SKEW_TOLERANCE_SECONDS);\n const insertResult = await options.replayStore.insert(jwk.kid, replayScope, parsedInput.params.nonce, ttl, now);\n if (insertResult === 'replayed') {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n if (insertResult === 'rate_abuse') {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n\n const agent_url = options.agentUrlForKeyid?.(jwk.kid);\n return { status: 'verified', keyid: jwk.kid, agent_url, verified_at: now };\n}\n\nfunction jsonRpcProtocolMethods(body: string | undefined): string[] {\n if (!body) return [];\n if (exceedsUnsignedBodyInspectionCap(body)) return [];\n try {\n const parsed = JSON.parse(body) as unknown;\n const messages = Array.isArray(parsed) ? parsed : [parsed];\n return messages.flatMap(message =>\n message !== null && typeof message === 'object' && typeof (message as { method?: unknown }).method === 'string'\n ? [(message as { method: string }).method]\n : []\n );\n } catch {\n return [];\n }\n}\n\nfunction requireParams(parsed: ParsedSignatureInput): void {\n const required: Array<keyof ParsedSignatureInput['params']> = ['created', 'expires', 'nonce', 'keyid', 'alg', 'tag'];\n const missing = required.filter(k => parsed.params[k] === undefined);\n if (missing.length) {\n throw new RequestSignatureError(\n 'request_signature_params_incomplete',\n 2,\n `Signature-Input missing required parameter(s): ${missing.join(', ')}`\n );\n }\n}\n\nfunction validateWindow(created: number, expires: number, now: number): void {\n if (expires <= created) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature expires must be strictly greater than created'\n );\n }\n if (expires - created > MAX_SIGNATURE_WINDOW_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n `Signature window exceeds ${MAX_SIGNATURE_WINDOW_SECONDS}s maximum`\n );\n }\n if (now > expires + CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError('request_signature_window_invalid', 5, 'Signature is expired');\n }\n if (now < created - CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature created is in the future beyond skew tolerance'\n );\n }\n}\n\n/**\n * Reject JWKs whose alg/kty/crv are mutually inconsistent OR don't match\n * the sig-params alg. Three checks in one place:\n *\n * 1. JWK MUST declare `alg`. A missing alg lets a signer substitute one\n * algorithm family for another (e.g. EdDSA sig-params against a P-256\n * JWK) and defer failure to step 10's crypto verify — the wrong step\n * and the wrong error code.\n * 2. JWK `alg` MUST match the sig-params `alg` after AdCP→JOSE mapping\n * (`ed25519`↔`EdDSA`, `ecdsa-p256-sha256`↔`ES256`). Prevents sig-params\n * alg-downgrade against a JWK that was published for a stronger alg.\n * 3. JWK kty/crv MUST be consistent with the declared alg per RFC 8037\n * (EdDSA→OKP/Ed25519|Ed448) and RFC 7518 (ES256→EC/P-256).\n */\nfunction validateJwkParameterConsistency(\n jwk: { kid: string; kty?: string; crv?: string; alg?: string },\n sigParamsAlg: string\n): void {\n if (!jwk.alg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" does not declare an alg; cannot bind to sig-params alg=\"${sigParamsAlg}\"`\n );\n }\n const expectedJoseAlg = SIG_PARAMS_ALG_TO_JOSE[sigParamsAlg];\n if (expectedJoseAlg && jwk.alg !== expectedJoseAlg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=${jwk.alg} but the request is signed with alg=\"${sigParamsAlg}\" (JOSE ${expectedJoseAlg})`\n );\n }\n if (jwk.alg === 'EdDSA') {\n if (jwk.kty !== 'OKP' || (jwk.crv !== 'Ed25519' && jwk.crv !== 'Ed448')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=EdDSA but kty/crv (${jwk.kty}/${jwk.crv}) is not a valid Edwards-curve pair`\n );\n }\n return;\n }\n if (jwk.alg === 'ES256') {\n if (jwk.kty !== 'EC' || jwk.crv !== 'P-256') {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=ES256 but kty/crv (${jwk.kty}/${jwk.crv}) is not EC/P-256`\n );\n }\n }\n}\n\nconst SIG_PARAMS_ALG_TO_JOSE: Record<string, string> = {\n ed25519: 'EdDSA',\n 'ecdsa-p256-sha256': 'ES256',\n};\n\n/**\n * Covered components referencing single-valued HTTP header fields\n * (Content-Type per RFC 9110, Content-Digest per RFC 9530) MUST NOT arrive\n * as a comma-joined multi-value. A proxy or buggy client emitting the field\n * twice produces a concatenated value whose meaning relative to the\n * signature base is undefined — reject at parse rather than pick one.\n */\nfunction validateSingleValuedCoveredHeaders(components: string[], request: RequestLike): void {\n if (components.includes('content-type')) {\n const value = getHeaderValue(request.headers, 'Content-Type');\n if (value !== undefined && containsTopLevelComma(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Type header covered by signature must be single-valued; multiple field values are ambiguous'\n );\n }\n }\n if (components.includes('content-digest')) {\n const value = getHeaderValue(request.headers, 'Content-Digest');\n if (value !== undefined) {\n try {\n const dict = parseDictionary(value);\n const seen = new Map<string, number>();\n for (const key of dict.keys()) {\n seen.set(key, (seen.get(key) ?? 0) + 1);\n }\n for (const [key, count] of seen) {\n if (count > 1) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n `Content-Digest declares the \"${key}\" algorithm more than once; duplicate members are ambiguous`\n );\n }\n }\n } catch (e) {\n if (e instanceof RequestSignatureError) throw e;\n }\n // The structured-headers library deduplicates dictionary keys silently.\n // Detect the multi-value case at the raw-string level too so the\n // specific \"two sha-256 members\" shape is caught.\n if (containsDuplicateDictKey(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Digest declares the same algorithm more than once; duplicate members are ambiguous'\n );\n }\n }\n }\n}\n\nfunction containsTopLevelComma(value: string): boolean {\n let inQuoted = false;\n for (let i = 0; i < value.length; i++) {\n const ch = value[i]!;\n if (ch === '\\\\' && i + 1 < value.length && inQuoted) {\n i++;\n continue;\n }\n if (ch === '\"') {\n inQuoted = !inQuoted;\n continue;\n }\n if (ch === ',' && !inQuoted) return true;\n }\n return false;\n}\n\nfunction containsDuplicateDictKey(value: string): boolean {\n const keys: string[] = [];\n let i = 0;\n const len = value.length;\n let atEntryStart = true;\n while (i < len) {\n if (atEntryStart) {\n while (i < len && (value[i] === ' ' || value[i] === '\\t')) i++;\n const start = i;\n while (i < len && /[A-Za-z0-9_*-]/.test(value[i]!)) i++;\n if (i > start) keys.push(value.slice(start, i).toLowerCase());\n atEntryStart = false;\n continue;\n }\n const ch = value[i]!;\n if (ch === '\"') {\n i++;\n while (i < len) {\n if (value[i] === '\\\\' && i + 1 < len) {\n i += 2;\n continue;\n }\n if (value[i] === '\"') {\n i++;\n break;\n }\n i++;\n }\n continue;\n }\n if (ch === ':') {\n i++;\n while (i < len && value[i] !== ':') i++;\n if (i < len) i++;\n continue;\n }\n if (ch === ',') {\n atEntryStart = true;\n i++;\n continue;\n }\n i++;\n }\n const seen = new Set<string>();\n for (const key of keys) {\n if (seen.has(key)) return true;\n seen.add(key);\n }\n return false;\n}\n\n/**\n * Upper bound on request body size we will JSON.parse and walk on the\n * unsigned branch. Real AdCP `update_media_buy` / `create_media_buy` payloads\n * are O(kilobytes); 1 MB is ~1000x that. Over this, require signing rather\n * than spend unbounded pre-crypto CPU on attacker-shaped input — a 1 MB\n * nested-array body would otherwise let an unauthenticated caller force\n * deep recursion on every request.\n */\nconst MAX_UNSIGNED_BODY_INSPECTION_BYTES = 1_048_576;\n/**\n * Upper bound on recursion depth in `containsWebhookAuthentication`. Real\n * AdCP payloads reach depth ~5-8; 64 is generous without leaving room for\n * a pathological `{\"a\":{\"a\":{...}}}` to burn the stack.\n */\nfunction exceedsUnsignedBodyInspectionCap(body: string | undefined): boolean {\n return typeof body === 'string' && body.length > MAX_UNSIGNED_BODY_INSPECTION_BYTES;\n}\n\n/**\n * Scan a JSON request body for a non-empty\n * webhook authentication object — anywhere in the tree, including inside\n * arrays of pending config updates. MCP uses `push_notification_config`;\n * A2A uses `pushNotificationConfig`. Runs only on the\n * unsigned branch; returning `true` triggers the webhook-security downgrade\n * rejection in {@link verifyRequestSignature}.\n *\n * The check is Content-Type independent: an attacker can't evade by\n * labeling a JSON body `text/plain`. A body that fails to parse as JSON\n * returns false so legitimate non-JSON transports (SSE posts, form uploads)\n * aren't blocked; signed-request enforcement for those remains\n * `required_for`-driven. A body over {@link MAX_UNSIGNED_BODY_INSPECTION_BYTES}\n * returns `true` — we can't prove absence of webhook auth within our DoS\n * budget, and oversized unsigned bodies are outside normal AdCP operation.\n */\nfunction carriesWebhookAuthentication(request: RequestLike): boolean {\n const body = request.body;\n if (!body) return false;\n if (exceedsUnsignedBodyInspectionCap(body)) return true;\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return false;\n }\n return containsWebhookAuthentication(parsed, WEBHOOK_AUTH_TRAVERSAL_DEPTH);\n}\n\nfunction validateCoveredComponents(components: string[], capability: VerifierCapability, request: RequestLike): void {\n for (const mandatory of MANDATORY_COMPONENTS) {\n if (!components.includes(mandatory)) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n `Covered components must include \"${mandatory}\"`\n );\n }\n }\n const hasBody = (request.body ?? '').length > 0;\n if (hasBody && !components.includes('content-type')) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Covered components must include \"content-type\" when a request body is present'\n );\n }\n const includesDigest = components.includes('content-digest');\n if (capability.covers_content_digest === 'required' && !includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Verifier requires content-digest coverage'\n );\n }\n if (capability.covers_content_digest === 'forbidden' && includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_unexpected',\n 6,\n 'Verifier forbids content-digest coverage'\n );\n }\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
|
const WEBHOOK_AUTH_TRAVERSAL_DEPTH = 64;
|
|
8
2
|
function containsWebhookAuthentication(value, depthRemaining = WEBHOOK_AUTH_TRAVERSAL_DEPTH) {
|
|
9
3
|
if (depthRemaining <= 0) return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/signing/webhook-auth-detection.ts"],"sourcesContent":["export const WEBHOOK_AUTH_TRAVERSAL_DEPTH = 64;\n\n/**\n * Scan a parsed JSON value for a non-empty authentication object. The false\n * positive cost is over-signing; the false negative cost is an unsigned\n * webhook-credential registration.\n *\n * Inspection-budget exhaustion fails closed by returning true.\n */\nexport function containsWebhookAuthentication(value: unknown, depthRemaining = WEBHOOK_AUTH_TRAVERSAL_DEPTH): boolean {\n if (depthRemaining <= 0) return true;\n if (!value || typeof value !== 'object') return false;\n if (Array.isArray(value)) {\n return value.some(item => containsWebhookAuthentication(item, depthRemaining - 1));\n }\n\n const obj = value as Record<string, unknown>;\n if (hasNonEmptyAuthenticationObject(obj.authentication)) return true;\n\n for (const [key, nested] of Object.entries(obj)) {\n if (key === 'authentication') continue;\n if (containsWebhookAuthentication(nested, depthRemaining - 1)) return true;\n }\n return false;\n}\n\nfunction hasNonEmptyAuthenticationObject(value: unknown): boolean {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n return Object.keys(value).length > 0;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/signing/webhook-auth-detection.ts"],"sourcesContent":["export const WEBHOOK_AUTH_TRAVERSAL_DEPTH = 64;\n\n/**\n * Scan a parsed JSON value for a non-empty authentication object. The false\n * positive cost is over-signing; the false negative cost is an unsigned\n * webhook-credential registration.\n *\n * Inspection-budget exhaustion fails closed by returning true.\n */\nexport function containsWebhookAuthentication(value: unknown, depthRemaining = WEBHOOK_AUTH_TRAVERSAL_DEPTH): boolean {\n if (depthRemaining <= 0) return true;\n if (!value || typeof value !== 'object') return false;\n if (Array.isArray(value)) {\n return value.some(item => containsWebhookAuthentication(item, depthRemaining - 1));\n }\n\n const obj = value as Record<string, unknown>;\n if (hasNonEmptyAuthenticationObject(obj.authentication)) return true;\n\n for (const [key, nested] of Object.entries(obj)) {\n if (key === 'authentication') continue;\n if (containsWebhookAuthentication(nested, depthRemaining - 1)) return true;\n }\n return false;\n}\n\nfunction hasNonEmptyAuthenticationObject(value: unknown): boolean {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n return Object.keys(value).length > 0;\n}\n"],"mappings":"AAAO,MAAM,+BAA+B;AASrC,SAAS,8BAA8B,OAAgB,iBAAiB,8BAAuC;AACpH,MAAI,kBAAkB,EAAG,QAAO;AAChC,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,KAAK,UAAQ,8BAA8B,MAAM,iBAAiB,CAAC,CAAC;AAAA,EACnF;AAEA,QAAM,MAAM;AACZ,MAAI,gCAAgC,IAAI,cAAc,EAAG,QAAO;AAEhE,aAAW,CAAC,KAAK,MAAM,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC/C,QAAI,QAAQ,iBAAkB;AAC9B,QAAI,8BAA8B,QAAQ,iBAAiB,CAAC,EAAG,QAAO;AAAA,EACxE;AACA,SAAO;AACT;AAEA,SAAS,gCAAgC,OAAyB;AAChE,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,SAAO,OAAO,KAAK,KAAK,EAAE,SAAS;AACrC;","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 { buildSignatureBase, canonicalTargetUri, getHeaderValue } from "./canonicalize.mjs";
|
|
8
2
|
import { contentDigestMatches } from "./content-digest.mjs";
|
|
9
3
|
import { RequestSignatureError, WebhookSignatureError } from "./errors.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/signing/webhook-verifier.ts"],"sourcesContent":["/**\n * RFC 9421 webhook-signing verifier (AdCP 3.0 webhook profile).\n *\n * Companion to `verifier.ts`, which verifies outbound request signatures.\n * This verifier runs on the receiving side of a publisher webhook: the\n * storyboard runner's receiver captures a POST and hands the request (headers\n * + raw body) here for signature validation.\n *\n * Distinct from request-signing:\n * - Tag: `adcp/webhook-signing/v1` (vs `adcp/request-signing/v1`).\n * - Key purpose: `adcp_use: \"webhook-signing\"` OR `\"request-signing\"` — a\n * signer may reuse its request-signing key for webhooks (see step 8).\n * - Covered components MUST include `@method`, `@target-uri`, `@authority`,\n * `content-type`, and `content-digest` — `content-digest` is unconditional\n * for webhooks (vs policy-driven on requests) because every webhook\n * carries a JSON body.\n * - JWKS resolved via the publisher's `brand.json` `agents[]` `jwks_uri`,\n * not the request-path capability document.\n *\n * Checklist steps below mirror the 14-step shape in\n * `docs/building/implementation/security.mdx#verifier-checklist-for-webhooks`\n * so failures can point at a specific spec clause. Numbers are 1-based to\n * match the security doc.\n */\n\nimport { buildSignatureBase, canonicalTargetUri, getHeaderValue, type RequestLike } from './canonicalize';\nimport { contentDigestMatches } from './content-digest';\nimport { RequestSignatureError, WebhookSignatureError } from './errors';\nimport { parseSignature, parseSignatureInput, type ParsedSignatureInput } from './parser';\nimport { jwkToPublicKey, verifySignature } from './crypto';\nimport type { JwksResolver } from './jwks';\nimport { InMemoryReplayStore, type ReplayStore } from './replay';\nimport { InMemoryRevocationStore, type RevocationStore } from './revocation';\nimport { ALLOWED_ALGS, CLOCK_SKEW_TOLERANCE_SECONDS, MAX_SIGNATURE_WINDOW_SECONDS } from './types';\n\nexport const WEBHOOK_SIGNING_TAG = 'adcp/webhook-signing/v1';\n\n/**\n * Covered-component minimum for webhooks. Unlike request-signing, every\n * webhook carries a body so `content-type` and `content-digest` are\n * unconditional — a webhook without `content-digest` coverage can't be\n * verified because the body has no anchor.\n */\nexport const WEBHOOK_MANDATORY_COMPONENTS: ReadonlyArray<string> = [\n '@method',\n '@target-uri',\n '@authority',\n 'content-type',\n 'content-digest',\n];\n\nexport interface VerifyWebhookOptions {\n jwks: JwksResolver;\n replayStore: ReplayStore;\n revocationStore: RevocationStore;\n /** Now in seconds since epoch. Defaults to `Date.now() / 1000`. */\n now?: () => number;\n /**\n * Optional tag override — spec currently defines exactly\n * `adcp/webhook-signing/v1`; the override lets test vectors pin a version.\n */\n requiredTag?: string;\n /** Optional reverse-lookup (kid → publisher URL) for result attribution. */\n agentUrlForKeyid?: (keyid: string) => string | undefined;\n}\n\nexport interface VerifyWebhookResult {\n status: 'verified';\n keyid: string;\n agent_url?: string;\n verified_at: number;\n}\n\n/**\n * Verify an inbound webhook's RFC 9421 signature.\n *\n * Checklist steps 1–13 plus sub-step 9a, matching the canonical numbering\n * in `security.mdx#verifier-checklist-for-webhooks` (and the `failed_step`\n * values on the conformance vectors at\n * `test-vectors/webhook-signing/negative/`). Throws\n * `WebhookSignatureError` on the first failed step.\n *\n * Ordering invariant: cheap invariant checks (tag, alg, window, components)\n * run before JWKS resolution (step 7); revocation (9) and rate-abuse (9a)\n * run before cryptographic verify (10) so an attacker can't amplify\n * Ed25519/ECDSA work. Replay insert (13) commits only after every earlier\n * step has passed — any signature failing at step 10 never consumes a cap\n * entry, so external traffic can't grow the per-keyid cache.\n */\nexport async function verifyWebhookSignature(\n request: RequestLike,\n options: VerifyWebhookOptions\n): Promise<VerifyWebhookResult> {\n const now = options.now ? options.now() : Math.floor(Date.now() / 1000);\n const requiredTag = options.requiredTag ?? WEBHOOK_SIGNING_TAG;\n\n // Step 1: both signature headers present AND parseable. The bound-pair\n // rule (`Signature` without `Signature-Input` or vice versa) rejects here.\n const sigInputHeader = getHeaderValue(request.headers, 'Signature-Input');\n const sigHeader = getHeaderValue(request.headers, 'Signature');\n if (!sigInputHeader || !sigHeader) {\n throw new WebhookSignatureError(\n 'webhook_signature_header_malformed',\n 1,\n 'Webhook is missing Signature or Signature-Input headers.'\n );\n }\n let parsedInput: ParsedSignatureInput;\n let parsedSig: ReturnType<typeof parseSignature>;\n try {\n parsedInput = parseSignatureInput(sigInputHeader);\n parsedSig = parseSignature(sigHeader, parsedInput.label);\n } catch (err) {\n throw new WebhookSignatureError(\n 'webhook_signature_header_malformed',\n 1,\n err instanceof Error ? err.message : String(err)\n );\n }\n\n // Step 2: required params present.\n requireParams(parsedInput);\n\n // Step 3: tag match.\n if (parsedInput.params.tag !== requiredTag) {\n throw new WebhookSignatureError(\n 'webhook_signature_tag_invalid',\n 3,\n `Signature tag must be \"${requiredTag}\"; got \"${parsedInput.params.tag}\".`\n );\n }\n\n // Step 4: alg allowlist.\n if (!ALLOWED_ALGS.has(parsedInput.params.alg)) {\n throw new WebhookSignatureError(\n 'webhook_signature_alg_not_allowed',\n 4,\n `Signature alg \"${parsedInput.params.alg}\" is not in the AdCP allowlist.`\n );\n }\n\n // Step 5: window valid.\n validateWindow(parsedInput.params.created, parsedInput.params.expires, now);\n\n // Step 6: covered components.\n validateCoveredComponents(parsedInput.components);\n\n // Step 6a: `@target-uri` syntactic validation. Runs before JWKS resolution\n // and crypto verify so a malformed URI short-circuits without JWKS\n // side-effects. Distinct from `header_malformed` (the Signature /\n // Signature-Input headers themselves) — this flags the covered URI value\n // that will be fed into the signature base. Non-parseable URLs, non-https\n // schemes, embedded userinfo, and fragments are all rejected.\n validateTargetUri(request.url);\n\n // Step 7: resolve keyid.\n const jwk = await options.jwks.resolve(parsedInput.params.keyid);\n if (!jwk) {\n throw new WebhookSignatureError(\n 'webhook_signature_key_unknown',\n 7,\n `No JWK found for keyid \"${parsedInput.params.keyid}\".`\n );\n }\n if (jwk.kid !== parsedInput.params.keyid) {\n throw new WebhookSignatureError(\n 'webhook_signature_key_unknown',\n 7,\n `JWKS resolver returned a JWK whose kid \"${jwk.kid}\" does not match requested keyid \"${parsedInput.params.keyid}\".`\n );\n }\n\n // Step 8: key purpose — webhooks are signed with a `request-signing` key.\n //\n // Webhooks carry no separate key purpose: the signer uses its\n // `adcp_use: \"request-signing\"` key (the deprecated `\"webhook-signing\"`\n // value is still accepted here for backward compatibility, pending removal — adcontextprotocol/adcp#5555).\n // This is safe because cross-protocol confusion is prevented by the\n // signature `tag` (step 3, `adcp/webhook-signing/v1`, part of the signed\n // base) and mandatory `content-digest` coverage (step 6) — not by the\n // key-purpose discriminator. A captured request signature\n // (`tag=adcp/request-signing/v1`) can never be replayed against this\n // verifier because step 3 rejects it. Webhook key isolation, when wanted, is\n // a second `request-signing` key under a distinct `kid` — not a distinct\n // `adcp_use`.\n //\n // All key-purpose failures use `webhook_signature_key_purpose_invalid`:\n // absent `adcp_use`, a missing `verify` key_op, or an `adcp_use` outside the\n // accepted set (e.g. `response-signing`, `governance-signing`). We do NOT\n // reuse `webhook_mode_mismatch` here — that code is reserved for the\n // HMAC-vs-9421 auth-mode selector mismatch, and overloading it would collapse\n // two distinct failure classes onto one stable code receivers branch on.\n if (\n jwk.adcp_use === undefined ||\n !jwk.key_ops?.includes('verify') ||\n (jwk.adcp_use !== 'request-signing' && jwk.adcp_use !== 'webhook-signing')\n ) {\n throw new WebhookSignatureError(\n 'webhook_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" is not scoped for webhook delivery: adcp_use must be \"request-signing\" (or the deprecated \"webhook-signing\") with a \"verify\" key_op (got adcp_use=\"${jwk.adcp_use}\").`\n );\n }\n\n // Step 9: revocation. Shared store throws `request_signature_revocation_stale`\n // when its cached snapshot is past grace — re-map to the webhook taxonomy\n // so callers see a consistent error surface.\n try {\n if (await options.revocationStore.isRevoked(jwk.kid)) {\n throw new WebhookSignatureError('webhook_signature_key_revoked', 9, `JWK \"${jwk.kid}\" is revoked.`);\n }\n } catch (err) {\n if (err instanceof RequestSignatureError && err.code === 'request_signature_revocation_stale') {\n throw new WebhookSignatureError('webhook_signature_revocation_stale', 9, err.message);\n }\n throw err;\n }\n\n // Replay cache is scoped by `(keyid, @target-uri)` per adcp#2460 — a\n // webhook captured on one receiver path MUST NOT count against the replay\n // budget for a different path under the same keyid. Canonicalize once and\n // reuse for both pre-check and commit.\n const replayScope = canonicalTargetUri(request.url);\n\n // Step 9a: per-keyid rate abuse. Distinct code from step 12's replay —\n // cap exhaustion is a compromised-key / misconfig signal that SHOULD\n // alert operators, not \"same nonce twice.\"\n if (await options.replayStore.isCapHit(jwk.kid, replayScope, now)) {\n throw new WebhookSignatureError(\n 'webhook_signature_rate_abuse',\n 9,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}.`\n );\n }\n\n // Pre-check step 12's replay before crypto so a replayed nonce short-\n // circuits an expensive Ed25519/ECDSA verify.\n if (await options.replayStore.has(jwk.kid, replayScope, parsedInput.params.nonce, now)) {\n throw new WebhookSignatureError(\n 'webhook_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window.`\n );\n }\n\n // Step 10: cryptographic verify.\n const base = buildSignatureBase(\n parsedInput.components,\n request,\n parsedInput.params,\n parsedInput.signatureParamsValue\n );\n const publicKey = jwkToPublicKey(jwk);\n const valid = verifySignature(parsedInput.params.alg, publicKey, Buffer.from(base, 'utf8'), parsedSig.bytes);\n if (!valid) {\n throw new WebhookSignatureError(\n 'webhook_signature_invalid',\n 10,\n 'Cryptographic verification of webhook signature base failed.'\n );\n }\n\n // Step 11: content-digest match.\n const digestHeader = getHeaderValue(request.headers, 'Content-Digest');\n if (!digestHeader || !contentDigestMatches(digestHeader, request.body ?? '')) {\n throw new WebhookSignatureError(\n 'webhook_signature_digest_mismatch',\n 11,\n 'Content-Digest header does not match recomputed body hash.'\n );\n }\n\n // Step 13: commit nonce. Insert AFTER every prior check passes — this is\n // the load-bearing invariant that keeps external traffic from growing the\n // cap (any signature failing at step 10 never consumes an entry).\n const remaining = parsedInput.params.expires - now + CLOCK_SKEW_TOLERANCE_SECONDS;\n const ttl = Math.max(remaining, MAX_SIGNATURE_WINDOW_SECONDS + CLOCK_SKEW_TOLERANCE_SECONDS);\n const insertResult = await options.replayStore.insert(jwk.kid, replayScope, parsedInput.params.nonce, ttl, now);\n if (insertResult === 'replayed') {\n throw new WebhookSignatureError(\n 'webhook_signature_replayed',\n 13,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window.`\n );\n }\n if (insertResult === 'rate_abuse') {\n throw new WebhookSignatureError(\n 'webhook_signature_rate_abuse',\n 13,\n `Per-keyid replay cache cap exceeded on commit for keyid=${jwk.kid}.`\n );\n }\n\n const agent_url = options.agentUrlForKeyid?.(jwk.kid);\n return { status: 'verified', keyid: jwk.kid, ...(agent_url !== undefined && { agent_url }), verified_at: now };\n}\n\nfunction requireParams(parsed: ParsedSignatureInput): void {\n const required: Array<keyof ParsedSignatureInput['params']> = ['created', 'expires', 'nonce', 'keyid', 'alg', 'tag'];\n const missing = required.filter(k => parsed.params[k] === undefined);\n if (missing.length) {\n throw new WebhookSignatureError(\n 'webhook_signature_params_incomplete',\n 2,\n `Signature-Input missing required parameter(s): ${missing.join(', ')}.`\n );\n }\n}\n\nfunction validateWindow(created: number, expires: number, now: number): void {\n // Spec folds every window-level failure — expired, negative window,\n // over-long window, created-in-future — into webhook_signature_window_invalid.\n // The error message carries the specific subtype for diagnostics.\n if (expires <= created) {\n throw new WebhookSignatureError(\n 'webhook_signature_window_invalid',\n 5,\n 'Signature expires must be strictly greater than created.'\n );\n }\n if (expires - created > MAX_SIGNATURE_WINDOW_SECONDS) {\n throw new WebhookSignatureError(\n 'webhook_signature_window_invalid',\n 5,\n `Signature window exceeds ${MAX_SIGNATURE_WINDOW_SECONDS}s maximum.`\n );\n }\n if (now < created - CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new WebhookSignatureError(\n 'webhook_signature_window_invalid',\n 5,\n 'Signature created is in the future beyond skew tolerance.'\n );\n }\n if (now > expires + CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new WebhookSignatureError('webhook_signature_window_invalid', 5, 'Signature is expired.');\n }\n}\n\nfunction validateCoveredComponents(components: string[]): void {\n for (const mandatory of WEBHOOK_MANDATORY_COMPONENTS) {\n if (!components.includes(mandatory)) {\n throw new WebhookSignatureError(\n 'webhook_signature_components_incomplete',\n 6,\n `Covered components must include \"${mandatory}\".`\n );\n }\n }\n}\n\n/**\n * Syntactic validation of the `@target-uri` value before signature\n * computation. Four failure modes:\n * - URL doesn't parse at all.\n * - Scheme is not https (webhooks MUST be TLS-terminated; loopback\n * addresses are exempt so `loopback_mock` test receivers — the\n * storyboard runner's default — can operate over plain http).\n * - Authority contains userinfo — credentials don't belong in a signed URI.\n * - URL carries a fragment — fragments are client-side and never transmitted.\n *\n * Each throws `webhook_target_uri_malformed` with the failure reason in the\n * message. Distinct from `webhook_signature_header_malformed`, which flags\n * the Signature / Signature-Input headers themselves.\n */\nfunction validateTargetUri(rawUrl: string): void {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new WebhookSignatureError(\n 'webhook_target_uri_malformed',\n 6,\n `@target-uri \"${rawUrl}\" is not a parseable URL.`\n );\n }\n if (url.protocol !== 'https:' && !isLoopbackHost(url.hostname)) {\n throw new WebhookSignatureError(\n 'webhook_target_uri_malformed',\n 6,\n `@target-uri must use https; got \"${url.protocol}\" in \"${rawUrl}\".`\n );\n }\n if (url.username || url.password) {\n throw new WebhookSignatureError('webhook_target_uri_malformed', 6, '@target-uri must not embed userinfo.');\n }\n if (url.hash) {\n throw new WebhookSignatureError('webhook_target_uri_malformed', 6, '@target-uri must not carry a fragment.');\n }\n}\n\n/**\n * Loopback host exemption for the https-only rule. The storyboard runner's\n * `loopback_mock` webhook receiver binds to 127.0.0.1 (or the IPv6 [::1])\n * and publishers emit `http://127.0.0.1:<port>/…` URLs for delivery; the\n * signature covers `@target-uri` byte-for-byte, so the verifier sees the\n * http scheme. Production traffic never reaches loopback, so this exemption\n * can't be used to bypass TLS for real webhooks.\n */\nfunction isLoopbackHost(hostname: string): boolean {\n if (!hostname) return false;\n const normalized = hostname.toLowerCase();\n return normalized === 'localhost' || normalized === '::1' || normalized.startsWith('127.');\n}\n\n/**\n * Options for {@link createWebhookVerifier}. Identical to\n * {@link VerifyWebhookOptions} except `replayStore` and `revocationStore` are\n * optional — the factory defaults them once at creation time so replay state\n * is shared across every request the returned verifier handles.\n */\nexport interface CreateWebhookVerifierOptions extends Omit<VerifyWebhookOptions, 'replayStore' | 'revocationStore'> {\n /**\n * Stores `(keyid, scope, nonce)` tuples for replay detection.\n * Defaults to a fresh {@link InMemoryReplayStore} — suitable for single-process\n * deployments only. **Multi-replica deployments MUST pass an explicit shared\n * store** (Redis, Postgres, etc.) — the default in-memory store does not\n * survive process boundaries, so a signature accepted on replica A is\n * invisible to replica B and can be replayed there within the signature window.\n */\n replayStore?: ReplayStore;\n /**\n * Consulted for revoked `kid` before accepting a signature.\n * Defaults to a fresh {@link InMemoryRevocationStore}, which starts empty\n * and does not poll for updates. The default is sufficient when you don't\n * revoke keys at runtime; when you do, pass a store backed by your secrets\n * manager or admin tooling so revocations take effect without redeployment.\n */\n revocationStore?: RevocationStore;\n}\n\n/**\n * Create a bound webhook-signature verifier with shared replay and revocation\n * stores. Mirrors {@link createExpressVerifier} for the webhook profile.\n *\n * The returned function is the per-request entry point: call it with each\n * inbound webhook's {@link RequestLike} representation.\n *\n * **Why a factory?** Replay detection requires the same store instance to be\n * consulted across every request. Constructing stores inside a per-request\n * call would silently defeat replay dedup — each call would start with an\n * empty store and always accept the nonce. The factory pattern captures the\n * store instances in closure scope at wire-up time, guaranteeing they are\n * shared across calls for the lifetime of the verifier.\n *\n * **Multi-replica deployments MUST pass an explicit `replayStore`** backed by\n * a shared persistence layer (Redis, Postgres, etc.) — the default\n * `InMemoryReplayStore` does not survive process boundaries. A nonce accepted\n * on replica A is invisible to replica B; replaying the webhook to B succeeds\n * silently within the signature window.\n */\nexport function createWebhookVerifier(\n options: CreateWebhookVerifierOptions\n): (request: RequestLike) => Promise<VerifyWebhookResult> {\n // Instantiate defaults once at creation time so every request handled by\n // this verifier shares the same replay / revocation state. Per-request\n // construction would defeat replay detection entirely.\n const replayStore = options.replayStore ?? new InMemoryReplayStore();\n const revocationStore = options.revocationStore ?? new InMemoryRevocationStore();\n return (request: RequestLike) => verifyWebhookSignature(request, { ...options, replayStore, revocationStore });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/signing/webhook-verifier.ts"],"sourcesContent":["/**\n * RFC 9421 webhook-signing verifier (AdCP 3.0 webhook profile).\n *\n * Companion to `verifier.ts`, which verifies outbound request signatures.\n * This verifier runs on the receiving side of a publisher webhook: the\n * storyboard runner's receiver captures a POST and hands the request (headers\n * + raw body) here for signature validation.\n *\n * Distinct from request-signing:\n * - Tag: `adcp/webhook-signing/v1` (vs `adcp/request-signing/v1`).\n * - Key purpose: `adcp_use: \"webhook-signing\"` OR `\"request-signing\"` — a\n * signer may reuse its request-signing key for webhooks (see step 8).\n * - Covered components MUST include `@method`, `@target-uri`, `@authority`,\n * `content-type`, and `content-digest` — `content-digest` is unconditional\n * for webhooks (vs policy-driven on requests) because every webhook\n * carries a JSON body.\n * - JWKS resolved via the publisher's `brand.json` `agents[]` `jwks_uri`,\n * not the request-path capability document.\n *\n * Checklist steps below mirror the 14-step shape in\n * `docs/building/implementation/security.mdx#verifier-checklist-for-webhooks`\n * so failures can point at a specific spec clause. Numbers are 1-based to\n * match the security doc.\n */\n\nimport { buildSignatureBase, canonicalTargetUri, getHeaderValue, type RequestLike } from './canonicalize';\nimport { contentDigestMatches } from './content-digest';\nimport { RequestSignatureError, WebhookSignatureError } from './errors';\nimport { parseSignature, parseSignatureInput, type ParsedSignatureInput } from './parser';\nimport { jwkToPublicKey, verifySignature } from './crypto';\nimport type { JwksResolver } from './jwks';\nimport { InMemoryReplayStore, type ReplayStore } from './replay';\nimport { InMemoryRevocationStore, type RevocationStore } from './revocation';\nimport { ALLOWED_ALGS, CLOCK_SKEW_TOLERANCE_SECONDS, MAX_SIGNATURE_WINDOW_SECONDS } from './types';\n\nexport const WEBHOOK_SIGNING_TAG = 'adcp/webhook-signing/v1';\n\n/**\n * Covered-component minimum for webhooks. Unlike request-signing, every\n * webhook carries a body so `content-type` and `content-digest` are\n * unconditional — a webhook without `content-digest` coverage can't be\n * verified because the body has no anchor.\n */\nexport const WEBHOOK_MANDATORY_COMPONENTS: ReadonlyArray<string> = [\n '@method',\n '@target-uri',\n '@authority',\n 'content-type',\n 'content-digest',\n];\n\nexport interface VerifyWebhookOptions {\n jwks: JwksResolver;\n replayStore: ReplayStore;\n revocationStore: RevocationStore;\n /** Now in seconds since epoch. Defaults to `Date.now() / 1000`. */\n now?: () => number;\n /**\n * Optional tag override — spec currently defines exactly\n * `adcp/webhook-signing/v1`; the override lets test vectors pin a version.\n */\n requiredTag?: string;\n /** Optional reverse-lookup (kid → publisher URL) for result attribution. */\n agentUrlForKeyid?: (keyid: string) => string | undefined;\n}\n\nexport interface VerifyWebhookResult {\n status: 'verified';\n keyid: string;\n agent_url?: string;\n verified_at: number;\n}\n\n/**\n * Verify an inbound webhook's RFC 9421 signature.\n *\n * Checklist steps 1–13 plus sub-step 9a, matching the canonical numbering\n * in `security.mdx#verifier-checklist-for-webhooks` (and the `failed_step`\n * values on the conformance vectors at\n * `test-vectors/webhook-signing/negative/`). Throws\n * `WebhookSignatureError` on the first failed step.\n *\n * Ordering invariant: cheap invariant checks (tag, alg, window, components)\n * run before JWKS resolution (step 7); revocation (9) and rate-abuse (9a)\n * run before cryptographic verify (10) so an attacker can't amplify\n * Ed25519/ECDSA work. Replay insert (13) commits only after every earlier\n * step has passed — any signature failing at step 10 never consumes a cap\n * entry, so external traffic can't grow the per-keyid cache.\n */\nexport async function verifyWebhookSignature(\n request: RequestLike,\n options: VerifyWebhookOptions\n): Promise<VerifyWebhookResult> {\n const now = options.now ? options.now() : Math.floor(Date.now() / 1000);\n const requiredTag = options.requiredTag ?? WEBHOOK_SIGNING_TAG;\n\n // Step 1: both signature headers present AND parseable. The bound-pair\n // rule (`Signature` without `Signature-Input` or vice versa) rejects here.\n const sigInputHeader = getHeaderValue(request.headers, 'Signature-Input');\n const sigHeader = getHeaderValue(request.headers, 'Signature');\n if (!sigInputHeader || !sigHeader) {\n throw new WebhookSignatureError(\n 'webhook_signature_header_malformed',\n 1,\n 'Webhook is missing Signature or Signature-Input headers.'\n );\n }\n let parsedInput: ParsedSignatureInput;\n let parsedSig: ReturnType<typeof parseSignature>;\n try {\n parsedInput = parseSignatureInput(sigInputHeader);\n parsedSig = parseSignature(sigHeader, parsedInput.label);\n } catch (err) {\n throw new WebhookSignatureError(\n 'webhook_signature_header_malformed',\n 1,\n err instanceof Error ? err.message : String(err)\n );\n }\n\n // Step 2: required params present.\n requireParams(parsedInput);\n\n // Step 3: tag match.\n if (parsedInput.params.tag !== requiredTag) {\n throw new WebhookSignatureError(\n 'webhook_signature_tag_invalid',\n 3,\n `Signature tag must be \"${requiredTag}\"; got \"${parsedInput.params.tag}\".`\n );\n }\n\n // Step 4: alg allowlist.\n if (!ALLOWED_ALGS.has(parsedInput.params.alg)) {\n throw new WebhookSignatureError(\n 'webhook_signature_alg_not_allowed',\n 4,\n `Signature alg \"${parsedInput.params.alg}\" is not in the AdCP allowlist.`\n );\n }\n\n // Step 5: window valid.\n validateWindow(parsedInput.params.created, parsedInput.params.expires, now);\n\n // Step 6: covered components.\n validateCoveredComponents(parsedInput.components);\n\n // Step 6a: `@target-uri` syntactic validation. Runs before JWKS resolution\n // and crypto verify so a malformed URI short-circuits without JWKS\n // side-effects. Distinct from `header_malformed` (the Signature /\n // Signature-Input headers themselves) — this flags the covered URI value\n // that will be fed into the signature base. Non-parseable URLs, non-https\n // schemes, embedded userinfo, and fragments are all rejected.\n validateTargetUri(request.url);\n\n // Step 7: resolve keyid.\n const jwk = await options.jwks.resolve(parsedInput.params.keyid);\n if (!jwk) {\n throw new WebhookSignatureError(\n 'webhook_signature_key_unknown',\n 7,\n `No JWK found for keyid \"${parsedInput.params.keyid}\".`\n );\n }\n if (jwk.kid !== parsedInput.params.keyid) {\n throw new WebhookSignatureError(\n 'webhook_signature_key_unknown',\n 7,\n `JWKS resolver returned a JWK whose kid \"${jwk.kid}\" does not match requested keyid \"${parsedInput.params.keyid}\".`\n );\n }\n\n // Step 8: key purpose — webhooks are signed with a `request-signing` key.\n //\n // Webhooks carry no separate key purpose: the signer uses its\n // `adcp_use: \"request-signing\"` key (the deprecated `\"webhook-signing\"`\n // value is still accepted here for backward compatibility, pending removal — adcontextprotocol/adcp#5555).\n // This is safe because cross-protocol confusion is prevented by the\n // signature `tag` (step 3, `adcp/webhook-signing/v1`, part of the signed\n // base) and mandatory `content-digest` coverage (step 6) — not by the\n // key-purpose discriminator. A captured request signature\n // (`tag=adcp/request-signing/v1`) can never be replayed against this\n // verifier because step 3 rejects it. Webhook key isolation, when wanted, is\n // a second `request-signing` key under a distinct `kid` — not a distinct\n // `adcp_use`.\n //\n // All key-purpose failures use `webhook_signature_key_purpose_invalid`:\n // absent `adcp_use`, a missing `verify` key_op, or an `adcp_use` outside the\n // accepted set (e.g. `response-signing`, `governance-signing`). We do NOT\n // reuse `webhook_mode_mismatch` here — that code is reserved for the\n // HMAC-vs-9421 auth-mode selector mismatch, and overloading it would collapse\n // two distinct failure classes onto one stable code receivers branch on.\n if (\n jwk.adcp_use === undefined ||\n !jwk.key_ops?.includes('verify') ||\n (jwk.adcp_use !== 'request-signing' && jwk.adcp_use !== 'webhook-signing')\n ) {\n throw new WebhookSignatureError(\n 'webhook_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" is not scoped for webhook delivery: adcp_use must be \"request-signing\" (or the deprecated \"webhook-signing\") with a \"verify\" key_op (got adcp_use=\"${jwk.adcp_use}\").`\n );\n }\n\n // Step 9: revocation. Shared store throws `request_signature_revocation_stale`\n // when its cached snapshot is past grace — re-map to the webhook taxonomy\n // so callers see a consistent error surface.\n try {\n if (await options.revocationStore.isRevoked(jwk.kid)) {\n throw new WebhookSignatureError('webhook_signature_key_revoked', 9, `JWK \"${jwk.kid}\" is revoked.`);\n }\n } catch (err) {\n if (err instanceof RequestSignatureError && err.code === 'request_signature_revocation_stale') {\n throw new WebhookSignatureError('webhook_signature_revocation_stale', 9, err.message);\n }\n throw err;\n }\n\n // Replay cache is scoped by `(keyid, @target-uri)` per adcp#2460 — a\n // webhook captured on one receiver path MUST NOT count against the replay\n // budget for a different path under the same keyid. Canonicalize once and\n // reuse for both pre-check and commit.\n const replayScope = canonicalTargetUri(request.url);\n\n // Step 9a: per-keyid rate abuse. Distinct code from step 12's replay —\n // cap exhaustion is a compromised-key / misconfig signal that SHOULD\n // alert operators, not \"same nonce twice.\"\n if (await options.replayStore.isCapHit(jwk.kid, replayScope, now)) {\n throw new WebhookSignatureError(\n 'webhook_signature_rate_abuse',\n 9,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}.`\n );\n }\n\n // Pre-check step 12's replay before crypto so a replayed nonce short-\n // circuits an expensive Ed25519/ECDSA verify.\n if (await options.replayStore.has(jwk.kid, replayScope, parsedInput.params.nonce, now)) {\n throw new WebhookSignatureError(\n 'webhook_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window.`\n );\n }\n\n // Step 10: cryptographic verify.\n const base = buildSignatureBase(\n parsedInput.components,\n request,\n parsedInput.params,\n parsedInput.signatureParamsValue\n );\n const publicKey = jwkToPublicKey(jwk);\n const valid = verifySignature(parsedInput.params.alg, publicKey, Buffer.from(base, 'utf8'), parsedSig.bytes);\n if (!valid) {\n throw new WebhookSignatureError(\n 'webhook_signature_invalid',\n 10,\n 'Cryptographic verification of webhook signature base failed.'\n );\n }\n\n // Step 11: content-digest match.\n const digestHeader = getHeaderValue(request.headers, 'Content-Digest');\n if (!digestHeader || !contentDigestMatches(digestHeader, request.body ?? '')) {\n throw new WebhookSignatureError(\n 'webhook_signature_digest_mismatch',\n 11,\n 'Content-Digest header does not match recomputed body hash.'\n );\n }\n\n // Step 13: commit nonce. Insert AFTER every prior check passes — this is\n // the load-bearing invariant that keeps external traffic from growing the\n // cap (any signature failing at step 10 never consumes an entry).\n const remaining = parsedInput.params.expires - now + CLOCK_SKEW_TOLERANCE_SECONDS;\n const ttl = Math.max(remaining, MAX_SIGNATURE_WINDOW_SECONDS + CLOCK_SKEW_TOLERANCE_SECONDS);\n const insertResult = await options.replayStore.insert(jwk.kid, replayScope, parsedInput.params.nonce, ttl, now);\n if (insertResult === 'replayed') {\n throw new WebhookSignatureError(\n 'webhook_signature_replayed',\n 13,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window.`\n );\n }\n if (insertResult === 'rate_abuse') {\n throw new WebhookSignatureError(\n 'webhook_signature_rate_abuse',\n 13,\n `Per-keyid replay cache cap exceeded on commit for keyid=${jwk.kid}.`\n );\n }\n\n const agent_url = options.agentUrlForKeyid?.(jwk.kid);\n return { status: 'verified', keyid: jwk.kid, ...(agent_url !== undefined && { agent_url }), verified_at: now };\n}\n\nfunction requireParams(parsed: ParsedSignatureInput): void {\n const required: Array<keyof ParsedSignatureInput['params']> = ['created', 'expires', 'nonce', 'keyid', 'alg', 'tag'];\n const missing = required.filter(k => parsed.params[k] === undefined);\n if (missing.length) {\n throw new WebhookSignatureError(\n 'webhook_signature_params_incomplete',\n 2,\n `Signature-Input missing required parameter(s): ${missing.join(', ')}.`\n );\n }\n}\n\nfunction validateWindow(created: number, expires: number, now: number): void {\n // Spec folds every window-level failure — expired, negative window,\n // over-long window, created-in-future — into webhook_signature_window_invalid.\n // The error message carries the specific subtype for diagnostics.\n if (expires <= created) {\n throw new WebhookSignatureError(\n 'webhook_signature_window_invalid',\n 5,\n 'Signature expires must be strictly greater than created.'\n );\n }\n if (expires - created > MAX_SIGNATURE_WINDOW_SECONDS) {\n throw new WebhookSignatureError(\n 'webhook_signature_window_invalid',\n 5,\n `Signature window exceeds ${MAX_SIGNATURE_WINDOW_SECONDS}s maximum.`\n );\n }\n if (now < created - CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new WebhookSignatureError(\n 'webhook_signature_window_invalid',\n 5,\n 'Signature created is in the future beyond skew tolerance.'\n );\n }\n if (now > expires + CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new WebhookSignatureError('webhook_signature_window_invalid', 5, 'Signature is expired.');\n }\n}\n\nfunction validateCoveredComponents(components: string[]): void {\n for (const mandatory of WEBHOOK_MANDATORY_COMPONENTS) {\n if (!components.includes(mandatory)) {\n throw new WebhookSignatureError(\n 'webhook_signature_components_incomplete',\n 6,\n `Covered components must include \"${mandatory}\".`\n );\n }\n }\n}\n\n/**\n * Syntactic validation of the `@target-uri` value before signature\n * computation. Four failure modes:\n * - URL doesn't parse at all.\n * - Scheme is not https (webhooks MUST be TLS-terminated; loopback\n * addresses are exempt so `loopback_mock` test receivers — the\n * storyboard runner's default — can operate over plain http).\n * - Authority contains userinfo — credentials don't belong in a signed URI.\n * - URL carries a fragment — fragments are client-side and never transmitted.\n *\n * Each throws `webhook_target_uri_malformed` with the failure reason in the\n * message. Distinct from `webhook_signature_header_malformed`, which flags\n * the Signature / Signature-Input headers themselves.\n */\nfunction validateTargetUri(rawUrl: string): void {\n let url: URL;\n try {\n url = new URL(rawUrl);\n } catch {\n throw new WebhookSignatureError(\n 'webhook_target_uri_malformed',\n 6,\n `@target-uri \"${rawUrl}\" is not a parseable URL.`\n );\n }\n if (url.protocol !== 'https:' && !isLoopbackHost(url.hostname)) {\n throw new WebhookSignatureError(\n 'webhook_target_uri_malformed',\n 6,\n `@target-uri must use https; got \"${url.protocol}\" in \"${rawUrl}\".`\n );\n }\n if (url.username || url.password) {\n throw new WebhookSignatureError('webhook_target_uri_malformed', 6, '@target-uri must not embed userinfo.');\n }\n if (url.hash) {\n throw new WebhookSignatureError('webhook_target_uri_malformed', 6, '@target-uri must not carry a fragment.');\n }\n}\n\n/**\n * Loopback host exemption for the https-only rule. The storyboard runner's\n * `loopback_mock` webhook receiver binds to 127.0.0.1 (or the IPv6 [::1])\n * and publishers emit `http://127.0.0.1:<port>/…` URLs for delivery; the\n * signature covers `@target-uri` byte-for-byte, so the verifier sees the\n * http scheme. Production traffic never reaches loopback, so this exemption\n * can't be used to bypass TLS for real webhooks.\n */\nfunction isLoopbackHost(hostname: string): boolean {\n if (!hostname) return false;\n const normalized = hostname.toLowerCase();\n return normalized === 'localhost' || normalized === '::1' || normalized.startsWith('127.');\n}\n\n/**\n * Options for {@link createWebhookVerifier}. Identical to\n * {@link VerifyWebhookOptions} except `replayStore` and `revocationStore` are\n * optional — the factory defaults them once at creation time so replay state\n * is shared across every request the returned verifier handles.\n */\nexport interface CreateWebhookVerifierOptions extends Omit<VerifyWebhookOptions, 'replayStore' | 'revocationStore'> {\n /**\n * Stores `(keyid, scope, nonce)` tuples for replay detection.\n * Defaults to a fresh {@link InMemoryReplayStore} — suitable for single-process\n * deployments only. **Multi-replica deployments MUST pass an explicit shared\n * store** (Redis, Postgres, etc.) — the default in-memory store does not\n * survive process boundaries, so a signature accepted on replica A is\n * invisible to replica B and can be replayed there within the signature window.\n */\n replayStore?: ReplayStore;\n /**\n * Consulted for revoked `kid` before accepting a signature.\n * Defaults to a fresh {@link InMemoryRevocationStore}, which starts empty\n * and does not poll for updates. The default is sufficient when you don't\n * revoke keys at runtime; when you do, pass a store backed by your secrets\n * manager or admin tooling so revocations take effect without redeployment.\n */\n revocationStore?: RevocationStore;\n}\n\n/**\n * Create a bound webhook-signature verifier with shared replay and revocation\n * stores. Mirrors {@link createExpressVerifier} for the webhook profile.\n *\n * The returned function is the per-request entry point: call it with each\n * inbound webhook's {@link RequestLike} representation.\n *\n * **Why a factory?** Replay detection requires the same store instance to be\n * consulted across every request. Constructing stores inside a per-request\n * call would silently defeat replay dedup — each call would start with an\n * empty store and always accept the nonce. The factory pattern captures the\n * store instances in closure scope at wire-up time, guaranteeing they are\n * shared across calls for the lifetime of the verifier.\n *\n * **Multi-replica deployments MUST pass an explicit `replayStore`** backed by\n * a shared persistence layer (Redis, Postgres, etc.) — the default\n * `InMemoryReplayStore` does not survive process boundaries. A nonce accepted\n * on replica A is invisible to replica B; replaying the webhook to B succeeds\n * silently within the signature window.\n */\nexport function createWebhookVerifier(\n options: CreateWebhookVerifierOptions\n): (request: RequestLike) => Promise<VerifyWebhookResult> {\n // Instantiate defaults once at creation time so every request handled by\n // this verifier shares the same replay / revocation state. Per-request\n // construction would defeat replay detection entirely.\n const replayStore = options.replayStore ?? new InMemoryReplayStore();\n const revocationStore = options.revocationStore ?? new InMemoryRevocationStore();\n return (request: RequestLike) => verifyWebhookSignature(request, { ...options, replayStore, revocationStore });\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
|
class MemoryStorage {
|
|
8
2
|
constructor(options = {}) {
|
|
9
3
|
this.options = options;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/storage/MemoryStorage.ts"],"sourcesContent":["// Default in-memory storage implementation\n// Works out of the box without any external dependencies\n\nimport type { Storage, BatchStorage, PatternStorage } from './interfaces';\n\n/**\n * Stored item with optional expiration\n */\ninterface StoredItem<T> {\n value: T;\n expiresAt?: number;\n createdAt: number;\n}\n\n/**\n * In-memory storage implementation with TTL support\n *\n * This is the default storage used when no external storage is configured.\n * Features:\n * - TTL support with automatic cleanup\n * - Pattern matching\n * - Batch operations\n * - Memory-efficient (garbage collection of expired items)\n *\n * @example\n * ```typescript\n * const storage = new MemoryStorage<string>();\n * await storage.set('key', 'value', 60); // TTL of 60 seconds\n * const value = await storage.get('key');\n * ```\n */\nexport class MemoryStorage<T> implements Storage<T>, BatchStorage<T>, PatternStorage<T> {\n private store = new Map<string, StoredItem<T>>();\n private cleanupInterval?: NodeJS.Timeout;\n private lastCleanup = 0;\n\n constructor(\n private options: {\n /** How often to clean up expired items (ms), default 5 minutes */\n cleanupIntervalMs?: number;\n /** Maximum items to store before forcing cleanup, default 10000 */\n maxItems?: number;\n /** Whether to enable automatic cleanup, default true */\n autoCleanup?: boolean;\n } = {}\n ) {\n const {\n cleanupIntervalMs = 5 * 60 * 1000, // 5 minutes\n autoCleanup = true,\n } = options;\n\n if (autoCleanup) {\n this.cleanupInterval = setInterval(() => {\n this.cleanupExpired();\n }, cleanupIntervalMs);\n }\n }\n\n async get(key: string): Promise<T | undefined> {\n const item = this.store.get(key);\n\n if (!item) {\n return undefined;\n }\n\n // Check if expired\n if (item.expiresAt && Date.now() > item.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n\n return item.value;\n }\n\n async set(key: string, value: T, ttl?: number): Promise<void> {\n const now = Date.now();\n const expiresAt = ttl ? now + ttl * 1000 : undefined;\n\n this.store.set(key, {\n value,\n expiresAt,\n createdAt: now,\n });\n\n // Force cleanup if we're approaching max items\n const maxItems = this.options.maxItems || 10000;\n if (this.store.size > maxItems) {\n this.cleanupExpired();\n }\n }\n\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async has(key: string): Promise<boolean> {\n const value = await this.get(key);\n return value !== undefined;\n }\n\n async clear(): Promise<void> {\n this.store.clear();\n }\n\n async keys(): Promise<string[]> {\n // Return only non-expired keys\n const keys: string[] = [];\n const now = Date.now();\n\n for (const [key, item] of this.store) {\n if (!item.expiresAt || now <= item.expiresAt) {\n keys.push(key);\n }\n }\n\n return keys;\n }\n\n async size(): Promise<number> {\n // Count only non-expired items\n const keys = await this.keys();\n return keys.length;\n }\n\n // ====== BATCH OPERATIONS ======\n\n async mget(keys: string[]): Promise<(T | undefined)[]> {\n const promises = keys.map(key => this.get(key));\n return Promise.all(promises);\n }\n\n async mset(entries: Array<{ key: string; value: T; ttl?: number }>): Promise<void> {\n const promises = entries.map(({ key, value, ttl }) => this.set(key, value, ttl));\n await Promise.all(promises);\n }\n\n async mdel(keys: string[]): Promise<number> {\n let deleted = 0;\n for (const key of keys) {\n if (this.store.has(key)) {\n this.store.delete(key);\n deleted++;\n }\n }\n return deleted;\n }\n\n // ====== PATTERN OPERATIONS ======\n\n async scan(pattern: string): Promise<string[]> {\n const regex = this.patternToRegex(pattern);\n const allKeys = await this.keys();\n return allKeys.filter(key => regex.test(key));\n }\n\n async deletePattern(pattern: string): Promise<number> {\n const keys = await this.scan(pattern);\n return this.mdel(keys);\n }\n\n // ====== UTILITY METHODS ======\n\n /**\n * Manually trigger cleanup of expired items\n */\n cleanupExpired(): number {\n const now = Date.now();\n let cleaned = 0;\n\n for (const [key, item] of this.store) {\n if (item.expiresAt && now > item.expiresAt) {\n this.store.delete(key);\n cleaned++;\n }\n }\n\n this.lastCleanup = now;\n return cleaned;\n }\n\n /**\n * Get storage statistics\n */\n getStats(): {\n totalItems: number;\n expiredItems: number;\n memoryUsage: number;\n lastCleanup: number;\n oldestItem?: number;\n newestItem?: number;\n } {\n const now = Date.now();\n let expiredItems = 0;\n let oldestItem: number | undefined;\n let newestItem: number | undefined;\n\n for (const [, item] of this.store) {\n if (item.expiresAt && now > item.expiresAt) {\n expiredItems++;\n }\n\n if (!oldestItem || item.createdAt < oldestItem) {\n oldestItem = item.createdAt;\n }\n\n if (!newestItem || item.createdAt > newestItem) {\n newestItem = item.createdAt;\n }\n }\n\n return {\n totalItems: this.store.size,\n expiredItems,\n memoryUsage: this.estimateMemoryUsage(),\n lastCleanup: this.lastCleanup,\n oldestItem,\n newestItem,\n };\n }\n\n /**\n * Estimate memory usage (rough approximation)\n */\n private estimateMemoryUsage(): number {\n let bytes = 0;\n\n for (const [key, item] of this.store) {\n // Rough estimate: string length * 2 (UTF-16) + object overhead\n bytes += key.length * 2;\n bytes += JSON.stringify(item.value).length * 2;\n bytes += 64; // Overhead for object and metadata\n }\n\n return bytes;\n }\n\n /**\n * Convert glob-style pattern to regex\n */\n private patternToRegex(pattern: string): RegExp {\n // Escape special regex characters except * and ?\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&');\n\n // Convert glob wildcards to regex\n const regexPattern = escaped\n .replace(/\\*/g, '.*') // * matches any sequence\n .replace(/\\?/g, '.'); // ? matches any single character\n\n return new RegExp(`^${regexPattern}$`);\n }\n\n /**\n * Destroy the storage and cleanup resources\n */\n destroy(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = undefined;\n }\n this.store.clear();\n }\n}\n\n/**\n * Factory function to create a memory storage instance\n */\nexport function createMemoryStorage<T>(options?: {\n cleanupIntervalMs?: number;\n maxItems?: number;\n autoCleanup?: boolean;\n}): MemoryStorage<T> {\n return new MemoryStorage<T>(options);\n}\n\n/**\n * Create a complete storage configuration using memory storage\n */\nexport function createMemoryStorageConfig(): {\n capabilities: MemoryStorage<any>;\n conversations: MemoryStorage<any>;\n tokens: MemoryStorage<any>;\n debugLogs: MemoryStorage<any>;\n} {\n return {\n capabilities: createMemoryStorage({ maxItems: 1000 }),\n conversations: createMemoryStorage({ maxItems: 5000 }),\n tokens: createMemoryStorage({ maxItems: 10000 }),\n debugLogs: createMemoryStorage({ maxItems: 50000 }),\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/storage/MemoryStorage.ts"],"sourcesContent":["// Default in-memory storage implementation\n// Works out of the box without any external dependencies\n\nimport type { Storage, BatchStorage, PatternStorage } from './interfaces';\n\n/**\n * Stored item with optional expiration\n */\ninterface StoredItem<T> {\n value: T;\n expiresAt?: number;\n createdAt: number;\n}\n\n/**\n * In-memory storage implementation with TTL support\n *\n * This is the default storage used when no external storage is configured.\n * Features:\n * - TTL support with automatic cleanup\n * - Pattern matching\n * - Batch operations\n * - Memory-efficient (garbage collection of expired items)\n *\n * @example\n * ```typescript\n * const storage = new MemoryStorage<string>();\n * await storage.set('key', 'value', 60); // TTL of 60 seconds\n * const value = await storage.get('key');\n * ```\n */\nexport class MemoryStorage<T> implements Storage<T>, BatchStorage<T>, PatternStorage<T> {\n private store = new Map<string, StoredItem<T>>();\n private cleanupInterval?: NodeJS.Timeout;\n private lastCleanup = 0;\n\n constructor(\n private options: {\n /** How often to clean up expired items (ms), default 5 minutes */\n cleanupIntervalMs?: number;\n /** Maximum items to store before forcing cleanup, default 10000 */\n maxItems?: number;\n /** Whether to enable automatic cleanup, default true */\n autoCleanup?: boolean;\n } = {}\n ) {\n const {\n cleanupIntervalMs = 5 * 60 * 1000, // 5 minutes\n autoCleanup = true,\n } = options;\n\n if (autoCleanup) {\n this.cleanupInterval = setInterval(() => {\n this.cleanupExpired();\n }, cleanupIntervalMs);\n }\n }\n\n async get(key: string): Promise<T | undefined> {\n const item = this.store.get(key);\n\n if (!item) {\n return undefined;\n }\n\n // Check if expired\n if (item.expiresAt && Date.now() > item.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n\n return item.value;\n }\n\n async set(key: string, value: T, ttl?: number): Promise<void> {\n const now = Date.now();\n const expiresAt = ttl ? now + ttl * 1000 : undefined;\n\n this.store.set(key, {\n value,\n expiresAt,\n createdAt: now,\n });\n\n // Force cleanup if we're approaching max items\n const maxItems = this.options.maxItems || 10000;\n if (this.store.size > maxItems) {\n this.cleanupExpired();\n }\n }\n\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async has(key: string): Promise<boolean> {\n const value = await this.get(key);\n return value !== undefined;\n }\n\n async clear(): Promise<void> {\n this.store.clear();\n }\n\n async keys(): Promise<string[]> {\n // Return only non-expired keys\n const keys: string[] = [];\n const now = Date.now();\n\n for (const [key, item] of this.store) {\n if (!item.expiresAt || now <= item.expiresAt) {\n keys.push(key);\n }\n }\n\n return keys;\n }\n\n async size(): Promise<number> {\n // Count only non-expired items\n const keys = await this.keys();\n return keys.length;\n }\n\n // ====== BATCH OPERATIONS ======\n\n async mget(keys: string[]): Promise<(T | undefined)[]> {\n const promises = keys.map(key => this.get(key));\n return Promise.all(promises);\n }\n\n async mset(entries: Array<{ key: string; value: T; ttl?: number }>): Promise<void> {\n const promises = entries.map(({ key, value, ttl }) => this.set(key, value, ttl));\n await Promise.all(promises);\n }\n\n async mdel(keys: string[]): Promise<number> {\n let deleted = 0;\n for (const key of keys) {\n if (this.store.has(key)) {\n this.store.delete(key);\n deleted++;\n }\n }\n return deleted;\n }\n\n // ====== PATTERN OPERATIONS ======\n\n async scan(pattern: string): Promise<string[]> {\n const regex = this.patternToRegex(pattern);\n const allKeys = await this.keys();\n return allKeys.filter(key => regex.test(key));\n }\n\n async deletePattern(pattern: string): Promise<number> {\n const keys = await this.scan(pattern);\n return this.mdel(keys);\n }\n\n // ====== UTILITY METHODS ======\n\n /**\n * Manually trigger cleanup of expired items\n */\n cleanupExpired(): number {\n const now = Date.now();\n let cleaned = 0;\n\n for (const [key, item] of this.store) {\n if (item.expiresAt && now > item.expiresAt) {\n this.store.delete(key);\n cleaned++;\n }\n }\n\n this.lastCleanup = now;\n return cleaned;\n }\n\n /**\n * Get storage statistics\n */\n getStats(): {\n totalItems: number;\n expiredItems: number;\n memoryUsage: number;\n lastCleanup: number;\n oldestItem?: number;\n newestItem?: number;\n } {\n const now = Date.now();\n let expiredItems = 0;\n let oldestItem: number | undefined;\n let newestItem: number | undefined;\n\n for (const [, item] of this.store) {\n if (item.expiresAt && now > item.expiresAt) {\n expiredItems++;\n }\n\n if (!oldestItem || item.createdAt < oldestItem) {\n oldestItem = item.createdAt;\n }\n\n if (!newestItem || item.createdAt > newestItem) {\n newestItem = item.createdAt;\n }\n }\n\n return {\n totalItems: this.store.size,\n expiredItems,\n memoryUsage: this.estimateMemoryUsage(),\n lastCleanup: this.lastCleanup,\n oldestItem,\n newestItem,\n };\n }\n\n /**\n * Estimate memory usage (rough approximation)\n */\n private estimateMemoryUsage(): number {\n let bytes = 0;\n\n for (const [key, item] of this.store) {\n // Rough estimate: string length * 2 (UTF-16) + object overhead\n bytes += key.length * 2;\n bytes += JSON.stringify(item.value).length * 2;\n bytes += 64; // Overhead for object and metadata\n }\n\n return bytes;\n }\n\n /**\n * Convert glob-style pattern to regex\n */\n private patternToRegex(pattern: string): RegExp {\n // Escape special regex characters except * and ?\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&');\n\n // Convert glob wildcards to regex\n const regexPattern = escaped\n .replace(/\\*/g, '.*') // * matches any sequence\n .replace(/\\?/g, '.'); // ? matches any single character\n\n return new RegExp(`^${regexPattern}$`);\n }\n\n /**\n * Destroy the storage and cleanup resources\n */\n destroy(): void {\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = undefined;\n }\n this.store.clear();\n }\n}\n\n/**\n * Factory function to create a memory storage instance\n */\nexport function createMemoryStorage<T>(options?: {\n cleanupIntervalMs?: number;\n maxItems?: number;\n autoCleanup?: boolean;\n}): MemoryStorage<T> {\n return new MemoryStorage<T>(options);\n}\n\n/**\n * Create a complete storage configuration using memory storage\n */\nexport function createMemoryStorageConfig(): {\n capabilities: MemoryStorage<any>;\n conversations: MemoryStorage<any>;\n tokens: MemoryStorage<any>;\n debugLogs: MemoryStorage<any>;\n} {\n return {\n capabilities: createMemoryStorage({ maxItems: 1000 }),\n conversations: createMemoryStorage({ maxItems: 5000 }),\n tokens: createMemoryStorage({ maxItems: 10000 }),\n debugLogs: createMemoryStorage({ maxItems: 50000 }),\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1 @@
|
|
|
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
|
//# sourceMappingURL=interfaces.mjs.map
|
|
@@ -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 { encodeUnreserved } from "../rfc3986.mjs";
|
|
8
2
|
const ADCP_MACRO_PATTERN = /\{[A-Z][A-Z0-9_]*\}/;
|
|
9
3
|
class MacroInRawValueError extends Error {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/substitution/encoder/SubstitutionEncoder.ts"],"sourcesContent":["/**\n * Seller-side primitives for producing catalog-item macro substitution\n * output that satisfies the #2620 rule. The runtime counterpart of\n * {@link SubstitutionObserver} — both share the RFC 3986 implementation\n * in `../rfc3986.ts` so a single bug-fix path covers producer and verifier.\n *\n * ```ts\n * import { SubstitutionEncoder } from '@adcp/sdk/substitution';\n *\n * const encoder = new SubstitutionEncoder();\n * encoder.reject_if_contains_macro(rawValue); // optional input guard\n * const safe = encoder.encode_for_url_context(rawValue);\n * const url = template.replace('{SKU}', safe);\n * ```\n */\n\nimport { encodeUnreserved } from '../rfc3986';\n\n/**\n * Regex that matches any AdCP macro token (braces included). Catalog\n * sellers use this to refuse catalog values that would trigger a\n * second-round expansion if the downstream renderer re-scanned its\n * output. The check is a belt-and-suspenders guard; the encoder itself\n * already percent-encodes braces so a post-encoded value CAN NOT\n * re-expand.\n */\nconst ADCP_MACRO_PATTERN = /\\{[A-Z][A-Z0-9_]*\\}/;\n\nexport class MacroInRawValueError extends Error {\n readonly raw_value: string;\n readonly matched_macro: string;\n constructor(raw_value: string, matched_macro: string) {\n super(\n `Raw catalog value contains AdCP macro token ${matched_macro} — ` +\n `reject at ingest or accept the risk of second-round expansion in consumers that re-scan rendered output.`\n );\n this.name = 'MacroInRawValueError';\n this.raw_value = raw_value;\n this.matched_macro = matched_macro;\n }\n}\n\nexport class SubstitutionEncoder {\n /**\n * Strict RFC 3986 unreserved-whitelist percent-encoder. Only\n * `ALPHA / DIGIT / \"-\" / \".\" / \"_\" / \"~\"` pass through; every other\n * byte is UTF-8 + percent-encoded with uppercase hex digits.\n *\n * The behavior intentionally differs from `encodeURIComponent`, which\n * leaves `( ) * !` unescaped — those are sub-delims, not unreserved,\n * and leaving them raw fails the `url-scheme-injection-neutralized`\n * vector when a value like `javascript:alert(0)` substitutes into an\n * `href`-whole-value position.\n */\n encode_for_url_context(raw_value: string): string {\n return encodeUnreserved(raw_value);\n }\n\n /**\n * Throw if `raw_value` contains an AdCP macro token. The encoder\n * output is already safe from second-round expansion (braces become\n * `%7B` / `%7D`), so this is a defensive check for sellers who want\n * to fail-fast at catalog ingest rather than let suspicious values\n * propagate. Opt-in; callers that omit it accept the canonical\n * encode-and-move-on behavior shown by\n * `nested-expansion-preserved-as-literal`.\n */\n reject_if_contains_macro(raw_value: string): void {\n const match = raw_value.match(ADCP_MACRO_PATTERN);\n if (match) {\n throw new MacroInRawValueError(raw_value, match[0]);\n }\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/substitution/encoder/SubstitutionEncoder.ts"],"sourcesContent":["/**\n * Seller-side primitives for producing catalog-item macro substitution\n * output that satisfies the #2620 rule. The runtime counterpart of\n * {@link SubstitutionObserver} — both share the RFC 3986 implementation\n * in `../rfc3986.ts` so a single bug-fix path covers producer and verifier.\n *\n * ```ts\n * import { SubstitutionEncoder } from '@adcp/sdk/substitution';\n *\n * const encoder = new SubstitutionEncoder();\n * encoder.reject_if_contains_macro(rawValue); // optional input guard\n * const safe = encoder.encode_for_url_context(rawValue);\n * const url = template.replace('{SKU}', safe);\n * ```\n */\n\nimport { encodeUnreserved } from '../rfc3986';\n\n/**\n * Regex that matches any AdCP macro token (braces included). Catalog\n * sellers use this to refuse catalog values that would trigger a\n * second-round expansion if the downstream renderer re-scanned its\n * output. The check is a belt-and-suspenders guard; the encoder itself\n * already percent-encodes braces so a post-encoded value CAN NOT\n * re-expand.\n */\nconst ADCP_MACRO_PATTERN = /\\{[A-Z][A-Z0-9_]*\\}/;\n\nexport class MacroInRawValueError extends Error {\n readonly raw_value: string;\n readonly matched_macro: string;\n constructor(raw_value: string, matched_macro: string) {\n super(\n `Raw catalog value contains AdCP macro token ${matched_macro} — ` +\n `reject at ingest or accept the risk of second-round expansion in consumers that re-scan rendered output.`\n );\n this.name = 'MacroInRawValueError';\n this.raw_value = raw_value;\n this.matched_macro = matched_macro;\n }\n}\n\nexport class SubstitutionEncoder {\n /**\n * Strict RFC 3986 unreserved-whitelist percent-encoder. Only\n * `ALPHA / DIGIT / \"-\" / \".\" / \"_\" / \"~\"` pass through; every other\n * byte is UTF-8 + percent-encoded with uppercase hex digits.\n *\n * The behavior intentionally differs from `encodeURIComponent`, which\n * leaves `( ) * !` unescaped — those are sub-delims, not unreserved,\n * and leaving them raw fails the `url-scheme-injection-neutralized`\n * vector when a value like `javascript:alert(0)` substitutes into an\n * `href`-whole-value position.\n */\n encode_for_url_context(raw_value: string): string {\n return encodeUnreserved(raw_value);\n }\n\n /**\n * Throw if `raw_value` contains an AdCP macro token. The encoder\n * output is already safe from second-round expansion (braces become\n * `%7B` / `%7D`), so this is a defensive check for sellers who want\n * to fail-fast at catalog ingest rather than let suspicious values\n * propagate. Opt-in; callers that omit it accept the canonical\n * encode-and-move-on behavior shown by\n * `nested-expansion-preserved-as-literal`.\n */\n reject_if_contains_macro(raw_value: string): void {\n const match = raw_value.match(ADCP_MACRO_PATTERN);\n if (match) {\n throw new MacroInRawValueError(raw_value, match[0]);\n }\n }\n}\n"],"mappings":"AAgBA,SAAS,wBAAwB;AAUjC,MAAM,qBAAqB;AAEpB,MAAM,6BAA6B,MAAM;AAAA,EACrC;AAAA,EACA;AAAA,EACT,YAAY,WAAmB,eAAuB;AACpD;AAAA,MACE,+CAA+C,aAAa;AAAA,IAE9D;AACA,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,gBAAgB;AAAA,EACvB;AACF;AAEO,MAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY/B,uBAAuB,WAA2B;AAChD,WAAO,iBAAiB,SAAS;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,yBAAyB,WAAyB;AAChD,UAAM,QAAQ,UAAU,MAAM,kBAAkB;AAChD,QAAI,OAAO;AACT,YAAM,IAAI,qBAAqB,WAAW,MAAM,CAAC,CAAC;AAAA,IACpD;AAAA,EACF;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 { SubstitutionEncoder, MacroInRawValueError } from "./SubstitutionEncoder.mjs";
|
|
8
2
|
export {
|
|
9
3
|
MacroInRawValueError,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/substitution/encoder/index.ts"],"sourcesContent":["export { SubstitutionEncoder, MacroInRawValueError } from './SubstitutionEncoder';\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/substitution/encoder/index.ts"],"sourcesContent":["export { SubstitutionEncoder, MacroInRawValueError } from './SubstitutionEncoder';\n"],"mappings":"AAAA,SAAS,qBAAqB,4BAA4B;","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 { SubstitutionObserver, PreviewFetchError } from "./observer/SubstitutionObserver.mjs";
|
|
8
2
|
import { SubstitutionEncoder, MacroInRawValueError } from "./encoder/SubstitutionEncoder.mjs";
|
|
9
3
|
import { CATALOG_MACRO_VECTORS, getCatalogMacroVector } from "./vectors.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/substitution/index.ts"],"sourcesContent":["/**\n * Catalog-item macro substitution primitives — paired observer and\n * encoder surfaces implementing the `substitution-observer-runner`\n * test-kit contract and the #2620 encoding rule.\n *\n * Two disjoint consumer classes:\n *\n * - Runners grading conformance import from `./observer` (or the\n * re-exports here) for `parse_html`, `fetch_and_parse`,\n * `match_bindings`, and the assertion helpers.\n *\n * - Sales/retail-media agents implementing the #2620 rule import\n * `SubstitutionEncoder` from `./encoder`. It produces the exact\n * bytes the observer expects — one shared RFC 3986 implementation\n * means one bug-fix path for both directions.\n *\n * ```ts\n * // Seller-side\n * import { SubstitutionEncoder } from '@adcp/sdk/substitution';\n * const safe = new SubstitutionEncoder().encode_for_url_context(raw);\n *\n * // Runner-side\n * import { SubstitutionObserver, CATALOG_MACRO_VECTORS } from '@adcp/sdk/substitution';\n * const observer = new SubstitutionObserver();\n * const matches = observer.match_bindings(observer.parse_html(html), template, bindings);\n * ```\n */\n\nexport { SubstitutionObserver, PreviewFetchError } from './observer/SubstitutionObserver';\nexport type { ObserverFetchOptions, ObserverDispatcher } from './observer/SubstitutionObserver';\n\nexport { SubstitutionEncoder, MacroInRawValueError } from './encoder/SubstitutionEncoder';\n\nexport { CATALOG_MACRO_VECTORS, getCatalogMacroVector } from './vectors';\nexport type { CatalogMacroVectorName } from './vectors';\n\nexport {\n extractTrackerUrls,\n matchBindings,\n assertNoNestedExpansion,\n assertRfc3986Safe,\n assertSchemePreserved,\n assertUnreservedOnly,\n DEFAULT_MACRO_PROHIBITED_PATTERN,\n enforceSsrfPolicy,\n enforceSsrfPolicyResolved,\n DEFAULT_SSRF_POLICY,\n} from './observer';\n\nexport { encodeUnreserved, equalUnderHexCasePolicy, isUnreservedOnly, divergenceOffset } from './rfc3986';\n\nexport type {\n AssertionOptions,\n AssertionResult,\n BindingMatch,\n CatalogBinding,\n CatalogMacroVector,\n PolicyResult,\n SsrfPolicy,\n TrackerUrlRecord,\n} from './types';\n\nexport { translateUniversalMacros } from './translate';\nexport type { MacroMapping, TranslateResult } from './translate';\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/substitution/index.ts"],"sourcesContent":["/**\n * Catalog-item macro substitution primitives — paired observer and\n * encoder surfaces implementing the `substitution-observer-runner`\n * test-kit contract and the #2620 encoding rule.\n *\n * Two disjoint consumer classes:\n *\n * - Runners grading conformance import from `./observer` (or the\n * re-exports here) for `parse_html`, `fetch_and_parse`,\n * `match_bindings`, and the assertion helpers.\n *\n * - Sales/retail-media agents implementing the #2620 rule import\n * `SubstitutionEncoder` from `./encoder`. It produces the exact\n * bytes the observer expects — one shared RFC 3986 implementation\n * means one bug-fix path for both directions.\n *\n * ```ts\n * // Seller-side\n * import { SubstitutionEncoder } from '@adcp/sdk/substitution';\n * const safe = new SubstitutionEncoder().encode_for_url_context(raw);\n *\n * // Runner-side\n * import { SubstitutionObserver, CATALOG_MACRO_VECTORS } from '@adcp/sdk/substitution';\n * const observer = new SubstitutionObserver();\n * const matches = observer.match_bindings(observer.parse_html(html), template, bindings);\n * ```\n */\n\nexport { SubstitutionObserver, PreviewFetchError } from './observer/SubstitutionObserver';\nexport type { ObserverFetchOptions, ObserverDispatcher } from './observer/SubstitutionObserver';\n\nexport { SubstitutionEncoder, MacroInRawValueError } from './encoder/SubstitutionEncoder';\n\nexport { CATALOG_MACRO_VECTORS, getCatalogMacroVector } from './vectors';\nexport type { CatalogMacroVectorName } from './vectors';\n\nexport {\n extractTrackerUrls,\n matchBindings,\n assertNoNestedExpansion,\n assertRfc3986Safe,\n assertSchemePreserved,\n assertUnreservedOnly,\n DEFAULT_MACRO_PROHIBITED_PATTERN,\n enforceSsrfPolicy,\n enforceSsrfPolicyResolved,\n DEFAULT_SSRF_POLICY,\n} from './observer';\n\nexport { encodeUnreserved, equalUnderHexCasePolicy, isUnreservedOnly, divergenceOffset } from './rfc3986';\n\nexport type {\n AssertionOptions,\n AssertionResult,\n BindingMatch,\n CatalogBinding,\n CatalogMacroVector,\n PolicyResult,\n SsrfPolicy,\n TrackerUrlRecord,\n} from './types';\n\nexport { translateUniversalMacros } from './translate';\nexport type { MacroMapping, TranslateResult } from './translate';\n"],"mappings":"AA4BA,SAAS,sBAAsB,yBAAyB;AAGxD,SAAS,qBAAqB,4BAA4B;AAE1D,SAAS,uBAAuB,6BAA6B;AAG7D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,kBAAkB,yBAAyB,kBAAkB,wBAAwB;AAa9F,SAAS,gCAAgC;","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 dns from "node:dns/promises";
|
|
8
2
|
import { isIP, isIPv6 } from "node:net";
|
|
9
3
|
import { extractTrackerUrls } from "./html-parser.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/substitution/observer/SubstitutionObserver.ts"],"sourcesContent":["/**\n * Runner-side primitives for observing catalog-item macro substitution\n * in creative previews. Consumed by conformance runners grading the\n * `substitution-observer-runner` test-kit contract.\n *\n * Usage — inline HTML observation:\n * ```ts\n * const observer = new SubstitutionObserver();\n * const records = observer.parse_html(preview_html);\n * const matches = observer.match_bindings(records, template, bindings);\n * for (const m of matches) {\n * const r = observer.assert_rfc3986_safe(m);\n * if (!r.ok) report(r);\n * }\n * ```\n *\n * Usage — preview URL fetch (applies SSRF policy):\n * ```ts\n * const records = await observer.fetch_and_parse(new URL(preview_url));\n * ```\n */\n\nimport dns from 'node:dns/promises';\nimport { isIP, isIPv6 } from 'node:net';\n\nimport type {\n AssertionOptions,\n AssertionResult,\n BindingMatch,\n CatalogBinding,\n PolicyResult,\n SsrfPolicy,\n TrackerUrlRecord,\n} from '../types';\nimport { extractTrackerUrls } from './html-parser';\nimport { matchBindings } from './alignment';\nimport {\n DEFAULT_MACRO_PROHIBITED_PATTERN,\n assertNoNestedExpansion,\n assertRfc3986Safe,\n assertSchemePreserved,\n assertUnreservedOnly,\n} from './assertions';\nimport { DEFAULT_SSRF_POLICY, enforceSsrfPolicy, enforceSsrfPolicyResolved } from './ssrf';\n\n/**\n * Opaque dispatcher type — keeps `undici`'s type out of the public\n * surface. Callers constructing a dispatcher hand us the instance; we\n * pass it through to `undici.request` untouched.\n */\nexport type ObserverDispatcher = object;\n\n/**\n * Fetch knobs mirror the contract's `url_fetch.runner_config.fetch` block.\n * Callers SHOULD keep the defaults for AdCP Verified grading.\n */\nexport interface ObserverFetchOptions {\n /** Max body bytes to read before aborting. Default 262144 (256 KiB). */\n max_body_bytes?: number;\n /** Connect-timeout in seconds. Default 3. */\n max_connect_seconds?: number;\n /** Overall request timeout in seconds. Default 10. */\n timeout_seconds?: number;\n /** Acceptable response content-types. Default: `text/html`, `application/xhtml+xml`. */\n required_content_types?: readonly string[];\n /** SSRF policy to enforce. Default: {@link DEFAULT_SSRF_POLICY}. */\n ssrf_policy?: SsrfPolicy;\n /**\n * Custom undici dispatcher. Advanced callers inject here to plug in\n * a mock or a pre-pinned Agent. When omitted, the observer\n * constructs an `Agent` with `connect.lookup` pinned to the first\n * policy-allowed DNS address — this is the defense the contract's\n * `dns_revalidation: required` clause mandates.\n */\n dispatcher?: ObserverDispatcher;\n}\n\nexport class PreviewFetchError extends Error {\n readonly sub_reason:\n | 'http_status'\n | 'content_type'\n | 'size_exceeded'\n | 'redirect_returned'\n | 'ssrf_blocked'\n | 'fetch_timeout';\n readonly detail: string;\n constructor(sub_reason: PreviewFetchError['sub_reason'], detail: string) {\n super(`preview_url_unusable:${sub_reason} — ${detail}`);\n this.name = 'PreviewFetchError';\n this.sub_reason = sub_reason;\n this.detail = detail;\n }\n}\n\nexport class SubstitutionObserver {\n /**\n * Parse inline preview HTML and return tracker URLs from the\n * contract's normative tag/attribute set. URLs that fail WHATWG URL\n * parsing are skipped silently — a downstream binding that expected\n * them surfaces as `substitution_binding_missing`.\n */\n parse_html(html: string): TrackerUrlRecord[] {\n return extractTrackerUrls(html);\n }\n\n /**\n * Fetch `preview_url` under the SSRF policy, validate response\n * shape, and parse the body. Redirects are treated as failure\n * (policy: `follow_redirects: false`). The body is capped at\n * `max_body_bytes` bytes — exceeding the cap yields a\n * `size_exceeded` failure rather than a truncated parse.\n *\n * Throws {@link PreviewFetchError} on any fetch-layer failure so the\n * runner can map the sub-reason to the contract's\n * `preview_url_unusable_sub_reasons` vocabulary.\n */\n async fetch_and_parse(url: URL, options: ObserverFetchOptions = {}): Promise<TrackerUrlRecord[]> {\n const policy = options.ssrf_policy ?? DEFAULT_SSRF_POLICY;\n const maxBodyBytes = options.max_body_bytes ?? 262_144;\n const connectTimeoutMs = (options.max_connect_seconds ?? 3) * 1000;\n const timeoutMs = (options.timeout_seconds ?? 10) * 1000;\n const allowedContentTypes = options.required_content_types ?? ['text/html', 'application/xhtml+xml'];\n\n const presync = enforceSsrfPolicy(url, policy);\n if (!presync.allowed) {\n throw new PreviewFetchError('ssrf_blocked', presync.rule ?? 'unknown_rule');\n }\n\n // Every non-IP host path requires DNS revalidation — never pass a\n // hostname to the HTTP client without re-checking resolved addresses.\n const bareHost = unwrapHost(url.hostname);\n const isBareIp = isIP(bareHost) !== 0;\n let pinnedAddress: string | null = isBareIp ? bareHost : null;\n\n if (!isBareIp) {\n let addresses: string[] = [];\n try {\n const lookup = await dns.lookup(url.hostname, { all: true, verbatim: true });\n addresses = lookup.map(entry => entry.address);\n } catch (e) {\n throw new PreviewFetchError('fetch_timeout', `DNS lookup failed: ${(e as Error).message}`);\n }\n const resolvedCheck = enforceSsrfPolicyResolved(url, addresses, policy);\n if (!resolvedCheck.allowed) {\n throw new PreviewFetchError('ssrf_blocked', resolvedCheck.rule ?? 'unknown_rule');\n }\n pinnedAddress = addresses[0] ?? null;\n }\n\n const { request, Agent } = await loadUndici();\n\n // Pin to the already-policy-checked address. Providing `connect.lookup`\n // makes undici resolve through our callback instead of invoking the OS\n // resolver a second time — closes the DNS-rebinding window between\n // our `dns.lookup` above and the TCP connect below. If the caller\n // injected a dispatcher, trust their pinning.\n const ownsDispatcher = !options.dispatcher;\n const dispatcher: ObserverDispatcher =\n options.dispatcher ??\n new Agent({\n connect: {\n timeout: connectTimeoutMs,\n ...(pinnedAddress\n ? {\n lookup: (\n _host: string,\n _opts: unknown,\n cb: (err: Error | null, address: string, family: number) => void\n ) => cb(null, pinnedAddress, isIPv6(pinnedAddress) ? 6 : 4),\n }\n : {}),\n },\n bodyTimeout: timeoutMs,\n headersTimeout: timeoutMs,\n });\n\n const abortCtrl = new AbortController();\n const timerHandle = setTimeout(() => {\n abortCtrl.abort(new Error('preview fetch timeout'));\n }, timeoutMs);\n // Allow Node to exit even if the timer is still scheduled — a\n // stranded observer shouldn't keep the process alive.\n timerHandle.unref?.();\n\n try {\n const res = await request(url.href, {\n method: 'GET',\n dispatcher: dispatcher as import('undici').Dispatcher,\n maxRedirections: 0,\n signal: abortCtrl.signal,\n headers: { accept: allowedContentTypes.join(', ') },\n });\n\n if (res.statusCode >= 300 && res.statusCode < 400) {\n throw new PreviewFetchError('redirect_returned', `status=${res.statusCode}`);\n }\n if (res.statusCode < 200 || res.statusCode >= 300) {\n throw new PreviewFetchError('http_status', `status=${res.statusCode}`);\n }\n const contentType = (String(res.headers['content-type'] ?? '').split(';')[0] ?? '').trim().toLowerCase();\n if (!allowedContentTypes.includes(contentType)) {\n throw new PreviewFetchError('content_type', `content-type=${contentType || 'missing'}`);\n }\n\n const body = await readCapped(res.body, maxBodyBytes);\n return extractTrackerUrls(body);\n } catch (e) {\n if (e instanceof PreviewFetchError) throw e;\n if ((e as Error).name === 'AbortError') {\n throw new PreviewFetchError('fetch_timeout', `exceeded ${timeoutMs}ms`);\n }\n throw new PreviewFetchError('fetch_timeout', (e as Error).message);\n } finally {\n clearTimeout(timerHandle);\n abortCtrl.abort();\n if (ownsDispatcher) {\n const closable = dispatcher as { close?: () => Promise<void> };\n try {\n await closable.close?.();\n } catch {\n // Ignore — best-effort cleanup.\n }\n }\n }\n }\n\n /** See {@link matchBindings}. */\n match_bindings(\n records: readonly TrackerUrlRecord[],\n template: URL | string,\n bindings: readonly CatalogBinding[]\n ): BindingMatch[] {\n return matchBindings(records, template, bindings);\n }\n\n /** See {@link assertRfc3986Safe}. */\n assert_rfc3986_safe(match: BindingMatch, options: AssertionOptions = {}): AssertionResult {\n return assertRfc3986Safe(match, options);\n }\n\n /** See {@link assertUnreservedOnly}. */\n assert_unreserved_only(match: BindingMatch, options: AssertionOptions = {}): AssertionResult {\n return assertUnreservedOnly(match, options);\n }\n\n /** See {@link assertNoNestedExpansion}. */\n assert_no_nested_expansion(\n match: BindingMatch,\n prohibited_pattern: RegExp = DEFAULT_MACRO_PROHIBITED_PATTERN,\n options: AssertionOptions = {}\n ): AssertionResult {\n return assertNoNestedExpansion(match, prohibited_pattern, options);\n }\n\n /** See {@link assertSchemePreserved}. */\n assert_scheme_preserved(match: BindingMatch, template_scheme: string): AssertionResult {\n return assertSchemePreserved(match, template_scheme);\n }\n\n /** See {@link enforceSsrfPolicy}. */\n enforce_ssrf_policy(url: URL, policy: SsrfPolicy = DEFAULT_SSRF_POLICY): PolicyResult {\n return enforceSsrfPolicy(url, policy);\n }\n}\n\nfunction unwrapHost(host: string): string {\n return host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host;\n}\n\nasync function readCapped(body: NodeJS.ReadableStream, capBytes: number): Promise<string> {\n const chunks: Buffer[] = [];\n let total = 0;\n for await (const chunk of body as AsyncIterable<Buffer | string>) {\n const buf = typeof chunk === 'string' ? Buffer.from(chunk, 'utf8') : chunk;\n total += buf.length;\n if (total > capBytes) {\n throw new PreviewFetchError('size_exceeded', `>${capBytes} bytes`);\n }\n chunks.push(buf);\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n\nlet undiciModule: typeof import('undici') | undefined;\nasync function loadUndici(): Promise<typeof import('undici')> {\n if (!undiciModule) {\n undiciModule = await import('undici');\n }\n return undiciModule;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/substitution/observer/SubstitutionObserver.ts"],"sourcesContent":["/**\n * Runner-side primitives for observing catalog-item macro substitution\n * in creative previews. Consumed by conformance runners grading the\n * `substitution-observer-runner` test-kit contract.\n *\n * Usage — inline HTML observation:\n * ```ts\n * const observer = new SubstitutionObserver();\n * const records = observer.parse_html(preview_html);\n * const matches = observer.match_bindings(records, template, bindings);\n * for (const m of matches) {\n * const r = observer.assert_rfc3986_safe(m);\n * if (!r.ok) report(r);\n * }\n * ```\n *\n * Usage — preview URL fetch (applies SSRF policy):\n * ```ts\n * const records = await observer.fetch_and_parse(new URL(preview_url));\n * ```\n */\n\nimport dns from 'node:dns/promises';\nimport { isIP, isIPv6 } from 'node:net';\n\nimport type {\n AssertionOptions,\n AssertionResult,\n BindingMatch,\n CatalogBinding,\n PolicyResult,\n SsrfPolicy,\n TrackerUrlRecord,\n} from '../types';\nimport { extractTrackerUrls } from './html-parser';\nimport { matchBindings } from './alignment';\nimport {\n DEFAULT_MACRO_PROHIBITED_PATTERN,\n assertNoNestedExpansion,\n assertRfc3986Safe,\n assertSchemePreserved,\n assertUnreservedOnly,\n} from './assertions';\nimport { DEFAULT_SSRF_POLICY, enforceSsrfPolicy, enforceSsrfPolicyResolved } from './ssrf';\n\n/**\n * Opaque dispatcher type — keeps `undici`'s type out of the public\n * surface. Callers constructing a dispatcher hand us the instance; we\n * pass it through to `undici.request` untouched.\n */\nexport type ObserverDispatcher = object;\n\n/**\n * Fetch knobs mirror the contract's `url_fetch.runner_config.fetch` block.\n * Callers SHOULD keep the defaults for AdCP Verified grading.\n */\nexport interface ObserverFetchOptions {\n /** Max body bytes to read before aborting. Default 262144 (256 KiB). */\n max_body_bytes?: number;\n /** Connect-timeout in seconds. Default 3. */\n max_connect_seconds?: number;\n /** Overall request timeout in seconds. Default 10. */\n timeout_seconds?: number;\n /** Acceptable response content-types. Default: `text/html`, `application/xhtml+xml`. */\n required_content_types?: readonly string[];\n /** SSRF policy to enforce. Default: {@link DEFAULT_SSRF_POLICY}. */\n ssrf_policy?: SsrfPolicy;\n /**\n * Custom undici dispatcher. Advanced callers inject here to plug in\n * a mock or a pre-pinned Agent. When omitted, the observer\n * constructs an `Agent` with `connect.lookup` pinned to the first\n * policy-allowed DNS address — this is the defense the contract's\n * `dns_revalidation: required` clause mandates.\n */\n dispatcher?: ObserverDispatcher;\n}\n\nexport class PreviewFetchError extends Error {\n readonly sub_reason:\n | 'http_status'\n | 'content_type'\n | 'size_exceeded'\n | 'redirect_returned'\n | 'ssrf_blocked'\n | 'fetch_timeout';\n readonly detail: string;\n constructor(sub_reason: PreviewFetchError['sub_reason'], detail: string) {\n super(`preview_url_unusable:${sub_reason} — ${detail}`);\n this.name = 'PreviewFetchError';\n this.sub_reason = sub_reason;\n this.detail = detail;\n }\n}\n\nexport class SubstitutionObserver {\n /**\n * Parse inline preview HTML and return tracker URLs from the\n * contract's normative tag/attribute set. URLs that fail WHATWG URL\n * parsing are skipped silently — a downstream binding that expected\n * them surfaces as `substitution_binding_missing`.\n */\n parse_html(html: string): TrackerUrlRecord[] {\n return extractTrackerUrls(html);\n }\n\n /**\n * Fetch `preview_url` under the SSRF policy, validate response\n * shape, and parse the body. Redirects are treated as failure\n * (policy: `follow_redirects: false`). The body is capped at\n * `max_body_bytes` bytes — exceeding the cap yields a\n * `size_exceeded` failure rather than a truncated parse.\n *\n * Throws {@link PreviewFetchError} on any fetch-layer failure so the\n * runner can map the sub-reason to the contract's\n * `preview_url_unusable_sub_reasons` vocabulary.\n */\n async fetch_and_parse(url: URL, options: ObserverFetchOptions = {}): Promise<TrackerUrlRecord[]> {\n const policy = options.ssrf_policy ?? DEFAULT_SSRF_POLICY;\n const maxBodyBytes = options.max_body_bytes ?? 262_144;\n const connectTimeoutMs = (options.max_connect_seconds ?? 3) * 1000;\n const timeoutMs = (options.timeout_seconds ?? 10) * 1000;\n const allowedContentTypes = options.required_content_types ?? ['text/html', 'application/xhtml+xml'];\n\n const presync = enforceSsrfPolicy(url, policy);\n if (!presync.allowed) {\n throw new PreviewFetchError('ssrf_blocked', presync.rule ?? 'unknown_rule');\n }\n\n // Every non-IP host path requires DNS revalidation — never pass a\n // hostname to the HTTP client without re-checking resolved addresses.\n const bareHost = unwrapHost(url.hostname);\n const isBareIp = isIP(bareHost) !== 0;\n let pinnedAddress: string | null = isBareIp ? bareHost : null;\n\n if (!isBareIp) {\n let addresses: string[] = [];\n try {\n const lookup = await dns.lookup(url.hostname, { all: true, verbatim: true });\n addresses = lookup.map(entry => entry.address);\n } catch (e) {\n throw new PreviewFetchError('fetch_timeout', `DNS lookup failed: ${(e as Error).message}`);\n }\n const resolvedCheck = enforceSsrfPolicyResolved(url, addresses, policy);\n if (!resolvedCheck.allowed) {\n throw new PreviewFetchError('ssrf_blocked', resolvedCheck.rule ?? 'unknown_rule');\n }\n pinnedAddress = addresses[0] ?? null;\n }\n\n const { request, Agent } = await loadUndici();\n\n // Pin to the already-policy-checked address. Providing `connect.lookup`\n // makes undici resolve through our callback instead of invoking the OS\n // resolver a second time — closes the DNS-rebinding window between\n // our `dns.lookup` above and the TCP connect below. If the caller\n // injected a dispatcher, trust their pinning.\n const ownsDispatcher = !options.dispatcher;\n const dispatcher: ObserverDispatcher =\n options.dispatcher ??\n new Agent({\n connect: {\n timeout: connectTimeoutMs,\n ...(pinnedAddress\n ? {\n lookup: (\n _host: string,\n _opts: unknown,\n cb: (err: Error | null, address: string, family: number) => void\n ) => cb(null, pinnedAddress, isIPv6(pinnedAddress) ? 6 : 4),\n }\n : {}),\n },\n bodyTimeout: timeoutMs,\n headersTimeout: timeoutMs,\n });\n\n const abortCtrl = new AbortController();\n const timerHandle = setTimeout(() => {\n abortCtrl.abort(new Error('preview fetch timeout'));\n }, timeoutMs);\n // Allow Node to exit even if the timer is still scheduled — a\n // stranded observer shouldn't keep the process alive.\n timerHandle.unref?.();\n\n try {\n const res = await request(url.href, {\n method: 'GET',\n dispatcher: dispatcher as import('undici').Dispatcher,\n maxRedirections: 0,\n signal: abortCtrl.signal,\n headers: { accept: allowedContentTypes.join(', ') },\n });\n\n if (res.statusCode >= 300 && res.statusCode < 400) {\n throw new PreviewFetchError('redirect_returned', `status=${res.statusCode}`);\n }\n if (res.statusCode < 200 || res.statusCode >= 300) {\n throw new PreviewFetchError('http_status', `status=${res.statusCode}`);\n }\n const contentType = (String(res.headers['content-type'] ?? '').split(';')[0] ?? '').trim().toLowerCase();\n if (!allowedContentTypes.includes(contentType)) {\n throw new PreviewFetchError('content_type', `content-type=${contentType || 'missing'}`);\n }\n\n const body = await readCapped(res.body, maxBodyBytes);\n return extractTrackerUrls(body);\n } catch (e) {\n if (e instanceof PreviewFetchError) throw e;\n if ((e as Error).name === 'AbortError') {\n throw new PreviewFetchError('fetch_timeout', `exceeded ${timeoutMs}ms`);\n }\n throw new PreviewFetchError('fetch_timeout', (e as Error).message);\n } finally {\n clearTimeout(timerHandle);\n abortCtrl.abort();\n if (ownsDispatcher) {\n const closable = dispatcher as { close?: () => Promise<void> };\n try {\n await closable.close?.();\n } catch {\n // Ignore — best-effort cleanup.\n }\n }\n }\n }\n\n /** See {@link matchBindings}. */\n match_bindings(\n records: readonly TrackerUrlRecord[],\n template: URL | string,\n bindings: readonly CatalogBinding[]\n ): BindingMatch[] {\n return matchBindings(records, template, bindings);\n }\n\n /** See {@link assertRfc3986Safe}. */\n assert_rfc3986_safe(match: BindingMatch, options: AssertionOptions = {}): AssertionResult {\n return assertRfc3986Safe(match, options);\n }\n\n /** See {@link assertUnreservedOnly}. */\n assert_unreserved_only(match: BindingMatch, options: AssertionOptions = {}): AssertionResult {\n return assertUnreservedOnly(match, options);\n }\n\n /** See {@link assertNoNestedExpansion}. */\n assert_no_nested_expansion(\n match: BindingMatch,\n prohibited_pattern: RegExp = DEFAULT_MACRO_PROHIBITED_PATTERN,\n options: AssertionOptions = {}\n ): AssertionResult {\n return assertNoNestedExpansion(match, prohibited_pattern, options);\n }\n\n /** See {@link assertSchemePreserved}. */\n assert_scheme_preserved(match: BindingMatch, template_scheme: string): AssertionResult {\n return assertSchemePreserved(match, template_scheme);\n }\n\n /** See {@link enforceSsrfPolicy}. */\n enforce_ssrf_policy(url: URL, policy: SsrfPolicy = DEFAULT_SSRF_POLICY): PolicyResult {\n return enforceSsrfPolicy(url, policy);\n }\n}\n\nfunction unwrapHost(host: string): string {\n return host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host;\n}\n\nasync function readCapped(body: NodeJS.ReadableStream, capBytes: number): Promise<string> {\n const chunks: Buffer[] = [];\n let total = 0;\n for await (const chunk of body as AsyncIterable<Buffer | string>) {\n const buf = typeof chunk === 'string' ? Buffer.from(chunk, 'utf8') : chunk;\n total += buf.length;\n if (total > capBytes) {\n throw new PreviewFetchError('size_exceeded', `>${capBytes} bytes`);\n }\n chunks.push(buf);\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n\nlet undiciModule: typeof import('undici') | undefined;\nasync function loadUndici(): Promise<typeof import('undici')> {\n if (!undiciModule) {\n undiciModule = await import('undici');\n }\n return undiciModule;\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 { encodeUnreserved } from "../rfc3986.mjs";
|
|
8
2
|
import { getCatalogMacroVector } from "../vectors.mjs";
|
|
9
3
|
const MACRO_SEGMENT_RE = /\{[A-Z][A-Z0-9_]*\}/;
|