socketsecurity 2.4.2__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.2 → socketsecurity-2.4.3}/CHANGELOG.md +14 -0
  2. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/PKG-INFO +1 -1
  3. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/docs/cli-reference.md +12 -9
  4. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/pyproject.toml +1 -1
  5. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/__init__.py +1 -1
  6. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/config.py +67 -1
  7. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/__init__.py +81 -2
  8. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/socketcli.py +9 -1
  9. socketsecurity-2.4.3/tests/unit/test_exclude_paths.py +177 -0
  10. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/uv.lock +1 -1
  11. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/CODEOWNERS +0 -0
  12. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
  13. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  14. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
  15. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  16. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/actions/setup-docker/action.yml +0 -0
  17. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/actions/setup-hatch/action.yml +0 -0
  18. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/actions/setup-sfw/action.yml +0 -0
  19. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/dependabot.yml +0 -0
  20. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/workflows/dependency-review.yml +0 -0
  21. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/workflows/docker-stable.yml +0 -0
  22. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/workflows/e2e-test.yml +0 -0
  23. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/workflows/pr-preview.yml +0 -0
  24. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/workflows/python-tests.yml +0 -0
  25. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/workflows/release.yml +0 -0
  26. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/workflows/version-check.yml +0 -0
  27. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.github/zizmor.yml +0 -0
  28. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.gitignore +0 -0
  29. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.hooks/sync_version.py +0 -0
  30. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.pre-commit-config.yaml +0 -0
  31. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/.python-version +0 -0
  32. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/Dockerfile +0 -0
  33. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/LICENSE +0 -0
  34. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/Makefile +0 -0
  35. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/README.md +0 -0
  36. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/docs/ci-cd.md +0 -0
  37. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/docs/development.md +0 -0
  38. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/docs/troubleshooting.md +0 -0
  39. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/examples/config/sarif-dashboard-parity.json +0 -0
  40. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/examples/config/sarif-dashboard-parity.toml +0 -0
  41. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/examples/config/sarif-diff-ci-cd.json +0 -0
  42. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/examples/config/sarif-diff-ci-cd.toml +0 -0
  43. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/examples/config/sarif-instance-detail.json +0 -0
  44. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/examples/config/sarif-instance-detail.toml +0 -0
  45. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/instructions/gitlab-commit-status/uat.md +0 -0
  46. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/pytest.ini +0 -0
  47. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/scripts/build_container.sh +0 -0
  48. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/scripts/build_container_flexible.sh +0 -0
  49. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/scripts/deploy-test-docker.sh +0 -0
  50. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/scripts/deploy-test-pypi.sh +0 -0
  51. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/scripts/docker-entrypoint.sh +0 -0
  52. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/scripts/run.sh +0 -0
  53. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/session.md +0 -0
  54. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socket.yml +0 -0
  55. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/alert_selection.py +0 -0
  56. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/classes.py +0 -0
  57. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/cli_client.py +0 -0
  58. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/exceptions.py +0 -0
  59. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/git_interface.py +0 -0
  60. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/helper/__init__.py +0 -0
  61. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
  62. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/lazy_file_loader.py +0 -0
  63. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/logging.py +0 -0
  64. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/messages.py +0 -0
  65. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/resource_utils.py +0 -0
  66. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/scm/__init__.py +0 -0
  67. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/scm/base.py +0 -0
  68. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/scm/client.py +0 -0
  69. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/scm/github.py +0 -0
  70. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/scm/gitlab.py +0 -0
  71. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/scm_comments.py +0 -0
  72. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/socket_config.py +0 -0
  73. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/tools/reachability.py +0 -0
  74. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/core/utils.py +0 -0
  75. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/fossa_compat.py +0 -0
  76. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/output.py +0 -0
  77. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/plugins/__init__.py +0 -0
  78. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/plugins/base.py +0 -0
  79. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/plugins/formatters/__init__.py +0 -0
  80. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/plugins/formatters/slack.py +0 -0
  81. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/plugins/jira.py +0 -0
  82. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/plugins/manager.py +0 -0
  83. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/plugins/slack.py +0 -0
  84. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/plugins/teams.py +0 -0
  85. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/socketsecurity/plugins/webhook.py +0 -0
  86. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/__init__.py +0 -0
  87. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/core/conftest.py +0 -0
  88. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/core/create_diff_input.json +0 -0
  89. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/core/test_diff_alerts.py +0 -0
  90. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/core/test_diff_generation.py +0 -0
  91. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/core/test_facts_compression.py +0 -0
  92. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/core/test_has_manifest_files.py +0 -0
  93. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/core/test_package_and_alerts.py +0 -0
  94. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/core/test_sdk_methods.py +0 -0
  95. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/core/test_supporting_methods.py +0 -0
  96. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/fullscans/create_response.json +0 -0
  97. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/fullscans/diff/stream_diff.json +0 -0
  98. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
  99. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/fullscans/head_scan/metadata.json +0 -0
  100. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
  101. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
  102. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/fullscans/new_scan/metadata.json +0 -0
  103. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
  104. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/repos/repo_info_error.json +0 -0
  105. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/repos/repo_info_no_head.json +0 -0
  106. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/repos/repo_info_success.json +0 -0
  107. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/data/settings/security-policy.json +0 -0
  108. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/e2e/fixtures/simple-npm/index.js +0 -0
  109. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/e2e/fixtures/simple-npm/package.json +0 -0
  110. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/e2e/fixtures/simple-pypi/requirements.txt +0 -0
  111. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/e2e/validate-gitlab.sh +0 -0
  112. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/e2e/validate-json.sh +0 -0
  113. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/e2e/validate-reachability.sh +0 -0
  114. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/e2e/validate-sarif.sh +0 -0
  115. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/e2e/validate-scan.sh +0 -0
  116. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/fixtures/fossa/README.md +0 -0
  117. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-analyze-empty.json +0 -0
  118. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-analyze-populated.json +0 -0
  119. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-sbom-empty-deep.json +0 -0
  120. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-sbom-populated.json +0 -0
  121. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/__init__.py +0 -0
  122. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_alert_selection.py +0 -0
  123. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_cli_config.py +0 -0
  124. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_client.py +0 -0
  125. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_config.py +0 -0
  126. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_dependency_overview.py +0 -0
  127. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_disable_ignore.py +0 -0
  128. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_fossa_compat.py +0 -0
  129. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_fossa_parity.py +0 -0
  130. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_gitlab_auth.py +0 -0
  131. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_gitlab_auth_fallback.py +0 -0
  132. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_gitlab_commit_status.py +0 -0
  133. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_gitlab_format.py +0 -0
  134. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_ignore_telemetry_filtering.py +0 -0
  135. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_output.py +0 -0
  136. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_reachability.py +0 -0
  137. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_slack_plugin.py +0 -0
  138. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_socketcli.py +0 -0
  139. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/tests/unit/test_tier1_finalize.py +0 -0
  140. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/workflows/bitbucket-pipelines.yml +0 -0
  141. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/workflows/buildkite.yml +0 -0
  142. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/workflows/github-actions.yml +0 -0
  143. {socketsecurity-2.4.2 → socketsecurity-2.4.3}/workflows/gitlab-ci.yml +0 -0
