ca2a-runtime 0.2.0__tar.gz → 0.3.1__tar.gz

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 (303) hide show
  1. ca2a_runtime-0.3.1/.clusterfuzzlite/Dockerfile +5 -0
  2. ca2a_runtime-0.3.1/.clusterfuzzlite/README.md +60 -0
  3. ca2a_runtime-0.3.1/.clusterfuzzlite/build.sh +29 -0
  4. ca2a_runtime-0.3.1/.clusterfuzzlite/fuzz_attestation_parsers.py +62 -0
  5. ca2a_runtime-0.3.1/.clusterfuzzlite/fuzz_canonical_json.py +89 -0
  6. ca2a_runtime-0.3.1/.clusterfuzzlite/fuzz_wire_documents.py +92 -0
  7. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/CODEOWNERS +7 -7
  8. ca2a_runtime-0.3.1/.github/dependabot.yml +39 -0
  9. ca2a_runtime-0.3.1/.github/workflows/actionlint.yml +41 -0
  10. ca2a_runtime-0.3.1/.github/workflows/cflite_batch.yml +36 -0
  11. ca2a_runtime-0.3.1/.github/workflows/cflite_pr.yml +43 -0
  12. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/workflows/ci.yml +46 -16
  13. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/workflows/codeql.yml +4 -4
  14. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/workflows/contributor-check.yml +1 -1
  15. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/workflows/docker.yml +12 -7
  16. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/workflows/docs.yml +14 -5
  17. ca2a_runtime-0.3.1/.github/workflows/limitations-parity.yml +75 -0
  18. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/workflows/release-drafter.yml +7 -3
  19. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/workflows/release.yml +22 -9
  20. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/workflows/require-maintainer-approval.yml +6 -3
  21. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/workflows/sbom.yml +7 -5
  22. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/workflows/scorecard.yml +2 -2
  23. ca2a_runtime-0.3.1/.github/workflows/vouch-check.yml +111 -0
  24. ca2a_runtime-0.3.1/.github/workflows/vouch-command.yml +89 -0
  25. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/ADOPTERS.md +3 -0
  26. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/CHANGELOG.md +164 -2
  27. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/CHARTER.md +1 -1
  28. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/CONTRIBUTING.md +17 -1
  29. ca2a_runtime-0.3.1/Dockerfile +40 -0
  30. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/GOVERNANCE.md +22 -4
  31. ca2a_runtime-0.3.1/LIMITATIONS.md +104 -0
  32. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/MAINTAINERS.md +15 -1
  33. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/NOTICE +1 -1
  34. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/PKG-INFO +28 -13
  35. ca2a_runtime-0.3.1/PRIVACY.md +9 -0
  36. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/README.md +182 -168
  37. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/ROADMAP.md +1 -1
  38. ca2a_runtime-0.3.1/SPONSORS.md +20 -0
  39. ca2a_runtime-0.3.1/docs/assets/icon.svg +26 -0
  40. ca2a_runtime-0.3.1/docs/concepts.md +63 -0
  41. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/configuration.md +14 -1
  42. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/hardware-validation.md +74 -12
  43. ca2a_runtime-0.3.1/docs/mutual-hardware-acceptance.md +150 -0
  44. ca2a_runtime-0.3.1/docs/quickstart.md +125 -0
  45. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/spec/attestation.md +4 -4
  46. ca2a_runtime-0.3.1/docs/spec/call-graph.md +50 -0
  47. ca2a_runtime-0.3.1/docs/spec/cedar-policy.md +44 -0
  48. ca2a_runtime-0.3.1/docs/spec/component-model.md +39 -0
  49. ca2a_runtime-0.3.1/docs/spec/delegation-chain.md +187 -0
  50. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/spec/error-codes.md +18 -5
  51. ca2a_runtime-0.3.1/docs/spec/failure-modes.md +37 -0
  52. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/spec/mutual-attestation.md +11 -0
  53. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/spec/profile.md +9 -0
  54. ca2a_runtime-0.3.1/docs/spec/provenance-dag.md +45 -0
  55. ca2a_runtime-0.3.1/docs/spec/response-authentication.md +132 -0
  56. ca2a_runtime-0.3.1/docs/spec/response-binding-requirements.md +113 -0
  57. ca2a_runtime-0.3.1/docs/spec/sealed-channel.md +43 -0
  58. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/spec/threat-model.md +4 -1
  59. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/spec/transport.md +48 -3
  60. ca2a_runtime-0.3.1/docs/spec/verification-library.md +55 -0
  61. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/tutorials/authoring-a-delegation-credential.md +32 -12
  62. ca2a_runtime-0.3.1/docs/tutorials/emit-and-verify-provenance.md +104 -0
  63. ca2a_runtime-0.3.1/docs/tutorials/integrating-with-a2a.md +137 -0
  64. ca2a_runtime-0.3.1/docs/tutorials/reproducing-the-claims.md +56 -0
  65. ca2a_runtime-0.3.1/docs/tutorials/verify-a-delegation-chain.md +62 -0
  66. ca2a_runtime-0.3.1/examples/a2a-sdk/loopback.py +333 -0
  67. ca2a_runtime-0.3.1/examples/a2a-sdk/requirements.txt +3 -0
  68. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/README.md +10 -8
  69. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/demo.py +129 -48
  70. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/minimal/README.md +1 -1
  71. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/minimal/ca2a-config.yaml +4 -0
  72. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/rejection-with-proof/README.md +7 -5
  73. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/rejection-with-proof/demo.py +34 -13
  74. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/trace-dag/README.md +3 -2
  75. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/trace-dag/demo.py +16 -7
  76. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim1-attenuation-soundness/run.py +7 -6
  77. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim2-cross-chain-replay/run.py +9 -8
  78. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim3-scope-policy-intersection/run.py +9 -9
  79. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim4-sealed-payload-confidentiality/run.py +2 -2
  80. ca2a_runtime-0.3.1/experiments/hardware-validation/mutual-snp-2026-09-17/README.md +58 -0
  81. ca2a_runtime-0.3.1/experiments/hardware-validation/mutual-snp-2026-09-17/a-to-b.json +168 -0
  82. ca2a_runtime-0.3.1/experiments/hardware-validation/mutual-snp-2026-09-17/b-to-a.json +168 -0
  83. ca2a_runtime-0.3.1/experiments/hardware-validation/mutual-snp-2026-09-17/controls.py +85 -0
  84. ca2a_runtime-0.3.1/hooks/seo.py +102 -0
  85. ca2a_runtime-0.3.1/index.md +74 -0
  86. ca2a_runtime-0.3.1/mkdocs.yml +212 -0
  87. ca2a_runtime-0.3.1/overrides/main.html +130 -0
  88. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/pyproject.toml +8 -7
  89. ca2a_runtime-0.3.1/requirements/agt-overrides.txt +16 -0
  90. ca2a_runtime-0.3.1/requirements/agt.in +11 -0
  91. ca2a_runtime-0.3.1/requirements/agt.txt +1314 -0
  92. ca2a_runtime-0.3.1/requirements/build.in +6 -0
  93. ca2a_runtime-0.3.1/requirements/build.txt +26 -0
  94. ca2a_runtime-0.3.1/requirements/dev.in +27 -0
  95. ca2a_runtime-0.3.1/requirements/dev.txt +2380 -0
  96. ca2a_runtime-0.3.1/requirements/docs.txt +597 -0
  97. ca2a_runtime-0.3.1/requirements/release.in +5 -0
  98. ca2a_runtime-0.3.1/requirements/release.txt +488 -0
  99. ca2a_runtime-0.3.1/requirements/runtime.in +12 -0
  100. ca2a_runtime-0.3.1/requirements/runtime.txt +620 -0
  101. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/requirements-docs.txt +2 -2
  102. ca2a_runtime-0.3.1/robots.txt +38 -0
  103. ca2a_runtime-0.3.1/scripts/gen_action_fixtures.py +233 -0
  104. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/agent_manifest.py +7 -1
  105. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/attestation.py +12 -1
  106. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/benchmarks.py +1 -1
  107. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/bootstrap.py +60 -1
  108. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/canonical.py +26 -3
  109. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/challenge.py +10 -0
  110. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/cli.py +178 -9
  111. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/config.py +55 -0
  112. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/delegation/__init__.py +12 -0
  113. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/delegation/credential.py +108 -8
  114. ca2a_runtime-0.3.1/src/ca2a_runtime/delegation/revocation.py +292 -0
  115. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/errors.py +62 -0
  116. ca2a_runtime-0.3.1/src/ca2a_runtime/hardware_acceptance.py +264 -0
  117. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/node.py +29 -2
  118. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/peer.py +40 -11
  119. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/provenance.py +8 -2
  120. ca2a_runtime-0.3.1/src/ca2a_runtime/response.py +269 -0
  121. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/sev_snp.py +19 -0
  122. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/tdx.py +22 -6
  123. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/trace_binding.py +8 -1
  124. ca2a_runtime-0.3.1/src/ca2a_runtime/transport/_b64url.py +44 -0
  125. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/a2a_adapter.py +3 -35
  126. ca2a_runtime-0.3.1/src/ca2a_runtime/transport/a2a_sdk.py +294 -0
  127. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/client.py +90 -9
  128. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/server.py +16 -3
  129. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/wire.py +29 -6
  130. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/__init__.py +2 -0
  131. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/dag.py +47 -14
  132. ca2a_runtime-0.3.1/src/ca2a_verify/sev_snp.py +186 -0
  133. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/tpm.py +53 -8
  134. ca2a_runtime-0.3.1/src/ca2a_verify/verify.py +130 -0
  135. ca2a_runtime-0.3.1/tests/committed_blobs.py +56 -0
  136. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/conformance/README.md +68 -13
  137. ca2a_runtime-0.3.1/tests/conformance/test_action_fixture_bundles.py +131 -0
  138. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/conformance/test_profile_conformance.py +168 -31
  139. ca2a_runtime-0.3.1/tests/conftest.py +78 -0
  140. ca2a_runtime-0.3.1/tests/fixtures/action/README.md +68 -0
  141. ca2a_runtime-0.3.1/tests/fixtures/action/action-001-verified/chain.json +29 -0
  142. ca2a_runtime-0.3.1/tests/fixtures/action/action-001-verified/dag.json +27 -0
  143. ca2a_runtime-0.3.1/tests/fixtures/action/action-001-verified/expected.json +10 -0
  144. ca2a_runtime-0.3.1/tests/fixtures/action/action-002-parent-hash-mismatch/chain.json +29 -0
  145. ca2a_runtime-0.3.1/tests/fixtures/action/action-002-parent-hash-mismatch/dag.json +27 -0
  146. ca2a_runtime-0.3.1/tests/fixtures/action/action-002-parent-hash-mismatch/expected.json +10 -0
  147. ca2a_runtime-0.3.1/tests/fixtures/action/action-006-policy-denial/chain.json +42 -0
  148. ca2a_runtime-0.3.1/tests/fixtures/action/action-006-policy-denial/dag.json +55 -0
  149. ca2a_runtime-0.3.1/tests/fixtures/action/action-006-policy-denial/expected.json +10 -0
  150. ca2a_runtime-0.3.1/tests/fixtures/action/action-010-scope-escalation/chain.json +27 -0
  151. ca2a_runtime-0.3.1/tests/fixtures/action/action-010-scope-escalation/dag.json +25 -0
  152. ca2a_runtime-0.3.1/tests/fixtures/action/action-010-scope-escalation/expected.json +10 -0
  153. ca2a_runtime-0.3.1/tests/fixtures/action/action-012-credential-expired/chain.json +33 -0
  154. ca2a_runtime-0.3.1/tests/fixtures/action/action-012-credential-expired/dag.json +27 -0
  155. ca2a_runtime-0.3.1/tests/fixtures/action/action-012-credential-expired/expected.json +10 -0
  156. ca2a_runtime-0.3.1/tests/fixtures/response-mac-v1.json +82 -0
  157. ca2a_runtime-0.3.1/tests/fixtures/tpm/bare-rsa-prefix-0016/README.md +19 -0
  158. ca2a_runtime-0.3.1/tests/fixtures/tpm/bare-rsa-prefix-0016/ak-chain.pem +24 -0
  159. ca2a_runtime-0.3.1/tests/fixtures/tpm/bare-rsa-prefix-0016/attest.hex +1 -0
  160. ca2a_runtime-0.3.1/tests/fixtures/tpm/bare-rsa-prefix-0016/signature.hex +1 -0
  161. ca2a_runtime-0.3.1/tests/fixtures/tpm/bare-rsa-prefix-0016/trusted-root.pem +10 -0
  162. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_a2a_sdk_bridge.py +259 -6
  163. ca2a_runtime-0.3.1/tests/unit/test_a2a_sdk_loopback.py +243 -0
  164. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_agent_manifest_cose.py +31 -2
  165. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_bootstrap.py +115 -3
  166. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_canonical.py +32 -0
  167. {ca2a_runtime-0.2.0/tests → ca2a_runtime-0.3.1/tests/unit}/test_challenge.py +18 -0
  168. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim1_attenuation.py +5 -5
  169. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim2_replay.py +4 -3
  170. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_cli_start.py +46 -0
  171. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_committed_examples_verify.py +18 -20
  172. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_config.py +59 -0
  173. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_container_release.py +25 -3
  174. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_delegation.py +35 -11
  175. ca2a_runtime-0.3.1/tests/unit/test_dependency_security_floors.py +77 -0
  176. ca2a_runtime-0.3.1/tests/unit/test_docs_quickstart.py +39 -0
  177. ca2a_runtime-0.3.1/tests/unit/test_documented_experiments.py +59 -0
  178. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_example_rejection_with_proof.py +20 -10
  179. ca2a_runtime-0.3.1/tests/unit/test_hardware_acceptance.py +199 -0
  180. ca2a_runtime-0.3.1/tests/unit/test_lineage_cli.py +149 -0
  181. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_live_call.py +169 -1
  182. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_mutual_attestation.py +41 -0
  183. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_peer_request.py +48 -1
  184. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_release_artifacts.py +1 -1
  185. ca2a_runtime-0.3.1/tests/unit/test_response_authentication.py +452 -0
  186. ca2a_runtime-0.3.1/tests/unit/test_revocation.py +475 -0
  187. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_sev_snp.py +215 -3
  188. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_tdx.py +62 -0
  189. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_tpm_attest.py +157 -8
  190. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_trace_binding.py +179 -9
  191. ca2a_runtime-0.3.1/tests/unit/test_verify.py +139 -0
  192. ca2a_runtime-0.2.0/.github/dependabot.yml +0 -19
  193. ca2a_runtime-0.2.0/Dockerfile +0 -30
  194. ca2a_runtime-0.2.0/LIMITATIONS.md +0 -30
  195. ca2a_runtime-0.2.0/PRIVACY.md +0 -9
  196. ca2a_runtime-0.2.0/docs/assets/icon.svg +0 -29
  197. ca2a_runtime-0.2.0/docs/concepts.md +0 -42
  198. ca2a_runtime-0.2.0/docs/quickstart.md +0 -78
  199. ca2a_runtime-0.2.0/docs/spec/call-graph.md +0 -109
  200. ca2a_runtime-0.2.0/docs/spec/cedar-policy.md +0 -98
  201. ca2a_runtime-0.2.0/docs/spec/component-model.md +0 -63
  202. ca2a_runtime-0.2.0/docs/spec/delegation-chain.md +0 -84
  203. ca2a_runtime-0.2.0/docs/spec/failure-modes.md +0 -151
  204. ca2a_runtime-0.2.0/docs/spec/provenance-dag.md +0 -112
  205. ca2a_runtime-0.2.0/docs/spec/sealed-channel.md +0 -31
  206. ca2a_runtime-0.2.0/docs/spec/verification-library.md +0 -29
  207. ca2a_runtime-0.2.0/docs/stylesheets/extra.css +0 -24
  208. ca2a_runtime-0.2.0/docs/tutorials/emit-and-verify-provenance.md +0 -203
  209. ca2a_runtime-0.2.0/docs/tutorials/integrating-with-a2a.md +0 -171
  210. ca2a_runtime-0.2.0/docs/tutorials/reproducing-the-claims.md +0 -188
  211. ca2a_runtime-0.2.0/docs/tutorials/verify-a-delegation-chain.md +0 -61
  212. ca2a_runtime-0.2.0/mkdocs.yml +0 -179
  213. ca2a_runtime-0.2.0/overrides/main.html +0 -88
  214. ca2a_runtime-0.2.0/src/ca2a_runtime/transport/a2a_sdk.py +0 -141
  215. ca2a_runtime-0.2.0/src/ca2a_verify/sev_snp.py +0 -91
  216. ca2a_runtime-0.2.0/src/ca2a_verify/verify.py +0 -71
  217. ca2a_runtime-0.2.0/tests/unit/test_dependency_security_floors.py +0 -40
  218. ca2a_runtime-0.2.0/tests/unit/test_verify.py +0 -64
  219. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.dockerignore +0 -0
  220. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  221. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  222. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  223. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  224. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/.gitignore +0 -0
  225. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/CNAME +0 -0
  226. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/CODE_OF_CONDUCT.md +0 -0
  227. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/LICENSE +0 -0
  228. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/SECURITY.md +0 -0
  229. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/benchmarks/.gitkeep +0 -0
  230. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/SPEC.md +0 -0
  231. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/assets/og.png +0 -0
  232. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/spec/conformance.md +0 -0
  233. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/docs/spec/trace-a2a-profile.md +0 -0
  234. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/ca2a-config.yaml +0 -0
  235. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/chain.json +0 -0
  236. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/dag.json +0 -0
  237. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/cross-operator-delegation/policy.cedar +0 -0
  238. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/minimal/chain.json +0 -0
  239. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/rejection-with-proof/chain.json +0 -0
  240. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/examples/rejection-with-proof/dag.json +0 -0
  241. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/README.md +0 -0
  242. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim1-attenuation-soundness/README.md +0 -0
  243. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim2-cross-chain-replay/README.md +0 -0
  244. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim3-scope-policy-intersection/README.md +0 -0
  245. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim4-sealed-payload-confidentiality/README.md +0 -0
  246. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim5-provenance-dag-integrity/README.md +0 -0
  247. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim5-provenance-dag-integrity/run.py +0 -0
  248. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim6-cross-operator-attestation/README.md +0 -0
  249. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/claim6-cross-operator-attestation/run.py +0 -0
  250. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/README.md +0 -0
  251. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/ca2a-attested-peer-azure-2026-07-27/bridge.py +0 -0
  252. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/ca2a-attested-peer-azure-2026-07-27/transcript.json +0 -0
  253. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/ca2a-cross-tee-2026-07-27/snp_client.py +0 -0
  254. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/ca2a-cross-tee-2026-07-27/tdx_peer.py +0 -0
  255. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/experiments/hardware-validation/ca2a-cross-tee-2026-07-27/transcript.json +0 -0
  256. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/governance/ca2a-enforcement.yaml +0 -0
  257. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/scripts/gen_agt_evidence.py +0 -0
  258. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/scripts/gen_example_chain.py +0 -0
  259. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/__init__.py +0 -0
  260. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/cedar.py +0 -0
  261. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/channel/__init__.py +0 -0
  262. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/channel/sealed.py +0 -0
  263. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/delegation/holder.py +0 -0
  264. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/policy.py +0 -0
  265. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/__init__.py +0 -0
  266. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/base.py +0 -0
  267. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/binding.py +0 -0
  268. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/software.py +0 -0
  269. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/tpm.py +0 -0
  270. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/tee/tsm.py +0 -0
  271. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/__init__.py +0 -0
  272. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_runtime/transport/constants.py +0 -0
  273. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/tdx.py +0 -0
  274. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/src/ca2a_verify/tpm_roots.py +0 -0
  275. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/__init__.py +0 -0
  276. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/conformance/__init__.py +0 -0
  277. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/conformance/conftest.py +0 -0
  278. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/fixtures/a2a/send_message_ca2a.json +0 -0
  279. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/README.md +0 -0
  280. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/ak.pub +0 -0
  281. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/akcert_0x01C101D0.der +0 -0
  282. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/nonce.hex +0 -0
  283. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/pcrs.bin +0 -0
  284. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/quote.msg +0 -0
  285. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/fixtures/hardware/azure-vtpm-2026-07-27/quote.sig +0 -0
  286. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/fixtures/sev_snp/amd_milan_cert_chain.pem +0 -0
  287. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/fixtures/tdx/intel_sgx_root_ca.pem +0 -0
  288. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/__init__.py +0 -0
  289. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/conftest.py +0 -0
  290. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_a2a_adapter.py +0 -0
  291. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_cedar.py +0 -0
  292. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_channel_and_tee.py +0 -0
  293. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim3_scope_policy.py +0 -0
  294. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim4_sealed.py +0 -0
  295. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim5_provenance.py +0 -0
  296. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_claim6_cross_operator.py +0 -0
  297. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_errors.py +0 -0
  298. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_holder_binding.py +0 -0
  299. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_peer.py +0 -0
  300. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_provenance.py +0 -0
  301. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_sealed_channel.py +0 -0
  302. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_snp_tdx_attest.py +0 -0
  303. {ca2a_runtime-0.2.0 → ca2a_runtime-0.3.1}/tests/unit/test_tpm.py +0 -0
