controlzero 1.13.6__tar.gz → 1.13.8__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 (321) hide show
  1. {controlzero-1.13.6 → controlzero-1.13.8}/CHANGELOG.md +352 -0
  2. {controlzero-1.13.6 → controlzero-1.13.8}/PKG-INFO +2 -1
  3. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/__init__.py +1 -1
  4. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/action_aliases.py +1 -1
  5. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/action_validator.py +23 -2
  6. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/bundle.py +12 -4
  7. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/credential_hook.py +0 -1
  8. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/credential_scanner.py +0 -1
  9. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/dlp_scanner.py +0 -1
  10. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/enforcer.py +556 -22
  11. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/hook_extractors.py +187 -5
  12. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/io_capture_producer.py +4 -3
  13. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/types.py +1 -0
  14. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/audit_remote.py +6 -0
  15. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/canonical.py +2 -1
  16. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/_secrets.py +6 -5
  17. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/console.py +0 -1
  18. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/doctor.py +25 -16
  19. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/__init__.py +0 -1
  20. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/antigravity.py +16 -1
  21. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/base.py +82 -1
  22. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/claude_code.py +67 -1
  23. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/codex_cli.py +51 -1
  24. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/cursor.py +49 -1
  25. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/cursor_cli.py +18 -1
  26. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/gemini_cli.py +36 -1
  27. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/kiro.py +32 -14
  28. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/unknown.py +4 -1
  29. controlzero-1.13.8/controlzero/cli/kiro_adapter.py +901 -0
  30. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/main.py +417 -41
  31. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/migrate.py +5 -6
  32. controlzero-1.13.8/controlzero/cli/setup_wizard.py +481 -0
  33. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/spool_cmd.py +3 -0
  34. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/telemetry_consent.py +4 -5
  35. controlzero-1.13.8/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +15 -0
  36. controlzero-1.13.8/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +14 -0
  37. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/kiro/kiro.yaml +9 -6
  38. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/client.py +114 -24
  39. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/enrollment.py +10 -2
  40. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/error_codes.py +5 -5
  41. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/error_codes.yaml +9 -6
  42. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/errors.py +3 -2
  43. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/grant_protocol.py +4 -2
  44. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/mock.py +7 -7
  45. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/pending_approval.py +30 -3
  46. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hook_integrity.py +2 -1
  47. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hosted_policy.py +94 -3
  48. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/anthropic.py +5 -1
  49. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/autogen.py +2 -1
  50. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/crewai/agent.py +8 -6
  51. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/crewai/crew.py +18 -14
  52. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/crewai/task.py +11 -4
  53. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/crewai/tool.py +6 -4
  54. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/google.py +11 -3
  55. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/google_adk/agent.py +9 -7
  56. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/google_adk/tool.py +8 -5
  57. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/agent.py +14 -14
  58. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/callbacks.py +27 -23
  59. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/chain.py +10 -10
  60. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/graph.py +25 -25
  61. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/modern.py +2 -1
  62. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/tool.py +10 -7
  63. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/litellm.py +7 -2
  64. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/openai.py +6 -2
  65. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/pydantic_ai.py +6 -2
  66. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/vercel_ai.py +6 -2
  67. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/policy_loader.py +7 -2
  68. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/secrets/reference.py +2 -2
  69. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/secrets/resolver.py +9 -8
  70. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_keyring.py +65 -5
  71. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/tamper.py +3 -1
  72. {controlzero-1.13.6 → controlzero-1.13.8}/pyproject.toml +6 -1
  73. controlzero-1.13.8/ruff.toml +40 -0
  74. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_conformance.py +1 -1
  75. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_crash.py +1 -1
  76. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_durable_default_tamper.py +1 -2
  77. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_keychain_dek.py +142 -7
  78. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_uploader.py +1 -1
  79. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_action_validator_t86.py +17 -0
  80. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_adapter_contracts.py +0 -1
  81. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_adapter_interface.py +16 -4
  82. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_adapter.py +14 -2
  83. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_envelope_parity.py +13 -1
  84. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_ga_blockers_1248.py +16 -5
  85. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_tool_vocab_1303.py +0 -1
  86. controlzero-1.13.8/tests/test_api_url_config.py +72 -0
  87. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_audit_project_id_attribution_pr2.py +0 -1
  88. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_audit_remote.py +0 -1
  89. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_audit_remote_io_capture.py +0 -1
  90. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_audit_remote_sdk_version.py +1 -2
  91. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_bundle_cache_freshness_1303.py +1 -2
  92. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_bundle_parser.py +0 -1
  93. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_bundle_translate.py +0 -2
  94. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_debug_bundle.py +0 -1
  95. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_extractor_integration.py +0 -1
  96. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_hook.py +5 -0
  97. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_init_templates.py +1 -1
  98. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_condition_operators_1495.py +0 -1
  99. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_conditions.py +0 -1
  100. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_credential_hook.py +0 -1
  101. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cross_adapter_shell_enforcement.py +0 -1
  102. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cursor_adapter.py +14 -3
  103. controlzero-1.13.8/tests/test_dashboard_shell_template_1904.py +746 -0
  104. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_default_action.py +0 -1
  105. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_dlp_mask.py +0 -1
  106. controlzero-1.13.8/tests/test_dlp_mask_hosts_1938.py +164 -0
  107. controlzero-1.13.8/tests/test_dlp_origin_contract.py +620 -0
  108. controlzero-1.13.8/tests/test_dlp_scan_coverage_1927_1931.py +634 -0
  109. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_dlp_scanner.py +7 -7
  110. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_enrollment.py +10 -8
  111. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_epic_1247_bryan_acceptance.py +6 -4
  112. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_errors_e_codes.py +0 -1
  113. controlzero-1.13.8/tests/test_git_target_1734.py +242 -0
  114. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_5d_email_install.py +3 -3
  115. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_cli_flag.py +0 -1
  116. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_get_secret_hitl.py +26 -26
  117. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_mock_backend.py +0 -1
  118. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_request_approval.py +1 -2
  119. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_secret_leak_guard.py +0 -1
  120. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_wait.py +16 -7
  121. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_conformance.py +10 -11
  122. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_phase2b_protocol.py +29 -8
  123. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_reason_codes.py +5 -3
  124. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hook_command_pinning_1705.py +0 -1
  125. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hook_extractors.py +0 -1
  126. controlzero-1.13.8/tests/test_hook_warn_surfaces_reason_1903.py +479 -0
  127. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hosted_local_audit_1247.py +2 -0
  128. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hosted_policy_e2e.py +4 -3
  129. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hosts_adapter.py +29 -18
  130. controlzero-1.13.8/tests/test_install_api_url.py +69 -0
  131. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_install_hook_command.py +0 -1
  132. controlzero-1.13.8/tests/test_kiro_adapter.py +1227 -0
  133. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_layout_migration_t101.py +0 -1
  134. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_localized_reason.py +1 -0
  135. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_multi_client_per_project_175.py +8 -5
  136. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_per_client_isolation_1011.py +1 -2
  137. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_policy_freshness.py +0 -1
  138. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_policy_load_failclosed_f3.py +3 -4
  139. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_pr4_install_messaging.py +0 -1
  140. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_quarantine.py +0 -1
  141. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_reason_code.py +8 -3
  142. controlzero-1.13.8/tests/test_reason_code_hitl_grant_1596.py +34 -0
  143. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_secrets.py +0 -2
  144. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_secrets_reference_resolver.py +1 -2
  145. controlzero-1.13.8/tests/test_setup_wizard.py +196 -0
  146. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_shell_semantic_class.py +0 -1
  147. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_sql_semantic_class.py +0 -1
  148. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_synthetic_policy_id_t79.py +10 -5
  149. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_t103_precedence.py +0 -1
  150. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_t108_local_override_audit.py +0 -1
  151. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_t96_single_audit_log.py +0 -1
  152. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_t99_install_prefetch_bundle.py +0 -1
  153. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_tamper.py +0 -1
  154. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_tamper_behavior.py +1 -1
  155. controlzero-1.13.8/tests/test_tls_ca_bundle.py +112 -0
  156. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_unsafe_int_boundary.py +0 -1
  157. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_update_cmd.py +2 -1
  158. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_version_aware_fail_closed.py +1 -0
  159. controlzero-1.13.6/controlzero/cli/kiro_adapter.py +0 -223
  160. controlzero-1.13.6/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +0 -15
  161. controlzero-1.13.6/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +0 -14
  162. controlzero-1.13.6/tests/test_kiro_adapter.py +0 -257
  163. {controlzero-1.13.6 → controlzero-1.13.8}/.gitignore +0 -0
  164. {controlzero-1.13.6 → controlzero-1.13.8}/Dockerfile.test +0 -0
  165. {controlzero-1.13.6 → controlzero-1.13.8}/LICENSE +0 -0
  166. {controlzero-1.13.6 → controlzero-1.13.8}/README.md +0 -0
  167. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/__main__.py +0 -0
  168. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/__init__.py +0 -0
  169. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/_messages.py +0 -0
  170. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/condition_ops.py +0 -0
  171. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/credentials_data/__init__.py +0 -0
  172. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/credentials_data/built_in.yaml +0 -0
  173. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/source_labels.py +0 -0
  174. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/tool_extractors.json +0 -0
  175. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/audit_local.py +0 -0
  176. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/__init__.py +0 -0
  177. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/__main__.py +0 -0
  178. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/agent_config.py +0 -0
  179. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/debug_bundle.py +0 -0
  180. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/enforcement.py +0 -0
  181. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/exec_cmd.py +0 -0
  182. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hook_entry.py +0 -0
  183. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/output.py +0 -0
  184. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/antigravity/hooks.json +0 -0
  185. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/antigravity.yaml +0 -0
  186. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/autogen.yaml +0 -0
  187. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/claude-code.yaml +0 -0
  188. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/codex-cli.yaml +0 -0
  189. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/cost-cap.yaml +0 -0
  190. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/crewai.yaml +0 -0
  191. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/cursor.yaml +0 -0
  192. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/gemini-cli.yaml +0 -0
  193. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/generic.yaml +0 -0
  194. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/kiro/ide-file-save.kiro.hook +0 -0
  195. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/langchain.yaml +0 -0
  196. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/mcp.yaml +0 -0
  197. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/rag.yaml +0 -0
  198. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/update_cmd.py +0 -0
  199. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/device.py +0 -0
  200. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/__init__.py +0 -0
  201. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/secret_leak_guard.py +0 -0
  202. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/status.py +0 -0
  203. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hooks/__init__.py +0 -0
  204. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hooks/tool_output_handler.py +0 -0
  205. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/__init__.py +0 -0
  206. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/braintrust.py +0 -0
  207. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/crewai/__init__.py +1 -1
  208. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/google_adk/__init__.py +0 -0
  209. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/__init__.py +3 -3
  210. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langfuse.py +0 -0
  211. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/layout_migration.py +0 -0
  212. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/secrets/__init__.py +0 -0
  213. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/secrets/secretstr.py +0 -0
  214. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/__init__.py +0 -0
  215. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_compress.py +0 -0
  216. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_constants.py +0 -0
  217. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_crc32c.py +0 -0
  218. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_crypto.py +0 -0
  219. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_frame.py +0 -0
  220. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_metrics.py +0 -0
  221. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_spool.py +0 -0
  222. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_state.py +0 -0
  223. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_uploader.py +0 -0
  224. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/cz-audit-v1.dict +0 -0
  225. {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/tracecontext.py +0 -0
  226. {controlzero-1.13.6 → controlzero-1.13.8}/examples/hello_world.py +0 -0
  227. {controlzero-1.13.6 → controlzero-1.13.8}/tests/_fixtures/antigravity-envelope-expected.json +0 -0
  228. {controlzero-1.13.6 → controlzero-1.13.8}/tests/_fixtures/jcs_args_hash_vectors.json +0 -0
  229. {controlzero-1.13.6 → controlzero-1.13.8}/tests/conftest.py +0 -0
  230. {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/antigravity.json +0 -0
  231. {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/claude_code.json +0 -0
  232. {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/codex_cli.json +0 -0
  233. {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/cursor.json +0 -0
  234. {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/gemini_cli.json +0 -0
  235. {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/kiro.json +0 -0
  236. {controlzero-1.13.6 → controlzero-1.13.8}/tests/integrations/__init__.py +0 -0
  237. {controlzero-1.13.6 → controlzero-1.13.8}/tests/integrations/test_google.py +0 -0
  238. {controlzero-1.13.6 → controlzero-1.13.8}/tests/parity/action_aliases.json +0 -0
  239. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/__init__.py +0 -0
  240. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/conftest.py +0 -0
  241. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_cli.py +0 -0
  242. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_concurrency.py +0 -0
  243. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_core.py +0 -0
  244. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_diskfull.py +0 -0
  245. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_sink_wiring.py +0 -0
  246. {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_transcript_localack.py +0 -0
  247. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_action_aliases.py +0 -0
  248. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_action_canonicalization.py +0 -0
  249. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_agent_name_env.py +0 -0
  250. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_agy_docs_no_exit2_1373.py +0 -0
  251. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_hook_check.py +0 -0
  252. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_install.py +0 -0
  253. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_posttooluse_observe_58.py +0 -0
  254. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_api_key_mask.py +0 -0
  255. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_audit_sink_isolation.py +0 -0
  256. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_canonical_phase1a.py +0 -0
  257. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_capability_matrix_doc.py +0 -0
  258. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_carve_out.py +0 -0
  259. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_hook_entry.py +0 -0
  260. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_hosted_refresh.py +0 -0
  261. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_init.py +0 -0
  262. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_tail.py +0 -0
  263. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_test.py +0 -0
  264. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_validate.py +0 -0
  265. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_coding_agent_hooks.py +0 -0
  266. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_config_format_parity_1303.py +0 -0
  267. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_conformance.py +0 -0
  268. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_console.py +0 -0
  269. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_deny_rule_reconciliation_1011.py +0 -0
  270. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_deny_wins_default_catchall_1620.py +0 -0
  271. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_device.py +0 -0
  272. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_doctor.py +0 -0
  273. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_engine_version_consistency.py +0 -0
  274. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_env_dump_438.py +0 -0
  275. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_error_codes.py +0 -0
  276. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_fail_closed_eval.py +0 -0
  277. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_failopen_1303.py +1 -1
  278. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_glob_matching.py +0 -0
  279. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_exceptions.py +0 -0
  280. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_pending_approval.py +0 -0
  281. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_validator_keys.py +0 -0
  282. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hook_integrity.py +0 -0
  283. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hook_mask_failclosed.py +0 -0
  284. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hosted_local_shadow_warn_1265.py +0 -0
  285. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hybrid_mode_strict.py +0 -0
  286. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hybrid_mode_warn.py +0 -0
  287. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_install_hooks.py +4 -4
  288. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_io_capture_producer.py +0 -0
  289. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_cli_e2e.py +0 -0
  290. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_cli_hook_pin_1265.py +0 -0
  291. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_cli_v210_hooks.py +0 -0
  292. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_hook_templates.py +0 -0
  293. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_install.py +0 -0
  294. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_install_standardisation.py +0 -0
  295. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_layout_parity_t102.py +0 -0
  296. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_local_mode_dict.py +0 -0
  297. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_local_mode_file_json.py +0 -0
  298. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_local_mode_file_yaml.py +0 -0
  299. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_log_fallback_stderr.py +0 -0
  300. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_log_options_ignored_hosted.py +0 -0
  301. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_log_rotation.py +0 -0
  302. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_migrate.py +0 -0
  303. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_min_sdk_version_gate.py +0 -0
  304. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_no_policy_no_key.py +0 -0
  305. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_observe_mode_1247.py +0 -0
  306. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_package_rename_shim.py +0 -0
  307. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_part3_active_policy_count_1303.py +1 -1
  308. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_policy_engine_version_phase1b.py +0 -0
  309. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_policy_settings.py +0 -0
  310. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_policy_source_audit.py +0 -0
  311. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_pr4_output.py +0 -0
  312. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_pr4_status.py +0 -0
  313. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_probe_audit_isolation.py +0 -0
  314. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_refresh.py +1 -1
  315. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_source_labels.py +0 -0
  316. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_t104_cache_gc.py +0 -0
  317. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_tamper_hook.py +0 -0
  318. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_telemetry_consent.py +0 -0
  319. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_tracecontext.py +0 -0
  320. {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_upgrade_nudge.py +0 -0
  321. {controlzero-1.13.6 → controlzero-1.13.8}/tools/cz-kiro-adapter +0 -0
@@ -1,5 +1,357 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.13.8 (2026-07-27): warn decisions reach the agent + Kiro IDE adapter unblocked (P0)
4
+
5
+ ### Added
6
+
7
+ - **Branch-aware `git` actions and cloud-CLI subcommand actions (#1734, #1751).**
8
+ The #437 semantic-class layer classified a shell command's danger class but
9
+ never exposed its operand, so `git push origin main` and
10
+ `git push origin feature/x` both collapsed to `Bash:git` -- a policy could not
11
+ allow feature-branch pushes while denying a push to a protected branch. A
12
+ `Bash` call now additionally promotes `git <sub> ... <ref>` into a matchable
13
+ `git_<sub>:<target>` action (no ref named on the line -> `git_<sub>:*`) and
14
+ `aws` / `gcloud` / `kubectl` into `<cli>:<sub>`, so a rule can
15
+ `deny git_push:main`, `deny git_push:release/*` or `deny aws:s3` and let
16
+ everything else fall through to `default_action`. Refspecs reduce to their
17
+ destination (`src:dst` -> `dst`, a leading `+` stripped), `checkout -b` /
18
+ `switch -c NAME` resolve to the new branch name, and `git -C <dir>` /
19
+ `-c <name=value>` global options are skipped. The per-command `Bash:git`
20
+ action is still emitted, so existing `Bash:*` rules are unaffected.
21
+ Byte-identical to the Node and Go SDKs (`git_command_cases` and
22
+ `cloud_subcommand_cases` in `parity-cases.json`).
23
+ **Upgrade impact:** these are NEW candidate actions evaluated on every `Bash`
24
+ call. A policy that already carries a rule in the `git_*`, `aws:`, `gcloud:`
25
+ or `kubectl:` namespace will now match shell commands it never matched
26
+ before -- audit those rules before upgrading.
27
+ **Authoring note:** use explicit branch names or a namespace glob
28
+ (`release/*`). A bare `git_push:*` as an "any nested branch" matcher is not
29
+ portable: Python `fnmatch`, Node `minimatch` and Go `path.Match` disagree on
30
+ whether `*` crosses `/`.
31
+
32
+ ### Fixed
33
+
34
+ - **DLP never scanned a `warn` decision, so a `warn` catch-all silently
35
+ disabled data-leak scanning for an org's whole shell surface (#1927, P0).**
36
+ The scan gate in `_internal/enforcer.py` read
37
+ `if decision.allowed and args and self._dlp_scanner is not None:`.
38
+ `.allowed` is `effect == "allow"`; a `warn` is neither `.allowed` nor
39
+ `.denied`, so a warn-matched call proceeded to the tool with its arguments
40
+ NEVER scanned. Our own shipped "Shell Command Restrictions" template carried
41
+ an unconditional `warn bash:*` catch-all, so every shell call in an org using
42
+ it was exempt -- reported by a customer as "DLP isn't working". The
43
+ half-canonical `audit` policy effect is now rejected explicitly instead of
44
+ falling through differently across SDK and backend consumers. The gate is now a positive
45
+ membership test against `DLP_SCANNED_EFFECTS`, derived as
46
+ `ALL_EFFECTS - {"deny"}`, so every effect whose call PROCEEDS is scanned and
47
+ any effect added in future is scanned by default rather than silently
48
+ exempted.
49
+ - **The no-match default was structurally unreachable from the DLP scan
50
+ (#1931, P0).** That same gate sat INSIDE the per-rule loop, two lines before
51
+ its `return decision`. `evaluate()`'s no-match fallthrough returns from
52
+ outside the loop, so `default_action: allow` and `default_action: warn` --
53
+ the steady state for every allow-by-default org, on every call that matches
54
+ no rule -- were never scanned at all. Both decision origins now route
55
+ through a single chokepoint, `PolicyEvaluator.finalize_decision`, which is
56
+ the only caller of the scan.
57
+ - **An empty findings list meant both "scanned, clean" and "never scanned"
58
+ (#1927).** That ambiguity is why the bypass went unnoticed in the audit
59
+ trail for as long as it did. Every `PolicyDecision` and every row written to
60
+ the local audit log (`~/.controlzero/audit.log`) now carries required,
61
+ structured `dlp_outcome`, always with a non-empty reason. A `PolicyDecision`
62
+ cannot be constructed without it. The compatibility `dlp_status` projection
63
+ carries the same closed status and never defaults a missing value to clean.
64
+
65
+ The vocabulary is **one member, one meaning**:
66
+
67
+ | Status | Means |
68
+ | --- | --- |
69
+ | `clean` / `findings` | The scanner ran and returned a terminal verdict |
70
+ | `skipped` | Policy denied (nothing executes) or the data is explicitly ineligible |
71
+ | `unavailable` | The scanner could not be used. **Never** means clean |
72
+ | `mask_unsupported_on_host` | A mask matched on a host that cannot rewrite arguments |
73
+ | `suppressed` | **An admin deliberately turned enforcement off. Nothing else.** |
74
+ | `not_finalized` | Internal pre-finalization sentinel |
75
+ | `not_applicable` | The tool already ran (post-execution observation) |
76
+ | `unreachable` | An origin that structurally cannot reach the chokepoint |
77
+
78
+ An earlier draft of this fix routed six unrelated plumbing states through
79
+ `suppressed`. That would have rebuilt the very defect being fixed one field
80
+ over: `#1927` was an empty findings list meaning both "clean" and "never
81
+ looked", and a single `suppressed` would have meant both "an admin chose this"
82
+ and "the code did not get there" -- so an operator asking *show me everything
83
+ DLP did not block* could not separate deliberate policy from plumbing.
84
+ Disambiguating by also reading the free-text `reason` was rejected: `reason`
85
+ has no completeness guard and cannot carry a safety-critical distinction.
86
+
87
+ `suppressed` therefore has **no producer in this release** -- the explicit
88
+ admin observe posture is not implemented yet (#1939) and the default is
89
+ ENFORCE. The member is reserved so no plumbing state can claim the name
90
+ later. A critical telemetry alarm fires if the `not_finalized` sentinel
91
+ reaches an execution boundary, and both the idempotence guard and the
92
+ outcome-stamping check key on the status MEMBER, never on the reason string.
93
+ - **`guard_with_approval(retry_guard=False)` returned an allow that was
94
+ neither DLP-scanned nor audited (#1927).** On that path a human-approved
95
+ call proceeded with its ORIGINAL arguments and left no audit row. It now
96
+ routes through the same chokepoint and writes an audit row. A human
97
+ approving a tool call does not approve exfiltrating a secret in its
98
+ arguments.
99
+ - **The Node SDK is at parity, not tracking behind (#1937).**
100
+ `@controlzero/sdk` carried BOTH of the same bypasses -- `enforcer.ts` gated
101
+ the scan on `decision.allowed` inside the matched-rule branch -- so a `warn`
102
+ catch-all disabled DLP there exactly as it did in Python. Node now has the
103
+ same single chokepoint, the same subtraction-derived scanned-effect set, and
104
+ the same closed outcome vocabulary. Both suites assert against ONE shared
105
+ contract fixture (`tests/parity/dlp_control_outcomes.json`), so adding a
106
+ status or an effect to one SDK alone turns the other SDK's suite red. Nothing
107
+ structural previously stopped one from being fixed while the other was left
108
+ behind, which is how this shipped in two SDKs at once. **The Go SDK is NOT
109
+ ported and still carries the bypass** -- #1937 stays open for it.
110
+ - **A human-approved HITL request stays an `allow` (#1936).** An in-flight
111
+ revision of this work flipped the synthetic approve decision to a `deny` so an
112
+ unscanned allow could not escape. That produced the incoherent pair
113
+ `status == "approved"` with `decision.denied == True`, breaking every caller
114
+ reading `approval.decision.allowed`. The DLP concern belongs in the outcome,
115
+ not the effect: no scanner or arguments exist at that point, so the decision
116
+ records `unreachable(hitl_approval_requires_reguard)` -- which can never read
117
+ as "scanned, clean" -- and the `Client` re-guards the original request through
118
+ the chokepoint before anything executes.
119
+
120
+ - **The Kiro IDE preToolUse hook blocked every tool call because of OUR
121
+ adapter, not an upstream bug (#1885).** Verified against the shipped build on
122
+ the reporting machine -- Kiro IDE **0.12.333** (commit `c7e35289`, built
123
+ 2026-06-10, read from `Kiro.app/Contents/Resources/app/product.json`): the IDE
124
+ does NOT deliver `USER_PROMPT={}`. Its v1 hook provider sends
125
+ `USER_PROMPT=JSON.stringify(toolArgs)` -- the tool ARGUMENTS with **no tool
126
+ name** (postToolUse sends the full `{toolName, toolArgs, toolResult,
127
+ toolSuccess}`). `kiro_adapter._normalize` REQUIRED `toolName`, returned
128
+ `None` for that shape, and every real IDE tool call landed on the
129
+ unknown-payload fail-closed path. An args-only payload is now evaluated on
130
+ the arguments that did arrive, under the explicit placeholder tool
131
+ `kiro_ide_unknown_tool`, and MARKED (`cz_payload_degraded`) plus a loud
132
+ stderr line so a tool-identity-unknown decision can never be read as a normal
133
+ argument-level governed decision. A genuinely empty payload (`{}`, non-JSON,
134
+ a JSON non-object) still fails closed.
135
+ - **Adapter exit codes are now safe across the Kiro 1.0 boundary (#1885).**
136
+ Kiro 1.0 redefines a non-zero exit OTHER than 2 as a warning that lets the
137
+ tool PROCEED. The adapter now emits ONLY 0 or 2: an uncaught exception is
138
+ caught at the top level and collapsed to the block code instead of escaping
139
+ as Python's exit 1 (which blocks on 0.12.x and would silently allow on
140
+ 1.0.x). `controlzero install kiro` and `controlzero kiro verify` print the
141
+ version boundary, the detected Kiro build, and the residual risk.
142
+ - **Forward-compatible with the v2 hook provider (#1885).** The same 0.12.333
143
+ bundle carries a second provider (session setting `hooks.v2 === true`) that
144
+ spawns hooks with an EMPTY env and delivers a Claude-Code-shaped JSON payload
145
+ on STDIN. The adapter now falls back to a timeout-bounded stdin read when
146
+ `USER_PROMPT` is empty, so a flip to v2 yields full-fidelity governance
147
+ instead of fail-closed-on-everything. The v1 path never touches stdin.
148
+
149
+ - **A `warn` decision is no longer byte-identical to an allow on the hook
150
+ surface (#1903, P0).** `controlzero hook-check` had no warn branch: on the
151
+ enforcer's `PolicyDecision` a `warn` effect satisfies neither `.denied` nor
152
+ `.allowed`, and the CLI's only decision branch was `if decision.denied:`. A
153
+ warn therefore fell through to the allow tail and was re-emitted as
154
+ `{"decision":"allow","reason":"[Control Zero] Allowed: <tool>"}` -- the
155
+ operator's warn message was discarded on every host. In the reported
156
+ incident a `find <dir> -mindepth 1 -delete` was classified CORRECTLY as
157
+ semantic class `file_delete` with a matching `warn` rule, and the agent was
158
+ handed an unqualified allow; the warn was visible in the dashboard only.
159
+ A warn still PROCEEDS (unchanged advisory contract, exit 0, no host block or
160
+ prompt token on any surface), but the operator's reason now reaches the
161
+ agent and the human, branded `[Control Zero] WARN: <reason>` on stdout and
162
+ stderr. Claude Code and Codex CLI additionally use the documented
163
+ non-deciding channels `systemMessage` (human) and
164
+ `hookSpecificOutput.additionalContext` (model) -- never `permissionDecision`,
165
+ which would auto-approve and bypass the user's own permission prompts.
166
+ Antigravity keeps its documented `decision:"allow"` token and carries the
167
+ warn text as the reason. Cursor IDE/CLI, which drop `reason` entirely on the
168
+ proceed path, now carry it in `user_message` / `agent_message`.
169
+
170
+ - **E1404 (TLS verification failed) pointed at a knob the SDK never reads
171
+ (#1824).** The fix hint told users to set `REQUESTS_CA_BUNDLE`, which appears
172
+ nowhere in the SDK's TLS path, so anyone hitting a TLS failure against an
173
+ air-gapped or self-hosted backend followed it and stayed broken. The bundled
174
+ `error_codes.yaml` now names the real custom-CA surface --
175
+ `CONTROLZERO_TLS_CA_BUNDLE`, `tls_ca_bundle` in `~/.controlzero/config.yaml`,
176
+ or `controlzero setup --tls-ca-bundle` -- and leads with the private-CA /
177
+ self-signed certificate case (the common cause), keeping clock skew as the
178
+ fallback. This text is spliced into `str(exc)` for every E1404, so it is what
179
+ the user sees on CLI stderr and in logs.
180
+
181
+ ### Changed
182
+
183
+ - **A DLP `block` match now DENIES a `warn` decision, and a DLP
184
+ `mask` match now applies to it (#1927) -- READ THIS BEFORE UPGRADING.**
185
+ Now that proceeding decisions are actually scanned, the scan's verdict
186
+ applies to them. A `warn` rule promises that THE RULE will not block; it
187
+ does not exempt the call from a mandatory org-level data control, and the
188
+ alternative -- letting any `warn` rule silently downgrade the org's DLP
189
+ posture -- is precisely the bypass being fixed.
190
+ **Upgrade impact, stated plainly:** an organisation running a `warn`
191
+ catch-all (including the `warn bash:*` in our own shipped "Shell Command
192
+ Restrictions" template, and any observation-only deployment) will start
193
+ seeing DENIES it has never seen before. Those calls were always violating a
194
+ DLP `block` rule; the SDK was not looking. The same is true for
195
+ `default_action: allow` / `warn` on calls that match no rule. On the
196
+ coding-agent hook surface the change is wider still: `hook-check` escalates
197
+ ANY decision carrying `masked_args` to a block (no host hook protocol can
198
+ hand rewritten tool input back), so a `warn` or a no-match-default call with
199
+ a MASK hit -- which previously could not happen -- will now block on Claude
200
+ Code, Gemini CLI, Codex CLI, Kiro and Antigravity. Before upgrading, review
201
+ your DLP rules with `action: block` and `action: mask` against the traffic
202
+ your `warn` rules cover.
203
+ - **DLP masking is negotiated before the verdict, never corrected after audit
204
+ (#1927, #1938).** Masking rewrites the
205
+ arguments the agent receives. With warn/no-match-default now in scope,
206
+ it can do so on a path where the operator only asked to be WARNED, so a
207
+ masked decision carries an explicit `dlp_masked` flag and the audit row
208
+ records `dlp_masked: true`.
209
+
210
+ Mask capability is now **per host, declared as data** on each adapter's
211
+ coverage record (`can_rewrite_tool_input`, `rewrite_field`,
212
+ `rewrite_events`), verified against each vendor's own documentation. A
213
+ previous draft of this entry stated that the hook protocol of *every* host
214
+ "cannot return rewritten tool arguments". **That was wrong**, and correcting
215
+ it is why this paragraph exists (#202 no-false-claims):
216
+
217
+ | Host | Can rewrite tool input | Masking behaviour in 1.13.8 |
218
+ | --- | --- | --- |
219
+ | Claude Code | Yes -- `hookSpecificOutput.updatedInput` | **Masks and proceeds** |
220
+ | Gemini CLI | Yes -- `hookSpecificOutput.tool_input` (BeforeTool) | **Masks and proceeds** |
221
+ | Codex CLI | Yes, but only alongside `permissionDecision:"allow"` | Denies (see below) |
222
+ | Cursor IDE | Yes, on `preToolUse` only -- `updated_input` | Denies (see below) |
223
+ | Cursor CLI | No (only `beforeShellExecution` is wired) | Denies |
224
+ | Kiro CLI / IDE | No (exit-code-only contract) | Denies |
225
+ | Antigravity | No (`decision`/`reason`/`permissionOverrides` only) | Denies |
226
+ | Unknown host | Unknown, so treated as No | Denies |
227
+
228
+ **What changed for you:** on Claude Code and Gemini CLI a DLP `mask` match no
229
+ longer blocks the call -- the redacted arguments are sent to the host and the
230
+ call proceeds. Previously it was a hard deny on every host.
231
+
232
+ **Two capable hosts are deliberately NOT enabled**, and this is a named gap
233
+ rather than an oversight (#1938). Codex CLI's documented rewrite is only valid
234
+ with `permissionDecision:"allow"`, which would auto-approve the call and
235
+ bypass Codex's own confirmation -- trading a fail-closed deny for a fail-open
236
+ auto-approve is a separate security decision, not a side effect of a
237
+ scan-coverage fix. Cursor IDE's rewrite has not been observed taking effect on
238
+ a live host, and Cursor staff report `allow`/`ask` are currently ignored
239
+ there; enabling a mask we have not seen applied is the one DLP failure mode
240
+ with no recovery (we report a mask, the raw secret runs). Both stay at
241
+ today's behaviour: the mask escalates to a deny recorded as
242
+ `mask_unsupported_on_host(host_cannot_rewrite_arguments)`, decided BEFORE
243
+ audit and host rendering, so the audit row and the wire verdict agree.
244
+
245
+ Antigravity has an undocumented `overwrite` field in its Go runtime. It is
246
+ **not used and must not be**: it is reported broken (the permission review
247
+ fires on the ORIGINAL call and the overwrite is silently discarded), which
248
+ would mean believing we masked while the raw secret executed.
249
+ - `controlzero kiro verify` now reports **per-workspace Kiro IDE hook
250
+ coverage** and names the workspace path it inspected. Verify previously
251
+ reported PASS while the user's actual IDE workspace was ungoverned, because
252
+ IDE hooks are per-workspace and had been installed elsewhere. The CLI
253
+ PASS/FAIL verdict is unchanged and now says explicitly that it covers the
254
+ Kiro CLI surface only. New `--workspace` option (defaults to the current
255
+ directory).
256
+ - The IDE Pre Tool Use hook template still ships **disabled** (unchanged), but
257
+ its description no longer repeats the false "empty payload" claim. It now
258
+ states the verified build, the real payload shape, and the two real limits:
259
+ tool-NAME rules cannot match a v1 preToolUse call, and on the v1 provider the
260
+ block is LLM-mediated (any hook output intercepts the call and the model is
261
+ told the tool did not run, then decides) rather than a deterministic stop.
262
+
263
+ - **The Kiro IDE adapter now BLOCKS a deciding event whose DLP scan coverage
264
+ cannot be established (#1892) -- new behaviour on a hook that ships ENABLED.**
265
+ Where the adapter cannot prove the scanner will see the whole payload, it
266
+ exits 2 instead of forwarding an unvouched-for scan dressed up as a governed
267
+ decision. Triggers are policy-independent: a raw payload over 1,000,000
268
+ characters, decodable JSON still nested past depth 3, or any exception raised
269
+ while decoding. This reaches `preToolUse` AND `promptSubmit`, and
270
+ `ide-prompt-submit.kiro.hook` ships `"enabled": true` -- in 1.13.7 the
271
+ promptSubmit branch had no coverage concept and could not block at all. File
272
+ events (`fileSave`) are post-write and observe-only, so they proceed loudly
273
+ rather than pretending they were scanned. There is currently NO opt-out for
274
+ this path: `CZ_KIRO_UNKNOWN_PAYLOAD=open` is consulted only on the earlier
275
+ empty/unparseable-payload branch and does not cover it (tracked in #1925). If
276
+ you hit this on a legitimate payload, capture its SIZE and SHAPE (never its
277
+ content) and report against #1892.
278
+ - **The Kiro IDE adapter's DLP scan surface is wider, so previously-allowed
279
+ input can now DENY (#1892).** Every normalised payload now carries the full
280
+ received payload on `tool_input` -- every JSON key, duplicate members,
281
+ resolved `\uXXXX` escapes and one further decoded JSON layer, or the raw bytes
282
+ when it is not JSON. `hook-check` scans the recursively extracted string
283
+ values of `tool_input`, so a prompt or tool argument containing a JSON blob
284
+ whose KEY names or escaped content match a DLP block rule now denies where
285
+ 1.13.7 allowed. Known side effect: DLP findings for tool events can appear
286
+ twice in a single audit row, which changes finding counts for existing
287
+ customers.
288
+
289
+ ### Known gap
290
+
291
+ - The **Node** SDK adapter (`sdks/node/controlzero/src/cli/kiroAdapter.ts`)
292
+ still returns `null` when `toolName` is absent and therefore still blocks
293
+ every call if its IDE preToolUse hook is enabled. Its template description
294
+ now says so. The port of this fix is tracked in #1885.
295
+
296
+ - The **Node** SDK has no `warn` branch at all:
297
+ `sdks/node/controlzero/src/cli/main.ts` still branches only on
298
+ `decision.denied`, and no Node host adapter carries warn text on a proceed
299
+ path. The #1903 fix above is **Python-SDK only** -- for a `@controlzero/sdk`
300
+ user a `warn` is still discarded and re-emitted as a bare allow on every host
301
+ named there. The port is tracked in #1923.
302
+ - **DLP residuals from this release, named rather than left silent.** None of
303
+ these is a regression; each is a boundary this fix did not reach:
304
+ - **Three decision origins still proceed without reaching the DLP chokepoint
305
+ (#1936):** the CLI's pre-`Client` bundle-missing allow, the HITL approver
306
+ approval, and the no-policy pass-through allow. All three now RECORD
307
+ `unreachable` with a specific reason instead of producing a row that reads
308
+ like a clean scan, so they are visible in the audit trail -- but the scan
309
+ genuinely does not run there.
310
+ - **`dlp_findings` and `dlp_status` never leave the machine (#1935).**
311
+ `audit_remote._build_wire_entry` carries neither field, so the remote sink
312
+ and the dashboard do not see this signal. Everything this release says about
313
+ the audit trail is true of the LOCAL log (`~/.controlzero/audit.log`) and the
314
+ decision object; it is NOT yet true of the wire. Closing it needs matching
315
+ backend `AuditIngestEntry` columns.
316
+ - **The explicit admin observe posture is not implemented (#1939).** `suppressed`
317
+ is reserved and producerless; the only posture available today is ENFORCE.
318
+ An org that genuinely wants detect-only cannot yet elect it.
319
+ - **Codex CLI and Cursor IDE can rewrite tool input but are not enabled
320
+ (#1938)** -- see the masking entry above for why each was held back.
321
+ - **The Go SDK still carries both bypasses (#1937).** Only Python and Node are
322
+ fixed in this release.
323
+ - **Three origins write NO audit row at all (#1941)** -- broader than the DLP
324
+ gap above, and worth stating plainly: the no-policy pass-through allow, the
325
+ synthetic HITL grant deny (7 call sites, whose docstring claims it records
326
+ to the audit store while the body only logs a warning), and the CLI's
327
+ unenrolled first-run allow, which exits before a `Client` exists. Making
328
+ every decision carry a truthful `dlp_outcome` is worth nothing on a path
329
+ that never persists one.
330
+
331
+ ### Fixed (audit fidelity)
332
+
333
+ - **The observing posture survives a DLP override.** `observe` was set only on
334
+ the matched-rule construction, so every DERIVED decision -- the DLP block
335
+ override, the mask-boundary fail-closed deny, the size-cap deny and the
336
+ scanner-unavailable deny -- silently dropped it. A DLP-block deny made under
337
+ an observe posture was indistinguishable from an enforcing deny in the audit
338
+ row. Fixed in both SDKs.
339
+ - `controlzero validate` still warns on CUSTOM-VOCABULARY actions the backend
340
+ accepts -- `trade.execute`, `patient.read`, `data.read`, `mcp.tool.call`,
341
+ `production:*` -- so four shipped dashboard templates print `rule action
342
+ '<x>' is not a known canonical action or alias` and then parse and enforce
343
+ correctly. The warning is cosmetic (no decision depends on it) and is tracked
344
+ in #1918. The semantic-class namespaces are NO LONGER affected: #1913
345
+ registered `file_delete`, `network`, `git_<sub>` and `aws`/`gcloud`/`kubectl`
346
+ in all three SDK validators, so `warn: file_delete:*` -- the rule shape this
347
+ release makes useful -- now validates cleanly.
348
+ - Semantic-class coverage for destructive shell commands is partial (#1905).
349
+ `_SHELL_CLASS_MAP` deliberately excludes `dd`, `rsync`, `git`, `tar`, `awk`,
350
+ `openssl`, `sed` and every interpreter (`python`, `node`, `bash`, ...), so
351
+ `dd of=...`, `git clean -xfd`, `rsync --delete` and
352
+ `python -c "shutil.rmtree(...)"` classify as empty and match NO semantic-class
353
+ rule. A `file_delete` rule does not see destructive shell deletion in general.
354
+
3
355
  ## 1.13.6 (2026-07-12): interpreter-pinned hook command (P0)
4
356
 
5
357
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: controlzero
3
- Version: 1.13.6
3
+ Version: 1.13.8
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
@@ -28,6 +28,7 @@ Requires-Dist: httpx>=0.25.0
28
28
  Requires-Dist: loguru>=0.7.0
29
29
  Requires-Dist: pydantic>=2.0.0
30
30
  Requires-Dist: pyyaml>=6.0
31
+ Requires-Dist: questionary<3,>=2.0.0
31
32
  Requires-Dist: rfc8785<0.2,>=0.1.4
32
33
  Requires-Dist: rich>=13.0.0
33
34
  Requires-Dist: zstandard>=0.23.0
@@ -41,7 +41,7 @@ from controlzero.hitl.grant_protocol import (
41
41
  from controlzero.policy_loader import load_policy
42
42
  from controlzero.secrets import SecretRef, SecretStr, secret_ref
43
43
 
44
- __version__ = "1.13.6"
44
+ __version__ = "1.13.8"
45
45
 
46
46
  __all__ = [
47
47
  "Client",
@@ -39,7 +39,7 @@ modern intent is silently broken.
39
39
  from __future__ import annotations
40
40
 
41
41
  import json
42
- from typing import Iterable
42
+ from collections.abc import Iterable
43
43
 
44
44
  # Tool covered by this alias table. Currently only "database" carries
45
45
  # the legacy <-> canonical split; other tools were added post-#350 and
@@ -23,10 +23,10 @@ pass.
23
23
  """
24
24
  from __future__ import annotations
25
25
 
26
- from typing import Iterable
26
+ from collections.abc import Iterable
27
27
 
28
- from controlzero._internal.action_aliases import TOOL as _ALIAS_TOOL
29
28
  from controlzero._internal.action_aliases import _AMBIGUOUS, _CLASSES
29
+ from controlzero._internal.action_aliases import TOOL as _ALIAS_TOOL
30
30
 
31
31
  # Mirror of the canonical tool set + host-aliases the extractors
32
32
  # accept. Source of truth is tool_extractors.json; this list is
@@ -34,6 +34,16 @@ from controlzero._internal.action_aliases import _AMBIGUOUS, _CLASSES
34
34
  _CANONICAL_TOOLS: set[str] = {
35
35
  "Bash", "database", "http", "web_search", "browser",
36
36
  "file_read", "file_write", "file_search", "task",
37
+ # Shell semantic-class NAMESPACES (#437). The evaluator promotes a shell
38
+ # command's danger class into its own namespace as `<class>:<class>`
39
+ # (enforcer.py `evaluate`), so `file_delete:*` and `network:*` are
40
+ # first-class rule actions exactly like the `file_read:*` / `file_write:*`
41
+ # siblings already listed above. #437 registered two of the four classes
42
+ # and missed these two, so the shipped "Shell Command Restrictions"
43
+ # dashboard template warned "not a known canonical action" on rules that
44
+ # were in fact firing correctly (#1904). Warn-only path; no decision
45
+ # depends on this set.
46
+ "file_delete", "network",
37
47
  # database aliases
38
48
  "sql", "Database", "PostgreSQL", "MySQL", "postgres", "sqlite",
39
49
  # Bash aliases
@@ -61,6 +71,14 @@ _DATABASE_TOOL_ALIASES = {
61
71
  "database", "sql", "Database", "PostgreSQL", "MySQL", "postgres", "sqlite",
62
72
  }
63
73
 
74
+ # Context-sensitive namespaces the evaluator promotes for a shell call
75
+ # (#1734): `git <sub> ... <ref>` becomes `git_<sub>:<target>` and
76
+ # `aws|gcloud|kubectl <sub>` becomes `<cli>:<sub>`. The git subcommand set is
77
+ # open (any `git_*` tool token is a promoted action), so it is matched by
78
+ # prefix rather than enumerated.
79
+ _GIT_ACTION_TOOL_PREFIX = "git_"
80
+ _CLOUD_CLI_TOOLS = {"aws", "gcloud", "kubectl"}
81
+
64
82
 
65
83
  def _build_known_database_methods() -> set[str]:
66
84
  out: set[str] = {"*"}
@@ -87,6 +105,9 @@ def is_known_action(action: str) -> bool:
87
105
  tool, _, method = action.partition(":")
88
106
  if tool == "*":
89
107
  return True
108
+ # #1734 promoted namespaces: git_<sub>:<target>, <cloud_cli>:<sub>.
109
+ if tool.startswith(_GIT_ACTION_TOOL_PREFIX) or tool in _CLOUD_CLI_TOOLS:
110
+ return True
90
111
  if tool not in _CANONICAL_TOOLS:
91
112
  return False
92
113
  if method == "*" or method == "":
@@ -322,6 +322,7 @@ def _zstd_decompress(data: bytes) -> bytes:
322
322
  # Preferred: `zstandard` is the de-facto PyPI package.
323
323
  try:
324
324
  import io
325
+
325
326
  import zstandard as zstd
326
327
 
327
328
  dctx = zstd.ZstdDecompressor()
@@ -620,7 +621,7 @@ def translate_to_local_policy(
620
621
  :func:`controlzero.policy_loader.load_policy`: a dict with
621
622
  ``version: "1"`` and a flat ``rules`` list where each entry is
622
623
  either ``{"deny": "<tool>"}``, ``{"allow": "<tool>"}`` or
623
- ``{"effect": "<deny|allow|warn|audit>", "action": "<tool>"}``.
624
+ ``{"effect": "<deny|allow|warn>", "action": "<tool>"}``.
624
625
 
625
626
  Policies are sorted by ``priority`` DESCENDING (higher number = higher
626
627
  precedence = evaluated first; first-match-wins), so SDKs in every language
@@ -961,15 +962,22 @@ def _translate_rule(rule: dict, policy_id: str) -> Optional[dict]:
961
962
  # behavior) turned an unknown rule into allow-*-for-its-pattern -- a
962
963
  # fail-open for a security gate (#1303). A deny here over-denies that one
963
964
  # pattern at worst, which is the safe direction.
965
+ #
966
+ # ALL_EFFECTS is the single source of truth for that vocabulary (#1927);
967
+ # DLP_SCANNED_EFFECTS is derived from it by subtraction, so any effect that
968
+ # can reach a decision from here is DLP-scanned by default. Lazy import:
969
+ # enforcer imports from this file.
970
+ from controlzero._internal.enforcer import ALL_EFFECTS
971
+
964
972
  effect_raw = rule.get("effect")
965
973
  if not effect_raw:
966
974
  # Only fall back to rule["action"] for effect if it looks like
967
975
  # one of the canonical effects. Otherwise "action" is a tool
968
976
  # name and we should not use it here.
969
977
  fallback = rule.get("action")
970
- if fallback in ("allow", "deny", "warn", "audit"):
978
+ if fallback in ALL_EFFECTS:
971
979
  effect_raw = fallback
972
- effect = effect_raw if effect_raw in ("allow", "deny", "warn", "audit") else "deny"
980
+ effect = effect_raw if effect_raw in ALL_EFFECTS else "deny"
973
981
 
974
982
  # Tool pattern resolution order:
975
983
  # 1. plural ``actions`` (list, as emitted by the backend)
@@ -992,7 +1000,7 @@ def _translate_rule(rule: dict, policy_id: str) -> Optional[dict]:
992
1000
  # it is NOT a canonical effect name (effect already captured
993
1001
  # above).
994
1002
  candidate = rule.get("action")
995
- if candidate and candidate not in ("allow", "deny", "warn", "audit"):
1003
+ if candidate and candidate not in ALL_EFFECTS:
996
1004
  patterns = [str(candidate)]
997
1005
 
998
1006
  if not patterns:
@@ -39,7 +39,6 @@ from typing import Any, Literal
39
39
  from controlzero._internal.credential_scanner import scan_for_credentials
40
40
  from controlzero.errors import CredentialLeakBlocked
41
41
 
42
-
43
42
  Action = Literal["warn", "redact", "block"]
44
43
  Source = Literal["tool_output", "tool_stderr", "file_read", "grep_match"]
45
44
 
@@ -34,7 +34,6 @@ from functools import lru_cache
34
34
  from importlib import resources
35
35
  from typing import Any, Optional
36
36
 
37
-
38
37
  # Default per-occurrence byte cap for marker-block patterns that omit
39
38
  # `max_block_bytes`. Matches the Rust scanner's
40
39
  # `DEFAULT_MAX_BLOCK_BYTES` constant; 8192 bytes covers every PEM
@@ -28,7 +28,6 @@ import re
28
28
  from dataclasses import dataclass, field
29
29
  from typing import Any, Optional
30
30
 
31
-
32
31
  # ---------------------------------------------------------------------------
33
32
  # Built-in patterns grouped by category.
34
33
  # All builtins use action="detect". Admins add action="block" via custom rules.