@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
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/signing/verifier.ts"],"sourcesContent":["import { parseDictionary } from 'structured-headers';\nimport {\n buildSignatureBase,\n canonicalTargetUri,\n getHeaderValue,\n rejectNonAsciiHost,\n type RequestLike,\n} from './canonicalize';\nimport { contentDigestMatches } from './content-digest';\nimport { RequestSignatureError } from './errors';\nimport { parseSignature, parseSignatureInput, type ParsedSignatureInput } from './parser';\nimport { jwkToPublicKey, verifySignature } from './crypto';\nimport type { JwksResolver } from './jwks';\nimport type { ReplayStore } from './replay';\nimport type { RevocationStore } from './revocation';\nimport { containsWebhookAuthentication, WEBHOOK_AUTH_TRAVERSAL_DEPTH } from './webhook-auth-detection';\nimport {\n ALLOWED_ALGS,\n CLOCK_SKEW_TOLERANCE_SECONDS,\n MANDATORY_COMPONENTS,\n MAX_SIGNATURE_WINDOW_SECONDS,\n REQUEST_SIGNING_TAG,\n type VerifierCapability,\n type VerifyResult,\n} from './types';\n\nexport interface VerifyRequestOptions {\n capability: VerifierCapability;\n jwks: JwksResolver;\n replayStore: ReplayStore;\n revocationStore: RevocationStore;\n now?: () => number;\n /**\n * The AdCP operation being requested, used to consult\n * `capability.required_for` when an unsigned request arrives. When omitted,\n * the verifier treats the operation as \"not in any required_for list\" and\n * returns an unsigned result rather than rejecting — callers in\n * always-verify mode (where every request is signed) can leave this blank.\n */\n operation?: string;\n agentUrlForKeyid?: (keyid: string) => string | undefined;\n}\n\nexport async function verifyRequestSignature(\n request: RequestLike,\n options: VerifyRequestOptions\n): Promise<VerifyResult> {\n const now = options.now ? options.now() : Math.floor(Date.now() / 1000);\n const sigInputHeader = getHeaderValue(request.headers, 'Signature-Input');\n const sigHeader = getHeaderValue(request.headers, 'Signature');\n\n // Pre-check: both headers present or both absent.\n if (!sigInputHeader && !sigHeader) {\n const operation = options.operation;\n // Precedence is intentionally fail-specific: AdCP tool required_for,\n // raw JSON-RPC protocol methods, then payload-driven webhook-auth\n // elevation. The specific signed-only contract should win before the\n // generic body scan runs.\n if (operation && options.capability.required_for.includes(operation)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Operation \"${operation}\" requires a signed request`\n );\n }\n const protocolMethodsRequiredFor = options.capability.protocol_methods_required_for ?? [];\n if (protocolMethodsRequiredFor.length > 0 && exceedsUnsignedBodyInspectionCap(request.body)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Unsigned request body exceeds the protocol method inspection cap while protocol methods require signing'\n );\n }\n const protocolMethods = jsonRpcProtocolMethods(request.body);\n const requiredProtocolMethod = protocolMethods.find(method => protocolMethodsRequiredFor.includes(method));\n if (requiredProtocolMethod) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Protocol method \"${requiredProtocolMethod}\" requires a signed request`\n );\n }\n // Payload-driven elevation: any request carrying webhook receiver\n // credentials (task, reporting, artifact, or revocation callbacks) MUST\n // be RFC 9421 signed, regardless of whether the operation appears in\n // `required_for`\n // (#webhook-security downgrade-resistance). A bearer-only channel\n // would otherwise let an attacker who captured a token register or\n // update webhook credentials and redirect callbacks to a hostile URL.\n if (carriesWebhookAuthentication(request)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Requests carrying webhook authentication must be RFC 9421 signed'\n );\n }\n return { status: 'unsigned', verified_at: now };\n }\n if (!sigInputHeader || !sigHeader) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Signature and Signature-Input headers must be present as a pair'\n );\n }\n\n // Step 1: parse.\n const parsedInput = parseSignatureInput(sigInputHeader);\n const parsedSig = parseSignature(sigHeader, parsedInput.label);\n rejectNonAsciiHost(request.url);\n validateSingleValuedCoveredHeaders(parsedInput.components, request);\n\n // Step 2: required params present.\n requireParams(parsedInput);\n\n // Step 3: tag match.\n if (parsedInput.params.tag !== REQUEST_SIGNING_TAG) {\n throw new RequestSignatureError(\n 'request_signature_tag_invalid',\n 3,\n `Signature tag must be \"${REQUEST_SIGNING_TAG}\"; got \"${parsedInput.params.tag}\"`\n );\n }\n\n // Step 4: alg allowlist.\n if (!ALLOWED_ALGS.has(parsedInput.params.alg)) {\n throw new RequestSignatureError(\n 'request_signature_alg_not_allowed',\n 4,\n `Signature alg \"${parsedInput.params.alg}\" is not in the AdCP allowlist`\n );\n }\n\n // Step 5: window valid.\n validateWindow(parsedInput.params.created, parsedInput.params.expires, now);\n\n // Step 6: covered components.\n validateCoveredComponents(parsedInput.components, options.capability, request);\n\n // Step 7: resolve keyid.\n const jwk = await options.jwks.resolve(parsedInput.params.keyid);\n if (!jwk) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `No JWK found for keyid \"${parsedInput.params.keyid}\"`\n );\n }\n if (jwk.kid !== parsedInput.params.keyid) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `JWKS resolver returned a JWK whose kid \"${jwk.kid}\" does not match requested keyid \"${parsedInput.params.keyid}\"`\n );\n }\n\n // Step 8: key purpose.\n if (jwk.adcp_use !== 'request-signing' || !jwk.key_ops?.includes('verify')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" is not scoped for request-signing verification`\n );\n }\n validateJwkParameterConsistency(jwk, parsedInput.params.alg);\n\n // Step 9: revocation (runs BEFORE crypto to prevent amplification attacks).\n if (await options.revocationStore.isRevoked(jwk.kid)) {\n throw new RequestSignatureError('request_signature_key_revoked', 9, `JWK \"${jwk.kid}\" is revoked`);\n }\n\n // Replay cache is scoped by `(keyid, @target-uri)` per adcp#2460 — a\n // signature captured on one endpoint (e.g. /create_media_buy) MUST NOT\n // count against the replay budget for a different endpoint under the\n // same keyid. Canonicalize once and reuse for both pre-check and commit.\n const replayScope = canonicalTargetUri(request.url);\n\n // Step 12 pre-checks (rate-abuse cap + replay hit) run before crypto so a\n // compromised-key cache cap or a replayed nonce short-circuits an expensive\n // Ed25519/ECDSA verify. The committing insert happens after step 11.\n if (await options.replayStore.isCapHit(jwk.kid, replayScope, now)) {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n if (await options.replayStore.has(jwk.kid, replayScope, parsedInput.params.nonce, now)) {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n\n // Step 10: cryptographic verify. Use the received Signature-Input value\n // verbatim as the @signature-params line so a peer emitting params in a\n // different legal order still produces a byte-identical base.\n const base = buildSignatureBase(\n parsedInput.components,\n request,\n parsedInput.params,\n parsedInput.signatureParamsValue\n );\n const publicKey = jwkToPublicKey(jwk);\n const valid = verifySignature(parsedInput.params.alg, publicKey, Buffer.from(base, 'utf8'), parsedSig.bytes);\n if (!valid) {\n throw new RequestSignatureError(\n 'request_signature_invalid',\n 10,\n 'Cryptographic verification of signature base failed'\n );\n }\n\n // Step 11: content-digest match (only if covered).\n if (parsedInput.components.includes('content-digest')) {\n const digestHeader = getHeaderValue(request.headers, 'Content-Digest');\n if (!digestHeader || !contentDigestMatches(digestHeader, request.body ?? '')) {\n throw new RequestSignatureError(\n 'request_signature_digest_mismatch',\n 11,\n 'Content-Digest header does not match recomputed body hash'\n );\n }\n }\n\n // Step 12: commit the (keyid, nonce) into the replay cache now that all\n // prior checks have passed. Floor the TTL at one max-window + skew so a\n // signer minting tiny validity windows can't replay outside the configured\n // replay horizon, and so cross-replica eventual consistency has headroom.\n const remaining = parsedInput.params.expires - now + CLOCK_SKEW_TOLERANCE_SECONDS;\n const ttl = Math.max(remaining, MAX_SIGNATURE_WINDOW_SECONDS + CLOCK_SKEW_TOLERANCE_SECONDS);\n const insertResult = await options.replayStore.insert(jwk.kid, replayScope, parsedInput.params.nonce, ttl, now);\n if (insertResult === 'replayed') {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n if (insertResult === 'rate_abuse') {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n\n const agent_url = options.agentUrlForKeyid?.(jwk.kid);\n return { status: 'verified', keyid: jwk.kid, agent_url, verified_at: now };\n}\n\nfunction jsonRpcProtocolMethods(body: string | undefined): string[] {\n if (!body) return [];\n if (exceedsUnsignedBodyInspectionCap(body)) return [];\n try {\n const parsed = JSON.parse(body) as unknown;\n const messages = Array.isArray(parsed) ? parsed : [parsed];\n return messages.flatMap(message =>\n message !== null && typeof message === 'object' && typeof (message as { method?: unknown }).method === 'string'\n ? [(message as { method: string }).method]\n : []\n );\n } catch {\n return [];\n }\n}\n\nfunction requireParams(parsed: ParsedSignatureInput): void {\n const required: Array<keyof ParsedSignatureInput['params']> = ['created', 'expires', 'nonce', 'keyid', 'alg', 'tag'];\n const missing = required.filter(k => parsed.params[k] === undefined);\n if (missing.length) {\n throw new RequestSignatureError(\n 'request_signature_params_incomplete',\n 2,\n `Signature-Input missing required parameter(s): ${missing.join(', ')}`\n );\n }\n}\n\nfunction validateWindow(created: number, expires: number, now: number): void {\n if (expires <= created) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature expires must be strictly greater than created'\n );\n }\n if (expires - created > MAX_SIGNATURE_WINDOW_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n `Signature window exceeds ${MAX_SIGNATURE_WINDOW_SECONDS}s maximum`\n );\n }\n if (now > expires + CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError('request_signature_window_invalid', 5, 'Signature is expired');\n }\n if (now < created - CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature created is in the future beyond skew tolerance'\n );\n }\n}\n\n/**\n * Reject JWKs whose alg/kty/crv are mutually inconsistent OR don't match\n * the sig-params alg. Three checks in one place:\n *\n * 1. JWK MUST declare `alg`. A missing alg lets a signer substitute one\n * algorithm family for another (e.g. EdDSA sig-params against a P-256\n * JWK) and defer failure to step 10's crypto verify — the wrong step\n * and the wrong error code.\n * 2. JWK `alg` MUST match the sig-params `alg` after AdCP→JOSE mapping\n * (`ed25519`↔`EdDSA`, `ecdsa-p256-sha256`↔`ES256`). Prevents sig-params\n * alg-downgrade against a JWK that was published for a stronger alg.\n * 3. JWK kty/crv MUST be consistent with the declared alg per RFC 8037\n * (EdDSA→OKP/Ed25519|Ed448) and RFC 7518 (ES256→EC/P-256).\n */\nfunction validateJwkParameterConsistency(\n jwk: { kid: string; kty?: string; crv?: string; alg?: string },\n sigParamsAlg: string\n): void {\n if (!jwk.alg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" does not declare an alg; cannot bind to sig-params alg=\"${sigParamsAlg}\"`\n );\n }\n const expectedJoseAlg = SIG_PARAMS_ALG_TO_JOSE[sigParamsAlg];\n if (expectedJoseAlg && jwk.alg !== expectedJoseAlg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=${jwk.alg} but the request is signed with alg=\"${sigParamsAlg}\" (JOSE ${expectedJoseAlg})`\n );\n }\n if (jwk.alg === 'EdDSA') {\n if (jwk.kty !== 'OKP' || (jwk.crv !== 'Ed25519' && jwk.crv !== 'Ed448')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=EdDSA but kty/crv (${jwk.kty}/${jwk.crv}) is not a valid Edwards-curve pair`\n );\n }\n return;\n }\n if (jwk.alg === 'ES256') {\n if (jwk.kty !== 'EC' || jwk.crv !== 'P-256') {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=ES256 but kty/crv (${jwk.kty}/${jwk.crv}) is not EC/P-256`\n );\n }\n }\n}\n\nconst SIG_PARAMS_ALG_TO_JOSE: Record<string, string> = {\n ed25519: 'EdDSA',\n 'ecdsa-p256-sha256': 'ES256',\n};\n\n/**\n * Covered components referencing single-valued HTTP header fields\n * (Content-Type per RFC 9110, Content-Digest per RFC 9530) MUST NOT arrive\n * as a comma-joined multi-value. A proxy or buggy client emitting the field\n * twice produces a concatenated value whose meaning relative to the\n * signature base is undefined — reject at parse rather than pick one.\n */\nfunction validateSingleValuedCoveredHeaders(components: string[], request: RequestLike): void {\n if (components.includes('content-type')) {\n const value = getHeaderValue(request.headers, 'Content-Type');\n if (value !== undefined && containsTopLevelComma(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Type header covered by signature must be single-valued; multiple field values are ambiguous'\n );\n }\n }\n if (components.includes('content-digest')) {\n const value = getHeaderValue(request.headers, 'Content-Digest');\n if (value !== undefined) {\n try {\n const dict = parseDictionary(value);\n const seen = new Map<string, number>();\n for (const key of dict.keys()) {\n seen.set(key, (seen.get(key) ?? 0) + 1);\n }\n for (const [key, count] of seen) {\n if (count > 1) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n `Content-Digest declares the \"${key}\" algorithm more than once; duplicate members are ambiguous`\n );\n }\n }\n } catch (e) {\n if (e instanceof RequestSignatureError) throw e;\n }\n // The structured-headers library deduplicates dictionary keys silently.\n // Detect the multi-value case at the raw-string level too so the\n // specific \"two sha-256 members\" shape is caught.\n if (containsDuplicateDictKey(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Digest declares the same algorithm more than once; duplicate members are ambiguous'\n );\n }\n }\n }\n}\n\nfunction containsTopLevelComma(value: string): boolean {\n let inQuoted = false;\n for (let i = 0; i < value.length; i++) {\n const ch = value[i]!;\n if (ch === '\\\\' && i + 1 < value.length && inQuoted) {\n i++;\n continue;\n }\n if (ch === '\"') {\n inQuoted = !inQuoted;\n continue;\n }\n if (ch === ',' && !inQuoted) return true;\n }\n return false;\n}\n\nfunction containsDuplicateDictKey(value: string): boolean {\n const keys: string[] = [];\n let i = 0;\n const len = value.length;\n let atEntryStart = true;\n while (i < len) {\n if (atEntryStart) {\n while (i < len && (value[i] === ' ' || value[i] === '\\t')) i++;\n const start = i;\n while (i < len && /[A-Za-z0-9_*-]/.test(value[i]!)) i++;\n if (i > start) keys.push(value.slice(start, i).toLowerCase());\n atEntryStart = false;\n continue;\n }\n const ch = value[i]!;\n if (ch === '\"') {\n i++;\n while (i < len) {\n if (value[i] === '\\\\' && i + 1 < len) {\n i += 2;\n continue;\n }\n if (value[i] === '\"') {\n i++;\n break;\n }\n i++;\n }\n continue;\n }\n if (ch === ':') {\n i++;\n while (i < len && value[i] !== ':') i++;\n if (i < len) i++;\n continue;\n }\n if (ch === ',') {\n atEntryStart = true;\n i++;\n continue;\n }\n i++;\n }\n const seen = new Set<string>();\n for (const key of keys) {\n if (seen.has(key)) return true;\n seen.add(key);\n }\n return false;\n}\n\n/**\n * Upper bound on request body size we will JSON.parse and walk on the\n * unsigned branch. Real AdCP `update_media_buy` / `create_media_buy` payloads\n * are O(kilobytes); 1 MB is ~1000x that. Over this, require signing rather\n * than spend unbounded pre-crypto CPU on attacker-shaped input — a 1 MB\n * nested-array body would otherwise let an unauthenticated caller force\n * deep recursion on every request.\n */\nconst MAX_UNSIGNED_BODY_INSPECTION_BYTES = 1_048_576;\n/**\n * Upper bound on recursion depth in `containsWebhookAuthentication`. Real\n * AdCP payloads reach depth ~5-8; 64 is generous without leaving room for\n * a pathological `{\"a\":{\"a\":{...}}}` to burn the stack.\n */\nfunction exceedsUnsignedBodyInspectionCap(body: string | undefined): boolean {\n return typeof body === 'string' && body.length > MAX_UNSIGNED_BODY_INSPECTION_BYTES;\n}\n\n/**\n * Scan a JSON request body for a non-empty\n * webhook authentication object — anywhere in the tree, including inside\n * arrays of pending config updates. MCP uses `push_notification_config`;\n * A2A uses `pushNotificationConfig`. Runs only on the\n * unsigned branch; returning `true` triggers the webhook-security downgrade\n * rejection in {@link verifyRequestSignature}.\n *\n * The check is Content-Type independent: an attacker can't evade by\n * labeling a JSON body `text/plain`. A body that fails to parse as JSON\n * returns false so legitimate non-JSON transports (SSE posts, form uploads)\n * aren't blocked; signed-request enforcement for those remains\n * `required_for`-driven. A body over {@link MAX_UNSIGNED_BODY_INSPECTION_BYTES}\n * returns `true` — we can't prove absence of webhook auth within our DoS\n * budget, and oversized unsigned bodies are outside normal AdCP operation.\n */\nfunction carriesWebhookAuthentication(request: RequestLike): boolean {\n const body = request.body;\n if (!body) return false;\n if (exceedsUnsignedBodyInspectionCap(body)) return true;\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return false;\n }\n return containsWebhookAuthentication(parsed, WEBHOOK_AUTH_TRAVERSAL_DEPTH);\n}\n\nfunction validateCoveredComponents(components: string[], capability: VerifierCapability, request: RequestLike): void {\n for (const mandatory of MANDATORY_COMPONENTS) {\n if (!components.includes(mandatory)) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n `Covered components must include \"${mandatory}\"`\n );\n }\n }\n const hasBody = (request.body ?? '').length > 0;\n if (hasBody && !components.includes('content-type')) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Covered components must include \"content-type\" when a request body is present'\n );\n }\n const includesDigest = components.includes('content-digest');\n if (capability.covers_content_digest === 'required' && !includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Verifier requires content-digest coverage'\n );\n }\n if (capability.covers_content_digest === 'forbidden' && includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_unexpected',\n 6,\n 'Verifier forbids content-digest coverage'\n );\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/lib/signing/verifier.ts"],"sourcesContent":["import { parseDictionary } from 'structured-headers';\nimport {\n buildSignatureBase,\n canonicalTargetUri,\n getHeaderValue,\n rejectNonAsciiHost,\n type RequestLike,\n} from './canonicalize';\nimport { contentDigestMatches } from './content-digest';\nimport { RequestSignatureError } from './errors';\nimport { parseSignature, parseSignatureInput, type ParsedSignatureInput } from './parser';\nimport { jwkToPublicKey, verifySignature } from './crypto';\nimport type { JwksResolver } from './jwks';\nimport type { ReplayStore } from './replay';\nimport type { RevocationStore } from './revocation';\nimport { containsWebhookAuthentication, WEBHOOK_AUTH_TRAVERSAL_DEPTH } from './webhook-auth-detection';\nimport { canonicalA2AProtocolMethod, protocolMethodListIncludes } from './protocol-methods';\nimport {\n ALLOWED_ALGS,\n CLOCK_SKEW_TOLERANCE_SECONDS,\n MANDATORY_COMPONENTS,\n MAX_SIGNATURE_WINDOW_SECONDS,\n REQUEST_SIGNING_TAG,\n type VerifierCapability,\n type VerifyResult,\n} from './types';\n\nexport interface VerifyRequestOptions {\n capability: VerifierCapability;\n jwks: JwksResolver;\n replayStore: ReplayStore;\n revocationStore: RevocationStore;\n now?: () => number;\n /**\n * The AdCP operation being requested, used to consult\n * `capability.required_for` when an unsigned request arrives. When omitted,\n * the verifier treats the operation as \"not in any required_for list\" and\n * returns an unsigned result rather than rejecting — callers in\n * always-verify mode (where every request is signed) can leave this blank.\n */\n operation?: string;\n agentUrlForKeyid?: (keyid: string) => string | undefined;\n}\n\nexport async function verifyRequestSignature(\n request: RequestLike,\n options: VerifyRequestOptions\n): Promise<VerifyResult> {\n const now = options.now ? options.now() : Math.floor(Date.now() / 1000);\n const sigInputHeader = getHeaderValue(request.headers, 'Signature-Input');\n const sigHeader = getHeaderValue(request.headers, 'Signature');\n\n // Pre-check: both headers present or both absent.\n if (!sigInputHeader && !sigHeader) {\n const operation = options.operation;\n // Precedence is intentionally fail-specific: AdCP tool required_for,\n // raw JSON-RPC protocol methods, then payload-driven webhook-auth\n // elevation. The specific signed-only contract should win before the\n // generic body scan runs.\n if (operation && options.capability.required_for.includes(operation)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Operation \"${operation}\" requires a signed request`\n );\n }\n const protocolMethodsRequiredFor = options.capability.protocol_methods_required_for ?? [];\n if (protocolMethodsRequiredFor.length > 0 && exceedsUnsignedBodyInspectionCap(request.body)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Unsigned request body exceeds the protocol method inspection cap while protocol methods require signing'\n );\n }\n const protocolMethods = jsonRpcProtocolMethods(request.body);\n const requiredProtocolMethod = protocolMethods.find(wireMethod =>\n protocolMethodListIncludes(protocolMethodsRequiredFor, wireMethod)\n );\n if (requiredProtocolMethod) {\n const declaredMethod = canonicalA2AProtocolMethod(requiredProtocolMethod);\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Protocol method \"${declaredMethod}\" requires a signed request` +\n (requiredProtocolMethod === declaredMethod ? '' : ` (wire method \"${requiredProtocolMethod}\")`)\n );\n }\n // Payload-driven elevation: any request carrying webhook receiver\n // credentials (task, reporting, artifact, or revocation callbacks) MUST\n // be RFC 9421 signed, regardless of whether the operation appears in\n // `required_for`\n // (#webhook-security downgrade-resistance). A bearer-only channel\n // would otherwise let an attacker who captured a token register or\n // update webhook credentials and redirect callbacks to a hostile URL.\n if (carriesWebhookAuthentication(request)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Requests carrying webhook authentication must be RFC 9421 signed'\n );\n }\n return { status: 'unsigned', verified_at: now };\n }\n if (!sigInputHeader || !sigHeader) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Signature and Signature-Input headers must be present as a pair'\n );\n }\n\n // Step 1: parse.\n const parsedInput = parseSignatureInput(sigInputHeader);\n const parsedSig = parseSignature(sigHeader, parsedInput.label);\n rejectNonAsciiHost(request.url);\n validateSingleValuedCoveredHeaders(parsedInput.components, request);\n\n // Step 2: required params present.\n requireParams(parsedInput);\n\n // Step 3: tag match.\n if (parsedInput.params.tag !== REQUEST_SIGNING_TAG) {\n throw new RequestSignatureError(\n 'request_signature_tag_invalid',\n 3,\n `Signature tag must be \"${REQUEST_SIGNING_TAG}\"; got \"${parsedInput.params.tag}\"`\n );\n }\n\n // Step 4: alg allowlist.\n if (!ALLOWED_ALGS.has(parsedInput.params.alg)) {\n throw new RequestSignatureError(\n 'request_signature_alg_not_allowed',\n 4,\n `Signature alg \"${parsedInput.params.alg}\" is not in the AdCP allowlist`\n );\n }\n\n // Step 5: window valid.\n validateWindow(parsedInput.params.created, parsedInput.params.expires, now);\n\n // Step 6: covered components.\n validateCoveredComponents(parsedInput.components, options.capability, request);\n\n // Step 7: resolve keyid.\n const jwk = await options.jwks.resolve(parsedInput.params.keyid);\n if (!jwk) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `No JWK found for keyid \"${parsedInput.params.keyid}\"`\n );\n }\n if (jwk.kid !== parsedInput.params.keyid) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `JWKS resolver returned a JWK whose kid \"${jwk.kid}\" does not match requested keyid \"${parsedInput.params.keyid}\"`\n );\n }\n\n // Step 8: key purpose.\n if (jwk.adcp_use !== 'request-signing' || !jwk.key_ops?.includes('verify')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" is not scoped for request-signing verification`\n );\n }\n validateJwkParameterConsistency(jwk, parsedInput.params.alg);\n\n // Step 9: revocation (runs BEFORE crypto to prevent amplification attacks).\n if (await options.revocationStore.isRevoked(jwk.kid)) {\n throw new RequestSignatureError('request_signature_key_revoked', 9, `JWK \"${jwk.kid}\" is revoked`);\n }\n\n // Replay cache is scoped by `(keyid, @target-uri)` per adcp#2460 — a\n // signature captured on one endpoint (e.g. /create_media_buy) MUST NOT\n // count against the replay budget for a different endpoint under the\n // same keyid. Canonicalize once and reuse for both pre-check and commit.\n const replayScope = canonicalTargetUri(request.url);\n\n // Step 12 pre-checks (rate-abuse cap + replay hit) run before crypto so a\n // compromised-key cache cap or a replayed nonce short-circuits an expensive\n // Ed25519/ECDSA verify. The committing insert happens after step 11.\n if (await options.replayStore.isCapHit(jwk.kid, replayScope, now)) {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n if (await options.replayStore.has(jwk.kid, replayScope, parsedInput.params.nonce, now)) {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n\n // Step 10: cryptographic verify. Use the received Signature-Input value\n // verbatim as the @signature-params line so a peer emitting params in a\n // different legal order still produces a byte-identical base.\n const base = buildSignatureBase(\n parsedInput.components,\n request,\n parsedInput.params,\n parsedInput.signatureParamsValue\n );\n const publicKey = jwkToPublicKey(jwk);\n const valid = verifySignature(parsedInput.params.alg, publicKey, Buffer.from(base, 'utf8'), parsedSig.bytes);\n if (!valid) {\n throw new RequestSignatureError(\n 'request_signature_invalid',\n 10,\n 'Cryptographic verification of signature base failed'\n );\n }\n\n // Step 11: content-digest match (only if covered).\n if (parsedInput.components.includes('content-digest')) {\n const digestHeader = getHeaderValue(request.headers, 'Content-Digest');\n if (!digestHeader || !contentDigestMatches(digestHeader, request.body ?? '')) {\n throw new RequestSignatureError(\n 'request_signature_digest_mismatch',\n 11,\n 'Content-Digest header does not match recomputed body hash'\n );\n }\n }\n\n // Step 12: commit the (keyid, nonce) into the replay cache now that all\n // prior checks have passed. Floor the TTL at one max-window + skew so a\n // signer minting tiny validity windows can't replay outside the configured\n // replay horizon, and so cross-replica eventual consistency has headroom.\n const remaining = parsedInput.params.expires - now + CLOCK_SKEW_TOLERANCE_SECONDS;\n const ttl = Math.max(remaining, MAX_SIGNATURE_WINDOW_SECONDS + CLOCK_SKEW_TOLERANCE_SECONDS);\n const insertResult = await options.replayStore.insert(jwk.kid, replayScope, parsedInput.params.nonce, ttl, now);\n if (insertResult === 'replayed') {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n if (insertResult === 'rate_abuse') {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n\n const agent_url = options.agentUrlForKeyid?.(jwk.kid);\n return { status: 'verified', keyid: jwk.kid, agent_url, verified_at: now };\n}\n\nfunction jsonRpcProtocolMethods(body: string | undefined): string[] {\n if (!body) return [];\n if (exceedsUnsignedBodyInspectionCap(body)) return [];\n try {\n const parsed = JSON.parse(body) as unknown;\n const messages = Array.isArray(parsed) ? parsed : [parsed];\n return messages.flatMap(message =>\n message !== null && typeof message === 'object' && typeof (message as { method?: unknown }).method === 'string'\n ? [(message as { method: string }).method]\n : []\n );\n } catch {\n return [];\n }\n}\n\nfunction requireParams(parsed: ParsedSignatureInput): void {\n const required: Array<keyof ParsedSignatureInput['params']> = ['created', 'expires', 'nonce', 'keyid', 'alg', 'tag'];\n const missing = required.filter(k => parsed.params[k] === undefined);\n if (missing.length) {\n throw new RequestSignatureError(\n 'request_signature_params_incomplete',\n 2,\n `Signature-Input missing required parameter(s): ${missing.join(', ')}`\n );\n }\n}\n\nfunction validateWindow(created: number, expires: number, now: number): void {\n if (expires <= created) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature expires must be strictly greater than created'\n );\n }\n if (expires - created > MAX_SIGNATURE_WINDOW_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n `Signature window exceeds ${MAX_SIGNATURE_WINDOW_SECONDS}s maximum`\n );\n }\n if (now > expires + CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError('request_signature_window_invalid', 5, 'Signature is expired');\n }\n if (now < created - CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature created is in the future beyond skew tolerance'\n );\n }\n}\n\n/**\n * Reject JWKs whose alg/kty/crv are mutually inconsistent OR don't match\n * the sig-params alg. Three checks in one place:\n *\n * 1. JWK MUST declare `alg`. A missing alg lets a signer substitute one\n * algorithm family for another (e.g. EdDSA sig-params against a P-256\n * JWK) and defer failure to step 10's crypto verify — the wrong step\n * and the wrong error code.\n * 2. JWK `alg` MUST match the sig-params `alg` after AdCP→JOSE mapping\n * (`ed25519`↔`EdDSA`, `ecdsa-p256-sha256`↔`ES256`). Prevents sig-params\n * alg-downgrade against a JWK that was published for a stronger alg.\n * 3. JWK kty/crv MUST be consistent with the declared alg per RFC 8037\n * (EdDSA→OKP/Ed25519|Ed448) and RFC 7518 (ES256→EC/P-256).\n */\nfunction validateJwkParameterConsistency(\n jwk: { kid: string; kty?: string; crv?: string; alg?: string },\n sigParamsAlg: string\n): void {\n if (!jwk.alg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" does not declare an alg; cannot bind to sig-params alg=\"${sigParamsAlg}\"`\n );\n }\n const expectedJoseAlg = SIG_PARAMS_ALG_TO_JOSE[sigParamsAlg];\n if (expectedJoseAlg && jwk.alg !== expectedJoseAlg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=${jwk.alg} but the request is signed with alg=\"${sigParamsAlg}\" (JOSE ${expectedJoseAlg})`\n );\n }\n if (jwk.alg === 'EdDSA') {\n if (jwk.kty !== 'OKP' || (jwk.crv !== 'Ed25519' && jwk.crv !== 'Ed448')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=EdDSA but kty/crv (${jwk.kty}/${jwk.crv}) is not a valid Edwards-curve pair`\n );\n }\n return;\n }\n if (jwk.alg === 'ES256') {\n if (jwk.kty !== 'EC' || jwk.crv !== 'P-256') {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=ES256 but kty/crv (${jwk.kty}/${jwk.crv}) is not EC/P-256`\n );\n }\n }\n}\n\nconst SIG_PARAMS_ALG_TO_JOSE: Record<string, string> = {\n ed25519: 'EdDSA',\n 'ecdsa-p256-sha256': 'ES256',\n};\n\n/**\n * Covered components referencing single-valued HTTP header fields\n * (Content-Type per RFC 9110, Content-Digest per RFC 9530) MUST NOT arrive\n * as a comma-joined multi-value. A proxy or buggy client emitting the field\n * twice produces a concatenated value whose meaning relative to the\n * signature base is undefined — reject at parse rather than pick one.\n */\nfunction validateSingleValuedCoveredHeaders(components: string[], request: RequestLike): void {\n if (components.includes('content-type')) {\n const value = getHeaderValue(request.headers, 'Content-Type');\n if (value !== undefined && containsTopLevelComma(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Type header covered by signature must be single-valued; multiple field values are ambiguous'\n );\n }\n }\n if (components.includes('content-digest')) {\n const value = getHeaderValue(request.headers, 'Content-Digest');\n if (value !== undefined) {\n try {\n const dict = parseDictionary(value);\n const seen = new Map<string, number>();\n for (const key of dict.keys()) {\n seen.set(key, (seen.get(key) ?? 0) + 1);\n }\n for (const [key, count] of seen) {\n if (count > 1) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n `Content-Digest declares the \"${key}\" algorithm more than once; duplicate members are ambiguous`\n );\n }\n }\n } catch (e) {\n if (e instanceof RequestSignatureError) throw e;\n }\n // The structured-headers library deduplicates dictionary keys silently.\n // Detect the multi-value case at the raw-string level too so the\n // specific \"two sha-256 members\" shape is caught.\n if (containsDuplicateDictKey(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Digest declares the same algorithm more than once; duplicate members are ambiguous'\n );\n }\n }\n }\n}\n\nfunction containsTopLevelComma(value: string): boolean {\n let inQuoted = false;\n for (let i = 0; i < value.length; i++) {\n const ch = value[i]!;\n if (ch === '\\\\' && i + 1 < value.length && inQuoted) {\n i++;\n continue;\n }\n if (ch === '\"') {\n inQuoted = !inQuoted;\n continue;\n }\n if (ch === ',' && !inQuoted) return true;\n }\n return false;\n}\n\nfunction containsDuplicateDictKey(value: string): boolean {\n const keys: string[] = [];\n let i = 0;\n const len = value.length;\n let atEntryStart = true;\n while (i < len) {\n if (atEntryStart) {\n while (i < len && (value[i] === ' ' || value[i] === '\\t')) i++;\n const start = i;\n while (i < len && /[A-Za-z0-9_*-]/.test(value[i]!)) i++;\n if (i > start) keys.push(value.slice(start, i).toLowerCase());\n atEntryStart = false;\n continue;\n }\n const ch = value[i]!;\n if (ch === '\"') {\n i++;\n while (i < len) {\n if (value[i] === '\\\\' && i + 1 < len) {\n i += 2;\n continue;\n }\n if (value[i] === '\"') {\n i++;\n break;\n }\n i++;\n }\n continue;\n }\n if (ch === ':') {\n i++;\n while (i < len && value[i] !== ':') i++;\n if (i < len) i++;\n continue;\n }\n if (ch === ',') {\n atEntryStart = true;\n i++;\n continue;\n }\n i++;\n }\n const seen = new Set<string>();\n for (const key of keys) {\n if (seen.has(key)) return true;\n seen.add(key);\n }\n return false;\n}\n\n/**\n * Upper bound on request body size we will JSON.parse and walk on the\n * unsigned branch. Real AdCP `update_media_buy` / `create_media_buy` payloads\n * are O(kilobytes); 1 MB is ~1000x that. Over this, require signing rather\n * than spend unbounded pre-crypto CPU on attacker-shaped input — a 1 MB\n * nested-array body would otherwise let an unauthenticated caller force\n * deep recursion on every request.\n */\nconst MAX_UNSIGNED_BODY_INSPECTION_BYTES = 1_048_576;\n/**\n * Upper bound on recursion depth in `containsWebhookAuthentication`. Real\n * AdCP payloads reach depth ~5-8; 64 is generous without leaving room for\n * a pathological `{\"a\":{\"a\":{...}}}` to burn the stack.\n */\nfunction exceedsUnsignedBodyInspectionCap(body: string | undefined): boolean {\n return typeof body === 'string' && body.length > MAX_UNSIGNED_BODY_INSPECTION_BYTES;\n}\n\n/**\n * Scan a JSON request body for a non-empty\n * webhook authentication object — anywhere in the tree, including inside\n * arrays of pending config updates. MCP uses `push_notification_config`;\n * A2A uses `pushNotificationConfig`. Runs only on the\n * unsigned branch; returning `true` triggers the webhook-security downgrade\n * rejection in {@link verifyRequestSignature}.\n *\n * The check is Content-Type independent: an attacker can't evade by\n * labeling a JSON body `text/plain`. A body that fails to parse as JSON\n * returns false so legitimate non-JSON transports (SSE posts, form uploads)\n * aren't blocked; signed-request enforcement for those remains\n * `required_for`-driven. A body over {@link MAX_UNSIGNED_BODY_INSPECTION_BYTES}\n * returns `true` — we can't prove absence of webhook auth within our DoS\n * budget, and oversized unsigned bodies are outside normal AdCP operation.\n */\nfunction carriesWebhookAuthentication(request: RequestLike): boolean {\n const body = request.body;\n if (!body) return false;\n if (exceedsUnsignedBodyInspectionCap(body)) return true;\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return false;\n }\n return containsWebhookAuthentication(parsed, WEBHOOK_AUTH_TRAVERSAL_DEPTH);\n}\n\nfunction validateCoveredComponents(components: string[], capability: VerifierCapability, request: RequestLike): void {\n for (const mandatory of MANDATORY_COMPONENTS) {\n if (!components.includes(mandatory)) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n `Covered components must include \"${mandatory}\"`\n );\n }\n }\n const hasBody = (request.body ?? '').length > 0;\n if (hasBody && !components.includes('content-type')) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Covered components must include \"content-type\" when a request body is present'\n );\n }\n const includesDigest = components.includes('content-digest');\n if (capability.covers_content_digest === 'required' && !includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Verifier requires content-digest coverage'\n );\n }\n if (capability.covers_content_digest === 'forbidden' && includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_unexpected',\n 6,\n 'Verifier forbids content-digest coverage'\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCAAgC;AAChC,0BAMO;AACP,4BAAqC;AACrC,oBAAsC;AACtC,oBAA+E;AAC/E,oBAAgD;AAIhD,oCAA4E;AAC5E,8BAAuE;AACvE,mBAQO;AAmBP,eAAsB,uBACpB,SACA,SACuB;AACvB,QAAM,MAAM,QAAQ,MAAM,QAAQ,IAAI,IAAI,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACtE,QAAM,qBAAiB,oCAAe,QAAQ,SAAS,iBAAiB;AACxE,QAAM,gBAAY,oCAAe,QAAQ,SAAS,WAAW;AAG7D,MAAI,CAAC,kBAAkB,CAAC,WAAW;AACjC,UAAM,YAAY,QAAQ;AAK1B,QAAI,aAAa,QAAQ,WAAW,aAAa,SAAS,SAAS,GAAG;AACpE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA,cAAc,SAAS;AAAA,MACzB;AAAA,IACF;AACA,UAAM,6BAA6B,QAAQ,WAAW,iCAAiC,CAAC;AACxF,QAAI,2BAA2B,SAAS,KAAK,iCAAiC,QAAQ,IAAI,GAAG;AAC3F,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,kBAAkB,uBAAuB,QAAQ,IAAI;AAC3D,UAAM,yBAAyB,gBAAgB;AAAA,MAAK,oBAClD,oDAA2B,4BAA4B,UAAU;AAAA,IACnE;AACA,QAAI,wBAAwB;AAC1B,YAAM,qBAAiB,oDAA2B,sBAAsB;AACxE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA,oBAAoB,cAAc,iCAC/B,2BAA2B,iBAAiB,KAAK,kBAAkB,sBAAsB;AAAA,MAC9F;AAAA,IACF;AAQA,QAAI,6BAA6B,OAAO,GAAG;AACzC,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,QAAQ,YAAY,aAAa,IAAI;AAAA,EAChD;AACA,MAAI,CAAC,kBAAkB,CAAC,WAAW;AACjC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,QAAM,kBAAc,mCAAoB,cAAc;AACtD,QAAM,gBAAY,8BAAe,WAAW,YAAY,KAAK;AAC7D,8CAAmB,QAAQ,GAAG;AAC9B,qCAAmC,YAAY,YAAY,OAAO;AAGlE,gBAAc,WAAW;AAGzB,MAAI,YAAY,OAAO,QAAQ,kCAAqB;AAClD,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,0BAA0B,gCAAmB,WAAW,YAAY,OAAO,GAAG;AAAA,IAChF;AAAA,EACF;AAGA,MAAI,CAAC,0BAAa,IAAI,YAAY,OAAO,GAAG,GAAG;AAC7C,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,kBAAkB,YAAY,OAAO,GAAG;AAAA,IAC1C;AAAA,EACF;AAGA,iBAAe,YAAY,OAAO,SAAS,YAAY,OAAO,SAAS,GAAG;AAG1E,4BAA0B,YAAY,YAAY,QAAQ,YAAY,OAAO;AAG7E,QAAM,MAAM,MAAM,QAAQ,KAAK,QAAQ,YAAY,OAAO,KAAK;AAC/D,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,2BAA2B,YAAY,OAAO,KAAK;AAAA,IACrD;AAAA,EACF;AACA,MAAI,IAAI,QAAQ,YAAY,OAAO,OAAO;AACxC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,2CAA2C,IAAI,GAAG,qCAAqC,YAAY,OAAO,KAAK;AAAA,IACjH;AAAA,EACF;AAGA,MAAI,IAAI,aAAa,qBAAqB,CAAC,IAAI,SAAS,SAAS,QAAQ,GAAG;AAC1E,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,QAAQ,IAAI,GAAG;AAAA,IACjB;AAAA,EACF;AACA,kCAAgC,KAAK,YAAY,OAAO,GAAG;AAG3D,MAAI,MAAM,QAAQ,gBAAgB,UAAU,IAAI,GAAG,GAAG;AACpD,UAAM,IAAI,oCAAsB,iCAAiC,GAAG,QAAQ,IAAI,GAAG,cAAc;AAAA,EACnG;AAMA,QAAM,kBAAc,wCAAmB,QAAQ,GAAG;AAKlD,MAAI,MAAM,QAAQ,YAAY,SAAS,IAAI,KAAK,aAAa,GAAG,GAAG;AACjE,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,iDAAiD,IAAI,GAAG;AAAA,IAC1D;AAAA,EACF;AACA,MAAI,MAAM,QAAQ,YAAY,IAAI,IAAI,KAAK,aAAa,YAAY,OAAO,OAAO,GAAG,GAAG;AACtF,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,oBAAoB,IAAI,GAAG,WAAW,YAAY,OAAO,KAAK;AAAA,IAChE;AAAA,EACF;AAKA,QAAM,WAAO;AAAA,IACX,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,YAAY;AAAA,EACd;AACA,QAAM,gBAAY,8BAAe,GAAG;AACpC,QAAM,YAAQ,+BAAgB,YAAY,OAAO,KAAK,WAAW,OAAO,KAAK,MAAM,MAAM,GAAG,UAAU,KAAK;AAC3G,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,MAAI,YAAY,WAAW,SAAS,gBAAgB,GAAG;AACrD,UAAM,mBAAe,oCAAe,QAAQ,SAAS,gBAAgB;AACrE,QAAI,CAAC,gBAAgB,KAAC,4CAAqB,cAAc,QAAQ,QAAQ,EAAE,GAAG;AAC5E,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAMA,QAAM,YAAY,YAAY,OAAO,UAAU,MAAM;AACrD,QAAM,MAAM,KAAK,IAAI,WAAW,4CAA+B,yCAA4B;AAC3F,QAAM,eAAe,MAAM,QAAQ,YAAY,OAAO,IAAI,KAAK,aAAa,YAAY,OAAO,OAAO,KAAK,GAAG;AAC9G,MAAI,iBAAiB,YAAY;AAC/B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,oBAAoB,IAAI,GAAG,WAAW,YAAY,OAAO,KAAK;AAAA,IAChE;AAAA,EACF;AACA,MAAI,iBAAiB,cAAc;AACjC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,iDAAiD,IAAI,GAAG;AAAA,IAC1D;AAAA,EACF;AAEA,QAAM,YAAY,QAAQ,mBAAmB,IAAI,GAAG;AACpD,SAAO,EAAE,QAAQ,YAAY,OAAO,IAAI,KAAK,WAAW,aAAa,IAAI;AAC3E;AAEA,SAAS,uBAAuB,MAAoC;AAClE,MAAI,CAAC,KAAM,QAAO,CAAC;AACnB,MAAI,iCAAiC,IAAI,EAAG,QAAO,CAAC;AACpD,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,UAAM,WAAW,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AACzD,WAAO,SAAS;AAAA,MAAQ,aACtB,YAAY,QAAQ,OAAO,YAAY,YAAY,OAAQ,QAAiC,WAAW,WACnG,CAAE,QAA+B,MAAM,IACvC,CAAC;AAAA,IACP;AAAA,EACF,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,cAAc,QAAoC;AACzD,QAAM,WAAwD,CAAC,WAAW,WAAW,SAAS,SAAS,OAAO,KAAK;AACnH,QAAM,UAAU,SAAS,OAAO,OAAK,OAAO,OAAO,CAAC,MAAM,MAAS;AACnE,MAAI,QAAQ,QAAQ;AAClB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,kDAAkD,QAAQ,KAAK,IAAI,CAAC;AAAA,IACtE;AAAA,EACF;AACF;AAEA,SAAS,eAAe,SAAiB,SAAiB,KAAmB;AAC3E,MAAI,WAAW,SAAS;AACtB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,UAAU,UAAU,2CAA8B;AACpD,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,4BAA4B,yCAA4B;AAAA,IAC1D;AAAA,EACF;AACA,MAAI,MAAM,UAAU,2CAA8B;AAChD,UAAM,IAAI,oCAAsB,oCAAoC,GAAG,sBAAsB;AAAA,EAC/F;AACA,MAAI,MAAM,UAAU,2CAA8B;AAChD,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAgBA,SAAS,gCACP,KACA,cACM;AACN,MAAI,CAAC,IAAI,KAAK;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,QAAQ,IAAI,GAAG,6DAA6D,YAAY;AAAA,IAC1F;AAAA,EACF;AACA,QAAM,kBAAkB,uBAAuB,YAAY;AAC3D,MAAI,mBAAmB,IAAI,QAAQ,iBAAiB;AAClD,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA,QAAQ,IAAI,GAAG,kBAAkB,IAAI,GAAG,wCAAwC,YAAY,WAAW,eAAe;AAAA,IACxH;AAAA,EACF;AACA,MAAI,IAAI,QAAQ,SAAS;AACvB,QAAI,IAAI,QAAQ,SAAU,IAAI,QAAQ,aAAa,IAAI,QAAQ,SAAU;AACvE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA,QAAQ,IAAI,GAAG,qCAAqC,IAAI,GAAG,IAAI,IAAI,GAAG;AAAA,MACxE;AAAA,IACF;AACA;AAAA,EACF;AACA,MAAI,IAAI,QAAQ,SAAS;AACvB,QAAI,IAAI,QAAQ,QAAQ,IAAI,QAAQ,SAAS;AAC3C,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA,QAAQ,IAAI,GAAG,qCAAqC,IAAI,GAAG,IAAI,IAAI,GAAG;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AACF;AAEA,MAAM,yBAAiD;AAAA,EACrD,SAAS;AAAA,EACT,qBAAqB;AACvB;AASA,SAAS,mCAAmC,YAAsB,SAA4B;AAC5F,MAAI,WAAW,SAAS,cAAc,GAAG;AACvC,UAAM,YAAQ,oCAAe,QAAQ,SAAS,cAAc;AAC5D,QAAI,UAAU,UAAa,sBAAsB,KAAK,GAAG;AACvD,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,WAAW,SAAS,gBAAgB,GAAG;AACzC,UAAM,YAAQ,oCAAe,QAAQ,SAAS,gBAAgB;AAC9D,QAAI,UAAU,QAAW;AACvB,UAAI;AACF,cAAM,WAAO,2CAAgB,KAAK;AAClC,cAAM,OAAO,oBAAI,IAAoB;AACrC,mBAAW,OAAO,KAAK,KAAK,GAAG;AAC7B,eAAK,IAAI,MAAM,KAAK,IAAI,GAAG,KAAK,KAAK,CAAC;AAAA,QACxC;AACA,mBAAW,CAAC,KAAK,KAAK,KAAK,MAAM;AAC/B,cAAI,QAAQ,GAAG;AACb,kBAAM,IAAI;AAAA,cACR;AAAA,cACA;AAAA,cACA,gCAAgC,GAAG;AAAA,YACrC;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,GAAG;AACV,YAAI,aAAa,oCAAuB,OAAM;AAAA,MAChD;AAIA,UAAI,yBAAyB,KAAK,GAAG;AACnC,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,sBAAsB,OAAwB;AACrD,MAAI,WAAW;AACf,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,KAAK,MAAM,CAAC;AAClB,QAAI,OAAO,QAAQ,IAAI,IAAI,MAAM,UAAU,UAAU;AACnD;AACA;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AACd,iBAAW,CAAC;AACZ;AAAA,IACF;AACA,QAAI,OAAO,OAAO,CAAC,SAAU,QAAO;AAAA,EACtC;AACA,SAAO;AACT;AAEA,SAAS,yBAAyB,OAAwB;AACxD,QAAM,OAAiB,CAAC;AACxB,MAAI,IAAI;AACR,QAAM,MAAM,MAAM;AAClB,MAAI,eAAe;AACnB,SAAO,IAAI,KAAK;AACd,QAAI,cAAc;AAChB,aAAO,IAAI,QAAQ,MAAM,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM,KAAO;AAC3D,YAAM,QAAQ;AACd,aAAO,IAAI,OAAO,iBAAiB,KAAK,MAAM,CAAC,CAAE,EAAG;AACpD,UAAI,IAAI,MAAO,MAAK,KAAK,MAAM,MAAM,OAAO,CAAC,EAAE,YAAY,CAAC;AAC5D,qBAAe;AACf;AAAA,IACF;AACA,UAAM,KAAK,MAAM,CAAC;AAClB,QAAI,OAAO,KAAK;AACd;AACA,aAAO,IAAI,KAAK;AACd,YAAI,MAAM,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK;AACpC,eAAK;AACL;AAAA,QACF;AACA,YAAI,MAAM,CAAC,MAAM,KAAK;AACpB;AACA;AAAA,QACF;AACA;AAAA,MACF;AACA;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AACd;AACA,aAAO,IAAI,OAAO,MAAM,CAAC,MAAM,IAAK;AACpC,UAAI,IAAI,IAAK;AACb;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AACd,qBAAe;AACf;AACA;AAAA,IACF;AACA;AAAA,EACF;AACA,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,OAAO,MAAM;AACtB,QAAI,KAAK,IAAI,GAAG,EAAG,QAAO;AAC1B,SAAK,IAAI,GAAG;AAAA,EACd;AACA,SAAO;AACT;AAUA,MAAM,qCAAqC;AAM3C,SAAS,iCAAiC,MAAmC;AAC3E,SAAO,OAAO,SAAS,YAAY,KAAK,SAAS;AACnD;AAkBA,SAAS,6BAA6B,SAA+B;AACnE,QAAM,OAAO,QAAQ;AACrB,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,iCAAiC,IAAI,EAAG,QAAO;AACnD,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,aAAO,6DAA8B,QAAQ,0DAA4B;AAC3E;AAEA,SAAS,0BAA0B,YAAsB,YAAgC,SAA4B;AACnH,aAAW,aAAa,mCAAsB;AAC5C,QAAI,CAAC,WAAW,SAAS,SAAS,GAAG;AACnC,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA,oCAAoC,SAAS;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AACA,QAAM,WAAW,QAAQ,QAAQ,IAAI,SAAS;AAC9C,MAAI,WAAW,CAAC,WAAW,SAAS,cAAc,GAAG;AACnD,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,iBAAiB,WAAW,SAAS,gBAAgB;AAC3D,MAAI,WAAW,0BAA0B,cAAc,CAAC,gBAAgB;AACtE,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,WAAW,0BAA0B,eAAe,gBAAgB;AACtE,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -10,6 +10,7 @@ import { RequestSignatureError } from "./errors.mjs";
10
10
  import { parseSignature, parseSignatureInput } from "./parser.mjs";
11
11
  import { jwkToPublicKey, verifySignature } from "./crypto.mjs";
12
12
  import { containsWebhookAuthentication, WEBHOOK_AUTH_TRAVERSAL_DEPTH } from "./webhook-auth-detection.mjs";
13
+ import { canonicalA2AProtocolMethod, protocolMethodListIncludes } from "./protocol-methods.mjs";
13
14
  import {
14
15
  ALLOWED_ALGS,
15
16
  CLOCK_SKEW_TOLERANCE_SECONDS,
@@ -39,12 +40,15 @@ async function verifyRequestSignature(request, options) {
39
40
  );
40
41
  }
41
42
  const protocolMethods = jsonRpcProtocolMethods(request.body);
42
- const requiredProtocolMethod = protocolMethods.find((method) => protocolMethodsRequiredFor.includes(method));
43
+ const requiredProtocolMethod = protocolMethods.find(
44
+ (wireMethod) => protocolMethodListIncludes(protocolMethodsRequiredFor, wireMethod)
45
+ );
43
46
  if (requiredProtocolMethod) {
47
+ const declaredMethod = canonicalA2AProtocolMethod(requiredProtocolMethod);
44
48
  throw new RequestSignatureError(
45
49
  "request_signature_required",
46
50
  0,
47
- `Protocol method "${requiredProtocolMethod}" requires a signed request`
51
+ `Protocol method "${declaredMethod}" requires a signed request` + (requiredProtocolMethod === declaredMethod ? "" : ` (wire method "${requiredProtocolMethod}")`)
48
52
  );
49
53
  }
50
54
  if (carriesWebhookAuthentication(request)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/signing/verifier.ts"],"sourcesContent":["import { parseDictionary } from 'structured-headers';\nimport {\n buildSignatureBase,\n canonicalTargetUri,\n getHeaderValue,\n rejectNonAsciiHost,\n type RequestLike,\n} from './canonicalize';\nimport { contentDigestMatches } from './content-digest';\nimport { RequestSignatureError } from './errors';\nimport { parseSignature, parseSignatureInput, type ParsedSignatureInput } from './parser';\nimport { jwkToPublicKey, verifySignature } from './crypto';\nimport type { JwksResolver } from './jwks';\nimport type { ReplayStore } from './replay';\nimport type { RevocationStore } from './revocation';\nimport { containsWebhookAuthentication, WEBHOOK_AUTH_TRAVERSAL_DEPTH } from './webhook-auth-detection';\nimport {\n ALLOWED_ALGS,\n CLOCK_SKEW_TOLERANCE_SECONDS,\n MANDATORY_COMPONENTS,\n MAX_SIGNATURE_WINDOW_SECONDS,\n REQUEST_SIGNING_TAG,\n type VerifierCapability,\n type VerifyResult,\n} from './types';\n\nexport interface VerifyRequestOptions {\n capability: VerifierCapability;\n jwks: JwksResolver;\n replayStore: ReplayStore;\n revocationStore: RevocationStore;\n now?: () => number;\n /**\n * The AdCP operation being requested, used to consult\n * `capability.required_for` when an unsigned request arrives. When omitted,\n * the verifier treats the operation as \"not in any required_for list\" and\n * returns an unsigned result rather than rejecting — callers in\n * always-verify mode (where every request is signed) can leave this blank.\n */\n operation?: string;\n agentUrlForKeyid?: (keyid: string) => string | undefined;\n}\n\nexport async function verifyRequestSignature(\n request: RequestLike,\n options: VerifyRequestOptions\n): Promise<VerifyResult> {\n const now = options.now ? options.now() : Math.floor(Date.now() / 1000);\n const sigInputHeader = getHeaderValue(request.headers, 'Signature-Input');\n const sigHeader = getHeaderValue(request.headers, 'Signature');\n\n // Pre-check: both headers present or both absent.\n if (!sigInputHeader && !sigHeader) {\n const operation = options.operation;\n // Precedence is intentionally fail-specific: AdCP tool required_for,\n // raw JSON-RPC protocol methods, then payload-driven webhook-auth\n // elevation. The specific signed-only contract should win before the\n // generic body scan runs.\n if (operation && options.capability.required_for.includes(operation)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Operation \"${operation}\" requires a signed request`\n );\n }\n const protocolMethodsRequiredFor = options.capability.protocol_methods_required_for ?? [];\n if (protocolMethodsRequiredFor.length > 0 && exceedsUnsignedBodyInspectionCap(request.body)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Unsigned request body exceeds the protocol method inspection cap while protocol methods require signing'\n );\n }\n const protocolMethods = jsonRpcProtocolMethods(request.body);\n const requiredProtocolMethod = protocolMethods.find(method => protocolMethodsRequiredFor.includes(method));\n if (requiredProtocolMethod) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Protocol method \"${requiredProtocolMethod}\" requires a signed request`\n );\n }\n // Payload-driven elevation: any request carrying webhook receiver\n // credentials (task, reporting, artifact, or revocation callbacks) MUST\n // be RFC 9421 signed, regardless of whether the operation appears in\n // `required_for`\n // (#webhook-security downgrade-resistance). A bearer-only channel\n // would otherwise let an attacker who captured a token register or\n // update webhook credentials and redirect callbacks to a hostile URL.\n if (carriesWebhookAuthentication(request)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Requests carrying webhook authentication must be RFC 9421 signed'\n );\n }\n return { status: 'unsigned', verified_at: now };\n }\n if (!sigInputHeader || !sigHeader) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Signature and Signature-Input headers must be present as a pair'\n );\n }\n\n // Step 1: parse.\n const parsedInput = parseSignatureInput(sigInputHeader);\n const parsedSig = parseSignature(sigHeader, parsedInput.label);\n rejectNonAsciiHost(request.url);\n validateSingleValuedCoveredHeaders(parsedInput.components, request);\n\n // Step 2: required params present.\n requireParams(parsedInput);\n\n // Step 3: tag match.\n if (parsedInput.params.tag !== REQUEST_SIGNING_TAG) {\n throw new RequestSignatureError(\n 'request_signature_tag_invalid',\n 3,\n `Signature tag must be \"${REQUEST_SIGNING_TAG}\"; got \"${parsedInput.params.tag}\"`\n );\n }\n\n // Step 4: alg allowlist.\n if (!ALLOWED_ALGS.has(parsedInput.params.alg)) {\n throw new RequestSignatureError(\n 'request_signature_alg_not_allowed',\n 4,\n `Signature alg \"${parsedInput.params.alg}\" is not in the AdCP allowlist`\n );\n }\n\n // Step 5: window valid.\n validateWindow(parsedInput.params.created, parsedInput.params.expires, now);\n\n // Step 6: covered components.\n validateCoveredComponents(parsedInput.components, options.capability, request);\n\n // Step 7: resolve keyid.\n const jwk = await options.jwks.resolve(parsedInput.params.keyid);\n if (!jwk) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `No JWK found for keyid \"${parsedInput.params.keyid}\"`\n );\n }\n if (jwk.kid !== parsedInput.params.keyid) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `JWKS resolver returned a JWK whose kid \"${jwk.kid}\" does not match requested keyid \"${parsedInput.params.keyid}\"`\n );\n }\n\n // Step 8: key purpose.\n if (jwk.adcp_use !== 'request-signing' || !jwk.key_ops?.includes('verify')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" is not scoped for request-signing verification`\n );\n }\n validateJwkParameterConsistency(jwk, parsedInput.params.alg);\n\n // Step 9: revocation (runs BEFORE crypto to prevent amplification attacks).\n if (await options.revocationStore.isRevoked(jwk.kid)) {\n throw new RequestSignatureError('request_signature_key_revoked', 9, `JWK \"${jwk.kid}\" is revoked`);\n }\n\n // Replay cache is scoped by `(keyid, @target-uri)` per adcp#2460 — a\n // signature captured on one endpoint (e.g. /create_media_buy) MUST NOT\n // count against the replay budget for a different endpoint under the\n // same keyid. Canonicalize once and reuse for both pre-check and commit.\n const replayScope = canonicalTargetUri(request.url);\n\n // Step 12 pre-checks (rate-abuse cap + replay hit) run before crypto so a\n // compromised-key cache cap or a replayed nonce short-circuits an expensive\n // Ed25519/ECDSA verify. The committing insert happens after step 11.\n if (await options.replayStore.isCapHit(jwk.kid, replayScope, now)) {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n if (await options.replayStore.has(jwk.kid, replayScope, parsedInput.params.nonce, now)) {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n\n // Step 10: cryptographic verify. Use the received Signature-Input value\n // verbatim as the @signature-params line so a peer emitting params in a\n // different legal order still produces a byte-identical base.\n const base = buildSignatureBase(\n parsedInput.components,\n request,\n parsedInput.params,\n parsedInput.signatureParamsValue\n );\n const publicKey = jwkToPublicKey(jwk);\n const valid = verifySignature(parsedInput.params.alg, publicKey, Buffer.from(base, 'utf8'), parsedSig.bytes);\n if (!valid) {\n throw new RequestSignatureError(\n 'request_signature_invalid',\n 10,\n 'Cryptographic verification of signature base failed'\n );\n }\n\n // Step 11: content-digest match (only if covered).\n if (parsedInput.components.includes('content-digest')) {\n const digestHeader = getHeaderValue(request.headers, 'Content-Digest');\n if (!digestHeader || !contentDigestMatches(digestHeader, request.body ?? '')) {\n throw new RequestSignatureError(\n 'request_signature_digest_mismatch',\n 11,\n 'Content-Digest header does not match recomputed body hash'\n );\n }\n }\n\n // Step 12: commit the (keyid, nonce) into the replay cache now that all\n // prior checks have passed. Floor the TTL at one max-window + skew so a\n // signer minting tiny validity windows can't replay outside the configured\n // replay horizon, and so cross-replica eventual consistency has headroom.\n const remaining = parsedInput.params.expires - now + CLOCK_SKEW_TOLERANCE_SECONDS;\n const ttl = Math.max(remaining, MAX_SIGNATURE_WINDOW_SECONDS + CLOCK_SKEW_TOLERANCE_SECONDS);\n const insertResult = await options.replayStore.insert(jwk.kid, replayScope, parsedInput.params.nonce, ttl, now);\n if (insertResult === 'replayed') {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n if (insertResult === 'rate_abuse') {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n\n const agent_url = options.agentUrlForKeyid?.(jwk.kid);\n return { status: 'verified', keyid: jwk.kid, agent_url, verified_at: now };\n}\n\nfunction jsonRpcProtocolMethods(body: string | undefined): string[] {\n if (!body) return [];\n if (exceedsUnsignedBodyInspectionCap(body)) return [];\n try {\n const parsed = JSON.parse(body) as unknown;\n const messages = Array.isArray(parsed) ? parsed : [parsed];\n return messages.flatMap(message =>\n message !== null && typeof message === 'object' && typeof (message as { method?: unknown }).method === 'string'\n ? [(message as { method: string }).method]\n : []\n );\n } catch {\n return [];\n }\n}\n\nfunction requireParams(parsed: ParsedSignatureInput): void {\n const required: Array<keyof ParsedSignatureInput['params']> = ['created', 'expires', 'nonce', 'keyid', 'alg', 'tag'];\n const missing = required.filter(k => parsed.params[k] === undefined);\n if (missing.length) {\n throw new RequestSignatureError(\n 'request_signature_params_incomplete',\n 2,\n `Signature-Input missing required parameter(s): ${missing.join(', ')}`\n );\n }\n}\n\nfunction validateWindow(created: number, expires: number, now: number): void {\n if (expires <= created) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature expires must be strictly greater than created'\n );\n }\n if (expires - created > MAX_SIGNATURE_WINDOW_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n `Signature window exceeds ${MAX_SIGNATURE_WINDOW_SECONDS}s maximum`\n );\n }\n if (now > expires + CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError('request_signature_window_invalid', 5, 'Signature is expired');\n }\n if (now < created - CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature created is in the future beyond skew tolerance'\n );\n }\n}\n\n/**\n * Reject JWKs whose alg/kty/crv are mutually inconsistent OR don't match\n * the sig-params alg. Three checks in one place:\n *\n * 1. JWK MUST declare `alg`. A missing alg lets a signer substitute one\n * algorithm family for another (e.g. EdDSA sig-params against a P-256\n * JWK) and defer failure to step 10's crypto verify — the wrong step\n * and the wrong error code.\n * 2. JWK `alg` MUST match the sig-params `alg` after AdCP→JOSE mapping\n * (`ed25519`↔`EdDSA`, `ecdsa-p256-sha256`↔`ES256`). Prevents sig-params\n * alg-downgrade against a JWK that was published for a stronger alg.\n * 3. JWK kty/crv MUST be consistent with the declared alg per RFC 8037\n * (EdDSA→OKP/Ed25519|Ed448) and RFC 7518 (ES256→EC/P-256).\n */\nfunction validateJwkParameterConsistency(\n jwk: { kid: string; kty?: string; crv?: string; alg?: string },\n sigParamsAlg: string\n): void {\n if (!jwk.alg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" does not declare an alg; cannot bind to sig-params alg=\"${sigParamsAlg}\"`\n );\n }\n const expectedJoseAlg = SIG_PARAMS_ALG_TO_JOSE[sigParamsAlg];\n if (expectedJoseAlg && jwk.alg !== expectedJoseAlg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=${jwk.alg} but the request is signed with alg=\"${sigParamsAlg}\" (JOSE ${expectedJoseAlg})`\n );\n }\n if (jwk.alg === 'EdDSA') {\n if (jwk.kty !== 'OKP' || (jwk.crv !== 'Ed25519' && jwk.crv !== 'Ed448')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=EdDSA but kty/crv (${jwk.kty}/${jwk.crv}) is not a valid Edwards-curve pair`\n );\n }\n return;\n }\n if (jwk.alg === 'ES256') {\n if (jwk.kty !== 'EC' || jwk.crv !== 'P-256') {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=ES256 but kty/crv (${jwk.kty}/${jwk.crv}) is not EC/P-256`\n );\n }\n }\n}\n\nconst SIG_PARAMS_ALG_TO_JOSE: Record<string, string> = {\n ed25519: 'EdDSA',\n 'ecdsa-p256-sha256': 'ES256',\n};\n\n/**\n * Covered components referencing single-valued HTTP header fields\n * (Content-Type per RFC 9110, Content-Digest per RFC 9530) MUST NOT arrive\n * as a comma-joined multi-value. A proxy or buggy client emitting the field\n * twice produces a concatenated value whose meaning relative to the\n * signature base is undefined — reject at parse rather than pick one.\n */\nfunction validateSingleValuedCoveredHeaders(components: string[], request: RequestLike): void {\n if (components.includes('content-type')) {\n const value = getHeaderValue(request.headers, 'Content-Type');\n if (value !== undefined && containsTopLevelComma(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Type header covered by signature must be single-valued; multiple field values are ambiguous'\n );\n }\n }\n if (components.includes('content-digest')) {\n const value = getHeaderValue(request.headers, 'Content-Digest');\n if (value !== undefined) {\n try {\n const dict = parseDictionary(value);\n const seen = new Map<string, number>();\n for (const key of dict.keys()) {\n seen.set(key, (seen.get(key) ?? 0) + 1);\n }\n for (const [key, count] of seen) {\n if (count > 1) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n `Content-Digest declares the \"${key}\" algorithm more than once; duplicate members are ambiguous`\n );\n }\n }\n } catch (e) {\n if (e instanceof RequestSignatureError) throw e;\n }\n // The structured-headers library deduplicates dictionary keys silently.\n // Detect the multi-value case at the raw-string level too so the\n // specific \"two sha-256 members\" shape is caught.\n if (containsDuplicateDictKey(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Digest declares the same algorithm more than once; duplicate members are ambiguous'\n );\n }\n }\n }\n}\n\nfunction containsTopLevelComma(value: string): boolean {\n let inQuoted = false;\n for (let i = 0; i < value.length; i++) {\n const ch = value[i]!;\n if (ch === '\\\\' && i + 1 < value.length && inQuoted) {\n i++;\n continue;\n }\n if (ch === '\"') {\n inQuoted = !inQuoted;\n continue;\n }\n if (ch === ',' && !inQuoted) return true;\n }\n return false;\n}\n\nfunction containsDuplicateDictKey(value: string): boolean {\n const keys: string[] = [];\n let i = 0;\n const len = value.length;\n let atEntryStart = true;\n while (i < len) {\n if (atEntryStart) {\n while (i < len && (value[i] === ' ' || value[i] === '\\t')) i++;\n const start = i;\n while (i < len && /[A-Za-z0-9_*-]/.test(value[i]!)) i++;\n if (i > start) keys.push(value.slice(start, i).toLowerCase());\n atEntryStart = false;\n continue;\n }\n const ch = value[i]!;\n if (ch === '\"') {\n i++;\n while (i < len) {\n if (value[i] === '\\\\' && i + 1 < len) {\n i += 2;\n continue;\n }\n if (value[i] === '\"') {\n i++;\n break;\n }\n i++;\n }\n continue;\n }\n if (ch === ':') {\n i++;\n while (i < len && value[i] !== ':') i++;\n if (i < len) i++;\n continue;\n }\n if (ch === ',') {\n atEntryStart = true;\n i++;\n continue;\n }\n i++;\n }\n const seen = new Set<string>();\n for (const key of keys) {\n if (seen.has(key)) return true;\n seen.add(key);\n }\n return false;\n}\n\n/**\n * Upper bound on request body size we will JSON.parse and walk on the\n * unsigned branch. Real AdCP `update_media_buy` / `create_media_buy` payloads\n * are O(kilobytes); 1 MB is ~1000x that. Over this, require signing rather\n * than spend unbounded pre-crypto CPU on attacker-shaped input — a 1 MB\n * nested-array body would otherwise let an unauthenticated caller force\n * deep recursion on every request.\n */\nconst MAX_UNSIGNED_BODY_INSPECTION_BYTES = 1_048_576;\n/**\n * Upper bound on recursion depth in `containsWebhookAuthentication`. Real\n * AdCP payloads reach depth ~5-8; 64 is generous without leaving room for\n * a pathological `{\"a\":{\"a\":{...}}}` to burn the stack.\n */\nfunction exceedsUnsignedBodyInspectionCap(body: string | undefined): boolean {\n return typeof body === 'string' && body.length > MAX_UNSIGNED_BODY_INSPECTION_BYTES;\n}\n\n/**\n * Scan a JSON request body for a non-empty\n * webhook authentication object — anywhere in the tree, including inside\n * arrays of pending config updates. MCP uses `push_notification_config`;\n * A2A uses `pushNotificationConfig`. Runs only on the\n * unsigned branch; returning `true` triggers the webhook-security downgrade\n * rejection in {@link verifyRequestSignature}.\n *\n * The check is Content-Type independent: an attacker can't evade by\n * labeling a JSON body `text/plain`. A body that fails to parse as JSON\n * returns false so legitimate non-JSON transports (SSE posts, form uploads)\n * aren't blocked; signed-request enforcement for those remains\n * `required_for`-driven. A body over {@link MAX_UNSIGNED_BODY_INSPECTION_BYTES}\n * returns `true` — we can't prove absence of webhook auth within our DoS\n * budget, and oversized unsigned bodies are outside normal AdCP operation.\n */\nfunction carriesWebhookAuthentication(request: RequestLike): boolean {\n const body = request.body;\n if (!body) return false;\n if (exceedsUnsignedBodyInspectionCap(body)) return true;\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return false;\n }\n return containsWebhookAuthentication(parsed, WEBHOOK_AUTH_TRAVERSAL_DEPTH);\n}\n\nfunction validateCoveredComponents(components: string[], capability: VerifierCapability, request: RequestLike): void {\n for (const mandatory of MANDATORY_COMPONENTS) {\n if (!components.includes(mandatory)) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n `Covered components must include \"${mandatory}\"`\n );\n }\n }\n const hasBody = (request.body ?? '').length > 0;\n if (hasBody && !components.includes('content-type')) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Covered components must include \"content-type\" when a request body is present'\n );\n }\n const includesDigest = components.includes('content-digest');\n if (capability.covers_content_digest === 'required' && !includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Verifier requires content-digest coverage'\n );\n }\n if (capability.covers_content_digest === 'forbidden' && includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_unexpected',\n 6,\n 'Verifier forbids content-digest coverage'\n );\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/lib/signing/verifier.ts"],"sourcesContent":["import { parseDictionary } from 'structured-headers';\nimport {\n buildSignatureBase,\n canonicalTargetUri,\n getHeaderValue,\n rejectNonAsciiHost,\n type RequestLike,\n} from './canonicalize';\nimport { contentDigestMatches } from './content-digest';\nimport { RequestSignatureError } from './errors';\nimport { parseSignature, parseSignatureInput, type ParsedSignatureInput } from './parser';\nimport { jwkToPublicKey, verifySignature } from './crypto';\nimport type { JwksResolver } from './jwks';\nimport type { ReplayStore } from './replay';\nimport type { RevocationStore } from './revocation';\nimport { containsWebhookAuthentication, WEBHOOK_AUTH_TRAVERSAL_DEPTH } from './webhook-auth-detection';\nimport { canonicalA2AProtocolMethod, protocolMethodListIncludes } from './protocol-methods';\nimport {\n ALLOWED_ALGS,\n CLOCK_SKEW_TOLERANCE_SECONDS,\n MANDATORY_COMPONENTS,\n MAX_SIGNATURE_WINDOW_SECONDS,\n REQUEST_SIGNING_TAG,\n type VerifierCapability,\n type VerifyResult,\n} from './types';\n\nexport interface VerifyRequestOptions {\n capability: VerifierCapability;\n jwks: JwksResolver;\n replayStore: ReplayStore;\n revocationStore: RevocationStore;\n now?: () => number;\n /**\n * The AdCP operation being requested, used to consult\n * `capability.required_for` when an unsigned request arrives. When omitted,\n * the verifier treats the operation as \"not in any required_for list\" and\n * returns an unsigned result rather than rejecting — callers in\n * always-verify mode (where every request is signed) can leave this blank.\n */\n operation?: string;\n agentUrlForKeyid?: (keyid: string) => string | undefined;\n}\n\nexport async function verifyRequestSignature(\n request: RequestLike,\n options: VerifyRequestOptions\n): Promise<VerifyResult> {\n const now = options.now ? options.now() : Math.floor(Date.now() / 1000);\n const sigInputHeader = getHeaderValue(request.headers, 'Signature-Input');\n const sigHeader = getHeaderValue(request.headers, 'Signature');\n\n // Pre-check: both headers present or both absent.\n if (!sigInputHeader && !sigHeader) {\n const operation = options.operation;\n // Precedence is intentionally fail-specific: AdCP tool required_for,\n // raw JSON-RPC protocol methods, then payload-driven webhook-auth\n // elevation. The specific signed-only contract should win before the\n // generic body scan runs.\n if (operation && options.capability.required_for.includes(operation)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Operation \"${operation}\" requires a signed request`\n );\n }\n const protocolMethodsRequiredFor = options.capability.protocol_methods_required_for ?? [];\n if (protocolMethodsRequiredFor.length > 0 && exceedsUnsignedBodyInspectionCap(request.body)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Unsigned request body exceeds the protocol method inspection cap while protocol methods require signing'\n );\n }\n const protocolMethods = jsonRpcProtocolMethods(request.body);\n const requiredProtocolMethod = protocolMethods.find(wireMethod =>\n protocolMethodListIncludes(protocolMethodsRequiredFor, wireMethod)\n );\n if (requiredProtocolMethod) {\n const declaredMethod = canonicalA2AProtocolMethod(requiredProtocolMethod);\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n `Protocol method \"${declaredMethod}\" requires a signed request` +\n (requiredProtocolMethod === declaredMethod ? '' : ` (wire method \"${requiredProtocolMethod}\")`)\n );\n }\n // Payload-driven elevation: any request carrying webhook receiver\n // credentials (task, reporting, artifact, or revocation callbacks) MUST\n // be RFC 9421 signed, regardless of whether the operation appears in\n // `required_for`\n // (#webhook-security downgrade-resistance). A bearer-only channel\n // would otherwise let an attacker who captured a token register or\n // update webhook credentials and redirect callbacks to a hostile URL.\n if (carriesWebhookAuthentication(request)) {\n throw new RequestSignatureError(\n 'request_signature_required',\n 0,\n 'Requests carrying webhook authentication must be RFC 9421 signed'\n );\n }\n return { status: 'unsigned', verified_at: now };\n }\n if (!sigInputHeader || !sigHeader) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Signature and Signature-Input headers must be present as a pair'\n );\n }\n\n // Step 1: parse.\n const parsedInput = parseSignatureInput(sigInputHeader);\n const parsedSig = parseSignature(sigHeader, parsedInput.label);\n rejectNonAsciiHost(request.url);\n validateSingleValuedCoveredHeaders(parsedInput.components, request);\n\n // Step 2: required params present.\n requireParams(parsedInput);\n\n // Step 3: tag match.\n if (parsedInput.params.tag !== REQUEST_SIGNING_TAG) {\n throw new RequestSignatureError(\n 'request_signature_tag_invalid',\n 3,\n `Signature tag must be \"${REQUEST_SIGNING_TAG}\"; got \"${parsedInput.params.tag}\"`\n );\n }\n\n // Step 4: alg allowlist.\n if (!ALLOWED_ALGS.has(parsedInput.params.alg)) {\n throw new RequestSignatureError(\n 'request_signature_alg_not_allowed',\n 4,\n `Signature alg \"${parsedInput.params.alg}\" is not in the AdCP allowlist`\n );\n }\n\n // Step 5: window valid.\n validateWindow(parsedInput.params.created, parsedInput.params.expires, now);\n\n // Step 6: covered components.\n validateCoveredComponents(parsedInput.components, options.capability, request);\n\n // Step 7: resolve keyid.\n const jwk = await options.jwks.resolve(parsedInput.params.keyid);\n if (!jwk) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `No JWK found for keyid \"${parsedInput.params.keyid}\"`\n );\n }\n if (jwk.kid !== parsedInput.params.keyid) {\n throw new RequestSignatureError(\n 'request_signature_key_unknown',\n 7,\n `JWKS resolver returned a JWK whose kid \"${jwk.kid}\" does not match requested keyid \"${parsedInput.params.keyid}\"`\n );\n }\n\n // Step 8: key purpose.\n if (jwk.adcp_use !== 'request-signing' || !jwk.key_ops?.includes('verify')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" is not scoped for request-signing verification`\n );\n }\n validateJwkParameterConsistency(jwk, parsedInput.params.alg);\n\n // Step 9: revocation (runs BEFORE crypto to prevent amplification attacks).\n if (await options.revocationStore.isRevoked(jwk.kid)) {\n throw new RequestSignatureError('request_signature_key_revoked', 9, `JWK \"${jwk.kid}\" is revoked`);\n }\n\n // Replay cache is scoped by `(keyid, @target-uri)` per adcp#2460 — a\n // signature captured on one endpoint (e.g. /create_media_buy) MUST NOT\n // count against the replay budget for a different endpoint under the\n // same keyid. Canonicalize once and reuse for both pre-check and commit.\n const replayScope = canonicalTargetUri(request.url);\n\n // Step 12 pre-checks (rate-abuse cap + replay hit) run before crypto so a\n // compromised-key cache cap or a replayed nonce short-circuits an expensive\n // Ed25519/ECDSA verify. The committing insert happens after step 11.\n if (await options.replayStore.isCapHit(jwk.kid, replayScope, now)) {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n if (await options.replayStore.has(jwk.kid, replayScope, parsedInput.params.nonce, now)) {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n\n // Step 10: cryptographic verify. Use the received Signature-Input value\n // verbatim as the @signature-params line so a peer emitting params in a\n // different legal order still produces a byte-identical base.\n const base = buildSignatureBase(\n parsedInput.components,\n request,\n parsedInput.params,\n parsedInput.signatureParamsValue\n );\n const publicKey = jwkToPublicKey(jwk);\n const valid = verifySignature(parsedInput.params.alg, publicKey, Buffer.from(base, 'utf8'), parsedSig.bytes);\n if (!valid) {\n throw new RequestSignatureError(\n 'request_signature_invalid',\n 10,\n 'Cryptographic verification of signature base failed'\n );\n }\n\n // Step 11: content-digest match (only if covered).\n if (parsedInput.components.includes('content-digest')) {\n const digestHeader = getHeaderValue(request.headers, 'Content-Digest');\n if (!digestHeader || !contentDigestMatches(digestHeader, request.body ?? '')) {\n throw new RequestSignatureError(\n 'request_signature_digest_mismatch',\n 11,\n 'Content-Digest header does not match recomputed body hash'\n );\n }\n }\n\n // Step 12: commit the (keyid, nonce) into the replay cache now that all\n // prior checks have passed. Floor the TTL at one max-window + skew so a\n // signer minting tiny validity windows can't replay outside the configured\n // replay horizon, and so cross-replica eventual consistency has headroom.\n const remaining = parsedInput.params.expires - now + CLOCK_SKEW_TOLERANCE_SECONDS;\n const ttl = Math.max(remaining, MAX_SIGNATURE_WINDOW_SECONDS + CLOCK_SKEW_TOLERANCE_SECONDS);\n const insertResult = await options.replayStore.insert(jwk.kid, replayScope, parsedInput.params.nonce, ttl, now);\n if (insertResult === 'replayed') {\n throw new RequestSignatureError(\n 'request_signature_replayed',\n 12,\n `Replay of (keyid=${jwk.kid}, nonce=${parsedInput.params.nonce}) within signature window`\n );\n }\n if (insertResult === 'rate_abuse') {\n throw new RequestSignatureError(\n 'request_signature_rate_abuse',\n 12,\n `Per-keyid replay cache cap exceeded for keyid=${jwk.kid}`\n );\n }\n\n const agent_url = options.agentUrlForKeyid?.(jwk.kid);\n return { status: 'verified', keyid: jwk.kid, agent_url, verified_at: now };\n}\n\nfunction jsonRpcProtocolMethods(body: string | undefined): string[] {\n if (!body) return [];\n if (exceedsUnsignedBodyInspectionCap(body)) return [];\n try {\n const parsed = JSON.parse(body) as unknown;\n const messages = Array.isArray(parsed) ? parsed : [parsed];\n return messages.flatMap(message =>\n message !== null && typeof message === 'object' && typeof (message as { method?: unknown }).method === 'string'\n ? [(message as { method: string }).method]\n : []\n );\n } catch {\n return [];\n }\n}\n\nfunction requireParams(parsed: ParsedSignatureInput): void {\n const required: Array<keyof ParsedSignatureInput['params']> = ['created', 'expires', 'nonce', 'keyid', 'alg', 'tag'];\n const missing = required.filter(k => parsed.params[k] === undefined);\n if (missing.length) {\n throw new RequestSignatureError(\n 'request_signature_params_incomplete',\n 2,\n `Signature-Input missing required parameter(s): ${missing.join(', ')}`\n );\n }\n}\n\nfunction validateWindow(created: number, expires: number, now: number): void {\n if (expires <= created) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature expires must be strictly greater than created'\n );\n }\n if (expires - created > MAX_SIGNATURE_WINDOW_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n `Signature window exceeds ${MAX_SIGNATURE_WINDOW_SECONDS}s maximum`\n );\n }\n if (now > expires + CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError('request_signature_window_invalid', 5, 'Signature is expired');\n }\n if (now < created - CLOCK_SKEW_TOLERANCE_SECONDS) {\n throw new RequestSignatureError(\n 'request_signature_window_invalid',\n 5,\n 'Signature created is in the future beyond skew tolerance'\n );\n }\n}\n\n/**\n * Reject JWKs whose alg/kty/crv are mutually inconsistent OR don't match\n * the sig-params alg. Three checks in one place:\n *\n * 1. JWK MUST declare `alg`. A missing alg lets a signer substitute one\n * algorithm family for another (e.g. EdDSA sig-params against a P-256\n * JWK) and defer failure to step 10's crypto verify — the wrong step\n * and the wrong error code.\n * 2. JWK `alg` MUST match the sig-params `alg` after AdCP→JOSE mapping\n * (`ed25519`↔`EdDSA`, `ecdsa-p256-sha256`↔`ES256`). Prevents sig-params\n * alg-downgrade against a JWK that was published for a stronger alg.\n * 3. JWK kty/crv MUST be consistent with the declared alg per RFC 8037\n * (EdDSA→OKP/Ed25519|Ed448) and RFC 7518 (ES256→EC/P-256).\n */\nfunction validateJwkParameterConsistency(\n jwk: { kid: string; kty?: string; crv?: string; alg?: string },\n sigParamsAlg: string\n): void {\n if (!jwk.alg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" does not declare an alg; cannot bind to sig-params alg=\"${sigParamsAlg}\"`\n );\n }\n const expectedJoseAlg = SIG_PARAMS_ALG_TO_JOSE[sigParamsAlg];\n if (expectedJoseAlg && jwk.alg !== expectedJoseAlg) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=${jwk.alg} but the request is signed with alg=\"${sigParamsAlg}\" (JOSE ${expectedJoseAlg})`\n );\n }\n if (jwk.alg === 'EdDSA') {\n if (jwk.kty !== 'OKP' || (jwk.crv !== 'Ed25519' && jwk.crv !== 'Ed448')) {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=EdDSA but kty/crv (${jwk.kty}/${jwk.crv}) is not a valid Edwards-curve pair`\n );\n }\n return;\n }\n if (jwk.alg === 'ES256') {\n if (jwk.kty !== 'EC' || jwk.crv !== 'P-256') {\n throw new RequestSignatureError(\n 'request_signature_key_purpose_invalid',\n 8,\n `JWK \"${jwk.kid}\" declares alg=ES256 but kty/crv (${jwk.kty}/${jwk.crv}) is not EC/P-256`\n );\n }\n }\n}\n\nconst SIG_PARAMS_ALG_TO_JOSE: Record<string, string> = {\n ed25519: 'EdDSA',\n 'ecdsa-p256-sha256': 'ES256',\n};\n\n/**\n * Covered components referencing single-valued HTTP header fields\n * (Content-Type per RFC 9110, Content-Digest per RFC 9530) MUST NOT arrive\n * as a comma-joined multi-value. A proxy or buggy client emitting the field\n * twice produces a concatenated value whose meaning relative to the\n * signature base is undefined — reject at parse rather than pick one.\n */\nfunction validateSingleValuedCoveredHeaders(components: string[], request: RequestLike): void {\n if (components.includes('content-type')) {\n const value = getHeaderValue(request.headers, 'Content-Type');\n if (value !== undefined && containsTopLevelComma(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Type header covered by signature must be single-valued; multiple field values are ambiguous'\n );\n }\n }\n if (components.includes('content-digest')) {\n const value = getHeaderValue(request.headers, 'Content-Digest');\n if (value !== undefined) {\n try {\n const dict = parseDictionary(value);\n const seen = new Map<string, number>();\n for (const key of dict.keys()) {\n seen.set(key, (seen.get(key) ?? 0) + 1);\n }\n for (const [key, count] of seen) {\n if (count > 1) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n `Content-Digest declares the \"${key}\" algorithm more than once; duplicate members are ambiguous`\n );\n }\n }\n } catch (e) {\n if (e instanceof RequestSignatureError) throw e;\n }\n // The structured-headers library deduplicates dictionary keys silently.\n // Detect the multi-value case at the raw-string level too so the\n // specific \"two sha-256 members\" shape is caught.\n if (containsDuplicateDictKey(value)) {\n throw new RequestSignatureError(\n 'request_signature_header_malformed',\n 1,\n 'Content-Digest declares the same algorithm more than once; duplicate members are ambiguous'\n );\n }\n }\n }\n}\n\nfunction containsTopLevelComma(value: string): boolean {\n let inQuoted = false;\n for (let i = 0; i < value.length; i++) {\n const ch = value[i]!;\n if (ch === '\\\\' && i + 1 < value.length && inQuoted) {\n i++;\n continue;\n }\n if (ch === '\"') {\n inQuoted = !inQuoted;\n continue;\n }\n if (ch === ',' && !inQuoted) return true;\n }\n return false;\n}\n\nfunction containsDuplicateDictKey(value: string): boolean {\n const keys: string[] = [];\n let i = 0;\n const len = value.length;\n let atEntryStart = true;\n while (i < len) {\n if (atEntryStart) {\n while (i < len && (value[i] === ' ' || value[i] === '\\t')) i++;\n const start = i;\n while (i < len && /[A-Za-z0-9_*-]/.test(value[i]!)) i++;\n if (i > start) keys.push(value.slice(start, i).toLowerCase());\n atEntryStart = false;\n continue;\n }\n const ch = value[i]!;\n if (ch === '\"') {\n i++;\n while (i < len) {\n if (value[i] === '\\\\' && i + 1 < len) {\n i += 2;\n continue;\n }\n if (value[i] === '\"') {\n i++;\n break;\n }\n i++;\n }\n continue;\n }\n if (ch === ':') {\n i++;\n while (i < len && value[i] !== ':') i++;\n if (i < len) i++;\n continue;\n }\n if (ch === ',') {\n atEntryStart = true;\n i++;\n continue;\n }\n i++;\n }\n const seen = new Set<string>();\n for (const key of keys) {\n if (seen.has(key)) return true;\n seen.add(key);\n }\n return false;\n}\n\n/**\n * Upper bound on request body size we will JSON.parse and walk on the\n * unsigned branch. Real AdCP `update_media_buy` / `create_media_buy` payloads\n * are O(kilobytes); 1 MB is ~1000x that. Over this, require signing rather\n * than spend unbounded pre-crypto CPU on attacker-shaped input — a 1 MB\n * nested-array body would otherwise let an unauthenticated caller force\n * deep recursion on every request.\n */\nconst MAX_UNSIGNED_BODY_INSPECTION_BYTES = 1_048_576;\n/**\n * Upper bound on recursion depth in `containsWebhookAuthentication`. Real\n * AdCP payloads reach depth ~5-8; 64 is generous without leaving room for\n * a pathological `{\"a\":{\"a\":{...}}}` to burn the stack.\n */\nfunction exceedsUnsignedBodyInspectionCap(body: string | undefined): boolean {\n return typeof body === 'string' && body.length > MAX_UNSIGNED_BODY_INSPECTION_BYTES;\n}\n\n/**\n * Scan a JSON request body for a non-empty\n * webhook authentication object — anywhere in the tree, including inside\n * arrays of pending config updates. MCP uses `push_notification_config`;\n * A2A uses `pushNotificationConfig`. Runs only on the\n * unsigned branch; returning `true` triggers the webhook-security downgrade\n * rejection in {@link verifyRequestSignature}.\n *\n * The check is Content-Type independent: an attacker can't evade by\n * labeling a JSON body `text/plain`. A body that fails to parse as JSON\n * returns false so legitimate non-JSON transports (SSE posts, form uploads)\n * aren't blocked; signed-request enforcement for those remains\n * `required_for`-driven. A body over {@link MAX_UNSIGNED_BODY_INSPECTION_BYTES}\n * returns `true` — we can't prove absence of webhook auth within our DoS\n * budget, and oversized unsigned bodies are outside normal AdCP operation.\n */\nfunction carriesWebhookAuthentication(request: RequestLike): boolean {\n const body = request.body;\n if (!body) return false;\n if (exceedsUnsignedBodyInspectionCap(body)) return true;\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch {\n return false;\n }\n return containsWebhookAuthentication(parsed, WEBHOOK_AUTH_TRAVERSAL_DEPTH);\n}\n\nfunction validateCoveredComponents(components: string[], capability: VerifierCapability, request: RequestLike): void {\n for (const mandatory of MANDATORY_COMPONENTS) {\n if (!components.includes(mandatory)) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n `Covered components must include \"${mandatory}\"`\n );\n }\n }\n const hasBody = (request.body ?? '').length > 0;\n if (hasBody && !components.includes('content-type')) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Covered components must include \"content-type\" when a request body is present'\n );\n }\n const includesDigest = components.includes('content-digest');\n if (capability.covers_content_digest === 'required' && !includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_incomplete',\n 6,\n 'Verifier requires content-digest coverage'\n );\n }\n if (capability.covers_content_digest === 'forbidden' && includesDigest) {\n throw new RequestSignatureError(\n 'request_signature_components_unexpected',\n 6,\n 'Verifier forbids content-digest coverage'\n );\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/testing/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/testing/client.ts"],"names":[],"mappings":"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"}
@@ -150,6 +150,9 @@ function createTestClient(agentUrl, protocol = "mcp", options = {}) {
150
150
  },
151
151
  ...options.transport?.requestTimeoutMs !== void 0 && {
152
152
  requestTimeoutMs: options.transport.requestTimeoutMs
153
+ },
154
+ ...options.transport?.legacyCompat?.enabled !== void 0 && {
155
+ legacyCompatEnabled: options.transport.legacyCompat.enabled
153
156
  }
154
157
  },
155
158
  enumerable: false
@@ -182,7 +185,7 @@ function testClientMatchesVersionOptions(client, options) {
182
185
  const expectedAdcpVersion = effectiveOptions.adcpVersion ?? import_version.ADCP_VERSION;
183
186
  const expectedWireAdcpVersion = effectiveOptions.wireAdcpVersion;
184
187
  const expectedVersionEnvelope = effectiveOptions.versionEnvelope ?? "auto";
185
- return meta.adcpVersion === expectedAdcpVersion && meta.wireAdcpVersion === expectedWireAdcpVersion && meta.versionEnvelope === expectedVersionEnvelope && meta.strictResponseSchemaValidation === (effectiveOptions.strictResponseSchemaValidation !== false) && meta.authMode === expectedAuthMode && meta.fetchFn === effectiveOptions.transport?.trustedFetchFn && meta.maxResponseBytes === effectiveOptions.transport?.maxResponseBytes && meta.requestTimeoutMs === effectiveOptions.transport?.requestTimeoutMs;
188
+ return meta.adcpVersion === expectedAdcpVersion && meta.wireAdcpVersion === expectedWireAdcpVersion && meta.versionEnvelope === expectedVersionEnvelope && meta.strictResponseSchemaValidation === (effectiveOptions.strictResponseSchemaValidation !== false) && meta.authMode === expectedAuthMode && meta.fetchFn === effectiveOptions.transport?.trustedFetchFn && meta.maxResponseBytes === effectiveOptions.transport?.maxResponseBytes && meta.requestTimeoutMs === effectiveOptions.transport?.requestTimeoutMs && meta.legacyCompatEnabled === effectiveOptions.transport?.legacyCompat?.enabled;
186
189
  }
187
190
  function withTestKitAuthDefaults(options) {
188
191
  if (options.auth) return options;