@@ -0,0 +1,5 @@
1
+ FROM gcr.io/oss-fuzz-base/base-builder-python
2
+
3
+ COPY . $SRC/ca2a
4
+ COPY .clusterfuzzlite/build.sh $SRC/build.sh
5
+ WORKDIR $SRC/ca2a
@@ -0,0 +1,60 @@
1
+ # Fuzzing
2
+
3
+ Coverage-guided fuzzing via [ClusterFuzzLite](https://google.github.io/clusterfuzzlite/),
4
+ running Atheris against the input cA2A reads before it has decided to trust the
5
+ party that sent it. Mirrors the setup in agentrust-io/agent-manifest and
6
+ agentrust-io/cmcp.
7
+
8
+ ## What is fuzzed
9
+
10
+ | Target | Surface |
11
+ | --- | --- |
12
+ | `fuzz_wire_documents.py` | JSON from outside: the A2A request `PeerNode.handle` serves (delegation chain, holder proof, caller offer, sealed payload), the authenticated-response envelope, a revocation snapshot, and the chain and TRACE DAG documents the offline verifier loads. |
13
+ | `fuzz_attestation_parsers.py` | TDX quote, SEV-SNP report, TPM quote and TPMT_SIGNATURE parsing, plus `verify_challenge` over caller-supplied challenge text. |
14
+ | `fuzz_canonical_json.py` | The RFC 8785 bytes every credential, holder proof, revocation statement and response MAC is computed over. |
15
+
16
+ ## The properties
17
+
18
+ The wire and parser targets assert that each surface fails closed: it returns,
19
+ or raises the error family it documents (`CA2AError`, and `AttestationFailed`
20
+ for evidence). A `TypeError`, `struct.error`, `AttributeError` or
21
+ `RecursionError` escaping means a caller written against the documented
22
+ exception will not catch it, and on the reference server the connection drops
23
+ with no response.
24
+
25
+ `fuzz_canonical_json.py` asserts a round trip: parsing the canonical output must
26
+ reproduce the input exactly, and integers outside +/-(2**53 - 1) must be
27
+ refused. cA2A's data model has no floats, so the comparison is exact.
28
+
29
+ ## Standing when added
30
+
31
+ A local plain-Python run of each `TestOneInput` (Atheris does not install on the
32
+ Windows machine it was written on) found three escapes that are fixed in the
33
+ same change, with regression tests: `TdxQuote.parse` raising `struct.error` on a
34
+ quote whose signature section is shorter than its declared contents,
35
+ `verify_challenge` raising `TypeError` on a non-ASCII MAC, and
36
+ `DelegationCredential.from_dict` raising `TypeError` on a hop that is not an
37
+ object.
38
+
39
+ ## Bundling gotcha
40
+
41
+ `compile_python_fuzzer` bundles each target with PyInstaller, which follows
42
+ static imports only and bundles code, not package data. `build.sh` passes
43
+ `--collect-submodules=email` for the lazy `email.mime` import in the
44
+ cryptography and pydantic stacks, and `--collect-data` for `agentrust_trace`,
45
+ whose TRACE schema `ca2a_verify.dag` loads at import time.
46
+
47
+ ## Running locally
48
+
49
+ ```
50
+ git clone https://github.com/google/clusterfuzzlite --depth 1 /tmp/clusterfuzzlite
51
+ python /tmp/clusterfuzzlite/infra/helper.py build_image --external $PWD
52
+ python /tmp/clusterfuzzlite/infra/helper.py build_fuzzers --external --sanitizer address $PWD
53
+ python /tmp/clusterfuzzlite/infra/helper.py run_fuzzer --external $PWD fuzz_wire_documents
54
+ ```
55
+
56
+ ## In CI
57
+
58
+ `cflite_pr.yml` fuzzes code the pull request touched, for five minutes.
59
+ `cflite_batch.yml` runs every target for thirty minutes, nightly. Both are
60
+ read-only and time out at 45 minutes; the oss-fuzz base image build dominates.
@@ -0,0 +1,29 @@
1
+ #!/bin/bash -eu
2
+ # Build the fuzz targets for ClusterFuzzLite.
3
+ #
4
+ # Installed rather than put on the path so the targets exercise the same import
5
+ # surface a consumer gets. Third-party dependencies come from the same
6
+ # hash-pinned locks the container image uses, and the package goes in with
7
+ # --no-deps and without build isolation, so nothing here resolves unpinned.
8
+
9
+ cd "$SRC/ca2a"
10
+ pip3 install --no-cache-dir --require-hashes -r requirements/build.txt
11
+ pip3 install --no-cache-dir --require-hashes -r requirements/runtime.txt
12
+ pip3 install --no-cache-dir --no-deps --no-build-isolation .
13
+
14
+ # compile_python_fuzzer bundles each target with PyInstaller, which follows
15
+ # static imports only and bundles code, not package data.
16
+ PYI_ARGS=(
17
+ # The cryptography and pydantic stacks reach email.mime lazily; without this
18
+ # the bundled target dies with ModuleNotFoundError, which libFuzzer reports
19
+ # as a crash in the target.
20
+ --collect-submodules=email
21
+ # ca2a_verify.dag validates records against the TRACE JSON schema, which
22
+ # agentrust_trace loads from inside its package at import time.
23
+ --collect-data=agentrust_trace
24
+ --collect-data=agent_manifest
25
+ )
26
+
27
+ for target in "$SRC"/ca2a/.clusterfuzzlite/fuzz_*.py; do
28
+ compile_python_fuzzer "$target" "${PYI_ARGS[@]}"
29
+ done
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/python3
2
+ """Fuzz the attestation evidence parsers.
3
+
4
+ A callee reads a caller's evidence (``raw_evidence`` in a ``caller_offer``)
5
+ before deciding to trust anything about it, and an auditor feeds recorded
6
+ quotes to ``ca2a_verify`` offline. These parsers read that binary at fixed
7
+ offsets under declared lengths the blob itself supplies: a TDX quote with four
8
+ nested size fields, an SNP report, a TPM quote and a TPMT_SIGNATURE.
9
+
10
+ The property is that each fails closed: it returns, or raises
11
+ AttestationFailed, the error the verifier documents. A struct.error, IndexError
12
+ or OverflowError reaching the caller means a declared length was believed. That
13
+ is the bug this target was written against: TdxQuote.parse used to raise
14
+ struct.error on a quote whose signature section was shorter than it claimed.
15
+
16
+ The challenge verifier is included because its input, the offer nonce and the
17
+ holder proof's challenge, is caller text read before anything authenticates it.
18
+ """
19
+
20
+ import sys
21
+
22
+ import atheris
23
+
24
+ with atheris.instrument_imports():
25
+ from ca2a_runtime.challenge import verify_challenge
26
+ from ca2a_runtime.errors import AttestationFailed
27
+ from ca2a_runtime.tee.sev_snp import SevSnpReport
28
+ from ca2a_runtime.tee.tdx import TdxQuote
29
+ from ca2a_runtime.tee.tpm import TpmQuote
30
+ from ca2a_verify.tpm import parse_tpmt_signature
31
+
32
+ _SECRET = b"\x00" * 32
33
+
34
+ _PARSERS = [
35
+ TdxQuote.parse,
36
+ SevSnpReport.parse,
37
+ TpmQuote.parse,
38
+ parse_tpmt_signature,
39
+ ]
40
+
41
+
42
+ def TestOneInput(data: bytes) -> None:
43
+ if not data:
44
+ return
45
+ fdp = atheris.FuzzedDataProvider(data)
46
+ choice = fdp.ConsumeIntInRange(0, len(_PARSERS))
47
+ try:
48
+ if choice == len(_PARSERS):
49
+ verify_challenge(_SECRET, fdp.ConsumeUnicode(fdp.remaining_bytes()))
50
+ else:
51
+ _PARSERS[choice](fdp.ConsumeBytes(fdp.remaining_bytes()))
52
+ except AttestationFailed:
53
+ pass
54
+
55
+
56
+ def main() -> None:
57
+ atheris.Setup(sys.argv, TestOneInput)
58
+ atheris.Fuzz()
59
+
60
+
61
+ if __name__ == "__main__":
62
+ main()
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/python3
2
+ """Fuzz the RFC 8785 canonicalizer for the invariant its signatures depend on.
3
+
4
+ canonicalize() produces the bytes every delegation credential, holder proof,
5
+ revocation statement and response MAC is computed over, so the bytes must be a
6
+ faithful, lossless encoding of the input. The property asserted is a round trip:
7
+ parsing the canonical output back must reproduce the input exactly.
8
+
9
+ A crash-only check would pass over the bugs that matter here. The ones found in
10
+ sibling canonicalizers were silent: two distinct keys serialized as one key
11
+ twice, a parser kept one of the pair, and a field vanished from a document whose
12
+ signature claimed to cover it.
13
+
14
+ Structure is built from the fuzz data rather than by mutating JSON text, so the
15
+ budget goes on key and value shapes (combining marks, surrogate pairs, control
16
+ characters, integers straddling 2**53) rather than on producing valid JSON.
17
+ """
18
+
19
+ import json
20
+ import sys
21
+
22
+ import atheris
23
+
24
+ with atheris.instrument_imports():
25
+ from ca2a_runtime.canonical import canonicalize
26
+
27
+ _MAX_DEPTH = 4
28
+ _MAX_ITEMS = 6
29
+ _SAFE = 2**53 - 1
30
+
31
+
32
+ def _build(fdp, depth=0):
33
+ if depth >= _MAX_DEPTH or fdp.remaining_bytes() == 0:
34
+ return fdp.ConsumeUnicodeNoSurrogates(16)
35
+ kind = fdp.ConsumeIntInRange(0, 6)
36
+ if kind == 0:
37
+ return None
38
+ if kind == 1:
39
+ return fdp.ConsumeBool()
40
+ if kind == 2:
41
+ # Straddle the safe-integer boundary: outside it canonicalize() must
42
+ # refuse rather than emit digits a conforming verifier would not.
43
+ return fdp.ConsumeIntInRange(-(2**54), 2**54)
44
+ if kind == 3:
45
+ return fdp.ConsumeUnicodeNoSurrogates(64)
46
+ if kind == 4:
47
+ return [_build(fdp, depth + 1) for _ in range(fdp.ConsumeIntInRange(0, _MAX_ITEMS))]
48
+ return {
49
+ fdp.ConsumeUnicodeNoSurrogates(24): _build(fdp, depth + 1)
50
+ for _ in range(fdp.ConsumeIntInRange(0, _MAX_ITEMS))
51
+ }
52
+
53
+
54
+ def _has_unsafe_int(value):
55
+ if isinstance(value, bool):
56
+ return False
57
+ if isinstance(value, int):
58
+ return not -_SAFE <= value <= _SAFE
59
+ if isinstance(value, dict):
60
+ return any(_has_unsafe_int(v) for v in value.values())
61
+ if isinstance(value, list):
62
+ return any(_has_unsafe_int(v) for v in value)
63
+ return False
64
+
65
+
66
+ def TestOneInput(data: bytes) -> None:
67
+ fdp = atheris.FuzzedDataProvider(data)
68
+ value = _build(fdp)
69
+ try:
70
+ out = canonicalize(value)
71
+ except ValueError:
72
+ # Declared: integers outside the RFC 8785 safe domain, and only those.
73
+ assert _has_unsafe_int(value), "canonicalize() refused an in-domain value"
74
+ return
75
+ assert not _has_unsafe_int(value), "an out-of-domain integer was serialized"
76
+
77
+ # cA2A's data model has no floats, so every number re-parses as the int it
78
+ # was and the round trip can be asserted exactly, bools included.
79
+ assert json.loads(out) == value, f"canonical bytes did not round-trip: {out!r}"
80
+ assert canonicalize(json.loads(out)) == out, "canonical form is not a fixed point"
81
+
82
+
83
+ def main() -> None:
84
+ atheris.Setup(sys.argv, TestOneInput)
85
+ atheris.Fuzz()
86
+
87
+
88
+ if __name__ == "__main__":
89
+ main()
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/python3
2
+ """Fuzz the JSON documents cA2A accepts from outside.
3
+
4
+ Each surface takes arbitrary JSON from a party that has not been authenticated
5
+ yet, and each documents one error family for anything malformed:
6
+
7
+ - the A2A request a callee serves (``PeerNode.handle``), which parses the
8
+ delegation chain, holder proof, caller offer and sealed payload, then runs
9
+ the inbound pipeline up to the first refusal: CA2AError;
10
+ - the authenticated-response envelope (``response.decode``):
11
+ ResponseAuthenticationFailed, a CA2AError;
12
+ - a revocation snapshot (``RevocationSnapshot.from_dict``): InvalidRevocation;
13
+ - a delegation chain document and a TRACE DAG handed to the offline verifier:
14
+ CA2AError.
15
+
16
+ Anything else escaping (TypeError, AttributeError, KeyError, UnicodeError,
17
+ RecursionError) is a crash: on the live path it drops the connection with no
18
+ response, and offline it escapes the verifier's contract. The input is decoded
19
+ the way the reference server decodes a body, so what reaches the parsers is
20
+ exactly what the wire can deliver.
21
+ """
22
+
23
+ import contextlib
24
+ import json
25
+ import sys
26
+
27
+ import atheris
28
+
29
+ with atheris.instrument_imports():
30
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
31
+
32
+ from ca2a_runtime import response
33
+ from ca2a_runtime.delegation import RevocationSnapshot
34
+ from ca2a_runtime.errors import CA2AError
35
+ from ca2a_runtime.node import PeerNode
36
+ from ca2a_runtime.policy import LocalPolicy
37
+ from ca2a_verify.dag import verify_trace_dag
38
+ from ca2a_verify.verify import _parse_chain, verify_delegation_chain
39
+
40
+ _ROOT = Ed25519PrivateKey.from_private_bytes(b"\x01" * 32).public_key().public_bytes_raw().hex()
41
+ _NODE = PeerNode(LocalPolicy.of(["read", "write"]), trusted_root_issuers={_ROOT})
42
+
43
+
44
+ def _handle(doc):
45
+ if isinstance(doc, dict):
46
+ _NODE.handle(doc)
47
+
48
+
49
+ def _revocations(doc):
50
+ RevocationSnapshot.from_dict(doc)
51
+
52
+
53
+ def _chain(doc):
54
+ verify_delegation_chain(_parse_chain(doc), trusted_root_issuers={_ROOT})
55
+
56
+
57
+ def _dag(doc):
58
+ if isinstance(doc, dict):
59
+ doc = doc.get("records", doc)
60
+ if isinstance(doc, list) and doc:
61
+ verify_trace_dag(doc, trusted_keys=[])
62
+
63
+
64
+ _JSON_TARGETS = [_handle, _revocations, _chain, _dag]
65
+
66
+
67
+ def TestOneInput(data: bytes) -> None:
68
+ if not data:
69
+ return
70
+ fdp = atheris.FuzzedDataProvider(data)
71
+ choice = fdp.ConsumeIntInRange(0, len(_JSON_TARGETS))
72
+ raw = fdp.ConsumeBytes(fdp.remaining_bytes())
73
+ if choice == len(_JSON_TARGETS):
74
+ with contextlib.suppress(CA2AError):
75
+ response.decode(raw)
76
+ return
77
+ try:
78
+ doc = json.loads(raw)
79
+ except (ValueError, RecursionError):
80
+ # The reference server answers 400 before any parser runs.
81
+ return
82
+ with contextlib.suppress(CA2AError):
83
+ _JSON_TARGETS[choice](doc)
84
+
85
+
86
+ def main() -> None:
87
+ atheris.Setup(sys.argv, TestOneInput)
88
+ atheris.Fuzz()
89
+
90
+
91
+ if __name__ == "__main__":
92
+ main()
@@ -1,14 +1,14 @@
1
1
  # Default owners for all files
2
- * @agentrust-io/maintainers
2
+ * @agentrust-io/maintainers @carloshvp @zohebk8s
3
3
 
4
4
  # Security-sensitive paths require security-reviewers sign-off
5
- src/ca2a_runtime/delegation/ @agentrust-io/security-reviewers @agentrust-io/maintainers
6
- src/ca2a_runtime/channel/ @agentrust-io/security-reviewers @agentrust-io/maintainers
7
- src/ca2a_runtime/tee/ @agentrust-io/security-reviewers @agentrust-io/maintainers
8
- src/ca2a_verify/ @agentrust-io/security-reviewers @agentrust-io/maintainers
5
+ src/ca2a_runtime/delegation/ @agentrust-io/security-reviewers @agentrust-io/maintainers @carloshvp @zohebk8s
6
+ src/ca2a_runtime/channel/ @agentrust-io/security-reviewers @agentrust-io/maintainers @carloshvp @zohebk8s
7
+ src/ca2a_runtime/tee/ @agentrust-io/security-reviewers @agentrust-io/maintainers @carloshvp @zohebk8s
8
+ src/ca2a_verify/ @agentrust-io/security-reviewers @agentrust-io/maintainers @carloshvp @zohebk8s
9
9
 
10
10
  # CI/CD workflow changes
11
- .github/workflows/ @agentrust-io/maintainers
11
+ .github/workflows/ @agentrust-io/maintainers @carloshvp @zohebk8s
12
12
 
13
13
  # Package configuration
14
- pyproject.toml @agentrust-io/maintainers
14
+ pyproject.toml @agentrust-io/maintainers @carloshvp @zohebk8s
@@ -0,0 +1,39 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ day: "monday"
8
+ open-pull-requests-limit: 5
9
+ ignore:
10
+ - dependency-name: "*"
11
+ update-types:
12
+ - "version-update:semver-major"
13
+
14
+ - package-ecosystem: "github-actions"
15
+ directory: "/"
16
+ schedule:
17
+ interval: "weekly"
18
+ day: "monday"
19
+ open-pull-requests-limit: 5
20
+ groups:
21
+ codeql-action:
22
+ patterns:
23
+ - "github/codeql-action*"
24
+
25
+ # Refreshes the digest the Dockerfile pins its base image to. Without this the
26
+ # pin would hold the image at whatever it was on the day it was written.
27
+ - package-ecosystem: "docker"
28
+ directory: "/"
29
+ schedule:
30
+ interval: "weekly"
31
+ day: "monday"
32
+ open-pull-requests-limit: 5
33
+ # Digest refreshes and 3.11 patch releases only. Moving the image to a new
34
+ # Python minor is a deliberate change with its own PR and test run.
35
+ ignore:
36
+ - dependency-name: "python"
37
+ update-types:
38
+ - "version-update:semver-major"
39
+ - "version-update:semver-minor"
@@ -0,0 +1,41 @@
1
+ name: Workflow lint
2
+
3
+ # actionlint catches what a YAML parser cannot: duplicate mapping keys that
4
+ # make GitHub refuse to load a workflow, invalid ${{ }} expressions, and shell
5
+ # problems inside run: blocks. A duplicate env: key silently broke this repo
6
+ # family's release workflow once; yaml.safe_load keeps the last value without
7
+ # complaining, so local validation passed while Actions rejected the file.
8
+ on:
9
+ push:
10
+ branches: [main]
11
+ paths: ['.github/workflows/**']
12
+ pull_request:
13
+ paths: ['.github/workflows/**']
14
+ workflow_dispatch:
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ actionlint:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24
+
25
+ # Fetched and checksum-verified rather than run as a third-party action,
26
+ # so this check adds no new action to the supply chain it exists to guard.
27
+ - name: Install actionlint
28
+ env:
29
+ ACTIONLINT_VERSION: 1.7.12
30
+ ACTIONLINT_SHA256: 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8
31
+ run: |
32
+ set -euo pipefail
33
+ archive="actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
34
+ curl -sSfL -o "$archive" \
35
+ "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/${archive}"
36
+ echo "${ACTIONLINT_SHA256} ${archive}" | sha256sum -c -
37
+ tar -xzf "$archive" actionlint
38
+ install -m 0755 actionlint /usr/local/bin/actionlint
39
+
40
+ - name: Lint workflows
41
+ run: actionlint -color
@@ -0,0 +1,36 @@
1
+ name: ClusterFuzzLite batch
2
+
3
+ on:
4
+ schedule:
5
+ # 04:40 UTC daily, off the hour and off agent-manifest's 04:20 slot.
6
+ - cron: '40 4 * * *'
7
+ workflow_dispatch:
8
+
9
+ permissions: read-all
10
+
11
+ concurrency:
12
+ group: cflite-batch
13
+ cancel-in-progress: false
14
+
15
+ jobs:
16
+ fuzz:
17
+ name: Batch fuzz
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 45
20
+ steps:
21
+ - name: Build fuzzers
22
+ uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1
23
+ with:
24
+ language: python
25
+ github-token: ${{ secrets.GITHUB_TOKEN }}
26
+ sanitizer: address
27
+
28
+ - name: Run fuzzers
29
+ uses: google/clusterfuzzlite/actions/run_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1
30
+ with:
31
+ github-token: ${{ secrets.GITHUB_TOKEN }}
32
+ # Every target, not just what changed. Thirty minutes of fuzzing
33
+ # leaves room for the base image build inside the 45 minute timeout.
34
+ fuzz-seconds: 1800
35
+ mode: batch
36
+ sanitizer: address
@@ -0,0 +1,43 @@
1
+ name: ClusterFuzzLite PR
2
+
3
+ on:
4
+ pull_request:
5
+ paths:
6
+ - 'src/**'
7
+ - 'requirements/build.txt'
8
+ - 'requirements/runtime.txt'
9
+ - '.clusterfuzzlite/**'
10
+ - '.github/workflows/cflite_pr.yml'
11
+
12
+ # Read-only. Findings surface in the job log and the uploaded crash artifact
13
+ # rather than as code-scanning alerts, so no security-events: write is needed.
14
+ permissions: read-all
15
+
16
+ concurrency:
17
+ group: cflite-pr-${{ github.ref }}
18
+ cancel-in-progress: true
19
+
20
+ jobs:
21
+ fuzz:
22
+ name: Fuzz changed code
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 45
25
+ steps:
26
+ # Only the address sanitizer. The targets are pure Python under Atheris,
27
+ # where undefined-behaviour instrumentation has nothing to instrument.
28
+ - name: Build fuzzers
29
+ uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1
30
+ with:
31
+ language: python
32
+ github-token: ${{ secrets.GITHUB_TOKEN }}
33
+ sanitizer: address
34
+
35
+ - name: Run fuzzers
36
+ uses: google/clusterfuzzlite/actions/run_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 # v1
37
+ with:
38
+ github-token: ${{ secrets.GITHUB_TOKEN }}
39
+ fuzz-seconds: 300
40
+ # code-change fuzzes only what the PR touched, which is what keeps
41
+ # this inside a PR's time budget. The nightly batch covers the rest.
42
+ mode: code-change
43
+ sanitizer: address
@@ -12,8 +12,13 @@ on:
12
12
  required: false
13
13
  default: ""
14
14
 
15
+ permissions:
16
+ contents: read
15
17
  jobs:
16
18
  test:
19
+ permissions:
20
+ contents: read
21
+ id-token: write # Authenticate coverage uploads with GitHub OIDC.
17
22
  runs-on: ${{ matrix.os }}
18
23
  strategy:
19
24
  matrix:
@@ -21,17 +26,26 @@ jobs:
21
26
  os: [ubuntu-latest, windows-latest]
22
27
 
23
28
  steps:
24
- - uses: actions/checkout@v7
29
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
25
30
 
26
- - uses: actions/setup-python@v7
31
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
27
32
  with:
28
33
  python-version: ${{ matrix.python-version }}
29
34
 
30
35
  - name: Install dependencies
31
- run: python -m pip install --upgrade pip setuptools && pip install -e ".[dev]"
36
+ # Third-party dependencies come from the hash-pinned lock first; the
37
+ # local package then goes in with --no-deps, because pip cannot
38
+ # hash-pin an editable install in the same invocation.
39
+ run: python -m pip install --require-hashes -r requirements/dev.txt
40
+
41
+ - name: Install local package
42
+ run: python -m pip install --no-deps -e .
32
43
 
33
44
  - name: Security scan
34
- run: pip install bandit pip-audit && bandit -r src/ -c pyproject.toml && pip-audit
45
+ # bandit and pip-audit are already in the dev extra above, so the
46
+ # scan runs against the same pinned versions rather than fetching
47
+ # whatever is current at scan time.
48
+ run: bandit -r src/ -c pyproject.toml && pip-audit
35
49
 
36
50
  - name: Lint
37
51
  run: ruff check src/ tests/
@@ -48,23 +62,31 @@ jobs:
48
62
  run: pytest tests/unit/ tests/conformance/ -v --tb=short --cov=src --cov-report=xml
49
63
 
50
64
  - name: Upload coverage report
51
- uses: codecov/codecov-action@v7
65
+ uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
52
66
  with:
53
- fail_ci_if_error: false
67
+ use_oidc: true
68
+ files: coverage.xml
69
+ fail_ci_if_error: true
54
70
 
55
71
  governance:
56
72
  runs-on: ubuntu-latest
57
73
  needs: test
58
74
 
59
75
  steps:
60
- - uses: actions/checkout@v7
76
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
61
77
 
62
- - uses: actions/setup-python@v7
78
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
63
79
  with:
64
80
  python-version: "3.12"
65
81
 
66
82
  - name: Install dependencies
67
- run: python -m pip install --upgrade pip setuptools && pip install -e ".[dev]"
83
+ # Third-party dependencies come from the hash-pinned lock first; the
84
+ # local package then goes in with --no-deps, because pip cannot
85
+ # hash-pin an editable install in the same invocation.
86
+ run: python -m pip install --require-hashes -r requirements/dev.txt
87
+
88
+ - name: Install local package
89
+ run: python -m pip install --no-deps -e .
68
90
 
69
91
  # AGT 4.1 currently constrains cryptography below 49, while the runtime
70
92
  # requires 50+ for published security fixes. Keep the scanner isolated
@@ -73,8 +95,10 @@ jobs:
73
95
  - name: Install governance tooling in an isolated environment
74
96
  run: |
75
97
  python -m venv .agt-venv
76
- .agt-venv/bin/pip install "agent-governance-toolkit[full]>=4.1"
77
- .agt-venv/bin/pip install --upgrade --no-deps "cryptography>=50.0"
98
+ # --no-deps because the lock already lists every transitive
99
+ # dependency, and pip would otherwise re-resolve and reject the
100
+ # cryptography override the compile deliberately applied.
101
+ .agt-venv/bin/pip install --require-hashes --no-deps -r requirements/agt.txt
78
102
 
79
103
  - name: Generate evidence file
80
104
  run: python scripts/gen_agt_evidence.py
@@ -89,7 +113,7 @@ jobs:
89
113
  run: .agt-venv/bin/agt --json verify --evidence agt-evidence.json > agt-attestation.json
90
114
 
91
115
  - name: Upload governance artifacts
92
- uses: actions/upload-artifact@v7
116
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0
93
117
  with:
94
118
  name: agt-governance-${{ github.sha }}
95
119
  path: |
@@ -103,20 +127,26 @@ jobs:
103
127
  if: github.ref == 'refs/heads/main'
104
128
 
105
129
  steps:
106
- - uses: actions/checkout@v7
130
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
107
131
 
108
- - uses: actions/setup-python@v7
132
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
109
133
  with:
110
134
  python-version: "3.12"
111
135
 
112
136
  - name: Install dependencies
113
- run: python -m pip install --upgrade pip setuptools && pip install -e ".[dev]"
137
+ # Third-party dependencies come from the hash-pinned lock first; the
138
+ # local package then goes in with --no-deps, because pip cannot
139
+ # hash-pin an editable install in the same invocation.
140
+ run: python -m pip install --require-hashes -r requirements/dev.txt
141
+
142
+ - name: Install local package
143
+ run: python -m pip install --no-deps -e .
114
144
 
115
145
  - name: Run benchmark (software-only, CI gate p99 < 5ms)
116
146
  run: python -m ca2a_runtime.benchmarks --provider software-only --hops 10000 --out benchmarks/
117
147
 
118
148
  - name: Upload benchmark results
119
- uses: actions/upload-artifact@v7
149
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0
120
150
  with:
121
151
  name: benchmark-results
122
152
  path: benchmarks/