@adcp/sdk 13.0.4 → 13.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/bin/adcp-grade.js +4 -3
  2. package/bin/adcp.js +18 -0
  3. package/dist/lib/conformance/invariants/uniformError.d.ts.map +1 -1
  4. package/dist/lib/conformance/invariants/uniformError.js +21 -0
  5. package/dist/lib/conformance/invariants/uniformError.js.map +1 -1
  6. package/dist/lib/conformance/invariants/uniformError.mjs +22 -1
  7. package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
  8. package/dist/lib/core/AgentClient.d.mts +1 -1
  9. package/dist/lib/core/AgentClient.d.ts +1 -1
  10. package/dist/lib/core/AgentClient.d.ts.map +1 -1
  11. package/dist/lib/core/AgentClient.js +1 -1
  12. package/dist/lib/core/AgentClient.js.map +1 -1
  13. package/dist/lib/core/AgentClient.mjs +1 -1
  14. package/dist/lib/core/AgentClient.mjs.map +1 -1
  15. package/dist/lib/core/SingleAgentClient.d.mts +1 -1
  16. package/dist/lib/core/SingleAgentClient.d.ts +1 -1
  17. package/dist/lib/core/SingleAgentClient.d.ts.map +1 -1
  18. package/dist/lib/core/SingleAgentClient.js +88 -48
  19. package/dist/lib/core/SingleAgentClient.js.map +1 -1
  20. package/dist/lib/core/SingleAgentClient.mjs +88 -48
  21. package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
  22. package/dist/lib/core/TaskExecutor.d.ts.map +1 -1
  23. package/dist/lib/core/TaskExecutor.js +8 -4
  24. package/dist/lib/core/TaskExecutor.js.map +1 -1
  25. package/dist/lib/core/TaskExecutor.mjs +8 -4
  26. package/dist/lib/core/TaskExecutor.mjs.map +1 -1
  27. package/dist/lib/index.d.mts +1 -1
  28. package/dist/lib/index.d.ts +1 -1
  29. package/dist/lib/index.d.ts.map +1 -1
  30. package/dist/lib/index.js.map +1 -1
  31. package/dist/lib/index.mjs.map +1 -1
  32. package/dist/lib/net/agent-transport-fetch.d.mts +15 -0
  33. package/dist/lib/net/agent-transport-fetch.d.ts +15 -0
  34. package/dist/lib/net/agent-transport-fetch.d.ts.map +1 -1
  35. package/dist/lib/net/agent-transport-fetch.js +37 -2
  36. package/dist/lib/net/agent-transport-fetch.js.map +1 -1
  37. package/dist/lib/net/agent-transport-fetch.mjs +37 -2
  38. package/dist/lib/net/agent-transport-fetch.mjs.map +1 -1
  39. package/dist/lib/net/credential-headers.d.mts +1 -0
  40. package/dist/lib/net/credential-headers.d.ts +2 -0
  41. package/dist/lib/net/credential-headers.d.ts.map +1 -0
  42. package/dist/lib/net/credential-headers.js +34 -0
  43. package/dist/lib/net/credential-headers.js.map +1 -0
  44. package/dist/lib/net/credential-headers.mjs +10 -0
  45. package/dist/lib/net/credential-headers.mjs.map +1 -0
  46. package/dist/lib/protocols/a2a-native-v1.d.mts +18 -0
  47. package/dist/lib/protocols/a2a-native-v1.d.ts +19 -0
  48. package/dist/lib/protocols/a2a-native-v1.d.ts.map +1 -0
  49. package/dist/lib/protocols/a2a-native-v1.js +183 -0
  50. package/dist/lib/protocols/a2a-native-v1.js.map +1 -0
  51. package/dist/lib/protocols/a2a-native-v1.mjs +167 -0
  52. package/dist/lib/protocols/a2a-native-v1.mjs.map +1 -0
  53. package/dist/lib/protocols/a2a-push-notification.d.mts +13 -0
  54. package/dist/lib/protocols/a2a-push-notification.d.ts +14 -0
  55. package/dist/lib/protocols/a2a-push-notification.d.ts.map +1 -0
  56. package/dist/lib/protocols/a2a-push-notification.js +42 -0
  57. package/dist/lib/protocols/a2a-push-notification.js.map +1 -0
  58. package/dist/lib/protocols/a2a-push-notification.mjs +23 -0
  59. package/dist/lib/protocols/a2a-push-notification.mjs.map +1 -0
  60. package/dist/lib/protocols/a2a.d.mts +15 -5
  61. package/dist/lib/protocols/a2a.d.ts +15 -5
  62. package/dist/lib/protocols/a2a.d.ts.map +1 -1
  63. package/dist/lib/protocols/a2a.js +102 -13
  64. package/dist/lib/protocols/a2a.js.map +1 -1
  65. package/dist/lib/protocols/a2a.mjs +90 -12
  66. package/dist/lib/protocols/a2a.mjs.map +1 -1
  67. package/dist/lib/protocols/index.d.mts +3 -0
  68. package/dist/lib/protocols/index.d.ts +3 -0
  69. package/dist/lib/protocols/index.d.ts.map +1 -1
  70. package/dist/lib/protocols/index.js +18 -6
  71. package/dist/lib/protocols/index.js.map +1 -1
  72. package/dist/lib/protocols/index.mjs +18 -6
  73. package/dist/lib/protocols/index.mjs.map +1 -1
  74. package/dist/lib/protocols/mcp-modern.d.ts.map +1 -1
  75. package/dist/lib/protocols/mcp-modern.js +3 -1
  76. package/dist/lib/protocols/mcp-modern.js.map +1 -1
  77. package/dist/lib/protocols/mcp-modern.mjs +3 -1
  78. package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
  79. package/dist/lib/protocols/mcp-tasks.d.ts.map +1 -1
  80. package/dist/lib/protocols/mcp-tasks.js +2 -1
  81. package/dist/lib/protocols/mcp-tasks.js.map +1 -1
  82. package/dist/lib/protocols/mcp-tasks.mjs +2 -1
  83. package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
  84. package/dist/lib/protocols/mcp.d.mts +8 -5
  85. package/dist/lib/protocols/mcp.d.ts +8 -5
  86. package/dist/lib/protocols/mcp.d.ts.map +1 -1
  87. package/dist/lib/protocols/mcp.js +12 -4
  88. package/dist/lib/protocols/mcp.js.map +1 -1
  89. package/dist/lib/protocols/mcp.mjs +12 -4
  90. package/dist/lib/protocols/mcp.mjs.map +1 -1
  91. package/dist/lib/protocols/rawResponseCapture.d.mts +9 -1
  92. package/dist/lib/protocols/rawResponseCapture.d.ts +9 -1
  93. package/dist/lib/protocols/rawResponseCapture.d.ts.map +1 -1
  94. package/dist/lib/protocols/rawResponseCapture.js +153 -10
  95. package/dist/lib/protocols/rawResponseCapture.js.map +1 -1
  96. package/dist/lib/protocols/rawResponseCapture.mjs +153 -10
  97. package/dist/lib/protocols/rawResponseCapture.mjs.map +1 -1
  98. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  99. package/dist/lib/signing/agent-fetch.d.mts +3 -1
  100. package/dist/lib/signing/agent-fetch.d.ts +3 -1
  101. package/dist/lib/signing/agent-fetch.d.ts.map +1 -1
  102. package/dist/lib/signing/agent-fetch.js +4 -2
  103. package/dist/lib/signing/agent-fetch.js.map +1 -1
  104. package/dist/lib/signing/agent-fetch.mjs +4 -2
  105. package/dist/lib/signing/agent-fetch.mjs.map +1 -1
  106. package/dist/lib/signing/protocol-methods.d.mts +24 -0
  107. package/dist/lib/signing/protocol-methods.d.ts +25 -0
  108. package/dist/lib/signing/protocol-methods.d.ts.map +1 -0
  109. package/dist/lib/signing/protocol-methods.js +51 -0
  110. package/dist/lib/signing/protocol-methods.js.map +1 -0
  111. package/dist/lib/signing/protocol-methods.mjs +25 -0
  112. package/dist/lib/signing/protocol-methods.mjs.map +1 -0
  113. package/dist/lib/signing/types.d.mts +2 -0
  114. package/dist/lib/signing/types.d.ts +2 -0
  115. package/dist/lib/signing/types.d.ts.map +1 -1
  116. package/dist/lib/signing/types.js.map +1 -1
  117. package/dist/lib/signing/types.mjs.map +1 -1
  118. package/dist/lib/signing/verifier.d.ts.map +1 -1
  119. package/dist/lib/signing/verifier.js +6 -2
  120. package/dist/lib/signing/verifier.js.map +1 -1
  121. package/dist/lib/signing/verifier.mjs +6 -2
  122. package/dist/lib/signing/verifier.mjs.map +1 -1
  123. package/dist/lib/testing/client.d.ts.map +1 -1
  124. package/dist/lib/testing/client.js +4 -1
  125. package/dist/lib/testing/client.js.map +1 -1
  126. package/dist/lib/testing/client.mjs +4 -1
  127. package/dist/lib/testing/client.mjs.map +1 -1
  128. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  129. package/dist/lib/testing/compliance/comply.js +3 -0
  130. package/dist/lib/testing/compliance/comply.js.map +1 -1
  131. package/dist/lib/testing/compliance/comply.mjs +3 -0
  132. package/dist/lib/testing/compliance/comply.mjs.map +1 -1
  133. package/dist/lib/testing/storyboard/agent-routing.d.mts +3 -1
  134. package/dist/lib/testing/storyboard/agent-routing.d.ts +3 -1
  135. package/dist/lib/testing/storyboard/agent-routing.d.ts.map +1 -1
  136. package/dist/lib/testing/storyboard/agent-routing.js +5 -2
  137. package/dist/lib/testing/storyboard/agent-routing.js.map +1 -1
  138. package/dist/lib/testing/storyboard/agent-routing.mjs +4 -2
  139. package/dist/lib/testing/storyboard/agent-routing.mjs.map +1 -1
  140. package/dist/lib/testing/storyboard/compliance.d.mts +1 -1
  141. package/dist/lib/testing/storyboard/compliance.d.ts +1 -1
  142. package/dist/lib/testing/storyboard/compliance.d.ts.map +1 -1
  143. package/dist/lib/testing/storyboard/compliance.js +4 -1
  144. package/dist/lib/testing/storyboard/compliance.js.map +1 -1
  145. package/dist/lib/testing/storyboard/compliance.mjs +4 -1
  146. package/dist/lib/testing/storyboard/compliance.mjs.map +1 -1
  147. package/dist/lib/testing/storyboard/native-a2a-compliance.d.mts +7 -0
  148. package/dist/lib/testing/storyboard/native-a2a-compliance.d.ts +8 -0
  149. package/dist/lib/testing/storyboard/native-a2a-compliance.d.ts.map +1 -0
  150. package/dist/lib/testing/storyboard/native-a2a-compliance.js +45 -0
  151. package/dist/lib/testing/storyboard/native-a2a-compliance.js.map +1 -0
  152. package/dist/lib/testing/storyboard/native-a2a-compliance.mjs +21 -0
  153. package/dist/lib/testing/storyboard/native-a2a-compliance.mjs.map +1 -0
  154. package/dist/lib/testing/storyboard/probes.d.mts +2 -0
  155. package/dist/lib/testing/storyboard/probes.d.ts +2 -0
  156. package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
  157. package/dist/lib/testing/storyboard/probes.js +3 -2
  158. package/dist/lib/testing/storyboard/probes.js.map +1 -1
  159. package/dist/lib/testing/storyboard/probes.mjs +3 -2
  160. package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
  161. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.mts +31 -0
  162. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.ts +32 -0
  163. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.ts.map +1 -0
  164. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.js +279 -0
  165. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.js.map +1 -0
  166. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.mjs +244 -0
  167. package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.mjs.map +1 -0
  168. package/dist/lib/testing/storyboard/request-signing/builder.d.mts +4 -1
  169. package/dist/lib/testing/storyboard/request-signing/builder.d.ts +4 -1
  170. package/dist/lib/testing/storyboard/request-signing/builder.d.ts.map +1 -1
  171. package/dist/lib/testing/storyboard/request-signing/builder.js +19 -0
  172. package/dist/lib/testing/storyboard/request-signing/builder.js.map +1 -1
  173. package/dist/lib/testing/storyboard/request-signing/builder.mjs +19 -0
  174. package/dist/lib/testing/storyboard/request-signing/builder.mjs.map +1 -1
  175. package/dist/lib/testing/storyboard/request-signing/grader.d.mts +5 -6
  176. package/dist/lib/testing/storyboard/request-signing/grader.d.ts +5 -6
  177. package/dist/lib/testing/storyboard/request-signing/grader.d.ts.map +1 -1
  178. package/dist/lib/testing/storyboard/request-signing/grader.js +82 -13
  179. package/dist/lib/testing/storyboard/request-signing/grader.js.map +1 -1
  180. package/dist/lib/testing/storyboard/request-signing/grader.mjs +86 -13
  181. package/dist/lib/testing/storyboard/request-signing/grader.mjs.map +1 -1
  182. package/dist/lib/testing/storyboard/request-signing/index.d.mts +1 -0
  183. package/dist/lib/testing/storyboard/request-signing/index.d.ts +1 -0
  184. package/dist/lib/testing/storyboard/request-signing/index.d.ts.map +1 -1
  185. package/dist/lib/testing/storyboard/request-signing/index.js +7 -0
  186. package/dist/lib/testing/storyboard/request-signing/index.js.map +1 -1
  187. package/dist/lib/testing/storyboard/request-signing/index.mjs +8 -0
  188. package/dist/lib/testing/storyboard/request-signing/index.mjs.map +1 -1
  189. package/dist/lib/testing/storyboard/request-signing/probe-dispatch.d.mts +2 -2
  190. package/dist/lib/testing/storyboard/request-signing/probe-dispatch.d.ts +2 -2
  191. package/dist/lib/testing/storyboard/request-signing/probe-dispatch.d.ts.map +1 -1
  192. package/dist/lib/testing/storyboard/request-signing/probe-dispatch.js +6 -5
  193. package/dist/lib/testing/storyboard/request-signing/probe-dispatch.js.map +1 -1
  194. package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs +6 -5
  195. package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs.map +1 -1
  196. package/dist/lib/testing/storyboard/request-signing/types.d.mts +1 -0
  197. package/dist/lib/testing/storyboard/request-signing/types.d.ts +1 -0
  198. package/dist/lib/testing/storyboard/request-signing/types.d.ts.map +1 -1
  199. package/dist/lib/testing/storyboard/request-signing/types.js.map +1 -1
  200. package/dist/lib/testing/storyboard/request-signing/types.mjs.map +1 -1
  201. package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts.map +1 -1
  202. package/dist/lib/testing/storyboard/request-signing/vector-loader.js +1 -0
  203. package/dist/lib/testing/storyboard/request-signing/vector-loader.js.map +1 -1
  204. package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +1 -0
  205. package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs.map +1 -1
  206. package/dist/lib/testing/storyboard/runner.d.mts +28 -1
  207. package/dist/lib/testing/storyboard/runner.d.ts +28 -1
  208. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  209. package/dist/lib/testing/storyboard/runner.js +251 -55
  210. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  211. package/dist/lib/testing/storyboard/runner.mjs +255 -60
  212. package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
  213. package/dist/lib/testing/storyboard/types.d.mts +4 -2
  214. package/dist/lib/testing/storyboard/types.d.ts +4 -2
  215. package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
  216. package/dist/lib/testing/storyboard/types.js +1 -0
  217. package/dist/lib/testing/storyboard/types.js.map +1 -1
  218. package/dist/lib/testing/storyboard/types.mjs +1 -0
  219. package/dist/lib/testing/storyboard/types.mjs.map +1 -1
  220. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  221. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  222. package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
  223. package/dist/lib/testing/storyboard/validations.js +97 -22
  224. package/dist/lib/testing/storyboard/validations.js.map +1 -1
  225. package/dist/lib/testing/storyboard/validations.mjs +98 -22
  226. package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
  227. package/dist/lib/version.d.mts +3 -3
  228. package/dist/lib/version.d.ts +3 -3
  229. package/dist/lib/version.js +3 -3
  230. package/dist/lib/version.js.map +1 -1
  231. package/dist/lib/version.mjs +3 -3
  232. package/dist/lib/version.mjs.map +1 -1
  233. package/docs/guides/BUILD-AN-AGENT.md +1 -1
  234. package/docs/llms.txt +5 -3
  235. package/package.json +2 -1
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var native_a2a_compliance_exports = {};
20
+ __export(native_a2a_compliance_exports, {
21
+ applyNativeA2AComplianceTransportOptions: () => applyNativeA2AComplianceTransportOptions
22
+ });
23
+ module.exports = __toCommonJS(native_a2a_compliance_exports);
24
+ var import_protocols = require('../../protocols/index.js');
25
+ function applyNativeA2AComplianceTransportOptions(options) {
26
+ const transport = (0, import_protocols.normalizeTransportOptions)(options.transport);
27
+ if (options.protocol !== "a2a") {
28
+ return transport === options.transport ? options : { ...options, transport };
29
+ }
30
+ if (transport?.legacyCompat !== void 0) {
31
+ return transport === options.transport ? options : { ...options, transport };
32
+ }
33
+ return {
34
+ ...options,
35
+ transport: {
36
+ ...transport,
37
+ legacyCompat: { enabled: false }
38
+ }
39
+ };
40
+ }
41
+ // Annotate the CommonJS export names for ESM import in node:
42
+ 0 && (module.exports = {
43
+ applyNativeA2AComplianceTransportOptions
44
+ });
45
+ //# sourceMappingURL=native-a2a-compliance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/lib/testing/storyboard/native-a2a-compliance.ts"],"sourcesContent":["import { normalizeTransportOptions } from '../../protocols';\nimport type { StoryboardRunOptions } from './types';\n\n/**\n * Default compliance runs to native A2A 1.0. An explicit compatibility\n * policy is preserved so 13.x can still regression-test its maintained 0.3\n * public server adapter without changing that adapter's wire contract.\n */\nexport function applyNativeA2AComplianceTransportOptions(options: StoryboardRunOptions): StoryboardRunOptions {\n const transport = normalizeTransportOptions(options.transport);\n if (options.protocol !== 'a2a') {\n return transport === options.transport ? options : { ...options, transport };\n }\n if (transport?.legacyCompat !== undefined) {\n return transport === options.transport ? options : { ...options, transport };\n }\n return {\n ...options,\n transport: {\n ...transport,\n legacyCompat: { enabled: false },\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAA0C;AAQnC,SAAS,yCAAyC,SAAqD;AAC5G,QAAM,gBAAY,4CAA0B,QAAQ,SAAS;AAC7D,MAAI,QAAQ,aAAa,OAAO;AAC9B,WAAO,cAAc,QAAQ,YAAY,UAAU,EAAE,GAAG,SAAS,UAAU;AAAA,EAC7E;AACA,MAAI,WAAW,iBAAiB,QAAW;AACzC,WAAO,cAAc,QAAQ,YAAY,UAAU,EAAE,GAAG,SAAS,UAAU;AAAA,EAC7E;AACA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,WAAW;AAAA,MACT,GAAG;AAAA,MACH,cAAc,EAAE,SAAS,MAAM;AAAA,IACjC;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,21 @@
1
+ import { normalizeTransportOptions } from "../../protocols/index.mjs";
2
+ function applyNativeA2AComplianceTransportOptions(options) {
3
+ const transport = normalizeTransportOptions(options.transport);
4
+ if (options.protocol !== "a2a") {
5
+ return transport === options.transport ? options : { ...options, transport };
6
+ }
7
+ if (transport?.legacyCompat !== void 0) {
8
+ return transport === options.transport ? options : { ...options, transport };
9
+ }
10
+ return {
11
+ ...options,
12
+ transport: {
13
+ ...transport,
14
+ legacyCompat: { enabled: false }
15
+ }
16
+ };
17
+ }
18
+ export {
19
+ applyNativeA2AComplianceTransportOptions
20
+ };
21
+ //# sourceMappingURL=native-a2a-compliance.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/lib/testing/storyboard/native-a2a-compliance.ts"],"sourcesContent":["import { normalizeTransportOptions } from '../../protocols';\nimport type { StoryboardRunOptions } from './types';\n\n/**\n * Default compliance runs to native A2A 1.0. An explicit compatibility\n * policy is preserved so 13.x can still regression-test its maintained 0.3\n * public server adapter without changing that adapter's wire contract.\n */\nexport function applyNativeA2AComplianceTransportOptions(options: StoryboardRunOptions): StoryboardRunOptions {\n const transport = normalizeTransportOptions(options.transport);\n if (options.protocol !== 'a2a') {\n return transport === options.transport ? options : { ...options, transport };\n }\n if (transport?.legacyCompat !== undefined) {\n return transport === options.transport ? options : { ...options, transport };\n }\n return {\n ...options,\n transport: {\n ...transport,\n legacyCompat: { enabled: false },\n },\n };\n}\n"],"mappings":"AAAA,SAAS,iCAAiC;AAQnC,SAAS,yCAAyC,SAAqD;AAC5G,QAAM,YAAY,0BAA0B,QAAQ,SAAS;AAC7D,MAAI,QAAQ,aAAa,OAAO;AAC9B,WAAO,cAAc,QAAQ,YAAY,UAAU,EAAE,GAAG,SAAS,UAAU;AAAA,EAC7E;AACA,MAAI,WAAW,iBAAiB,QAAW;AACzC,WAAO,cAAc,QAAQ,YAAY,UAAU,EAAE,GAAG,SAAS,UAAU;AAAA,EAC7E;AACA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,WAAW;AAAA,MACT,GAAG;AAAA,MACH,cAAc,EAAE,SAAS,MAAM;AAAA,IACjC;AAAA,EACF;AACF;","names":[]}
@@ -136,6 +136,8 @@ export declare function rawA2aProbe(options: {
136
136
  headers?: Record<string, string>;
137
137
  /** Allow http:// and private-IP agent URLs (dev loops). Default false. */
138
138
  allowPrivateIp?: boolean;
139
+ /** Trusted scoped fetch; must enforce DNS-rebinding protection. */
140
+ fetchFn?: typeof fetch;
139
141
  }): Promise<{
140
142
  httpResult: HttpProbeResult;
141
143
  taskResult?: TaskResult;
@@ -136,6 +136,8 @@ export declare function rawA2aProbe(options: {
136
136
  headers?: Record<string, string>;
137
137
  /** Allow http:// and private-IP agent URLs (dev loops). Default false. */
138
138
  allowPrivateIp?: boolean;
139
+ /** Trusted scoped fetch; must enforce DNS-rebinding protection. */
140
+ fetchFn?: typeof fetch;
139
141
  }): Promise<{
140
142
  httpResult: HttpProbeResult;
141
143
  taskResult?: TaskResult;
@@ -1 +1 @@
1
- {"version":3,"file":"probes.d.ts","sourceRoot":"","sources":["../../../../src/lib/testing/storyboard/probes.ts"],"names":[],"mappings":"AAeA,OAAO,EAGL,gBAAgB,EAChB,eAAe,IAAI,qBAAqB,EACxC,WAAW,IAAI,iBAAiB,EACjC,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAM3C,sEAAsE;AACtE,eAAO,MAAM,WAAW,aAatB,CAAC;AAMH;;;;;;;GAOG;AACH,wBAAsB,8BAA8B,CAClD,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GACjE,OAAO,CAAC,eAAe,CAAC,CAO1B;AAMD;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,eAAe,GAAG,SAAS,EACvC,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GACjE,OAAO,CAAC,eAAe,CAAC,CA2B1B;AAMD,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC,CAwBvG;AAMD;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAOjD;AA2LD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,0EAA0E;IAC1E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,eAAe,CAAC;IAAC,UAAU,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,CAmGpE;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE;IACzC,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,0EAA0E;IAC1E,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,eAAe,CAAC;IAAC,UAAU,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,CAkFpE;AAMD,eAAO,MAAM,eAAe,8BAAwB,CAAC;AACrD,eAAO,MAAM,WAAW,0BAAoB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"probes.d.ts","sourceRoot":"","sources":["../../../../src/lib/testing/storyboard/probes.ts"],"names":[],"mappings":"AAeA,OAAO,EAGL,gBAAgB,EAChB,eAAe,IAAI,qBAAqB,EACxC,WAAW,IAAI,iBAAiB,EACjC,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAM3C,sEAAsE;AACtE,eAAO,MAAM,WAAW,aAatB,CAAC;AAMH;;;;;;;GAOG;AACH,wBAAsB,8BAA8B,CAClD,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GACjE,OAAO,CAAC,eAAe,CAAC,CAO1B;AAMD;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,eAAe,GAAG,SAAS,EACvC,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GACjE,OAAO,CAAC,eAAe,CAAC,CA2B1B;AAMD,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC,CAwBvG;AAMD;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAOjD;AA2LD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,0EAA0E;IAC1E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,eAAe,CAAC;IAAC,UAAU,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,CAmGpE;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE;IACzC,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,0EAA0E;IAC1E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,eAAe,CAAC;IAAC,UAAU,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,CAmFpE;AAMD,eAAO,MAAM,eAAe,8BAAwB,CAAC;AACrD,eAAO,MAAM,WAAW,0BAAoB,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -337,7 +337,7 @@ async function rawMcpProbe(options) {
337
337
  return { httpResult, taskResult: taskResultFromRpc(httpResult, parsed) };
338
338
  }
339
339
  async function rawA2aProbe(options) {
340
- const { agentUrl, method, params, headers = {}, allowPrivateIp = false } = options;
340
+ const { agentUrl, method, params, headers = {}, allowPrivateIp = false, fetchFn } = options;
341
341
  const body = JSON.stringify({
342
342
  jsonrpc: "2.0",
343
343
  id: ++probeRequestId,
@@ -354,7 +354,8 @@ async function rawA2aProbe(options) {
354
354
  ...headers
355
355
  },
356
356
  body,
357
- allowPrivateIp
357
+ allowPrivateIp,
358
+ ...fetchFn ? { trustedFetchFn: fetchFn } : {}
358
359
  });
359
360
  httpResult.status = res.status;
360
361
  httpResult.headers = res.headers;
@@ -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 'replay_trusted_match_context_vector',\n 'trusted_match_missing_auth_context_probe',\n 'trusted_match_invalid_auth_context_probe',\n 'trusted_match_missing_auth_identity_probe',\n 'trusted_match_invalid_auth_identity_probe',\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"]}
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 'replay_trusted_match_context_vector',\n 'trusted_match_missing_auth_context_probe',\n 'trusted_match_invalid_auth_context_probe',\n 'trusted_match_missing_auth_identity_probe',\n 'trusted_match_invalid_auth_identity_probe',\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 /** Trusted scoped fetch; must enforce DNS-rebinding protection. */\n fetchFn?: typeof fetch;\n}): Promise<{ httpResult: HttpProbeResult; taskResult?: TaskResult }> {\n const { agentUrl, method, params, headers = {}, allowPrivateIp = false, fetchFn } = 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 ...(fetchFn ? { trustedFetchFn: 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 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"]}
@@ -310,7 +310,7 @@ async function rawMcpProbe(options) {
310
310
  return { httpResult, taskResult: taskResultFromRpc(httpResult, parsed) };
311
311
  }
312
312
  async function rawA2aProbe(options) {
313
- const { agentUrl, method, params, headers = {}, allowPrivateIp = false } = options;
313
+ const { agentUrl, method, params, headers = {}, allowPrivateIp = false, fetchFn } = options;
314
314
  const body = JSON.stringify({
315
315
  jsonrpc: "2.0",
316
316
  id: ++probeRequestId,
@@ -327,7 +327,8 @@ async function rawA2aProbe(options) {
327
327
  ...headers
328
328
  },
329
329
  body,
330
- allowPrivateIp
330
+ allowPrivateIp,
331
+ ...fetchFn ? { trustedFetchFn: fetchFn } : {}
331
332
  });
332
333
  httpResult.status = res.status;
333
334
  httpResult.headers = res.headers;
@@ -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 'replay_trusted_match_context_vector',\n 'trusted_match_missing_auth_context_probe',\n 'trusted_match_invalid_auth_context_probe',\n 'trusted_match_missing_auth_identity_probe',\n 'trusted_match_invalid_auth_identity_probe',\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
+ {"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 'replay_trusted_match_context_vector',\n 'trusted_match_missing_auth_context_probe',\n 'trusted_match_invalid_auth_context_probe',\n 'trusted_match_missing_auth_identity_probe',\n 'trusted_match_invalid_auth_identity_probe',\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 /** Trusted scoped fetch; must enforce DNS-rebinding protection. */\n fetchFn?: typeof fetch;\n}): Promise<{ httpResult: HttpProbeResult; taskResult?: TaskResult }> {\n const { agentUrl, method, params, headers = {}, allowPrivateIp = false, fetchFn } = 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 ...(fetchFn ? { trustedFetchFn: 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 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":[]}
@@ -0,0 +1,31 @@
1
+ export interface CapturedA2aRequest {
2
+ url: string;
3
+ method: string;
4
+ headers: Record<string, string>;
5
+ body: string;
6
+ }
7
+ export type A2aCall = {
8
+ kind: 'sendMessage';
9
+ operation: string;
10
+ args: Record<string, unknown>;
11
+ } | {
12
+ kind: 'cancelTask';
13
+ taskId: string;
14
+ };
15
+ export interface A2aDispatchOptions {
16
+ timeoutMs?: number;
17
+ allowPrivateIp?: boolean;
18
+ /** Trusted fetch used only for agent-card discovery. */
19
+ cardFetch?: typeof fetch;
20
+ }
21
+ export declare function captureA2aRequest(agentUrl: string, call: A2aCall, options?: A2aDispatchOptions): Promise<CapturedA2aRequest>;
22
+ /**
23
+ * Cache discovery within one grading run without turning the platform fetch
24
+ * into a trusted fetch override. The guarded transport performs DNS/private
25
+ * address enforcement before this wrapper sees a response.
26
+ */
27
+ export declare function createCachedA2aCardFetch(agentUrl: string, options?: A2aDispatchOptions): typeof fetch;
28
+ export declare function operationFromVectorUrl(vectorUrl: string): string;
29
+ export declare function resolveA2aDispatchTarget(agentUrl: string, options?: A2aDispatchOptions): Promise<{
30
+ endpoint: string;
31
+ }>;
@@ -0,0 +1,32 @@
1
+ export interface CapturedA2aRequest {
2
+ url: string;
3
+ method: string;
4
+ headers: Record<string, string>;
5
+ body: string;
6
+ }
7
+ export type A2aCall = {
8
+ kind: 'sendMessage';
9
+ operation: string;
10
+ args: Record<string, unknown>;
11
+ } | {
12
+ kind: 'cancelTask';
13
+ taskId: string;
14
+ };
15
+ export interface A2aDispatchOptions {
16
+ timeoutMs?: number;
17
+ allowPrivateIp?: boolean;
18
+ /** Trusted fetch used only for agent-card discovery. */
19
+ cardFetch?: typeof fetch;
20
+ }
21
+ export declare function captureA2aRequest(agentUrl: string, call: A2aCall, options?: A2aDispatchOptions): Promise<CapturedA2aRequest>;
22
+ /**
23
+ * Cache discovery within one grading run without turning the platform fetch
24
+ * into a trusted fetch override. The guarded transport performs DNS/private
25
+ * address enforcement before this wrapper sees a response.
26
+ */
27
+ export declare function createCachedA2aCardFetch(agentUrl: string, options?: A2aDispatchOptions): typeof fetch;
28
+ export declare function operationFromVectorUrl(vectorUrl: string): string;
29
+ export declare function resolveA2aDispatchTarget(agentUrl: string, options?: A2aDispatchOptions): Promise<{
30
+ endpoint: string;
31
+ }>;
32
+ //# sourceMappingURL=a2a-dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a2a-dispatch.d.ts","sourceRoot":"","sources":["../../../../../src/lib/testing/storyboard/request-signing/a2a-dispatch.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACzE;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3C,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wDAAwD;IACxD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAQD,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,kBAAkB,CAAC,CAgG7B;AA0CD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,KAAK,CA+BzG;AAqCD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAMhE;AAED,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAyB/B"}