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.
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/CHANGELOG.md +34 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/PKG-INFO +1 -1
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/docs/cli-reference.md +12 -9
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/pyproject.toml +1 -1
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/__init__.py +1 -1
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/config.py +103 -5
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/__init__.py +121 -14
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/tools/reachability.py +40 -6
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/socketcli.py +27 -6
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_config.py +49 -0
- socketsecurity-2.4.3/tests/unit/test_exclude_paths.py +177 -0
- socketsecurity-2.4.3/tests/unit/test_reachability.py +106 -0
- socketsecurity-2.4.3/tests/unit/test_tier1_finalize.py +70 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/uv.lock +1 -1
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/CODEOWNERS +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/actions/setup-docker/action.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/actions/setup-hatch/action.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/actions/setup-sfw/action.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/dependabot.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/dependency-review.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/docker-stable.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/e2e-test.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/pr-preview.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/python-tests.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/release.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/workflows/version-check.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.github/zizmor.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.gitignore +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.hooks/sync_version.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.pre-commit-config.yaml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/.python-version +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/Dockerfile +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/LICENSE +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/Makefile +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/README.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/docs/ci-cd.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/docs/development.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/docs/troubleshooting.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-dashboard-parity.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-dashboard-parity.toml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-diff-ci-cd.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-diff-ci-cd.toml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-instance-detail.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/examples/config/sarif-instance-detail.toml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/instructions/gitlab-commit-status/uat.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/pytest.ini +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/build_container.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/build_container_flexible.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/deploy-test-docker.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/deploy-test-pypi.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/docker-entrypoint.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/scripts/run.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/session.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socket.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/alert_selection.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/cli_client.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/helper/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/lazy_file_loader.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/logging.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/messages.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/resource_utils.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm/base.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm/client.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm/github.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm/gitlab.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/socket_config.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/core/utils.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/fossa_compat.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/output.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/base.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/formatters/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/formatters/slack.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/jira.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/manager.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/slack.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/teams.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/socketsecurity/plugins/webhook.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/conftest.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/create_diff_input.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_diff_alerts.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_diff_generation.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_facts_compression.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_has_manifest_files.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_package_and_alerts.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_sdk_methods.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/core/test_supporting_methods.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/create_response.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/diff/stream_diff.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/head_scan/metadata.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/new_scan/metadata.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/repos/repo_info_error.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/repos/repo_info_no_head.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/repos/repo_info_success.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/data/settings/security-policy.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/fixtures/simple-npm/index.js +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/fixtures/simple-npm/package.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/fixtures/simple-pypi/requirements.txt +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/validate-gitlab.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/validate-json.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/validate-reachability.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/validate-sarif.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/e2e/validate-scan.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/fixtures/fossa/README.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-analyze-empty.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-analyze-populated.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-sbom-empty-deep.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/fixtures/fossa/fossa-sbom-populated.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_alert_selection.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_cli_config.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_client.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_dependency_overview.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_disable_ignore.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_fossa_compat.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_fossa_parity.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_gitlab_auth.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_gitlab_auth_fallback.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_gitlab_commit_status.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_gitlab_format.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_ignore_telemetry_filtering.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_output.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_slack_plugin.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/tests/unit/test_socketcli.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/workflows/bitbucket-pipelines.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/workflows/buildkite.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.3}/workflows/github-actions.yml +0 -0
- {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
|
|
@@ -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-
|
|
157
|
-
[--reach-min-severity {low,medium,high,critical}] [--reach-skip-cache] [--reach-disable-analytics] [--reach-
|
|
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`
|
|
243
|
-
| `--reach-memory-limit`
|
|
244
|
-
| `--reach-concurrency` | False |
|
|
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:**
|
|
@@ -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-
|
|
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
|
|
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)
|
|
364
|
-
|
|
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
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
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
|
-
|
|
560
|
-
|
|
561
|
-
|
|
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
|
-
|
|
564
|
-
|
|
565
|
-
|
|
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.
|
|
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
|
-
#
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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")
|