socketsecurity 2.4.1__tar.gz → 2.4.3__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.
Files changed (143) hide show
  1. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/CHANGELOG.md +34 -0
  2. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/PKG-INFO +1 -1
  3. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/docs/cli-reference.md +12 -9
  4. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/pyproject.toml +1 -1
  5. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/__init__.py +1 -1
  6. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/config.py +103 -5
  7. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/__init__.py +121 -14
  8. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/tools/reachability.py +40 -6
  9. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/socketcli.py +27 -6
  10. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_config.py +49 -0
  11. socketsecurity-2.4.3/tests/unit/test_exclude_paths.py +177 -0
  12. socketsecurity-2.4.3/tests/unit/test_reachability.py +106 -0
  13. socketsecurity-2.4.3/tests/unit/test_tier1_finalize.py +70 -0
  14. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/uv.lock +1 -1
  15. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/CODEOWNERS +0 -0
  16. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
  17. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  18. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
  19. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  20. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/actions/setup-docker/action.yml +0 -0
  21. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/actions/setup-hatch/action.yml +0 -0
  22. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/actions/setup-sfw/action.yml +0 -0
  23. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/dependabot.yml +0 -0
  24. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/dependency-review.yml +0 -0
  25. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/docker-stable.yml +0 -0
  26. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/e2e-test.yml +0 -0
  27. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/pr-preview.yml +0 -0
  28. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/python-tests.yml +0 -0
  29. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/release.yml +0 -0
  30. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/version-check.yml +0 -0
  31. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/zizmor.yml +0 -0
  32. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.gitignore +0 -0
  33. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.hooks/sync_version.py +0 -0
  34. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.pre-commit-config.yaml +0 -0
  35. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.python-version +0 -0
  36. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/Dockerfile +0 -0
  37. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/LICENSE +0 -0
  38. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/Makefile +0 -0
  39. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/README.md +0 -0
  40. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/docs/ci-cd.md +0 -0
  41. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/docs/development.md +0 -0
  42. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/docs/troubleshooting.md +0 -0
  43. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-dashboard-parity.json +0 -0
  44. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-dashboard-parity.toml +0 -0
  45. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-diff-ci-cd.json +0 -0
  46. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-diff-ci-cd.toml +0 -0
  47. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-instance-detail.json +0 -0
  48. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-instance-detail.toml +0 -0
  49. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/instructions/gitlab-commit-status/uat.md +0 -0
  50. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/pytest.ini +0 -0
  51. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/build_container.sh +0 -0
  52. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/build_container_flexible.sh +0 -0
  53. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/deploy-test-docker.sh +0 -0
  54. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/deploy-test-pypi.sh +0 -0
  55. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/docker-entrypoint.sh +0 -0
  56. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/run.sh +0 -0
  57. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/session.md +0 -0
  58. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socket.yml +0 -0
  59. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/alert_selection.py +0 -0
  60. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/classes.py +0 -0
  61. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/cli_client.py +0 -0
  62. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/exceptions.py +0 -0
  63. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/git_interface.py +0 -0
  64. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/helper/__init__.py +0 -0
  65. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
  66. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/lazy_file_loader.py +0 -0
  67. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/logging.py +0 -0
  68. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/messages.py +0 -0
  69. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/resource_utils.py +0 -0
  70. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm/__init__.py +0 -0
  71. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm/base.py +0 -0
  72. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm/client.py +0 -0
  73. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm/github.py +0 -0
  74. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm/gitlab.py +0 -0
  75. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm_comments.py +0 -0
  76. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/socket_config.py +0 -0
  77. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/utils.py +0 -0
  78. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/fossa_compat.py +0 -0
  79. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/output.py +0 -0
  80. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/__init__.py +0 -0
  81. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/base.py +0 -0
  82. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/formatters/__init__.py +0 -0
  83. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/formatters/slack.py +0 -0
  84. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/jira.py +0 -0
  85. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/manager.py +0 -0
  86. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/slack.py +0 -0
  87. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/teams.py +0 -0
  88. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/webhook.py +0 -0
  89. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/__init__.py +0 -0
  90. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/conftest.py +0 -0
  91. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/create_diff_input.json +0 -0
  92. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_diff_alerts.py +0 -0
  93. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_diff_generation.py +0 -0
  94. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_facts_compression.py +0 -0
  95. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_has_manifest_files.py +0 -0
  96. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_package_and_alerts.py +0 -0
  97. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_sdk_methods.py +0 -0
  98. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_supporting_methods.py +0 -0
  99. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/create_response.json +0 -0
  100. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/diff/stream_diff.json +0 -0
  101. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
  102. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/head_scan/metadata.json +0 -0
  103. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
  104. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
  105. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/new_scan/metadata.json +0 -0
  106. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
  107. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/repos/repo_info_error.json +0 -0
  108. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/repos/repo_info_no_head.json +0 -0
  109. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/repos/repo_info_success.json +0 -0
  110. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/settings/security-policy.json +0 -0
  111. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/fixtures/simple-npm/index.js +0 -0
  112. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/fixtures/simple-npm/package.json +0 -0
  113. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/fixtures/simple-pypi/requirements.txt +0 -0
  114. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/validate-gitlab.sh +0 -0
  115. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/validate-json.sh +0 -0
  116. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/validate-reachability.sh +0 -0
  117. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/validate-sarif.sh +0 -0
  118. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/validate-scan.sh +0 -0
  119. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/fixtures/fossa/README.md +0 -0
  120. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-analyze-empty.json +0 -0
  121. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-analyze-populated.json +0 -0
  122. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-sbom-empty-deep.json +0 -0
  123. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-sbom-populated.json +0 -0
  124. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/__init__.py +0 -0
  125. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_alert_selection.py +0 -0
  126. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_cli_config.py +0 -0
  127. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_client.py +0 -0
  128. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_dependency_overview.py +0 -0
  129. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_disable_ignore.py +0 -0
  130. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_fossa_compat.py +0 -0
  131. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_fossa_parity.py +0 -0
  132. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_gitlab_auth.py +0 -0
  133. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_gitlab_auth_fallback.py +0 -0
  134. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_gitlab_commit_status.py +0 -0
  135. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_gitlab_format.py +0 -0
  136. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_ignore_telemetry_filtering.py +0 -0
  137. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_output.py +0 -0
  138. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_slack_plugin.py +0 -0
  139. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_socketcli.py +0 -0
  140. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/workflows/bitbucket-pipelines.yml +0 -0
  141. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/workflows/buildkite.yml +0 -0
  142. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/workflows/github-actions.yml +0 -0
  143. {socketsecurity-2.4.1 → socketsecurity-2.4.3}/workflows/gitlab-ci.yml +0 -0
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.4.3
4
+
5
+ ### Added: unified `--exclude-paths` for manifest discovery and reachability
6
+
7
+ - New `--exclude-paths` flag (comma-separated globs) that excludes matching paths from
8
+ BOTH SCA manifest discovery and reachability analysis. Patterns are scan-root-relative
9
+ anchored globs (`*` does not cross `/`, `**` does), matching the Node CLI's behavior.
10
+ - Pattern validation rejects unsupported forms (negation, absolute paths, `..` traversal,
11
+ and match-everything patterns). Patterns may be supplied on the CLI as a comma-separated
12
+ string or via a `--config` file list.
13
+ - `--reach-exclude-paths` is now deprecated in favor of `--exclude-paths`. It still works
14
+ (and is unioned into the Coana `--exclude-dirs` argument) but is marked deprecated in
15
+ `--help` and warns at runtime.
16
+
17
+ ## 2.4.2
18
+
19
+ ### Added: reachability flag and Coana environment alignment with the Node CLI
20
+
21
+ - New `--reach-disable-external-tool-checks` flag (passes `--disable-external-tool-checks`
22
+ to the Coana CLI).
23
+ - New `--reach-debug` flag to enable Coana debug output (`--debug`) independently of the
24
+ global `--enable-debug`.
25
+ - Node-style `--reach-analysis-timeout` and `--reach-analysis-memory-limit` are now the
26
+ primary flag names; the previous `--reach-timeout` / `--reach-memory-limit` continue to
27
+ work as hidden aliases.
28
+ - The Coana subprocess now receives `SOCKET_CLI_VERSION` and `SOCKET_CALLER_USER_AGENT` so
29
+ calls are attributed to the Python CLI. Proxies continue to work via the inherited
30
+ `HTTPS_PROXY` / `HTTP_PROXY` environment variables, which Coana reads itself.
31
+ - `SOCKET_REPO_NAME` / `SOCKET_BRANCH_NAME` are no longer forwarded to Coana when the repo
32
+ and branch are the default sentinels, avoiding cross-run reachability cache-bucket
33
+ collisions.
34
+ - Tier 1 reachability finalize now retries with exponential backoff instead of giving up on
35
+ the first transient error.
36
+
3
37
  ## 2.4.1