@@ -1,5 +1,19 @@
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
+
3
17
  ## 2.4.2
4
18
 
5
19
  ### Added: reachability flag and Coana environment alignment with the Node CLI
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socketsecurity
3
- Version: 2.4.2
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.2"
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.2'
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)
@@ -167,6 +212,12 @@ class CliConfig:
167
212
 
168
213
  args = parser.parse_args(args_list)
169
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
+
170
221
  # Get API token from env or args (check multiple env var names)
171
222
  api_token = (
172
223
  os.getenv("SOCKET_SECURITY_API_KEY") or
@@ -258,6 +309,7 @@ class CliConfig:
258
309
  'reach_lazy_mode': args.reach_lazy_mode,
259
310
  'reach_ecosystems': args.reach_ecosystems.split(',') if args.reach_ecosystems else None,
260
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),
261
313
  'reach_skip_cache': args.reach_skip_cache,
262
314
  'reach_min_severity': args.reach_min_severity,
263
315
  'reach_output_file': args.reach_output_file,
@@ -361,6 +413,10 @@ class CliConfig:
361
413
  logging.error("--sarif-reachability potentially/reachable-or-potentially requires --sarif-scope full")
362
414
  exit(1)
363
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
+
364
420
  # Validate that only_facts_file requires reach
365
421
  if args.only_facts_file and not args.reach:
366
422
  logging.error("--only-facts-file requires --reach to be specified")
@@ -570,6 +626,15 @@ def create_argument_parser() -> argparse.ArgumentParser:
570
626
  help="List of ecosystems to exclude from analysis (JSON array string)"
571
627
  )
572
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
+
573
638
  # Branch and Scan Configuration
574
639
  config_group = parser.add_argument_group('Branch and Scan Configuration')
575
640
  config_group.add_argument(
@@ -919,7 +984,8 @@ def create_argument_parser() -> argparse.ArgumentParser:
919
984
  "--reach-exclude-paths",
920
985
  dest="reach_exclude_paths",
921
986
  metavar="<list>",
922
- 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."
923
989
  )
924
990
  reachability_group.add_argument(
925
991
  "--reach-min-severity",
@@ -213,6 +213,67 @@ class Core:
213
213
  return True
214
214
  return False
215
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
+
216
277
  def save_submitted_files_list(self, files: List[str], output_path: str) -> None:
217
278
  """
218
279
  Save the list of submitted file names to a JSON file for debugging.
@@ -336,6 +397,17 @@ class Core:
336
397
  start_time = time.time()
337
398
  files: Set[str] = set()
338
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
+
339
411
  # Get supported patterns from the API
340
412
  patterns = self.get_supported_patterns()
341
413
 
@@ -365,8 +437,15 @@ class Core:
365
437
 
366
438
  for glob_file in glob_files:
367
439
  glob_file_str = str(glob_file)
368
- if os.path.isfile(glob_file_str) and not Core.is_excluded(glob_file_str, self.config.excluded_dirs):
369
- 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("\\", "/"))
370
449
 
371
450
  glob_end = time.time()
372
451
  log.debug(f"Globbing took {glob_end - glob_start:.4f} seconds")
@@ -388,7 +388,15 @@ def main_code():
388
388
  timeout=config.reach_analysis_timeout,
389
389
  memory_limit=config.reach_analysis_memory_limit,
390
390
  ecosystems=config.reach_ecosystems,
391
- 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,
392
400
  min_severity=config.reach_min_severity,
393
401
  skip_cache=config.reach_skip_cache or False,
394
402
  disable_analytics=config.reach_disable_analytics or False,
@@ -0,0 +1,177 @@
1
+ """Tests for the unified --exclude-paths flag (G2, Node alignment).
2
+
3
+ Covers the path matcher, config parsing + soft-deprecation of --reach-exclude-paths,
4
+ and that --exclude-paths filters SCA manifest discovery via Core.find_files.
5
+ """
6
+ import logging
7
+ import types
8
+ from unittest.mock import MagicMock
9
+
10
+ import pytest
11
+
12
+ from socketsecurity.config import CliConfig
13
+ from socketsecurity.core import Core
14
+ from socketsecurity.core.socket_config import SocketConfig
15
+
16
+ # ---- matcher -------------------------------------------------------------
17
+
18
+ @pytest.mark.parametrize(
19
+ "rel, patterns, expected",
20
+ [
21
+ # directory prefix -> the directory's whole subtree
22
+ ("packages/legacy/package.json", ["packages/legacy"], True),
23
+ ("packages/keep/package.json", ["packages/legacy"], False),
24
+ # root-anchored: a bare name matches at the root only, NOT nested
25
+ ("tests/x.json", ["tests"], True),
26
+ ("src/tests/x.json", ["tests"], False),
27
+ # **/ matches at any depth
28
+ ("src/tests/x.json", ["**/tests"], True),
29
+ ("tests/unit/x.json", ["tests/**"], True),
30
+ ("tests", ["tests/**"], False), # P/** is the subtree, not P itself
31
+ # '*' does NOT cross '/': anchored basename glob is root-level only
32
+ ("index.spec.ts", ["*.spec.ts"], True),
33
+ ("src/app/index.spec.ts", ["*.spec.ts"], False),
34
+ ("src/app/index.spec.ts", ["**/*.spec.ts"], True),
35
+ ("src/app/index.ts", ["**/*.spec.ts"], False),
36
+ # single-star matches exactly one path segment
37
+ ("packages/a/node_modules/x.json", ["packages/*/node_modules"], True),
38
+ ("packages/a/b/node_modules/x.json", ["packages/*/node_modules"], False),
39
+ ],
40
+ )
41
+ def test_matches_exclude_paths(rel, patterns, expected):
42
+ assert Core.matches_exclude_paths(rel, ".", patterns) is expected
43
+
44
+
45
+ @pytest.mark.parametrize(
46
+ "pattern, excluded, kept",
47
+ [
48
+ # Node parity cases (src/commands/scan/exclude-paths.mts), anchored at scan root.
49
+ ("tests", "tests/pkg/package.json", "src/tests/package.json"),
50
+ ("package-lock.json", "package-lock.json", "packages/a/package-lock.json"),
51
+ ("**/node_modules", "packages/a/node_modules/dep/package.json", "src/app/package.json"),
52
+ ("packages/legacy", "packages/legacy/p.json", "packages/legacy-x/p.json"),
53
+ ("src/*.json", "src/a.json", "src/sub/a.json"),
54
+ ],
55
+ )
56
+ def test_matches_exclude_paths_node_parity(pattern, excluded, kept):
57
+ assert Core.matches_exclude_paths(excluded, ".", [pattern]) is True
58
+ assert Core.matches_exclude_paths(kept, ".", [pattern]) is False
59
+
60
+
61
+ def test_matches_exclude_paths_empty_is_false():
62
+ assert Core.matches_exclude_paths("a/b.json", ".", []) is False
63
+ assert Core.matches_exclude_paths("a/b.json", ".", [" "]) is False
64
+
65
+
66
+ # ---- config parsing ------------------------------------------------------
67
+
68
+ BASE_ARGS = ["--api-token", "test-token", "--repo", "test-repo"]
69
+
70
+
71
+ def test_exclude_paths_parses_to_list():
72
+ config = CliConfig.from_args(BASE_ARGS + ["--exclude-paths", "tests/**, packages/legacy , *.spec.ts"])
73
+ assert config.exclude_paths == ["tests/**", "packages/legacy", "*.spec.ts"]
74
+
75
+
76
+ def test_exclude_paths_defaults_none():
77
+ config = CliConfig.from_args(BASE_ARGS)
78
+ assert config.exclude_paths is None
79
+
80
+
81
+ def test_reach_exclude_paths_still_works_and_warns(caplog):
82
+ with caplog.at_level(logging.WARNING):
83
+ config = CliConfig.from_args(BASE_ARGS + ["--reach", "--reach-exclude-paths", "a,b"])
84
+ assert config.reach_exclude_paths == ["a", "b"]
85
+ assert any("deprecated" in r.message for r in caplog.records)
86
+
87
+
88
+ @pytest.mark.parametrize(
89
+ "bad",
90
+ ["!foo", "/abs/path", "..", "../escape", "a/../b", ".", "**", "**/", "/**", "./", "./**"],
91
+ )
92
+ def test_exclude_paths_validation_rejects(bad):
93
+ with pytest.raises(SystemExit) as exc:
94
+ CliConfig.from_args(BASE_ARGS + ["--exclude-paths", bad])
95
+ assert exc.value.code == 1
96
+
97
+
98
+ def test_exclude_paths_validation_rejects_within_csv():
99
+ with pytest.raises(SystemExit) as exc:
100
+ CliConfig.from_args(BASE_ARGS + ["--exclude-paths", "src,..,tests"])
101
+ assert exc.value.code == 1
102
+
103
+
104
+ def _write_config(tmp_path, value):
105
+ import json
106
+ path = tmp_path / "socketcli.json"
107
+ path.write_text(json.dumps({"socketcli": {"exclude_paths": value}}), encoding="utf-8")
108
+ return str(path)
109
+
110
+
111
+ def test_exclude_paths_from_config_file_list(tmp_path):
112
+ """A JSON list in --config flows through normalization (not just CSV strings)."""
113
+ cfg = _write_config(tmp_path, ["tests/**", "packages/legacy"])
114
+ config = CliConfig.from_args(BASE_ARGS + ["--config", cfg])
115
+ assert config.exclude_paths == ["tests/**", "packages/legacy"]
116
+
117
+
118
+ def test_exclude_paths_from_config_file_string(tmp_path):
119
+ cfg = _write_config(tmp_path, "tests/**, packages/legacy")
120
+ config = CliConfig.from_args(BASE_ARGS + ["--config", cfg])
121
+ assert config.exclude_paths == ["tests/**", "packages/legacy"]
122
+
123
+
124
+ def test_exclude_paths_from_config_file_is_validated(tmp_path):
125
+ """Config-file patterns are validated too (not bypassed)."""
126
+ cfg = _write_config(tmp_path, ["../escape"])
127
+ with pytest.raises(SystemExit) as exc:
128
+ CliConfig.from_args(BASE_ARGS + ["--config", cfg])
129
+ assert exc.value.code == 1
130
+
131
+
132
+ def test_exclude_paths_valid_globs_accepted():
133
+ config = CliConfig.from_args(BASE_ARGS + ["--exclude-paths", "tests/**,**/*.spec.ts,packages/legacy"])
134
+ assert config.exclude_paths == ["tests/**", "**/*.spec.ts", "packages/legacy"]
135
+
136
+
137
+ # ---- find_files integration ---------------------------------------------
138
+
139
+ def _make_core(exclude_paths):
140
+ core = Core.__new__(Core)
141
+ core.config = SocketConfig(api_key="test-key")
142
+ core.cli_config = types.SimpleNamespace(exclude_paths=exclude_paths)
143
+ core.sdk = MagicMock()
144
+ return core
145
+
146
+
147
+ def _seed_manifests(tmp_path):
148
+ for rel in ("package.json", "sub/package.json", "legacy/package.json"):
149
+ p = tmp_path / rel
150
+ p.parent.mkdir(parents=True, exist_ok=True)
151
+ p.write_text("{}", encoding="utf-8")
152
+
153
+
154
+ def test_find_files_excludes_matching_paths(tmp_path, mocker):
155
+ _seed_manifests(tmp_path)
156
+ core = _make_core(["legacy"])
157
+ mocker.patch.object(
158
+ core, "get_supported_patterns",
159
+ return_value={"npm": {"package.json": {"pattern": "package.json"}}},
160
+ )
161
+
162
+ found = core.find_files(str(tmp_path))
163
+ assert any(f.endswith("/package.json") and "/legacy/" not in f for f in found)
164
+ assert not any("/legacy/" in f for f in found)
165
+
166
+
167
+ def test_find_files_no_exclude_paths_keeps_all(tmp_path, mocker):
168
+ _seed_manifests(tmp_path)
169
+ core = _make_core(None)
170
+ mocker.patch.object(
171
+ core, "get_supported_patterns",
172
+ return_value={"npm": {"package.json": {"pattern": "package.json"}}},
173
+ )
174
+
175
+ found = core.find_files(str(tmp_path))
176
+ assert any("/legacy/" in f for f in found)
177
+ assert len(found) == 3
@@ -1270,7 +1270,7 @@ wheels = [
1270
1270
 
1271
1271
  [[package]]
1272
1272
  name = "socketsecurity"
1273
- version = "2.4.2"
1273
+ version = "2.4.3"
1274
1274
  source = { editable = "." }
1275
1275
  dependencies = [
1276
1276
  { name = "brotli", marker = "platform_python_implementation == 'CPython'" },
File without changes
File without changes
File without changes