cycode 3.16.1.dev7__tar.gz → 3.16.2__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.16.1.dev7 → cycode-3.16.2}/PKG-INFO +1 -1
- cycode-3.16.2/cycode/__init__.py +8 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/app.py +88 -15
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/ides/_plugin_utils.py +22 -10
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/ides/base.py +10 -4
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/ides/claude_code.py +14 -11
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/ides/codex.py +19 -11
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/ides/cursor.py +13 -4
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/session_start_command.py +17 -3
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/code_scanner.py +15 -12
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/scan_parameters.py +2 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/scan_result.py +4 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/consts.py +24 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/file_excluder.py +8 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/ai_security_manager_client.py +9 -3
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/base_token_auth_client.py +27 -14
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/cycode_client_base.py +24 -21
- {cycode-3.16.1.dev7 → cycode-3.16.2}/pyproject.toml +1 -1
- cycode-3.16.1.dev7/cycode/__init__.py +0 -1
- {cycode-3.16.1.dev7 → cycode-3.16.2}/LICENCE +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/README.md +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/__main__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/activation_manager.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/command_utils.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/consts.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/hooks_manager.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/ides/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/install_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/scan/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/scan/consts.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/scan/handlers.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/scan/payload.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/scan/policy.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/scan/scan_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/scan/types.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/scan/utils.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/status_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_guardrails/uninstall_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_remediation/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_remediation/ai_remediation_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_remediation/apply_fix.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ai_remediation/print_remediation.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/api/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/api/api_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/api/openapi_spec.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/auth/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/auth/auth_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/auth/auth_common.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/auth/auth_manager.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/auth/models.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/configure/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/configure/configure_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/configure/consts.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/configure/messages.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/configure/prompts.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ignore/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/ignore/ignore_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/mcp/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/mcp/mcp_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report/report_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report/sbom/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report/sbom/common.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report/sbom/path/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report/sbom/path/path_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report/sbom/repository_url/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report/sbom/repository_url/repository_url_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report/sbom/sbom_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report/sbom/sbom_report_file.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report_import/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report_import/report_import_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report_import/sbom/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/report_import/sbom/sbom_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/sca_options.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/aggregation_report.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/commit_history/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/commit_history/commit_history_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/commit_range_scanner.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/detection_excluder.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/path/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/path/path_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/pre_commit/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/pre_commit/pre_commit_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/pre_push/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/pre_push/pre_push_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/pre_receive/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/pre_receive/pre_receive_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/remote_url_resolver.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/repository/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/repository/repository_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/scan_ci/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/scan_ci/ci_integrations.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/scan_ci/scan_ci_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/scan/scan_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/status/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/status/get_cli_status.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/status/models.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/status/status_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/apps/status/version_command.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/cli_types.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/config.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/console.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/exceptions/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/exceptions/custom_exceptions.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/exceptions/handle_ai_remediation_errors.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/exceptions/handle_auth_errors.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/exceptions/handle_errors.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/exceptions/handle_report_sbom_errors.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/exceptions/handle_scan_errors.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/commit_range_documents.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/documents_walk_ignore.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/iac/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/models/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/path_documents.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/repository_documents.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/base_restore_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/go/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/maven/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/npm/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/nuget/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/php/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/python/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/python/restore_uv_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/ruby/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/ruby/restore_ruby_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/sbt/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/sca/sca_file_collector.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/walk_ignore.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/files_collector/zip_documents.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/logger.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/main.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/models.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/console_printer.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/json_printer.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/printer_base.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/rich_printer.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/tables/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/tables/sca_table_printer.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/tables/table.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/tables/table_models.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/tables/table_printer.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/tables/table_printer_base.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/text_printer.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/utils/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/utils/code_snippet_syntax.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/utils/detection_data.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/utils/detection_ordering/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/utils/detection_ordering/common_ordering.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/utils/detection_ordering/sca_ordering.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/printers/utils/rich_helpers.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/user_settings/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/user_settings/base_file_manager.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/user_settings/config_file_manager.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/user_settings/configuration_manager.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/user_settings/credentials_manager.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/user_settings/jwt_creator.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/binary_utils.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/enum_utils.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/get_api_client.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/git_proxy.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/ignore_utils.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/jwt_utils.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/path_utils.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/progress_bar.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/scan_batch.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/scan_utils.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/shell_executor.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/string_utils.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/task_timer.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/url_utils.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/version_checker.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cli/utils/yaml_utils.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/config.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/__init__.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/ai_security_manager_service_config.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/auth_client.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/cli_activation_client.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/client_creator.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/config.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/config_dev.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/cycode_client.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/cycode_dev_based_client.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/cycode_oidc_based_client.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/cycode_token_based_client.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/headers.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/import_sbom_client.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/logger.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/models.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/report_client.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/scan_client.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/cyclient/scan_config_base.py +0 -0
- {cycode-3.16.1.dev7 → cycode-3.16.2}/cycode/logger.py +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import time as _time
|
|
2
|
+
|
|
3
|
+
# Unix-epoch wall clock captured at the earliest possible moment of CLI
|
|
4
|
+
# startup. Sent as `scan_parameters.cli_start_time` so the server can compute
|
|
5
|
+
# end-to-end scan duration from the moment the user actually triggered it.
|
|
6
|
+
_BOOT_WALL: float = _time.time()
|
|
7
|
+
|
|
8
|
+
__version__ = '3.16.2' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import importlib
|
|
1
2
|
import logging
|
|
2
3
|
import sys
|
|
3
4
|
from typing import Annotated, Optional
|
|
@@ -10,12 +11,7 @@ from typer._completion_shared import Shells
|
|
|
10
11
|
from typer.completion import install_callback, show_callback
|
|
11
12
|
|
|
12
13
|
from cycode import __version__
|
|
13
|
-
from cycode.cli.apps import ai_guardrails, ai_remediation, auth, configure, ignore, report, report_import, scan, status
|
|
14
14
|
from cycode.cli.apps.api import get_platform_group
|
|
15
|
-
|
|
16
|
-
if sys.version_info >= (3, 10):
|
|
17
|
-
from cycode.cli.apps import mcp
|
|
18
|
-
|
|
19
15
|
from cycode.cli.cli_types import OutputTypeOption
|
|
20
16
|
from cycode.cli.consts import CLI_CONTEXT_SETTINGS
|
|
21
17
|
from cycode.cli.printers import ConsolePrinter
|
|
@@ -46,17 +42,88 @@ app = typer.Typer(
|
|
|
46
42
|
add_completion=False, # we add it manually to control the rich help panel
|
|
47
43
|
)
|
|
48
44
|
|
|
49
|
-
app.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
45
|
+
# Top-level subcommand → module providing its Typer app. Peeking at sys.argv
|
|
46
|
+
# lets us import only the invoked subapp on the hot path (e.g.
|
|
47
|
+
# `cycode ai-guardrails scan`), skipping ~300ms of unrelated imports.
|
|
48
|
+
_SUBAPP_MODULES: dict[str, str] = {
|
|
49
|
+
'ai-guardrails': 'cycode.cli.apps.ai_guardrails',
|
|
50
|
+
'ai-remediation': 'cycode.cli.apps.ai_remediation',
|
|
51
|
+
'auth': 'cycode.cli.apps.auth',
|
|
52
|
+
'configure': 'cycode.cli.apps.configure',
|
|
53
|
+
'ignore': 'cycode.cli.apps.ignore',
|
|
54
|
+
'report': 'cycode.cli.apps.report',
|
|
55
|
+
'import': 'cycode.cli.apps.report_import',
|
|
56
|
+
'scan': 'cycode.cli.apps.scan',
|
|
57
|
+
'status': 'cycode.cli.apps.status',
|
|
58
|
+
}
|
|
58
59
|
if sys.version_info >= (3, 10):
|
|
59
|
-
|
|
60
|
+
_SUBAPP_MODULES['mcp'] = 'cycode.cli.apps.mcp'
|
|
61
|
+
|
|
62
|
+
# Aliases: alternate spellings that resolve to a primary subcommand key.
|
|
63
|
+
_SUBAPP_ALIASES: dict[str, str] = {
|
|
64
|
+
'ai_remediation': 'ai-remediation', # backward-compat underscore form
|
|
65
|
+
'version': 'status',
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Root-level options that consume a following value; argv-peek must skip past
|
|
69
|
+
# both the option and its value when scanning for the first positional arg.
|
|
70
|
+
_ROOT_OPTS_WITH_VALUE = frozenset(
|
|
71
|
+
{
|
|
72
|
+
'--output',
|
|
73
|
+
'-o',
|
|
74
|
+
'--user-agent',
|
|
75
|
+
'--client-secret',
|
|
76
|
+
'--client-id',
|
|
77
|
+
'--id-token',
|
|
78
|
+
'--show-completion',
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _detect_invocation() -> tuple[Optional[str], Optional[str]]:
|
|
84
|
+
"""Return (top-level-subapp, second-level-subcommand) parsed from sys.argv.
|
|
85
|
+
|
|
86
|
+
Both values may be None: when no positional arg matches a known subapp,
|
|
87
|
+
or when the user only provided a top-level subcommand.
|
|
88
|
+
"""
|
|
89
|
+
positionals = []
|
|
90
|
+
args = sys.argv[1:]
|
|
91
|
+
i = 0
|
|
92
|
+
while i < len(args):
|
|
93
|
+
arg = args[i]
|
|
94
|
+
if arg in _ROOT_OPTS_WITH_VALUE:
|
|
95
|
+
i += 2
|
|
96
|
+
elif arg.startswith('-'):
|
|
97
|
+
# Any flag form: short, long, --key=value, or '--' marker. Skip the token only.
|
|
98
|
+
i += 1
|
|
99
|
+
else:
|
|
100
|
+
positionals.append(arg)
|
|
101
|
+
if len(positionals) >= 2:
|
|
102
|
+
break
|
|
103
|
+
i += 1
|
|
104
|
+
subapp = positionals[0] if positionals else None
|
|
105
|
+
subapp = _SUBAPP_ALIASES.get(subapp, subapp)
|
|
106
|
+
if subapp not in _SUBAPP_MODULES:
|
|
107
|
+
return None, None
|
|
108
|
+
subcommand = positionals[1] if len(positionals) >= 2 else None
|
|
109
|
+
return subapp, subcommand
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# Computed once at import; reused by lazy registration and the version-checker skip.
|
|
113
|
+
_INVOKED_SUBAPP, _INVOKED_SUBCOMMAND = _detect_invocation()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _register_subapps(only: Optional[str]) -> None:
|
|
117
|
+
if only is not None:
|
|
118
|
+
app.add_typer(importlib.import_module(_SUBAPP_MODULES[only]).app)
|
|
119
|
+
return
|
|
120
|
+
# Cold path (--help, completion, unknown subcommand): load all modules so
|
|
121
|
+
# root help lists everything. Deduplicate since aliases share modules.
|
|
122
|
+
for module_path in dict.fromkeys(_SUBAPP_MODULES.values()):
|
|
123
|
+
app.add_typer(importlib.import_module(module_path).app)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
_register_subapps(_INVOKED_SUBAPP)
|
|
60
127
|
|
|
61
128
|
# Register the `platform` command group (dynamically built from the OpenAPI spec).
|
|
62
129
|
# The group itself is constructed cheaply at import time; the spec is only fetched
|
|
@@ -81,6 +148,12 @@ typer.main.get_group = _get_group_with_platform
|
|
|
81
148
|
|
|
82
149
|
|
|
83
150
|
def check_latest_version_on_close(ctx: typer.Context) -> None:
|
|
151
|
+
# Skip on `cycode ai-guardrails scan` — it emits JSON to stdout, so an
|
|
152
|
+
# upgrade notice would corrupt the response. Human-driven sibling commands
|
|
153
|
+
# (install, uninstall, status, session-start) still get the notice.
|
|
154
|
+
if (_INVOKED_SUBAPP, _INVOKED_SUBCOMMAND) == ('ai-guardrails', 'scan'):
|
|
155
|
+
return
|
|
156
|
+
|
|
84
157
|
output = ctx.obj.get('output')
|
|
85
158
|
# don't print anything if the output is JSON
|
|
86
159
|
if output == OutputTypeOption.JSON:
|
|
@@ -26,13 +26,26 @@ def load_plugin_json(path: Path) -> Optional[dict]:
|
|
|
26
26
|
return None
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def build_global_config_file(path: Path, mcp_servers: Optional[dict]) -> Optional[dict]:
|
|
30
|
+
"""Wrap a global (non-plugin) MCP config into the session-context file shape.
|
|
31
|
+
|
|
32
|
+
Returns ``{"path": <full path>, "content": <{"mcpServers": ...} JSON>}`` when
|
|
33
|
+
there are servers, else ``None``. ``content`` is normalized to the canonical
|
|
34
|
+
``{"mcpServers": {...}}`` shape, dropping everything else in the source file.
|
|
35
|
+
"""
|
|
36
|
+
servers = mcp_servers or {}
|
|
37
|
+
if not servers:
|
|
38
|
+
return None
|
|
39
|
+
return {'path': str(path), 'content': json.dumps({'mcpServers': servers})}
|
|
40
|
+
|
|
41
|
+
|
|
29
42
|
def walk_enabled_plugins(
|
|
30
43
|
plugin_entries: dict[str, Any],
|
|
31
44
|
is_enabled: Callable[[Any], bool],
|
|
32
45
|
locate_dir: Callable[[str, str], Optional[Path]],
|
|
33
46
|
read_plugin: Callable[[Path], tuple[dict, dict]],
|
|
34
|
-
) ->
|
|
35
|
-
"""Iterate enabled plugins
|
|
47
|
+
) -> dict:
|
|
48
|
+
"""Iterate enabled plugins and build their inventory metadata.
|
|
36
49
|
|
|
37
50
|
Args:
|
|
38
51
|
plugin_entries: ``{<plugin>@<marketplace>: settings}`` map from the IDE config.
|
|
@@ -42,13 +55,13 @@ def walk_enabled_plugins(
|
|
|
42
55
|
filesystem path or None if it can't be resolved.
|
|
43
56
|
read_plugin: given the plugin path, returns ``(entry_fields, servers)``:
|
|
44
57
|
``entry_fields`` are extra metadata to attach to the inventory entry
|
|
45
|
-
(name/version/description/...)
|
|
58
|
+
(name/version/description/...); ``servers`` are the plugin's MCP
|
|
59
|
+
servers, which ``read_plugin`` uses to derive that metadata.
|
|
46
60
|
|
|
47
|
-
Returns ``
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
Returns ``enriched_plugins``. Plugin keys without ``@`` (or that fail to
|
|
62
|
+
resolve to a directory) still appear in the inventory with just
|
|
63
|
+
``{'enabled': True}`` so we don't silently drop them.
|
|
50
64
|
"""
|
|
51
|
-
merged_mcp: dict = {}
|
|
52
65
|
enriched: dict = {}
|
|
53
66
|
|
|
54
67
|
for plugin_key, settings in plugin_entries.items():
|
|
@@ -66,8 +79,7 @@ def walk_enabled_plugins(
|
|
|
66
79
|
if plugin_dir is None:
|
|
67
80
|
continue
|
|
68
81
|
|
|
69
|
-
plugin_fields,
|
|
82
|
+
plugin_fields, _ = read_plugin(plugin_dir)
|
|
70
83
|
entry.update(plugin_fields)
|
|
71
|
-
merged_mcp.update(servers)
|
|
72
84
|
|
|
73
|
-
return
|
|
85
|
+
return enriched
|
|
@@ -167,10 +167,16 @@ class IDE(ABC):
|
|
|
167
167
|
"""
|
|
168
168
|
return None
|
|
169
169
|
|
|
170
|
-
def get_session_context(self) -> tuple[dict, dict]:
|
|
171
|
-
"""Return ``(
|
|
170
|
+
def get_session_context(self) -> tuple[Optional[dict], dict]:
|
|
171
|
+
"""Return ``(global_config_file, enabled_plugins)`` for session-context reporting.
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
``global_config_file`` is the IDE's global (non-plugin) MCP config as
|
|
174
|
+
``{"path": <full path>, "content": <normalized {"mcpServers": ...} JSON>}``,
|
|
175
|
+
or ``None`` when there is no global MCP config. ``enabled_plugins`` maps each
|
|
176
|
+
enabled plugin key to its metadata (including its own ``mcp_config_file``
|
|
177
|
+
content and ``mcp_config_file_path``).
|
|
178
|
+
|
|
179
|
+
Default: ``(None, {})`` (no plugin system, no discoverable MCP config).
|
|
174
180
|
Override to surface MCP/plugin inventory.
|
|
175
181
|
"""
|
|
176
|
-
return
|
|
182
|
+
return None, {}
|
|
@@ -7,7 +7,11 @@ from pathlib import Path
|
|
|
7
7
|
from typing import ClassVar, Optional
|
|
8
8
|
|
|
9
9
|
from cycode.cli.apps.ai_guardrails.consts import CYCODE_SCAN_PROMPT_COMMAND, CYCODE_SESSION_START_COMMAND
|
|
10
|
-
from cycode.cli.apps.ai_guardrails.ides._plugin_utils import
|
|
10
|
+
from cycode.cli.apps.ai_guardrails.ides._plugin_utils import (
|
|
11
|
+
build_global_config_file,
|
|
12
|
+
load_plugin_json,
|
|
13
|
+
walk_enabled_plugins,
|
|
14
|
+
)
|
|
11
15
|
from cycode.cli.apps.ai_guardrails.ides.base import IDE, DecisionAction, HookDecision
|
|
12
16
|
from cycode.cli.apps.ai_guardrails.scan.payload import AIHookPayload
|
|
13
17
|
from cycode.cli.apps.ai_guardrails.scan.types import AiHookEventType
|
|
@@ -184,14 +188,17 @@ def _read_claude_plugin(plugin_dir: Path) -> tuple[dict, dict]:
|
|
|
184
188
|
if field in manifest:
|
|
185
189
|
entry[field] = manifest[field]
|
|
186
190
|
|
|
187
|
-
|
|
191
|
+
mcp_config_path = plugin_dir / '.mcp.json'
|
|
192
|
+
mcp_config = load_plugin_json(mcp_config_path) or {}
|
|
188
193
|
servers: dict = mcp_config.get('mcpServers') or {}
|
|
189
194
|
if servers:
|
|
190
195
|
entry['mcp_server_names'] = list(servers.keys())
|
|
196
|
+
entry['mcp_config_file_path'] = str(mcp_config_path)
|
|
197
|
+
entry['mcp_config_file'] = json.dumps(mcp_config)
|
|
191
198
|
return entry, servers
|
|
192
199
|
|
|
193
200
|
|
|
194
|
-
def resolve_plugins(settings: dict) ->
|
|
201
|
+
def resolve_plugins(settings: dict) -> dict:
|
|
195
202
|
"""Walk Claude Code's ``enabledPlugins`` via the shared plugin walker.
|
|
196
203
|
|
|
197
204
|
Each enabled plugin's marketplace is resolved through
|
|
@@ -354,15 +361,11 @@ class ClaudeCode(IDE):
|
|
|
354
361
|
config = load_claude_config()
|
|
355
362
|
return _email_from_config(config) if config else None
|
|
356
363
|
|
|
357
|
-
def get_session_context(self) -> tuple[dict, dict]:
|
|
364
|
+
def get_session_context(self) -> tuple[Optional[dict], dict]:
|
|
358
365
|
config = load_claude_config()
|
|
359
|
-
|
|
366
|
+
global_config_file = build_global_config_file(_CLAUDE_CONFIG_PATH, get_mcp_servers(config)) if config else None
|
|
360
367
|
|
|
361
368
|
settings = load_claude_settings()
|
|
362
|
-
if settings
|
|
363
|
-
plugin_mcp, enriched_plugins = resolve_plugins(settings)
|
|
364
|
-
mcp_servers.update(plugin_mcp)
|
|
365
|
-
else:
|
|
366
|
-
enriched_plugins = {}
|
|
369
|
+
enriched_plugins = resolve_plugins(settings) if settings else {}
|
|
367
370
|
|
|
368
|
-
return
|
|
371
|
+
return global_config_file, enriched_plugins
|
|
@@ -14,7 +14,11 @@ else: # pragma: no cover - py<3.11 fallback
|
|
|
14
14
|
import tomli as tomllib
|
|
15
15
|
|
|
16
16
|
from cycode.cli.apps.ai_guardrails.consts import CYCODE_SCAN_PROMPT_COMMAND, CYCODE_SESSION_START_COMMAND
|
|
17
|
-
from cycode.cli.apps.ai_guardrails.ides._plugin_utils import
|
|
17
|
+
from cycode.cli.apps.ai_guardrails.ides._plugin_utils import (
|
|
18
|
+
build_global_config_file,
|
|
19
|
+
load_plugin_json,
|
|
20
|
+
walk_enabled_plugins,
|
|
21
|
+
)
|
|
18
22
|
from cycode.cli.apps.ai_guardrails.ides.base import IDE, DecisionAction, HookDecision
|
|
19
23
|
from cycode.cli.apps.ai_guardrails.scan.payload import AIHookPayload
|
|
20
24
|
from cycode.cli.apps.ai_guardrails.scan.types import AiHookEventType
|
|
@@ -129,16 +133,19 @@ def _read_codex_plugin(plugin_dir: Path) -> tuple[dict, dict]:
|
|
|
129
133
|
mcp_ref = manifest.get('mcpServers')
|
|
130
134
|
if not mcp_ref:
|
|
131
135
|
return entry, {}
|
|
132
|
-
|
|
136
|
+
mcp_config_path = plugin_dir / mcp_ref
|
|
137
|
+
mcp_doc = load_plugin_json(mcp_config_path) or {}
|
|
133
138
|
servers = mcp_doc.get('mcpServers', mcp_doc)
|
|
134
139
|
if not isinstance(servers, dict):
|
|
135
140
|
servers = {}
|
|
136
141
|
if servers:
|
|
137
142
|
entry['mcp_server_names'] = list(servers.keys())
|
|
143
|
+
entry['mcp_config_file_path'] = str(mcp_config_path)
|
|
144
|
+
entry['mcp_config_file'] = json.dumps(mcp_doc)
|
|
138
145
|
return entry, servers
|
|
139
146
|
|
|
140
147
|
|
|
141
|
-
def _resolve_codex_plugins(config: dict) ->
|
|
148
|
+
def _resolve_codex_plugins(config: dict) -> dict:
|
|
142
149
|
"""Walk enabled ``[plugins."<plugin>@<marketplace>"]`` entries."""
|
|
143
150
|
return walk_enabled_plugins(
|
|
144
151
|
plugin_entries=config.get('plugins') or {},
|
|
@@ -297,13 +304,14 @@ class Codex(IDE):
|
|
|
297
304
|
def get_user_email(self) -> Optional[str]:
|
|
298
305
|
return _email_from_auth()
|
|
299
306
|
|
|
300
|
-
def get_session_context(self) -> tuple[dict, dict]:
|
|
307
|
+
def get_session_context(self) -> tuple[Optional[dict], dict]:
|
|
301
308
|
config = _load_codex_config()
|
|
302
309
|
if not config:
|
|
303
|
-
return
|
|
304
|
-
# Codex stores MCP servers under `[mcp_servers.<name>]
|
|
305
|
-
#
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
+
return None, {}
|
|
311
|
+
# Codex stores MCP servers under `[mcp_servers.<name>]`; the global config
|
|
312
|
+
# file becomes its own session-context file. Plugins (via
|
|
313
|
+
# `[plugins."<plugin>@<marketplace>"]`) carry their own config files.
|
|
314
|
+
config_path = _codex_config_toml_path('user')
|
|
315
|
+
global_config_file = build_global_config_file(config_path, config.get('mcp_servers'))
|
|
316
|
+
enriched_plugins = _resolve_codex_plugins(config)
|
|
317
|
+
return global_config_file, enriched_plugins
|
|
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from typing import ClassVar, Optional
|
|
7
7
|
|
|
8
8
|
from cycode.cli.apps.ai_guardrails.consts import CYCODE_SCAN_PROMPT_COMMAND, CYCODE_SESSION_START_COMMAND
|
|
9
|
+
from cycode.cli.apps.ai_guardrails.ides._plugin_utils import build_global_config_file
|
|
9
10
|
from cycode.cli.apps.ai_guardrails.ides.base import IDE, DecisionAction, HookDecision
|
|
10
11
|
from cycode.cli.apps.ai_guardrails.scan.payload import AIHookPayload
|
|
11
12
|
from cycode.cli.apps.ai_guardrails.scan.types import AiHookEventType
|
|
@@ -39,9 +40,14 @@ def _user_hooks_dir() -> Path:
|
|
|
39
40
|
return Path.home() / '.config' / 'Cursor'
|
|
40
41
|
|
|
41
42
|
|
|
43
|
+
def _cursor_mcp_config_path() -> Path:
|
|
44
|
+
"""User-scope Cursor MCP config path (``~/.cursor/mcp.json``, all platforms)."""
|
|
45
|
+
return Path.home() / '.cursor' / _MCP_CONFIG_FILENAME
|
|
46
|
+
|
|
47
|
+
|
|
42
48
|
def _load_cursor_mcp_config(config_path: Optional[Path] = None) -> Optional[dict]:
|
|
43
49
|
"""Load and parse `~/.cursor/mcp.json`. Returns None if missing/invalid."""
|
|
44
|
-
path = config_path or (
|
|
50
|
+
path = config_path or _cursor_mcp_config_path()
|
|
45
51
|
if not path.exists():
|
|
46
52
|
logger.debug('Cursor MCP config file not found, %s', {'path': str(path)})
|
|
47
53
|
return None
|
|
@@ -113,7 +119,10 @@ class Cursor(IDE):
|
|
|
113
119
|
ide_version=raw_payload.get('cursor_version'),
|
|
114
120
|
)
|
|
115
121
|
|
|
116
|
-
def get_session_context(self) -> tuple[dict, dict]:
|
|
122
|
+
def get_session_context(self) -> tuple[Optional[dict], dict]:
|
|
117
123
|
config = _load_cursor_mcp_config()
|
|
118
|
-
|
|
119
|
-
|
|
124
|
+
if not config:
|
|
125
|
+
return None, {}
|
|
126
|
+
config_path = _cursor_mcp_config_path()
|
|
127
|
+
global_config_file = build_global_config_file(config_path, config.get('mcpServers'))
|
|
128
|
+
return global_config_file, {}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"""Handle AI guardrails session start: auth, conversation creation, session context."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import socket
|
|
3
6
|
import sys
|
|
4
7
|
from typing import TYPE_CHECKING, Annotated, Optional
|
|
5
8
|
|
|
@@ -20,14 +23,25 @@ if TYPE_CHECKING:
|
|
|
20
23
|
logger = get_logger('AI Guardrails')
|
|
21
24
|
|
|
22
25
|
|
|
26
|
+
def _get_logged_in_user() -> Optional[str]:
|
|
27
|
+
"""Best-effort OS account name (whoami). None if it can't be resolved."""
|
|
28
|
+
try:
|
|
29
|
+
return os.getlogin()
|
|
30
|
+
except Exception:
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
23
34
|
def _report_session_context(ai_client: 'AISecurityManagerClient', ide: IDE, user_email: Optional[str]) -> None:
|
|
24
35
|
"""Report IDE session context to the AI security manager. Never raises."""
|
|
25
36
|
try:
|
|
26
|
-
|
|
27
|
-
if not
|
|
37
|
+
global_config_file, enabled_plugins = ide.get_session_context()
|
|
38
|
+
if not global_config_file and not enabled_plugins:
|
|
28
39
|
return
|
|
29
40
|
ai_client.report_session_context(
|
|
30
|
-
|
|
41
|
+
hostname=socket.gethostname(),
|
|
42
|
+
platform=platform.system(),
|
|
43
|
+
logged_in_user=_get_logged_in_user(),
|
|
44
|
+
global_config_file=global_config_file,
|
|
31
45
|
enabled_plugins=enabled_plugins,
|
|
32
46
|
user_email=user_email,
|
|
33
47
|
)
|
|
@@ -204,18 +204,21 @@ def _get_scan_documents_thread_func(
|
|
|
204
204
|
'zip_file_size': zip_file_size,
|
|
205
205
|
},
|
|
206
206
|
)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
207
|
+
# Sync flows already received the full result inline; only async flows
|
|
208
|
+
# need a separate status report to signal polling completion.
|
|
209
|
+
if not should_use_sync_flow:
|
|
210
|
+
report_scan_status(
|
|
211
|
+
cycode_client,
|
|
212
|
+
scan_type,
|
|
213
|
+
scan_id,
|
|
214
|
+
scan_completed,
|
|
215
|
+
relevant_detections_count,
|
|
216
|
+
detections_count,
|
|
217
|
+
len(batch),
|
|
218
|
+
zip_file_size,
|
|
219
|
+
command_scan_type,
|
|
220
|
+
error_message,
|
|
221
|
+
)
|
|
219
222
|
|
|
220
223
|
return scan_id, error, local_scan_result
|
|
221
224
|
|
|
@@ -2,6 +2,7 @@ from typing import Optional
|
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
4
|
|
|
5
|
+
from cycode import _BOOT_WALL
|
|
5
6
|
from cycode.cli.apps.scan.remote_url_resolver import get_remote_url_scan_parameter
|
|
6
7
|
from cycode.cli.utils.scan_utils import generate_unique_scan_id
|
|
7
8
|
from cycode.logger import get_logger
|
|
@@ -17,6 +18,7 @@ def _get_default_scan_parameters(ctx: typer.Context) -> dict:
|
|
|
17
18
|
'license_compliance': ctx.obj.get('license-compliance'),
|
|
18
19
|
'command_type': ctx.info_name.replace('-', '_'), # save backward compatibility
|
|
19
20
|
'aggregation_id': str(generate_unique_scan_id()),
|
|
21
|
+
'cli_start_time': _BOOT_WALL,
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
|
|
@@ -189,6 +189,10 @@ def enrich_scan_result_with_data_from_detection_rules(
|
|
|
189
189
|
for detection in detections_per_file.detections:
|
|
190
190
|
detection_rule_ids.add(detection.detection_rule_id)
|
|
191
191
|
|
|
192
|
+
if not detection_rule_ids:
|
|
193
|
+
logger.debug('No detections to enrich, skipping detection_rules fetch')
|
|
194
|
+
return
|
|
195
|
+
|
|
192
196
|
detection_rules = cycode_client.get_detection_rules(detection_rule_ids)
|
|
193
197
|
detection_rules_by_id = {detection_rule.detection_rule_id: detection_rule for detection_rule in detection_rules}
|
|
194
198
|
|
|
@@ -53,6 +53,30 @@ SECRET_SCAN_FILE_EXTENSIONS_TO_IGNORE = (
|
|
|
53
53
|
'.iso',
|
|
54
54
|
)
|
|
55
55
|
|
|
56
|
+
# Fallback block-list used for SAST only when the server does not return scannable extensions
|
|
57
|
+
# (e.g. when the customer has custom rules, any text file is scannable). These are non-source
|
|
58
|
+
# data formats that can slip past binary detection (the EICAR test file and ClamAV signature
|
|
59
|
+
# databases are plain ASCII) and may be quarantined by object-storage antivirus after upload.
|
|
60
|
+
SAST_SCAN_FILE_EXTENSIONS_TO_IGNORE = (
|
|
61
|
+
'.bin',
|
|
62
|
+
'.cvd',
|
|
63
|
+
'.cld',
|
|
64
|
+
'.cud',
|
|
65
|
+
'.hdb',
|
|
66
|
+
'.hsb',
|
|
67
|
+
'.mdb',
|
|
68
|
+
'.msb',
|
|
69
|
+
'.ndb',
|
|
70
|
+
'.ndu',
|
|
71
|
+
'.ldb',
|
|
72
|
+
'.ldu',
|
|
73
|
+
'.idb',
|
|
74
|
+
'.fp',
|
|
75
|
+
'.sfp',
|
|
76
|
+
'.ign',
|
|
77
|
+
'.ign2',
|
|
78
|
+
)
|
|
79
|
+
|
|
56
80
|
SCA_CONFIGURATION_SCAN_SUPPORTED_FILES = ( # keep in lowercase
|
|
57
81
|
'cargo.lock',
|
|
58
82
|
'cargo.toml',
|
|
@@ -63,7 +63,10 @@ class Excluder:
|
|
|
63
63
|
}
|
|
64
64
|
self._non_scannable_extensions: dict[str, tuple[str, ...]] = {
|
|
65
65
|
consts.SECRET_SCAN_TYPE: consts.SECRET_SCAN_FILE_EXTENSIONS_TO_IGNORE,
|
|
66
|
+
consts.SAST_SCAN_TYPE: consts.SAST_SCAN_FILE_EXTENSIONS_TO_IGNORE,
|
|
66
67
|
}
|
|
68
|
+
# Tracks scan types for which the SAST fallback log has already been emitted (log once, not per file)
|
|
69
|
+
self._logged_sast_fallback = False
|
|
67
70
|
|
|
68
71
|
def apply_scan_config(self, scan_type: str, scan_config: 'models.ScanConfiguration') -> None:
|
|
69
72
|
if scan_config.scannable_extensions:
|
|
@@ -86,6 +89,11 @@ class Excluder:
|
|
|
86
89
|
|
|
87
90
|
non_scannable_extensions = self._non_scannable_extensions.get(scan_type)
|
|
88
91
|
if non_scannable_extensions:
|
|
92
|
+
# For SAST, reaching the block-list means the server returned no scannable extensions
|
|
93
|
+
# (e.g. custom rules, or no remote config). Log once so this is diagnosable.
|
|
94
|
+
if scan_type == consts.SAST_SCAN_TYPE and not self._logged_sast_fallback:
|
|
95
|
+
self._logged_sast_fallback = True
|
|
96
|
+
logger.debug('No scannable extensions provided for SAST; falling back to the built-in ignore list')
|
|
89
97
|
return not filename.endswith(non_scannable_extensions)
|
|
90
98
|
|
|
91
99
|
return True
|
|
@@ -93,15 +93,21 @@ class AISecurityManagerClient:
|
|
|
93
93
|
|
|
94
94
|
def report_session_context(
|
|
95
95
|
self,
|
|
96
|
-
|
|
96
|
+
hostname: Optional[str] = None,
|
|
97
|
+
platform: Optional[str] = None,
|
|
98
|
+
logged_in_user: Optional[str] = None,
|
|
99
|
+
global_config_file: Optional[dict] = None,
|
|
97
100
|
enabled_plugins: Optional[dict] = None,
|
|
98
101
|
user_email: Optional[str] = None,
|
|
99
102
|
) -> None:
|
|
100
103
|
"""Report session context to the backend."""
|
|
101
104
|
body: dict = {
|
|
102
|
-
'
|
|
103
|
-
'
|
|
105
|
+
'hostname': hostname,
|
|
106
|
+
'platform': platform,
|
|
107
|
+
'logged_in_user': logged_in_user,
|
|
104
108
|
'user_email': user_email,
|
|
109
|
+
'global_config_file': global_config_file,
|
|
110
|
+
'enabled_plugins': enabled_plugins,
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
try:
|
|
@@ -24,19 +24,10 @@ class BaseTokenAuthClient(CycodeClient, ABC):
|
|
|
24
24
|
self.client_id = client_id
|
|
25
25
|
|
|
26
26
|
self._credentials_manager = CredentialsManager()
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
self._access_token = self._expires_in = None
|
|
31
|
-
expected_creator = self._create_jwt_creator()
|
|
32
|
-
if creator == expected_creator:
|
|
33
|
-
# we must be sure that cached access token is created using the same client id and client secret.
|
|
34
|
-
# because client id and client secret could be passed via command, via env vars or via config file.
|
|
35
|
-
# we must not use cached access token if client id or client secret was changed.
|
|
36
|
-
self._access_token = access_token
|
|
37
|
-
self._expires_in = arrow.get(expires_in) if expires_in else None
|
|
38
|
-
|
|
27
|
+
self._access_token = None
|
|
28
|
+
self._expires_in = None
|
|
39
29
|
self._lock = Lock()
|
|
30
|
+
self._load_token_from_disk()
|
|
40
31
|
|
|
41
32
|
def get_access_token(self) -> str:
|
|
42
33
|
with self._lock:
|
|
@@ -51,8 +42,30 @@ class BaseTokenAuthClient(CycodeClient, ABC):
|
|
|
51
42
|
self._credentials_manager.update_access_token(None, None, None)
|
|
52
43
|
|
|
53
44
|
def refresh_access_token_if_needed(self) -> None:
|
|
54
|
-
if self.
|
|
55
|
-
|
|
45
|
+
if self._has_valid_token():
|
|
46
|
+
return
|
|
47
|
+
# Re-check disk before doing the network refresh: another client instance
|
|
48
|
+
# in this process may have already refreshed and persisted a fresh token.
|
|
49
|
+
self._load_token_from_disk()
|
|
50
|
+
if self._has_valid_token():
|
|
51
|
+
return
|
|
52
|
+
self.refresh_access_token()
|
|
53
|
+
|
|
54
|
+
def _has_valid_token(self) -> bool:
|
|
55
|
+
return self._access_token is not None and self._expires_in is not None and arrow.utcnow() < self._expires_in
|
|
56
|
+
|
|
57
|
+
def _load_token_from_disk(self) -> None:
|
|
58
|
+
access_token, expires_in, creator = self._credentials_manager.get_access_token()
|
|
59
|
+
expected_creator = self._create_jwt_creator()
|
|
60
|
+
# We must be sure that cached access token is created using the same client id and client secret.
|
|
61
|
+
# Because client id and client secret could be passed via command, via env vars or via config file.
|
|
62
|
+
# We must not use cached access token if client id or client secret was changed.
|
|
63
|
+
if creator == expected_creator and access_token:
|
|
64
|
+
self._access_token = access_token
|
|
65
|
+
self._expires_in = arrow.get(expires_in) if expires_in else None
|
|
66
|
+
else:
|
|
67
|
+
self._access_token = None
|
|
68
|
+
self._expires_in = None
|
|
56
69
|
|
|
57
70
|
def refresh_access_token(self) -> None:
|
|
58
71
|
auth_response = self._request_new_access_token()
|