controlzero 1.6.0__tar.gz → 1.8.0__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 (225) hide show
  1. {controlzero-1.6.0 → controlzero-1.8.0}/.gitignore +7 -0
  2. {controlzero-1.6.0 → controlzero-1.8.0}/CHANGELOG.md +68 -0
  3. {controlzero-1.6.0 → controlzero-1.8.0}/PKG-INFO +15 -8
  4. {controlzero-1.6.0 → controlzero-1.8.0}/README.md +14 -7
  5. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/__init__.py +21 -1
  6. controlzero-1.8.0/controlzero/_internal/action_validator.py +182 -0
  7. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/_internal/bundle.py +15 -1
  8. controlzero-1.8.0/controlzero/_internal/credential_hook.py +339 -0
  9. controlzero-1.8.0/controlzero/_internal/credential_scanner.py +391 -0
  10. controlzero-1.8.0/controlzero/_internal/credentials_data/__init__.py +12 -0
  11. controlzero-1.8.0/controlzero/_internal/credentials_data/built_in.yaml +2259 -0
  12. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/_internal/enforcer.py +11 -0
  13. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/audit_remote.py +349 -6
  14. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/hosts/__init__.py +8 -0
  15. controlzero-1.8.0/controlzero/cli/hosts/antigravity.py +76 -0
  16. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/hosts/base.py +22 -0
  17. controlzero-1.8.0/controlzero/cli/hosts/kiro.py +108 -0
  18. controlzero-1.8.0/controlzero/cli/kiro_adapter.py +129 -0
  19. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/main.py +496 -17
  20. controlzero-1.8.0/controlzero/cli/spool_cmd.py +270 -0
  21. controlzero-1.8.0/controlzero/cli/templates/kiro/ide-file-save.kiro.hook +15 -0
  22. controlzero-1.8.0/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +15 -0
  23. controlzero-1.8.0/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +14 -0
  24. controlzero-1.8.0/controlzero/cli/templates/kiro/kiro.yaml +84 -0
  25. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/client.py +241 -0
  26. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/device.py +30 -7
  27. controlzero-1.8.0/controlzero/error_codes.py +157 -0
  28. controlzero-1.8.0/controlzero/error_codes.yaml +520 -0
  29. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/errors.py +68 -0
  30. controlzero-1.8.0/controlzero/hitl/grant_protocol.py +368 -0
  31. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/hitl/pending_approval.py +12 -2
  32. controlzero-1.8.0/controlzero/hitl/status.py +77 -0
  33. controlzero-1.8.0/controlzero/hooks/__init__.py +8 -0
  34. controlzero-1.8.0/controlzero/hooks/tool_output_handler.py +94 -0
  35. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/policy_loader.py +32 -0
  36. controlzero-1.8.0/controlzero/spool/__init__.py +99 -0
  37. controlzero-1.8.0/controlzero/spool/_compress.py +89 -0
  38. controlzero-1.8.0/controlzero/spool/_constants.py +65 -0
  39. controlzero-1.8.0/controlzero/spool/_crc32c.py +36 -0
  40. controlzero-1.8.0/controlzero/spool/_crypto.py +100 -0
  41. controlzero-1.8.0/controlzero/spool/_frame.py +329 -0
  42. controlzero-1.8.0/controlzero/spool/_metrics.py +74 -0
  43. controlzero-1.8.0/controlzero/spool/_spool.py +1126 -0
  44. controlzero-1.8.0/controlzero/spool/_state.py +154 -0
  45. controlzero-1.8.0/controlzero/spool/_uploader.py +724 -0
  46. controlzero-1.8.0/controlzero/spool/cz-audit-v1.dict +0 -0
  47. controlzero-1.8.0/controlzero/tracecontext.py +130 -0
  48. {controlzero-1.6.0 → controlzero-1.8.0}/pyproject.toml +28 -1
  49. controlzero-1.8.0/tests/spool/__init__.py +0 -0
  50. controlzero-1.8.0/tests/spool/conftest.py +208 -0
  51. controlzero-1.8.0/tests/spool/test_spool_cli.py +168 -0
  52. controlzero-1.8.0/tests/spool/test_spool_concurrency.py +110 -0
  53. controlzero-1.8.0/tests/spool/test_spool_conformance.py +316 -0
  54. controlzero-1.8.0/tests/spool/test_spool_core.py +415 -0
  55. controlzero-1.8.0/tests/spool/test_spool_crash.py +106 -0
  56. controlzero-1.8.0/tests/spool/test_spool_diskfull.py +77 -0
  57. controlzero-1.8.0/tests/spool/test_spool_sink_wiring.py +447 -0
  58. controlzero-1.8.0/tests/spool/test_spool_transcript_localack.py +102 -0
  59. controlzero-1.8.0/tests/spool/test_spool_uploader.py +569 -0
  60. controlzero-1.8.0/tests/test_action_validator_t86.py +112 -0
  61. controlzero-1.8.0/tests/test_conformance.py +335 -0
  62. controlzero-1.8.0/tests/test_credential_hook.py +738 -0
  63. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_dlp_scanner.py +154 -0
  64. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_error_codes.py +58 -0
  65. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hitl_6a_pending_approval.py +10 -0
  66. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hitl_6a_request_approval.py +62 -0
  67. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hitl_6a_wait.py +78 -0
  68. controlzero-1.8.0/tests/test_hitl_conformance.py +287 -0
  69. controlzero-1.8.0/tests/test_hitl_phase2b_protocol.py +568 -0
  70. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hosted_policy_e2e.py +152 -0
  71. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hosts_adapter.py +177 -0
  72. controlzero-1.8.0/tests/test_kiro_adapter.py +166 -0
  73. controlzero-1.8.0/tests/test_kiro_install.py +75 -0
  74. controlzero-1.8.0/tests/test_policy_source_audit.py +149 -0
  75. controlzero-1.8.0/tests/test_tracecontext.py +89 -0
  76. controlzero-1.8.0/tools/cz-kiro-adapter +31 -0
  77. controlzero-1.6.0/controlzero/error_codes.py +0 -572
  78. controlzero-1.6.0/controlzero/hitl/status.py +0 -46
  79. {controlzero-1.6.0 → controlzero-1.8.0}/Dockerfile.test +0 -0
  80. {controlzero-1.6.0 → controlzero-1.8.0}/LICENSE +0 -0
  81. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/_internal/__init__.py +0 -0
  82. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/_internal/action_aliases.py +0 -0
  83. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/_internal/dlp_scanner.py +0 -0
  84. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/_internal/hook_extractors.py +0 -0
  85. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/_internal/tool_extractors.json +0 -0
  86. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/_internal/types.py +0 -0
  87. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/audit_local.py +0 -0
  88. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/canonical.py +0 -0
  89. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/__init__.py +0 -0
  90. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/_secrets.py +0 -0
  91. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/console.py +0 -0
  92. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/debug_bundle.py +0 -0
  93. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/doctor.py +0 -0
  94. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/hosts/claude_code.py +0 -0
  95. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/hosts/codex_cli.py +0 -0
  96. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/hosts/gemini_cli.py +0 -0
  97. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/hosts/unknown.py +0 -0
  98. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/migrate.py +0 -0
  99. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/telemetry_consent.py +0 -0
  100. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/templates/autogen.yaml +0 -0
  101. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/templates/claude-code.yaml +0 -0
  102. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/templates/codex-cli.yaml +0 -0
  103. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/templates/cost-cap.yaml +0 -0
  104. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/templates/crewai.yaml +0 -0
  105. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/templates/cursor.yaml +0 -0
  106. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/templates/gemini-cli.yaml +0 -0
  107. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/templates/generic.yaml +0 -0
  108. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/templates/langchain.yaml +0 -0
  109. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/templates/mcp.yaml +0 -0
  110. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/cli/templates/rag.yaml +0 -0
  111. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/enrollment.py +0 -0
  112. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/hitl/__init__.py +0 -0
  113. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/hitl/mock.py +0 -0
  114. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/hitl/secret_leak_guard.py +0 -0
  115. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/hosted_policy.py +0 -0
  116. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/__init__.py +0 -0
  117. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/anthropic.py +0 -0
  118. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/autogen.py +0 -0
  119. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/braintrust.py +0 -0
  120. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/crewai/__init__.py +0 -0
  121. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/crewai/agent.py +0 -0
  122. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/crewai/crew.py +0 -0
  123. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/crewai/task.py +0 -0
  124. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/crewai/tool.py +0 -0
  125. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/google.py +0 -0
  126. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/google_adk/__init__.py +0 -0
  127. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/google_adk/agent.py +0 -0
  128. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/google_adk/tool.py +0 -0
  129. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/langchain/__init__.py +0 -0
  130. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/langchain/agent.py +0 -0
  131. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/langchain/callbacks.py +0 -0
  132. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/langchain/chain.py +0 -0
  133. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/langchain/graph.py +0 -0
  134. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/langchain/modern.py +0 -0
  135. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/langchain/tool.py +0 -0
  136. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/langfuse.py +0 -0
  137. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/litellm.py +0 -0
  138. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/openai.py +0 -0
  139. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/pydantic_ai.py +0 -0
  140. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/integrations/vercel_ai.py +0 -0
  141. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/layout_migration.py +0 -0
  142. {controlzero-1.6.0 → controlzero-1.8.0}/controlzero/tamper.py +0 -0
  143. {controlzero-1.6.0 → controlzero-1.8.0}/examples/hello_world.py +0 -0
  144. {controlzero-1.6.0 → controlzero-1.8.0}/tests/_fixtures/jcs_args_hash_vectors.json +0 -0
  145. {controlzero-1.6.0 → controlzero-1.8.0}/tests/conftest.py +0 -0
  146. {controlzero-1.6.0 → controlzero-1.8.0}/tests/integrations/__init__.py +0 -0
  147. {controlzero-1.6.0 → controlzero-1.8.0}/tests/integrations/test_google.py +0 -0
  148. {controlzero-1.6.0 → controlzero-1.8.0}/tests/parity/action_aliases.json +0 -0
  149. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_action_aliases.py +0 -0
  150. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_action_canonicalization.py +0 -0
  151. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_agent_name_env.py +0 -0
  152. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_api_key_mask.py +0 -0
  153. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_audit_remote.py +0 -0
  154. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_audit_remote_sdk_version.py +0 -0
  155. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_audit_sink_isolation.py +0 -0
  156. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_bundle_parser.py +0 -0
  157. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_bundle_translate.py +0 -0
  158. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_canonical_phase1a.py +0 -0
  159. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_cli_carve_out.py +0 -0
  160. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_cli_debug_bundle.py +0 -0
  161. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_cli_extractor_integration.py +0 -0
  162. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_cli_hook.py +0 -0
  163. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_cli_hosted_refresh.py +0 -0
  164. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_cli_init.py +0 -0
  165. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_cli_init_templates.py +0 -0
  166. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_cli_tail.py +0 -0
  167. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_cli_test.py +0 -0
  168. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_cli_validate.py +0 -0
  169. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_coding_agent_hooks.py +0 -0
  170. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_conditions.py +0 -0
  171. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_console.py +0 -0
  172. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_default_action.py +0 -0
  173. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_device.py +0 -0
  174. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_doctor.py +0 -0
  175. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_engine_version_consistency.py +0 -0
  176. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_enrollment.py +0 -0
  177. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_env_dump_438.py +0 -0
  178. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_errors_e_codes.py +0 -0
  179. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_fail_closed_eval.py +0 -0
  180. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_glob_matching.py +0 -0
  181. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hitl_5d_email_install.py +0 -0
  182. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hitl_6a_cli_flag.py +0 -0
  183. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hitl_6a_exceptions.py +0 -0
  184. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hitl_6a_get_secret_hitl.py +0 -0
  185. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hitl_6a_mock_backend.py +0 -0
  186. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hitl_6a_secret_leak_guard.py +0 -0
  187. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hitl_reason_codes.py +0 -0
  188. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hitl_validator_keys.py +0 -0
  189. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hook_extractors.py +0 -0
  190. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hybrid_mode_strict.py +0 -0
  191. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_hybrid_mode_warn.py +0 -0
  192. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_install_hook_command.py +0 -0
  193. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_install_hooks.py +0 -0
  194. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_layout_migration_t101.py +0 -0
  195. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_layout_parity_t102.py +0 -0
  196. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_local_mode_dict.py +0 -0
  197. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_local_mode_file_json.py +0 -0
  198. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_local_mode_file_yaml.py +0 -0
  199. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_log_fallback_stderr.py +0 -0
  200. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_log_options_ignored_hosted.py +0 -0
  201. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_log_rotation.py +0 -0
  202. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_migrate.py +0 -0
  203. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_min_sdk_version_gate.py +0 -0
  204. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_multi_client_per_project_175.py +0 -0
  205. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_no_policy_no_key.py +0 -0
  206. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_package_rename_shim.py +0 -0
  207. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_policy_engine_version_phase1b.py +0 -0
  208. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_policy_freshness.py +0 -0
  209. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_policy_settings.py +0 -0
  210. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_quarantine.py +0 -0
  211. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_reason_code.py +0 -0
  212. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_refresh.py +0 -0
  213. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_secrets.py +0 -0
  214. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_sql_semantic_class.py +0 -0
  215. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_synthetic_policy_id_t79.py +0 -0
  216. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_t103_precedence.py +0 -0
  217. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_t104_cache_gc.py +0 -0
  218. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_t108_local_override_audit.py +0 -0
  219. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_t96_single_audit_log.py +0 -0
  220. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_t99_install_prefetch_bundle.py +0 -0
  221. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_tamper.py +0 -0
  222. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_tamper_behavior.py +0 -0
  223. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_tamper_hook.py +0 -0
  224. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_telemetry_consent.py +0 -0
  225. {controlzero-1.6.0 → controlzero-1.8.0}/tests/test_unsafe_int_boundary.py +0 -0
