@adcp/sdk 12.1.0 → 13.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/bin/adcp-fuzz.js +3 -1
- package/dist/lib/adapters/content-standards-adapter.d.mts +9 -9
- package/dist/lib/adapters/content-standards-adapter.d.ts +9 -9
- package/dist/lib/adapters/content-standards-adapter.d.ts.map +1 -1
- package/dist/lib/adapters/content-standards-adapter.js +22 -22
- package/dist/lib/adapters/content-standards-adapter.js.map +1 -1
- package/dist/lib/adapters/content-standards-adapter.mjs +18 -18
- package/dist/lib/adapters/content-standards-adapter.mjs.map +1 -1
- package/dist/lib/adapters/index.d.mts +2 -2
- package/dist/lib/adapters/index.d.ts +2 -2
- package/dist/lib/adapters/index.d.ts.map +1 -1
- package/dist/lib/adapters/index.js +10 -10
- package/dist/lib/adapters/index.js.map +1 -1
- package/dist/lib/adapters/index.mjs +9 -9
- package/dist/lib/adapters/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.d.mts +3 -0
- package/dist/lib/auth/oauth/authorization-required.d.ts +3 -0
- package/dist/lib/auth/oauth/authorization-required.d.ts.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.js +12 -9
- package/dist/lib/auth/oauth/authorization-required.js.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.mjs +12 -9
- package/dist/lib/auth/oauth/authorization-required.mjs.map +1 -1
- package/dist/lib/auth/oauth/provider-cache.d.mts +7 -0
- package/dist/lib/auth/oauth/provider-cache.d.ts +8 -0
- package/dist/lib/auth/oauth/provider-cache.d.ts.map +1 -0
- package/dist/lib/auth/oauth/provider-cache.js +44 -0
- package/dist/lib/auth/oauth/provider-cache.js.map +1 -0
- package/dist/lib/auth/oauth/provider-cache.mjs +19 -0
- package/dist/lib/auth/oauth/provider-cache.mjs.map +1 -0
- package/dist/lib/conformance/invariants/uniformError.js +1 -1
- package/dist/lib/conformance/invariants/uniformError.js.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
- package/dist/lib/conformance/runners.js +1 -1
- package/dist/lib/conformance/runners.js.map +1 -1
- package/dist/lib/conformance/runners.mjs +1 -1
- package/dist/lib/conformance/runners.mjs.map +1 -1
- package/dist/lib/conformance/seeder.js +5 -5
- package/dist/lib/conformance/seeder.js.map +1 -1
- package/dist/lib/conformance/seeder.mjs +5 -5
- package/dist/lib/conformance/seeder.mjs.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.d.mts +26 -29
- package/dist/lib/core/ADCPMultiAgentClient.d.ts +26 -29
- package/dist/lib/core/ADCPMultiAgentClient.d.ts.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.js +27 -19
- package/dist/lib/core/ADCPMultiAgentClient.js.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.mjs +27 -19
- package/dist/lib/core/ADCPMultiAgentClient.mjs.map +1 -1
- package/dist/lib/core/AgentClient.d.mts +148 -138
- package/dist/lib/core/AgentClient.d.ts +148 -138
- package/dist/lib/core/AgentClient.d.ts.map +1 -1
- package/dist/lib/core/AgentClient.js +174 -85
- package/dist/lib/core/AgentClient.js.map +1 -1
- package/dist/lib/core/AgentClient.mjs +174 -85
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/AsyncHandler.d.mts +18 -16
- package/dist/lib/core/AsyncHandler.d.ts +18 -16
- package/dist/lib/core/AsyncHandler.d.ts.map +1 -1
- package/dist/lib/core/AsyncHandler.js +22 -1
- package/dist/lib/core/AsyncHandler.js.map +1 -1
- package/dist/lib/core/AsyncHandler.mjs +22 -1
- package/dist/lib/core/AsyncHandler.mjs.map +1 -1
- package/dist/lib/core/ConversationTypes.d.mts +6 -2
- package/dist/lib/core/ConversationTypes.d.ts +6 -2
- package/dist/lib/core/ConversationTypes.d.ts.map +1 -1
- package/dist/lib/core/ConversationTypes.js.map +1 -1
- package/dist/lib/core/CreativeAgentClient.d.mts +41 -17
- package/dist/lib/core/CreativeAgentClient.d.ts +41 -17
- package/dist/lib/core/CreativeAgentClient.d.ts.map +1 -1
- package/dist/lib/core/CreativeAgentClient.js +39 -12
- package/dist/lib/core/CreativeAgentClient.js.map +1 -1
- package/dist/lib/core/CreativeAgentClient.mjs +39 -12
- package/dist/lib/core/CreativeAgentClient.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.d.mts +129 -52
- package/dist/lib/core/SingleAgentClient.d.ts +129 -52
- package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
- package/dist/lib/core/SingleAgentClient.js +1748 -218
- package/dist/lib/core/SingleAgentClient.js.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +1758 -218
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.d.mts +9 -0
- package/dist/lib/core/TaskExecutor.d.ts +9 -0
- package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
- package/dist/lib/core/TaskExecutor.js +137 -44
- package/dist/lib/core/TaskExecutor.js.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +137 -44
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/index.d.mts +111 -17
- package/dist/lib/index.d.ts +111 -17
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +94 -94
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +93 -94
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/net/ssrf-fetch.d.mts +13 -1
- package/dist/lib/net/ssrf-fetch.d.ts +13 -1
- package/dist/lib/net/ssrf-fetch.d.ts.map +1 -1
- package/dist/lib/net/ssrf-fetch.js +14 -7
- package/dist/lib/net/ssrf-fetch.js.map +1 -1
- package/dist/lib/net/ssrf-fetch.mjs +14 -7
- package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
- package/dist/lib/protocols/a2a.d.mts +2 -2
- package/dist/lib/protocols/a2a.d.ts +2 -2
- package/dist/lib/protocols/a2a.d.ts.map +1 -1
- package/dist/lib/protocols/a2a.js +14 -9
- package/dist/lib/protocols/a2a.js.map +1 -1
- package/dist/lib/protocols/a2a.mjs +14 -9
- package/dist/lib/protocols/a2a.mjs.map +1 -1
- package/dist/lib/protocols/index.d.mts +20 -3
- package/dist/lib/protocols/index.d.ts +20 -3
- package/dist/lib/protocols/index.d.ts.map +1 -1
- package/dist/lib/protocols/index.js +44 -31
- package/dist/lib/protocols/index.js.map +1 -1
- package/dist/lib/protocols/index.mjs +44 -32
- package/dist/lib/protocols/index.mjs.map +1 -1
- package/dist/lib/protocols/mcp-modern.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-modern.js +87 -12
- package/dist/lib/protocols/mcp-modern.js.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +89 -12
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/protocols/mcp-tasks.d.mts +3 -0
- package/dist/lib/protocols/mcp-tasks.d.ts +3 -0
- package/dist/lib/protocols/mcp-tasks.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-tasks.js +21 -11
- package/dist/lib/protocols/mcp-tasks.js.map +1 -1
- package/dist/lib/protocols/mcp-tasks.mjs +21 -11
- package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +6 -0
- package/dist/lib/server/create-adcp-server.d.ts +6 -0
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +295 -48
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/create-adcp-server.mjs +301 -50
- package/dist/lib/server/create-adcp-server.mjs.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.d.mts +27 -13
- package/dist/lib/server/decisioning/assembly-helpers.d.ts +27 -13
- package/dist/lib/server/decisioning/assembly-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.js +136 -14
- package/dist/lib/server/decisioning/assembly-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.mjs +134 -13
- package/dist/lib/server/decisioning/assembly-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/index.d.mts +13 -12
- package/dist/lib/server/decisioning/index.d.ts +13 -12
- package/dist/lib/server/decisioning/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/index.js +6 -0
- package/dist/lib/server/decisioning/index.js.map +1 -1
- package/dist/lib/server/decisioning/index.mjs +9 -3
- package/dist/lib/server/decisioning/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.d.mts +13 -3
- package/dist/lib/server/decisioning/list-helpers.d.ts +13 -3
- package/dist/lib/server/decisioning/list-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.js +13 -3
- package/dist/lib/server/decisioning/list-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.mjs +11 -2
- package/dist/lib/server/decisioning/list-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.d.mts +5 -3
- package/dist/lib/server/decisioning/manifest-helpers.d.ts +5 -3
- package/dist/lib/server/decisioning/manifest-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.d.mts +2 -2
- package/dist/lib/server/decisioning/proposal/dispatch.d.ts +2 -2
- package/dist/lib/server/decisioning/proposal/dispatch.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/dispatch.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.mts +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.ts +1 -1
- package/dist/lib/server/decisioning/proposal/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.d.mts +3 -2
- package/dist/lib/server/decisioning/proposal/lifecycle.d.ts +3 -2
- package/dist/lib/server/decisioning/proposal/lifecycle.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.d.mts +3 -3
- package/dist/lib/server/decisioning/proposal/mock-manager.d.ts +3 -3
- package/dist/lib/server/decisioning/proposal/mock-manager.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.d.mts +10 -6
- package/dist/lib/server/decisioning/proposal/types.d.ts +10 -6
- package/dist/lib/server/decisioning/proposal/types.d.ts.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.js.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.mts +21 -2
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +21 -2
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +754 -81
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +763 -81
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.mts +9 -9
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts +9 -9
- package/dist/lib/server/decisioning/specialisms/brand-rights.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/brand-rights.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/content-standards.d.mts +16 -16
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts +16 -16
- package/dist/lib/server/decisioning/specialisms/content-standards.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/content-standards.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.mts +15 -13
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts +15 -13
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative.d.mts +12 -11
- package/dist/lib/server/decisioning/specialisms/creative.d.ts +12 -11
- package/dist/lib/server/decisioning/specialisms/creative.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/creative.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sales.d.mts +25 -15
- package/dist/lib/server/decisioning/specialisms/sales.d.ts +25 -15
- package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -1
- package/dist/lib/server/index.d.mts +3 -3
- package/dist/lib/server/index.d.ts +3 -3
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +72 -72
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/index.mjs +36 -36
- package/dist/lib/server/index.mjs.map +1 -1
- package/dist/lib/server/legacy/v5/index.d.mts +1 -0
- package/dist/lib/server/legacy/v5/index.d.ts +1 -0
- package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
- package/dist/lib/server/legacy/v5/index.js +74 -1
- package/dist/lib/server/legacy/v5/index.js.map +1 -1
- package/dist/lib/server/legacy/v5/index.mjs +75 -1
- package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
- package/dist/lib/server/responses.d.mts +2 -2
- package/dist/lib/server/responses.d.ts +2 -2
- package/dist/lib/server/responses.js.map +1 -1
- package/dist/lib/server/responses.mjs.map +1 -1
- package/dist/lib/testing/client.d.ts.map +1 -1
- package/dist/lib/testing/client.js +13 -5
- package/dist/lib/testing/client.js.map +1 -1
- package/dist/lib/testing/client.mjs +13 -5
- package/dist/lib/testing/client.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.d.mts +1 -1
- package/dist/lib/testing/compliance/comply.d.ts +1 -1
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +16 -6
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +16 -6
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.js +6 -6
- package/dist/lib/testing/scenarios/brand-rights.js.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.mjs +6 -6
- package/dist/lib/testing/scenarios/brand-rights.mjs.map +1 -1
- package/dist/lib/testing/scenarios/creative.js +7 -7
- package/dist/lib/testing/scenarios/creative.js.map +1 -1
- package/dist/lib/testing/scenarios/creative.mjs +7 -7
- package/dist/lib/testing/scenarios/creative.mjs.map +1 -1
- package/dist/lib/testing/scenarios/discovery.js +2 -2
- package/dist/lib/testing/scenarios/discovery.js.map +1 -1
- package/dist/lib/testing/scenarios/discovery.mjs +2 -2
- package/dist/lib/testing/scenarios/discovery.mjs.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.js +11 -11
- package/dist/lib/testing/scenarios/edge-cases.js.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.mjs +11 -11
- package/dist/lib/testing/scenarios/edge-cases.mjs.map +1 -1
- package/dist/lib/testing/scenarios/governance.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/governance.js +16 -22
- package/dist/lib/testing/scenarios/governance.js.map +1 -1
- package/dist/lib/testing/scenarios/governance.mjs +16 -22
- package/dist/lib/testing/scenarios/governance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.js +5 -5
- package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.mjs +5 -5
- package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.js +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.js.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.js +4 -3
- package/dist/lib/testing/scenarios/trusted-match.js.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.mjs +4 -3
- package/dist/lib/testing/scenarios/trusted-match.mjs.map +1 -1
- package/dist/lib/testing/storyboard/probes.d.mts +6 -0
- package/dist/lib/testing/storyboard/probes.d.ts +6 -0
- package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/probes.js +16 -5
- package/dist/lib/testing/storyboard/probes.js.map +1 -1
- package/dist/lib/testing/storyboard/probes.mjs +16 -5
- package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +6 -2
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +6 -2
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.d.mts +3 -4
- package/dist/lib/testing/storyboard/shape-drift-hints.d.ts +3 -4
- package/dist/lib/testing/storyboard/shape-drift-hints.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.js +27 -27
- package/dist/lib/testing/storyboard/shape-drift-hints.js.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs +27 -27
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/task-map.js +43 -5
- package/dist/lib/testing/storyboard/task-map.js.map +1 -1
- package/dist/lib/testing/storyboard/task-map.mjs +43 -5
- package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
- package/dist/lib/testing/test-helpers.d.mts +2 -2
- package/dist/lib/testing/test-helpers.d.ts +2 -2
- package/dist/lib/testing/test-helpers.js.map +1 -1
- package/dist/lib/testing/test-helpers.mjs.map +1 -1
- package/dist/lib/testing/types.d.mts +7 -1
- package/dist/lib/testing/types.d.ts +7 -1
- package/dist/lib/testing/types.d.ts.map +1 -1
- package/dist/lib/testing/types.js.map +1 -1
- package/dist/lib/types/adcp.d.mts +14 -14
- package/dist/lib/types/adcp.d.ts +14 -14
- package/dist/lib/types/adcp.d.ts.map +1 -1
- package/dist/lib/types/adcp.js.map +1 -1
- package/dist/lib/types/adcp.mjs.map +1 -1
- package/dist/lib/types/index.d.mts +2 -2
- package/dist/lib/types/index.d.ts +2 -2
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/index.mjs.map +1 -1
- package/dist/lib/utils/capabilities.d.mts +6 -0
- package/dist/lib/utils/capabilities.d.ts +6 -0
- package/dist/lib/utils/capabilities.d.ts.map +1 -1
- package/dist/lib/utils/capabilities.js +4 -0
- package/dist/lib/utils/capabilities.js.map +1 -1
- package/dist/lib/utils/capabilities.mjs +4 -0
- package/dist/lib/utils/capabilities.mjs.map +1 -1
- package/dist/lib/utils/creative-delivery.d.mts +52 -10
- package/dist/lib/utils/creative-delivery.d.ts +52 -10
- package/dist/lib/utils/creative-delivery.d.ts.map +1 -1
- package/dist/lib/utils/creative-delivery.js +104 -9
- package/dist/lib/utils/creative-delivery.js.map +1 -1
- package/dist/lib/utils/creative-delivery.mjs +104 -8
- package/dist/lib/utils/creative-delivery.mjs.map +1 -1
- package/dist/lib/utils/format-assets.d.mts +41 -38
- package/dist/lib/utils/format-assets.d.ts +41 -38
- package/dist/lib/utils/format-assets.d.ts.map +1 -1
- package/dist/lib/utils/format-assets.js +3 -1
- package/dist/lib/utils/format-assets.js.map +1 -1
- package/dist/lib/utils/format-assets.mjs +3 -1
- package/dist/lib/utils/format-assets.mjs.map +1 -1
- package/dist/lib/utils/index.d.mts +8 -8
- package/dist/lib/utils/index.d.ts +8 -8
- package/dist/lib/utils/index.d.ts.map +1 -1
- package/dist/lib/utils/index.js +11 -9
- package/dist/lib/utils/index.js.map +1 -1
- package/dist/lib/utils/index.mjs +9 -8
- package/dist/lib/utils/index.mjs.map +1 -1
- package/dist/lib/utils/preview-utils.d.mts +11 -8
- package/dist/lib/utils/preview-utils.d.ts +11 -8
- package/dist/lib/utils/preview-utils.d.ts.map +1 -1
- package/dist/lib/utils/preview-utils.js +4 -4
- package/dist/lib/utils/preview-utils.js.map +1 -1
- package/dist/lib/utils/preview-utils.mjs +3 -3
- package/dist/lib/utils/preview-utils.mjs.map +1 -1
- package/dist/lib/utils/tool-request-schemas.d.mts +2 -2
- package/dist/lib/utils/tool-request-schemas.d.ts +2 -2
- package/dist/lib/v2/projection/aao-reference-formats.json +492 -0
- package/dist/lib/v2/projection/augment-response.d.mts +31 -11
- package/dist/lib/v2/projection/augment-response.d.ts +31 -11
- package/dist/lib/v2/projection/augment-response.d.ts.map +1 -1
- package/dist/lib/v2/projection/augment-response.js +233 -27
- package/dist/lib/v2/projection/augment-response.js.map +1 -1
- package/dist/lib/v2/projection/augment-response.mjs +232 -27
- package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
- package/dist/lib/v2/projection/builders.d.mts +7 -6
- package/dist/lib/v2/projection/builders.d.ts +7 -6
- package/dist/lib/v2/projection/builders.d.ts.map +1 -1
- package/dist/lib/v2/projection/builders.js +11 -8
- package/dist/lib/v2/projection/builders.js.map +1 -1
- package/dist/lib/v2/projection/builders.mjs +9 -6
- package/dist/lib/v2/projection/builders.mjs.map +1 -1
- package/dist/lib/v2/projection/catalog-snapshot.d.mts +47 -0
- package/dist/lib/v2/projection/catalog-snapshot.d.ts +48 -0
- package/dist/lib/v2/projection/catalog-snapshot.d.ts.map +1 -0
- package/dist/lib/v2/projection/catalog-snapshot.js +291 -0
- package/dist/lib/v2/projection/catalog-snapshot.js.map +1 -0
- package/dist/lib/v2/projection/catalog-snapshot.mjs +265 -0
- package/dist/lib/v2/projection/catalog-snapshot.mjs.map +1 -0
- package/dist/lib/v2/projection/catalog.d.mts +23 -2
- package/dist/lib/v2/projection/catalog.d.ts +23 -2
- package/dist/lib/v2/projection/catalog.d.ts.map +1 -1
- package/dist/lib/v2/projection/catalog.js +34 -10
- package/dist/lib/v2/projection/catalog.js.map +1 -1
- package/dist/lib/v2/projection/catalog.mjs +33 -10
- package/dist/lib/v2/projection/catalog.mjs.map +1 -1
- package/dist/lib/v2/projection/creative-delivery.d.mts +157 -0
- package/dist/lib/v2/projection/creative-delivery.d.ts +158 -0
- package/dist/lib/v2/projection/creative-delivery.d.ts.map +1 -0
- package/dist/lib/v2/projection/creative-delivery.js +967 -0
- package/dist/lib/v2/projection/creative-delivery.js.map +1 -0
- package/dist/lib/v2/projection/creative-delivery.mjs +941 -0
- package/dist/lib/v2/projection/creative-delivery.mjs.map +1 -0
- package/dist/lib/v2/projection/index.d.mts +8 -6
- package/dist/lib/v2/projection/index.d.ts +8 -6
- package/dist/lib/v2/projection/index.d.ts.map +1 -1
- package/dist/lib/v2/projection/index.js +26 -4
- package/dist/lib/v2/projection/index.js.map +1 -1
- package/dist/lib/v2/projection/index.mjs +28 -4
- package/dist/lib/v2/projection/index.mjs.map +1 -1
- package/dist/lib/v2/projection/legacy-metadata.d.mts +21 -0
- package/dist/lib/v2/projection/legacy-metadata.d.ts +22 -0
- package/dist/lib/v2/projection/legacy-metadata.d.ts.map +1 -0
- package/dist/lib/v2/projection/legacy-metadata.js +73 -0
- package/dist/lib/v2/projection/legacy-metadata.js.map +1 -0
- package/dist/lib/v2/projection/legacy-metadata.mjs +45 -0
- package/dist/lib/v2/projection/legacy-metadata.mjs.map +1 -0
- package/dist/lib/v2/projection/types.d.mts +29 -23
- package/dist/lib/v2/projection/types.d.ts +29 -23
- package/dist/lib/v2/projection/types.d.ts.map +1 -1
- package/dist/lib/v2/projection/types.js.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.d.mts +29 -6
- package/dist/lib/v2/projection/v1-to-v2.d.ts +29 -6
- package/dist/lib/v2/projection/v1-to-v2.d.ts.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.js +218 -9
- package/dist/lib/v2/projection/v1-to-v2.js.map +1 -1
- package/dist/lib/v2/projection/v1-to-v2.mjs +219 -10
- package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.d.mts +31 -2
- package/dist/lib/v2/projection/v2-to-v1.d.ts +31 -2
- package/dist/lib/v2/projection/v2-to-v1.d.ts.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.js +138 -7
- package/dist/lib/v2/projection/v2-to-v1.js.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.mjs +135 -6
- package/dist/lib/v2/projection/v2-to-v1.mjs.map +1 -1
- package/dist/lib/v2/projection/write-side.d.mts +12 -13
- package/dist/lib/v2/projection/write-side.d.ts +12 -13
- package/dist/lib/v2/projection/write-side.d.ts.map +1 -1
- package/dist/lib/v2/projection/write-side.js +16 -12
- package/dist/lib/v2/projection/write-side.js.map +1 -1
- package/dist/lib/v2/projection/write-side.mjs +16 -12
- package/dist/lib/v2/projection/write-side.mjs.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.d.ts.map +1 -1
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/version.mjs.map +1 -1
- package/docs/guides/BUILD-AN-AGENT.md +7 -2
- package/docs/guides/PREVIEW-ASSET-DURABILITY.md +6 -2
- package/docs/llms.txt +1 -0
- package/examples/README.md +1 -1
- package/examples/batch-preview-test.ts +3 -3
- package/examples/conversation-client.ts +2 -2
- package/examples/debug-preview-response.ts +1 -1
- package/examples/decisioning-platform-broadcast-tv.ts +10 -4
- package/examples/decisioning-platform-mock-seller.ts +16 -9
- package/examples/decisioning-platform-programmatic.ts +18 -7
- package/examples/error-compliant-server.ts +26 -16
- package/examples/generative-creative-demo.ts +7 -19
- package/examples/hello_creative_adapter_ad_server.ts +37 -16
- package/examples/hello_creative_adapter_template.ts +12 -10
- package/examples/hello_seller_adapter_guaranteed.ts +52 -23
- package/examples/hello_seller_adapter_multi_tenant.ts +6 -3
- package/examples/hello_seller_adapter_non_guaranteed.ts +54 -26
- package/examples/hello_seller_adapter_proposal_mode.ts +40 -14
- package/examples/hello_seller_adapter_social.ts +2 -7
- package/examples/inspect-card-formats.ts +1 -1
- package/examples/simple-getting-started.ts +1 -1
- package/examples/test-helpers-demo.ts +1 -1
- package/package.json +2 -2
- package/skills/build-decisioning-creative-template/SKILL.md +11 -11
- package/skills/build-decisioning-platform/advanced/REFERENCE.md +8 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/testing/storyboard/probes.ts"],"sourcesContent":["/**\n * HTTP probes used by security-baseline storyboard tasks.\n *\n * Three synthetic tasks dispatch through here instead of the MCP client:\n *\n * - `protected_resource_metadata` — GET the agent's\n * `/.well-known/oauth-protected-resource<mountPath>` and verify RFC 9728.\n * - `oauth_auth_server_metadata` — GET `<issuer>/.well-known/oauth-authorization-server`\n * using the first issuer from the previous step's response. Hardened against\n * SSRF because the URL comes from agent-controlled data.\n * - `assert_contribution` — no network; evaluates accumulated flags set by\n * prior steps that carried `contributes_to`.\n */\nimport { randomBytes } from 'crypto';\nimport { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } from '@modelcontextprotocol/sdk/types.js';\nimport {\n ssrfSafeFetch,\n decodeBodyAsJsonOrText,\n SsrfRefusedError,\n isAlwaysBlocked as sharedIsAlwaysBlocked,\n isPrivateIp as sharedIsPrivateIp,\n} from '../../net';\nimport type { HttpProbeResult } from './types';\nimport type { TaskResult } from '../types';\n\n// Timeout + body-cap defaults come from `ssrfSafeFetch` (10 s, 64 KiB).\n// The probe wrappers deliberately don't override them so probe behavior\n// stays in sync with the shared primitive.\n\n/** Task names dispatched via HTTP probes (not via the MCP client). */\nexport const PROBE_TASKS = new Set([\n 'protected_resource_metadata',\n 'oauth_auth_server_metadata',\n 'assert_contribution',\n 'request_signing_probe',\n 'fetch_brand_jwks',\n 'assert_jwks_purpose',\n 'expect_rate_limit_not_replayed',\n]);\n\n// ---------------------------------------------------------------------------\n// Protected-resource metadata probe\n// ---------------------------------------------------------------------------\n\n/**\n * GET `<agentUrl origin>/.well-known/oauth-protected-resource<agentUrl path>`.\n * Same-origin as the agent, so SSRF risk is bounded.\n *\n * When `allowPrivateIp` is set (matches the runner's `--allow-http` flag),\n * loopback and RFC 1918 targets are allowed so dev loops against localhost\n * agents work end-to-end.\n */\nexport async function probeProtectedResourceMetadata(\n agentUrl: string,\n options: { allowPrivateIp?: boolean } = {}\n): Promise<HttpProbeResult> {\n const u = new URL(agentUrl);\n const metadataUrl = `${u.origin}/.well-known/oauth-protected-resource${u.pathname}`;\n return fetchProbe(metadataUrl, { allowPrivateIp: options.allowPrivateIp ?? false });\n}\n\n// ---------------------------------------------------------------------------\n// OAuth authorization-server metadata probe\n// ---------------------------------------------------------------------------\n\n/**\n * GET `<issuer>/.well-known/oauth-authorization-server` for the first issuer\n * named in the protected-resource metadata. Because the URL is agent-supplied,\n * this is the SSRF-hot path — {@link fetchProbe} rejects private networks,\n * non-https schemes, and unbounded responses.\n */\nexport async function probeOauthAuthServerMetadata(\n priorProbe: HttpProbeResult | undefined,\n options: { allowPrivateIp?: boolean } = {}\n): Promise<HttpProbeResult> {\n if (!priorProbe || priorProbe.error) {\n return {\n url: '',\n status: 0,\n headers: {},\n body: null,\n error: 'protected_resource_metadata step missing or errored — cannot resolve issuer',\n };\n }\n const body = priorProbe.body as { authorization_servers?: unknown } | null;\n const servers = Array.isArray(body?.authorization_servers) ? (body!.authorization_servers as string[]) : [];\n if (servers.length === 0 || typeof servers[0] !== 'string') {\n return {\n url: '',\n status: 0,\n headers: {},\n body: null,\n error: 'No authorization_servers[0] found in protected-resource metadata',\n };\n }\n const issuer = servers[0].replace(/\\/$/, '');\n const metadataUrl = `${issuer}/.well-known/oauth-authorization-server`;\n return fetchProbe(metadataUrl, { allowPrivateIp: options.allowPrivateIp ?? false });\n}\n\n// ---------------------------------------------------------------------------\n// Fetch with guardrails\n// ---------------------------------------------------------------------------\n\nexport interface FetchProbeOptions {\n /** Allow http:// and private-IP destinations. Default false. */\n allowPrivateIp?: boolean;\n /** Override timeout for specific call sites. */\n timeoutMs?: number;\n}\n\n/**\n * Perform a GET against an attacker-influenceable URL with defensive limits.\n *\n * Guardrails (RFC 9728 / RFC 8414 metadata endpoints typically live on public\n * HTTPS; anything else is suspicious):\n * - Scheme: `https:` only by default; `http:` allowed only when\n * `allowPrivateIp` is set. `file:`, `ftp:`, `data:`, etc. are always rejected.\n * - DNS: resolves all A/AAAA records once, rejects if any is private, then\n * pins the outbound connection to the validated IP. Defeats DNS rebinding\n * where an attacker's authoritative nameserver returns a public address\n * to our guard lookup and a private address to the connect-time lookup.\n * - Private-IP block applies RFC 1918, loopback, link-local, IPv6 ULA,\n * CGNAT (100.64/10), multicast, broadcast, and IPv4-mapped IPv6.\n * - IMDS (169.254.169.254 / fe80::) stays blocked **even under\n * `allowPrivateIp`** — no legitimate dev use for probing it.\n * - Redirects are NOT followed (`redirect: 'manual'`).\n * - Body capped at 64 KiB, total fetch time capped at 10 s.\n */\nexport async function fetchProbe(url: string, options: FetchProbeOptions = {}): Promise<HttpProbeResult> {\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'GET',\n headers: { accept: 'application/json' },\n allowPrivateIp: options.allowPrivateIp ?? false,\n ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n });\n return {\n url,\n status: res.status,\n headers: res.headers,\n body: decodeBodyAsJsonOrText(res.body, res.headers['content-type']),\n };\n } catch (err) {\n return {\n url,\n status: 0,\n headers: {},\n body: null,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Credential generators (value_strategy)\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a per-run bogus API key. Prefix is human-readable for log grep;\n * the 32 random hex bytes guarantee no allowlist collision.\n */\nexport function generateRandomInvalidApiKey(): string {\n return `invalid-${randomBytes(32).toString('hex')}`;\n}\n\n/**\n * Generate a per-run bogus JWT-shaped Bearer token. Emits three segments with\n * valid base64url-encoded JSON header/payload and a random signature — so\n * well-implemented validators fail at signature verification (→ 401), and\n * strict parse-time validators that reject at the structural level also fail\n * cleanly (→ 400 per RFC 6750 §3.1). Either is conformant.\n */\nexport function generateRandomInvalidJwt(): string {\n const header = base64url(Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })));\n const payload = base64url(\n Buffer.from(JSON.stringify({ sub: `invalid-${randomBytes(8).toString('hex')}`, aud: 'invalid-probe' }))\n );\n const signature = base64url(randomBytes(32));\n return `${header}.${payload}.${signature}`;\n}\n\nfunction base64url(buf: Buffer): string {\n return buf.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n// ---------------------------------------------------------------------------\n// Raw-MCP probe (auth-override dispatch)\n// ---------------------------------------------------------------------------\n\nlet probeRequestId = 0;\nconst MCP_SESSION_ID_HEADER = 'mcp-session-id';\nconst MCP_PROTOCOL_VERSION_HEADER = 'mcp-protocol-version';\n\ntype JsonRpcEnvelope = {\n jsonrpc: '2.0';\n id?: number;\n method?: string;\n params?: Record<string, unknown>;\n result?: { structuredContent?: unknown; content?: unknown; isError?: boolean; protocolVersion?: string };\n error?: { message?: string; code?: number };\n};\n\ninterface RawJsonRpcPostResult {\n httpResult: HttpProbeResult;\n parsed?: JsonRpcEnvelope;\n parseError?: boolean;\n}\n\nasync function postRawMcpJsonRpc(options: {\n agentUrl: string;\n envelope: Record<string, unknown>;\n headers: Record<string, string>;\n allowPrivateIp: boolean;\n sessionId?: string;\n protocolVersion?: string;\n responseId?: number;\n parseBody?: boolean;\n allowEmptyBody?: boolean;\n}): Promise<RawJsonRpcPostResult> {\n const {\n agentUrl,\n envelope,\n headers,\n allowPrivateIp,\n sessionId,\n protocolVersion,\n responseId,\n parseBody = true,\n allowEmptyBody = false,\n } = options;\n const httpResult: HttpProbeResult = { url: agentUrl, status: 0, headers: {}, body: null };\n try {\n const res = await ssrfSafeFetch(agentUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json, text/event-stream',\n ...withoutMcpSessionHeaders(headers),\n ...(sessionId ? { [MCP_SESSION_ID_HEADER]: sessionId } : {}),\n ...(protocolVersion ? { [MCP_PROTOCOL_VERSION_HEADER]: protocolVersion } : {}),\n },\n body: JSON.stringify(envelope),\n allowPrivateIp,\n });\n httpResult.status = res.status;\n httpResult.headers = res.headers;\n\n const text = Buffer.from(res.body.buffer, res.body.byteOffset, res.body.byteLength).toString('utf8');\n if (!parseBody) {\n httpResult.body = text || null;\n return { httpResult };\n }\n if (allowEmptyBody && text.trim() === '') {\n httpResult.body = null;\n return { httpResult };\n }\n try {\n const parsed = parseMcpJsonRpcResponse(text, httpResult.headers['content-type'], responseId);\n httpResult.body = parsed;\n return { httpResult, parsed };\n } catch {\n httpResult.body = text;\n return { httpResult, parseError: true };\n }\n } catch (err) {\n httpResult.error = err instanceof Error ? err.message : String(err);\n return { httpResult };\n }\n}\n\nfunction withoutMcpSessionHeaders(headers: Record<string, string>): Record<string, string> {\n const cleaned: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n const normalized = key.toLowerCase();\n if (normalized === MCP_SESSION_ID_HEADER || normalized === MCP_PROTOCOL_VERSION_HEADER) continue;\n cleaned[key] = value;\n }\n return cleaned;\n}\n\nfunction parseMcpJsonRpcResponse(text: string, contentType: string | undefined, responseId?: number): JsonRpcEnvelope {\n if (contentType?.toLowerCase().includes('text/event-stream')) {\n // Streamable-HTTP MCP: the response is one or more SSE events whose\n // `data:` payloads are JSON-RPC envelopes. The spec lets a server emit\n // notifications before the final response, so choose the matching id.\n const dataLines = text.split(/\\r?\\n/).filter(l => l.startsWith('data:'));\n if (dataLines.length === 0) throw new Error('SSE response with no data event');\n let matched: JsonRpcEnvelope | undefined;\n let lastParsed: JsonRpcEnvelope | undefined;\n for (const line of dataLines) {\n const payload = line.slice('data:'.length).trim();\n if (!payload) continue;\n try {\n const envelope = JSON.parse(payload) as JsonRpcEnvelope;\n lastParsed = envelope;\n if (responseId !== undefined && envelope?.id === responseId) {\n matched = envelope;\n break;\n }\n } catch {\n // Skip non-JSON data lines (heartbeats, etc.); keep walking.\n }\n }\n const parsed = matched ?? lastParsed;\n if (parsed === undefined) throw new Error('SSE response had data events but none were parseable JSON');\n return parsed;\n }\n return JSON.parse(text) as JsonRpcEnvelope;\n}\n\nfunction taskResultFromRpc(httpResult: HttpProbeResult, rpc: JsonRpcEnvelope): TaskResult {\n if (httpResult.status >= 400) {\n return {\n success: false,\n data: undefined,\n error: rpc.error?.message ?? `HTTP ${httpResult.status}`,\n _extraction_path: 'error',\n };\n }\n if (rpc.error) {\n const code = rpc.error.code;\n return {\n success: false,\n data: undefined,\n error:\n code !== undefined\n ? `JSON-RPC error ${code}: ${rpc.error.message ?? 'no message'}`\n : (rpc.error.message ?? 'JSON-RPC error (no code)'),\n _extraction_path: 'error',\n };\n }\n const structured = rpc.result?.structuredContent;\n const hasStructured = structured !== undefined && structured !== null;\n const data = hasStructured ? structured : rpc.result?.content;\n const isError = !!rpc.result?.isError;\n const extractionPath: 'structured_content' | 'text_fallback' | 'error' | 'none' = isError\n ? 'error'\n : hasStructured\n ? 'structured_content'\n : data !== undefined && data !== null\n ? 'text_fallback'\n : 'none';\n return { success: !isError, data, _extraction_path: extractionPath };\n}\n\nfunction failedTaskResult(message: string): TaskResult {\n return {\n success: false,\n data: undefined,\n error: message,\n _extraction_path: 'error',\n };\n}\n\nfunction taskResultFromPostFailure(posted: RawJsonRpcPostResult): TaskResult {\n if (posted.parseError) {\n return failedTaskResult(\n `Non-JSON response body (content-type: ${posted.httpResult.headers['content-type'] ?? 'unknown'}).`\n );\n }\n if (posted.parsed) return taskResultFromRpc(posted.httpResult, posted.parsed);\n return failedTaskResult(posted.httpResult.error ?? `HTTP ${posted.httpResult.status}`);\n}\n\n/**\n * POST a JSON-RPC `tools/call` request to the MCP endpoint with caller-provided\n * headers. The probe first performs the Streamable HTTP initialize handshake,\n * including `notifications/initialized`, so auth probes hit the same session\n * boundary as normal MCP clients while still exposing raw HTTP status and\n * `WWW-Authenticate` headers for security storyboards.\n *\n * **Args are not secret** — must not contain credentials or PII. The server's\n * response body lands in `httpResult.body` and is written to compliance\n * reports. Outbound request body is not persisted.\n *\n * Returns an HttpProbeResult plus a synthetic TaskResult for steps that also\n * want to validate body shape — the structuredContent is unwrapped so\n * `field_present: \"context\"` resolves naturally.\n */\nexport async function rawMcpProbe(options: {\n agentUrl: string;\n toolName: string;\n args: Record<string, unknown>;\n headers?: Record<string, string>;\n /** Allow http:// and private-IP agent URLs (dev loops). Default false. */\n allowPrivateIp?: boolean;\n}): Promise<{ httpResult: HttpProbeResult; taskResult?: TaskResult }> {\n const { agentUrl, toolName, args, headers = {}, allowPrivateIp = false } = options;\n const initializeId = ++probeRequestId;\n const initialize = await postRawMcpJsonRpc({\n agentUrl,\n envelope: {\n jsonrpc: '2.0',\n id: initializeId,\n method: 'initialize',\n params: {\n protocolVersion: LATEST_PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: 'AdCP Storyboard Raw MCP Probe', version: '1.0.0' },\n },\n },\n headers,\n allowPrivateIp,\n responseId: initializeId,\n });\n if (\n initialize.httpResult.error ||\n initialize.httpResult.status >= 400 ||\n !initialize.parsed ||\n initialize.parsed.error\n ) {\n return {\n httpResult: initialize.httpResult,\n taskResult: taskResultFromPostFailure(initialize),\n };\n }\n const negotiatedProtocolVersion = initialize.parsed.result?.protocolVersion;\n if (\n typeof negotiatedProtocolVersion !== 'string' ||\n !SUPPORTED_PROTOCOL_VERSIONS.includes(negotiatedProtocolVersion)\n ) {\n return {\n httpResult: initialize.httpResult,\n taskResult: failedTaskResult(\n negotiatedProtocolVersion\n ? `Server's protocol version is not supported: ${negotiatedProtocolVersion}`\n : 'Server sent invalid initialize result: missing protocolVersion'\n ),\n };\n }\n\n const sessionId = initialize.httpResult.headers[MCP_SESSION_ID_HEADER];\n const initialized = await postRawMcpJsonRpc({\n agentUrl,\n envelope: {\n jsonrpc: '2.0',\n method: 'notifications/initialized',\n params: {},\n },\n headers,\n allowPrivateIp,\n ...(sessionId && { sessionId }),\n protocolVersion: negotiatedProtocolVersion,\n allowEmptyBody: true,\n });\n if (\n initialized.httpResult.error ||\n initialized.httpResult.status >= 400 ||\n initialized.parseError ||\n initialized.parsed?.error\n ) {\n return {\n httpResult: initialized.httpResult,\n taskResult: taskResultFromPostFailure(initialized),\n };\n }\n\n const requestId = ++probeRequestId;\n const toolEnvelope = {\n jsonrpc: '2.0',\n id: requestId,\n method: 'tools/call',\n params: { name: toolName, arguments: args },\n };\n const posted = await postRawMcpJsonRpc({\n agentUrl,\n envelope: toolEnvelope,\n headers,\n allowPrivateIp,\n ...(sessionId && { sessionId }),\n protocolVersion: negotiatedProtocolVersion,\n responseId: requestId,\n });\n const { httpResult, parsed } = posted;\n if (httpResult.error) return { httpResult, taskResult: taskResultFromPostFailure(posted) };\n if (!parsed) {\n return {\n httpResult,\n taskResult: taskResultFromPostFailure(posted),\n };\n }\n return { httpResult, taskResult: taskResultFromRpc(httpResult, parsed) };\n}\n\n// ---------------------------------------------------------------------------\n// Raw-A2A probe (transport-layer diagnostics for A2A agents)\n// ---------------------------------------------------------------------------\n\n/**\n * POST a JSON-RPC 2.0 request to an A2A agent endpoint with caller-provided\n * headers. Bypasses the A2A SDK so raw HTTP status, headers, and JSON-RPC\n * error codes can be captured by storyboard diagnostics.\n *\n * Mirrors `rawMcpProbe` in structure and SSRF-safety contract. Key\n * differences from the MCP variant:\n *\n * - The caller supplies `method` + optional `params` (not a fixed `tools/call`\n * body). A2A has no single canonical method — use `\"message/send\"` for most\n * auth and error-code probes, `\"tasks/get\"` / `\"tasks/cancel\"` for lifecycle\n * checks. **Note:** `message/send` requires `params.message` (a full A2A\n * `Message` object with `messageId`, `role`, `kind`, `parts`). Passing\n * `params: {}` will produce `-32602 Invalid params` from a conformant server,\n * masking auth rejections — supply a minimal message when probing auth paths.\n * - A2A JSON-RPC error codes differ from MCP's. Notably `-32002` means\n * `TaskNotCancelable` in A2A (not session-not-initialized). The probe\n * surfaces raw numeric codes without protocol-specific aliasing so\n * storyboards can assert on the exact code.\n * - SSE (streaming) responses are handled the same way as `rawMcpProbe`:\n * `Accept: application/json` is sent; non-JSON bodies surface a distinct\n * error so callers don't mistake an event-stream for a silent success.\n *\n * **Args are not secret** — must not contain credentials or PII. The\n * server's response body lands in `httpResult.body` and is written to\n * compliance reports.\n *\n * Returns an `HttpProbeResult` plus an optional `TaskResult` (same shape as\n * `rawMcpProbe`) so the storyboard `ValidationContext` can consume both probes\n * interchangeably. The A2A success `_extraction_path` is `'text_fallback'`\n * (not `'structured_content'`) because A2A's `result` field is a plain object,\n * not an MCP structured-content envelope.\n */\nexport async function rawA2aProbe(options: {\n /** Base URL of the A2A agent endpoint (e.g. `https://agent.example.com/a2a`). */\n agentUrl: string;\n /** A2A/JSON-RPC 2.0 method name (e.g. `\"message/send\"`, `\"tasks/get\"`). */\n method: string;\n /** JSON-RPC params. Defaults to `{}` so the probe always emits a valid envelope. */\n params?: Record<string, unknown>;\n headers?: Record<string, string>;\n /** Allow http:// and private-IP agent URLs (dev loops). Default false. */\n allowPrivateIp?: boolean;\n}): Promise<{ httpResult: HttpProbeResult; taskResult?: TaskResult }> {\n const { agentUrl, method, params, headers = {}, allowPrivateIp = false } = options;\n const body = JSON.stringify({\n jsonrpc: '2.0',\n id: ++probeRequestId,\n method,\n params: params ?? {},\n });\n\n const httpResult: HttpProbeResult = { url: agentUrl, status: 0, headers: {}, body: null };\n try {\n const res = await ssrfSafeFetch(agentUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json',\n ...headers,\n },\n body,\n allowPrivateIp,\n });\n httpResult.status = res.status;\n httpResult.headers = res.headers;\n\n const text = Buffer.from(res.body.buffer, res.body.byteOffset, res.body.byteLength).toString('utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n httpResult.body = text;\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error: `Non-JSON response body (content-type: ${httpResult.headers['content-type'] ?? 'unknown'}).`,\n _extraction_path: 'error',\n },\n };\n }\n httpResult.body = parsed;\n\n const rpc = parsed as {\n result?: unknown;\n error?: { message?: string; code?: number };\n };\n\n if (httpResult.status >= 400) {\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error: rpc.error?.message ?? `HTTP ${httpResult.status}`,\n _extraction_path: 'error',\n },\n };\n }\n\n if (rpc.error) {\n const code = rpc.error.code;\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error:\n code !== undefined\n ? `JSON-RPC error ${code}: ${rpc.error.message ?? 'no message'}`\n : (rpc.error.message ?? 'JSON-RPC error (no code)'),\n _extraction_path: 'error',\n },\n };\n }\n\n const data = rpc.result;\n const extractionPath: 'text_fallback' | 'none' = data !== undefined && data !== null ? 'text_fallback' : 'none';\n return { httpResult, taskResult: { success: true, data, _extraction_path: extractionPath } };\n } catch (err) {\n httpResult.error = err instanceof Error ? err.message : String(err);\n return { httpResult };\n }\n}\n\n// IP classifiers live in `src/lib/net/address-guards.ts` so the SSRF-safe\n// fetch primitive can use them without depending on the testing module.\n// Re-exported here for existing import sites (storyboard-security test + any\n// external probe consumers).\nexport const isAlwaysBlocked = sharedIsAlwaysBlocked;\nexport const isPrivateIp = sharedIsPrivateIp;\nexport { SsrfRefusedError };\n"],"mappings":"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","names":["sharedIsAlwaysBlocked","sharedIsPrivateIp"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/testing/storyboard/probes.ts"],"sourcesContent":["/**\n * HTTP probes used by security-baseline storyboard tasks.\n *\n * Three synthetic tasks dispatch through here instead of the MCP client:\n *\n * - `protected_resource_metadata` — GET the agent's\n * `/.well-known/oauth-protected-resource<mountPath>` and verify RFC 9728.\n * - `oauth_auth_server_metadata` — GET `<issuer>/.well-known/oauth-authorization-server`\n * using the first issuer from the previous step's response. Hardened against\n * SSRF because the URL comes from agent-controlled data.\n * - `assert_contribution` — no network; evaluates accumulated flags set by\n * prior steps that carried `contributes_to`.\n */\nimport { randomBytes } from 'crypto';\nimport { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } from '@modelcontextprotocol/sdk/types.js';\nimport {\n ssrfSafeFetch,\n decodeBodyAsJsonOrText,\n SsrfRefusedError,\n isAlwaysBlocked as sharedIsAlwaysBlocked,\n isPrivateIp as sharedIsPrivateIp,\n} from '../../net';\nimport type { HttpProbeResult } from './types';\nimport type { TaskResult } from '../types';\n\n// Timeout + body-cap defaults come from `ssrfSafeFetch` (10 s, 64 KiB).\n// The probe wrappers deliberately don't override them so probe behavior\n// stays in sync with the shared primitive.\n\n/** Task names dispatched via HTTP probes (not via the MCP client). */\nexport const PROBE_TASKS = new Set([\n 'protected_resource_metadata',\n 'oauth_auth_server_metadata',\n 'assert_contribution',\n 'request_signing_probe',\n 'fetch_brand_jwks',\n 'assert_jwks_purpose',\n 'expect_rate_limit_not_replayed',\n]);\n\n// ---------------------------------------------------------------------------\n// Protected-resource metadata probe\n// ---------------------------------------------------------------------------\n\n/**\n * GET `<agentUrl origin>/.well-known/oauth-protected-resource<agentUrl path>`.\n * Same-origin as the agent, so SSRF risk is bounded.\n *\n * When `allowPrivateIp` is set (matches the runner's `--allow-http` flag),\n * loopback and RFC 1918 targets are allowed so dev loops against localhost\n * agents work end-to-end.\n */\nexport async function probeProtectedResourceMetadata(\n agentUrl: string,\n options: { allowPrivateIp?: boolean; fetchFn?: typeof fetch } = {}\n): Promise<HttpProbeResult> {\n const u = new URL(agentUrl);\n const metadataUrl = `${u.origin}/.well-known/oauth-protected-resource${u.pathname}`;\n return fetchProbe(metadataUrl, {\n allowPrivateIp: options.allowPrivateIp ?? false,\n fetchFn: options.fetchFn,\n });\n}\n\n// ---------------------------------------------------------------------------\n// OAuth authorization-server metadata probe\n// ---------------------------------------------------------------------------\n\n/**\n * GET `<issuer>/.well-known/oauth-authorization-server` for the first issuer\n * named in the protected-resource metadata. Because the URL is agent-supplied,\n * this is the SSRF-hot path — {@link fetchProbe} rejects private networks,\n * non-https schemes, and unbounded responses.\n */\nexport async function probeOauthAuthServerMetadata(\n priorProbe: HttpProbeResult | undefined,\n options: { allowPrivateIp?: boolean; fetchFn?: typeof fetch } = {}\n): Promise<HttpProbeResult> {\n if (!priorProbe || priorProbe.error) {\n return {\n url: '',\n status: 0,\n headers: {},\n body: null,\n error: 'protected_resource_metadata step missing or errored — cannot resolve issuer',\n };\n }\n const body = priorProbe.body as { authorization_servers?: unknown } | null;\n const servers = Array.isArray(body?.authorization_servers) ? (body!.authorization_servers as string[]) : [];\n if (servers.length === 0 || typeof servers[0] !== 'string') {\n return {\n url: '',\n status: 0,\n headers: {},\n body: null,\n error: 'No authorization_servers[0] found in protected-resource metadata',\n };\n }\n const issuer = servers[0].replace(/\\/$/, '');\n const metadataUrl = `${issuer}/.well-known/oauth-authorization-server`;\n return fetchProbe(metadataUrl, {\n allowPrivateIp: options.allowPrivateIp ?? false,\n fetchFn: options.fetchFn,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Fetch with guardrails\n// ---------------------------------------------------------------------------\n\nexport interface FetchProbeOptions {\n /** Allow http:// and private-IP destinations. Default false. */\n allowPrivateIp?: boolean;\n /** Override timeout for specific call sites. */\n timeoutMs?: number;\n /** Trusted scoped fetch; must enforce DNS-rebinding protection. */\n fetchFn?: typeof fetch;\n}\n\n/**\n * Perform a GET against an attacker-influenceable URL with defensive limits.\n *\n * Guardrails (RFC 9728 / RFC 8414 metadata endpoints typically live on public\n * HTTPS; anything else is suspicious):\n * - Scheme: `https:` only by default; `http:` allowed only when\n * `allowPrivateIp` is set. `file:`, `ftp:`, `data:`, etc. are always rejected.\n * - DNS: resolves all A/AAAA records once, rejects if any is private, then\n * pins the outbound connection to the validated IP. Defeats DNS rebinding\n * where an attacker's authoritative nameserver returns a public address\n * to our guard lookup and a private address to the connect-time lookup.\n * - Private-IP block applies RFC 1918, loopback, link-local, IPv6 ULA,\n * CGNAT (100.64/10), multicast, broadcast, and IPv4-mapped IPv6.\n * - IMDS (169.254.169.254 / fe80::) stays blocked **even under\n * `allowPrivateIp`** — no legitimate dev use for probing it.\n * - Redirects are NOT followed (`redirect: 'manual'`).\n * - Body capped at 64 KiB, total fetch time capped at 10 s.\n */\nexport async function fetchProbe(url: string, options: FetchProbeOptions = {}): Promise<HttpProbeResult> {\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'GET',\n headers: { accept: 'application/json' },\n allowPrivateIp: options.allowPrivateIp ?? false,\n ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n ...(options.fetchFn ? { trustedFetchFn: options.fetchFn } : {}),\n });\n return {\n url,\n status: res.status,\n headers: res.headers,\n body: decodeBodyAsJsonOrText(res.body, res.headers['content-type']),\n };\n } catch (err) {\n return {\n url,\n status: 0,\n headers: {},\n body: null,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Credential generators (value_strategy)\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a per-run bogus API key. Prefix is human-readable for log grep;\n * the 32 random hex bytes guarantee no allowlist collision.\n */\nexport function generateRandomInvalidApiKey(): string {\n return `invalid-${randomBytes(32).toString('hex')}`;\n}\n\n/**\n * Generate a per-run bogus JWT-shaped Bearer token. Emits three segments with\n * valid base64url-encoded JSON header/payload and a random signature — so\n * well-implemented validators fail at signature verification (→ 401), and\n * strict parse-time validators that reject at the structural level also fail\n * cleanly (→ 400 per RFC 6750 §3.1). Either is conformant.\n */\nexport function generateRandomInvalidJwt(): string {\n const header = base64url(Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })));\n const payload = base64url(\n Buffer.from(JSON.stringify({ sub: `invalid-${randomBytes(8).toString('hex')}`, aud: 'invalid-probe' }))\n );\n const signature = base64url(randomBytes(32));\n return `${header}.${payload}.${signature}`;\n}\n\nfunction base64url(buf: Buffer): string {\n return buf.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n// ---------------------------------------------------------------------------\n// Raw-MCP probe (auth-override dispatch)\n// ---------------------------------------------------------------------------\n\nlet probeRequestId = 0;\nconst MCP_SESSION_ID_HEADER = 'mcp-session-id';\nconst MCP_PROTOCOL_VERSION_HEADER = 'mcp-protocol-version';\n\ntype JsonRpcEnvelope = {\n jsonrpc: '2.0';\n id?: number;\n method?: string;\n params?: Record<string, unknown>;\n result?: { structuredContent?: unknown; content?: unknown; isError?: boolean; protocolVersion?: string };\n error?: { message?: string; code?: number };\n};\n\ninterface RawJsonRpcPostResult {\n httpResult: HttpProbeResult;\n parsed?: JsonRpcEnvelope;\n parseError?: boolean;\n}\n\nasync function postRawMcpJsonRpc(options: {\n agentUrl: string;\n envelope: Record<string, unknown>;\n headers: Record<string, string>;\n allowPrivateIp: boolean;\n sessionId?: string;\n protocolVersion?: string;\n responseId?: number;\n parseBody?: boolean;\n allowEmptyBody?: boolean;\n fetchFn?: typeof fetch;\n}): Promise<RawJsonRpcPostResult> {\n const {\n agentUrl,\n envelope,\n headers,\n allowPrivateIp,\n sessionId,\n protocolVersion,\n responseId,\n parseBody = true,\n allowEmptyBody = false,\n } = options;\n const httpResult: HttpProbeResult = { url: agentUrl, status: 0, headers: {}, body: null };\n try {\n const res = await ssrfSafeFetch(agentUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json, text/event-stream',\n ...withoutMcpSessionHeaders(headers),\n ...(sessionId ? { [MCP_SESSION_ID_HEADER]: sessionId } : {}),\n ...(protocolVersion ? { [MCP_PROTOCOL_VERSION_HEADER]: protocolVersion } : {}),\n },\n body: JSON.stringify(envelope),\n allowPrivateIp,\n ...(options.fetchFn ? { trustedFetchFn: options.fetchFn } : {}),\n });\n httpResult.status = res.status;\n httpResult.headers = res.headers;\n\n const text = Buffer.from(res.body.buffer, res.body.byteOffset, res.body.byteLength).toString('utf8');\n if (!parseBody) {\n httpResult.body = text || null;\n return { httpResult };\n }\n if (allowEmptyBody && text.trim() === '') {\n httpResult.body = null;\n return { httpResult };\n }\n try {\n const parsed = parseMcpJsonRpcResponse(text, httpResult.headers['content-type'], responseId);\n httpResult.body = parsed;\n return { httpResult, parsed };\n } catch {\n httpResult.body = text;\n return { httpResult, parseError: true };\n }\n } catch (err) {\n httpResult.error = err instanceof Error ? err.message : String(err);\n return { httpResult };\n }\n}\n\nfunction withoutMcpSessionHeaders(headers: Record<string, string>): Record<string, string> {\n const cleaned: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n const normalized = key.toLowerCase();\n if (normalized === MCP_SESSION_ID_HEADER || normalized === MCP_PROTOCOL_VERSION_HEADER) continue;\n cleaned[key] = value;\n }\n return cleaned;\n}\n\nfunction parseMcpJsonRpcResponse(text: string, contentType: string | undefined, responseId?: number): JsonRpcEnvelope {\n if (contentType?.toLowerCase().includes('text/event-stream')) {\n // Streamable-HTTP MCP: the response is one or more SSE events whose\n // `data:` payloads are JSON-RPC envelopes. The spec lets a server emit\n // notifications before the final response, so choose the matching id.\n const dataLines = text.split(/\\r?\\n/).filter(l => l.startsWith('data:'));\n if (dataLines.length === 0) throw new Error('SSE response with no data event');\n let matched: JsonRpcEnvelope | undefined;\n let lastParsed: JsonRpcEnvelope | undefined;\n for (const line of dataLines) {\n const payload = line.slice('data:'.length).trim();\n if (!payload) continue;\n try {\n const envelope = JSON.parse(payload) as JsonRpcEnvelope;\n lastParsed = envelope;\n if (responseId !== undefined && envelope?.id === responseId) {\n matched = envelope;\n break;\n }\n } catch {\n // Skip non-JSON data lines (heartbeats, etc.); keep walking.\n }\n }\n const parsed = matched ?? lastParsed;\n if (parsed === undefined) throw new Error('SSE response had data events but none were parseable JSON');\n return parsed;\n }\n return JSON.parse(text) as JsonRpcEnvelope;\n}\n\nfunction taskResultFromRpc(httpResult: HttpProbeResult, rpc: JsonRpcEnvelope): TaskResult {\n if (httpResult.status >= 400) {\n return {\n success: false,\n data: undefined,\n error: rpc.error?.message ?? `HTTP ${httpResult.status}`,\n _extraction_path: 'error',\n };\n }\n if (rpc.error) {\n const code = rpc.error.code;\n return {\n success: false,\n data: undefined,\n error:\n code !== undefined\n ? `JSON-RPC error ${code}: ${rpc.error.message ?? 'no message'}`\n : (rpc.error.message ?? 'JSON-RPC error (no code)'),\n _extraction_path: 'error',\n };\n }\n const structured = rpc.result?.structuredContent;\n const hasStructured = structured !== undefined && structured !== null;\n const data = hasStructured ? structured : rpc.result?.content;\n const isError = !!rpc.result?.isError;\n const extractionPath: 'structured_content' | 'text_fallback' | 'error' | 'none' = isError\n ? 'error'\n : hasStructured\n ? 'structured_content'\n : data !== undefined && data !== null\n ? 'text_fallback'\n : 'none';\n return { success: !isError, data, _extraction_path: extractionPath };\n}\n\nfunction failedTaskResult(message: string): TaskResult {\n return {\n success: false,\n data: undefined,\n error: message,\n _extraction_path: 'error',\n };\n}\n\nfunction taskResultFromPostFailure(posted: RawJsonRpcPostResult): TaskResult {\n if (posted.parseError) {\n return failedTaskResult(\n `Non-JSON response body (content-type: ${posted.httpResult.headers['content-type'] ?? 'unknown'}).`\n );\n }\n if (posted.parsed) return taskResultFromRpc(posted.httpResult, posted.parsed);\n return failedTaskResult(posted.httpResult.error ?? `HTTP ${posted.httpResult.status}`);\n}\n\n/**\n * POST a JSON-RPC `tools/call` request to the MCP endpoint with caller-provided\n * headers. The probe first performs the Streamable HTTP initialize handshake,\n * including `notifications/initialized`, so auth probes hit the same session\n * boundary as normal MCP clients while still exposing raw HTTP status and\n * `WWW-Authenticate` headers for security storyboards.\n *\n * **Args are not secret** — must not contain credentials or PII. The server's\n * response body lands in `httpResult.body` and is written to compliance\n * reports. Outbound request body is not persisted.\n *\n * Returns an HttpProbeResult plus a synthetic TaskResult for steps that also\n * want to validate body shape — the structuredContent is unwrapped so\n * `field_present: \"context\"` resolves naturally.\n */\nexport async function rawMcpProbe(options: {\n agentUrl: string;\n toolName: string;\n args: Record<string, unknown>;\n headers?: Record<string, string>;\n /** Allow http:// and private-IP agent URLs (dev loops). Default false. */\n allowPrivateIp?: boolean;\n /** Scoped fetch implementation for every handshake and tool request. */\n fetchFn?: typeof fetch;\n}): Promise<{ httpResult: HttpProbeResult; taskResult?: TaskResult }> {\n const { agentUrl, toolName, args, headers = {}, allowPrivateIp = false, fetchFn } = options;\n const initializeId = ++probeRequestId;\n const initialize = await postRawMcpJsonRpc({\n agentUrl,\n envelope: {\n jsonrpc: '2.0',\n id: initializeId,\n method: 'initialize',\n params: {\n protocolVersion: LATEST_PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: 'AdCP Storyboard Raw MCP Probe', version: '1.0.0' },\n },\n },\n headers,\n allowPrivateIp,\n fetchFn,\n responseId: initializeId,\n });\n if (\n initialize.httpResult.error ||\n initialize.httpResult.status >= 400 ||\n !initialize.parsed ||\n initialize.parsed.error\n ) {\n return {\n httpResult: initialize.httpResult,\n taskResult: taskResultFromPostFailure(initialize),\n };\n }\n const negotiatedProtocolVersion = initialize.parsed.result?.protocolVersion;\n if (\n typeof negotiatedProtocolVersion !== 'string' ||\n !SUPPORTED_PROTOCOL_VERSIONS.includes(negotiatedProtocolVersion)\n ) {\n return {\n httpResult: initialize.httpResult,\n taskResult: failedTaskResult(\n negotiatedProtocolVersion\n ? `Server's protocol version is not supported: ${negotiatedProtocolVersion}`\n : 'Server sent invalid initialize result: missing protocolVersion'\n ),\n };\n }\n\n const sessionId = initialize.httpResult.headers[MCP_SESSION_ID_HEADER];\n const initialized = await postRawMcpJsonRpc({\n agentUrl,\n envelope: {\n jsonrpc: '2.0',\n method: 'notifications/initialized',\n params: {},\n },\n headers,\n allowPrivateIp,\n fetchFn,\n ...(sessionId && { sessionId }),\n protocolVersion: negotiatedProtocolVersion,\n allowEmptyBody: true,\n });\n if (\n initialized.httpResult.error ||\n initialized.httpResult.status >= 400 ||\n initialized.parseError ||\n initialized.parsed?.error\n ) {\n return {\n httpResult: initialized.httpResult,\n taskResult: taskResultFromPostFailure(initialized),\n };\n }\n\n const requestId = ++probeRequestId;\n const toolEnvelope = {\n jsonrpc: '2.0',\n id: requestId,\n method: 'tools/call',\n params: { name: toolName, arguments: args },\n };\n const posted = await postRawMcpJsonRpc({\n agentUrl,\n envelope: toolEnvelope,\n headers,\n allowPrivateIp,\n fetchFn,\n ...(sessionId && { sessionId }),\n protocolVersion: negotiatedProtocolVersion,\n responseId: requestId,\n });\n const { httpResult, parsed } = posted;\n if (httpResult.error) return { httpResult, taskResult: taskResultFromPostFailure(posted) };\n if (!parsed) {\n return {\n httpResult,\n taskResult: taskResultFromPostFailure(posted),\n };\n }\n return { httpResult, taskResult: taskResultFromRpc(httpResult, parsed) };\n}\n\n// ---------------------------------------------------------------------------\n// Raw-A2A probe (transport-layer diagnostics for A2A agents)\n// ---------------------------------------------------------------------------\n\n/**\n * POST a JSON-RPC 2.0 request to an A2A agent endpoint with caller-provided\n * headers. Bypasses the A2A SDK so raw HTTP status, headers, and JSON-RPC\n * error codes can be captured by storyboard diagnostics.\n *\n * Mirrors `rawMcpProbe` in structure and SSRF-safety contract. Key\n * differences from the MCP variant:\n *\n * - The caller supplies `method` + optional `params` (not a fixed `tools/call`\n * body). A2A has no single canonical method — use `\"message/send\"` for most\n * auth and error-code probes, `\"tasks/get\"` / `\"tasks/cancel\"` for lifecycle\n * checks. **Note:** `message/send` requires `params.message` (a full A2A\n * `Message` object with `messageId`, `role`, `kind`, `parts`). Passing\n * `params: {}` will produce `-32602 Invalid params` from a conformant server,\n * masking auth rejections — supply a minimal message when probing auth paths.\n * - A2A JSON-RPC error codes differ from MCP's. Notably `-32002` means\n * `TaskNotCancelable` in A2A (not session-not-initialized). The probe\n * surfaces raw numeric codes without protocol-specific aliasing so\n * storyboards can assert on the exact code.\n * - SSE (streaming) responses are handled the same way as `rawMcpProbe`:\n * `Accept: application/json` is sent; non-JSON bodies surface a distinct\n * error so callers don't mistake an event-stream for a silent success.\n *\n * **Args are not secret** — must not contain credentials or PII. The\n * server's response body lands in `httpResult.body` and is written to\n * compliance reports.\n *\n * Returns an `HttpProbeResult` plus an optional `TaskResult` (same shape as\n * `rawMcpProbe`) so the storyboard `ValidationContext` can consume both probes\n * interchangeably. The A2A success `_extraction_path` is `'text_fallback'`\n * (not `'structured_content'`) because A2A's `result` field is a plain object,\n * not an MCP structured-content envelope.\n */\nexport async function rawA2aProbe(options: {\n /** Base URL of the A2A agent endpoint (e.g. `https://agent.example.com/a2a`). */\n agentUrl: string;\n /** A2A/JSON-RPC 2.0 method name (e.g. `\"message/send\"`, `\"tasks/get\"`). */\n method: string;\n /** JSON-RPC params. Defaults to `{}` so the probe always emits a valid envelope. */\n params?: Record<string, unknown>;\n headers?: Record<string, string>;\n /** Allow http:// and private-IP agent URLs (dev loops). Default false. */\n allowPrivateIp?: boolean;\n}): Promise<{ httpResult: HttpProbeResult; taskResult?: TaskResult }> {\n const { agentUrl, method, params, headers = {}, allowPrivateIp = false } = options;\n const body = JSON.stringify({\n jsonrpc: '2.0',\n id: ++probeRequestId,\n method,\n params: params ?? {},\n });\n\n const httpResult: HttpProbeResult = { url: agentUrl, status: 0, headers: {}, body: null };\n try {\n const res = await ssrfSafeFetch(agentUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json',\n ...headers,\n },\n body,\n allowPrivateIp,\n });\n httpResult.status = res.status;\n httpResult.headers = res.headers;\n\n const text = Buffer.from(res.body.buffer, res.body.byteOffset, res.body.byteLength).toString('utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n httpResult.body = text;\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error: `Non-JSON response body (content-type: ${httpResult.headers['content-type'] ?? 'unknown'}).`,\n _extraction_path: 'error',\n },\n };\n }\n httpResult.body = parsed;\n\n const rpc = parsed as {\n result?: unknown;\n error?: { message?: string; code?: number };\n };\n\n if (httpResult.status >= 400) {\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error: rpc.error?.message ?? `HTTP ${httpResult.status}`,\n _extraction_path: 'error',\n },\n };\n }\n\n if (rpc.error) {\n const code = rpc.error.code;\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error:\n code !== undefined\n ? `JSON-RPC error ${code}: ${rpc.error.message ?? 'no message'}`\n : (rpc.error.message ?? 'JSON-RPC error (no code)'),\n _extraction_path: 'error',\n },\n };\n }\n\n const data = rpc.result;\n const extractionPath: 'text_fallback' | 'none' = data !== undefined && data !== null ? 'text_fallback' : 'none';\n return { httpResult, taskResult: { success: true, data, _extraction_path: extractionPath } };\n } catch (err) {\n httpResult.error = err instanceof Error ? err.message : String(err);\n return { httpResult };\n }\n}\n\n// IP classifiers live in `src/lib/net/address-guards.ts` so the SSRF-safe\n// fetch primitive can use them without depending on the testing module.\n// Re-exported here for existing import sites (storyboard-security test + any\n// external probe consumers).\nexport const isAlwaysBlocked = sharedIsAlwaysBlocked;\nexport const isPrivateIp = sharedIsPrivateIp;\nexport { SsrfRefusedError };\n"],"mappings":"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","names":["sharedIsAlwaysBlocked","sharedIsPrivateIp"]}
|
|
@@ -19,7 +19,10 @@ const PROBE_TASKS = /* @__PURE__ */ new Set([
|
|
|
19
19
|
async function probeProtectedResourceMetadata(agentUrl, options = {}) {
|
|
20
20
|
const u = new URL(agentUrl);
|
|
21
21
|
const metadataUrl = `${u.origin}/.well-known/oauth-protected-resource${u.pathname}`;
|
|
22
|
-
return fetchProbe(metadataUrl, {
|
|
22
|
+
return fetchProbe(metadataUrl, {
|
|
23
|
+
allowPrivateIp: options.allowPrivateIp ?? false,
|
|
24
|
+
fetchFn: options.fetchFn
|
|
25
|
+
});
|
|
23
26
|
}
|
|
24
27
|
async function probeOauthAuthServerMetadata(priorProbe, options = {}) {
|
|
25
28
|
if (!priorProbe || priorProbe.error) {
|
|
@@ -44,7 +47,10 @@ async function probeOauthAuthServerMetadata(priorProbe, options = {}) {
|
|
|
44
47
|
}
|
|
45
48
|
const issuer = servers[0].replace(/\/$/, "");
|
|
46
49
|
const metadataUrl = `${issuer}/.well-known/oauth-authorization-server`;
|
|
47
|
-
return fetchProbe(metadataUrl, {
|
|
50
|
+
return fetchProbe(metadataUrl, {
|
|
51
|
+
allowPrivateIp: options.allowPrivateIp ?? false,
|
|
52
|
+
fetchFn: options.fetchFn
|
|
53
|
+
});
|
|
48
54
|
}
|
|
49
55
|
async function fetchProbe(url, options = {}) {
|
|
50
56
|
try {
|
|
@@ -52,7 +58,8 @@ async function fetchProbe(url, options = {}) {
|
|
|
52
58
|
method: "GET",
|
|
53
59
|
headers: { accept: "application/json" },
|
|
54
60
|
allowPrivateIp: options.allowPrivateIp ?? false,
|
|
55
|
-
...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {}
|
|
61
|
+
...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {},
|
|
62
|
+
...options.fetchFn ? { trustedFetchFn: options.fetchFn } : {}
|
|
56
63
|
});
|
|
57
64
|
return {
|
|
58
65
|
url,
|
|
@@ -111,7 +118,8 @@ async function postRawMcpJsonRpc(options) {
|
|
|
111
118
|
...protocolVersion ? { [MCP_PROTOCOL_VERSION_HEADER]: protocolVersion } : {}
|
|
112
119
|
},
|
|
113
120
|
body: JSON.stringify(envelope),
|
|
114
|
-
allowPrivateIp
|
|
121
|
+
allowPrivateIp,
|
|
122
|
+
...options.fetchFn ? { trustedFetchFn: options.fetchFn } : {}
|
|
115
123
|
});
|
|
116
124
|
httpResult.status = res.status;
|
|
117
125
|
httpResult.headers = res.headers;
|
|
@@ -214,7 +222,7 @@ function taskResultFromPostFailure(posted) {
|
|
|
214
222
|
return failedTaskResult(posted.httpResult.error ?? `HTTP ${posted.httpResult.status}`);
|
|
215
223
|
}
|
|
216
224
|
async function rawMcpProbe(options) {
|
|
217
|
-
const { agentUrl, toolName, args, headers = {}, allowPrivateIp = false } = options;
|
|
225
|
+
const { agentUrl, toolName, args, headers = {}, allowPrivateIp = false, fetchFn } = options;
|
|
218
226
|
const initializeId = ++probeRequestId;
|
|
219
227
|
const initialize = await postRawMcpJsonRpc({
|
|
220
228
|
agentUrl,
|
|
@@ -230,6 +238,7 @@ async function rawMcpProbe(options) {
|
|
|
230
238
|
},
|
|
231
239
|
headers,
|
|
232
240
|
allowPrivateIp,
|
|
241
|
+
fetchFn,
|
|
233
242
|
responseId: initializeId
|
|
234
243
|
});
|
|
235
244
|
if (initialize.httpResult.error || initialize.httpResult.status >= 400 || !initialize.parsed || initialize.parsed.error) {
|
|
@@ -257,6 +266,7 @@ async function rawMcpProbe(options) {
|
|
|
257
266
|
},
|
|
258
267
|
headers,
|
|
259
268
|
allowPrivateIp,
|
|
269
|
+
fetchFn,
|
|
260
270
|
...sessionId && { sessionId },
|
|
261
271
|
protocolVersion: negotiatedProtocolVersion,
|
|
262
272
|
allowEmptyBody: true
|
|
@@ -279,6 +289,7 @@ async function rawMcpProbe(options) {
|
|
|
279
289
|
envelope: toolEnvelope,
|
|
280
290
|
headers,
|
|
281
291
|
allowPrivateIp,
|
|
292
|
+
fetchFn,
|
|
282
293
|
...sessionId && { sessionId },
|
|
283
294
|
protocolVersion: negotiatedProtocolVersion,
|
|
284
295
|
responseId: requestId
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/testing/storyboard/probes.ts"],"sourcesContent":["/**\n * HTTP probes used by security-baseline storyboard tasks.\n *\n * Three synthetic tasks dispatch through here instead of the MCP client:\n *\n * - `protected_resource_metadata` — GET the agent's\n * `/.well-known/oauth-protected-resource<mountPath>` and verify RFC 9728.\n * - `oauth_auth_server_metadata` — GET `<issuer>/.well-known/oauth-authorization-server`\n * using the first issuer from the previous step's response. Hardened against\n * SSRF because the URL comes from agent-controlled data.\n * - `assert_contribution` — no network; evaluates accumulated flags set by\n * prior steps that carried `contributes_to`.\n */\nimport { randomBytes } from 'crypto';\nimport { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } from '@modelcontextprotocol/sdk/types.js';\nimport {\n ssrfSafeFetch,\n decodeBodyAsJsonOrText,\n SsrfRefusedError,\n isAlwaysBlocked as sharedIsAlwaysBlocked,\n isPrivateIp as sharedIsPrivateIp,\n} from '../../net';\nimport type { HttpProbeResult } from './types';\nimport type { TaskResult } from '../types';\n\n// Timeout + body-cap defaults come from `ssrfSafeFetch` (10 s, 64 KiB).\n// The probe wrappers deliberately don't override them so probe behavior\n// stays in sync with the shared primitive.\n\n/** Task names dispatched via HTTP probes (not via the MCP client). */\nexport const PROBE_TASKS = new Set([\n 'protected_resource_metadata',\n 'oauth_auth_server_metadata',\n 'assert_contribution',\n 'request_signing_probe',\n 'fetch_brand_jwks',\n 'assert_jwks_purpose',\n 'expect_rate_limit_not_replayed',\n]);\n\n// ---------------------------------------------------------------------------\n// Protected-resource metadata probe\n// ---------------------------------------------------------------------------\n\n/**\n * GET `<agentUrl origin>/.well-known/oauth-protected-resource<agentUrl path>`.\n * Same-origin as the agent, so SSRF risk is bounded.\n *\n * When `allowPrivateIp` is set (matches the runner's `--allow-http` flag),\n * loopback and RFC 1918 targets are allowed so dev loops against localhost\n * agents work end-to-end.\n */\nexport async function probeProtectedResourceMetadata(\n agentUrl: string,\n options: { allowPrivateIp?: boolean } = {}\n): Promise<HttpProbeResult> {\n const u = new URL(agentUrl);\n const metadataUrl = `${u.origin}/.well-known/oauth-protected-resource${u.pathname}`;\n return fetchProbe(metadataUrl, { allowPrivateIp: options.allowPrivateIp ?? false });\n}\n\n// ---------------------------------------------------------------------------\n// OAuth authorization-server metadata probe\n// ---------------------------------------------------------------------------\n\n/**\n * GET `<issuer>/.well-known/oauth-authorization-server` for the first issuer\n * named in the protected-resource metadata. Because the URL is agent-supplied,\n * this is the SSRF-hot path — {@link fetchProbe} rejects private networks,\n * non-https schemes, and unbounded responses.\n */\nexport async function probeOauthAuthServerMetadata(\n priorProbe: HttpProbeResult | undefined,\n options: { allowPrivateIp?: boolean } = {}\n): Promise<HttpProbeResult> {\n if (!priorProbe || priorProbe.error) {\n return {\n url: '',\n status: 0,\n headers: {},\n body: null,\n error: 'protected_resource_metadata step missing or errored — cannot resolve issuer',\n };\n }\n const body = priorProbe.body as { authorization_servers?: unknown } | null;\n const servers = Array.isArray(body?.authorization_servers) ? (body!.authorization_servers as string[]) : [];\n if (servers.length === 0 || typeof servers[0] !== 'string') {\n return {\n url: '',\n status: 0,\n headers: {},\n body: null,\n error: 'No authorization_servers[0] found in protected-resource metadata',\n };\n }\n const issuer = servers[0].replace(/\\/$/, '');\n const metadataUrl = `${issuer}/.well-known/oauth-authorization-server`;\n return fetchProbe(metadataUrl, { allowPrivateIp: options.allowPrivateIp ?? false });\n}\n\n// ---------------------------------------------------------------------------\n// Fetch with guardrails\n// ---------------------------------------------------------------------------\n\nexport interface FetchProbeOptions {\n /** Allow http:// and private-IP destinations. Default false. */\n allowPrivateIp?: boolean;\n /** Override timeout for specific call sites. */\n timeoutMs?: number;\n}\n\n/**\n * Perform a GET against an attacker-influenceable URL with defensive limits.\n *\n * Guardrails (RFC 9728 / RFC 8414 metadata endpoints typically live on public\n * HTTPS; anything else is suspicious):\n * - Scheme: `https:` only by default; `http:` allowed only when\n * `allowPrivateIp` is set. `file:`, `ftp:`, `data:`, etc. are always rejected.\n * - DNS: resolves all A/AAAA records once, rejects if any is private, then\n * pins the outbound connection to the validated IP. Defeats DNS rebinding\n * where an attacker's authoritative nameserver returns a public address\n * to our guard lookup and a private address to the connect-time lookup.\n * - Private-IP block applies RFC 1918, loopback, link-local, IPv6 ULA,\n * CGNAT (100.64/10), multicast, broadcast, and IPv4-mapped IPv6.\n * - IMDS (169.254.169.254 / fe80::) stays blocked **even under\n * `allowPrivateIp`** — no legitimate dev use for probing it.\n * - Redirects are NOT followed (`redirect: 'manual'`).\n * - Body capped at 64 KiB, total fetch time capped at 10 s.\n */\nexport async function fetchProbe(url: string, options: FetchProbeOptions = {}): Promise<HttpProbeResult> {\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'GET',\n headers: { accept: 'application/json' },\n allowPrivateIp: options.allowPrivateIp ?? false,\n ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n });\n return {\n url,\n status: res.status,\n headers: res.headers,\n body: decodeBodyAsJsonOrText(res.body, res.headers['content-type']),\n };\n } catch (err) {\n return {\n url,\n status: 0,\n headers: {},\n body: null,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Credential generators (value_strategy)\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a per-run bogus API key. Prefix is human-readable for log grep;\n * the 32 random hex bytes guarantee no allowlist collision.\n */\nexport function generateRandomInvalidApiKey(): string {\n return `invalid-${randomBytes(32).toString('hex')}`;\n}\n\n/**\n * Generate a per-run bogus JWT-shaped Bearer token. Emits three segments with\n * valid base64url-encoded JSON header/payload and a random signature — so\n * well-implemented validators fail at signature verification (→ 401), and\n * strict parse-time validators that reject at the structural level also fail\n * cleanly (→ 400 per RFC 6750 §3.1). Either is conformant.\n */\nexport function generateRandomInvalidJwt(): string {\n const header = base64url(Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })));\n const payload = base64url(\n Buffer.from(JSON.stringify({ sub: `invalid-${randomBytes(8).toString('hex')}`, aud: 'invalid-probe' }))\n );\n const signature = base64url(randomBytes(32));\n return `${header}.${payload}.${signature}`;\n}\n\nfunction base64url(buf: Buffer): string {\n return buf.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n// ---------------------------------------------------------------------------\n// Raw-MCP probe (auth-override dispatch)\n// ---------------------------------------------------------------------------\n\nlet probeRequestId = 0;\nconst MCP_SESSION_ID_HEADER = 'mcp-session-id';\nconst MCP_PROTOCOL_VERSION_HEADER = 'mcp-protocol-version';\n\ntype JsonRpcEnvelope = {\n jsonrpc: '2.0';\n id?: number;\n method?: string;\n params?: Record<string, unknown>;\n result?: { structuredContent?: unknown; content?: unknown; isError?: boolean; protocolVersion?: string };\n error?: { message?: string; code?: number };\n};\n\ninterface RawJsonRpcPostResult {\n httpResult: HttpProbeResult;\n parsed?: JsonRpcEnvelope;\n parseError?: boolean;\n}\n\nasync function postRawMcpJsonRpc(options: {\n agentUrl: string;\n envelope: Record<string, unknown>;\n headers: Record<string, string>;\n allowPrivateIp: boolean;\n sessionId?: string;\n protocolVersion?: string;\n responseId?: number;\n parseBody?: boolean;\n allowEmptyBody?: boolean;\n}): Promise<RawJsonRpcPostResult> {\n const {\n agentUrl,\n envelope,\n headers,\n allowPrivateIp,\n sessionId,\n protocolVersion,\n responseId,\n parseBody = true,\n allowEmptyBody = false,\n } = options;\n const httpResult: HttpProbeResult = { url: agentUrl, status: 0, headers: {}, body: null };\n try {\n const res = await ssrfSafeFetch(agentUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json, text/event-stream',\n ...withoutMcpSessionHeaders(headers),\n ...(sessionId ? { [MCP_SESSION_ID_HEADER]: sessionId } : {}),\n ...(protocolVersion ? { [MCP_PROTOCOL_VERSION_HEADER]: protocolVersion } : {}),\n },\n body: JSON.stringify(envelope),\n allowPrivateIp,\n });\n httpResult.status = res.status;\n httpResult.headers = res.headers;\n\n const text = Buffer.from(res.body.buffer, res.body.byteOffset, res.body.byteLength).toString('utf8');\n if (!parseBody) {\n httpResult.body = text || null;\n return { httpResult };\n }\n if (allowEmptyBody && text.trim() === '') {\n httpResult.body = null;\n return { httpResult };\n }\n try {\n const parsed = parseMcpJsonRpcResponse(text, httpResult.headers['content-type'], responseId);\n httpResult.body = parsed;\n return { httpResult, parsed };\n } catch {\n httpResult.body = text;\n return { httpResult, parseError: true };\n }\n } catch (err) {\n httpResult.error = err instanceof Error ? err.message : String(err);\n return { httpResult };\n }\n}\n\nfunction withoutMcpSessionHeaders(headers: Record<string, string>): Record<string, string> {\n const cleaned: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n const normalized = key.toLowerCase();\n if (normalized === MCP_SESSION_ID_HEADER || normalized === MCP_PROTOCOL_VERSION_HEADER) continue;\n cleaned[key] = value;\n }\n return cleaned;\n}\n\nfunction parseMcpJsonRpcResponse(text: string, contentType: string | undefined, responseId?: number): JsonRpcEnvelope {\n if (contentType?.toLowerCase().includes('text/event-stream')) {\n // Streamable-HTTP MCP: the response is one or more SSE events whose\n // `data:` payloads are JSON-RPC envelopes. The spec lets a server emit\n // notifications before the final response, so choose the matching id.\n const dataLines = text.split(/\\r?\\n/).filter(l => l.startsWith('data:'));\n if (dataLines.length === 0) throw new Error('SSE response with no data event');\n let matched: JsonRpcEnvelope | undefined;\n let lastParsed: JsonRpcEnvelope | undefined;\n for (const line of dataLines) {\n const payload = line.slice('data:'.length).trim();\n if (!payload) continue;\n try {\n const envelope = JSON.parse(payload) as JsonRpcEnvelope;\n lastParsed = envelope;\n if (responseId !== undefined && envelope?.id === responseId) {\n matched = envelope;\n break;\n }\n } catch {\n // Skip non-JSON data lines (heartbeats, etc.); keep walking.\n }\n }\n const parsed = matched ?? lastParsed;\n if (parsed === undefined) throw new Error('SSE response had data events but none were parseable JSON');\n return parsed;\n }\n return JSON.parse(text) as JsonRpcEnvelope;\n}\n\nfunction taskResultFromRpc(httpResult: HttpProbeResult, rpc: JsonRpcEnvelope): TaskResult {\n if (httpResult.status >= 400) {\n return {\n success: false,\n data: undefined,\n error: rpc.error?.message ?? `HTTP ${httpResult.status}`,\n _extraction_path: 'error',\n };\n }\n if (rpc.error) {\n const code = rpc.error.code;\n return {\n success: false,\n data: undefined,\n error:\n code !== undefined\n ? `JSON-RPC error ${code}: ${rpc.error.message ?? 'no message'}`\n : (rpc.error.message ?? 'JSON-RPC error (no code)'),\n _extraction_path: 'error',\n };\n }\n const structured = rpc.result?.structuredContent;\n const hasStructured = structured !== undefined && structured !== null;\n const data = hasStructured ? structured : rpc.result?.content;\n const isError = !!rpc.result?.isError;\n const extractionPath: 'structured_content' | 'text_fallback' | 'error' | 'none' = isError\n ? 'error'\n : hasStructured\n ? 'structured_content'\n : data !== undefined && data !== null\n ? 'text_fallback'\n : 'none';\n return { success: !isError, data, _extraction_path: extractionPath };\n}\n\nfunction failedTaskResult(message: string): TaskResult {\n return {\n success: false,\n data: undefined,\n error: message,\n _extraction_path: 'error',\n };\n}\n\nfunction taskResultFromPostFailure(posted: RawJsonRpcPostResult): TaskResult {\n if (posted.parseError) {\n return failedTaskResult(\n `Non-JSON response body (content-type: ${posted.httpResult.headers['content-type'] ?? 'unknown'}).`\n );\n }\n if (posted.parsed) return taskResultFromRpc(posted.httpResult, posted.parsed);\n return failedTaskResult(posted.httpResult.error ?? `HTTP ${posted.httpResult.status}`);\n}\n\n/**\n * POST a JSON-RPC `tools/call` request to the MCP endpoint with caller-provided\n * headers. The probe first performs the Streamable HTTP initialize handshake,\n * including `notifications/initialized`, so auth probes hit the same session\n * boundary as normal MCP clients while still exposing raw HTTP status and\n * `WWW-Authenticate` headers for security storyboards.\n *\n * **Args are not secret** — must not contain credentials or PII. The server's\n * response body lands in `httpResult.body` and is written to compliance\n * reports. Outbound request body is not persisted.\n *\n * Returns an HttpProbeResult plus a synthetic TaskResult for steps that also\n * want to validate body shape — the structuredContent is unwrapped so\n * `field_present: \"context\"` resolves naturally.\n */\nexport async function rawMcpProbe(options: {\n agentUrl: string;\n toolName: string;\n args: Record<string, unknown>;\n headers?: Record<string, string>;\n /** Allow http:// and private-IP agent URLs (dev loops). Default false. */\n allowPrivateIp?: boolean;\n}): Promise<{ httpResult: HttpProbeResult; taskResult?: TaskResult }> {\n const { agentUrl, toolName, args, headers = {}, allowPrivateIp = false } = options;\n const initializeId = ++probeRequestId;\n const initialize = await postRawMcpJsonRpc({\n agentUrl,\n envelope: {\n jsonrpc: '2.0',\n id: initializeId,\n method: 'initialize',\n params: {\n protocolVersion: LATEST_PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: 'AdCP Storyboard Raw MCP Probe', version: '1.0.0' },\n },\n },\n headers,\n allowPrivateIp,\n responseId: initializeId,\n });\n if (\n initialize.httpResult.error ||\n initialize.httpResult.status >= 400 ||\n !initialize.parsed ||\n initialize.parsed.error\n ) {\n return {\n httpResult: initialize.httpResult,\n taskResult: taskResultFromPostFailure(initialize),\n };\n }\n const negotiatedProtocolVersion = initialize.parsed.result?.protocolVersion;\n if (\n typeof negotiatedProtocolVersion !== 'string' ||\n !SUPPORTED_PROTOCOL_VERSIONS.includes(negotiatedProtocolVersion)\n ) {\n return {\n httpResult: initialize.httpResult,\n taskResult: failedTaskResult(\n negotiatedProtocolVersion\n ? `Server's protocol version is not supported: ${negotiatedProtocolVersion}`\n : 'Server sent invalid initialize result: missing protocolVersion'\n ),\n };\n }\n\n const sessionId = initialize.httpResult.headers[MCP_SESSION_ID_HEADER];\n const initialized = await postRawMcpJsonRpc({\n agentUrl,\n envelope: {\n jsonrpc: '2.0',\n method: 'notifications/initialized',\n params: {},\n },\n headers,\n allowPrivateIp,\n ...(sessionId && { sessionId }),\n protocolVersion: negotiatedProtocolVersion,\n allowEmptyBody: true,\n });\n if (\n initialized.httpResult.error ||\n initialized.httpResult.status >= 400 ||\n initialized.parseError ||\n initialized.parsed?.error\n ) {\n return {\n httpResult: initialized.httpResult,\n taskResult: taskResultFromPostFailure(initialized),\n };\n }\n\n const requestId = ++probeRequestId;\n const toolEnvelope = {\n jsonrpc: '2.0',\n id: requestId,\n method: 'tools/call',\n params: { name: toolName, arguments: args },\n };\n const posted = await postRawMcpJsonRpc({\n agentUrl,\n envelope: toolEnvelope,\n headers,\n allowPrivateIp,\n ...(sessionId && { sessionId }),\n protocolVersion: negotiatedProtocolVersion,\n responseId: requestId,\n });\n const { httpResult, parsed } = posted;\n if (httpResult.error) return { httpResult, taskResult: taskResultFromPostFailure(posted) };\n if (!parsed) {\n return {\n httpResult,\n taskResult: taskResultFromPostFailure(posted),\n };\n }\n return { httpResult, taskResult: taskResultFromRpc(httpResult, parsed) };\n}\n\n// ---------------------------------------------------------------------------\n// Raw-A2A probe (transport-layer diagnostics for A2A agents)\n// ---------------------------------------------------------------------------\n\n/**\n * POST a JSON-RPC 2.0 request to an A2A agent endpoint with caller-provided\n * headers. Bypasses the A2A SDK so raw HTTP status, headers, and JSON-RPC\n * error codes can be captured by storyboard diagnostics.\n *\n * Mirrors `rawMcpProbe` in structure and SSRF-safety contract. Key\n * differences from the MCP variant:\n *\n * - The caller supplies `method` + optional `params` (not a fixed `tools/call`\n * body). A2A has no single canonical method — use `\"message/send\"` for most\n * auth and error-code probes, `\"tasks/get\"` / `\"tasks/cancel\"` for lifecycle\n * checks. **Note:** `message/send` requires `params.message` (a full A2A\n * `Message` object with `messageId`, `role`, `kind`, `parts`). Passing\n * `params: {}` will produce `-32602 Invalid params` from a conformant server,\n * masking auth rejections — supply a minimal message when probing auth paths.\n * - A2A JSON-RPC error codes differ from MCP's. Notably `-32002` means\n * `TaskNotCancelable` in A2A (not session-not-initialized). The probe\n * surfaces raw numeric codes without protocol-specific aliasing so\n * storyboards can assert on the exact code.\n * - SSE (streaming) responses are handled the same way as `rawMcpProbe`:\n * `Accept: application/json` is sent; non-JSON bodies surface a distinct\n * error so callers don't mistake an event-stream for a silent success.\n *\n * **Args are not secret** — must not contain credentials or PII. The\n * server's response body lands in `httpResult.body` and is written to\n * compliance reports.\n *\n * Returns an `HttpProbeResult` plus an optional `TaskResult` (same shape as\n * `rawMcpProbe`) so the storyboard `ValidationContext` can consume both probes\n * interchangeably. The A2A success `_extraction_path` is `'text_fallback'`\n * (not `'structured_content'`) because A2A's `result` field is a plain object,\n * not an MCP structured-content envelope.\n */\nexport async function rawA2aProbe(options: {\n /** Base URL of the A2A agent endpoint (e.g. `https://agent.example.com/a2a`). */\n agentUrl: string;\n /** A2A/JSON-RPC 2.0 method name (e.g. `\"message/send\"`, `\"tasks/get\"`). */\n method: string;\n /** JSON-RPC params. Defaults to `{}` so the probe always emits a valid envelope. */\n params?: Record<string, unknown>;\n headers?: Record<string, string>;\n /** Allow http:// and private-IP agent URLs (dev loops). Default false. */\n allowPrivateIp?: boolean;\n}): Promise<{ httpResult: HttpProbeResult; taskResult?: TaskResult }> {\n const { agentUrl, method, params, headers = {}, allowPrivateIp = false } = options;\n const body = JSON.stringify({\n jsonrpc: '2.0',\n id: ++probeRequestId,\n method,\n params: params ?? {},\n });\n\n const httpResult: HttpProbeResult = { url: agentUrl, status: 0, headers: {}, body: null };\n try {\n const res = await ssrfSafeFetch(agentUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json',\n ...headers,\n },\n body,\n allowPrivateIp,\n });\n httpResult.status = res.status;\n httpResult.headers = res.headers;\n\n const text = Buffer.from(res.body.buffer, res.body.byteOffset, res.body.byteLength).toString('utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n httpResult.body = text;\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error: `Non-JSON response body (content-type: ${httpResult.headers['content-type'] ?? 'unknown'}).`,\n _extraction_path: 'error',\n },\n };\n }\n httpResult.body = parsed;\n\n const rpc = parsed as {\n result?: unknown;\n error?: { message?: string; code?: number };\n };\n\n if (httpResult.status >= 400) {\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error: rpc.error?.message ?? `HTTP ${httpResult.status}`,\n _extraction_path: 'error',\n },\n };\n }\n\n if (rpc.error) {\n const code = rpc.error.code;\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error:\n code !== undefined\n ? `JSON-RPC error ${code}: ${rpc.error.message ?? 'no message'}`\n : (rpc.error.message ?? 'JSON-RPC error (no code)'),\n _extraction_path: 'error',\n },\n };\n }\n\n const data = rpc.result;\n const extractionPath: 'text_fallback' | 'none' = data !== undefined && data !== null ? 'text_fallback' : 'none';\n return { httpResult, taskResult: { success: true, data, _extraction_path: extractionPath } };\n } catch (err) {\n httpResult.error = err instanceof Error ? err.message : String(err);\n return { httpResult };\n }\n}\n\n// IP classifiers live in `src/lib/net/address-guards.ts` so the SSRF-safe\n// fetch primitive can use them without depending on the testing module.\n// Re-exported here for existing import sites (storyboard-security test + any\n// external probe consumers).\nexport const isAlwaysBlocked = sharedIsAlwaysBlocked;\nexport const isPrivateIp = sharedIsPrivateIp;\nexport { SsrfRefusedError };\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/testing/storyboard/probes.ts"],"sourcesContent":["/**\n * HTTP probes used by security-baseline storyboard tasks.\n *\n * Three synthetic tasks dispatch through here instead of the MCP client:\n *\n * - `protected_resource_metadata` — GET the agent's\n * `/.well-known/oauth-protected-resource<mountPath>` and verify RFC 9728.\n * - `oauth_auth_server_metadata` — GET `<issuer>/.well-known/oauth-authorization-server`\n * using the first issuer from the previous step's response. Hardened against\n * SSRF because the URL comes from agent-controlled data.\n * - `assert_contribution` — no network; evaluates accumulated flags set by\n * prior steps that carried `contributes_to`.\n */\nimport { randomBytes } from 'crypto';\nimport { LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } from '@modelcontextprotocol/sdk/types.js';\nimport {\n ssrfSafeFetch,\n decodeBodyAsJsonOrText,\n SsrfRefusedError,\n isAlwaysBlocked as sharedIsAlwaysBlocked,\n isPrivateIp as sharedIsPrivateIp,\n} from '../../net';\nimport type { HttpProbeResult } from './types';\nimport type { TaskResult } from '../types';\n\n// Timeout + body-cap defaults come from `ssrfSafeFetch` (10 s, 64 KiB).\n// The probe wrappers deliberately don't override them so probe behavior\n// stays in sync with the shared primitive.\n\n/** Task names dispatched via HTTP probes (not via the MCP client). */\nexport const PROBE_TASKS = new Set([\n 'protected_resource_metadata',\n 'oauth_auth_server_metadata',\n 'assert_contribution',\n 'request_signing_probe',\n 'fetch_brand_jwks',\n 'assert_jwks_purpose',\n 'expect_rate_limit_not_replayed',\n]);\n\n// ---------------------------------------------------------------------------\n// Protected-resource metadata probe\n// ---------------------------------------------------------------------------\n\n/**\n * GET `<agentUrl origin>/.well-known/oauth-protected-resource<agentUrl path>`.\n * Same-origin as the agent, so SSRF risk is bounded.\n *\n * When `allowPrivateIp` is set (matches the runner's `--allow-http` flag),\n * loopback and RFC 1918 targets are allowed so dev loops against localhost\n * agents work end-to-end.\n */\nexport async function probeProtectedResourceMetadata(\n agentUrl: string,\n options: { allowPrivateIp?: boolean; fetchFn?: typeof fetch } = {}\n): Promise<HttpProbeResult> {\n const u = new URL(agentUrl);\n const metadataUrl = `${u.origin}/.well-known/oauth-protected-resource${u.pathname}`;\n return fetchProbe(metadataUrl, {\n allowPrivateIp: options.allowPrivateIp ?? false,\n fetchFn: options.fetchFn,\n });\n}\n\n// ---------------------------------------------------------------------------\n// OAuth authorization-server metadata probe\n// ---------------------------------------------------------------------------\n\n/**\n * GET `<issuer>/.well-known/oauth-authorization-server` for the first issuer\n * named in the protected-resource metadata. Because the URL is agent-supplied,\n * this is the SSRF-hot path — {@link fetchProbe} rejects private networks,\n * non-https schemes, and unbounded responses.\n */\nexport async function probeOauthAuthServerMetadata(\n priorProbe: HttpProbeResult | undefined,\n options: { allowPrivateIp?: boolean; fetchFn?: typeof fetch } = {}\n): Promise<HttpProbeResult> {\n if (!priorProbe || priorProbe.error) {\n return {\n url: '',\n status: 0,\n headers: {},\n body: null,\n error: 'protected_resource_metadata step missing or errored — cannot resolve issuer',\n };\n }\n const body = priorProbe.body as { authorization_servers?: unknown } | null;\n const servers = Array.isArray(body?.authorization_servers) ? (body!.authorization_servers as string[]) : [];\n if (servers.length === 0 || typeof servers[0] !== 'string') {\n return {\n url: '',\n status: 0,\n headers: {},\n body: null,\n error: 'No authorization_servers[0] found in protected-resource metadata',\n };\n }\n const issuer = servers[0].replace(/\\/$/, '');\n const metadataUrl = `${issuer}/.well-known/oauth-authorization-server`;\n return fetchProbe(metadataUrl, {\n allowPrivateIp: options.allowPrivateIp ?? false,\n fetchFn: options.fetchFn,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Fetch with guardrails\n// ---------------------------------------------------------------------------\n\nexport interface FetchProbeOptions {\n /** Allow http:// and private-IP destinations. Default false. */\n allowPrivateIp?: boolean;\n /** Override timeout for specific call sites. */\n timeoutMs?: number;\n /** Trusted scoped fetch; must enforce DNS-rebinding protection. */\n fetchFn?: typeof fetch;\n}\n\n/**\n * Perform a GET against an attacker-influenceable URL with defensive limits.\n *\n * Guardrails (RFC 9728 / RFC 8414 metadata endpoints typically live on public\n * HTTPS; anything else is suspicious):\n * - Scheme: `https:` only by default; `http:` allowed only when\n * `allowPrivateIp` is set. `file:`, `ftp:`, `data:`, etc. are always rejected.\n * - DNS: resolves all A/AAAA records once, rejects if any is private, then\n * pins the outbound connection to the validated IP. Defeats DNS rebinding\n * where an attacker's authoritative nameserver returns a public address\n * to our guard lookup and a private address to the connect-time lookup.\n * - Private-IP block applies RFC 1918, loopback, link-local, IPv6 ULA,\n * CGNAT (100.64/10), multicast, broadcast, and IPv4-mapped IPv6.\n * - IMDS (169.254.169.254 / fe80::) stays blocked **even under\n * `allowPrivateIp`** — no legitimate dev use for probing it.\n * - Redirects are NOT followed (`redirect: 'manual'`).\n * - Body capped at 64 KiB, total fetch time capped at 10 s.\n */\nexport async function fetchProbe(url: string, options: FetchProbeOptions = {}): Promise<HttpProbeResult> {\n try {\n const res = await ssrfSafeFetch(url, {\n method: 'GET',\n headers: { accept: 'application/json' },\n allowPrivateIp: options.allowPrivateIp ?? false,\n ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n ...(options.fetchFn ? { trustedFetchFn: options.fetchFn } : {}),\n });\n return {\n url,\n status: res.status,\n headers: res.headers,\n body: decodeBodyAsJsonOrText(res.body, res.headers['content-type']),\n };\n } catch (err) {\n return {\n url,\n status: 0,\n headers: {},\n body: null,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Credential generators (value_strategy)\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a per-run bogus API key. Prefix is human-readable for log grep;\n * the 32 random hex bytes guarantee no allowlist collision.\n */\nexport function generateRandomInvalidApiKey(): string {\n return `invalid-${randomBytes(32).toString('hex')}`;\n}\n\n/**\n * Generate a per-run bogus JWT-shaped Bearer token. Emits three segments with\n * valid base64url-encoded JSON header/payload and a random signature — so\n * well-implemented validators fail at signature verification (→ 401), and\n * strict parse-time validators that reject at the structural level also fail\n * cleanly (→ 400 per RFC 6750 §3.1). Either is conformant.\n */\nexport function generateRandomInvalidJwt(): string {\n const header = base64url(Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })));\n const payload = base64url(\n Buffer.from(JSON.stringify({ sub: `invalid-${randomBytes(8).toString('hex')}`, aud: 'invalid-probe' }))\n );\n const signature = base64url(randomBytes(32));\n return `${header}.${payload}.${signature}`;\n}\n\nfunction base64url(buf: Buffer): string {\n return buf.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n// ---------------------------------------------------------------------------\n// Raw-MCP probe (auth-override dispatch)\n// ---------------------------------------------------------------------------\n\nlet probeRequestId = 0;\nconst MCP_SESSION_ID_HEADER = 'mcp-session-id';\nconst MCP_PROTOCOL_VERSION_HEADER = 'mcp-protocol-version';\n\ntype JsonRpcEnvelope = {\n jsonrpc: '2.0';\n id?: number;\n method?: string;\n params?: Record<string, unknown>;\n result?: { structuredContent?: unknown; content?: unknown; isError?: boolean; protocolVersion?: string };\n error?: { message?: string; code?: number };\n};\n\ninterface RawJsonRpcPostResult {\n httpResult: HttpProbeResult;\n parsed?: JsonRpcEnvelope;\n parseError?: boolean;\n}\n\nasync function postRawMcpJsonRpc(options: {\n agentUrl: string;\n envelope: Record<string, unknown>;\n headers: Record<string, string>;\n allowPrivateIp: boolean;\n sessionId?: string;\n protocolVersion?: string;\n responseId?: number;\n parseBody?: boolean;\n allowEmptyBody?: boolean;\n fetchFn?: typeof fetch;\n}): Promise<RawJsonRpcPostResult> {\n const {\n agentUrl,\n envelope,\n headers,\n allowPrivateIp,\n sessionId,\n protocolVersion,\n responseId,\n parseBody = true,\n allowEmptyBody = false,\n } = options;\n const httpResult: HttpProbeResult = { url: agentUrl, status: 0, headers: {}, body: null };\n try {\n const res = await ssrfSafeFetch(agentUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json, text/event-stream',\n ...withoutMcpSessionHeaders(headers),\n ...(sessionId ? { [MCP_SESSION_ID_HEADER]: sessionId } : {}),\n ...(protocolVersion ? { [MCP_PROTOCOL_VERSION_HEADER]: protocolVersion } : {}),\n },\n body: JSON.stringify(envelope),\n allowPrivateIp,\n ...(options.fetchFn ? { trustedFetchFn: options.fetchFn } : {}),\n });\n httpResult.status = res.status;\n httpResult.headers = res.headers;\n\n const text = Buffer.from(res.body.buffer, res.body.byteOffset, res.body.byteLength).toString('utf8');\n if (!parseBody) {\n httpResult.body = text || null;\n return { httpResult };\n }\n if (allowEmptyBody && text.trim() === '') {\n httpResult.body = null;\n return { httpResult };\n }\n try {\n const parsed = parseMcpJsonRpcResponse(text, httpResult.headers['content-type'], responseId);\n httpResult.body = parsed;\n return { httpResult, parsed };\n } catch {\n httpResult.body = text;\n return { httpResult, parseError: true };\n }\n } catch (err) {\n httpResult.error = err instanceof Error ? err.message : String(err);\n return { httpResult };\n }\n}\n\nfunction withoutMcpSessionHeaders(headers: Record<string, string>): Record<string, string> {\n const cleaned: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n const normalized = key.toLowerCase();\n if (normalized === MCP_SESSION_ID_HEADER || normalized === MCP_PROTOCOL_VERSION_HEADER) continue;\n cleaned[key] = value;\n }\n return cleaned;\n}\n\nfunction parseMcpJsonRpcResponse(text: string, contentType: string | undefined, responseId?: number): JsonRpcEnvelope {\n if (contentType?.toLowerCase().includes('text/event-stream')) {\n // Streamable-HTTP MCP: the response is one or more SSE events whose\n // `data:` payloads are JSON-RPC envelopes. The spec lets a server emit\n // notifications before the final response, so choose the matching id.\n const dataLines = text.split(/\\r?\\n/).filter(l => l.startsWith('data:'));\n if (dataLines.length === 0) throw new Error('SSE response with no data event');\n let matched: JsonRpcEnvelope | undefined;\n let lastParsed: JsonRpcEnvelope | undefined;\n for (const line of dataLines) {\n const payload = line.slice('data:'.length).trim();\n if (!payload) continue;\n try {\n const envelope = JSON.parse(payload) as JsonRpcEnvelope;\n lastParsed = envelope;\n if (responseId !== undefined && envelope?.id === responseId) {\n matched = envelope;\n break;\n }\n } catch {\n // Skip non-JSON data lines (heartbeats, etc.); keep walking.\n }\n }\n const parsed = matched ?? lastParsed;\n if (parsed === undefined) throw new Error('SSE response had data events but none were parseable JSON');\n return parsed;\n }\n return JSON.parse(text) as JsonRpcEnvelope;\n}\n\nfunction taskResultFromRpc(httpResult: HttpProbeResult, rpc: JsonRpcEnvelope): TaskResult {\n if (httpResult.status >= 400) {\n return {\n success: false,\n data: undefined,\n error: rpc.error?.message ?? `HTTP ${httpResult.status}`,\n _extraction_path: 'error',\n };\n }\n if (rpc.error) {\n const code = rpc.error.code;\n return {\n success: false,\n data: undefined,\n error:\n code !== undefined\n ? `JSON-RPC error ${code}: ${rpc.error.message ?? 'no message'}`\n : (rpc.error.message ?? 'JSON-RPC error (no code)'),\n _extraction_path: 'error',\n };\n }\n const structured = rpc.result?.structuredContent;\n const hasStructured = structured !== undefined && structured !== null;\n const data = hasStructured ? structured : rpc.result?.content;\n const isError = !!rpc.result?.isError;\n const extractionPath: 'structured_content' | 'text_fallback' | 'error' | 'none' = isError\n ? 'error'\n : hasStructured\n ? 'structured_content'\n : data !== undefined && data !== null\n ? 'text_fallback'\n : 'none';\n return { success: !isError, data, _extraction_path: extractionPath };\n}\n\nfunction failedTaskResult(message: string): TaskResult {\n return {\n success: false,\n data: undefined,\n error: message,\n _extraction_path: 'error',\n };\n}\n\nfunction taskResultFromPostFailure(posted: RawJsonRpcPostResult): TaskResult {\n if (posted.parseError) {\n return failedTaskResult(\n `Non-JSON response body (content-type: ${posted.httpResult.headers['content-type'] ?? 'unknown'}).`\n );\n }\n if (posted.parsed) return taskResultFromRpc(posted.httpResult, posted.parsed);\n return failedTaskResult(posted.httpResult.error ?? `HTTP ${posted.httpResult.status}`);\n}\n\n/**\n * POST a JSON-RPC `tools/call` request to the MCP endpoint with caller-provided\n * headers. The probe first performs the Streamable HTTP initialize handshake,\n * including `notifications/initialized`, so auth probes hit the same session\n * boundary as normal MCP clients while still exposing raw HTTP status and\n * `WWW-Authenticate` headers for security storyboards.\n *\n * **Args are not secret** — must not contain credentials or PII. The server's\n * response body lands in `httpResult.body` and is written to compliance\n * reports. Outbound request body is not persisted.\n *\n * Returns an HttpProbeResult plus a synthetic TaskResult for steps that also\n * want to validate body shape — the structuredContent is unwrapped so\n * `field_present: \"context\"` resolves naturally.\n */\nexport async function rawMcpProbe(options: {\n agentUrl: string;\n toolName: string;\n args: Record<string, unknown>;\n headers?: Record<string, string>;\n /** Allow http:// and private-IP agent URLs (dev loops). Default false. */\n allowPrivateIp?: boolean;\n /** Scoped fetch implementation for every handshake and tool request. */\n fetchFn?: typeof fetch;\n}): Promise<{ httpResult: HttpProbeResult; taskResult?: TaskResult }> {\n const { agentUrl, toolName, args, headers = {}, allowPrivateIp = false, fetchFn } = options;\n const initializeId = ++probeRequestId;\n const initialize = await postRawMcpJsonRpc({\n agentUrl,\n envelope: {\n jsonrpc: '2.0',\n id: initializeId,\n method: 'initialize',\n params: {\n protocolVersion: LATEST_PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: 'AdCP Storyboard Raw MCP Probe', version: '1.0.0' },\n },\n },\n headers,\n allowPrivateIp,\n fetchFn,\n responseId: initializeId,\n });\n if (\n initialize.httpResult.error ||\n initialize.httpResult.status >= 400 ||\n !initialize.parsed ||\n initialize.parsed.error\n ) {\n return {\n httpResult: initialize.httpResult,\n taskResult: taskResultFromPostFailure(initialize),\n };\n }\n const negotiatedProtocolVersion = initialize.parsed.result?.protocolVersion;\n if (\n typeof negotiatedProtocolVersion !== 'string' ||\n !SUPPORTED_PROTOCOL_VERSIONS.includes(negotiatedProtocolVersion)\n ) {\n return {\n httpResult: initialize.httpResult,\n taskResult: failedTaskResult(\n negotiatedProtocolVersion\n ? `Server's protocol version is not supported: ${negotiatedProtocolVersion}`\n : 'Server sent invalid initialize result: missing protocolVersion'\n ),\n };\n }\n\n const sessionId = initialize.httpResult.headers[MCP_SESSION_ID_HEADER];\n const initialized = await postRawMcpJsonRpc({\n agentUrl,\n envelope: {\n jsonrpc: '2.0',\n method: 'notifications/initialized',\n params: {},\n },\n headers,\n allowPrivateIp,\n fetchFn,\n ...(sessionId && { sessionId }),\n protocolVersion: negotiatedProtocolVersion,\n allowEmptyBody: true,\n });\n if (\n initialized.httpResult.error ||\n initialized.httpResult.status >= 400 ||\n initialized.parseError ||\n initialized.parsed?.error\n ) {\n return {\n httpResult: initialized.httpResult,\n taskResult: taskResultFromPostFailure(initialized),\n };\n }\n\n const requestId = ++probeRequestId;\n const toolEnvelope = {\n jsonrpc: '2.0',\n id: requestId,\n method: 'tools/call',\n params: { name: toolName, arguments: args },\n };\n const posted = await postRawMcpJsonRpc({\n agentUrl,\n envelope: toolEnvelope,\n headers,\n allowPrivateIp,\n fetchFn,\n ...(sessionId && { sessionId }),\n protocolVersion: negotiatedProtocolVersion,\n responseId: requestId,\n });\n const { httpResult, parsed } = posted;\n if (httpResult.error) return { httpResult, taskResult: taskResultFromPostFailure(posted) };\n if (!parsed) {\n return {\n httpResult,\n taskResult: taskResultFromPostFailure(posted),\n };\n }\n return { httpResult, taskResult: taskResultFromRpc(httpResult, parsed) };\n}\n\n// ---------------------------------------------------------------------------\n// Raw-A2A probe (transport-layer diagnostics for A2A agents)\n// ---------------------------------------------------------------------------\n\n/**\n * POST a JSON-RPC 2.0 request to an A2A agent endpoint with caller-provided\n * headers. Bypasses the A2A SDK so raw HTTP status, headers, and JSON-RPC\n * error codes can be captured by storyboard diagnostics.\n *\n * Mirrors `rawMcpProbe` in structure and SSRF-safety contract. Key\n * differences from the MCP variant:\n *\n * - The caller supplies `method` + optional `params` (not a fixed `tools/call`\n * body). A2A has no single canonical method — use `\"message/send\"` for most\n * auth and error-code probes, `\"tasks/get\"` / `\"tasks/cancel\"` for lifecycle\n * checks. **Note:** `message/send` requires `params.message` (a full A2A\n * `Message` object with `messageId`, `role`, `kind`, `parts`). Passing\n * `params: {}` will produce `-32602 Invalid params` from a conformant server,\n * masking auth rejections — supply a minimal message when probing auth paths.\n * - A2A JSON-RPC error codes differ from MCP's. Notably `-32002` means\n * `TaskNotCancelable` in A2A (not session-not-initialized). The probe\n * surfaces raw numeric codes without protocol-specific aliasing so\n * storyboards can assert on the exact code.\n * - SSE (streaming) responses are handled the same way as `rawMcpProbe`:\n * `Accept: application/json` is sent; non-JSON bodies surface a distinct\n * error so callers don't mistake an event-stream for a silent success.\n *\n * **Args are not secret** — must not contain credentials or PII. The\n * server's response body lands in `httpResult.body` and is written to\n * compliance reports.\n *\n * Returns an `HttpProbeResult` plus an optional `TaskResult` (same shape as\n * `rawMcpProbe`) so the storyboard `ValidationContext` can consume both probes\n * interchangeably. The A2A success `_extraction_path` is `'text_fallback'`\n * (not `'structured_content'`) because A2A's `result` field is a plain object,\n * not an MCP structured-content envelope.\n */\nexport async function rawA2aProbe(options: {\n /** Base URL of the A2A agent endpoint (e.g. `https://agent.example.com/a2a`). */\n agentUrl: string;\n /** A2A/JSON-RPC 2.0 method name (e.g. `\"message/send\"`, `\"tasks/get\"`). */\n method: string;\n /** JSON-RPC params. Defaults to `{}` so the probe always emits a valid envelope. */\n params?: Record<string, unknown>;\n headers?: Record<string, string>;\n /** Allow http:// and private-IP agent URLs (dev loops). Default false. */\n allowPrivateIp?: boolean;\n}): Promise<{ httpResult: HttpProbeResult; taskResult?: TaskResult }> {\n const { agentUrl, method, params, headers = {}, allowPrivateIp = false } = options;\n const body = JSON.stringify({\n jsonrpc: '2.0',\n id: ++probeRequestId,\n method,\n params: params ?? {},\n });\n\n const httpResult: HttpProbeResult = { url: agentUrl, status: 0, headers: {}, body: null };\n try {\n const res = await ssrfSafeFetch(agentUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json',\n ...headers,\n },\n body,\n allowPrivateIp,\n });\n httpResult.status = res.status;\n httpResult.headers = res.headers;\n\n const text = Buffer.from(res.body.buffer, res.body.byteOffset, res.body.byteLength).toString('utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n httpResult.body = text;\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error: `Non-JSON response body (content-type: ${httpResult.headers['content-type'] ?? 'unknown'}).`,\n _extraction_path: 'error',\n },\n };\n }\n httpResult.body = parsed;\n\n const rpc = parsed as {\n result?: unknown;\n error?: { message?: string; code?: number };\n };\n\n if (httpResult.status >= 400) {\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error: rpc.error?.message ?? `HTTP ${httpResult.status}`,\n _extraction_path: 'error',\n },\n };\n }\n\n if (rpc.error) {\n const code = rpc.error.code;\n return {\n httpResult,\n taskResult: {\n success: false,\n data: undefined,\n error:\n code !== undefined\n ? `JSON-RPC error ${code}: ${rpc.error.message ?? 'no message'}`\n : (rpc.error.message ?? 'JSON-RPC error (no code)'),\n _extraction_path: 'error',\n },\n };\n }\n\n const data = rpc.result;\n const extractionPath: 'text_fallback' | 'none' = data !== undefined && data !== null ? 'text_fallback' : 'none';\n return { httpResult, taskResult: { success: true, data, _extraction_path: extractionPath } };\n } catch (err) {\n httpResult.error = err instanceof Error ? err.message : String(err);\n return { httpResult };\n }\n}\n\n// IP classifiers live in `src/lib/net/address-guards.ts` so the SSRF-safe\n// fetch primitive can use them without depending on the testing module.\n// Re-exported here for existing import sites (storyboard-security test + any\n// external probe consumers).\nexport const isAlwaysBlocked = sharedIsAlwaysBlocked;\nexport const isPrivateIp = sharedIsPrivateIp;\nexport { SsrfRefusedError };\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../../src/lib/testing/storyboard/runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA+EH,OAAO,KAAK,EAeV,2BAA2B,EAG3B,UAAU,EACV,cAAc,EAEd,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAEhB,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EAErB,uBAAuB,EACvB,qBAAqB,EAGtB,MAAM,SAAS,CAAC;AASjB,OAAO,KAAK,EAA4B,cAAc,EAAE,MAAM,UAAU,CAAC;AAoCzE,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,oBAAoB,GAC5B,oBAAoB,CAEtB;AAED,wBAAgB,0BAA0B,CACxC,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,OAAO,EAAE,oBAAoB,GAC5B,oBAAoB,CAUtB;AAsED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAS/E;AAoCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,2BAA2B,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CA2ClH;AA+kBD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE9D;AAED,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAC1C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAEpC;AAED,wBAAgB,sCAAsC,CACpD,OAAO,EAAE,oBAAoB,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAEpC;AAiCD;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,cAAc,EAAE,MAAM,GAAG,MAAM,EAAE,EACjC,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,gBAAgB,CAAC,CAS3B;AAiqBD;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EAAE,EACnB,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,cAAc,GAC5B,gBAAgB,CA8ClB;AA4qDD;;;;;;;;;;;;;;;;GAgBG;AACH;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,qBAAqB,EAAE,GAC9B,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,qBAAqB,EAAE,CAAA;CAAE,CAAC,CA0B9G;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,uBAAuB,CA4BlG;AAMD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,oBAAoB,CAAC,CAU/B;
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../../src/lib/testing/storyboard/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -2348,7 +2348,8 @@ async function executeStep(client, step, storyboardId, phaseId, context, allStep
|
|
|
2348
2348
|
toolName: effectiveStep.task,
|
|
2349
2349
|
args: request,
|
|
2350
2350
|
headers: rawProbeHeaders,
|
|
2351
|
-
allowPrivateIp: options.allow_http === true
|
|
2351
|
+
allowPrivateIp: options.allow_http === true,
|
|
2352
|
+
fetchFn: options.transport?.fetchFn
|
|
2352
2353
|
});
|
|
2353
2354
|
httpResult = probe.httpResult;
|
|
2354
2355
|
taskResult = probe.taskResult;
|
|
@@ -2776,7 +2777,10 @@ async function executeStep(client, step, storyboardId, phaseId, context, allStep
|
|
|
2776
2777
|
async function executeProbeStep(client, step, phaseId, context, allSteps, options, runState) {
|
|
2777
2778
|
const start = Date.now();
|
|
2778
2779
|
let httpResult;
|
|
2779
|
-
const probeOpts = {
|
|
2780
|
+
const probeOpts = {
|
|
2781
|
+
allowPrivateIp: options.allow_http === true,
|
|
2782
|
+
fetchFn: options.transport?.fetchFn
|
|
2783
|
+
};
|
|
2780
2784
|
let requestRecordOverride;
|
|
2781
2785
|
if (step.requires_contract) {
|
|
2782
2786
|
const contracts = new Set(options.contracts ?? []);
|