cycode 3.13.1.dev1__tar.gz → 3.13.1.dev2__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.13.1.dev1 → cycode-3.13.1.dev2}/PKG-INFO +1 -1
- cycode-3.13.1.dev2/cycode/__init__.py +1 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/__init__.py +12 -11
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/consts.py +3 -3
- cycode-3.13.1.dev2/cycode/cli/apps/ai_guardrails/scan/claude_config.py +159 -0
- cycode-3.13.1.dev2/cycode/cli/apps/ai_guardrails/scan/cursor_config.py +36 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/handlers.py +0 -3
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/payload.py +3 -3
- cycode-3.13.1.dev2/cycode/cli/apps/ai_guardrails/session_start_command.py +150 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/ai_security_manager_client.py +18 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/pyproject.toml +1 -1
- cycode-3.13.1.dev1/cycode/__init__.py +0 -1
- cycode-3.13.1.dev1/cycode/cli/apps/ai_guardrails/ensure_auth_command.py +0 -21
- cycode-3.13.1.dev1/cycode/cli/apps/ai_guardrails/scan/claude_config.py +0 -44
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/LICENCE +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/README.md +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/__main__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/app.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/activation_manager.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/command_utils.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/hooks_manager.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/install_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/consts.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/policy.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/response_builders.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/scan_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/types.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/utils.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/status_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/uninstall_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_remediation/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_remediation/ai_remediation_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_remediation/apply_fix.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_remediation/print_remediation.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/api/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/api/api_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/api/openapi_spec.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/auth/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/auth/auth_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/auth/auth_common.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/auth/auth_manager.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/auth/models.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/configure/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/configure/configure_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/configure/consts.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/configure/messages.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/configure/prompts.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ignore/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ignore/ignore_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/mcp/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/mcp/mcp_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/report_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/common.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/path/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/path/path_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/repository_url/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/repository_url/repository_url_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/sbom_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report/sbom/sbom_report_file.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report_import/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report_import/report_import_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report_import/sbom/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/report_import/sbom/sbom_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/sca_options.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/aggregation_report.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/code_scanner.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/commit_history/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/commit_history/commit_history_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/commit_range_scanner.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/detection_excluder.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/path/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/path/path_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_commit/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_commit/pre_commit_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_push/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_push/pre_push_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_receive/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/pre_receive/pre_receive_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/remote_url_resolver.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/repository/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/repository/repository_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_ci/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_ci/ci_integrations.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_ci/scan_ci_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_parameters.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/scan/scan_result.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/status/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/status/get_cli_status.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/status/models.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/status/status_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/status/version_command.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/cli_types.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/config.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/console.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/consts.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/custom_exceptions.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/handle_ai_remediation_errors.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/handle_auth_errors.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/handle_errors.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/handle_report_sbom_errors.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/exceptions/handle_scan_errors.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/commit_range_documents.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/documents_walk_ignore.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/file_excluder.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/iac/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/models/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/path_documents.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/repository_documents.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/base_restore_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/go/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/maven/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/npm/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/nuget/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/php/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/python/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/ruby/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/ruby/restore_ruby_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/sbt/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/sca/sca_file_collector.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/walk_ignore.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/files_collector/zip_documents.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/logger.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/main.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/models.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/console_printer.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/json_printer.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/printer_base.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/rich_printer.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/sca_table_printer.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/table.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/table_models.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/table_printer.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/tables/table_printer_base.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/text_printer.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/code_snippet_syntax.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/detection_data.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/detection_ordering/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/detection_ordering/common_ordering.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/detection_ordering/sca_ordering.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/printers/utils/rich_helpers.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/base_file_manager.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/config_file_manager.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/configuration_manager.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/credentials_manager.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/user_settings/jwt_creator.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/binary_utils.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/enum_utils.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/get_api_client.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/git_proxy.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/ignore_utils.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/jwt_utils.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/path_utils.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/progress_bar.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/scan_batch.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/scan_utils.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/shell_executor.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/string_utils.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/task_timer.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/url_utils.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/version_checker.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/utils/yaml_utils.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/config.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/__init__.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/ai_security_manager_service_config.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/auth_client.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/base_token_auth_client.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cli_activation_client.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/client_creator.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/config.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/config_dev.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cycode_client.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cycode_client_base.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cycode_dev_based_client.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cycode_oidc_based_client.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/cycode_token_based_client.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/headers.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/import_sbom_client.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/logger.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/models.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/report_client.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/scan_client.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cyclient/scan_config_base.py +0 -0
- {cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/logger.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '3.13.1.dev2' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import typer
|
|
2
2
|
|
|
3
|
-
from cycode.cli.apps.ai_guardrails.
|
|
4
|
-
from cycode.cli.apps.ai_guardrails.
|
|
5
|
-
from cycode.cli.apps.ai_guardrails.
|
|
6
|
-
from cycode.cli.apps.ai_guardrails.status_command import status_command
|
|
7
|
-
from cycode.cli.apps.ai_guardrails.uninstall_command import uninstall_command
|
|
3
|
+
from cycode.cli.apps.ai_guardrails.install_command import install_command as _install_command
|
|
4
|
+
from cycode.cli.apps.ai_guardrails.scan.scan_command import scan_command as _scan_command
|
|
5
|
+
from cycode.cli.apps.ai_guardrails.session_start_command import session_start_command as _session_start_command
|
|
6
|
+
from cycode.cli.apps.ai_guardrails.status_command import status_command as _status_command
|
|
7
|
+
from cycode.cli.apps.ai_guardrails.uninstall_command import uninstall_command as _uninstall_command
|
|
8
8
|
|
|
9
9
|
app = typer.Typer(name='ai-guardrails', no_args_is_help=True, hidden=True)
|
|
10
10
|
|
|
11
|
-
app.command(hidden=True, name='install', short_help='Install AI guardrails hooks for supported IDEs.')(
|
|
11
|
+
app.command(hidden=True, name='install', short_help='Install AI guardrails hooks for supported IDEs.')(_install_command)
|
|
12
12
|
app.command(hidden=True, name='uninstall', short_help='Remove AI guardrails hooks from supported IDEs.')(
|
|
13
|
-
|
|
13
|
+
_uninstall_command
|
|
14
14
|
)
|
|
15
|
-
app.command(hidden=True, name='status', short_help='Show AI guardrails hook installation status.')(
|
|
15
|
+
app.command(hidden=True, name='status', short_help='Show AI guardrails hook installation status.')(_status_command)
|
|
16
16
|
app.command(
|
|
17
17
|
hidden=True,
|
|
18
18
|
name='scan',
|
|
19
19
|
short_help='Scan content from AI IDE hooks for secrets (reads JSON from stdin).',
|
|
20
|
-
)(
|
|
21
|
-
app.command(hidden=True, name='
|
|
22
|
-
|
|
20
|
+
)(_scan_command)
|
|
21
|
+
app.command(hidden=True, name='session-start', short_help='Handle session start: auth, conversation, session context.')(
|
|
22
|
+
_session_start_command
|
|
23
23
|
)
|
|
24
|
+
app.command(hidden=True, name='ensure-auth', short_help='[Deprecated] Alias for session-start.')(_session_start_command)
|
|
@@ -84,7 +84,7 @@ DEFAULT_IDE = AIIDEType.CURSOR
|
|
|
84
84
|
|
|
85
85
|
# Command used in hooks
|
|
86
86
|
CYCODE_SCAN_PROMPT_COMMAND = 'cycode ai-guardrails scan'
|
|
87
|
-
|
|
87
|
+
CYCODE_SESSION_START_COMMAND = 'cycode ai-guardrails session-start'
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
def _get_cursor_hooks_config(async_mode: bool = False) -> dict:
|
|
@@ -92,7 +92,7 @@ def _get_cursor_hooks_config(async_mode: bool = False) -> dict:
|
|
|
92
92
|
config = IDE_CONFIGS[AIIDEType.CURSOR]
|
|
93
93
|
command = f'{CYCODE_SCAN_PROMPT_COMMAND} &' if async_mode else CYCODE_SCAN_PROMPT_COMMAND
|
|
94
94
|
hooks = {event: [{'command': command}] for event in config.hook_events}
|
|
95
|
-
hooks['sessionStart'] = [{'command':
|
|
95
|
+
hooks['sessionStart'] = [{'command': f'{CYCODE_SESSION_START_COMMAND} --ide cursor'}]
|
|
96
96
|
|
|
97
97
|
return {
|
|
98
98
|
'version': 1,
|
|
@@ -119,7 +119,7 @@ def _get_claude_code_hooks_config(async_mode: bool = False) -> dict:
|
|
|
119
119
|
'SessionStart': [
|
|
120
120
|
{
|
|
121
121
|
'matcher': 'startup',
|
|
122
|
-
'hooks': [{'type': 'command', 'command':
|
|
122
|
+
'hooks': [{'type': 'command', 'command': f'{CYCODE_SESSION_START_COMMAND} --ide claude-code'}],
|
|
123
123
|
}
|
|
124
124
|
],
|
|
125
125
|
'UserPromptSubmit': [
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Reader for ~/.claude.json configuration file.
|
|
2
|
+
|
|
3
|
+
Extracts user email from the Claude Code global config file
|
|
4
|
+
for use in AI guardrails scan enrichment.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from cycode.logger import get_logger
|
|
12
|
+
|
|
13
|
+
logger = get_logger('AI Guardrails Claude Config')
|
|
14
|
+
|
|
15
|
+
_CLAUDE_CONFIG_PATH = Path.home() / '.claude.json'
|
|
16
|
+
_CLAUDE_SETTINGS_PATH = Path.home() / '.claude' / 'settings.json'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def load_claude_config(config_path: Optional[Path] = None) -> Optional[dict]:
|
|
20
|
+
"""Load and parse ~/.claude.json.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
config_path: Override path for testing. Defaults to ~/.claude.json.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Parsed dict or None if file is missing or invalid.
|
|
27
|
+
"""
|
|
28
|
+
path = config_path or _CLAUDE_CONFIG_PATH
|
|
29
|
+
if not path.exists():
|
|
30
|
+
logger.debug('Claude config file not found', extra={'path': str(path)})
|
|
31
|
+
return None
|
|
32
|
+
try:
|
|
33
|
+
content = path.read_text(encoding='utf-8')
|
|
34
|
+
return json.loads(content)
|
|
35
|
+
except Exception as e:
|
|
36
|
+
logger.debug('Failed to load Claude config file', exc_info=e)
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_user_email(config: dict) -> Optional[str]:
|
|
41
|
+
"""Extract user email from Claude config.
|
|
42
|
+
|
|
43
|
+
Reads oauthAccount.emailAddress from the config dict.
|
|
44
|
+
"""
|
|
45
|
+
return config.get('oauthAccount', {}).get('emailAddress')
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_mcp_servers(config: dict) -> Optional[dict]:
|
|
49
|
+
"""Extract MCP servers from Claude config.
|
|
50
|
+
|
|
51
|
+
Reads mcpServers from the config dict.
|
|
52
|
+
"""
|
|
53
|
+
return config.get('mcpServers')
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def load_claude_settings(settings_path: Optional[Path] = None) -> Optional[dict]:
|
|
57
|
+
"""Load and parse ~/.claude/settings.json.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
settings_path: Override path for testing. Defaults to ~/.claude/settings.json.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Parsed dict or None if file is missing or invalid.
|
|
64
|
+
"""
|
|
65
|
+
path = settings_path or _CLAUDE_SETTINGS_PATH
|
|
66
|
+
if not path.exists():
|
|
67
|
+
logger.debug('Claude settings file not found', extra={'path': str(path)})
|
|
68
|
+
return None
|
|
69
|
+
try:
|
|
70
|
+
content = path.read_text(encoding='utf-8')
|
|
71
|
+
return json.loads(content)
|
|
72
|
+
except Exception as e:
|
|
73
|
+
logger.debug('Failed to load Claude settings file', exc_info=e)
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _resolve_marketplace_path(marketplace: dict) -> Optional[Path]:
|
|
78
|
+
"""
|
|
79
|
+
Resolve filesystem path for a directory-type marketplace.
|
|
80
|
+
"""
|
|
81
|
+
source = marketplace.get('source', {})
|
|
82
|
+
if source.get('source') != 'directory':
|
|
83
|
+
return None
|
|
84
|
+
raw = source.get('path')
|
|
85
|
+
if not raw:
|
|
86
|
+
return None
|
|
87
|
+
path = Path(raw)
|
|
88
|
+
return path if path.is_dir() else None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _load_plugin_json_file(plugin_path: Path, relative_path: str) -> Optional[dict]:
|
|
92
|
+
"""Load and parse a JSON file inside a plugin directory.
|
|
93
|
+
|
|
94
|
+
Returns None if the file is missing, unreadable, or has invalid JSON.
|
|
95
|
+
"""
|
|
96
|
+
target = plugin_path / relative_path
|
|
97
|
+
if not target.exists():
|
|
98
|
+
return None
|
|
99
|
+
try:
|
|
100
|
+
return json.loads(target.read_text(encoding='utf-8'))
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.debug('Failed to load plugin file', extra={'path': str(target)}, exc_info=e)
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def resolve_plugins(settings: dict) -> tuple[dict, dict]:
|
|
107
|
+
"""Resolve enabled plugins to their MCP servers and metadata.
|
|
108
|
+
|
|
109
|
+
Walks enabledPlugins from claude settings, resolves each plugin's 'marketplace' directory
|
|
110
|
+
via the 'extraKnownMarketplaces' field, and reads:
|
|
111
|
+
- <path>/.mcp.json for MCP servers (merged into a flat dict)
|
|
112
|
+
- <path>/.claude-plugin/plugin.json for metadata (name, version, description)
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
settings: Parsed ~/.claude/settings.json dict.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Tuple of (merged_mcp_servers, enriched_plugins):
|
|
119
|
+
- merged_mcp_servers: {server_name: server_config, ...}
|
|
120
|
+
- enriched_plugins: {plugin_key: {"enabled": True, "name": ..., ...}, ...}
|
|
121
|
+
"""
|
|
122
|
+
enabled = settings.get('enabledPlugins') or {}
|
|
123
|
+
marketplaces = settings.get('extraKnownMarketplaces') or {}
|
|
124
|
+
merged_mcp: dict = {}
|
|
125
|
+
enriched: dict = {}
|
|
126
|
+
|
|
127
|
+
for plugin_key, is_enabled in enabled.items():
|
|
128
|
+
if not is_enabled:
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
entry: dict = {'enabled': True}
|
|
132
|
+
enriched[plugin_key] = entry
|
|
133
|
+
|
|
134
|
+
if '@' not in plugin_key:
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
_plugin_name, marketplace_name = plugin_key.split('@', 1)
|
|
138
|
+
marketplace = marketplaces.get(marketplace_name)
|
|
139
|
+
if not marketplace:
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
plugin_path = _resolve_marketplace_path(marketplace)
|
|
143
|
+
if plugin_path is None:
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
metadata = _load_plugin_json_file(plugin_path, '.claude-plugin/plugin.json') or {}
|
|
147
|
+
for field in ('name', 'version', 'description'):
|
|
148
|
+
if field in metadata:
|
|
149
|
+
entry[field] = metadata[field]
|
|
150
|
+
|
|
151
|
+
mcp_config = _load_plugin_json_file(plugin_path, '.mcp.json') or {}
|
|
152
|
+
plugin_server_names = []
|
|
153
|
+
for server_name, server_cfg in (mcp_config.get('mcpServers') or {}).items():
|
|
154
|
+
merged_mcp[server_name] = server_cfg
|
|
155
|
+
plugin_server_names.append(server_name)
|
|
156
|
+
if plugin_server_names:
|
|
157
|
+
entry['mcp_server_names'] = plugin_server_names
|
|
158
|
+
|
|
159
|
+
return merged_mcp, enriched
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Reader for ~/.cursor/mcp.json configuration file.
|
|
2
|
+
|
|
3
|
+
Extracts MCP server definitions from the Cursor global config file
|
|
4
|
+
for use in AI guardrails session-context reporting.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from cycode.logger import get_logger
|
|
12
|
+
|
|
13
|
+
logger = get_logger('AI Guardrails Cursor Config')
|
|
14
|
+
|
|
15
|
+
_CURSOR_MCP_CONFIG_PATH = Path.home() / '.cursor' / 'mcp.json'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_cursor_config(config_path: Optional[Path] = None) -> Optional[dict]:
|
|
19
|
+
"""Load and parse ~/.cursor/mcp.json.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
config_path: Override path for testing. Defaults to ~/.cursor/mcp.json.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Parsed dict or None if file is missing or invalid.
|
|
26
|
+
"""
|
|
27
|
+
path = config_path or _CURSOR_MCP_CONFIG_PATH
|
|
28
|
+
if not path.exists():
|
|
29
|
+
logger.debug('Cursor MCP config file not found', extra={'path': str(path)})
|
|
30
|
+
return None
|
|
31
|
+
try:
|
|
32
|
+
content = path.read_text(encoding='utf-8')
|
|
33
|
+
return json.loads(content)
|
|
34
|
+
except Exception as e:
|
|
35
|
+
logger.debug('Failed to load Cursor MCP config file', exc_info=e)
|
|
36
|
+
return None
|
|
@@ -42,7 +42,6 @@ def handle_before_submit_prompt(ctx: typer.Context, payload: AIHookPayload, poli
|
|
|
42
42
|
response_builder = get_response_builder(ide)
|
|
43
43
|
|
|
44
44
|
prompt_config = get_policy_value(policy, 'prompt', default={})
|
|
45
|
-
ai_client.create_conversation(payload)
|
|
46
45
|
if not get_policy_value(prompt_config, 'enabled', default=True):
|
|
47
46
|
ai_client.create_event(payload, AiHookEventType.PROMPT, AIHookOutcome.ALLOWED)
|
|
48
47
|
return response_builder.allow_prompt()
|
|
@@ -100,7 +99,6 @@ def handle_before_read_file(ctx: typer.Context, payload: AIHookPayload, policy:
|
|
|
100
99
|
response_builder = get_response_builder(ide)
|
|
101
100
|
|
|
102
101
|
file_read_config = get_policy_value(policy, 'file_read', default={})
|
|
103
|
-
ai_client.create_conversation(payload)
|
|
104
102
|
if not get_policy_value(file_read_config, 'enabled', default=True):
|
|
105
103
|
ai_client.create_event(payload, AiHookEventType.FILE_READ, AIHookOutcome.ALLOWED)
|
|
106
104
|
return response_builder.allow_permission()
|
|
@@ -203,7 +201,6 @@ def handle_before_mcp_execution(ctx: typer.Context, payload: AIHookPayload, poli
|
|
|
203
201
|
response_builder = get_response_builder(ide)
|
|
204
202
|
|
|
205
203
|
mcp_config = get_policy_value(policy, 'mcp', default={})
|
|
206
|
-
ai_client.create_conversation(payload)
|
|
207
204
|
if not get_policy_value(mcp_config, 'enabled', default=True):
|
|
208
205
|
ai_client.create_event(payload, AiHookEventType.MCP_EXECUTION, AIHookOutcome.ALLOWED)
|
|
209
206
|
return response_builder.allow_permission()
|
|
@@ -71,7 +71,7 @@ def _extract_generation_id(entry: dict) -> Optional[str]:
|
|
|
71
71
|
return None
|
|
72
72
|
|
|
73
73
|
|
|
74
|
-
def
|
|
74
|
+
def extract_from_claude_transcript(
|
|
75
75
|
transcript_path: str,
|
|
76
76
|
) -> tuple[Optional[str], Optional[str], Optional[str]]:
|
|
77
77
|
"""Extract IDE version, model, and latest generation ID from Claude Code transcript file.
|
|
@@ -123,7 +123,7 @@ class AIHookPayload:
|
|
|
123
123
|
"""Unified payload object that normalizes field names from different AI tools."""
|
|
124
124
|
|
|
125
125
|
# Event identification
|
|
126
|
-
event_name: str # Canonical event type (e.g., 'prompt', 'file_read', 'mcp_execution')
|
|
126
|
+
event_name: Optional[str] = None # Canonical event type (e.g., 'prompt', 'file_read', 'mcp_execution')
|
|
127
127
|
conversation_id: Optional[str] = None
|
|
128
128
|
generation_id: Optional[str] = None
|
|
129
129
|
|
|
@@ -206,7 +206,7 @@ class AIHookPayload:
|
|
|
206
206
|
mcp_tool_name = parts[2]
|
|
207
207
|
|
|
208
208
|
# Extract IDE version, model, and generation ID from transcript file
|
|
209
|
-
ide_version, model, generation_id =
|
|
209
|
+
ide_version, model, generation_id = extract_from_claude_transcript(payload.get('transcript_path'))
|
|
210
210
|
|
|
211
211
|
# Extract user email from ~/.claude.json
|
|
212
212
|
claude_config = load_claude_config()
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from typing import TYPE_CHECKING, Annotated
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
|
|
6
|
+
from cycode.cli.apps.ai_guardrails.consts import AIIDEType
|
|
7
|
+
from cycode.cli.apps.ai_guardrails.scan.claude_config import (
|
|
8
|
+
get_mcp_servers,
|
|
9
|
+
get_user_email,
|
|
10
|
+
load_claude_config,
|
|
11
|
+
load_claude_settings,
|
|
12
|
+
resolve_plugins,
|
|
13
|
+
)
|
|
14
|
+
from cycode.cli.apps.ai_guardrails.scan.cursor_config import load_cursor_config
|
|
15
|
+
from cycode.cli.apps.ai_guardrails.scan.payload import AIHookPayload, extract_from_claude_transcript
|
|
16
|
+
from cycode.cli.apps.ai_guardrails.scan.utils import safe_json_parse
|
|
17
|
+
from cycode.cli.apps.auth.auth_common import get_authorization_info
|
|
18
|
+
from cycode.cli.apps.auth.auth_manager import AuthManager
|
|
19
|
+
from cycode.cli.exceptions.handle_auth_errors import handle_auth_exception
|
|
20
|
+
from cycode.cli.utils.get_api_client import get_ai_security_manager_client
|
|
21
|
+
from cycode.logger import get_logger
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from cycode.cyclient.ai_security_manager_client import AISecurityManagerClient
|
|
25
|
+
|
|
26
|
+
logger = get_logger('AI Guardrails')
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _build_session_payload(payload: dict, ide: str) -> AIHookPayload:
|
|
30
|
+
"""Build an AIHookPayload from a session-start stdin payload."""
|
|
31
|
+
if ide == AIIDEType.CLAUDE_CODE:
|
|
32
|
+
claude_config = load_claude_config()
|
|
33
|
+
ide_user_email = get_user_email(claude_config) if claude_config else None
|
|
34
|
+
ide_version, _, _ = extract_from_claude_transcript(payload.get('transcript_path'))
|
|
35
|
+
|
|
36
|
+
return AIHookPayload(
|
|
37
|
+
conversation_id=payload.get('session_id'),
|
|
38
|
+
ide_user_email=ide_user_email,
|
|
39
|
+
model=payload.get('model'),
|
|
40
|
+
ide_provider=AIIDEType.CLAUDE_CODE.value,
|
|
41
|
+
ide_version=ide_version,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Cursor
|
|
45
|
+
return AIHookPayload(
|
|
46
|
+
conversation_id=payload.get('conversation_id'),
|
|
47
|
+
ide_user_email=payload.get('user_email'),
|
|
48
|
+
model=payload.get('model'),
|
|
49
|
+
ide_provider=AIIDEType.CURSOR.value,
|
|
50
|
+
ide_version=payload.get('cursor_version'),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _get_claude_code_session_context() -> tuple[dict, dict]:
|
|
55
|
+
"""Return (mcp_servers, enabled_plugins) for Claude Code.
|
|
56
|
+
|
|
57
|
+
Merges MCP servers from ~/.claude.json (user-configured) with those contributed
|
|
58
|
+
by enabled plugins. Plugin metadata (name, version, description) is included in
|
|
59
|
+
the enabled_plugins dict when resolvable.
|
|
60
|
+
"""
|
|
61
|
+
config = load_claude_config()
|
|
62
|
+
mcp_servers = dict(get_mcp_servers(config) or {}) if config else {}
|
|
63
|
+
|
|
64
|
+
settings = load_claude_settings()
|
|
65
|
+
if settings:
|
|
66
|
+
plugin_mcp, enriched_plugins = resolve_plugins(settings)
|
|
67
|
+
mcp_servers.update(plugin_mcp)
|
|
68
|
+
else:
|
|
69
|
+
enriched_plugins = {}
|
|
70
|
+
|
|
71
|
+
return mcp_servers, enriched_plugins
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _get_cursor_session_context() -> tuple[dict, dict]:
|
|
75
|
+
"""Return (mcp_servers, enabled_plugins) for Cursor. Cursor has no plugin system."""
|
|
76
|
+
config = load_cursor_config()
|
|
77
|
+
mcp_servers = dict(get_mcp_servers(config) or {}) if config else {}
|
|
78
|
+
return mcp_servers, {}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _report_session_context(ai_client: 'AISecurityManagerClient', ide: str) -> None:
|
|
82
|
+
"""Report IDE session context to the AI security manager. Never raises."""
|
|
83
|
+
try:
|
|
84
|
+
if ide == AIIDEType.CLAUDE_CODE:
|
|
85
|
+
mcp_servers, enabled_plugins = _get_claude_code_session_context()
|
|
86
|
+
elif ide == AIIDEType.CURSOR:
|
|
87
|
+
mcp_servers, enabled_plugins = _get_cursor_session_context()
|
|
88
|
+
else:
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
if not mcp_servers and not enabled_plugins:
|
|
92
|
+
return
|
|
93
|
+
ai_client.report_session_context(mcp_servers=mcp_servers, enabled_plugins=enabled_plugins)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.debug('Failed to report session context', exc_info=e)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def session_start_command(
|
|
99
|
+
ctx: typer.Context,
|
|
100
|
+
ide: Annotated[
|
|
101
|
+
str,
|
|
102
|
+
typer.Option(
|
|
103
|
+
'--ide',
|
|
104
|
+
help='IDE that triggered the session start.',
|
|
105
|
+
hidden=True,
|
|
106
|
+
),
|
|
107
|
+
] = AIIDEType.CURSOR.value,
|
|
108
|
+
) -> None:
|
|
109
|
+
"""Handle session start: ensure auth, create conversation, report session context."""
|
|
110
|
+
# Step 1: Ensure authentication
|
|
111
|
+
auth_info = get_authorization_info(ctx)
|
|
112
|
+
if auth_info is None:
|
|
113
|
+
logger.debug('Not authenticated, starting authentication')
|
|
114
|
+
try:
|
|
115
|
+
auth_manager = AuthManager()
|
|
116
|
+
auth_manager.authenticate()
|
|
117
|
+
except Exception as err:
|
|
118
|
+
handle_auth_exception(ctx, err)
|
|
119
|
+
return
|
|
120
|
+
else:
|
|
121
|
+
logger.debug('Already authenticated')
|
|
122
|
+
|
|
123
|
+
# Step 2: Read stdin payload (backward compat: old hooks pipe no stdin)
|
|
124
|
+
if sys.stdin.isatty():
|
|
125
|
+
logger.debug('No stdin payload (TTY), skipping session initialization')
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
stdin_data = sys.stdin.read().strip()
|
|
129
|
+
payload = safe_json_parse(stdin_data)
|
|
130
|
+
if not payload:
|
|
131
|
+
logger.debug('Empty or invalid stdin payload, skipping session initialization')
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
# Step 3: Build session payload and initialize API client
|
|
135
|
+
session_payload = _build_session_payload(payload, ide)
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
ai_client = get_ai_security_manager_client(ctx)
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.debug('Failed to initialize AI security client', exc_info=e)
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
# Step 4: Create conversation
|
|
144
|
+
try:
|
|
145
|
+
ai_client.create_conversation(session_payload)
|
|
146
|
+
except Exception as e:
|
|
147
|
+
logger.debug('Failed to create conversation during session start', exc_info=e)
|
|
148
|
+
|
|
149
|
+
# Step 5: Report session context (MCP servers)
|
|
150
|
+
_report_session_context(ai_client, ide)
|
|
@@ -17,6 +17,7 @@ class AISecurityManagerClient:
|
|
|
17
17
|
|
|
18
18
|
_CONVERSATIONS_PATH = 'v4/ai-security/interactions/conversations'
|
|
19
19
|
_EVENTS_PATH = 'v4/ai-security/interactions/events'
|
|
20
|
+
_SESSION_CONTEXT_PATH = 'v4/ai-security/interactions/session-context'
|
|
20
21
|
|
|
21
22
|
def __init__(self, client: CycodeClientBase, service_config: 'AISecurityManagerServiceConfigBase') -> None:
|
|
22
23
|
self.client = client
|
|
@@ -88,3 +89,20 @@ class AISecurityManagerClient:
|
|
|
88
89
|
except Exception as e:
|
|
89
90
|
logger.debug('Failed to create AI hook event', exc_info=e)
|
|
90
91
|
# Don't fail the hook if tracking fails
|
|
92
|
+
|
|
93
|
+
def report_session_context(
|
|
94
|
+
self,
|
|
95
|
+
mcp_servers: Optional[dict] = None,
|
|
96
|
+
enabled_plugins: Optional[dict] = None,
|
|
97
|
+
) -> None:
|
|
98
|
+
"""Report session context to the backend."""
|
|
99
|
+
body: dict = {
|
|
100
|
+
'mcp_servers': mcp_servers,
|
|
101
|
+
'enabled_plugins': enabled_plugins,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
self.client.post(self._build_endpoint_path(self._SESSION_CONTEXT_PATH), body=body)
|
|
106
|
+
except Exception as e:
|
|
107
|
+
logger.debug('Failed to report session context', exc_info=e)
|
|
108
|
+
# Don't fail the session if reporting fails
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '3.13.1.dev1' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import typer
|
|
2
|
-
|
|
3
|
-
from cycode.cli.apps.auth.auth_common import get_authorization_info
|
|
4
|
-
from cycode.cli.apps.auth.auth_manager import AuthManager
|
|
5
|
-
from cycode.cli.exceptions.handle_auth_errors import handle_auth_exception
|
|
6
|
-
from cycode.cli.logger import logger
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def ensure_auth_command(ctx: typer.Context) -> None:
|
|
10
|
-
"""Ensure the user is authenticated, triggering authentication if needed."""
|
|
11
|
-
auth_info = get_authorization_info(ctx)
|
|
12
|
-
if auth_info is not None:
|
|
13
|
-
logger.debug('Already authenticated')
|
|
14
|
-
return
|
|
15
|
-
|
|
16
|
-
logger.debug('Not authenticated, starting authentication')
|
|
17
|
-
try:
|
|
18
|
-
auth_manager = AuthManager()
|
|
19
|
-
auth_manager.authenticate()
|
|
20
|
-
except Exception as err:
|
|
21
|
-
handle_auth_exception(ctx, err)
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"""Reader for ~/.claude.json configuration file.
|
|
2
|
-
|
|
3
|
-
Extracts user email from the Claude Code global config file
|
|
4
|
-
for use in AI guardrails scan enrichment.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import json
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from typing import Optional
|
|
10
|
-
|
|
11
|
-
from cycode.logger import get_logger
|
|
12
|
-
|
|
13
|
-
logger = get_logger('AI Guardrails Claude Config')
|
|
14
|
-
|
|
15
|
-
_CLAUDE_CONFIG_PATH = Path.home() / '.claude.json'
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def load_claude_config(config_path: Optional[Path] = None) -> Optional[dict]:
|
|
19
|
-
"""Load and parse ~/.claude.json.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
config_path: Override path for testing. Defaults to ~/.claude.json.
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
Parsed dict or None if file is missing or invalid.
|
|
26
|
-
"""
|
|
27
|
-
path = config_path or _CLAUDE_CONFIG_PATH
|
|
28
|
-
if not path.exists():
|
|
29
|
-
logger.debug('Claude config file not found', extra={'path': str(path)})
|
|
30
|
-
return None
|
|
31
|
-
try:
|
|
32
|
-
content = path.read_text(encoding='utf-8')
|
|
33
|
-
return json.loads(content)
|
|
34
|
-
except Exception as e:
|
|
35
|
-
logger.debug('Failed to load Claude config file', exc_info=e)
|
|
36
|
-
return None
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def get_user_email(config: dict) -> Optional[str]:
|
|
40
|
-
"""Extract user email from Claude config.
|
|
41
|
-
|
|
42
|
-
Reads oauthAccount.emailAddress from the config dict.
|
|
43
|
-
"""
|
|
44
|
-
return config.get('oauthAccount', {}).get('emailAddress')
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/response_builders.py
RENAMED
|
File without changes
|
{cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/scan/scan_command.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_guardrails/uninstall_command.py
RENAMED
|
File without changes
|
|
File without changes
|
{cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/cycode/cli/apps/ai_remediation/ai_remediation_command.py
RENAMED
|
File without changes
|
|
File without changes
|
{cycode-3.13.1.dev1 → cycode-3.13.1.dev2}/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
|