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.
- {controlzero-1.13.6 → controlzero-1.13.8}/CHANGELOG.md +352 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/PKG-INFO +2 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/__init__.py +1 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/action_aliases.py +1 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/action_validator.py +23 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/bundle.py +12 -4
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/credential_hook.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/credential_scanner.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/dlp_scanner.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/enforcer.py +556 -22
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/hook_extractors.py +187 -5
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/io_capture_producer.py +4 -3
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/types.py +1 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/audit_remote.py +6 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/canonical.py +2 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/_secrets.py +6 -5
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/console.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/doctor.py +25 -16
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/__init__.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/antigravity.py +16 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/base.py +82 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/claude_code.py +67 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/codex_cli.py +51 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/cursor.py +49 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/cursor_cli.py +18 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/gemini_cli.py +36 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/kiro.py +32 -14
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hosts/unknown.py +4 -1
- controlzero-1.13.8/controlzero/cli/kiro_adapter.py +901 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/main.py +417 -41
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/migrate.py +5 -6
- controlzero-1.13.8/controlzero/cli/setup_wizard.py +481 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/spool_cmd.py +3 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/telemetry_consent.py +4 -5
- controlzero-1.13.8/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +15 -0
- controlzero-1.13.8/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +14 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/kiro/kiro.yaml +9 -6
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/client.py +114 -24
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/enrollment.py +10 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/error_codes.py +5 -5
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/error_codes.yaml +9 -6
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/errors.py +3 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/grant_protocol.py +4 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/mock.py +7 -7
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/pending_approval.py +30 -3
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hook_integrity.py +2 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hosted_policy.py +94 -3
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/anthropic.py +5 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/autogen.py +2 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/crewai/agent.py +8 -6
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/crewai/crew.py +18 -14
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/crewai/task.py +11 -4
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/crewai/tool.py +6 -4
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/google.py +11 -3
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/google_adk/agent.py +9 -7
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/google_adk/tool.py +8 -5
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/agent.py +14 -14
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/callbacks.py +27 -23
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/chain.py +10 -10
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/graph.py +25 -25
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/modern.py +2 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/tool.py +10 -7
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/litellm.py +7 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/openai.py +6 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/pydantic_ai.py +6 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/vercel_ai.py +6 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/policy_loader.py +7 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/secrets/reference.py +2 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/secrets/resolver.py +9 -8
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_keyring.py +65 -5
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/tamper.py +3 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/pyproject.toml +6 -1
- controlzero-1.13.8/ruff.toml +40 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_conformance.py +1 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_crash.py +1 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_durable_default_tamper.py +1 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_keychain_dek.py +142 -7
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_uploader.py +1 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_action_validator_t86.py +17 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_adapter_contracts.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_adapter_interface.py +16 -4
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_adapter.py +14 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_envelope_parity.py +13 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_ga_blockers_1248.py +16 -5
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_tool_vocab_1303.py +0 -1
- controlzero-1.13.8/tests/test_api_url_config.py +72 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_audit_project_id_attribution_pr2.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_audit_remote.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_audit_remote_io_capture.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_audit_remote_sdk_version.py +1 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_bundle_cache_freshness_1303.py +1 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_bundle_parser.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_bundle_translate.py +0 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_debug_bundle.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_extractor_integration.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_hook.py +5 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_init_templates.py +1 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_condition_operators_1495.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_conditions.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_credential_hook.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cross_adapter_shell_enforcement.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cursor_adapter.py +14 -3
- controlzero-1.13.8/tests/test_dashboard_shell_template_1904.py +746 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_default_action.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_dlp_mask.py +0 -1
- controlzero-1.13.8/tests/test_dlp_mask_hosts_1938.py +164 -0
- controlzero-1.13.8/tests/test_dlp_origin_contract.py +620 -0
- controlzero-1.13.8/tests/test_dlp_scan_coverage_1927_1931.py +634 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_dlp_scanner.py +7 -7
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_enrollment.py +10 -8
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_epic_1247_bryan_acceptance.py +6 -4
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_errors_e_codes.py +0 -1
- controlzero-1.13.8/tests/test_git_target_1734.py +242 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_5d_email_install.py +3 -3
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_cli_flag.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_get_secret_hitl.py +26 -26
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_mock_backend.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_request_approval.py +1 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_secret_leak_guard.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_wait.py +16 -7
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_conformance.py +10 -11
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_phase2b_protocol.py +29 -8
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_reason_codes.py +5 -3
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hook_command_pinning_1705.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hook_extractors.py +0 -1
- controlzero-1.13.8/tests/test_hook_warn_surfaces_reason_1903.py +479 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hosted_local_audit_1247.py +2 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hosted_policy_e2e.py +4 -3
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hosts_adapter.py +29 -18
- controlzero-1.13.8/tests/test_install_api_url.py +69 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_install_hook_command.py +0 -1
- controlzero-1.13.8/tests/test_kiro_adapter.py +1227 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_layout_migration_t101.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_localized_reason.py +1 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_multi_client_per_project_175.py +8 -5
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_per_client_isolation_1011.py +1 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_policy_freshness.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_policy_load_failclosed_f3.py +3 -4
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_pr4_install_messaging.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_quarantine.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_reason_code.py +8 -3
- controlzero-1.13.8/tests/test_reason_code_hitl_grant_1596.py +34 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_secrets.py +0 -2
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_secrets_reference_resolver.py +1 -2
- controlzero-1.13.8/tests/test_setup_wizard.py +196 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_shell_semantic_class.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_sql_semantic_class.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_synthetic_policy_id_t79.py +10 -5
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_t103_precedence.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_t108_local_override_audit.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_t96_single_audit_log.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_t99_install_prefetch_bundle.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_tamper.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_tamper_behavior.py +1 -1
- controlzero-1.13.8/tests/test_tls_ca_bundle.py +112 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_unsafe_int_boundary.py +0 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_update_cmd.py +2 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_version_aware_fail_closed.py +1 -0
- controlzero-1.13.6/controlzero/cli/kiro_adapter.py +0 -223
- controlzero-1.13.6/controlzero/cli/templates/kiro/ide-pre-tool-use.kiro.hook +0 -15
- controlzero-1.13.6/controlzero/cli/templates/kiro/ide-prompt-submit.kiro.hook +0 -14
- controlzero-1.13.6/tests/test_kiro_adapter.py +0 -257
- {controlzero-1.13.6 → controlzero-1.13.8}/.gitignore +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/Dockerfile.test +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/LICENSE +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/README.md +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/__main__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/__init__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/_messages.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/condition_ops.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/credentials_data/__init__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/credentials_data/built_in.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/source_labels.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/_internal/tool_extractors.json +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/audit_local.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/__init__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/__main__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/agent_config.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/debug_bundle.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/enforcement.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/exec_cmd.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/hook_entry.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/output.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/antigravity/hooks.json +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/antigravity.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/autogen.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/claude-code.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/codex-cli.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/cost-cap.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/crewai.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/cursor.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/gemini-cli.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/generic.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/kiro/ide-file-save.kiro.hook +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/langchain.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/mcp.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/templates/rag.yaml +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/cli/update_cmd.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/device.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/__init__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/secret_leak_guard.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hitl/status.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hooks/__init__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/hooks/tool_output_handler.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/__init__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/braintrust.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/crewai/__init__.py +1 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/google_adk/__init__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langchain/__init__.py +3 -3
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/integrations/langfuse.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/layout_migration.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/secrets/__init__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/secrets/secretstr.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/__init__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_compress.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_constants.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_crc32c.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_crypto.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_frame.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_metrics.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_spool.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_state.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/_uploader.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/spool/cz-audit-v1.dict +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/controlzero/tracecontext.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/examples/hello_world.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/_fixtures/antigravity-envelope-expected.json +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/_fixtures/jcs_args_hash_vectors.json +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/conftest.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/antigravity.json +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/claude_code.json +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/codex_cli.json +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/cursor.json +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/gemini_cli.json +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/fixtures/adapter-contracts/kiro.json +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/integrations/__init__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/integrations/test_google.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/parity/action_aliases.json +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/__init__.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/conftest.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_cli.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_concurrency.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_core.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_diskfull.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_sink_wiring.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/spool/test_spool_transcript_localack.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_action_aliases.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_action_canonicalization.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_agent_name_env.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_agy_docs_no_exit2_1373.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_hook_check.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_install.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_antigravity_posttooluse_observe_58.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_api_key_mask.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_audit_sink_isolation.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_canonical_phase1a.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_capability_matrix_doc.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_carve_out.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_hook_entry.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_hosted_refresh.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_init.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_tail.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_test.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_cli_validate.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_coding_agent_hooks.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_config_format_parity_1303.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_conformance.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_console.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_deny_rule_reconciliation_1011.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_deny_wins_default_catchall_1620.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_device.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_doctor.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_engine_version_consistency.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_env_dump_438.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_error_codes.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_fail_closed_eval.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_failopen_1303.py +1 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_glob_matching.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_exceptions.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_6a_pending_approval.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hitl_validator_keys.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hook_integrity.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hook_mask_failclosed.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hosted_local_shadow_warn_1265.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hybrid_mode_strict.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_hybrid_mode_warn.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_install_hooks.py +4 -4
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_io_capture_producer.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_cli_e2e.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_cli_hook_pin_1265.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_cli_v210_hooks.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_hook_templates.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_install.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_kiro_install_standardisation.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_layout_parity_t102.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_local_mode_dict.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_local_mode_file_json.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_local_mode_file_yaml.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_log_fallback_stderr.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_log_options_ignored_hosted.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_log_rotation.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_migrate.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_min_sdk_version_gate.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_no_policy_no_key.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_observe_mode_1247.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_package_rename_shim.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_part3_active_policy_count_1303.py +1 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_policy_engine_version_phase1b.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_policy_settings.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_policy_source_audit.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_pr4_output.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_pr4_status.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_probe_audit_isolation.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_refresh.py +1 -1
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_source_labels.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_t104_cache_gc.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_tamper_hook.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_telemetry_consent.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_tracecontext.py +0 -0
- {controlzero-1.13.6 → controlzero-1.13.8}/tests/test_upgrade_nudge.py +0 -0
- {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.
|
|
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
|
|
@@ -39,7 +39,7 @@ modern intent is silently broken.
|
|
|
39
39
|
from __future__ import annotations
|
|
40
40
|
|
|
41
41
|
import json
|
|
42
|
-
from
|
|
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
|
|
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
|
|
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
|
|
978
|
+
if fallback in ALL_EFFECTS:
|
|
971
979
|
effect_raw = fallback
|
|
972
|
-
effect = effect_raw if effect_raw in
|
|
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
|
|
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.
|