cycode 3.9.0__tar.gz → 3.9.1.dev1__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.
- {cycode-3.9.0 → cycode-3.9.1.dev1}/PKG-INFO +1 -1
- cycode-3.9.1.dev1/cycode/__init__.py +1 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/command_utils.py +7 -5
- cycode-3.9.1.dev1/cycode/cli/apps/ai_guardrails/consts.py +133 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/hooks_manager.py +29 -3
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/install_command.py +29 -10
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/handlers.py +54 -35
- cycode-3.9.1.dev1/cycode/cli/apps/ai_guardrails/scan/payload.py +268 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/response_builders.py +57 -5
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/scan_command.py +12 -1
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/types.py +11 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/status_command.py +32 -23
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/uninstall_command.py +29 -10
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/ai_security_manager_client.py +2 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/pyproject.toml +1 -1
- cycode-3.9.0/cycode/__init__.py +0 -1
- cycode-3.9.0/cycode/cli/apps/ai_guardrails/consts.py +0 -78
- cycode-3.9.0/cycode/cli/apps/ai_guardrails/scan/payload.py +0 -72
- {cycode-3.9.0 → cycode-3.9.1.dev1}/LICENCE +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/README.md +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/__main__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/app.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/consts.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/policy.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_guardrails/scan/utils.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_remediation/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_remediation/ai_remediation_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_remediation/apply_fix.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ai_remediation/print_remediation.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/auth/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/auth/auth_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/auth/auth_common.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/auth/auth_manager.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/auth/models.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/configure/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/configure/configure_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/configure/consts.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/configure/messages.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/configure/prompts.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ignore/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/ignore/ignore_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/mcp/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/mcp/mcp_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/report_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/common.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/path/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/path/path_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/repository_url/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/repository_url/repository_url_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/sbom_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report/sbom/sbom_report_file.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report_import/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report_import/report_import_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report_import/sbom/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/report_import/sbom/sbom_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/aggregation_report.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/code_scanner.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/commit_history/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/commit_history/commit_history_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/commit_range_scanner.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/detection_excluder.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/path/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/path/path_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_commit/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_commit/pre_commit_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_push/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_push/pre_push_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_receive/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/pre_receive/pre_receive_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/remote_url_resolver.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/repository/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/repository/repository_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_ci/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_ci/ci_integrations.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_ci/scan_ci_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_parameters.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/scan/scan_result.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/status/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/status/get_cli_status.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/status/models.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/status/status_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/apps/status/version_command.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/cli_types.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/config.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/console.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/consts.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/custom_exceptions.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/handle_ai_remediation_errors.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/handle_auth_errors.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/handle_errors.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/handle_report_sbom_errors.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/exceptions/handle_scan_errors.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/commit_range_documents.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/documents_walk_ignore.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/file_excluder.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/iac/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/models/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/path_documents.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/repository_documents.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/base_restore_dependencies.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/go/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/maven/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/npm/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/nuget/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/ruby/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/ruby/restore_ruby_dependencies.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/sbt/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/sca/sca_file_collector.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/walk_ignore.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/files_collector/zip_documents.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/logger.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/main.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/models.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/console_printer.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/json_printer.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/printer_base.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/rich_printer.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/sca_table_printer.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/table.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/table_models.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/table_printer.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/tables/table_printer_base.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/text_printer.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/code_snippet_syntax.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/detection_data.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/detection_ordering/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/detection_ordering/common_ordering.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/detection_ordering/sca_ordering.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/printers/utils/rich_helpers.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/base_file_manager.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/config_file_manager.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/configuration_manager.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/credentials_manager.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/user_settings/jwt_creator.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/enum_utils.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/get_api_client.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/git_proxy.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/ignore_utils.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/jwt_utils.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/path_utils.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/progress_bar.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/scan_batch.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/scan_utils.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/sentry.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/shell_executor.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/string_utils.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/task_timer.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/url_utils.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/version_checker.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cli/utils/yaml_utils.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/config.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/__init__.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/ai_security_manager_service_config.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/auth_client.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/base_token_auth_client.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/client_creator.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/config.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/config_dev.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/cycode_client.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/cycode_client_base.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/cycode_dev_based_client.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/cycode_oidc_based_client.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/cycode_token_based_client.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/headers.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/import_sbom_client.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/logger.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/models.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/report_client.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/scan_client.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/cyclient/scan_config_base.py +0 -0
- {cycode-3.9.0 → cycode-3.9.1.dev1}/cycode/logger.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '3.9.1.dev1' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
|
|
@@ -12,24 +12,26 @@ from cycode.cli.apps.ai_guardrails.consts import AIIDEType
|
|
|
12
12
|
console = Console()
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def validate_and_parse_ide(ide: str) -> AIIDEType:
|
|
16
|
-
"""Validate IDE parameter
|
|
15
|
+
def validate_and_parse_ide(ide: str) -> Optional[AIIDEType]:
|
|
16
|
+
"""Validate IDE parameter, returning None for 'all'.
|
|
17
17
|
|
|
18
18
|
Args:
|
|
19
|
-
ide: IDE name string (e.g., 'cursor')
|
|
19
|
+
ide: IDE name string (e.g., 'cursor', 'claude-code', 'all')
|
|
20
20
|
|
|
21
21
|
Returns:
|
|
22
|
-
AIIDEType enum value
|
|
22
|
+
AIIDEType enum value, or None if 'all' was specified
|
|
23
23
|
|
|
24
24
|
Raises:
|
|
25
25
|
typer.Exit: If IDE is invalid
|
|
26
26
|
"""
|
|
27
|
+
if ide.lower() == 'all':
|
|
28
|
+
return None
|
|
27
29
|
try:
|
|
28
30
|
return AIIDEType(ide.lower())
|
|
29
31
|
except ValueError:
|
|
30
32
|
valid_ides = ', '.join([ide_type.value for ide_type in AIIDEType])
|
|
31
33
|
console.print(
|
|
32
|
-
f'[red]Error:[/] Invalid IDE "{ide}". Supported IDEs: {valid_ides}',
|
|
34
|
+
f'[red]Error:[/] Invalid IDE "{ide}". Supported IDEs: {valid_ides}, all',
|
|
33
35
|
style='bold red',
|
|
34
36
|
)
|
|
35
37
|
raise typer.Exit(1) from None
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Constants for AI guardrails hooks management.
|
|
2
|
+
|
|
3
|
+
Currently supports:
|
|
4
|
+
- Cursor
|
|
5
|
+
- Claude Code
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import platform
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import NamedTuple
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AIIDEType(str, Enum):
|
|
15
|
+
"""Supported AI IDE types."""
|
|
16
|
+
|
|
17
|
+
CURSOR = 'cursor'
|
|
18
|
+
CLAUDE_CODE = 'claude-code'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PolicyMode(str, Enum):
|
|
22
|
+
"""Policy enforcement mode for global mode and per-feature actions."""
|
|
23
|
+
|
|
24
|
+
BLOCK = 'block'
|
|
25
|
+
WARN = 'warn'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class IDEConfig(NamedTuple):
|
|
29
|
+
"""Configuration for an AI IDE."""
|
|
30
|
+
|
|
31
|
+
name: str
|
|
32
|
+
hooks_dir: Path
|
|
33
|
+
repo_hooks_subdir: str # Subdirectory in repo for hooks (e.g., '.cursor')
|
|
34
|
+
hooks_file_name: str
|
|
35
|
+
hook_events: list[str] # List of supported hook event names for this IDE
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _get_cursor_hooks_dir() -> Path:
|
|
39
|
+
"""Get Cursor hooks directory based on platform."""
|
|
40
|
+
if platform.system() == 'Darwin':
|
|
41
|
+
return Path.home() / '.cursor'
|
|
42
|
+
if platform.system() == 'Windows':
|
|
43
|
+
return Path.home() / 'AppData' / 'Roaming' / 'Cursor'
|
|
44
|
+
# Linux
|
|
45
|
+
return Path.home() / '.config' / 'Cursor'
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _get_claude_code_hooks_dir() -> Path:
|
|
49
|
+
"""Get Claude Code hooks directory.
|
|
50
|
+
|
|
51
|
+
Claude Code uses ~/.claude on all platforms.
|
|
52
|
+
"""
|
|
53
|
+
return Path.home() / '.claude'
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# IDE-specific configurations
|
|
57
|
+
IDE_CONFIGS: dict[AIIDEType, IDEConfig] = {
|
|
58
|
+
AIIDEType.CURSOR: IDEConfig(
|
|
59
|
+
name='Cursor',
|
|
60
|
+
hooks_dir=_get_cursor_hooks_dir(),
|
|
61
|
+
repo_hooks_subdir='.cursor',
|
|
62
|
+
hooks_file_name='hooks.json',
|
|
63
|
+
hook_events=['beforeSubmitPrompt', 'beforeReadFile', 'beforeMCPExecution'],
|
|
64
|
+
),
|
|
65
|
+
AIIDEType.CLAUDE_CODE: IDEConfig(
|
|
66
|
+
name='Claude Code',
|
|
67
|
+
hooks_dir=_get_claude_code_hooks_dir(),
|
|
68
|
+
repo_hooks_subdir='.claude',
|
|
69
|
+
hooks_file_name='settings.json',
|
|
70
|
+
hook_events=['UserPromptSubmit', 'PreToolUse:Read', 'PreToolUse:mcp'],
|
|
71
|
+
),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Default IDE
|
|
75
|
+
DEFAULT_IDE = AIIDEType.CURSOR
|
|
76
|
+
|
|
77
|
+
# Command used in hooks
|
|
78
|
+
CYCODE_SCAN_PROMPT_COMMAND = 'cycode ai-guardrails scan'
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _get_cursor_hooks_config() -> dict:
|
|
82
|
+
"""Get Cursor-specific hooks configuration."""
|
|
83
|
+
config = IDE_CONFIGS[AIIDEType.CURSOR]
|
|
84
|
+
hooks = {event: [{'command': CYCODE_SCAN_PROMPT_COMMAND}] for event in config.hook_events}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
'version': 1,
|
|
88
|
+
'hooks': hooks,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _get_claude_code_hooks_config() -> dict:
|
|
93
|
+
"""Get Claude Code-specific hooks configuration.
|
|
94
|
+
|
|
95
|
+
Claude Code uses a different hook format with nested structure:
|
|
96
|
+
- hooks are arrays of objects with 'hooks' containing command arrays
|
|
97
|
+
- PreToolUse uses 'matcher' field to specify which tools to intercept
|
|
98
|
+
"""
|
|
99
|
+
command = f'{CYCODE_SCAN_PROMPT_COMMAND} --ide claude-code'
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
'hooks': {
|
|
103
|
+
'UserPromptSubmit': [
|
|
104
|
+
{
|
|
105
|
+
'hooks': [{'type': 'command', 'command': command}],
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
'PreToolUse': [
|
|
109
|
+
{
|
|
110
|
+
'matcher': 'Read',
|
|
111
|
+
'hooks': [{'type': 'command', 'command': command}],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
'matcher': 'mcp__.*',
|
|
115
|
+
'hooks': [{'type': 'command', 'command': command}],
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def get_hooks_config(ide: AIIDEType) -> dict:
|
|
123
|
+
"""Get the hooks configuration for a specific IDE.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
ide: The AI IDE type
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Dict with hooks configuration for the specified IDE
|
|
130
|
+
"""
|
|
131
|
+
if ide == AIIDEType.CLAUDE_CODE:
|
|
132
|
+
return _get_claude_code_hooks_config()
|
|
133
|
+
return _get_cursor_hooks_config()
|
|
@@ -59,9 +59,27 @@ def save_hooks_file(hooks_path: Path, hooks_config: dict) -> bool:
|
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
def is_cycode_hook_entry(entry: dict) -> bool:
|
|
62
|
-
"""Check if a hook entry is from cycode-cli.
|
|
62
|
+
"""Check if a hook entry is from cycode-cli.
|
|
63
|
+
|
|
64
|
+
Handles both Cursor format (flat) and Claude Code format (nested).
|
|
65
|
+
|
|
66
|
+
Cursor format: {"command": "cycode ai-guardrails scan"}
|
|
67
|
+
Claude Code format: {"hooks": [{"type": "command", "command": "cycode ai-guardrails scan --ide claude-code"}]}
|
|
68
|
+
"""
|
|
69
|
+
# Check Cursor format (flat command)
|
|
63
70
|
command = entry.get('command', '')
|
|
64
|
-
|
|
71
|
+
if CYCODE_SCAN_PROMPT_COMMAND in command:
|
|
72
|
+
return True
|
|
73
|
+
|
|
74
|
+
# Check Claude Code format (nested hooks array)
|
|
75
|
+
hooks = entry.get('hooks', [])
|
|
76
|
+
for hook in hooks:
|
|
77
|
+
if isinstance(hook, dict):
|
|
78
|
+
hook_command = hook.get('command', '')
|
|
79
|
+
if CYCODE_SCAN_PROMPT_COMMAND in hook_command:
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
return False
|
|
65
83
|
|
|
66
84
|
|
|
67
85
|
def install_hooks(
|
|
@@ -185,7 +203,15 @@ def get_hooks_status(scope: str = 'user', repo_path: Optional[Path] = None, ide:
|
|
|
185
203
|
ide_config = IDE_CONFIGS[ide]
|
|
186
204
|
has_cycode_hooks = False
|
|
187
205
|
for event in ide_config.hook_events:
|
|
188
|
-
|
|
206
|
+
# Handle event:matcher format
|
|
207
|
+
if ':' in event:
|
|
208
|
+
actual_event, matcher_prefix = event.split(':', 1)
|
|
209
|
+
all_entries = existing.get('hooks', {}).get(actual_event, [])
|
|
210
|
+
# Filter entries by matcher
|
|
211
|
+
entries = [e for e in all_entries if e.get('matcher', '').startswith(matcher_prefix)]
|
|
212
|
+
else:
|
|
213
|
+
entries = existing.get('hooks', {}).get(event, [])
|
|
214
|
+
|
|
189
215
|
cycode_entries = [e for e in entries if is_cycode_hook_entry(e)]
|
|
190
216
|
if cycode_entries:
|
|
191
217
|
has_cycode_hooks = True
|
|
@@ -11,7 +11,7 @@ from cycode.cli.apps.ai_guardrails.command_utils import (
|
|
|
11
11
|
validate_and_parse_ide,
|
|
12
12
|
validate_scope,
|
|
13
13
|
)
|
|
14
|
-
from cycode.cli.apps.ai_guardrails.consts import IDE_CONFIGS
|
|
14
|
+
from cycode.cli.apps.ai_guardrails.consts import IDE_CONFIGS, AIIDEType
|
|
15
15
|
from cycode.cli.apps.ai_guardrails.hooks_manager import install_hooks
|
|
16
16
|
from cycode.cli.utils.sentry import add_breadcrumb
|
|
17
17
|
|
|
@@ -30,9 +30,9 @@ def install_command(
|
|
|
30
30
|
str,
|
|
31
31
|
typer.Option(
|
|
32
32
|
'--ide',
|
|
33
|
-
help='IDE to install hooks for (e.g., "cursor"). Defaults to cursor.',
|
|
33
|
+
help='IDE to install hooks for (e.g., "cursor", "claude-code", or "all" for all IDEs). Defaults to cursor.',
|
|
34
34
|
),
|
|
35
|
-
] =
|
|
35
|
+
] = AIIDEType.CURSOR,
|
|
36
36
|
repo_path: Annotated[
|
|
37
37
|
Optional[Path],
|
|
38
38
|
typer.Option(
|
|
@@ -54,6 +54,7 @@ def install_command(
|
|
|
54
54
|
cycode ai-guardrails install # Install for all projects (user scope)
|
|
55
55
|
cycode ai-guardrails install --scope repo # Install for current repo only
|
|
56
56
|
cycode ai-guardrails install --ide cursor # Install for Cursor IDE
|
|
57
|
+
cycode ai-guardrails install --ide all # Install for all supported IDEs
|
|
57
58
|
cycode ai-guardrails install --scope repo --repo-path /path/to/repo
|
|
58
59
|
"""
|
|
59
60
|
add_breadcrumb('ai-guardrails-install')
|
|
@@ -62,17 +63,35 @@ def install_command(
|
|
|
62
63
|
validate_scope(scope)
|
|
63
64
|
repo_path = resolve_repo_path(scope, repo_path)
|
|
64
65
|
ide_type = validate_and_parse_ide(ide)
|
|
65
|
-
ide_name = IDE_CONFIGS[ide_type].name
|
|
66
|
-
success, message = install_hooks(scope, repo_path, ide=ide_type)
|
|
67
66
|
|
|
68
|
-
if
|
|
69
|
-
|
|
67
|
+
ides_to_install: list[AIIDEType] = list(AIIDEType) if ide_type is None else [ide_type]
|
|
68
|
+
|
|
69
|
+
results: list[tuple[str, bool, str]] = []
|
|
70
|
+
for current_ide in ides_to_install:
|
|
71
|
+
ide_name = IDE_CONFIGS[current_ide].name
|
|
72
|
+
success, message = install_hooks(scope, repo_path, ide=current_ide)
|
|
73
|
+
results.append((ide_name, success, message))
|
|
74
|
+
|
|
75
|
+
# Report results for each IDE
|
|
76
|
+
any_success = False
|
|
77
|
+
all_success = True
|
|
78
|
+
for _ide_name, success, message in results:
|
|
79
|
+
if success:
|
|
80
|
+
console.print(f'[green]✓[/] {message}')
|
|
81
|
+
any_success = True
|
|
82
|
+
else:
|
|
83
|
+
console.print(f'[red]✗[/] {message}', style='bold red')
|
|
84
|
+
all_success = False
|
|
85
|
+
|
|
86
|
+
if any_success:
|
|
70
87
|
console.print()
|
|
71
88
|
console.print('[bold]Next steps:[/]')
|
|
72
|
-
|
|
89
|
+
successful_ides = [name for name, success, _ in results if success]
|
|
90
|
+
ide_list = ', '.join(successful_ides)
|
|
91
|
+
console.print(f'1. Restart {ide_list} to activate the hooks')
|
|
73
92
|
console.print('2. (Optional) Customize policy in ~/.cycode/ai-guardrails.yaml')
|
|
74
93
|
console.print()
|
|
75
94
|
console.print('[dim]The hooks will scan prompts, file reads, and MCP tool calls for secrets.[/]')
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
|
|
96
|
+
if not all_success:
|
|
78
97
|
raise typer.Exit(1)
|
|
@@ -13,6 +13,7 @@ from typing import Callable, Optional
|
|
|
13
13
|
|
|
14
14
|
import typer
|
|
15
15
|
|
|
16
|
+
from cycode.cli.apps.ai_guardrails.consts import PolicyMode
|
|
16
17
|
from cycode.cli.apps.ai_guardrails.scan.payload import AIHookPayload
|
|
17
18
|
from cycode.cli.apps.ai_guardrails.scan.policy import get_policy_value
|
|
18
19
|
from cycode.cli.apps.ai_guardrails.scan.response_builders import get_response_builder
|
|
@@ -46,7 +47,7 @@ def handle_before_submit_prompt(ctx: typer.Context, payload: AIHookPayload, poli
|
|
|
46
47
|
ai_client.create_event(payload, AiHookEventType.PROMPT, AIHookOutcome.ALLOWED)
|
|
47
48
|
return response_builder.allow_prompt()
|
|
48
49
|
|
|
49
|
-
mode = get_policy_value(policy, 'mode', default=
|
|
50
|
+
mode = get_policy_value(policy, 'mode', default=PolicyMode.BLOCK)
|
|
50
51
|
prompt = payload.prompt or ''
|
|
51
52
|
max_bytes = get_policy_value(policy, 'secrets', 'max_bytes', default=200000)
|
|
52
53
|
timeout_ms = get_policy_value(policy, 'secrets', 'timeout_ms', default=30000)
|
|
@@ -55,29 +56,26 @@ def handle_before_submit_prompt(ctx: typer.Context, payload: AIHookPayload, poli
|
|
|
55
56
|
scan_id = None
|
|
56
57
|
block_reason = None
|
|
57
58
|
outcome = AIHookOutcome.ALLOWED
|
|
59
|
+
error_message = None
|
|
58
60
|
|
|
59
61
|
try:
|
|
60
62
|
violation_summary, scan_id = _scan_text_for_secrets(ctx, clipped, timeout_ms)
|
|
61
63
|
|
|
62
|
-
if
|
|
63
|
-
violation_summary
|
|
64
|
-
and get_policy_value(prompt_config, 'action', default='block') == 'block'
|
|
65
|
-
and mode == 'block'
|
|
66
|
-
):
|
|
67
|
-
outcome = AIHookOutcome.BLOCKED
|
|
64
|
+
if violation_summary:
|
|
68
65
|
block_reason = BlockReason.SECRETS_IN_PROMPT
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return
|
|
66
|
+
action = get_policy_value(prompt_config, 'action', default=PolicyMode.BLOCK)
|
|
67
|
+
if action == PolicyMode.BLOCK and mode == PolicyMode.BLOCK:
|
|
68
|
+
outcome = AIHookOutcome.BLOCKED
|
|
69
|
+
user_message = f'{violation_summary}. Remove secrets before sending.'
|
|
70
|
+
return response_builder.deny_prompt(user_message)
|
|
71
|
+
outcome = AIHookOutcome.WARNED
|
|
72
|
+
return response_builder.allow_prompt()
|
|
76
73
|
except Exception as e:
|
|
77
74
|
outcome = (
|
|
78
75
|
AIHookOutcome.ALLOWED if get_policy_value(policy, 'fail_open', default=True) else AIHookOutcome.BLOCKED
|
|
79
76
|
)
|
|
80
|
-
block_reason = BlockReason.SCAN_FAILURE
|
|
77
|
+
block_reason = BlockReason.SCAN_FAILURE
|
|
78
|
+
error_message = str(e)
|
|
81
79
|
raise e
|
|
82
80
|
finally:
|
|
83
81
|
ai_client.create_event(
|
|
@@ -86,6 +84,7 @@ def handle_before_submit_prompt(ctx: typer.Context, payload: AIHookPayload, poli
|
|
|
86
84
|
outcome,
|
|
87
85
|
scan_id=scan_id,
|
|
88
86
|
block_reason=block_reason,
|
|
87
|
+
error_message=error_message,
|
|
89
88
|
)
|
|
90
89
|
|
|
91
90
|
|
|
@@ -106,38 +105,53 @@ def handle_before_read_file(ctx: typer.Context, payload: AIHookPayload, policy:
|
|
|
106
105
|
ai_client.create_event(payload, AiHookEventType.FILE_READ, AIHookOutcome.ALLOWED)
|
|
107
106
|
return response_builder.allow_permission()
|
|
108
107
|
|
|
109
|
-
mode = get_policy_value(policy, 'mode', default=
|
|
108
|
+
mode = get_policy_value(policy, 'mode', default=PolicyMode.BLOCK)
|
|
110
109
|
file_path = payload.file_path or ''
|
|
111
|
-
action = get_policy_value(file_read_config, 'action', default=
|
|
110
|
+
action = get_policy_value(file_read_config, 'action', default=PolicyMode.BLOCK)
|
|
112
111
|
|
|
113
112
|
scan_id = None
|
|
114
113
|
block_reason = None
|
|
115
114
|
outcome = AIHookOutcome.ALLOWED
|
|
115
|
+
error_message = None
|
|
116
116
|
|
|
117
117
|
try:
|
|
118
118
|
# Check path-based denylist first
|
|
119
|
-
if is_denied_path(file_path, policy)
|
|
120
|
-
outcome = AIHookOutcome.BLOCKED
|
|
119
|
+
if is_denied_path(file_path, policy):
|
|
121
120
|
block_reason = BlockReason.SENSITIVE_PATH
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
if mode == PolicyMode.BLOCK and action == PolicyMode.BLOCK:
|
|
122
|
+
outcome = AIHookOutcome.BLOCKED
|
|
123
|
+
user_message = f'Cycode blocked sending {file_path} to the AI (sensitive path policy).'
|
|
124
|
+
return response_builder.deny_permission(
|
|
125
|
+
user_message,
|
|
126
|
+
'This file path is classified as sensitive; do not read/send it to the model.',
|
|
127
|
+
)
|
|
128
|
+
# Warn mode - ask user for permission
|
|
129
|
+
outcome = AIHookOutcome.WARNED
|
|
130
|
+
user_message = f'Cycode flagged {file_path} as sensitive. Allow reading?'
|
|
131
|
+
return response_builder.ask_permission(
|
|
124
132
|
user_message,
|
|
125
|
-
'This file path is classified as sensitive;
|
|
133
|
+
'This file path is classified as sensitive; proceed with caution.',
|
|
126
134
|
)
|
|
127
135
|
|
|
128
136
|
# Scan file content if enabled
|
|
129
137
|
if get_policy_value(file_read_config, 'scan_content', default=True):
|
|
130
138
|
violation_summary, scan_id = _scan_path_for_secrets(ctx, file_path, policy)
|
|
131
|
-
if violation_summary
|
|
132
|
-
outcome = AIHookOutcome.BLOCKED
|
|
139
|
+
if violation_summary:
|
|
133
140
|
block_reason = BlockReason.SECRETS_IN_FILE
|
|
134
|
-
|
|
135
|
-
|
|
141
|
+
if mode == PolicyMode.BLOCK and action == PolicyMode.BLOCK:
|
|
142
|
+
outcome = AIHookOutcome.BLOCKED
|
|
143
|
+
user_message = f'Cycode blocked reading {file_path}. {violation_summary}'
|
|
144
|
+
return response_builder.deny_permission(
|
|
145
|
+
user_message,
|
|
146
|
+
'Secrets detected; do not send this file to the model.',
|
|
147
|
+
)
|
|
148
|
+
# Warn mode - ask user for permission
|
|
149
|
+
outcome = AIHookOutcome.WARNED
|
|
150
|
+
user_message = f'Cycode detected secrets in {file_path}. {violation_summary}'
|
|
151
|
+
return response_builder.ask_permission(
|
|
136
152
|
user_message,
|
|
137
|
-
'
|
|
153
|
+
'Possible secrets detected; proceed with caution.',
|
|
138
154
|
)
|
|
139
|
-
if violation_summary:
|
|
140
|
-
outcome = AIHookOutcome.WARNED
|
|
141
155
|
return response_builder.allow_permission()
|
|
142
156
|
|
|
143
157
|
return response_builder.allow_permission()
|
|
@@ -145,7 +159,8 @@ def handle_before_read_file(ctx: typer.Context, payload: AIHookPayload, policy:
|
|
|
145
159
|
outcome = (
|
|
146
160
|
AIHookOutcome.ALLOWED if get_policy_value(policy, 'fail_open', default=True) else AIHookOutcome.BLOCKED
|
|
147
161
|
)
|
|
148
|
-
block_reason = BlockReason.SCAN_FAILURE
|
|
162
|
+
block_reason = BlockReason.SCAN_FAILURE
|
|
163
|
+
error_message = str(e)
|
|
149
164
|
raise e
|
|
150
165
|
finally:
|
|
151
166
|
ai_client.create_event(
|
|
@@ -154,6 +169,7 @@ def handle_before_read_file(ctx: typer.Context, payload: AIHookPayload, policy:
|
|
|
154
169
|
outcome,
|
|
155
170
|
scan_id=scan_id,
|
|
156
171
|
block_reason=block_reason,
|
|
172
|
+
error_message=error_message,
|
|
157
173
|
)
|
|
158
174
|
|
|
159
175
|
|
|
@@ -175,26 +191,27 @@ def handle_before_mcp_execution(ctx: typer.Context, payload: AIHookPayload, poli
|
|
|
175
191
|
ai_client.create_event(payload, AiHookEventType.MCP_EXECUTION, AIHookOutcome.ALLOWED)
|
|
176
192
|
return response_builder.allow_permission()
|
|
177
193
|
|
|
178
|
-
mode = get_policy_value(policy, 'mode', default=
|
|
194
|
+
mode = get_policy_value(policy, 'mode', default=PolicyMode.BLOCK)
|
|
179
195
|
tool = payload.mcp_tool_name or 'unknown'
|
|
180
196
|
args = payload.mcp_arguments or {}
|
|
181
197
|
args_text = args if isinstance(args, str) else json.dumps(args)
|
|
182
198
|
max_bytes = get_policy_value(policy, 'secrets', 'max_bytes', default=200000)
|
|
183
199
|
timeout_ms = get_policy_value(policy, 'secrets', 'timeout_ms', default=30000)
|
|
184
200
|
clipped = truncate_utf8(args_text, max_bytes)
|
|
185
|
-
action = get_policy_value(mcp_config, 'action', default=
|
|
201
|
+
action = get_policy_value(mcp_config, 'action', default=PolicyMode.BLOCK)
|
|
186
202
|
|
|
187
203
|
scan_id = None
|
|
188
204
|
block_reason = None
|
|
189
205
|
outcome = AIHookOutcome.ALLOWED
|
|
206
|
+
error_message = None
|
|
190
207
|
|
|
191
208
|
try:
|
|
192
209
|
if get_policy_value(mcp_config, 'scan_arguments', default=True):
|
|
193
210
|
violation_summary, scan_id = _scan_text_for_secrets(ctx, clipped, timeout_ms)
|
|
194
211
|
if violation_summary:
|
|
195
|
-
|
|
212
|
+
block_reason = BlockReason.SECRETS_IN_MCP_ARGS
|
|
213
|
+
if mode == PolicyMode.BLOCK and action == PolicyMode.BLOCK:
|
|
196
214
|
outcome = AIHookOutcome.BLOCKED
|
|
197
|
-
block_reason = BlockReason.SECRETS_IN_MCP_ARGS
|
|
198
215
|
user_message = f'Cycode blocked MCP tool call "{tool}". {violation_summary}'
|
|
199
216
|
return response_builder.deny_permission(
|
|
200
217
|
user_message,
|
|
@@ -211,7 +228,8 @@ def handle_before_mcp_execution(ctx: typer.Context, payload: AIHookPayload, poli
|
|
|
211
228
|
outcome = (
|
|
212
229
|
AIHookOutcome.ALLOWED if get_policy_value(policy, 'fail_open', default=True) else AIHookOutcome.BLOCKED
|
|
213
230
|
)
|
|
214
|
-
block_reason = BlockReason.SCAN_FAILURE
|
|
231
|
+
block_reason = BlockReason.SCAN_FAILURE
|
|
232
|
+
error_message = str(e)
|
|
215
233
|
raise e
|
|
216
234
|
finally:
|
|
217
235
|
ai_client.create_event(
|
|
@@ -220,6 +238,7 @@ def handle_before_mcp_execution(ctx: typer.Context, payload: AIHookPayload, poli
|
|
|
220
238
|
outcome,
|
|
221
239
|
scan_id=scan_id,
|
|
222
240
|
block_reason=block_reason,
|
|
241
|
+
error_message=error_message,
|
|
223
242
|
)
|
|
224
243
|
|
|
225
244
|
|