4
38
 
5
39
  ### Added: pyenv in the Docker image
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socketsecurity
3
- Version: 2.4.1
3
+ Version: 2.4.3
4
4
  Summary: Socket Security CLI for CI/CD
5
5
  Project-URL: Homepage, https://socket.dev
6
6
  Author-email: Douglas Coburn <douglas@socket.dev>
@@ -148,14 +148,14 @@ socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--workspace WORKSPACE] [--
148
148
  [--owner OWNER] [--pr-number PR_NUMBER] [--commit-message COMMIT_MESSAGE] [--commit-sha COMMIT_SHA] [--committers [COMMITTERS ...]]
149
149
  [--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--license-file-name LICENSE_FILE_NAME] [--save-submitted-files-list SAVE_SUBMITTED_FILES_LIST]
150
150
  [--save-manifest-tar SAVE_MANIFEST_TAR] [--files FILES] [--sub-path SUB_PATH] [--workspace-name WORKSPACE_NAME]
151
- [--excluded-ecosystems EXCLUDED_ECOSYSTEMS] [--default-branch] [--pending-head] [--generate-license] [--enable-debug]
151
+ [--excluded-ecosystems EXCLUDED_ECOSYSTEMS] [--exclude-paths EXCLUDE_PATHS] [--default-branch] [--pending-head] [--generate-license] [--enable-debug]
152
152
  [--enable-json] [--enable-sarif] [--sarif-file <path>] [--sarif-scope {diff,full}] [--sarif-grouping {instance,alert}] [--sarif-reachability {all,reachable,potentially,reachable-or-potentially}] [--enable-gitlab-security] [--gitlab-security-file <path>]
153
153
  [--disable-overview] [--exclude-license-details] [--allow-unverified] [--disable-security-issue]
154
154
  [--ignore-commit-files] [--disable-blocking] [--disable-ignore] [--enable-diff] [--scm SCM] [--timeout TIMEOUT] [--include-module-folders]
155
- [--reach] [--reach-version REACH_VERSION] [--reach-timeout REACH_ANALYSIS_TIMEOUT]
156
- [--reach-memory-limit REACH_ANALYSIS_MEMORY_LIMIT] [--reach-ecosystems REACH_ECOSYSTEMS] [--reach-exclude-paths REACH_EXCLUDE_PATHS]
157
- [--reach-min-severity {low,medium,high,critical}] [--reach-skip-cache] [--reach-disable-analytics] [--reach-output-file REACH_OUTPUT_FILE]
158
- [--only-facts-file] [--version]
155
+ [--reach] [--reach-version REACH_VERSION] [--reach-analysis-timeout REACH_ANALYSIS_TIMEOUT]
156
+ [--reach-analysis-memory-limit REACH_ANALYSIS_MEMORY_LIMIT] [--reach-concurrency REACH_CONCURRENCY] [--reach-ecosystems REACH_ECOSYSTEMS]
157
+ [--reach-min-severity {low,medium,high,critical}] [--reach-skip-cache] [--reach-disable-analytics] [--reach-debug] [--reach-disable-external-tool-checks]
158
+ [--reach-output-file REACH_OUTPUT_FILE] [--only-facts-file] [--version]
159
159
  ````
160
160
 
161
161
  If you don't want to provide the Socket API Token every time then you can use the environment variable `SOCKET_SECURITY_API_TOKEN`
@@ -203,6 +203,7 @@ If you don't want to provide the Socket API Token every time then you can use th
203
203
  | `--sub-path` | False | | Sub-path within target-path for manifest file scanning (can be specified multiple times). All sub-paths are combined into a single workspace scan while preserving git context from target-path. Must be used with `--workspace-name` |
204
204
  | `--workspace-name` | False | | Workspace name suffix to append to repository name (repo-name-workspace_name). Must be used with `--sub-path` |
205
205
  | `--excluded-ecosystems` | False | [] | List of ecosystems to exclude from analysis (JSON array string). You can get supported files from the [Supported Files API](https://docs.socket.dev/reference/getsupportedfiles) |
206
+ | `--exclude-paths` | False | | Comma-separated paths/globs to exclude from **both** manifest discovery (every scan) **and** reachability analysis (e.g. `tests/**,packages/legacy,*.spec.ts`). Patterns are scan-root-relative, case-sensitive globs where `*` does not cross `/` and `**` does. Supersedes `--reach-exclude-paths`. |
206
207
 
207
208
  #### Branch and Scan Configuration
208
209
  | Parameter | Required | Default | Description |
@@ -239,16 +240,18 @@ If you don't want to provide the Socket API Token every time then you can use th
239
240
  |:---------------------------------|:---------|:--------|:---------------------------------------------------------------------------------------------------------------------------|
240
241
  | `--reach` | False | False | Enable reachability analysis to identify which vulnerable functions are actually called by your code |
241
242
  | `--reach-version` | False | latest | Version of @coana-tech/cli to use for analysis |
242
- | `--reach-timeout` | False | 1200 | Timeout in seconds for the reachability analysis (default: 1200 seconds / 20 minutes) |
243
- | `--reach-memory-limit` | False | 4096 | Memory limit in MB for the reachability analysis (default: 4096 MB / 4 GB) |
244
- | `--reach-concurrency` | False | | Control parallel analysis execution (must be >= 1) |
243
+ | `--reach-analysis-timeout` | False | *coana* | Timeout in seconds for the reachability analysis. Omitted by default, so coana applies its own (currently 600s). Alias: `--reach-timeout` |
244
+ | `--reach-analysis-memory-limit` | False | *coana* | Memory limit in MB for the reachability analysis. Omitted by default, so coana applies its own (currently 8192). Alias: `--reach-memory-limit` |
245
+ | `--reach-concurrency` | False | *coana* | Control parallel analysis execution (must be >= 1). Omitted by default, so coana applies its own (currently 1) |
245
246
  | `--reach-additional-params` | False | | Pass custom parameters to the coana CLI tool |
246
247
  | `--reach-ecosystems` | False | | Comma-separated list of ecosystems to analyze (e.g., "npm,pypi"). If not specified, all supported ecosystems are analyzed |
247
- | `--reach-exclude-paths` | False | | Comma-separated list of file paths or patterns to exclude from reachability analysis |
248
248
  | `--reach-min-severity` | False | | Minimum severity level for reporting reachability results (low, medium, high, critical) |
249
249
  | `--reach-skip-cache` | False | False | Skip cache and force fresh reachability analysis |
250
250
  | `--reach-disable-analytics` | False | False | Disable analytics collection during reachability analysis |
251
+ | `--reach-debug` | False | False | Enable coana debug output (`--debug`) for the analysis, independent of the global `--enable-debug` |
252
+ | `--reach-disable-external-tool-checks` | False | False | Disable coana's external tool availability checks (passes `--disable-external-tool-checks`) |
251
253
  | `--reach-output-file` | False | .socket.facts.json | Path where reachability analysis results should be saved |
254
+ | `--reach-exclude-paths` | False | | **[DEPRECATED — use `--exclude-paths`]** Comma-separated paths to exclude from reachability analysis. Still honored (unioned with `--exclude-paths`) but will be hidden in a future release |
252
255
  | `--only-facts-file` | False | False | Submit only the .socket.facts.json file to an existing scan (requires --reach and a prior scan) |
253
256
 
254
257
  **Reachability Analysis Requirements:**
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "socketsecurity"
9
- version = "2.4.1"
9
+ version = "2.4.3"
10
10
  requires-python = ">= 3.11"
11
11
  license = {"file" = "LICENSE"}
12
12
  dependencies = [
@@ -1,3 +1,3 @@
1
1
  __author__ = 'socket.dev'
2
- __version__ = '2.4.1'
2
+ __version__ = '2.4.3'
3
3
  USER_AGENT = f'SocketPythonCLI/{__version__}'
@@ -55,6 +55,50 @@ def load_cli_config_file(config_path: str) -> dict:
55
55
  return scoped
56
56
  return data
57
57
 
58
+ def normalize_exclude_paths(value) -> Optional[List[str]]:
59
+ """Normalize a --exclude-paths value into a clean list of patterns.
60
+
61
+ Accepts a comma-separated string (CLI) or a list/tuple (e.g. a JSON/TOML --config file
62
+ value), so config-file-supplied patterns flow through the same validation as CLI ones.
63
+ """
64
+ if not value:
65
+ return None
66
+ if isinstance(value, str):
67
+ items = value.split(",")
68
+ elif isinstance(value, (list, tuple)):
69
+ items = value
70
+ else:
71
+ return None
72
+ cleaned = [str(p).strip() for p in items if str(p).strip()]
73
+ return cleaned or None
74
+
75
+
76
+ def validate_exclude_paths(patterns: List[str]) -> None:
77
+ """Validate --exclude-paths patterns (mirrors Node's assertValidExcludePaths).
78
+
79
+ Patterns are scan-root-relative globs. Reject the cases coana's --exclude-dirs / fast-glob
80
+ cannot honor: negation, absolute paths, ``..`` traversal, and degenerate match-everything.
81
+ Exits with code 1 on the first invalid pattern.
82
+ """
83
+ # Degenerate match-everything forms, compared against the trailing-slash-stripped pattern
84
+ # (so "**/" reduces to "**" and is rejected, matching Node's stripTrailingSlash + check).
85
+ degenerate = {"", ".", "**", "./**", "/**"}
86
+ for p in patterns:
87
+ norm = (p or "").strip().replace("\\", "/")
88
+ if norm.startswith("!"):
89
+ logging.error(f"--exclude-paths: negation patterns are not supported: {p!r}")
90
+ exit(1)
91
+ if norm.startswith("/"):
92
+ logging.error(f"--exclude-paths: patterns must be scan-root relative (no leading '/'): {p!r}")
93
+ exit(1)
94
+ if norm == ".." or norm.startswith("../") or "/../" in norm or norm.endswith("/.."):
95
+ logging.error(f"--exclude-paths: '..' path traversal is not allowed: {p!r}")
96
+ exit(1)
97
+ if norm.rstrip("/") in degenerate:
98
+ logging.error(f"--exclude-paths: pattern would exclude everything: {p!r}")
99
+ exit(1)
100
+
101
+
58
102
  @dataclass
59
103
  class PluginConfig:
60
104
  enabled: bool = False
@@ -106,6 +150,7 @@ class CliConfig:
106
150
  include_module_folders: bool = False
107
151
  repo_is_public: bool = False
108
152
  excluded_ecosystems: list[str] = field(default_factory=lambda: [])
153
+ exclude_paths: Optional[List[str]] = None
109
154
  version: str = __version__
110
155
  jira_plugin: PluginConfig = field(default_factory=PluginConfig)
111
156
  slack_plugin: PluginConfig = field(default_factory=PluginConfig)
@@ -139,6 +184,8 @@ class CliConfig:
139
184
  reach_continue_on_install_errors: bool = False
140
185
  reach_continue_on_missing_lock_files: bool = False
141
186
  reach_continue_on_no_source_files: bool = False
187
+ reach_debug: bool = False
188
+ reach_disable_external_tool_checks: bool = False
142
189
  max_purl_batch_size: int = 5000
143
190
  enable_commit_status: bool = False
144
191
  legal: bool = False
@@ -165,6 +212,12 @@ class CliConfig:
165
212
 
166
213
  args = parser.parse_args(args_list)
167
214
 
215
+ if args.reach_exclude_paths:
216
+ logging.warning(
217
+ "--reach-exclude-paths is deprecated; use --exclude-paths instead. "
218
+ "It is still honored and unioned with --exclude-paths."
219
+ )
220
+
168
221
  # Get API token from env or args (check multiple env var names)
169
222
  api_token = (
170
223
  os.getenv("SOCKET_SECURITY_API_KEY") or
@@ -256,6 +309,7 @@ class CliConfig:
256
309
  'reach_lazy_mode': args.reach_lazy_mode,
257
310
  'reach_ecosystems': args.reach_ecosystems.split(',') if args.reach_ecosystems else None,
258
311
  'reach_exclude_paths': args.reach_exclude_paths.split(',') if args.reach_exclude_paths else None,
312
+ 'exclude_paths': normalize_exclude_paths(args.exclude_paths),
259
313
  'reach_skip_cache': args.reach_skip_cache,
260
314
  'reach_min_severity': args.reach_min_severity,
261
315
  'reach_output_file': args.reach_output_file,
@@ -267,6 +321,8 @@ class CliConfig:
267
321
  'reach_continue_on_install_errors': args.reach_continue_on_install_errors,
268
322
  'reach_continue_on_missing_lock_files': args.reach_continue_on_missing_lock_files,
269
323
  'reach_continue_on_no_source_files': args.reach_continue_on_no_source_files,
324
+ 'reach_debug': args.reach_debug,
325
+ 'reach_disable_external_tool_checks': args.reach_disable_external_tool_checks,
270
326
  'max_purl_batch_size': args.max_purl_batch_size,
271
327
  'enable_commit_status': args.enable_commit_status,
272
328
  'legal': args.legal or args.legal_format == "fossa",
@@ -357,6 +413,10 @@ class CliConfig:
357
413
  logging.error("--sarif-reachability potentially/reachable-or-potentially requires --sarif-scope full")
358
414
  exit(1)
359
415
 
416
+ # Validate --exclude-paths patterns up front (mirrors Node's assertValidExcludePaths).
417
+ if config_args.get("exclude_paths"):
418
+ validate_exclude_paths(config_args["exclude_paths"])
419
+
360
420
  # Validate that only_facts_file requires reach
361
421
  if args.only_facts_file and not args.reach:
362
422
  logging.error("--only-facts-file requires --reach to be specified")
@@ -566,6 +626,15 @@ def create_argument_parser() -> argparse.ArgumentParser:
566
626
  help="List of ecosystems to exclude from analysis (JSON array string)"
567
627
  )
568
628
 
629
+ path_group.add_argument(
630
+ "--exclude-paths",
631
+ dest="exclude_paths",
632
+ metavar="<list>",
633
+ help="Comma-separated paths/globs to exclude from BOTH manifest discovery and "
634
+ "reachability analysis (e.g. 'tests/**,packages/legacy,*.spec.ts'). "
635
+ "Supersedes --reach-exclude-paths."
636
+ )
637
+
569
638
  # Branch and Scan Configuration
570
639
  config_group = parser.add_argument_group('Branch and Scan Configuration')
571
640
  config_group.add_argument(
@@ -878,18 +947,32 @@ def create_argument_parser() -> argparse.ArgumentParser:
878
947
  help="Specific version of @coana-tech/cli to use (e.g., '1.2.3')"
879
948
  )
880
949
  reachability_group.add_argument(
881
- "--reach-timeout",
950
+ "--reach-analysis-timeout",
882
951
  dest="reach_analysis_timeout",
883
952
  type=int,
884
953
  metavar="<seconds>",
885
954
  help="Timeout for reachability analysis in seconds"
886
955
  )
956
+ # Backwards-compatible alias for the pre-alignment name. Kept working, hidden from help.
887
957
  reachability_group.add_argument(
888
- "--reach-memory-limit",
958
+ "--reach-timeout",
959
+ dest="reach_analysis_timeout",
960
+ type=int,
961
+ help=argparse.SUPPRESS
962
+ )
963
+ reachability_group.add_argument(
964
+ "--reach-analysis-memory-limit",
889
965
  dest="reach_analysis_memory_limit",
890
966
  type=int,
891
967
  metavar="<mb>",
892
- help="Memory limit for reachability analysis in MB"
968
+ help="Memory limit for reachability analysis in MB (defaults to the coana CLI's own default, currently 8192)"
969
+ )
970
+ # Backwards-compatible alias for the pre-alignment name. Kept working, hidden from help.
971
+ reachability_group.add_argument(
972
+ "--reach-memory-limit",
973
+ dest="reach_analysis_memory_limit",
974
+ type=int,
975
+ help=argparse.SUPPRESS
893
976
  )
894
977
  reachability_group.add_argument(
895
978
  "--reach-ecosystems",
@@ -901,7 +984,8 @@ def create_argument_parser() -> argparse.ArgumentParser:
901
984
  "--reach-exclude-paths",
902
985
  dest="reach_exclude_paths",
903
986
  metavar="<list>",
904
- help="Paths to exclude from reachability analysis (comma-separated)"
987
+ help="[DEPRECATED: use --exclude-paths] Paths to exclude from reachability analysis "
988
+ "(comma-separated). Still honored and unioned with --exclude-paths."
905
989
  )
906
990
  reachability_group.add_argument(
907
991
  "--reach-min-severity",
@@ -957,7 +1041,7 @@ def create_argument_parser() -> argparse.ArgumentParser:
957
1041
  dest="reach_concurrency",
958
1042
  type=int,
959
1043
  metavar="<number>",
960
- help="Concurrency level for reachability analysis (must be >= 1)"
1044
+ help="Concurrency level for reachability analysis (must be >= 1; defaults to the coana CLI's own default, currently 1)"
961
1045
  )
962
1046
  reachability_group.add_argument(
963
1047
  "--reach-additional-params",
@@ -1002,6 +1086,20 @@ def create_argument_parser() -> argparse.ArgumentParser:
1002
1086
  action="store_true",
1003
1087
  help=argparse.SUPPRESS
1004
1088
  )
1089
+ reachability_group.add_argument(
1090
+ "--reach-debug",
1091
+ dest="reach_debug",
1092
+ action="store_true",
1093
+ help="Enable debug output for the reachability analysis (passes --debug to the coana CLI). "
1094
+ "Independent of the global --enable-debug flag."
1095
+ )
1096
+ reachability_group.add_argument(
1097
+ "--reach-disable-external-tool-checks",
1098
+ dest="reach_disable_external_tool_checks",
1099
+ action="store_true",
1100
+ help="Disable coana's external tool availability checks during reachability analysis "
1101
+ "(passes --disable-external-tool-checks to the coana CLI)."
1102
+ )
1005
1103
 
1006
1104
  parser.add_argument(
1007
1105
  '--version',
@@ -71,6 +71,11 @@ SOCKET_FACTS_BROTLI_LGWIN = 24
71
71
  # Stream the facts file in 1 MiB chunks so large files aren't held fully in memory.
72
72
  SOCKET_FACTS_BROTLI_CHUNK_SIZE = 1024 * 1024
73
73
 
74
+ # Tier 1 reachability finalize retry policy. The finalize call links the tier1 scan to the
75
+ # full scan and can fail transiently (network/API blips); a few backoff retries make it robust.
76
+ TIER1_FINALIZE_MAX_ATTEMPTS = 3
77
+ TIER1_FINALIZE_BACKOFF_SECONDS = 1.0
78
+
74
79
 
75
80
  def _humanize_alert_type(alert_type: str) -> str:
76
81
  """Convert a camelCase/PascalCase alert type into a Title-Cased label.
@@ -208,6 +213,67 @@ class Core:
208
213
  return True
209
214
  return False
210
215
 
216
+ @staticmethod
217
+ def _exclude_glob_to_regex(pattern: str) -> str:
218
+ """Translate a micromatch-style glob into an anchored regex string.
219
+
220
+ Mirrors the Node CLI's --exclude-paths matcher (src/commands/scan/exclude-paths.mts):
221
+ patterns are matched against scan-root-relative POSIX paths, case-sensitively, where
222
+ ``*`` does NOT cross ``/`` and ``**`` DOES. Patterns are anchored at the scan root, so
223
+ ``tests`` matches ``tests`` (not ``src/tests``); use ``**/tests`` to match at any depth.
224
+ """
225
+ i, n = 0, len(pattern)
226
+ out = ["^"]
227
+ while i < n:
228
+ c = pattern[i]
229
+ if c == "*":
230
+ if i + 1 < n and pattern[i + 1] == "*":
231
+ if i + 2 < n and pattern[i + 2] == "/":
232
+ out.append("(?:[^/]+/)*") # '**/' -> zero or more path segments
233
+ i += 3
234
+ else:
235
+ out.append(".*") # '**' at end / before non-slash -> any, incl '/'
236
+ i += 2
237
+ else:
238
+ out.append("[^/]*") # '*' -> within a single path segment
239
+ i += 1
240
+ elif c == "?":
241
+ out.append("[^/]")
242
+ i += 1
243
+ else:
244
+ out.append(re.escape(c))
245
+ i += 1
246
+ out.append("$")
247
+ return "".join(out)
248
+
249
+ @staticmethod
250
+ def compile_exclude_paths(patterns: Optional[List[str]]) -> List["re.Pattern"]:
251
+ """Compile --exclude-paths globs into anchored regexes (compiled once per scan).
252
+
253
+ Each pattern ``P`` is expanded the way Node feeds fast-glob's ``ignore``: ``P`` (a file-
254
+ or dir-shaped exact match) plus ``P/**`` (its subtree), unless ``P`` already ends with
255
+ ``/**``. Validation of the patterns happens earlier, in CliConfig.from_args.
256
+ """
257
+ compiled: List["re.Pattern"] = []
258
+ for raw in patterns or []:
259
+ p = (raw or "").strip().replace("\\", "/").rstrip("/")
260
+ if not p:
261
+ continue
262
+ globs = [p] if p.endswith("/**") else [p, f"{p}/**"]
263
+ compiled.extend(re.compile(Core._exclude_glob_to_regex(g)) for g in globs)
264
+ return compiled
265
+
266
+ @staticmethod
267
+ def path_matches_exclude_regexes(rel_path: str, regexes: List["re.Pattern"]) -> bool:
268
+ rp = rel_path.replace(os.sep, "/").replace("\\", "/")
269
+ return any(r.match(rp) for r in regexes)
270
+
271
+ @staticmethod
272
+ def matches_exclude_paths(file_path: str, base_path: str, patterns: List[str]) -> bool:
273
+ """Convenience matcher (compiles patterns per call); used in tests/ad-hoc checks."""
274
+ rel_path = os.path.relpath(file_path, base_path).replace(os.sep, "/")
275
+ return Core.path_matches_exclude_regexes(rel_path, Core.compile_exclude_paths(patterns))
276
+
211
277
  def save_submitted_files_list(self, files: List[str], output_path: str) -> None:
212
278
  """
213
279
  Save the list of submitted file names to a JSON file for debugging.
@@ -331,6 +397,17 @@ class Core:
331
397
  start_time = time.time()
332
398
  files: Set[str] = set()
333
399
 
400
+ # Unified --exclude-paths: filter discovered manifests by the same paths/globs that are
401
+ # forwarded to coana's --exclude-dirs. Only consulted when the user supplied the flag.
402
+ # Patterns are anchored to `path` (the scan root this pass walks), matching coana's
403
+ # target and the Node CLI's fast-glob cwd. NOTE: when scanning multiple --sub-path
404
+ # targets, find_files runs once per sub-path, so a pattern like `tests` anchors to each
405
+ # sub-path independently (Node anchors all patterns to a single scan-root cwd). This only
406
+ # differs for the multi-target full-scan + --exclude-paths combo; the reach flow is
407
+ # single-target, so it matches Node there.
408
+ exclude_paths = getattr(self.cli_config, "exclude_paths", None) if self.cli_config else None
409
+ exclude_regexes = Core.compile_exclude_paths(exclude_paths) if exclude_paths else []
410
+
334
411
  # Get supported patterns from the API
335
412
  patterns = self.get_supported_patterns()
336
413
 
@@ -360,8 +437,15 @@ class Core:
360
437
 
361
438
  for glob_file in glob_files:
362
439
  glob_file_str = str(glob_file)
363
- if os.path.isfile(glob_file_str) and not Core.is_excluded(glob_file_str, self.config.excluded_dirs):
364
- files.add(glob_file_str.replace("\\", "/"))
440
+ if not os.path.isfile(glob_file_str):
441
+ continue
442
+ if Core.is_excluded(glob_file_str, self.config.excluded_dirs):
443
+ continue
444
+ if exclude_regexes:
445
+ rel = os.path.relpath(glob_file_str, path)
446
+ if Core.path_matches_exclude_regexes(rel, exclude_regexes):
447
+ continue
448
+ files.add(glob_file_str.replace("\\", "/"))
365
449
 
366
450
  glob_end = time.time()
367
451
  log.debug(f"Globbing took {glob_end - glob_start:.4f} seconds")
@@ -549,20 +633,43 @@ class Core:
549
633
  log.debug(f"Failed to read tier1ReachabilityScanId from {facts_file_path}: {e}")
550
634
  return False
551
635
 
552
- # Call the SDK to finalize the tier 1 scan
553
- try:
554
- success = self.sdk.fullscans.finalize_tier1(
555
- full_scan_id=full_scan_id,
556
- tier1_reachability_scan_id=tier1_scan_id,
557
- )
636
+ # Call the SDK to finalize the tier 1 scan, retrying transient failures with backoff.
637
+ last_error: Optional[Exception] = None
638
+ for attempt in range(1, TIER1_FINALIZE_MAX_ATTEMPTS + 1):
639
+ try:
640
+ success = self.sdk.fullscans.finalize_tier1(
641
+ full_scan_id=full_scan_id,
642
+ tier1_reachability_scan_id=tier1_scan_id,
643
+ )
558
644
 
559
- if success:
560
- log.debug(f"Successfully finalized tier 1 scan {tier1_scan_id} for full scan {full_scan_id}")
561
- return success
645
+ if success:
646
+ log.debug(f"Successfully finalized tier 1 scan {tier1_scan_id} for full scan {full_scan_id}")
647
+ return True
562
648
 
563
- except Exception as e:
564
- log.debug(f"Unable to finalize tier 1 scan: {e}")
565
- return False
649
+ log.debug(
650
+ f"finalize_tier1 returned a falsy result for scan {tier1_scan_id} "
651
+ f"(attempt {attempt}/{TIER1_FINALIZE_MAX_ATTEMPTS})"
652
+ )
653
+ except Exception as e:
654
+ last_error = e
655
+ log.debug(
656
+ f"Unable to finalize tier 1 scan (attempt {attempt}/{TIER1_FINALIZE_MAX_ATTEMPTS}): {e}"
657
+ )
658
+
659
+ if attempt < TIER1_FINALIZE_MAX_ATTEMPTS:
660
+ time.sleep(TIER1_FINALIZE_BACKOFF_SECONDS * (2 ** (attempt - 1)))
661
+
662
+ if last_error is not None:
663
+ log.debug(
664
+ f"Giving up finalizing tier 1 scan {tier1_scan_id} after "
665
+ f"{TIER1_FINALIZE_MAX_ATTEMPTS} attempts: {last_error}"
666
+ )
667
+ else:
668
+ log.debug(
669
+ f"Giving up finalizing tier 1 scan {tier1_scan_id} after "
670
+ f"{TIER1_FINALIZE_MAX_ATTEMPTS} attempts"
671
+ )
672
+ return False
566
673
 
567
674
  @staticmethod
568
675
  def _compress_facts_file(source_path: str) -> str:
@@ -1,15 +1,31 @@
1
1
  from socketdev import socketdev
2
2
  from typing import List, Optional, Dict, Any
3
3
  import os
4
+ import platform
4
5
  import subprocess
5
6
  import json
6
7
  import pathlib
7
8
  import logging
8
9
  import sys
9
10
 
11
+ from socketsecurity import __version__
12
+
10
13
  log = logging.getLogger(__name__)
11
14
 
12
15
 
16
+ def _build_caller_user_agent() -> str:
17
+ """Build the SOCKET_CALLER_USER_AGENT string forwarded to the coana CLI.
18
+
19
+ Mirrors the Node CLI's ``<product>/<version> <runtime>/<version> <platform>/<arch>``
20
+ shape so the backend can attribute reachability calls to the Python CLI.
21
+ """
22
+ return (
23
+ f"socket/{__version__} "
24
+ f"python/{platform.python_version()} "
25
+ f"{platform.system().lower()}/{platform.machine().lower()}"
26
+ )
27
+
28
+
13
29
  class ReachabilityAnalyzer:
14
30
  def __init__(self, sdk: socketdev, api_token: str):
15
31
  self.sdk = sdk
@@ -108,6 +124,8 @@ class ReachabilityAnalyzer:
108
124
  continue_on_install_errors: bool = False,
109
125
  continue_on_missing_lock_files: bool = False,
110
126
  continue_on_no_source_files: bool = False,
127
+ reach_debug: bool = False,
128
+ disable_external_tool_checks: bool = False,
111
129
  ) -> Dict[str, Any]:
112
130
  """
113
131
  Run reachability analysis.
@@ -147,8 +165,7 @@ class ReachabilityAnalyzer:
147
165
 
148
166
  # Add required arguments
149
167
  output_dir = str(pathlib.Path(output_path).parent)
150
- log.warning(f"output_dir: {output_dir}")
151
- log.warning(f"output_path: {output_path}")
168
+ log.debug(f"output_dir: {output_dir}, output_path: {output_path}")
152
169
  cmd.extend([
153
170
  "--output-dir", output_dir,
154
171
  "--socket-mode", output_path,
@@ -197,6 +214,12 @@ class ReachabilityAnalyzer:
197
214
  if enable_debug:
198
215
  cmd.append("-d")
199
216
 
217
+ if reach_debug:
218
+ cmd.append("--debug")
219
+
220
+ if disable_external_tool_checks:
221
+ cmd.append("--disable-external-tool-checks")
222
+
200
223
  if use_only_pregenerated_sboms:
201
224
  cmd.append("--use-only-pregenerated-sboms")
202
225
 
@@ -222,14 +245,25 @@ class ReachabilityAnalyzer:
222
245
  # Required environment variables for Coana CLI
223
246
  env["SOCKET_ORG_SLUG"] = org_slug
224
247
  env["SOCKET_CLI_API_TOKEN"] = self.api_token
225
-
226
- # Optional environment variables
248
+
249
+ # Identify the calling CLI to the coana tool / backend (parity with the Node CLI).
250
+ env["SOCKET_CLI_VERSION"] = __version__
251
+ env["SOCKET_CALLER_USER_AGENT"] = _build_caller_user_agent()
252
+
253
+ # NOTE: no proxy env is set here. coana already reads HTTPS_PROXY/HTTP_PROXY itself, and
254
+ # we pass the full parent env above, so it inherits them. A SOCKET_CLI_API_PROXY override
255
+ # should only be set from an explicit --proxy flag (not yet implemented), since seeding it
256
+ # from HTTPS_PROXY would be a no-op (it's the same value coana already resolves).
257
+
258
+ # Optional environment variables.
259
+ # NOTE: repo/branch are intentionally omitted by the caller (passed as None) when they
260
+ # are the default sentinels, to avoid polluting coana's per-repo/branch cache buckets.
227
261
  if repo_name:
228
262
  env["SOCKET_REPO_NAME"] = repo_name
229
-
263
+
230
264
  if branch_name:
231
265
  env["SOCKET_BRANCH_NAME"] = branch_name
232
-
266
+
233
267
  # Set NODE_TLS_REJECT_UNAUTHORIZED=0 if allow_unverified is True
234
268
  if allow_unverified:
235
269
  env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0"
@@ -95,6 +95,12 @@ def _write_attribution_file(config, payload: dict) -> None:
95
95
 
96
96
  DEFAULT_API_TIMEOUT = 1200
97
97
 
98
+ # Sentinel repo/branch names used when none can be detected from git or supplied via flags.
99
+ # When the repo/branch are these defaults we skip forwarding SOCKET_REPO_NAME/SOCKET_BRANCH_NAME
100
+ # to the coana CLI so unrelated default-named runs don't share reachability cache buckets.
101
+ DEFAULT_REPO_NAME = "socket-default-repo"
102
+ DEFAULT_BRANCH_NAME = "socket-default-branch"
103
+
98
104
 
99
105
  def get_api_request_timeout(config: CliConfig) -> int:
100
106
  return config.timeout if config.timeout is not None else DEFAULT_API_TIMEOUT
@@ -288,16 +294,21 @@ def main_code():
288
294
  except NoSuchPathError:
289
295
  raise Exception(f"Unable to find path {config.target_path}")
290
296
 
297
+ # Track whether repo/branch fell back to the default sentinels so reachability can skip
298
+ # forwarding them as coana cache-bucket keys (computed before any workspace suffixing).
299
+ repo_defaulted = not config.repo
300
+ branch_defaulted = not config.branch
301
+
291
302
  if not config.repo:
292
- base_repo_name = "socket-default-repo"
303
+ base_repo_name = DEFAULT_REPO_NAME
293
304
  if config.workspace_name:
294
305
  config.repo = f"{base_repo_name}-{config.workspace_name}"
295
306
  else:
296
307
  config.repo = base_repo_name
297
308
  log.debug(f"Using default repository name: {config.repo}")
298
-
309
+
299
310
  if not config.branch:
300
- config.branch = "socket-default-branch"
311
+ config.branch = DEFAULT_BRANCH_NAME
301
312
  log.debug(f"Using default branch name: {config.branch}")
302
313
 
303
314
  # Calculate the scan paths - combine target_path with sub_paths if provided
@@ -377,15 +388,23 @@ def main_code():
377
388
  timeout=config.reach_analysis_timeout,
378
389
  memory_limit=config.reach_analysis_memory_limit,
379
390
  ecosystems=config.reach_ecosystems,
380
- exclude_paths=config.reach_exclude_paths,
391
+ # Union the deprecated --reach-exclude-paths with the unified --exclude-paths
392
+ # and forward verbatim to coana's --exclude-dirs. Patterns are scan-root
393
+ # relative; coana resolves --exclude-dirs relative to its `run` target, which
394
+ # here is `.` == cwd == scan root, so passthrough is correct. If a nested
395
+ # target is ever supported, re-anchor patterns to the target first (see Node's
396
+ # pathRelativeToTarget in exclude-paths.mts).
397
+ exclude_paths=(
398
+ (config.reach_exclude_paths or []) + (config.exclude_paths or [])
399
+ ) or None,
381
400
  min_severity=config.reach_min_severity,
382
401
  skip_cache=config.reach_skip_cache or False,
383
402
  disable_analytics=config.reach_disable_analytics or False,
384
403
  enable_analysis_splitting=config.reach_enable_analysis_splitting or False,
385
404
  detailed_analysis_log_file=config.reach_detailed_analysis_log_file or False,
386
405
  lazy_mode=config.reach_lazy_mode or False,
387
- repo_name=config.repo,
388
- branch_name=config.branch,
406
+ repo_name=None if repo_defaulted else config.repo,
407
+ branch_name=None if branch_defaulted else config.branch,
389
408
  version=config.reach_version,
390
409
  concurrency=config.reach_concurrency,
391
410
  additional_params=config.reach_additional_params,
@@ -396,6 +415,8 @@ def main_code():
396
415
  continue_on_install_errors=config.reach_continue_on_install_errors,
397
416
  continue_on_missing_lock_files=config.reach_continue_on_missing_lock_files,
398
417
  continue_on_no_source_files=config.reach_continue_on_no_source_files,
418
+ reach_debug=config.reach_debug,
419
+ disable_external_tool_checks=config.reach_disable_external_tool_checks,
399
420
  )
400
421
 
401
422
  log.info("Reachability analysis completed successfully")