cycode 3.25.1.dev3__tar.gz → 3.25.1.dev5__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.
Potentially problematic release.
This version of cycode might be problematic. Click here for more details.
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/PKG-INFO +1 -1
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/__init__.py +1 -1
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/ides/codex.py +9 -12
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/npm/restore_bun_dependencies.py +10 -3
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/npm/restore_deno_dependencies.py +1 -1
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py +32 -13
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/npm/restore_pnpm_dependencies.py +10 -3
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/npm/restore_yarn_dependencies.py +10 -3
- cycode-3.25.1.dev5/cycode/cli/files_collector/sca/npm/workspace/__init__.py +49 -0
- cycode-3.25.1.dev5/cycode/cli/files_collector/sca/npm/workspace/coverage.py +142 -0
- cycode-3.25.1.dev5/cycode/cli/files_collector/sca/npm/workspace/files.py +38 -0
- cycode-3.25.1.dev5/cycode/cli/files_collector/sca/npm/workspace/globs.py +116 -0
- cycode-3.25.1.dev5/cycode/cli/files_collector/sca/npm/workspace/names.py +16 -0
- cycode-3.25.1.dev5/cycode/cli/files_collector/sca/npm/workspace/resolvers.py +294 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/sca_file_collector.py +3 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/path_utils.py +31 -4
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/pyproject.toml +1 -1
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/LICENCE +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/README.md +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/__main__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/app.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/activation_manager.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/command_utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/consts.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/hooks_manager.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/ides/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/ides/_plugin_utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/ides/_skill_utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/ides/base.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/ides/claude_code.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/ides/copilot.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/ides/cursor.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/install_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/scan/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/scan/consts.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/scan/detach.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/scan/guardrail_config.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/scan/handlers.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/scan/payload.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/scan/policy.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/scan/scan_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/scan/types.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/scan/utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/session_start_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/status_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_guardrails/uninstall_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_remediation/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_remediation/ai_remediation_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_remediation/apply_fix.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ai_remediation/print_remediation.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/api/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/api/api_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/api/openapi_spec.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/auth/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/auth/auth_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/auth/auth_common.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/auth/auth_manager.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/auth/models.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/configure/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/configure/configure_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/configure/consts.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/configure/messages.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/configure/prompts.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ignore/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/ignore/ignore_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/mcp/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/mcp/mcp_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report/report_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report/sbom/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report/sbom/common.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report/sbom/path/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report/sbom/path/path_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report/sbom/repository_url/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report/sbom/repository_url/repository_url_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report/sbom/sbom_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report/sbom/sbom_report_file.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report_import/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report_import/report_import_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report_import/sbom/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/report_import/sbom/sbom_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/sca_options.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/aggregation_report.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/code_scanner.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/commit_history/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/commit_history/commit_history_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/commit_range_scanner.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/detection_excluder.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/path/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/path/path_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/pre_commit/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/pre_commit/pre_commit_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/pre_push/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/pre_push/pre_push_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/pre_receive/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/pre_receive/pre_receive_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/remote_url_resolver.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/repository/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/repository/repository_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/scan_ci/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/scan_ci/ci_integrations.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/scan_ci/scan_ci_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/scan_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/scan_parameters.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/scan/scan_result.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/status/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/status/get_cli_status.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/status/models.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/status/status_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/apps/status/version_command.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/cli_types.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/config.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/console.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/consts.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/exceptions/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/exceptions/custom_exceptions.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/exceptions/handle_ai_remediation_errors.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/exceptions/handle_auth_errors.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/exceptions/handle_errors.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/exceptions/handle_report_sbom_errors.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/exceptions/handle_scan_errors.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/commit_range_documents.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/documents_walk_ignore.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/file_excluder.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/iac/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/iac/tf_content_generator.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/models/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/models/in_memory_zip.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/path_documents.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/repository_documents.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/base_restore_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/go/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/go/restore_go_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/maven/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/maven/restore_gradle_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/maven/restore_maven_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/npm/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/nuget/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/nuget/restore_nuget_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/php/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/php/restore_composer_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/python/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/python/restore_pip_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/python/restore_pipenv_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/python/restore_poetry_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/python/restore_uv_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/ruby/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/ruby/restore_ruby_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/sbt/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/sca/sbt/restore_sbt_dependencies.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/walk_ignore.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/files_collector/zip_documents.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/logger.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/main.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/models.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/console_printer.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/json_printer.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/printer_base.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/rich_printer.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/tables/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/tables/sca_table_printer.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/tables/table.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/tables/table_models.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/tables/table_printer.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/tables/table_printer_base.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/text_printer.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/utils/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/utils/code_snippet_syntax.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/utils/detection_data.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/utils/detection_ordering/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/utils/detection_ordering/common_ordering.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/utils/detection_ordering/sca_ordering.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/utils/rich_helpers.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/utils/sca_ossf.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/printers/utils/sca_policy_details.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/user_settings/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/user_settings/base_file_manager.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/user_settings/config_file_manager.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/user_settings/configuration_manager.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/user_settings/credentials_manager.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/user_settings/jwt_creator.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/binary_utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/enum_utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/get_api_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/git_proxy.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/host_info.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/ignore_utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/jwt_utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/progress_bar.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/scan_batch.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/scan_utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/shell_executor.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/string_utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/task_timer.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/trust_store.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/url_utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/version_checker.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cli/utils/yaml_utils.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/config.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/__init__.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/ai_security_manager_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/ai_security_manager_service_config.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/auth_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/base_token_auth_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/cli_activation_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/client_creator.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/config.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/config_dev.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/cycode_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/cycode_client_base.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/cycode_dev_based_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/cycode_oidc_based_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/cycode_token_based_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/headers.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/import_sbom_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/logger.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/models.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/report_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/scan_client.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/cyclient/scan_config_base.py +0 -0
- {cycode-3.25.1.dev3 → cycode-3.25.1.dev5}/cycode/logger.py +0 -0
|
@@ -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.25.1.
|
|
8
|
+
__version__ = '3.25.1.dev5' # DON'T TOUCH. Placeholder. Will be filled automatically on poetry build from Git Tag
|
|
@@ -269,21 +269,18 @@ class Codex(IDE):
|
|
|
269
269
|
)
|
|
270
270
|
|
|
271
271
|
def build_hook_response(self, decision: HookDecision) -> dict:
|
|
272
|
-
# Codex
|
|
273
|
-
# -
|
|
274
|
-
#
|
|
272
|
+
# Codex hook response shapes:
|
|
273
|
+
# - allow: empty (no objection). Older Codex runtimes reject an explicit
|
|
274
|
+
# PreToolUse permissionDecision "allow" as unsupported and report a
|
|
275
|
+
# failed hook on every call, so never send it.
|
|
276
|
+
# - PROMPT deny: {"decision": "block", "reason": ...}
|
|
277
|
+
# - PreToolUse deny: hookSpecificOutput.permissionDecision
|
|
278
|
+
if decision.action == DecisionAction.ALLOW:
|
|
279
|
+
return {}
|
|
280
|
+
|
|
275
281
|
if decision.event_type == AiHookEventType.PROMPT:
|
|
276
|
-
if decision.action == DecisionAction.ALLOW:
|
|
277
|
-
return {}
|
|
278
282
|
return {'decision': 'block', 'reason': decision.user_message or ''}
|
|
279
283
|
|
|
280
|
-
if decision.action == DecisionAction.ALLOW:
|
|
281
|
-
return {
|
|
282
|
-
'hookSpecificOutput': {
|
|
283
|
-
'hookEventName': 'PreToolUse',
|
|
284
|
-
'permissionDecision': 'allow',
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
284
|
return {
|
|
288
285
|
'hookSpecificOutput': {
|
|
289
286
|
'hookEventName': 'PreToolUse',
|
|
@@ -6,15 +6,19 @@ from typing import Optional
|
|
|
6
6
|
import typer
|
|
7
7
|
|
|
8
8
|
from cycode.cli.files_collector.sca.base_restore_dependencies import BaseRestoreDependencies, build_dep_tree_path
|
|
9
|
+
from cycode.cli.files_collector.sca.npm.workspace import (
|
|
10
|
+
BUN_LOCK_FILE_NAME,
|
|
11
|
+
MANIFEST_FILE_NAME,
|
|
12
|
+
is_covered_workspace_member,
|
|
13
|
+
)
|
|
9
14
|
from cycode.cli.models import Document
|
|
10
|
-
from cycode.cli.utils.path_utils import get_file_content
|
|
15
|
+
from cycode.cli.utils.path_utils import get_file_content, get_scan_roots_from_context
|
|
11
16
|
from cycode.cli.utils.shell_executor import shell
|
|
12
17
|
from cycode.logger import get_logger
|
|
13
18
|
|
|
14
19
|
logger = get_logger('Bun Restore Dependencies')
|
|
15
20
|
|
|
16
|
-
BUN_MANIFEST_FILE_NAME =
|
|
17
|
-
BUN_LOCK_FILE_NAME = 'bun.lock'
|
|
21
|
+
BUN_MANIFEST_FILE_NAME = MANIFEST_FILE_NAME
|
|
18
22
|
|
|
19
23
|
# Only Bun >=1.2 produces the text-based `bun.lock` lockfile that we parse.
|
|
20
24
|
# Older Bun versions emit a binary `bun.lockb`, which is not supported.
|
|
@@ -61,6 +65,9 @@ class RestoreBunDependencies(BaseRestoreDependencies):
|
|
|
61
65
|
if manifest_dir and (Path(manifest_dir) / BUN_LOCK_FILE_NAME).is_file():
|
|
62
66
|
return True
|
|
63
67
|
|
|
68
|
+
if is_covered_workspace_member(manifest_dir, document.path, get_scan_roots_from_context(self.ctx)):
|
|
69
|
+
return False
|
|
70
|
+
|
|
64
71
|
return _indicates_bun(document.content)
|
|
65
72
|
|
|
66
73
|
def _is_supported_bun_version(self) -> bool:
|
|
@@ -4,6 +4,7 @@ from typing import Optional
|
|
|
4
4
|
import typer
|
|
5
5
|
|
|
6
6
|
from cycode.cli.files_collector.sca.base_restore_dependencies import BaseRestoreDependencies, build_dep_tree_path
|
|
7
|
+
from cycode.cli.files_collector.sca.npm.workspace import DENO_LOCK_FILE_NAME
|
|
7
8
|
from cycode.cli.models import Document
|
|
8
9
|
from cycode.cli.utils.path_utils import get_file_content
|
|
9
10
|
from cycode.logger import get_logger
|
|
@@ -11,7 +12,6 @@ from cycode.logger import get_logger
|
|
|
11
12
|
logger = get_logger('Deno Restore Dependencies')
|
|
12
13
|
|
|
13
14
|
DENO_MANIFEST_FILE_NAMES = ('deno.json', 'deno.jsonc')
|
|
14
|
-
DENO_LOCK_FILE_NAME = 'deno.lock'
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class RestoreDenoDependencies(BaseRestoreDependencies):
|
|
@@ -3,15 +3,25 @@ from pathlib import Path
|
|
|
3
3
|
import typer
|
|
4
4
|
|
|
5
5
|
from cycode.cli.files_collector.sca.base_restore_dependencies import BaseRestoreDependencies
|
|
6
|
+
from cycode.cli.files_collector.sca.npm.workspace import (
|
|
7
|
+
BUN_LOCK_FILE_NAME,
|
|
8
|
+
DENO_LOCK_FILE_NAME,
|
|
9
|
+
MANIFEST_FILE_NAME,
|
|
10
|
+
NPM_LOCK_FILE_NAME,
|
|
11
|
+
NPM_SHRINKWRAP_FILE_NAME,
|
|
12
|
+
PNPM_LOCK_FILE_NAME,
|
|
13
|
+
YARN_LOCK_FILE_NAME,
|
|
14
|
+
is_covered_workspace_member,
|
|
15
|
+
)
|
|
6
16
|
from cycode.cli.models import Document
|
|
17
|
+
from cycode.cli.utils.path_utils import get_scan_roots_from_context
|
|
7
18
|
from cycode.logger import get_logger
|
|
8
19
|
|
|
9
20
|
logger = get_logger('NPM Restore Dependencies')
|
|
10
21
|
|
|
11
|
-
NPM_MANIFEST_FILE_NAME =
|
|
12
|
-
NPM_LOCK_FILE_NAME = 'package-lock.json'
|
|
22
|
+
NPM_MANIFEST_FILE_NAME = MANIFEST_FILE_NAME
|
|
13
23
|
# These lockfiles indicate another package manager owns the project — NPM should not run
|
|
14
|
-
_ALTERNATIVE_LOCK_FILES = (
|
|
24
|
+
_ALTERNATIVE_LOCK_FILES = (YARN_LOCK_FILE_NAME, PNPM_LOCK_FILE_NAME, DENO_LOCK_FILE_NAME, BUN_LOCK_FILE_NAME)
|
|
15
25
|
|
|
16
26
|
|
|
17
27
|
class RestoreNpmDependencies(BaseRestoreDependencies):
|
|
@@ -24,6 +34,9 @@ class RestoreNpmDependencies(BaseRestoreDependencies):
|
|
|
24
34
|
Yarn and pnpm projects are handled by their dedicated handlers, which run before
|
|
25
35
|
this one in the handler list. This handler is the npm fallback.
|
|
26
36
|
|
|
37
|
+
A manifest is also declined when a lockfile further up already resolves it, whichever
|
|
38
|
+
package manager wrote that lockfile; see the workspace package for how that is decided.
|
|
39
|
+
|
|
27
40
|
NOTE: this guard only excludes a project when an alternative lockfile is *physically
|
|
28
41
|
present on disk*. It does not inspect the `packageManager`/`engines` signal in
|
|
29
42
|
package.json. So a project that declares e.g. `packageManager: "bun@..."` (or pnpm)
|
|
@@ -37,16 +50,18 @@ class RestoreNpmDependencies(BaseRestoreDependencies):
|
|
|
37
50
|
return False
|
|
38
51
|
|
|
39
52
|
manifest_dir = self.get_manifest_dir(document)
|
|
40
|
-
if manifest_dir:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
if not manifest_dir:
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
for lock_file in _ALTERNATIVE_LOCK_FILES:
|
|
57
|
+
if (Path(manifest_dir) / lock_file).is_file():
|
|
58
|
+
logger.debug(
|
|
59
|
+
'Skipping npm restore: alternative lockfile detected, %s',
|
|
60
|
+
{'path': document.path, 'lockfile': lock_file},
|
|
61
|
+
)
|
|
62
|
+
return False
|
|
48
63
|
|
|
49
|
-
return
|
|
64
|
+
return not is_covered_workspace_member(manifest_dir, document.path, get_scan_roots_from_context(self.ctx))
|
|
50
65
|
|
|
51
66
|
def get_commands(self, manifest_file_path: str) -> list[list[str]]:
|
|
52
67
|
return [
|
|
@@ -65,7 +80,11 @@ class RestoreNpmDependencies(BaseRestoreDependencies):
|
|
|
65
80
|
return NPM_LOCK_FILE_NAME
|
|
66
81
|
|
|
67
82
|
def get_lock_file_names(self) -> list[str]:
|
|
68
|
-
return [NPM_LOCK_FILE_NAME]
|
|
83
|
+
return [NPM_LOCK_FILE_NAME, NPM_SHRINKWRAP_FILE_NAME]
|
|
84
|
+
|
|
85
|
+
def get_restored_lock_file_name(self, restore_file_path: str) -> str:
|
|
86
|
+
name = Path(restore_file_path).name
|
|
87
|
+
return name if name in self.get_lock_file_names() else self.get_lock_file_name()
|
|
69
88
|
|
|
70
89
|
@staticmethod
|
|
71
90
|
def prepare_manifest_file_path_for_command(manifest_file_path: str) -> str:
|
|
@@ -5,14 +5,18 @@ from typing import Optional
|
|
|
5
5
|
import typer
|
|
6
6
|
|
|
7
7
|
from cycode.cli.files_collector.sca.base_restore_dependencies import BaseRestoreDependencies, build_dep_tree_path
|
|
8
|
+
from cycode.cli.files_collector.sca.npm.workspace import (
|
|
9
|
+
MANIFEST_FILE_NAME,
|
|
10
|
+
PNPM_LOCK_FILE_NAME,
|
|
11
|
+
is_covered_workspace_member,
|
|
12
|
+
)
|
|
8
13
|
from cycode.cli.models import Document
|
|
9
|
-
from cycode.cli.utils.path_utils import get_file_content
|
|
14
|
+
from cycode.cli.utils.path_utils import get_file_content, get_scan_roots_from_context
|
|
10
15
|
from cycode.logger import get_logger
|
|
11
16
|
|
|
12
17
|
logger = get_logger('Pnpm Restore Dependencies')
|
|
13
18
|
|
|
14
|
-
PNPM_MANIFEST_FILE_NAME =
|
|
15
|
-
PNPM_LOCK_FILE_NAME = 'pnpm-lock.yaml'
|
|
19
|
+
PNPM_MANIFEST_FILE_NAME = MANIFEST_FILE_NAME
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
def _indicates_pnpm(package_json_content: Optional[str]) -> bool:
|
|
@@ -44,6 +48,9 @@ class RestorePnpmDependencies(BaseRestoreDependencies):
|
|
|
44
48
|
if manifest_dir and (Path(manifest_dir) / PNPM_LOCK_FILE_NAME).is_file():
|
|
45
49
|
return True
|
|
46
50
|
|
|
51
|
+
if is_covered_workspace_member(manifest_dir, document.path, get_scan_roots_from_context(self.ctx)):
|
|
52
|
+
return False
|
|
53
|
+
|
|
47
54
|
return _indicates_pnpm(document.content)
|
|
48
55
|
|
|
49
56
|
def try_restore_dependencies(self, document: Document) -> Optional[Document]:
|
|
@@ -5,14 +5,18 @@ from typing import Optional
|
|
|
5
5
|
import typer
|
|
6
6
|
|
|
7
7
|
from cycode.cli.files_collector.sca.base_restore_dependencies import BaseRestoreDependencies, build_dep_tree_path
|
|
8
|
+
from cycode.cli.files_collector.sca.npm.workspace import (
|
|
9
|
+
MANIFEST_FILE_NAME,
|
|
10
|
+
YARN_LOCK_FILE_NAME,
|
|
11
|
+
is_covered_workspace_member,
|
|
12
|
+
)
|
|
8
13
|
from cycode.cli.models import Document
|
|
9
|
-
from cycode.cli.utils.path_utils import get_file_content
|
|
14
|
+
from cycode.cli.utils.path_utils import get_file_content, get_scan_roots_from_context
|
|
10
15
|
from cycode.logger import get_logger
|
|
11
16
|
|
|
12
17
|
logger = get_logger('Yarn Restore Dependencies')
|
|
13
18
|
|
|
14
|
-
YARN_MANIFEST_FILE_NAME =
|
|
15
|
-
YARN_LOCK_FILE_NAME = 'yarn.lock'
|
|
19
|
+
YARN_MANIFEST_FILE_NAME = MANIFEST_FILE_NAME
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
def _indicates_yarn(package_json_content: Optional[str]) -> bool:
|
|
@@ -44,6 +48,9 @@ class RestoreYarnDependencies(BaseRestoreDependencies):
|
|
|
44
48
|
if manifest_dir and (Path(manifest_dir) / YARN_LOCK_FILE_NAME).is_file():
|
|
45
49
|
return True
|
|
46
50
|
|
|
51
|
+
if is_covered_workspace_member(manifest_dir, document.path, get_scan_roots_from_context(self.ctx)):
|
|
52
|
+
return False
|
|
53
|
+
|
|
47
54
|
return _indicates_yarn(document.content)
|
|
48
55
|
|
|
49
56
|
def try_restore_dependencies(self, document: Document) -> Optional[Document]:
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Deciding whether a lockfile above a manifest already resolves that manifest's dependencies.
|
|
2
|
+
|
|
3
|
+
The lockfile is the authority wherever it can name its members; the manifest's workspaces globs
|
|
4
|
+
are a fallback for the formats that cannot. See resolvers.py for the per-format readers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from cycode.cli.files_collector.sca.npm.workspace import coverage as _coverage
|
|
8
|
+
from cycode.cli.files_collector.sca.npm.workspace import globs as _globs
|
|
9
|
+
from cycode.cli.files_collector.sca.npm.workspace import resolvers as _resolvers
|
|
10
|
+
from cycode.cli.files_collector.sca.npm.workspace.coverage import (
|
|
11
|
+
WorkspaceCoverage,
|
|
12
|
+
find_covering_workspace,
|
|
13
|
+
is_covered_workspace_member,
|
|
14
|
+
)
|
|
15
|
+
from cycode.cli.files_collector.sca.npm.workspace.names import (
|
|
16
|
+
BUN_BINARY_LOCK_FILE_NAME,
|
|
17
|
+
BUN_LOCK_FILE_NAME,
|
|
18
|
+
DENO_LOCK_FILE_NAME,
|
|
19
|
+
MANIFEST_FILE_NAME,
|
|
20
|
+
NPM_LOCK_FILE_NAME,
|
|
21
|
+
NPM_SHRINKWRAP_FILE_NAME,
|
|
22
|
+
PNPM_LOCK_FILE_NAME,
|
|
23
|
+
YARN_LOCK_FILE_NAME,
|
|
24
|
+
)
|
|
25
|
+
from cycode.cli.files_collector.sca.npm.workspace.resolvers import MEMBER_RESOLVERS
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def clear_cache() -> None:
|
|
29
|
+
"""Drop every memo, so one scan never inherits another scan's view of the filesystem."""
|
|
30
|
+
_resolvers.clear_cache()
|
|
31
|
+
_globs.clear_cache()
|
|
32
|
+
_coverage.clear_cache()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
'BUN_BINARY_LOCK_FILE_NAME',
|
|
37
|
+
'BUN_LOCK_FILE_NAME',
|
|
38
|
+
'DENO_LOCK_FILE_NAME',
|
|
39
|
+
'MANIFEST_FILE_NAME',
|
|
40
|
+
'MEMBER_RESOLVERS',
|
|
41
|
+
'NPM_LOCK_FILE_NAME',
|
|
42
|
+
'NPM_SHRINKWRAP_FILE_NAME',
|
|
43
|
+
'PNPM_LOCK_FILE_NAME',
|
|
44
|
+
'YARN_LOCK_FILE_NAME',
|
|
45
|
+
'WorkspaceCoverage',
|
|
46
|
+
'clear_cache',
|
|
47
|
+
'find_covering_workspace',
|
|
48
|
+
'is_covered_workspace_member',
|
|
49
|
+
]
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Walking up from a manifest to the lockfile that already resolves it."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import TYPE_CHECKING, NamedTuple, Optional
|
|
6
|
+
|
|
7
|
+
from cycode.cli.files_collector.sca.npm.workspace.files import logger, resolved_path
|
|
8
|
+
from cycode.cli.files_collector.sca.npm.workspace.globs import declares_workspace_member
|
|
9
|
+
from cycode.cli.files_collector.sca.npm.workspace.resolvers import MEMBER_RESOLVERS
|
|
10
|
+
from cycode.cli.utils.path_utils import is_sub_path
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from collections.abc import Iterator
|
|
14
|
+
|
|
15
|
+
_GIT_DIR_NAME = '.git'
|
|
16
|
+
|
|
17
|
+
_reported_unscanned_roots: set = set()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def clear_cache() -> None:
|
|
21
|
+
_reported_unscanned_roots.clear()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class WorkspaceCoverage(NamedTuple):
|
|
25
|
+
package_manager: str
|
|
26
|
+
lock_file: Path
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _scan_root_directories(scan_roots: tuple) -> list:
|
|
30
|
+
"""The directory each scanned path stands for; scanning a file scans its directory."""
|
|
31
|
+
directories = []
|
|
32
|
+
for scan_root in scan_roots:
|
|
33
|
+
resolved = resolved_path(scan_root)
|
|
34
|
+
if os.path.isfile(resolved):
|
|
35
|
+
resolved = os.path.dirname(resolved)
|
|
36
|
+
|
|
37
|
+
if resolved:
|
|
38
|
+
directories.append(resolved)
|
|
39
|
+
|
|
40
|
+
return directories
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _containing_scan_roots(manifest_dir: Path, scan_roots: tuple) -> list:
|
|
44
|
+
resolved_manifest_dir = resolved_path(manifest_dir)
|
|
45
|
+
return [
|
|
46
|
+
Path(directory)
|
|
47
|
+
for directory in _scan_root_directories(scan_roots)
|
|
48
|
+
if is_sub_path(directory, resolved_manifest_dir)
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _resolve_walk_boundary(manifest_dir: Path, scan_roots: tuple) -> Optional[Path]:
|
|
53
|
+
for root_dir in manifest_dir.parents:
|
|
54
|
+
if (root_dir / _GIT_DIR_NAME).exists():
|
|
55
|
+
return root_dir
|
|
56
|
+
|
|
57
|
+
containing = _containing_scan_roots(manifest_dir, scan_roots)
|
|
58
|
+
if containing:
|
|
59
|
+
return min(containing, key=lambda scan_root: len(scan_root.parts))
|
|
60
|
+
|
|
61
|
+
if scan_roots:
|
|
62
|
+
return manifest_dir
|
|
63
|
+
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _workspace_root_candidates(manifest_dir: Path, scan_roots: tuple) -> 'Iterator[Path]':
|
|
68
|
+
boundary = _resolve_walk_boundary(manifest_dir, scan_roots)
|
|
69
|
+
resolved_boundary = resolved_path(boundary) if boundary is not None else None
|
|
70
|
+
if resolved_boundary == resolved_path(manifest_dir):
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
for root_dir in manifest_dir.parents:
|
|
74
|
+
yield root_dir
|
|
75
|
+
if resolved_boundary is not None and resolved_path(root_dir) == resolved_boundary:
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _find_covering_workspace(manifest_dir: Path, scan_roots: tuple) -> Optional[WorkspaceCoverage]:
|
|
80
|
+
for root_dir in _workspace_root_candidates(manifest_dir, scan_roots):
|
|
81
|
+
member_path = manifest_dir.relative_to(root_dir).as_posix()
|
|
82
|
+
|
|
83
|
+
for resolver in MEMBER_RESOLVERS:
|
|
84
|
+
for lock_file_name in resolver.lock_file_names:
|
|
85
|
+
lock_file = root_dir / lock_file_name
|
|
86
|
+
if not lock_file.is_file():
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
member_names = resolver.resolve(lock_file)
|
|
90
|
+
if member_names is not None:
|
|
91
|
+
if member_path in member_names:
|
|
92
|
+
return WorkspaceCoverage(resolver.package_manager, lock_file)
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
if resolver.may_use_workspace_globs and declares_workspace_member(root_dir, member_path):
|
|
96
|
+
return WorkspaceCoverage(resolver.package_manager, lock_file)
|
|
97
|
+
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def find_covering_workspace(manifest_dir: Optional[str], scan_roots: tuple = ()) -> Optional[WorkspaceCoverage]:
|
|
102
|
+
if not manifest_dir:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
return _find_covering_workspace(Path(manifest_dir), scan_roots)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _is_inside_scanned_paths(scan_roots: tuple, root_dir: Path) -> bool:
|
|
109
|
+
directories = _scan_root_directories(scan_roots)
|
|
110
|
+
if not directories:
|
|
111
|
+
logger.debug('No scanned paths in context; treating the workspace root as scanned, %s', {'root': str(root_dir)})
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
resolved_root_dir = resolved_path(root_dir)
|
|
115
|
+
return any(is_sub_path(directory, resolved_root_dir) for directory in directories)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def is_covered_workspace_member(manifest_dir: Optional[str], document_path: str, scan_roots: tuple = ()) -> bool:
|
|
119
|
+
coverage = find_covering_workspace(manifest_dir, scan_roots)
|
|
120
|
+
if coverage is None:
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
details = {
|
|
124
|
+
'path': document_path,
|
|
125
|
+
'root_lockfile': str(coverage.lock_file),
|
|
126
|
+
'workspace': coverage.package_manager,
|
|
127
|
+
}
|
|
128
|
+
if _is_inside_scanned_paths(scan_roots, coverage.lock_file.parent):
|
|
129
|
+
logger.debug('Skipping restore: the workspace root lockfile already covers this member, %s', details)
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
report_key = (document_path, str(coverage.lock_file))
|
|
133
|
+
if report_key not in _reported_unscanned_roots:
|
|
134
|
+
_reported_unscanned_roots.add(report_key)
|
|
135
|
+
logger.warning(
|
|
136
|
+
'The workspace root lockfile is outside the scanned path and will not be collected, '
|
|
137
|
+
'so this member is restored on its own. Scan the workspace root for the versions it '
|
|
138
|
+
'actually installs, %s',
|
|
139
|
+
details,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return False
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Filesystem access shared by the readers: identity for caching, and tolerant parsing."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from cycode.cli.utils.path_utils import get_absolute_path
|
|
9
|
+
from cycode.logger import get_logger
|
|
10
|
+
|
|
11
|
+
logger = get_logger('SCA NPM Workspace')
|
|
12
|
+
|
|
13
|
+
FileStamp = tuple[str, int, int]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def resolved_path(path: object) -> str:
|
|
17
|
+
return os.path.realpath(get_absolute_path(str(path)))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def file_stamp(path: Path) -> Optional[FileStamp]:
|
|
21
|
+
try:
|
|
22
|
+
stat_result = path.stat()
|
|
23
|
+
except OSError:
|
|
24
|
+
return None
|
|
25
|
+
|
|
26
|
+
return str(path), stat_result.st_mtime_ns, stat_result.st_size
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def read_json_object(path: Path) -> Optional[dict]:
|
|
30
|
+
try:
|
|
31
|
+
content = json.loads(path.read_text(encoding='UTF-8'))
|
|
32
|
+
except FileNotFoundError:
|
|
33
|
+
return None
|
|
34
|
+
except (OSError, ValueError) as e:
|
|
35
|
+
logger.debug('Could not read an npm workspace file, %s', {'path': str(path), 'error': e})
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
return content if isinstance(content, dict) else None
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Matching a member against the manifest's workspaces globs.
|
|
2
|
+
|
|
3
|
+
Only reached for lockfiles that cannot name their own members: classic yarn.lock and the
|
|
4
|
+
binary bun.lockb. Every other format answers from the lockfile itself.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import fnmatch
|
|
8
|
+
from functools import lru_cache
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import NamedTuple
|
|
11
|
+
|
|
12
|
+
from cycode.cli.files_collector.sca.npm.workspace.files import FileStamp, file_stamp, read_json_object
|
|
13
|
+
from cycode.cli.files_collector.sca.npm.workspace.names import MANIFEST_FILE_NAME
|
|
14
|
+
|
|
15
|
+
_MANIFEST_WORKSPACES_SECTION = 'workspaces'
|
|
16
|
+
_MANIFEST_WORKSPACE_PACKAGES_SECTION = 'packages'
|
|
17
|
+
_NEGATION_PREFIX = '!'
|
|
18
|
+
_GLOBSTAR = '**'
|
|
19
|
+
_SEGMENT_MATCH_CACHE_SIZE = 4096
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class _WorkspacePatterns(NamedTuple):
|
|
23
|
+
included: tuple[str, ...]
|
|
24
|
+
excluded: tuple[str, ...]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
_EMPTY_WORKSPACE_PATTERNS = _WorkspacePatterns((), ())
|
|
28
|
+
|
|
29
|
+
_workspace_patterns_cache: dict[FileStamp, _WorkspacePatterns] = {}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def clear_cache() -> None:
|
|
33
|
+
_workspace_patterns_cache.clear()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _matches_workspace_pattern(pattern: str, member_path: str) -> bool:
|
|
37
|
+
"""Match a workspace glob against a member path, one path segment at a time.
|
|
38
|
+
|
|
39
|
+
fnmatch is wrong for a whole path because its * crosses a separator, but inside one
|
|
40
|
+
segment there is no separator, so it is exactly right there - and it brings character
|
|
41
|
+
classes with it. Only ** needs handling here, because only ** spans segments.
|
|
42
|
+
"""
|
|
43
|
+
return _match_segments(tuple(pattern.split('/')), tuple(member_path.split('/')))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@lru_cache(maxsize=_SEGMENT_MATCH_CACHE_SIZE)
|
|
47
|
+
def _match_segments(pattern_segments: tuple, path_segments: tuple) -> bool:
|
|
48
|
+
"""Memoised so that several ** in one pattern cannot make this exponential.
|
|
49
|
+
|
|
50
|
+
Each ** tries every split of the remaining path, so without memoisation a pattern such as
|
|
51
|
+
**/**/**/x against a deep tree multiplies out. The arguments are plain tuples of strings,
|
|
52
|
+
so a result can never go stale.
|
|
53
|
+
"""
|
|
54
|
+
if not pattern_segments:
|
|
55
|
+
return not path_segments
|
|
56
|
+
|
|
57
|
+
head, rest = pattern_segments[0], pattern_segments[1:]
|
|
58
|
+
if head == _GLOBSTAR:
|
|
59
|
+
# ** spans zero or more segments, and spanning zero is what makes dir/** match dir
|
|
60
|
+
return any(_match_segments(rest, path_segments[index:]) for index in range(len(path_segments) + 1))
|
|
61
|
+
|
|
62
|
+
if not path_segments:
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
return fnmatch.fnmatchcase(path_segments[0], head) and _match_segments(rest, path_segments[1:])
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _split_workspace_patterns(declared: list) -> _WorkspacePatterns:
|
|
69
|
+
included = []
|
|
70
|
+
excluded = []
|
|
71
|
+
for entry in declared:
|
|
72
|
+
stripped = entry.strip()
|
|
73
|
+
is_excluded = stripped.startswith(_NEGATION_PREFIX)
|
|
74
|
+
normalized = (stripped[1:] if is_excluded else stripped).strip()
|
|
75
|
+
if normalized.startswith('./'):
|
|
76
|
+
normalized = normalized[2:]
|
|
77
|
+
|
|
78
|
+
normalized = normalized.rstrip('/')
|
|
79
|
+
if not normalized:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
if is_excluded:
|
|
83
|
+
excluded.append(normalized)
|
|
84
|
+
else:
|
|
85
|
+
included.append(normalized)
|
|
86
|
+
|
|
87
|
+
return _WorkspacePatterns(tuple(included), tuple(excluded))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _read_manifest_workspace_patterns(root_dir: Path) -> _WorkspacePatterns:
|
|
91
|
+
manifest = root_dir / MANIFEST_FILE_NAME
|
|
92
|
+
stamp = file_stamp(manifest)
|
|
93
|
+
if stamp is None:
|
|
94
|
+
return _EMPTY_WORKSPACE_PATTERNS
|
|
95
|
+
|
|
96
|
+
cached = _workspace_patterns_cache.get(stamp)
|
|
97
|
+
if cached is not None:
|
|
98
|
+
return cached
|
|
99
|
+
|
|
100
|
+
content = read_json_object(manifest)
|
|
101
|
+
workspaces = content.get(_MANIFEST_WORKSPACES_SECTION) if content is not None else None
|
|
102
|
+
if isinstance(workspaces, dict):
|
|
103
|
+
workspaces = workspaces.get(_MANIFEST_WORKSPACE_PACKAGES_SECTION)
|
|
104
|
+
|
|
105
|
+
declared = [entry for entry in workspaces if isinstance(entry, str)] if isinstance(workspaces, list) else []
|
|
106
|
+
patterns = _split_workspace_patterns(declared)
|
|
107
|
+
_workspace_patterns_cache[stamp] = patterns
|
|
108
|
+
return patterns
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def declares_workspace_member(root_dir: Path, member_path: str) -> bool:
|
|
112
|
+
patterns = _read_manifest_workspace_patterns(root_dir)
|
|
113
|
+
if not any(_matches_workspace_pattern(pattern, member_path) for pattern in patterns.included):
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
return not any(_matches_workspace_pattern(pattern, member_path) for pattern in patterns.excluded)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""File names and package-manager identifiers shared across the workspace package."""
|
|
2
|
+
|
|
3
|
+
MANIFEST_FILE_NAME = 'package.json'
|
|
4
|
+
|
|
5
|
+
NPM_PACKAGE_MANAGER = 'npm'
|
|
6
|
+
YARN_PACKAGE_MANAGER = 'yarn'
|
|
7
|
+
PNPM_PACKAGE_MANAGER = 'pnpm'
|
|
8
|
+
BUN_PACKAGE_MANAGER = 'bun'
|
|
9
|
+
|
|
10
|
+
NPM_LOCK_FILE_NAME = 'package-lock.json'
|
|
11
|
+
NPM_SHRINKWRAP_FILE_NAME = 'npm-shrinkwrap.json'
|
|
12
|
+
YARN_LOCK_FILE_NAME = 'yarn.lock'
|
|
13
|
+
PNPM_LOCK_FILE_NAME = 'pnpm-lock.yaml'
|
|
14
|
+
BUN_LOCK_FILE_NAME = 'bun.lock'
|
|
15
|
+
BUN_BINARY_LOCK_FILE_NAME = 'bun.lockb'
|
|
16
|
+
DENO_LOCK_FILE_NAME = 'deno.lock'
|