cycode 3.18.1.dev1__tar.gz → 3.18.1.dev3__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.18.1.dev1 → cycode-3.18.1.dev3}/PKG-INFO +32 -2
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/README.md +31 -1
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/__init__.py +1 -1
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/consts.py +7 -2
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/ides/copilot.py +1 -3
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/install_command.py +7 -7
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/scan/handlers.py +88 -25
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/scan/scan_command.py +4 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/scan/types.py +9 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/host_info.py +52 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/pyproject.toml +1 -1
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/LICENCE +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/__main__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/app.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/activation_manager.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/command_utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/hooks_manager.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/ides/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/ides/_plugin_utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/ides/base.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/ides/claude_code.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/ides/codex.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/ides/cursor.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/scan/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/scan/consts.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/scan/payload.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/scan/policy.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/scan/utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/session_start_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/status_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/uninstall_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_remediation/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_remediation/ai_remediation_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_remediation/apply_fix.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_remediation/print_remediation.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/api/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/api/api_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/api/openapi_spec.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/auth/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/auth/auth_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/auth/auth_common.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/auth/auth_manager.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/auth/models.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/configure/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/configure/configure_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/configure/consts.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/configure/messages.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/configure/prompts.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ignore/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ignore/ignore_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/mcp/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/mcp/mcp_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report/report_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report/sbom/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report/sbom/common.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report/sbom/path/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report/sbom/path/path_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report/sbom/repository_url/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report/sbom/repository_url/repository_url_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report/sbom/sbom_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report/sbom/sbom_report_file.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report_import/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report_import/report_import_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report_import/sbom/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/report_import/sbom/sbom_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/sca_options.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/aggregation_report.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/code_scanner.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/commit_history/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/commit_history/commit_history_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/commit_range_scanner.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/detection_excluder.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/path/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/path/path_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/pre_commit/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/pre_commit/pre_commit_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/pre_push/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/pre_push/pre_push_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/pre_receive/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/pre_receive/pre_receive_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/remote_url_resolver.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/repository/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/repository/repository_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/scan_ci/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/scan_ci/ci_integrations.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/scan_ci/scan_ci_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/scan_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/scan_parameters.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/scan/scan_result.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/status/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/status/get_cli_status.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/status/models.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/status/status_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/status/version_command.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/cli_types.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/config.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/console.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/consts.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/exceptions/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/exceptions/custom_exceptions.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/exceptions/handle_ai_remediation_errors.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/exceptions/handle_auth_errors.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/exceptions/handle_errors.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/exceptions/handle_report_sbom_errors.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/exceptions/handle_scan_errors.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/commit_range_documents.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/documents_walk_ignore.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/file_excluder.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/iac/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/models/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/path_documents.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/repository_documents.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/base_restore_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/go/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/maven/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/npm/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/npm/restore_bun_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/nuget/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/php/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/python/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/python/restore_pip_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/python/restore_uv_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/ruby/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/ruby/restore_ruby_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/sbt/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/sca/sca_file_collector.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/walk_ignore.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/files_collector/zip_documents.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/logger.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/main.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/models.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/console_printer.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/json_printer.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/printer_base.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/rich_printer.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/tables/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/tables/sca_table_printer.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/tables/table.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/tables/table_models.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/tables/table_printer.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/tables/table_printer_base.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/text_printer.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/utils/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/utils/code_snippet_syntax.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/utils/detection_data.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/utils/detection_ordering/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/utils/detection_ordering/common_ordering.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/utils/detection_ordering/sca_ordering.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/printers/utils/rich_helpers.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/user_settings/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/user_settings/base_file_manager.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/user_settings/config_file_manager.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/user_settings/configuration_manager.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/user_settings/credentials_manager.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/user_settings/jwt_creator.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/binary_utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/enum_utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/get_api_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/git_proxy.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/ignore_utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/jwt_utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/path_utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/progress_bar.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/scan_batch.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/scan_utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/shell_executor.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/string_utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/task_timer.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/url_utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/version_checker.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/utils/yaml_utils.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/config.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/__init__.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/ai_security_manager_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/ai_security_manager_service_config.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/auth_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/base_token_auth_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/cli_activation_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/client_creator.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/config.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/config_dev.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/cycode_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/cycode_client_base.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/cycode_dev_based_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/cycode_oidc_based_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/cycode_token_based_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/headers.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/import_sbom_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/logger.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/models.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/report_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/scan_client.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cyclient/scan_config_base.py +0 -0
- {cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/logger.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cycode
|
|
3
|
-
Version: 3.18.1.
|
|
3
|
+
Version: 3.18.1.dev3
|
|
4
4
|
Summary: Boost security in your dev lifecycle via SAST, SCA, Secrets & IaC scanning.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENCE
|
|
@@ -44,6 +44,8 @@ Description-Content-Type: text/markdown
|
|
|
44
44
|
|
|
45
45
|
# Cycode CLI User Guide
|
|
46
46
|
|
|
47
|
+
[](https://mcptoplist.com/server/glama%2Fcycodehq%2Fcycode-cli)
|
|
48
|
+
|
|
47
49
|
The Cycode Command Line Interface (CLI) is an application you can install locally to scan your repositories for secrets, infrastructure as code misconfigurations, software composition analysis vulnerabilities, and static application security testing issues.
|
|
48
50
|
|
|
49
51
|
This guide walks you through both installation and usage.
|
|
@@ -70,7 +72,9 @@ This guide walks you through both installation and usage.
|
|
|
70
72
|
1. [Discovering Commands](#discovering-commands)
|
|
71
73
|
2. [Examples](#platform-examples)
|
|
72
74
|
3. [Notes & Limitations](#platform-notes--limitations)
|
|
73
|
-
6. [
|
|
75
|
+
6. [AI Guardrails](#ai-guardrails-beta)
|
|
76
|
+
1. [Data Collected by AI Guardrails](#data-collected-by-ai-guardrails)
|
|
77
|
+
7. [Scan Command](#scan-command)
|
|
74
78
|
1. [Running a Scan](#running-a-scan)
|
|
75
79
|
1. [Options](#options)
|
|
76
80
|
1. [Severity Threshold](#severity-option)
|
|
@@ -746,6 +750,32 @@ cycode platform projects list --page-size 100 | jq '.items[].name'
|
|
|
746
750
|
- **Override the cache TTL** with `CYCODE_SPEC_CACHE_TTL=<seconds>`.
|
|
747
751
|
|
|
748
752
|
|
|
753
|
+
# AI Guardrails \[BETA\]
|
|
754
|
+
|
|
755
|
+
AI Guardrails installs hooks into supported AI coding agents (Claude Code, Cursor, Copilot, Codex) so that
|
|
756
|
+
prompts, files the agent reads, and MCP tool arguments are scanned for secrets before they reach the model.
|
|
757
|
+
|
|
758
|
+
## Data Collected by AI Guardrails
|
|
759
|
+
|
|
760
|
+
Scanning happens server-side, so the scanned content leaves the machine: the prompt text, the contents of
|
|
761
|
+
files the agent reads, and MCP tool arguments are sent to your Cycode tenant to be checked for secrets.
|
|
762
|
+
|
|
763
|
+
Each event is also reported with context about the developer and the machine, so a finding can be attributed
|
|
764
|
+
to the device and user it came from. Some of this is personal data:
|
|
765
|
+
|
|
766
|
+
- **Device identifiers** — the machine's hostname and hardware serial number.
|
|
767
|
+
- **User identifiers** — the email address of the user signed in to the AI coding agent, and the local
|
|
768
|
+
operating-system username.
|
|
769
|
+
- **Environment details** — operating system and version, the AI agent, its version and the model in use,
|
|
770
|
+
the contents of the agent's MCP configuration files, and its enabled plugins.
|
|
771
|
+
|
|
772
|
+
The hardware serial number is cached in a local temporary file, readable only by the user who ran the
|
|
773
|
+
command, so repeated hook invocations don't re-query the hardware.
|
|
774
|
+
|
|
775
|
+
If collecting this data is not acceptable in your environment, do not install the guardrails hooks
|
|
776
|
+
(`cycode ai-guardrails uninstall` removes hooks that are already installed).
|
|
777
|
+
|
|
778
|
+
|
|
749
779
|
# Scan Command
|
|
750
780
|
|
|
751
781
|
## Running a Scan
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Cycode CLI User Guide
|
|
2
2
|
|
|
3
|
+
[](https://mcptoplist.com/server/glama%2Fcycodehq%2Fcycode-cli)
|
|
4
|
+
|
|
3
5
|
The Cycode Command Line Interface (CLI) is an application you can install locally to scan your repositories for secrets, infrastructure as code misconfigurations, software composition analysis vulnerabilities, and static application security testing issues.
|
|
4
6
|
|
|
5
7
|
This guide walks you through both installation and usage.
|
|
@@ -26,7 +28,9 @@ This guide walks you through both installation and usage.
|
|
|
26
28
|
1. [Discovering Commands](#discovering-commands)
|
|
27
29
|
2. [Examples](#platform-examples)
|
|
28
30
|
3. [Notes & Limitations](#platform-notes--limitations)
|
|
29
|
-
6. [
|
|
31
|
+
6. [AI Guardrails](#ai-guardrails-beta)
|
|
32
|
+
1. [Data Collected by AI Guardrails](#data-collected-by-ai-guardrails)
|
|
33
|
+
7. [Scan Command](#scan-command)
|
|
30
34
|
1. [Running a Scan](#running-a-scan)
|
|
31
35
|
1. [Options](#options)
|
|
32
36
|
1. [Severity Threshold](#severity-option)
|
|
@@ -702,6 +706,32 @@ cycode platform projects list --page-size 100 | jq '.items[].name'
|
|
|
702
706
|
- **Override the cache TTL** with `CYCODE_SPEC_CACHE_TTL=<seconds>`.
|
|
703
707
|
|
|
704
708
|
|
|
709
|
+
# AI Guardrails \[BETA\]
|
|
710
|
+
|
|
711
|
+
AI Guardrails installs hooks into supported AI coding agents (Claude Code, Cursor, Copilot, Codex) so that
|
|
712
|
+
prompts, files the agent reads, and MCP tool arguments are scanned for secrets before they reach the model.
|
|
713
|
+
|
|
714
|
+
## Data Collected by AI Guardrails
|
|
715
|
+
|
|
716
|
+
Scanning happens server-side, so the scanned content leaves the machine: the prompt text, the contents of
|
|
717
|
+
files the agent reads, and MCP tool arguments are sent to your Cycode tenant to be checked for secrets.
|
|
718
|
+
|
|
719
|
+
Each event is also reported with context about the developer and the machine, so a finding can be attributed
|
|
720
|
+
to the device and user it came from. Some of this is personal data:
|
|
721
|
+
|
|
722
|
+
- **Device identifiers** — the machine's hostname and hardware serial number.
|
|
723
|
+
- **User identifiers** — the email address of the user signed in to the AI coding agent, and the local
|
|
724
|
+
operating-system username.
|
|
725
|
+
- **Environment details** — operating system and version, the AI agent, its version and the model in use,
|
|
726
|
+
the contents of the agent's MCP configuration files, and its enabled plugins.
|
|
727
|
+
|
|
728
|
+
The hardware serial number is cached in a local temporary file, readable only by the user who ran the
|
|
729
|
+
command, so repeated hook invocations don't re-query the hardware.
|
|
730
|
+
|
|
731
|
+
If collecting this data is not acceptable in your environment, do not install the guardrails hooks
|
|
732
|
+
(`cycode ai-guardrails uninstall` removes hooks that are already installed).
|
|
733
|
+
|
|
734
|
+
|
|
705
735
|
# Scan Command
|
|
706
736
|
|
|
707
737
|
## Running a Scan
|
|
@@ -5,4 +5,4 @@ import time as _time
|
|
|
5
5
|
# end-to-end scan duration from the moment the user actually triggered it.
|
|
6
6
|
_BOOT_WALL: float = _time.time()
|
|
7
7
|
|
|
8
|
-
__version__ = '3.18.1.
|
|
8
|
+
__version__ = '3.18.1.dev3' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
|
|
@@ -10,8 +10,13 @@ class PolicyMode(str, Enum):
|
|
|
10
10
|
WARN = 'warn'
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class
|
|
14
|
-
"""
|
|
13
|
+
class GuardrailsMode(str, Enum):
|
|
14
|
+
"""Guardrails enforcement mode.
|
|
15
|
+
|
|
16
|
+
Used both as the ai-guardrails install-command mode and as the per-event
|
|
17
|
+
effective mode reported to the server (the ai_guardrails scan parameter's
|
|
18
|
+
`mode` field)
|
|
19
|
+
"""
|
|
15
20
|
|
|
16
21
|
REPORT = 'report'
|
|
17
22
|
BLOCK = 'block'
|
|
@@ -333,9 +333,7 @@ class Copilot(IDE):
|
|
|
333
333
|
return {
|
|
334
334
|
'version': 1,
|
|
335
335
|
'hooks': {
|
|
336
|
-
'sessionStart': [
|
|
337
|
-
{'type': 'command', 'command': _SESSION_START_COMMAND, 'timeoutSec': _HOOK_TIMEOUT_SEC}
|
|
338
|
-
],
|
|
336
|
+
'sessionStart': [{'type': 'command', 'command': _SESSION_START_COMMAND}],
|
|
339
337
|
'userPromptSubmitted': [entry(_SCAN_PROMPT_COMMAND)],
|
|
340
338
|
'preToolUse': [entry(_SCAN_TOOL_COMMAND)],
|
|
341
339
|
},
|
|
@@ -6,7 +6,7 @@ from typing import Annotated, Optional
|
|
|
6
6
|
import typer
|
|
7
7
|
|
|
8
8
|
from cycode.cli.apps.ai_guardrails.command_utils import console, resolve_repo_path, validate_scope
|
|
9
|
-
from cycode.cli.apps.ai_guardrails.consts import
|
|
9
|
+
from cycode.cli.apps.ai_guardrails.consts import GuardrailsMode, PolicyMode
|
|
10
10
|
from cycode.cli.apps.ai_guardrails.hooks_manager import create_policy_file, install_hooks
|
|
11
11
|
from cycode.cli.apps.ai_guardrails.ides import DEFAULT_IDE_NAME, IDES, resolve_ides
|
|
12
12
|
|
|
@@ -40,14 +40,14 @@ def install_command(
|
|
|
40
40
|
),
|
|
41
41
|
] = None,
|
|
42
42
|
mode: Annotated[
|
|
43
|
-
|
|
43
|
+
GuardrailsMode,
|
|
44
44
|
typer.Option(
|
|
45
45
|
'--mode',
|
|
46
46
|
'-m',
|
|
47
47
|
help='Installation mode: "report" for async non-blocking hooks with warn policy, '
|
|
48
48
|
'"block" for sync blocking hooks.',
|
|
49
49
|
),
|
|
50
|
-
] =
|
|
50
|
+
] = GuardrailsMode.REPORT,
|
|
51
51
|
) -> None:
|
|
52
52
|
"""Install AI guardrails hooks for supported IDEs.
|
|
53
53
|
|
|
@@ -65,7 +65,7 @@ def install_command(
|
|
|
65
65
|
repo_path = resolve_repo_path(scope, repo_path)
|
|
66
66
|
ides_to_install = resolve_ides(ide)
|
|
67
67
|
|
|
68
|
-
report_mode = mode ==
|
|
68
|
+
report_mode = mode == GuardrailsMode.REPORT
|
|
69
69
|
|
|
70
70
|
results: list[tuple[str, bool, str]] = []
|
|
71
71
|
for current_ide in ides_to_install:
|
|
@@ -83,7 +83,7 @@ def install_command(
|
|
|
83
83
|
all_success = False
|
|
84
84
|
|
|
85
85
|
if any_success:
|
|
86
|
-
policy_mode = PolicyMode.WARN if mode ==
|
|
86
|
+
policy_mode = PolicyMode.WARN if mode == GuardrailsMode.REPORT else PolicyMode.BLOCK
|
|
87
87
|
_install_policy(scope, repo_path, policy_mode)
|
|
88
88
|
_print_next_steps(results, mode)
|
|
89
89
|
|
|
@@ -99,7 +99,7 @@ def _install_policy(scope: str, repo_path: Optional[Path], policy_mode: PolicyMo
|
|
|
99
99
|
console.print(f'[red]✗[/] {policy_message}', style='bold red')
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
def _print_next_steps(results: list[tuple[str, bool, str]], mode:
|
|
102
|
+
def _print_next_steps(results: list[tuple[str, bool, str]], mode: GuardrailsMode) -> None:
|
|
103
103
|
console.print()
|
|
104
104
|
console.print('[bold]Next steps:[/]')
|
|
105
105
|
successful_ides = [name for name, success, _ in results if success]
|
|
@@ -107,7 +107,7 @@ def _print_next_steps(results: list[tuple[str, bool, str]], mode: InstallMode) -
|
|
|
107
107
|
console.print(f'1. Restart {ide_list} to activate the hooks')
|
|
108
108
|
console.print('2. (Optional) Customize policy in ~/.cycode/ai-guardrails.yaml')
|
|
109
109
|
console.print()
|
|
110
|
-
if mode ==
|
|
110
|
+
if mode == GuardrailsMode.REPORT:
|
|
111
111
|
console.print('[dim]Report mode: hooks run async (non-blocking) and policy is set to warn.[/]')
|
|
112
112
|
else:
|
|
113
113
|
console.print('[dim]The hooks will scan prompts, file reads, and MCP tool calls for secrets.[/]')
|
|
@@ -17,24 +17,29 @@ from typing import Callable, Optional
|
|
|
17
17
|
|
|
18
18
|
import typer
|
|
19
19
|
|
|
20
|
-
from cycode.cli.apps.ai_guardrails.consts import PolicyMode
|
|
20
|
+
from cycode.cli.apps.ai_guardrails.consts import GuardrailsMode, PolicyMode
|
|
21
21
|
from cycode.cli.apps.ai_guardrails.ides.base import HookDecision
|
|
22
22
|
from cycode.cli.apps.ai_guardrails.scan.payload import AIHookPayload
|
|
23
23
|
from cycode.cli.apps.ai_guardrails.scan.policy import get_policy_value
|
|
24
|
-
from cycode.cli.apps.ai_guardrails.scan.types import
|
|
24
|
+
from cycode.cli.apps.ai_guardrails.scan.types import (
|
|
25
|
+
SECRETS_BLOCK_REASON_BY_EVENT_TYPE,
|
|
26
|
+
AiHookEventType,
|
|
27
|
+
AIHookOutcome,
|
|
28
|
+
BlockReason,
|
|
29
|
+
)
|
|
25
30
|
from cycode.cli.apps.ai_guardrails.scan.utils import is_denied_path, truncate_utf8
|
|
26
31
|
from cycode.cli.apps.scan.code_scanner import _get_scan_documents_thread_func
|
|
27
32
|
from cycode.cli.apps.scan.scan_parameters import get_scan_parameters
|
|
28
33
|
from cycode.cli.cli_types import ScanTypeOption, SeverityOption
|
|
29
34
|
from cycode.cli.files_collector.file_excluder import is_path_configured_in_exclusions
|
|
30
35
|
from cycode.cli.models import Document
|
|
36
|
+
from cycode.cli.utils.host_info import get_hostname, get_serial_number
|
|
31
37
|
from cycode.cli.utils.progress_bar import DummyProgressBar, ScanProgressBarSection
|
|
32
38
|
from cycode.cli.utils.scan_utils import build_violation_summary
|
|
33
39
|
from cycode.logger import get_logger
|
|
34
40
|
|
|
35
41
|
logger = get_logger('AI Guardrails')
|
|
36
42
|
|
|
37
|
-
|
|
38
43
|
HandlerFn = Callable[[typer.Context, AIHookPayload, dict], HookDecision]
|
|
39
44
|
|
|
40
45
|
|
|
@@ -47,7 +52,7 @@ def handle_before_submit_prompt(ctx: typer.Context, payload: AIHookPayload, poli
|
|
|
47
52
|
ai_client.create_event(payload, AiHookEventType.PROMPT, AIHookOutcome.ALLOWED)
|
|
48
53
|
return HookDecision.allow(AiHookEventType.PROMPT)
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
effective_mode = get_effective_mode(policy, prompt_config)
|
|
51
56
|
prompt = payload.prompt or ''
|
|
52
57
|
max_bytes = get_policy_value(policy, 'secrets', 'max_bytes', default=200000)
|
|
53
58
|
timeout_ms = get_policy_value(policy, 'secrets', 'timeout_ms', default=30000)
|
|
@@ -59,12 +64,18 @@ def handle_before_submit_prompt(ctx: typer.Context, payload: AIHookPayload, poli
|
|
|
59
64
|
error_message = None
|
|
60
65
|
|
|
61
66
|
try:
|
|
62
|
-
violation_summary, scan_id = _scan_text_for_secrets(
|
|
67
|
+
violation_summary, scan_id = _scan_text_for_secrets(
|
|
68
|
+
ctx,
|
|
69
|
+
clipped,
|
|
70
|
+
timeout_ms,
|
|
71
|
+
payload=payload,
|
|
72
|
+
event_type=AiHookEventType.PROMPT,
|
|
73
|
+
effective_mode=effective_mode,
|
|
74
|
+
)
|
|
63
75
|
|
|
64
76
|
if violation_summary:
|
|
65
|
-
block_reason =
|
|
66
|
-
|
|
67
|
-
if action == PolicyMode.BLOCK and mode == PolicyMode.BLOCK:
|
|
77
|
+
block_reason = SECRETS_BLOCK_REASON_BY_EVENT_TYPE[AiHookEventType.PROMPT]
|
|
78
|
+
if effective_mode == GuardrailsMode.BLOCK:
|
|
68
79
|
outcome = AIHookOutcome.BLOCKED
|
|
69
80
|
user_message = f'{violation_summary}. Remove secrets before sending.'
|
|
70
81
|
return HookDecision.deny(AiHookEventType.PROMPT, user_message)
|
|
@@ -97,9 +108,8 @@ def handle_before_read_file(ctx: typer.Context, payload: AIHookPayload, policy:
|
|
|
97
108
|
ai_client.create_event(payload, AiHookEventType.FILE_READ, AIHookOutcome.ALLOWED)
|
|
98
109
|
return HookDecision.allow(AiHookEventType.FILE_READ)
|
|
99
110
|
|
|
100
|
-
mode = get_policy_value(policy, 'mode', default=PolicyMode.BLOCK)
|
|
101
111
|
file_path = payload.file_path or ''
|
|
102
|
-
|
|
112
|
+
effective_mode = get_effective_mode(policy, file_read_config)
|
|
103
113
|
|
|
104
114
|
scan_id = None
|
|
105
115
|
block_reason = None
|
|
@@ -110,7 +120,7 @@ def handle_before_read_file(ctx: typer.Context, payload: AIHookPayload, policy:
|
|
|
110
120
|
is_sensitive_path = is_denied_path(file_path, policy)
|
|
111
121
|
if is_sensitive_path:
|
|
112
122
|
block_reason = BlockReason.SENSITIVE_PATH
|
|
113
|
-
if
|
|
123
|
+
if effective_mode == GuardrailsMode.BLOCK:
|
|
114
124
|
outcome = AIHookOutcome.BLOCKED
|
|
115
125
|
user_message = f'Cycode blocked sending {file_path} to the AI (sensitive path policy).'
|
|
116
126
|
return HookDecision.deny(
|
|
@@ -133,10 +143,12 @@ def handle_before_read_file(ctx: typer.Context, payload: AIHookPayload, policy:
|
|
|
133
143
|
outcome = AIHookOutcome.ALLOWED
|
|
134
144
|
|
|
135
145
|
if get_policy_value(file_read_config, 'scan_content', default=True):
|
|
136
|
-
violation_summary, scan_id = _scan_path_for_secrets(
|
|
146
|
+
violation_summary, scan_id = _scan_path_for_secrets(
|
|
147
|
+
ctx, file_path, policy, payload=payload, effective_mode=effective_mode
|
|
148
|
+
)
|
|
137
149
|
if violation_summary:
|
|
138
|
-
block_reason =
|
|
139
|
-
if
|
|
150
|
+
block_reason = SECRETS_BLOCK_REASON_BY_EVENT_TYPE[AiHookEventType.FILE_READ]
|
|
151
|
+
if effective_mode == GuardrailsMode.BLOCK:
|
|
140
152
|
outcome = AIHookOutcome.BLOCKED
|
|
141
153
|
user_message = f'Cycode blocked reading {file_path}. {violation_summary}'
|
|
142
154
|
return HookDecision.deny(
|
|
@@ -191,7 +203,6 @@ class _ArgScanFeature:
|
|
|
191
203
|
policy_key: str # 'mcp' or 'command_exec'
|
|
192
204
|
scan_key: str # 'scan_arguments' or 'scan_command'
|
|
193
205
|
event_type: AiHookEventType
|
|
194
|
-
block_reason: BlockReason
|
|
195
206
|
deny_message: Callable[[str], str]
|
|
196
207
|
deny_agent_message: str
|
|
197
208
|
ask_message: Callable[[str], str]
|
|
@@ -213,11 +224,10 @@ def _handle_arg_scan(
|
|
|
213
224
|
ai_client.create_event(payload, feature.event_type, AIHookOutcome.ALLOWED)
|
|
214
225
|
return HookDecision.allow(feature.event_type)
|
|
215
226
|
|
|
216
|
-
mode = get_policy_value(policy, 'mode', default=PolicyMode.BLOCK)
|
|
217
227
|
max_bytes = get_policy_value(policy, 'secrets', 'max_bytes', default=200000)
|
|
218
228
|
timeout_ms = get_policy_value(policy, 'secrets', 'timeout_ms', default=30000)
|
|
219
229
|
clipped = truncate_utf8(scan_text, max_bytes)
|
|
220
|
-
|
|
230
|
+
effective_mode = get_effective_mode(policy, feature_config)
|
|
221
231
|
|
|
222
232
|
scan_id = None
|
|
223
233
|
block_reason = None
|
|
@@ -226,10 +236,17 @@ def _handle_arg_scan(
|
|
|
226
236
|
|
|
227
237
|
try:
|
|
228
238
|
if get_policy_value(feature_config, feature.scan_key, default=True):
|
|
229
|
-
violation_summary, scan_id = _scan_text_for_secrets(
|
|
239
|
+
violation_summary, scan_id = _scan_text_for_secrets(
|
|
240
|
+
ctx,
|
|
241
|
+
clipped,
|
|
242
|
+
timeout_ms,
|
|
243
|
+
payload=payload,
|
|
244
|
+
event_type=feature.event_type,
|
|
245
|
+
effective_mode=effective_mode,
|
|
246
|
+
)
|
|
230
247
|
if violation_summary:
|
|
231
|
-
block_reason = feature.
|
|
232
|
-
if
|
|
248
|
+
block_reason = SECRETS_BLOCK_REASON_BY_EVENT_TYPE[feature.event_type]
|
|
249
|
+
if effective_mode == GuardrailsMode.BLOCK:
|
|
233
250
|
outcome = AIHookOutcome.BLOCKED
|
|
234
251
|
return HookDecision.deny(
|
|
235
252
|
feature.event_type,
|
|
@@ -275,7 +292,6 @@ def handle_before_mcp_execution(ctx: typer.Context, payload: AIHookPayload, poli
|
|
|
275
292
|
policy_key='mcp',
|
|
276
293
|
scan_key='scan_arguments',
|
|
277
294
|
event_type=AiHookEventType.MCP_EXECUTION,
|
|
278
|
-
block_reason=BlockReason.SECRETS_IN_MCP_ARGS,
|
|
279
295
|
deny_message=lambda v: f'Cycode blocked MCP tool call "{tool}". {v}',
|
|
280
296
|
deny_agent_message='Do not pass secrets to tools. Use secret references (name/id) instead.',
|
|
281
297
|
ask_message=lambda v: f'{v} in MCP tool call "{tool}". Allow execution?',
|
|
@@ -295,6 +311,36 @@ def get_handler_for_event(event_type: str) -> Optional[HandlerFn]:
|
|
|
295
311
|
return handlers.get(event_type)
|
|
296
312
|
|
|
297
313
|
|
|
314
|
+
def get_effective_mode(policy: dict, feature_config: dict) -> GuardrailsMode:
|
|
315
|
+
"""The event only blocks when both the global mode and the per-guardrail action are block."""
|
|
316
|
+
mode = get_policy_value(policy, 'mode', default=PolicyMode.BLOCK)
|
|
317
|
+
action = get_policy_value(feature_config, 'action', default=PolicyMode.BLOCK)
|
|
318
|
+
return GuardrailsMode.BLOCK if (mode == PolicyMode.BLOCK and action == PolicyMode.BLOCK) else GuardrailsMode.REPORT
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def build_ai_guardrails_scan_parameters(
|
|
322
|
+
ctx: typer.Context,
|
|
323
|
+
paths: Optional[tuple[str, ...]],
|
|
324
|
+
payload: AIHookPayload,
|
|
325
|
+
event_type: AiHookEventType,
|
|
326
|
+
effective_mode: GuardrailsMode,
|
|
327
|
+
) -> dict:
|
|
328
|
+
scan_parameters = get_scan_parameters(ctx, paths)
|
|
329
|
+
scan_parameters.setdefault('metadata', {})['ai_guardrails'] = {
|
|
330
|
+
'mode': effective_mode.value,
|
|
331
|
+
'ide_provider': payload.ide_provider,
|
|
332
|
+
'detection_source': SECRETS_BLOCK_REASON_BY_EVENT_TYPE[event_type].value,
|
|
333
|
+
'device_id': get_serial_number(),
|
|
334
|
+
'device_hostname': get_hostname(),
|
|
335
|
+
'conversation_id': payload.conversation_id,
|
|
336
|
+
'generation_id': payload.generation_id,
|
|
337
|
+
'ide_user_email': payload.ide_user_email,
|
|
338
|
+
'mcp_server_name': payload.mcp_server_name,
|
|
339
|
+
'mcp_tool_name': payload.mcp_tool_name,
|
|
340
|
+
}
|
|
341
|
+
return scan_parameters
|
|
342
|
+
|
|
343
|
+
|
|
298
344
|
def _setup_scan_context(ctx: typer.Context) -> typer.Context:
|
|
299
345
|
"""Set up minimal context for scan_documents without progress bars or printing."""
|
|
300
346
|
ctx.obj['progress_bar'] = DummyProgressBar([ScanProgressBarSection])
|
|
@@ -345,7 +391,14 @@ def _perform_scan(
|
|
|
345
391
|
return None, scan_id
|
|
346
392
|
|
|
347
393
|
|
|
348
|
-
def _scan_text_for_secrets(
|
|
394
|
+
def _scan_text_for_secrets(
|
|
395
|
+
ctx: typer.Context,
|
|
396
|
+
text: str,
|
|
397
|
+
timeout_ms: int,
|
|
398
|
+
payload: AIHookPayload,
|
|
399
|
+
event_type: AiHookEventType,
|
|
400
|
+
effective_mode: GuardrailsMode,
|
|
401
|
+
) -> tuple[Optional[str], Optional[str]]:
|
|
349
402
|
"""Scan text content for secrets using Cycode CLI."""
|
|
350
403
|
if not text:
|
|
351
404
|
return None, None
|
|
@@ -353,10 +406,17 @@ def _scan_text_for_secrets(ctx: typer.Context, text: str, timeout_ms: int) -> tu
|
|
|
353
406
|
document = Document(path='prompt-content.txt', content=text, is_git_diff_format=False)
|
|
354
407
|
scan_ctx = _setup_scan_context(ctx)
|
|
355
408
|
timeout_seconds = timeout_ms / 1000.0
|
|
356
|
-
|
|
409
|
+
scan_parameters = build_ai_guardrails_scan_parameters(scan_ctx, None, payload, event_type, effective_mode)
|
|
410
|
+
return _perform_scan(scan_ctx, [document], scan_parameters, timeout_seconds)
|
|
357
411
|
|
|
358
412
|
|
|
359
|
-
def _scan_path_for_secrets(
|
|
413
|
+
def _scan_path_for_secrets(
|
|
414
|
+
ctx: typer.Context,
|
|
415
|
+
file_path: str,
|
|
416
|
+
policy: dict,
|
|
417
|
+
payload: AIHookPayload,
|
|
418
|
+
effective_mode: GuardrailsMode,
|
|
419
|
+
) -> tuple[Optional[str], Optional[str]]:
|
|
360
420
|
"""Scan a file path for secrets."""
|
|
361
421
|
if not file_path or not os.path.isfile(file_path):
|
|
362
422
|
return None, None
|
|
@@ -375,4 +435,7 @@ def _scan_path_for_secrets(ctx: typer.Context, file_path: str, policy: dict) ->
|
|
|
375
435
|
|
|
376
436
|
document = Document(path=os.path.basename(file_path), content=content, is_git_diff_format=False)
|
|
377
437
|
scan_ctx = _setup_scan_context(ctx)
|
|
378
|
-
|
|
438
|
+
scan_parameters = build_ai_guardrails_scan_parameters(
|
|
439
|
+
scan_ctx, (file_path,), payload, AiHookEventType.FILE_READ, effective_mode
|
|
440
|
+
)
|
|
441
|
+
return _perform_scan(scan_ctx, [document], scan_parameters, timeout_seconds)
|
{cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/scan/scan_command.py
RENAMED
|
@@ -8,6 +8,7 @@ The handlers in ``handlers.py`` are agent-agnostic (they return
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from typing import Annotated, Optional, Union
|
|
11
|
+
from uuid import uuid4
|
|
11
12
|
|
|
12
13
|
import click
|
|
13
14
|
import typer
|
|
@@ -125,6 +126,9 @@ def scan_command(
|
|
|
125
126
|
return
|
|
126
127
|
|
|
127
128
|
unified_payload = ide_integration.parse_hook_payload(payload)
|
|
129
|
+
if not unified_payload.generation_id:
|
|
130
|
+
# Not every IDE dialect provides a generation id (e.g. Copilot)
|
|
131
|
+
unified_payload.generation_id = str(uuid4())
|
|
128
132
|
event_name = unified_payload.event_name
|
|
129
133
|
logger.debug(
|
|
130
134
|
'Processing AI guardrails hook',
|
|
@@ -41,3 +41,12 @@ class BlockReason(StrEnum):
|
|
|
41
41
|
SECRETS_IN_MCP_ARGS = 'secrets_in_mcp_args'
|
|
42
42
|
SENSITIVE_PATH = 'sensitive_path'
|
|
43
43
|
SCAN_FAILURE = 'scan_failure'
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# The reason each event type yields when a secret is found in it. Also travels with the scan as
|
|
47
|
+
# `detection_source`, so the violation and the hook event are labelled from the same vocabulary.
|
|
48
|
+
SECRETS_BLOCK_REASON_BY_EVENT_TYPE: dict[AiHookEventType, BlockReason] = {
|
|
49
|
+
AiHookEventType.PROMPT: BlockReason.SECRETS_IN_PROMPT,
|
|
50
|
+
AiHookEventType.FILE_READ: BlockReason.SECRETS_IN_FILE,
|
|
51
|
+
AiHookEventType.MCP_EXECUTION: BlockReason.SECRETS_IN_MCP_ARGS,
|
|
52
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import getpass
|
|
2
|
+
import os
|
|
2
3
|
import platform
|
|
3
4
|
import re
|
|
4
5
|
import socket
|
|
5
6
|
import subprocess
|
|
7
|
+
import tempfile
|
|
8
|
+
from pathlib import Path
|
|
6
9
|
from typing import Optional
|
|
7
10
|
|
|
8
11
|
from cycode.logger import get_logger
|
|
@@ -11,6 +14,8 @@ logger = get_logger('HOST INFO')
|
|
|
11
14
|
|
|
12
15
|
_SUBPROCESS_TIMEOUT_SEC = 5
|
|
13
16
|
|
|
17
|
+
_SERIAL_NUMBER_CACHE_FILE_NAME = '.cycode-device-serial'
|
|
18
|
+
|
|
14
19
|
_PLATFORM_NAMES = {'Darwin': 'macOS', 'Windows': 'Windows', 'Linux': 'Linux'}
|
|
15
20
|
|
|
16
21
|
|
|
@@ -93,6 +98,19 @@ def get_last_login_user() -> Optional[str]:
|
|
|
93
98
|
|
|
94
99
|
|
|
95
100
|
def get_serial_number() -> Optional[str]:
|
|
101
|
+
# The serial is immutable hardware info, but resolving it shells out (ioreg/WMI)
|
|
102
|
+
# and this runs in a fresh process per AI hook event - cache it on disk.
|
|
103
|
+
cached = _read_serial_number_cache()
|
|
104
|
+
if cached:
|
|
105
|
+
return cached
|
|
106
|
+
|
|
107
|
+
serial = _resolve_serial_number()
|
|
108
|
+
if serial:
|
|
109
|
+
_write_serial_number_cache(serial)
|
|
110
|
+
return serial
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _resolve_serial_number() -> Optional[str]:
|
|
96
114
|
try:
|
|
97
115
|
system = platform.system()
|
|
98
116
|
if system == 'Darwin':
|
|
@@ -104,6 +122,40 @@ def get_serial_number() -> Optional[str]:
|
|
|
104
122
|
return None
|
|
105
123
|
|
|
106
124
|
|
|
125
|
+
def _serial_number_cache_path() -> Path:
|
|
126
|
+
# The username suffix avoids collisions on OSes with a shared temp dir
|
|
127
|
+
return Path(tempfile.gettempdir()) / f'.cycode-device-serial-{getpass.getuser()}'
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _read_serial_number_cache() -> Optional[str]:
|
|
131
|
+
try:
|
|
132
|
+
return _serial_number_cache_path().read_text(encoding='utf-8').strip() or None
|
|
133
|
+
except Exception:
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _write_serial_number_cache(serial: str) -> None:
|
|
138
|
+
try:
|
|
139
|
+
cache_path = _serial_number_cache_path()
|
|
140
|
+
|
|
141
|
+
# The serial identifies the machine, and the temp dir is shared, so the cache is created
|
|
142
|
+
# readable by its owner alone (what mkstemp does) and moved into place atomically - a hook
|
|
143
|
+
# racing another one never reads a half-written cache, and the rename can't be redirected
|
|
144
|
+
# by a symlink planted at the destination the way an in-place write could.
|
|
145
|
+
file_descriptor, temp_path = tempfile.mkstemp(
|
|
146
|
+
dir=cache_path.parent, prefix=f'{_SERIAL_NUMBER_CACHE_FILE_NAME}.'
|
|
147
|
+
)
|
|
148
|
+
try:
|
|
149
|
+
with os.fdopen(file_descriptor, 'w', encoding='utf-8') as temp_file:
|
|
150
|
+
temp_file.write(serial)
|
|
151
|
+
os.replace(temp_path, cache_path)
|
|
152
|
+
except Exception:
|
|
153
|
+
Path(temp_path).unlink(missing_ok=True)
|
|
154
|
+
raise
|
|
155
|
+
except Exception as e:
|
|
156
|
+
logger.debug('Failed to cache serial number', exc_info=e)
|
|
157
|
+
|
|
158
|
+
|
|
107
159
|
def _get_macos_serial_number() -> Optional[str]:
|
|
108
160
|
output = _run(['ioreg', '-c', 'IOPlatformExpertDevice', '-d', '2'])
|
|
109
161
|
if not output:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/ides/_plugin_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/session_start_command.py
RENAMED
|
File without changes
|
|
File without changes
|
{cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_guardrails/uninstall_command.py
RENAMED
|
File without changes
|
|
File without changes
|
{cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_remediation/ai_remediation_command.py
RENAMED
|
File without changes
|
|
File without changes
|
{cycode-3.18.1.dev1 → cycode-3.18.1.dev3}/cycode/cli/apps/ai_remediation/print_remediation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|