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