@@ -114,6 +114,9 @@ letsencrypt/
114
114
 
115
115
  # DLP test reports (generated, not committed)
116
116
  reports/
117
+ # ...but committed long-form markdown reports under docs/reports/ are kept
118
+ !docs/reports/
119
+ !docs/reports/**/*.md
117
120
 
118
121
  # MkDocs
119
122
  site/
@@ -246,3 +249,7 @@ doc_local/
246
249
  # Go build/module caches (generated by `go build`, `go test`, etc.)
247
250
  **/.gocache/
248
251
  **/.gomodcache/
252
+ /node_modules
253
+
254
+ # Antigravity (agy) CLI local workspace
255
+ .antigravitycli/
@@ -1,5 +1,73 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.8.0 -- 2026-06-15 (Kiro, epic gh#877)
4
+
5
+ ### Added
6
+
7
+ - **Kiro (AWS agentic IDE) governance.** Control Zero now governs Kiro the
8
+ same way it governs Claude Code / Codex / Gemini: tool calls route through
9
+ the shared `controlzero hook-check` -> policy engine -> audit path, with
10
+ project scope honored and audit rows attributed to `agent="kiro"`
11
+ (`canonical_source` `kiro_cli` / `kiro_ide`).
12
+
13
+ - **Kiro CLI (`kiro-cli chat`): BETA -- full fidelity.** The CLI passes the
14
+ event JSON on stdin in snake_case, byte-compatible with what hook-check
15
+ already consumes, so `preToolUse` / `postToolUse` / `userPromptSubmit`
16
+ governance (including argument-level deny rules) works directly. The new
17
+ `KiroCLIAdapter` claims these by their camelCase event names, which are
18
+ byte-disjoint from Claude Code's PascalCase events.
19
+
20
+ - **Kiro IDE: limited (see below).** The IDE delivers its hook payload via
21
+ the `USER_PROMPT` env var, not stdin. The packaged `cz-kiro-adapter`
22
+ console script normalises that to the stdin shape and pipes it to
23
+ hook-check. **The Prompt Submit and File Save hooks are active** (they
24
+ carry data). **The Pre Tool Use hook ships DISABLED** because upstream
25
+ Kiro (#7375/#7408/#7500, IDE v0.11) currently delivers an empty payload
26
+ (`USER_PROMPT={}`) to IDE `preToolUse` run-command hooks -- with no tool
27
+ context, an enabled hook would fail closed and block every tool call.
28
+ Re-enable it once upstream ships tool context (or set
29
+ `CZ_KIRO_UNKNOWN_PAYLOAD=open` to trade pre-tool policy for availability).
30
+
31
+ - **`controlzero kiro init`** one-command installer (`--surface cli|ide|both`,
32
+ idempotent, `--dry-run`, `--uninstall`). Writes the Kiro hook configs from
33
+ committed templates, installs the `~/.controlzero/policy.yaml` Kiro
34
+ template, and registers the Kiro agent fingerprint.
35
+
36
+ - **Fail-safe defaults.** The IDE shim never crashes the IDE: an empty /
37
+ unparseable payload is handled per `CZ_KIRO_UNKNOWN_PAYLOAD`
38
+ (default `closed` = block, governance-safe), and a missing `controlzero`
39
+ CLI allows rather than bricking the editor.
40
+
41
+ See `docs/integrations/kiro.md` for the covered-vs-bounded matrix and
42
+ `docs/integrations/kiro-hook-payloads.md` for the per-event payload shapes.
43
+
44
+ ## 1.7.0 -- 2026-05-19 (T86, gh#391)
45
+
46
+ ### Added
47
+
48
+ - **Unknown-action validator at policy-load time** (T86, GitHub #391).
49
+ When `controlzero.policy_loader.load_policy()` parses a policy whose
50
+ rules target an action name that is not in the canonical-or-alias
51
+ table (typo, made-up name like `database:queryy`), the loader now
52
+ emits a `logging.WARNING` per offending action with a did-you-mean
53
+ suggestion list. The policy still loads -- the validator is
54
+ warn-not-block at the SDK level (the platform backend blocks publish
55
+ with 422 on the same condition).
56
+
57
+ This catches the silent "rule lands but never fires" class of bug
58
+ that T84's alias shim was created to prevent: a customer typing
59
+ `database:queryy` gets a one-line warning pointing at
60
+ `database:query (legacy)` instead of the rule silently never matching.
61
+
62
+ The validator's known-action set is the union of canonical SDK
63
+ extractor tools, host-tool aliases (e.g. `Read` -> `file_read`), the
64
+ four canonical SQL semantic classes plus every legacy alias from the
65
+ T84 alias table, and wildcards (`*`, `tool:*`, `*:method`). Adding
66
+ a new alias to `_internal/action_aliases.py` automatically widens
67
+ what the validator accepts.
68
+
69
+ See `docs/concepts/policies.md#validation` for the full contract.
70
+
3
71
  ## v1.6.0 -- 2026-05-17 (HITL-6a, gh#542)
4
72
 
5
73
  First minor that turns the Human-in-the-Loop approval workflow on. 1.5.8
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: controlzero
3
- Version: 1.6.0
3
+ Version: 1.8.0
4
4
  Summary: AI agent governance: policies, audit, and observability for tool calls. Works locally with no signup.
5
5
  Project-URL: Homepage, https://controlzero.ai
6
6
  Project-URL: Documentation, https://docs.controlzero.ai
@@ -52,7 +52,7 @@ Provides-Extra: openai
52
52
  Requires-Dist: openai>=1.0.0; extra == 'openai'
53
53
  Description-Content-Type: text/markdown
54
54
 
55
- # control-zero
55
+ # controlzero
56
56
 
57
57
  AI agent governance for Python. Policies, audit, and observability for tool calls.
58
58
  Works locally with no signup.
@@ -116,12 +116,19 @@ The generated `controlzero.yaml` is the tutorial. It ships with annotated
116
116
  rules covering the common patterns: allow lists, deny lists, wildcards, and
117
117
  the catch-all.
118
118
 
119
- Templates available:
119
+ Templates available (`controlzero init -t <name>`):
120
120
 
121
- - `controlzero init` — Hello World template (default)
122
- - `controlzero init -t rag` — RAG agent template (block exfiltration)
123
- - `controlzero init -t mcp` — MCP server template
124
- - `controlzero init -t cost-cap` — model allow-listing and cost guards
121
+ - `generic` — Hello World template (default)
122
+ - `rag` — RAG agent template (block exfiltration)
123
+ - `mcp` — MCP server template
124
+ - `cost-cap` — model allow-listing and cost guards
125
+ - `claude-code` — Claude Code hook starter
126
+ - `langchain` — LangChain tool guardrails
127
+ - `crewai` — CrewAI starter policy
128
+ - `cursor` — Cursor / editor hook starter
129
+ - `autogen` — AutoGen starter policy
130
+ - `codex-cli` — Codex CLI hook starter
131
+ - `gemini-cli` — Gemini CLI hook starter
125
132
 
126
133
  ## Loading a policy
127
134
 
@@ -368,7 +375,7 @@ async def async_poll(request_id: str) -> dict:
368
375
  resolved = await pending.wait_async(async_poll)
369
376
  ```
370
377
 
371
- A built-in HTTP poller is planned for 1.7.0.
378
+ 1.7.0 ships `client.get_secret_poll_fn()` as a built-in poller helper for the secret-read approval path. The general `request_approval()` flow still takes an explicit `poll_fn` so callers can plug their own HTTP stack.
372
379
 
373
380
  ### Mock backend for tests
374
381
 
@@ -1,4 +1,4 @@
1
- # control-zero
1
+ # controlzero
2
2
 
3
3
  AI agent governance for Python. Policies, audit, and observability for tool calls.
4
4
  Works locally with no signup.
@@ -62,12 +62,19 @@ The generated `controlzero.yaml` is the tutorial. It ships with annotated
62
62
  rules covering the common patterns: allow lists, deny lists, wildcards, and
63
63
  the catch-all.
64
64
 
65
- Templates available:
65
+ Templates available (`controlzero init -t <name>`):
66
66
 
67
- - `controlzero init` — Hello World template (default)
68
- - `controlzero init -t rag` — RAG agent template (block exfiltration)
69
- - `controlzero init -t mcp` — MCP server template
70
- - `controlzero init -t cost-cap` — model allow-listing and cost guards
67
+ - `generic` — Hello World template (default)
68
+ - `rag` — RAG agent template (block exfiltration)
69
+ - `mcp` — MCP server template
70
+ - `cost-cap` — model allow-listing and cost guards
71
+ - `claude-code` — Claude Code hook starter
72
+ - `langchain` — LangChain tool guardrails
73
+ - `crewai` — CrewAI starter policy
74
+ - `cursor` — Cursor / editor hook starter
75
+ - `autogen` — AutoGen starter policy
76
+ - `codex-cli` — Codex CLI hook starter
77
+ - `gemini-cli` — Gemini CLI hook starter
71
78
 
72
79
  ## Loading a policy
73
80
 
@@ -314,7 +321,7 @@ async def async_poll(request_id: str) -> dict:
314
321
  resolved = await pending.wait_async(async_poll)
315
322
  ```
316
323
 
317
- A built-in HTTP poller is planned for 1.7.0.
324
+ 1.7.0 ships `client.get_secret_poll_fn()` as a built-in poller helper for the secret-read approval path. The general `request_approval()` flow still takes an explicit `poll_fn` so callers can plug their own HTTP stack.
318
325
 
319
326
  ### Mock backend for tests
320
327
 
@@ -27,13 +27,33 @@ from controlzero.errors import (
27
27
  PolicyValidationError,
28
28
  )
29
29
  from controlzero.hitl import PendingApproval
30
+ from controlzero.hitl.grant_protocol import (
31
+ REASON_HITL_BACKEND_UNREACHABLE,
32
+ REASON_HITL_GRANT_APPROVED,
33
+ REASON_HITL_GRANT_CANCELED,
34
+ REASON_HITL_GRANT_DENIED,
35
+ REASON_HITL_GRANT_EXPIRED,
36
+ REASON_HITL_GRANT_REVOKED,
37
+ REASON_HITL_GRANT_TIMEOUT,
38
+ REASON_HITL_RETRY_GUARD_FAILED,
39
+ REASON_HITL_RETRY_LOOP,
40
+ )
30
41
  from controlzero.policy_loader import load_policy
31
42
 
32
- __version__ = "1.6.0"
43
+ __version__ = "1.8.0"
33
44
 
34
45
  __all__ = [
35
46
  "Client",
36
47
  "PendingApproval",
48
+ "REASON_HITL_BACKEND_UNREACHABLE",
49
+ "REASON_HITL_GRANT_APPROVED",
50
+ "REASON_HITL_GRANT_CANCELED",
51
+ "REASON_HITL_GRANT_DENIED",
52
+ "REASON_HITL_GRANT_EXPIRED",
53
+ "REASON_HITL_GRANT_REVOKED",
54
+ "REASON_HITL_GRANT_TIMEOUT",
55
+ "REASON_HITL_RETRY_GUARD_FAILED",
56
+ "REASON_HITL_RETRY_LOOP",
37
57
  "PolicyDecision",
38
58
  "PolicyDeniedError",
39
59
  "PolicyLoadError",
@@ -0,0 +1,182 @@
1
+ """T86 / GitHub #391 -- unknown-action validator (warn-only at SDK load).
2
+
3
+ Pairs with the backend validator at
4
+ ``apps/control-zero-platform/backend/internal/policy/action_aliases.go``.
5
+ The backend BLOCKS publish on unknown actions (422); the SDK
6
+ WARNS at load time so a customer running local-policy mode (no
7
+ backend) still sees the typo before the rule silently never fires.
8
+
9
+ The known-action set is the union of:
10
+
11
+ - Canonical tools (``database``, ``Bash``, ``http``, ``web_search``,
12
+ ``browser``, ``file_read``, ``file_write``, ``file_search``,
13
+ ``task``) plus their host-tool aliases from the SDK extractor
14
+ spec (``sdks/python/controlzero/controlzero/_internal/tool_extractors.json``).
15
+ - For the ``database`` tool: the four canonical SQL semantic classes
16
+ (``read``/``write``/``admin``/``exec``), every legacy alias from
17
+ the T84 alias table, and the ambiguous ``delete`` alias.
18
+
19
+ For every other tool the validator accepts ANY method (open
20
+ extractor outputs -- Bash basenames, HTTP verbs, browser action
21
+ strings, etc.). Wildcards (``*``, ``tool:*``, ``*:method``) always
22
+ pass.
23
+ """
24
+ from __future__ import annotations
25
+
26
+ from typing import Iterable
27
+
28
+ from controlzero._internal.action_aliases import TOOL as _ALIAS_TOOL
29
+ from controlzero._internal.action_aliases import _AMBIGUOUS, _CLASSES
30
+
31
+ # Mirror of the canonical tool set + host-aliases the extractors
32
+ # accept. Source of truth is tool_extractors.json; this list is
33
+ # updated alongside it.
34
+ _CANONICAL_TOOLS: set[str] = {
35
+ "Bash", "database", "http", "web_search", "browser",
36
+ "file_read", "file_write", "file_search", "task",
37
+ # database aliases
38
+ "sql", "Database", "PostgreSQL", "MySQL", "postgres", "sqlite",
39
+ # Bash aliases
40
+ "bash", "shell", "ShellTool", "run_shell_command",
41
+ "PowerShell", "powershell", "Shell",
42
+ # http aliases
43
+ "fetch", "web_fetch", "WebFetch", "HTTPRequest", "request",
44
+ # web_search aliases
45
+ "WebSearch", "google_web_search", "SearchTool",
46
+ # browser aliases
47
+ "playwright", "Puppeteer",
48
+ # file_read aliases
49
+ "read_file", "Read", "ReadFile", "read_many_files",
50
+ # file_write aliases
51
+ "write_file", "Write", "WriteFile", "edit_file", "Edit",
52
+ "replace", "apply_patch",
53
+ # file_search aliases
54
+ "Grep", "grep_search", "Glob", "glob",
55
+ # task aliases
56
+ "Task", "Agent", "subagent", "spawn_agent",
57
+ }
58
+
59
+ _DATABASE_TOOL_ALIASES = {
60
+ "database", "sql", "Database", "PostgreSQL", "MySQL", "postgres", "sqlite",
61
+ }
62
+
63
+
64
+ def _build_known_database_methods() -> set[str]:
65
+ out: set[str] = {"*"}
66
+ for cls, aliases in _CLASSES.items():
67
+ out.add(cls)
68
+ for a in aliases:
69
+ out.add(a)
70
+ for alias in _AMBIGUOUS:
71
+ out.add(alias)
72
+ return out
73
+
74
+
75
+ _KNOWN_DATABASE_METHODS = _build_known_database_methods()
76
+
77
+
78
+ def is_known_action(action: str) -> bool:
79
+ """Return True if ``action`` is recognised by the SDK extractors / aliases."""
80
+ if not action:
81
+ return False
82
+ if action == "*":
83
+ return True
84
+ if ":" not in action:
85
+ return action in _CANONICAL_TOOLS
86
+ tool, _, method = action.partition(":")
87
+ if tool == "*":
88
+ return True
89
+ if tool not in _CANONICAL_TOOLS:
90
+ return False
91
+ if method == "*" or method == "":
92
+ return True
93
+ if tool in _DATABASE_TOOL_ALIASES:
94
+ return method in _KNOWN_DATABASE_METHODS
95
+ # Other tools: any method accepted (open extractor outputs).
96
+ return True
97
+
98
+
99
+ def _levenshtein(a: str, b: str) -> int:
100
+ if a == b:
101
+ return 0
102
+ if not a:
103
+ return len(b)
104
+ if not b:
105
+ return len(a)
106
+ prev = list(range(len(b) + 1))
107
+ curr = [0] * (len(b) + 1)
108
+ for i in range(1, len(a) + 1):
109
+ curr[0] = i
110
+ for j in range(1, len(b) + 1):
111
+ cost = 0 if a[i - 1] == b[j - 1] else 1
112
+ curr[j] = min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost)
113
+ prev, curr = curr, prev
114
+ return prev[len(b)]
115
+
116
+
117
+ def _shares_tool_prefix(a: str, b: str) -> bool:
118
+ if ":" not in a or ":" not in b:
119
+ return False
120
+ ta, _, ma = a.partition(":")
121
+ tb, _, mb = b.partition(":")
122
+ if ta != tb or not ma or not mb:
123
+ return False
124
+ short = min(len(ma), len(mb))
125
+ overlap = 0
126
+ for i in range(short):
127
+ if ma[i].lower() != mb[i].lower():
128
+ break
129
+ overlap += 1
130
+ return overlap * 2 >= short
131
+
132
+
133
+ def _candidates() -> list[tuple[str, bool]]:
134
+ """Enumerate (name, is_legacy) tuples for suggestion ranking."""
135
+ out: list[tuple[str, bool]] = []
136
+ for cls in _CLASSES:
137
+ out.append((f"{_ALIAS_TOOL}:{cls}", False))
138
+ for aliases in _CLASSES.values():
139
+ for a in aliases:
140
+ out.append((f"{_ALIAS_TOOL}:{a}", True))
141
+ for alias in _AMBIGUOUS:
142
+ out.append((f"{_ALIAS_TOOL}:{alias}", True))
143
+ for tool in _CANONICAL_TOOLS:
144
+ out.append((f"{tool}:*", False))
145
+ return out
146
+
147
+
148
+ def suggest_for_action(action: str, max_suggestions: int = 3) -> list[str]:
149
+ """Return up to ``max_suggestions`` did-you-mean candidates for ``action``."""
150
+ max_distance = 3
151
+ cands = _candidates()
152
+ hits: list[tuple[str, int, bool]] = []
153
+ for name, legacy in cands:
154
+ d = _levenshtein(action, name)
155
+ if d > max_distance and not _shares_tool_prefix(action, name):
156
+ continue
157
+ hits.append((name, d, legacy))
158
+ # Sort by distance, then prefer canonical over legacy, then name.
159
+ hits.sort(key=lambda h: (h[1], h[2], h[0]))
160
+ out: list[str] = []
161
+ for name, _d, legacy in hits[:max_suggestions]:
162
+ out.append(f"{name} (legacy)" if legacy else name)
163
+ return out
164
+
165
+
166
+ def validate_actions(actions: Iterable[str]) -> tuple[list[str], dict[str, list[str]]]:
167
+ """Return (unknown_actions, suggestions_map) for the given action list."""
168
+ unknown: list[str] = []
169
+ suggestions: dict[str, list[str]] = {}
170
+ seen: set[str] = set()
171
+ for a in actions:
172
+ if is_known_action(a):
173
+ continue
174
+ if a in seen:
175
+ continue
176
+ seen.add(a)
177
+ unknown.append(a)
178
+ suggestions[a] = suggest_for_action(a)
179
+ return unknown, suggestions
180
+
181
+
182
+ __all__ = ["is_known_action", "suggest_for_action", "validate_actions"]
@@ -570,7 +570,7 @@ def translate_to_local_policy(payload: dict) -> dict:
570
570
  "reason_code": "NO_ACTIVE_POLICIES",
571
571
  })
572
572
 
573
- return {
573
+ out = {
574
574
  "version": "1",
575
575
  "rules": flat,
576
576
  "settings": {
@@ -580,6 +580,20 @@ def translate_to_local_policy(payload: dict) -> dict:
580
580
  },
581
581
  }
582
582
 
583
+ # #906 / #907 Phase 2 seam (hosted path): carry the bundle-level
584
+ # ``dlp_rules`` array through to the translated local-policy dict under
585
+ # the SAME key the scanner reads (load_dlp_rules_from_policy reads
586
+ # ``dlp_rules``). Without this, a bundle-projected custom DLP rule reaches
587
+ # the hosted ``Client(api_key=...)`` path (via _get_raw_policy_data ->
588
+ # load_dlp_rules_from_policy) as nothing, so only the CLI /api/policy path
589
+ # would enforce custom rules. Only emit the key when present so the
590
+ # translated shape stays byte-identical for bundles without dlp_rules.
591
+ bundle_dlp_rules = payload.get("dlp_rules")
592
+ if isinstance(bundle_dlp_rules, list) and bundle_dlp_rules:
593
+ out["dlp_rules"] = bundle_dlp_rules
594
+
595
+ return out
596
+
583
597
 
584
598
  def make_bundle_missing_policy(
585
599
  default_on_missing: Optional[str] = None,