controlzero 1.13.10__tar.gz → 1.13.12__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.
- {controlzero-1.13.10 → controlzero-1.13.12}/.gitignore +14 -1
- {controlzero-1.13.10 → controlzero-1.13.12}/CHANGELOG.md +400 -6
- {controlzero-1.13.10 → controlzero-1.13.12}/PKG-INFO +1 -1
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/__init__.py +3 -2
- controlzero-1.13.12/controlzero/_internal/action_subject.py +474 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/action_validator.py +8 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/dlp_output_observer.py +7 -1
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/dlp_scanner.py +148 -2
- controlzero-1.13.12/controlzero/_internal/enforcement_outcome.py +646 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/enforcer.py +836 -54
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/hook_extractors.py +29 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/audit_remote.py +26 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/debug_bundle.py +26 -1
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/doctor.py +84 -23
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/enforcement.py +156 -11
- controlzero-1.13.12/controlzero/cli/enforcement_verdict.py +1316 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/antigravity.py +142 -36
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/base.py +151 -4
- controlzero-1.13.12/controlzero/cli/hosts/claims.py +566 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/claude_code.py +29 -1
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/codex_cli.py +14 -1
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/cursor_cli.py +27 -3
- controlzero-1.13.12/controlzero/cli/hosts/kiro.py +799 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/main.py +1176 -191
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/setup_wizard.py +55 -9
- controlzero-1.13.12/controlzero/cli/templates/kiro/kiro.yaml +176 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/client.py +800 -8
- controlzero-1.13.12/controlzero/hook_integrity.py +1347 -0
- controlzero-1.13.12/controlzero/host_invocation.py +723 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/_export_dlp.py +7 -1
- controlzero-1.13.12/controlzero/integrations/_mask_apply.py +146 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/anthropic.py +5 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/autogen.py +15 -4
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/crewai/agent.py +5 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/crewai/crew.py +15 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/crewai/task.py +10 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/crewai/tool.py +43 -4
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/google.py +5 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/google_adk/agent.py +5 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/google_adk/tool.py +34 -2
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/agent.py +20 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/chain.py +25 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/graph.py +30 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/tool.py +41 -2
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/litellm.py +5 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/openai.py +10 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/pydantic_ai.py +17 -2
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/vercel_ai.py +5 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/pyproject.toml +34 -1
- controlzero-1.13.10/Dockerfile.test +0 -24
- controlzero-1.13.10/controlzero/cli/hosts/kiro.py +0 -364
- controlzero-1.13.10/controlzero/cli/templates/kiro/kiro.yaml +0 -102
- controlzero-1.13.10/controlzero/hook_integrity.py +0 -680
- controlzero-1.13.10/examples/hello_world.py +0 -20
- controlzero-1.13.10/ruff.toml +0 -40
- controlzero-1.13.10/tests/_fixtures/antigravity-envelope-expected.json +0 -146
- controlzero-1.13.10/tests/_fixtures/jcs_args_hash_vectors.json +0 -111
- controlzero-1.13.10/tests/conftest.py +0 -68
- controlzero-1.13.10/tests/fixtures/adapter-contracts/antigravity.json +0 -21
- controlzero-1.13.10/tests/fixtures/adapter-contracts/claude_code.json +0 -44
- controlzero-1.13.10/tests/fixtures/adapter-contracts/codex_cli.json +0 -24
- controlzero-1.13.10/tests/fixtures/adapter-contracts/cursor.json +0 -31
- controlzero-1.13.10/tests/fixtures/adapter-contracts/gemini_cli.json +0 -36
- controlzero-1.13.10/tests/fixtures/adapter-contracts/kiro.json +0 -39
- controlzero-1.13.10/tests/integrations/__init__.py +0 -0
- controlzero-1.13.10/tests/integrations/test_google.py +0 -96
- controlzero-1.13.10/tests/parity/action_aliases.json +0 -55
- controlzero-1.13.10/tests/spool/__init__.py +0 -0
- controlzero-1.13.10/tests/spool/conftest.py +0 -208
- controlzero-1.13.10/tests/spool/test_spool_cli.py +0 -168
- controlzero-1.13.10/tests/spool/test_spool_concurrency.py +0 -110
- controlzero-1.13.10/tests/spool/test_spool_conformance.py +0 -316
- controlzero-1.13.10/tests/spool/test_spool_core.py +0 -415
- controlzero-1.13.10/tests/spool/test_spool_crash.py +0 -106
- controlzero-1.13.10/tests/spool/test_spool_diskfull.py +0 -77
- controlzero-1.13.10/tests/spool/test_spool_durable_default_tamper.py +0 -353
- controlzero-1.13.10/tests/spool/test_spool_keychain_dek.py +0 -444
- controlzero-1.13.10/tests/spool/test_spool_sink_wiring.py +0 -671
- controlzero-1.13.10/tests/spool/test_spool_transcript_localack.py +0 -102
- controlzero-1.13.10/tests/spool/test_spool_uploader.py +0 -569
- controlzero-1.13.10/tests/test_action_aliases.py +0 -224
- controlzero-1.13.10/tests/test_action_canonicalization.py +0 -36
- controlzero-1.13.10/tests/test_action_validator_t86.py +0 -129
- controlzero-1.13.10/tests/test_adapter_contracts.py +0 -215
- controlzero-1.13.10/tests/test_adapter_interface.py +0 -402
- controlzero-1.13.10/tests/test_agent_name_env.py +0 -83
- controlzero-1.13.10/tests/test_agy_docs_no_exit2_1373.py +0 -131
- controlzero-1.13.10/tests/test_antigravity_adapter.py +0 -531
- controlzero-1.13.10/tests/test_antigravity_envelope_parity.py +0 -108
- controlzero-1.13.10/tests/test_antigravity_ga_blockers_1248.py +0 -565
- controlzero-1.13.10/tests/test_antigravity_hook_check.py +0 -164
- controlzero-1.13.10/tests/test_antigravity_install.py +0 -431
- controlzero-1.13.10/tests/test_antigravity_posttooluse_observe_58.py +0 -295
- controlzero-1.13.10/tests/test_antigravity_tool_vocab_1303.py +0 -322
- controlzero-1.13.10/tests/test_api_key_mask.py +0 -59
- controlzero-1.13.10/tests/test_api_url_config.py +0 -72
- controlzero-1.13.10/tests/test_audit_project_id_attribution_pr2.py +0 -304
- controlzero-1.13.10/tests/test_audit_remote.py +0 -715
- controlzero-1.13.10/tests/test_audit_remote_io_capture.py +0 -141
- controlzero-1.13.10/tests/test_audit_remote_sdk_version.py +0 -144
- controlzero-1.13.10/tests/test_audit_sink_isolation.py +0 -67
- controlzero-1.13.10/tests/test_bundle_cache_freshness_1303.py +0 -302
- controlzero-1.13.10/tests/test_bundle_parser.py +0 -238
- controlzero-1.13.10/tests/test_bundle_translate.py +0 -263
- controlzero-1.13.10/tests/test_canonical_phase1a.py +0 -230
- controlzero-1.13.10/tests/test_capability_matrix_doc.py +0 -175
- controlzero-1.13.10/tests/test_cli_carve_out.py +0 -513
- controlzero-1.13.10/tests/test_cli_coverage.py +0 -62
- controlzero-1.13.10/tests/test_cli_debug_bundle.py +0 -390
- controlzero-1.13.10/tests/test_cli_extractor_integration.py +0 -527
- controlzero-1.13.10/tests/test_cli_hook.py +0 -521
- controlzero-1.13.10/tests/test_cli_hook_entry.py +0 -189
- controlzero-1.13.10/tests/test_cli_hosted_refresh.py +0 -103
- controlzero-1.13.10/tests/test_cli_init.py +0 -43
- controlzero-1.13.10/tests/test_cli_init_templates.py +0 -24
- controlzero-1.13.10/tests/test_cli_tail.py +0 -33
- controlzero-1.13.10/tests/test_cli_test.py +0 -59
- controlzero-1.13.10/tests/test_cli_validate.py +0 -37
- controlzero-1.13.10/tests/test_coding_agent_hooks.py +0 -894
- controlzero-1.13.10/tests/test_condition_operators_1495.py +0 -191
- controlzero-1.13.10/tests/test_conditions.py +0 -200
- controlzero-1.13.10/tests/test_config_format_parity_1303.py +0 -145
- controlzero-1.13.10/tests/test_conformance.py +0 -335
- controlzero-1.13.10/tests/test_console.py +0 -87
- controlzero-1.13.10/tests/test_credential_hook.py +0 -737
- controlzero-1.13.10/tests/test_cross_adapter_shell_enforcement.py +0 -334
- controlzero-1.13.10/tests/test_cursor_adapter.py +0 -373
- controlzero-1.13.10/tests/test_dashboard_shell_template_1904.py +0 -746
- controlzero-1.13.10/tests/test_default_action.py +0 -505
- controlzero-1.13.10/tests/test_deny_rule_reconciliation_1011.py +0 -247
- controlzero-1.13.10/tests/test_deny_wins_default_catchall_1620.py +0 -130
- controlzero-1.13.10/tests/test_device.py +0 -357
- controlzero-1.13.10/tests/test_dlp_agent_remediation_1938.py +0 -366
- controlzero-1.13.10/tests/test_dlp_audit_hashing_1881.py +0 -88
- controlzero-1.13.10/tests/test_dlp_egress_conformance_matrix.py +0 -1091
- controlzero-1.13.10/tests/test_dlp_mask.py +0 -519
- controlzero-1.13.10/tests/test_dlp_mask_hosts_1938.py +0 -164
- controlzero-1.13.10/tests/test_dlp_origin_contract.py +0 -620
- controlzero-1.13.10/tests/test_dlp_path5_audit_egress_1967.py +0 -174
- controlzero-1.13.10/tests/test_dlp_paths_8_9_observe_1967.py +0 -170
- controlzero-1.13.10/tests/test_dlp_scan_coverage_1927_1931.py +0 -634
- controlzero-1.13.10/tests/test_dlp_scanner.py +0 -1218
- controlzero-1.13.10/tests/test_doctor.py +0 -150
- controlzero-1.13.10/tests/test_engine_version_consistency.py +0 -82
- controlzero-1.13.10/tests/test_enrollment.py +0 -820
- controlzero-1.13.10/tests/test_env_dump_438.py +0 -148
- controlzero-1.13.10/tests/test_epic_1247_bryan_acceptance.py +0 -531
- controlzero-1.13.10/tests/test_error_codes.py +0 -122
- controlzero-1.13.10/tests/test_errors_e_codes.py +0 -301
- controlzero-1.13.10/tests/test_fail_closed_eval.py +0 -19
- controlzero-1.13.10/tests/test_failopen_1303.py +0 -96
- controlzero-1.13.10/tests/test_feature_parity_maturity.py +0 -187
- controlzero-1.13.10/tests/test_git_target_1734.py +0 -242
- controlzero-1.13.10/tests/test_glob_matching.py +0 -287
- controlzero-1.13.10/tests/test_hitl_5d_email_install.py +0 -137
- controlzero-1.13.10/tests/test_hitl_6a_cli_flag.py +0 -306
- controlzero-1.13.10/tests/test_hitl_6a_exceptions.py +0 -166
- controlzero-1.13.10/tests/test_hitl_6a_get_secret_hitl.py +0 -672
- controlzero-1.13.10/tests/test_hitl_6a_mock_backend.py +0 -342
- controlzero-1.13.10/tests/test_hitl_6a_pending_approval.py +0 -362
- controlzero-1.13.10/tests/test_hitl_6a_request_approval.py +0 -729
- controlzero-1.13.10/tests/test_hitl_6a_secret_leak_guard.py +0 -334
- controlzero-1.13.10/tests/test_hitl_6a_wait.py +0 -673
- controlzero-1.13.10/tests/test_hitl_conformance.py +0 -286
- controlzero-1.13.10/tests/test_hitl_phase2b_protocol.py +0 -643
- controlzero-1.13.10/tests/test_hitl_reason_codes.py +0 -78
- controlzero-1.13.10/tests/test_hitl_validator_keys.py +0 -271
- controlzero-1.13.10/tests/test_hook_command_pinning_1705.py +0 -325
- controlzero-1.13.10/tests/test_hook_extractors.py +0 -566
- controlzero-1.13.10/tests/test_hook_integrity.py +0 -521
- controlzero-1.13.10/tests/test_hook_mask_failclosed.py +0 -95
- controlzero-1.13.10/tests/test_hook_warn_surfaces_reason_1903.py +0 -479
- controlzero-1.13.10/tests/test_hosted_local_audit_1247.py +0 -219
- controlzero-1.13.10/tests/test_hosted_local_shadow_warn_1265.py +0 -265
- controlzero-1.13.10/tests/test_hosted_policy_e2e.py +0 -383
- controlzero-1.13.10/tests/test_hosts_adapter.py +0 -686
- controlzero-1.13.10/tests/test_hybrid_mode_strict.py +0 -90
- controlzero-1.13.10/tests/test_hybrid_mode_warn.py +0 -28
- controlzero-1.13.10/tests/test_install_api_url.py +0 -69
- controlzero-1.13.10/tests/test_install_hook_command.py +0 -145
- controlzero-1.13.10/tests/test_install_hooks.py +0 -1244
- controlzero-1.13.10/tests/test_integration_export_dlp_1967.py +0 -294
- controlzero-1.13.10/tests/test_io_capture_producer.py +0 -172
- controlzero-1.13.10/tests/test_kiro_adapter.py +0 -1422
- controlzero-1.13.10/tests/test_kiro_agent_harness_routing.py +0 -196
- controlzero-1.13.10/tests/test_kiro_cli_e2e.py +0 -353
- controlzero-1.13.10/tests/test_kiro_cli_hook_pin_1265.py +0 -133
- controlzero-1.13.10/tests/test_kiro_cli_v210_hooks.py +0 -120
- controlzero-1.13.10/tests/test_kiro_hook_templates.py +0 -324
- controlzero-1.13.10/tests/test_kiro_ide_v2_hooks_1944.py +0 -480
- controlzero-1.13.10/tests/test_kiro_install.py +0 -463
- controlzero-1.13.10/tests/test_kiro_install_standardisation.py +0 -218
- controlzero-1.13.10/tests/test_kiro_version_schemes.py +0 -123
- controlzero-1.13.10/tests/test_layout_migration_t101.py +0 -158
- controlzero-1.13.10/tests/test_layout_parity_t102.py +0 -229
- controlzero-1.13.10/tests/test_local_mode_dict.py +0 -98
- controlzero-1.13.10/tests/test_local_mode_file_json.py +0 -33
- controlzero-1.13.10/tests/test_local_mode_file_yaml.py +0 -62
- controlzero-1.13.10/tests/test_localized_reason.py +0 -303
- controlzero-1.13.10/tests/test_log_fallback_stderr.py +0 -33
- controlzero-1.13.10/tests/test_log_options_ignored_hosted.py +0 -50
- controlzero-1.13.10/tests/test_log_rotation.py +0 -66
- controlzero-1.13.10/tests/test_migrate.py +0 -128
- controlzero-1.13.10/tests/test_min_sdk_version_gate.py +0 -206
- controlzero-1.13.10/tests/test_multi_client_per_project_175.py +0 -810
- controlzero-1.13.10/tests/test_no_policy_no_key.py +0 -28
- controlzero-1.13.10/tests/test_observe_mode_1247.py +0 -362
- controlzero-1.13.10/tests/test_package_rename_shim.py +0 -41
- controlzero-1.13.10/tests/test_part3_active_policy_count_1303.py +0 -142
- controlzero-1.13.10/tests/test_per_client_isolation_1011.py +0 -250
- controlzero-1.13.10/tests/test_policy_engine_version_phase1b.py +0 -73
- controlzero-1.13.10/tests/test_policy_freshness.py +0 -274
- controlzero-1.13.10/tests/test_policy_load_failclosed_f3.py +0 -278
- controlzero-1.13.10/tests/test_policy_settings.py +0 -84
- controlzero-1.13.10/tests/test_policy_source_audit.py +0 -149
- controlzero-1.13.10/tests/test_pr4_install_messaging.py +0 -380
- controlzero-1.13.10/tests/test_pr4_output.py +0 -144
- controlzero-1.13.10/tests/test_pr4_status.py +0 -160
- controlzero-1.13.10/tests/test_probe_audit_isolation.py +0 -177
- controlzero-1.13.10/tests/test_prompt_submit_dlp_1966.py +0 -421
- controlzero-1.13.10/tests/test_quarantine.py +0 -191
- controlzero-1.13.10/tests/test_reason_code.py +0 -392
- controlzero-1.13.10/tests/test_reason_code_hitl_grant_1596.py +0 -34
- controlzero-1.13.10/tests/test_refresh.py +0 -567
- controlzero-1.13.10/tests/test_secrets.py +0 -225
- controlzero-1.13.10/tests/test_secrets_reference_resolver.py +0 -191
- controlzero-1.13.10/tests/test_setup_wizard.py +0 -504
- controlzero-1.13.10/tests/test_shell_semantic_class.py +0 -243
- controlzero-1.13.10/tests/test_source_labels.py +0 -95
- controlzero-1.13.10/tests/test_sql_semantic_class.py +0 -212
- controlzero-1.13.10/tests/test_synthetic_policy_id_t79.py +0 -324
- controlzero-1.13.10/tests/test_t103_precedence.py +0 -164
- controlzero-1.13.10/tests/test_t104_cache_gc.py +0 -174
- controlzero-1.13.10/tests/test_t108_local_override_audit.py +0 -161
- controlzero-1.13.10/tests/test_t96_single_audit_log.py +0 -125
- controlzero-1.13.10/tests/test_t99_install_prefetch_bundle.py +0 -182
- controlzero-1.13.10/tests/test_tamper.py +0 -203
- controlzero-1.13.10/tests/test_tamper_behavior.py +0 -163
- controlzero-1.13.10/tests/test_tamper_hook.py +0 -307
- controlzero-1.13.10/tests/test_telemetry_consent.py +0 -90
- controlzero-1.13.10/tests/test_tls_ca_bundle.py +0 -112
- controlzero-1.13.10/tests/test_tracecontext.py +0 -89
- controlzero-1.13.10/tests/test_unsafe_int_boundary.py +0 -66
- controlzero-1.13.10/tests/test_update_cmd.py +0 -77
- controlzero-1.13.10/tests/test_upgrade_nudge.py +0 -54
- controlzero-1.13.10/tests/test_version_aware_fail_closed.py +0 -212
- controlzero-1.13.10/tools/cz-kiro-adapter +0 -31
- {controlzero-1.13.10 → controlzero-1.13.12}/LICENSE +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/README.md +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/__main__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/_messages.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/action_aliases.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/bundle.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/condition_ops.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/credential_hook.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/credential_scanner.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/credentials_data/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/credentials_data/built_in.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/dlp_remediation.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/io_capture_producer.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/source_labels.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/tool_extractors.json +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/_internal/types.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/audit_local.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/canonical.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/__main__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/_secrets.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/agent_config.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/console.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/exec_cmd.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hook_entry.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/cursor.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/gemini_cli.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/hosts/unknown.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/kiro_adapter.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/maturity.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/migrate.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/output.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/spool_cmd.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/telemetry_consent.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/antigravity/hooks.json +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/antigravity.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/autogen.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/claude-code.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/codex-cli.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/cost-cap.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/crewai.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/cursor.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/gemini-cli.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/generic.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/kiro/ide-file-save.kiro.hook +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/langchain.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/mcp.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/templates/rag.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/cli/update_cmd.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/device.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/enrollment.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/error_codes.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/error_codes.yaml +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/errors.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/grant_protocol.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/mock.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/pending_approval.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/secret_leak_guard.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hitl/status.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hooks/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hooks/tool_output_handler.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/hosted_policy.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/braintrust.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/crewai/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/google_adk/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/callbacks.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langchain/modern.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/integrations/langfuse.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/layout_migration.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/policy_loader.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/secrets/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/secrets/reference.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/secrets/resolver.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/secrets/secretstr.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/__init__.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_compress.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_constants.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_crc32c.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_crypto.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_frame.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_keyring.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_metrics.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_spool.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_state.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/_uploader.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/spool/cz-audit-v1.dict +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/tamper.py +0 -0
- {controlzero-1.13.10 → controlzero-1.13.12}/controlzero/tracecontext.py +0 -0
|
@@ -141,8 +141,21 @@ site/
|
|
|
141
141
|
|
|
142
142
|
# Rust
|
|
143
143
|
target/
|
|
144
|
-
Cargo.lock
|
|
145
144
|
*.rlib
|
|
145
|
+
# Cargo.lock is COMMITTED, deliberately. Cargo's guidance is to commit the
|
|
146
|
+
# lockfile for binaries and applications and omit it only for libraries
|
|
147
|
+
# published to a registry. Nothing here is published to crates.io: every
|
|
148
|
+
# package in this repo is either a cdylib/staticlib compiled into the
|
|
149
|
+
# Python/Node SDKs across the FFI boundary (crates/*) or a shipped binary
|
|
150
|
+
# (services/shadow-ai-scout). Both are applications for lockfile purposes.
|
|
151
|
+
#
|
|
152
|
+
# It was ignored until 2026-08-14, which meant every CI run re-resolved
|
|
153
|
+
# dependencies from scratch. A third party publishing a new version could,
|
|
154
|
+
# and did, break main with no commit to this repo: icu_* 2.3.0 shipped with
|
|
155
|
+
# MSRV 1.88 and `cargo clippy` on our pinned 1.86.0 started failing on a
|
|
156
|
+
# tree nobody had touched. It also meant the artifact that was tested was
|
|
157
|
+
# not provably the artifact that shipped, so a dependency audit of one said
|
|
158
|
+
# nothing about the other.
|
|
146
159
|
|
|
147
160
|
# Python virtual environments
|
|
148
161
|
venv/
|
|
@@ -1,5 +1,390 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 1.13.12 (2026-08-16): your audit trail now states whether the HOST enforced, and deny rules that never fired now fire
|
|
6
|
+
|
|
7
|
+
Two things in this release change what a customer can conclude from what we
|
|
8
|
+
ship. Read them before the rest.
|
|
9
|
+
|
|
10
|
+
**1. The audit row used to claim a block the host never performs (#2113, #2114,
|
|
11
|
+
#2236).** The row is the artefact an auditor reads to decide whether a customer
|
|
12
|
+
was protected. Until this release, every decision row that REACHED the host-event
|
|
13
|
+
branch came out `enforcement_outcome: enforced`,
|
|
14
|
+
`enforcement_reason: host_enforced`, `enforced: True` -- and on the observe-only
|
|
15
|
+
surfaces that was false.
|
|
16
|
+
|
|
17
|
+
Stated precisely, because the scope matters: this was never all rows. The
|
|
18
|
+
pre-fix `resolve_enforcement` returned earlier and correctly for a non-blocking
|
|
19
|
+
effect (`not_applicable / effect_not_blocking`), a post-execution event
|
|
20
|
+
(`not_enforced / post_execution_event`) and an unknown tier
|
|
21
|
+
(`indeterminate / unknown_tier`), none of which depend on the declaration. The
|
|
22
|
+
rows that were wrong are the ones those guards let through: a blocking effect,
|
|
23
|
+
on a known tier, at a pre-execution event -- which is exactly the population an
|
|
24
|
+
auditor cares about, because it is the set where a block was supposedly imposed.
|
|
25
|
+
|
|
26
|
+
#2203 taught `resolve_enforcement` to consult each adapter's own
|
|
27
|
+
`CoverageDecl`, then wired it into two of its three consumers: the resolver and
|
|
28
|
+
the deny message. The third -- the audit row -- was missed. `client.py` had been
|
|
29
|
+
reading `raw_event` / `deciding_events` / `observe_events` out of
|
|
30
|
+
`context["cz_enforcement_ctx"]` since #2203, but nothing ever put them there.
|
|
31
|
+
Every row was therefore resolved against an empty
|
|
32
|
+
`EventDeclaration(raw="", deciding=(), observe=())`, `declares_anything` was
|
|
33
|
+
False on every row ever written, so the two declaration-dependent branches --
|
|
34
|
+
`not_enforced / event_declared_observe` and
|
|
35
|
+
`indeterminate / event_not_declared_deciding` -- could not fire at all, and any
|
|
36
|
+
row reaching them fell through to the affirmative default instead.
|
|
37
|
+
|
|
38
|
+
Driven on pristine `origin/main`, `kiro_cli` + `userPromptSubmit`:
|
|
39
|
+
|
|
40
|
+
enforcement_outcome : enforced
|
|
41
|
+
enforcement_reason : host_enforced
|
|
42
|
+
enforced : True
|
|
43
|
+
|
|
44
|
+
`KiroCLIAdapter` declares that exact event OBSERVE-ONLY, and kiro-cli 2.16.2 was
|
|
45
|
+
driven answering the prompt after BOTH an exit-2 hook and a structured stdout
|
|
46
|
+
deny. The same row now reads:
|
|
47
|
+
|
|
48
|
+
enforcement_outcome : not_enforced
|
|
49
|
+
enforcement_reason : event_declared_observe_only
|
|
50
|
+
enforced : False
|
|
51
|
+
|
|
52
|
+
**What this means for you.** Rows already in your audit store that say
|
|
53
|
+
`enforced / host_enforced` for a prompt-submission event on an observe-only host
|
|
54
|
+
are not evidence the call was blocked. Nothing about the host's behaviour
|
|
55
|
+
changed in this release -- kiro-cli did not start or stop enforcing anything.
|
|
56
|
+
What changed is that the record now distinguishes "the host imposed this" from
|
|
57
|
+
"we asked and cannot establish that it was imposed". If you query or alert on
|
|
58
|
+
`enforced`, expect observe-only surfaces to move from True to False, and treat
|
|
59
|
+
that as the field becoming correct rather than as a regression.
|
|
60
|
+
|
|
61
|
+
**2. A deny rule naming a host tool name was inert on the shipped hook path
|
|
62
|
+
(#2245).** `deny: Read` was accepted by `validate`, previewed as DENY by
|
|
63
|
+
`test`, and silently ALLOWED by the real `controlzero hook-check`. Three
|
|
64
|
+
surfaces told the customer they were protected while the enforcement path
|
|
65
|
+
ignored the rule, and the audit row for the allowed call was byte-identical to
|
|
66
|
+
one written with no rule authored at all. Measured across the shipped security
|
|
67
|
+
template -- 11 template tool names -- the base enforced 4 of 11 bare names; this
|
|
68
|
+
release enforces 11 of 11, and `controlzero test` now agrees with the hook
|
|
69
|
+
because both build the subject through the hook's own factory.
|
|
70
|
+
|
|
71
|
+
### Changed: what an audit row asserts about enforcement (#2113, #2114, #2236)
|
|
72
|
+
|
|
73
|
+
- **Enforcement is now bound to the host's own event declaration.** The
|
|
74
|
+
`cz_enforcement_ctx` keys the audit path has always read are now actually
|
|
75
|
+
populated, so the per-host branch can fire.
|
|
76
|
+
|
|
77
|
+
- **An absent enforcement-context key is silence, not an empty declaration
|
|
78
|
+
(#2218).** "This host declared nothing" and "this host declared an explicitly
|
|
79
|
+
empty deciding set" are different facts and are now encoded in the TYPE rather
|
|
80
|
+
than in a flag, so the two cannot collapse into one another.
|
|
81
|
+
|
|
82
|
+
- **`render()` now receives the host event.** It previously did not, so the WIRE
|
|
83
|
+
posture was resolved against an empty `EventDeclaration` while the audit row
|
|
84
|
+
used the real one. A single `hook-check` run could put
|
|
85
|
+
`indeterminate / event_not_declared_deciding` on the wire and write
|
|
86
|
+
`enforced / host_enforced` to the row -- two answers to "was the customer
|
|
87
|
+
protected?" out of one process, on 7 of the 9 shipped adapters. The method's
|
|
88
|
+
own docstring said this could not happen.
|
|
89
|
+
|
|
90
|
+
- **`CursorCLIAdapter.render` no longer drops the posture.** Its override could
|
|
91
|
+
not accept the event pair and rebuilt its `RenderResult` without
|
|
92
|
+
`enforcement`, silently downgrading the ask-path posture to NOT_FINALIZED. A
|
|
93
|
+
control applied at one point on one path, hidden in a subclass override.
|
|
94
|
+
|
|
95
|
+
- **A contradictory declaration is now a named posture, INDETERMINATE.** An
|
|
96
|
+
adapter declaring one event both deciding and observe was previously resolved
|
|
97
|
+
by EVALUATION ORDER -- `is_declared_observe()` is tested first, so the observe
|
|
98
|
+
branch won by position over a contradiction nobody was told about. A
|
|
99
|
+
contradictory declaration is not evidence the host let the call through, and
|
|
100
|
+
`_enforcement_notice` now carries a specific line for it that says it is our
|
|
101
|
+
defect to fix, instead of inheriting the generic "could not confirm" that
|
|
102
|
+
reads identically to the host merely being unobservable. No shipped adapter is
|
|
103
|
+
currently self-contradictory, and that is asserted in both directions.
|
|
104
|
+
|
|
105
|
+
### Fixed
|
|
106
|
+
|
|
107
|
+
- **A deny rule naming a HOST tool name was inert on the hook path (#2245).**
|
|
108
|
+
The originally-filed hypothesis -- a colon-less subject -- is REFUTED and
|
|
109
|
+
pinned as such: every matcher formats `tool:method` unconditionally, and
|
|
110
|
+
`Read:*` already matched the empty-method subject `Read:`. The real mechanism
|
|
111
|
+
is an alias-resolution asymmetry: the hook resolved the HOST name to its
|
|
112
|
+
canonical family name (`Read` -> `file_read`) and discarded the host spelling
|
|
113
|
+
before the gate ran, while the rule kept the host spelling. `Bash` survived
|
|
114
|
+
only because it is its own canonical name. The gate is now asked about every
|
|
115
|
+
name the call actually carried -- the host spelling AND its canonical family
|
|
116
|
+
name -- and NEVER about sibling aliases, because the alias map is many-to-one
|
|
117
|
+
and canonicalising the RULE would make `deny: Edit` also block `Write`. Both
|
|
118
|
+
directions are pinned. The widening ships behind a phase-1 monotone-closing
|
|
119
|
+
gate: closing flips ship, opening flips are suppressed and counted as
|
|
120
|
+
`match_semantics=new_suppressed`.
|
|
121
|
+
|
|
122
|
+
- **The #2245 phase gate could not see the context axis, and a deny became an
|
|
123
|
+
allow (#2320).** #2245 injects `subject_origin` into the decision context.
|
|
124
|
+
Rule conditions merge the whole context, so a rule can match on a key that did
|
|
125
|
+
not exist before -- and `_choose_match`'s monotone-closing gate enumerated the
|
|
126
|
+
ACTION axis only, so it could not observe a flip caused that way. Driven on
|
|
127
|
+
`main`:
|
|
128
|
+
|
|
129
|
+
base c99dba92e rc=2 DENY policy_id=old-deny (no match_semantics)
|
|
130
|
+
main 6dd972815 rc=0 ALLOW policy_id=origin-allow match_semantics=unchanged
|
|
131
|
+
fixed rc=2 DENY policy_id=old-deny match_semantics=new_suppressed
|
|
132
|
+
|
|
133
|
+
The audit stamped `unchanged` on the exact call whose effect flipped. That is
|
|
134
|
+
worse than an absent field: the record actively asserted the false thing. The
|
|
135
|
+
fix declares, per `SubjectOrigin`, which context keys pre-existed
|
|
136
|
+
(`PRE_WIDENING_CONTEXT_KEYS`, sourced from the pre-fix tree) and evaluates the
|
|
137
|
+
rule walk against both contexts. It is a positive set with import-time
|
|
138
|
+
exhaustiveness, so a new origin is unshippable until declared. `unchanged` is
|
|
139
|
+
now true BY CONSTRUCTION rather than by assumption, and `match_semantics`
|
|
140
|
+
gains `indeterminate` for "the pre-widening verdict could not be established".
|
|
141
|
+
The 11 x 3 action matrix is unchanged at 33/33, so #2245 loses nothing.
|
|
142
|
+
|
|
143
|
+
- **Kiro sub-agent SPAWNS were not gated at `preToolUse` (#2331).**
|
|
144
|
+
`_KIRO_V3_PRE_TOOL_MATCHERS` wired `execute_bash` / `fs_write` / `fs_read` /
|
|
145
|
+
`use_aws` only, so an operator who wrote `deny: use_subagent` had a rule that
|
|
146
|
+
could not fire. Driven on kiro-cli 2.18.1: with the old matcher list
|
|
147
|
+
`use_subagent` reached `preToolUse` ZERO times and the sub-agent completed;
|
|
148
|
+
with the spawn matchers added it reaches `preToolUse` and exit 2 blocks it
|
|
149
|
+
("PreToolHook blocked the tool execution").
|
|
150
|
+
|
|
151
|
+
**This is admission control in SCOPE, not enforcement in strength**, and the
|
|
152
|
+
limit was observed rather than theorised: in the same run the model responded
|
|
153
|
+
to the blocked spawn by doing the identical work through its own
|
|
154
|
+
`execute_bash`. The Kiro coverage tier stays `partial`.
|
|
155
|
+
|
|
156
|
+
**`use_subagent` is the only spawn tool established as real on kiro-cli
|
|
157
|
+
2.18.1.** The matcher tuple carries five names. Do not read that as five
|
|
158
|
+
gateable spawn surfaces:
|
|
159
|
+
|
|
160
|
+
- `delegate` -- INERT. Experimental and disabled; the binary says so verbatim
|
|
161
|
+
("Delegate tool is experimental and not enabled. Use /experiment to enable
|
|
162
|
+
it").
|
|
163
|
+
- `agent_crew` -- INERT. Present in the binary's alias table but not exposed.
|
|
164
|
+
- `spawn_session` -- INERT, and structurally so: it is not a tool name at all
|
|
165
|
+
but a `command` enum value inside the `session_management` tool, which makes
|
|
166
|
+
it wrong as a MATCHER rather than merely unused.
|
|
167
|
+
- `session_management` -- **status NOT ESTABLISHED.** The driven enumeration
|
|
168
|
+
accounted for the three names above; it recorded nothing either way about
|
|
169
|
+
this one, and the shipped Kiro template still carries commented guidance
|
|
170
|
+
naming it. It is neither claimed as coverage nor asserted to be inert here,
|
|
171
|
+
because neither has been driven. Treat it as unproven until it is.
|
|
172
|
+
|
|
173
|
+
Driven: the agent's own tool enumeration returned 13 tools and none of the
|
|
174
|
+
three named INERT above, and declaring all four explicitly surfaced none of
|
|
175
|
+
them. They are carried so the matcher is already in place if a future kiro-cli
|
|
176
|
+
exposes them. Today only `use_subagent` is shown to gate anything, and this
|
|
177
|
+
release claims nothing more.
|
|
178
|
+
|
|
179
|
+
- **`controlzero verify --prove` certified enforcement from an exit code we
|
|
180
|
+
produce (#2305).** Driven on a box where all seven host binaries were ABSENT,
|
|
181
|
+
with the hook repointed at a script whose entire body is
|
|
182
|
+
`cat > /dev/null; exit 2`, it reported `[GREEN] claude_code -- effective gate
|
|
183
|
+
holds + real deny blocked` and `status: ENFORCING`, exit 0. The same shim
|
|
184
|
+
exiting 0 produced RED, which is what establishes that no host was in the loop
|
|
185
|
+
at any point. Three states that must never collapse -- ENFORCING /
|
|
186
|
+
NOT_ENFORCING / UNVERIFIABLE -- were two, and the collapse always resolved
|
|
187
|
+
optimistically. `ENFORCING` is now reachable from ONE point in one resolver,
|
|
188
|
+
and the gate there is deliberately NOT "some host evidence exists" -- that
|
|
189
|
+
weaker gate shipped in the first draft of this module and was refuted, because
|
|
190
|
+
"a config file has a foreign key" unlocked the same sentence as an executed
|
|
191
|
+
binary. Evidence licenses NAMED claims, and an affirmative requires the
|
|
192
|
+
specific `HOST_PRESENT` claim, plus `effective_state_ok` and `probe_blocked`
|
|
193
|
+
both true -- so the verdict object cannot be constructed while asserting
|
|
194
|
+
something it does not hold. `HostEvidence` is produced by collectors that read
|
|
195
|
+
an artifact the HOST emitted, and evidence collected from one adapter cannot
|
|
196
|
+
certify another. Stated exactly rather than absolutely: this closes the
|
|
197
|
+
accidental paths, not a determined one -- the private sentinel can be imported
|
|
198
|
+
deliberately, so this is a chokepoint, not a proof of unforgeability. Also
|
|
199
|
+
fixed in the same pass: probe
|
|
200
|
+
errors collapsed into "did not block"; `doctor` rendered a bare all-clear
|
|
201
|
+
having examined nothing and swallowed reader exceptions; antigravity presence
|
|
202
|
+
tested a path the installer never writes; and install self-checks claimed a
|
|
203
|
+
host blocked something.
|
|
204
|
+
|
|
205
|
+
- **Cross-SDK prompt vocabulary is now pinned (#2331).** Every test asserted one
|
|
206
|
+
SDK against literals it restated itself, so a rename on either side left both
|
|
207
|
+
suites green -- the #2071 shape that let #2108 be closed while still live in
|
|
208
|
+
Python. The Node suite now EXTRACTS the vocabulary from Python source at run
|
|
209
|
+
time and drives Node against it, asserting an exact-set IFF over a bounded
|
|
210
|
+
pool rather than one-way containment, with a match-count and uniqueness
|
|
211
|
+
assertion on every extraction so a moved symbol fails loudly instead of
|
|
212
|
+
passing vacuously.
|
|
213
|
+
|
|
214
|
+
### Changed (AUDIT SCHEMA BREAK: the per-finding `masked` boolean is gone)
|
|
215
|
+
|
|
216
|
+
- **A DLP finding's `masked: True` is replaced by `mask_state` (#2096).**
|
|
217
|
+
`masked` was stamped from the RULE'S ACTION (`m.action == "mask"`), which is
|
|
218
|
+
what the rule says to DO -- never evidence that anything was done. A call
|
|
219
|
+
DENIED on `mask_unsupported_on_host` redacted nothing and never ran, yet
|
|
220
|
+
produced a findings array byte-identical to one for a call that really was
|
|
221
|
+
redacted. `args_hash` is taken over the ORIGINAL arguments on every path, so
|
|
222
|
+
nothing else in the row discriminated either. Established by driving
|
|
223
|
+
`Client.guard` against pristine `origin/main`: four deny scenarios and one
|
|
224
|
+
allow scenario all emitted `'masked': True`.
|
|
225
|
+
|
|
226
|
+
A boolean cannot carry this truth, so it is now a closed vocabulary, matching
|
|
227
|
+
the Node SDK exactly and asserted against the shared cross-SDK manifest
|
|
228
|
+
`tests/parity/dlp_control_outcomes.json`:
|
|
229
|
+
|
|
230
|
+
| `mask_state` | meaning |
|
|
231
|
+
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
232
|
+
| `applied` | the caller acknowledged consuming the redacted args via `decision.apply_masked_args()`. The ONLY value that asserts that acknowledgement. |
|
|
233
|
+
| `declared_not_confirmed` | the SDK redacted into `decision.masked_args` and the caller declared `supports_arg_rewrite=True`; whether it forwards them is not observable. |
|
|
234
|
+
| `not_applied` | a mask rule matched and NOTHING was redacted (the call was denied instead). |
|
|
235
|
+
| `not_applicable` | the rule's action is `detect`/`block`, or this is a post-execution output observation or a third-party export scan. |
|
|
236
|
+
|
|
237
|
+
Treat any unrecognised value as NOT `applied`.
|
|
238
|
+
|
|
239
|
+
**What `applied` does NOT mean.** It is one link short of "the tool received
|
|
240
|
+
redacted bytes", deliberately. `apply_masked_args()` emits the record and then
|
|
241
|
+
returns the args, so a caller can acknowledge and still forward the originals.
|
|
242
|
+
No value in this vocabulary asserts delivery to the tool, because the SDK
|
|
243
|
+
cannot observe what the caller does next.
|
|
244
|
+
|
|
245
|
+
- **`DLPScanner.get_findings_for_audit(matches)` now REQUIRES a second
|
|
246
|
+
argument**, `mask_state`, and rejects both `"applied"` and any value outside
|
|
247
|
+
the vocabulary with a `TypeError` before a single finding is built. The
|
|
248
|
+
posture for an unknown value is REJECT, not downgrade and not pass-through.
|
|
249
|
+
|
|
250
|
+
- **Every decision audit row now carries `dlp_mask_state`**, including rows with
|
|
251
|
+
no DLP findings at all (`not_applicable`). Absence is not a state: "no mask
|
|
252
|
+
was in play" and "a mask fired and was not honoured" are separate, queryable
|
|
253
|
+
facts.
|
|
254
|
+
|
|
255
|
+
- **`decision.masked_args` is deep-cloned and frozen** before `guard()` returns.
|
|
256
|
+
An in-place mutation through the ordinary mapping and sequence operations
|
|
257
|
+
raises the new, exported `MaskedArgsFrozenError` (a `TypeError` subclass) at
|
|
258
|
+
the line attempting it, rather than succeeding and leaving the
|
|
259
|
+
`dlp_mask_applied` record describing bytes the caller never forwarded. Take
|
|
260
|
+
`dict(decision.masked_args)` for a mutable copy. Chosen over a hash comparison
|
|
261
|
+
deliberately: a comparison detects the tamper only if someone remembers to
|
|
262
|
+
call it.
|
|
263
|
+
|
|
264
|
+
**The guarantee is BOUNDED, and the bound is stated rather than implied.**
|
|
265
|
+
Python has no true deep freeze. The protection covers the JSON-shaped tree,
|
|
266
|
+
which is the only shape a redaction is produced in; it is not a claim that
|
|
267
|
+
every in-place mutation is impossible. Specifically: a `set` / `frozenset`
|
|
268
|
+
leaf and a custom mutable object leaf are returned BY IDENTITY, so mutating
|
|
269
|
+
one the caller still holds does change the tree the acknowledgement returns.
|
|
270
|
+
Both are left alone deliberately -- neither is JSON-representable, so neither
|
|
271
|
+
can survive the RFC 8785 canonicaliser or any host envelope, meaning a mask
|
|
272
|
+
decision cannot legitimately carry one.
|
|
273
|
+
|
|
274
|
+
### Added
|
|
275
|
+
|
|
276
|
+
- **`decision.apply_masked_args()`** -- the single producer of
|
|
277
|
+
`mask_state: "applied"`. Present only on a decision that carries a redaction;
|
|
278
|
+
`None` everywhere else, so "there is nothing to acknowledge" and "the caller
|
|
279
|
+
declined to acknowledge" stay distinguishable. Calling it emits a
|
|
280
|
+
`dlp_mask_applied` audit event carrying `args_hash` + `masked_args_hash` (the
|
|
281
|
+
pair an auditor joins on to see that the bytes actually changed) and returns
|
|
282
|
+
the frozen redacted arguments.
|
|
283
|
+
|
|
284
|
+
- **`controlzero.MaskedArgsFrozenError`** is exported from the package root.
|
|
285
|
+
|
|
286
|
+
### Known limitations, stated plainly
|
|
287
|
+
|
|
288
|
+
- The Python SDK's DLP decision fields -- `dlp_findings`, `dlp_status`,
|
|
289
|
+
`dlp_masked` and now `dlp_mask_state` -- are written to the LOCAL audit log
|
|
290
|
+
only. `audit_remote._build_wire_entry` is an explicit allowlist and forwards
|
|
291
|
+
none of them, so none of this is visible in a hosted audit trail today. That
|
|
292
|
+
is pre-existing behaviour and unchanged here; #2096 corrects what the record
|
|
293
|
+
SAYS, not where it is shipped.
|
|
294
|
+
|
|
295
|
+
- The Go SDK's `GetFindingsForAudit` gained the same closed vocabulary, but it
|
|
296
|
+
still has NO first-party caller: the Go `Guard` attaches no findings to any
|
|
297
|
+
decision or audit row, so no Go audit row carries a `mask_state`. That is a
|
|
298
|
+
declared gap, pinned by a test that fails the day anyone wires it up. The Go
|
|
299
|
+
SDK also left the build on 2026-08-14 (#2291) and is not published from this
|
|
300
|
+
release.
|
|
301
|
+
|
|
302
|
+
- **Kiro sub-agent coverage is CONFIG-SCOPED, not absent, and one path stays
|
|
303
|
+
ungated.** It would be wrong in both directions to say sub-agents were
|
|
304
|
+
previously ungoverned. Driven on kiro-cli 2.18.1, 19 legs, each with a nonce
|
|
305
|
+
side-effect proving the call actually ran: a sub-agent that runs an agent
|
|
306
|
+
config carrying our hooks DOES fire `preToolUse` and `postToolUse` inside the
|
|
307
|
+
sub-agent (leg G), and a child hook exiting 2 blocks the sub-agent's own tool
|
|
308
|
+
call (leg J). Hooks resolve from the SUB-AGENT'S OWN config and are not
|
|
309
|
+
inherited from the parent.
|
|
310
|
+
|
|
311
|
+
The genuine gap is leg K: a sub-agent spawned WITHOUT an explicit `agent_name`
|
|
312
|
+
runs an implicit config that carries no hooks, and setting
|
|
313
|
+
`chat.defaultAgent` to a hooked agent does not help -- leg K establishes that
|
|
314
|
+
the implicit agent does not resolve to it, so the path cannot be reached by
|
|
315
|
+
writing hooks anywhere on disk. That is kiro **#7755**, which is OPEN with no
|
|
316
|
+
maintainer response and no assignee. Do not cite #7671 for this: it is CLOSED
|
|
317
|
+
AS COMPLETED, was filed against 0.11.130, and the behaviour it describes
|
|
318
|
+
("hooks will not trigger in subagents") is contradicted by the 2.18.1 binary.
|
|
319
|
+
The vendor documentation still carries that sentence; for a claim about what
|
|
320
|
+
ships today, the binary outranks the doc.
|
|
321
|
+
|
|
322
|
+
Gating the SPAWN is what makes `deny: use_subagent` reachable, and it narrows
|
|
323
|
+
the ungoverned surface rather than eliminating it.
|
|
324
|
+
|
|
325
|
+
- **Scope of every driven Kiro claim in this release: kiro-cli 2.18.1 on
|
|
326
|
+
macOS**, `chat --no-interactive`, `chat.enableSubagent=true`. It is NOT a
|
|
327
|
+
claim about the whole `>=2.1.0,<3.0.0` range the adapter declares.
|
|
328
|
+
|
|
329
|
+
- **Kiro matches by ALIAS FAMILY, not literal matcher name.** Observed on
|
|
330
|
+
2.18.1 in two independent pairs: an `execute_bash` matcher fired on
|
|
331
|
+
`tool_name` `shell`, and `fs_write` fired on `write`. Each matcher must stay
|
|
332
|
+
a single tool-name token -- a regex-style `shell|execute_bash` alternation
|
|
333
|
+
matches a tool LITERALLY named that, which is to say nothing at all, and
|
|
334
|
+
would silently disable enforcement.
|
|
335
|
+
|
|
336
|
+
## 1.13.11 (2026-08-10): kiro_cli prompts were never actually scanned
|
|
337
|
+
|
|
338
|
+
1.13.10 said "Prompt submission is now a scanned surface on the hosts that
|
|
339
|
+
expose one". For `kiro_cli` that was not true, and this release makes it true.
|
|
340
|
+
|
|
341
|
+
### Fixed
|
|
342
|
+
|
|
343
|
+
- **`kiro_cli` returned an affirmative ALLOW on a prompt it never read (#2108).**
|
|
344
|
+
A `userPromptSubmit` payload carries no `tool_name`, so it took the
|
|
345
|
+
missing-tool_name branch: under `--strict` (what the installer writes) it was
|
|
346
|
+
REFUSED without being scanned, and without `--strict` it was ALLOWED without
|
|
347
|
+
being scanned. Driven against the real kiro-cli 2.16.2 payload, all three of
|
|
348
|
+
a secret-bearing prompt, a clean prompt and an empty prompt produced
|
|
349
|
+
BYTE-IDENTICAL output -- the prompt text was not an input to the decision at
|
|
350
|
+
all -- and NO audit row was written, so the call was unattributable.
|
|
351
|
+
The prompt is now lifted into the `prompt` pseudo-tool and rides the existing
|
|
352
|
+
DLP path. The three cases now differ, and every one is audited.
|
|
353
|
+
|
|
354
|
+
- **An ambient `CLAUDECODE` environment variable stole the routing.** A
|
|
355
|
+
camelCase kiro payload in a shell where `CLAUDECODE=1` was claimed by the
|
|
356
|
+
Claude Code adapter, and the secret never reached the scanner. Anyone running
|
|
357
|
+
kiro-cli inside a Claude Code session had no prompt DLP at all.
|
|
358
|
+
|
|
359
|
+
- **Padding a prompt past the 1 MiB scan cap bypassed DLP entirely.** The
|
|
360
|
+
bounded UTF-8 prefix is now scanned, and a clean prefix is still a deny with
|
|
361
|
+
`skipped/payload_over_size_cap` -- never reported as `clean`.
|
|
362
|
+
|
|
363
|
+
- **`--strict` failed open on a non-dict payload.** It now refuses, like the
|
|
364
|
+
other four unusable-payload branches.
|
|
365
|
+
|
|
366
|
+
- **`prompt` was not a canonical tool**, so authoring `deny: prompt:*` -- the
|
|
367
|
+
exact rule this feature exists for -- printed "not a known canonical action".
|
|
368
|
+
|
|
369
|
+
- **kiro-cli v3 PascalCase prompt events** were uncovered; normalisation is now
|
|
370
|
+
shared by both Kiro adapters so the result does not depend on which adapter
|
|
371
|
+
won the routing race.
|
|
372
|
+
|
|
373
|
+
### Known limitations, stated plainly
|
|
374
|
+
|
|
375
|
+
- **kiro-cli does not enforce this event.** Driven on 2.16.2: a hook exiting 2
|
|
376
|
+
on `userPromptSubmit` is logged as failed and the prompt reaches the model
|
|
377
|
+
anyway; a structured stdout deny is also ignored. Control Zero SCANS and
|
|
378
|
+
RECORDS the prompt on this host. It does not block it. Nothing in this
|
|
379
|
+
release claims otherwise.
|
|
380
|
+
|
|
381
|
+
- **The raw-prompt marker does not reach the hosted dashboard.** It reaches the
|
|
382
|
+
DLP scanner and the LOCAL audit record; surfacing it remotely needs a backend
|
|
383
|
+
field and is not done here.
|
|
384
|
+
|
|
385
|
+
- **The Node SDK's equivalent fix is still unreachable as installed (#2208).**
|
|
386
|
+
This release fixes Python only.
|
|
387
|
+
|
|
3
388
|
## 1.13.10 (2026-07-30): DLP now covers prompts and tool output, not just tool arguments
|
|
4
389
|
|
|
5
390
|
Until this release, Control Zero scanned the ARGUMENTS of a tool call and
|
|
@@ -20,6 +405,15 @@ routes that looked identical to a working setup. All three are closed here.
|
|
|
20
405
|
keeping the secret it had just detected. Now gated on a named set of
|
|
21
406
|
value-bearing categories, so a category added later cannot reach cleartext.
|
|
22
407
|
|
|
408
|
+
- **Control Zero's own audit egress was shipping your raw tool input and
|
|
409
|
+
output (#1967 path 5).** `input_payload` / `output_payload` carried customer
|
|
410
|
+
content verbatim to our backend, and redaction was deferred to the backend --
|
|
411
|
+
i.e. applied after the bytes had already left your machine. They are now
|
|
412
|
+
scanned and redacted BEFORE the wire, every sensitive span rather than only
|
|
413
|
+
`mask`-action rules, and the payload is DROPPED rather than forwarded if the
|
|
414
|
+
scanner cannot run. `io_dlp_scan_status` is always set, so "not scanned" stays
|
|
415
|
+
distinguishable from "scanned and clean".
|
|
416
|
+
|
|
23
417
|
- **Spooled audit records were replayed without ever being scanned (#1967).**
|
|
24
418
|
Replay validated JSON shape, tool name and decision -- never payload content
|
|
25
419
|
-- then transmitted the stored bytes as-is, possibly days later on a
|
|
@@ -62,9 +456,9 @@ the loader does not consume.
|
|
|
62
456
|
|
|
63
457
|
### Not in this release
|
|
64
458
|
|
|
65
|
-
Tool output is observed, not blocked.
|
|
66
|
-
|
|
67
|
-
|
|
459
|
+
Tool output is observed, not blocked. That is the intended behaviour, not a
|
|
460
|
+
limitation awaiting a fix: an agent reading a file is normal work, and blocking
|
|
461
|
+
it would make Control Zero unusable.
|
|
68
462
|
|
|
69
463
|
## 1.13.9 (2026-07-28): Kiro IDE hooks execute on 1.0.x and stay in the correct project
|
|
70
464
|
|
|
@@ -549,7 +943,7 @@ coverage declaration intentionally remains observe-only.
|
|
|
549
943
|
|
|
550
944
|
### Fixed
|
|
551
945
|
|
|
552
|
-
- **kiro-cli Windows / non-UTF-8 install crash (
|
|
946
|
+
- **kiro-cli Windows / non-UTF-8 install crash (customer report).** `controlzero
|
|
553
947
|
kiro init` captured kiro-cli's output in text mode using the OS locale codec, so
|
|
554
948
|
kiro-cli's UTF-8 output crashed the install's subprocess reader thread on
|
|
555
949
|
non-UTF-8 Windows locales (Korean CP949, Japanese SJIS, Chinese GBK). That crash
|
|
@@ -1185,7 +1579,7 @@ changes do not perform.
|
|
|
1185
1579
|
|
|
1186
1580
|
## 1.9.3 -- 2026-06-16 (posture release: empty-bundle OBSERVE + self-explaining no-rule-match deny, epic gh#1247)
|
|
1187
1581
|
|
|
1188
|
-
The consolidated **posture release** for epic gh#1247 (customer
|
|
1582
|
+
The consolidated **posture release** for epic gh#1247 (a customer).
|
|
1189
1583
|
Three customer-visible behaviors land together so the engine's "what
|
|
1190
1584
|
happens when a tool isn't covered by a rule" story is coherent across
|
|
1191
1585
|
both SDKs and the backend:
|
|
@@ -1200,7 +1594,7 @@ both SDKs and the backend:
|
|
|
1200
1594
|
|
|
1201
1595
|
- **Hosted no-rule-match deny is now self-explaining (folds gh#1257).** A
|
|
1202
1596
|
non-empty bundle with `default_action=deny` and zero rule matches (the
|
|
1203
|
-
exact shape of
|
|
1597
|
+
exact shape of that customer's "Db read only" allow-list, which excludes
|
|
1204
1598
|
`bash`) used to deny with the bare, bug-looking message
|
|
1205
1599
|
`No matching policy rule (fail-closed default)` -- no path out. The
|
|
1206
1600
|
deny reason now NAMES the unmatched action (e.g. `bash:find`) and the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: controlzero
|
|
3
|
-
Version: 1.13.
|
|
3
|
+
Version: 1.13.12
|
|
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
|
|
@@ -19,7 +19,7 @@ for framework examples (LangChain, LangGraph, OpenAI Agents, Anthropic, CrewAI,
|
|
|
19
19
|
AutoGen, Pydantic AI, MCP servers, and more).
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
from controlzero.client import Client
|
|
22
|
+
from controlzero.client import Client, MaskedArgsFrozenError
|
|
23
23
|
from controlzero.errors import (
|
|
24
24
|
PolicyDecision,
|
|
25
25
|
PolicyDeniedError,
|
|
@@ -41,10 +41,11 @@ 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.
|
|
44
|
+
__version__ = "1.13.12"
|
|
45
45
|
|
|
46
46
|
__all__ = [
|
|
47
47
|
"Client",
|
|
48
|
+
"MaskedArgsFrozenError",
|
|
48
49
|
"PendingApproval",
|
|
49
50
|
"SecretRef",
|
|
50
51
|
"SecretStr",
|