socketsecurity 2.4.1__tar.gz → 2.4.2__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.2}/CHANGELOG.md +20 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/PKG-INFO +1 -1
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/pyproject.toml +1 -1
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/__init__.py +1 -1
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/config.py +36 -4
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/__init__.py +40 -12
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/tools/reachability.py +40 -6
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/socketcli.py +18 -5
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_config.py +49 -0
- socketsecurity-2.4.2/tests/unit/test_reachability.py +106 -0
- socketsecurity-2.4.2/tests/unit/test_tier1_finalize.py +70 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/uv.lock +1 -1
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/CODEOWNERS +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/actions/setup-docker/action.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/actions/setup-hatch/action.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/actions/setup-sfw/action.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/dependabot.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/workflows/dependency-review.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/workflows/docker-stable.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/workflows/e2e-test.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/workflows/pr-preview.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/workflows/python-tests.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/workflows/release.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/workflows/version-check.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.github/zizmor.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.gitignore +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.hooks/sync_version.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.pre-commit-config.yaml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/.python-version +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/Dockerfile +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/LICENSE +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/Makefile +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/README.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/docs/ci-cd.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/docs/cli-reference.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/docs/development.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/docs/troubleshooting.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/examples/config/sarif-dashboard-parity.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/examples/config/sarif-dashboard-parity.toml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/examples/config/sarif-diff-ci-cd.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/examples/config/sarif-diff-ci-cd.toml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/examples/config/sarif-instance-detail.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/examples/config/sarif-instance-detail.toml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/instructions/gitlab-commit-status/uat.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/pytest.ini +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/scripts/build_container.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/scripts/build_container_flexible.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/scripts/deploy-test-docker.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/scripts/deploy-test-pypi.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/scripts/docker-entrypoint.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/scripts/run.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/session.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socket.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/alert_selection.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/cli_client.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/helper/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/lazy_file_loader.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/logging.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/messages.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/resource_utils.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/scm/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/scm/base.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/scm/client.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/scm/github.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/scm/gitlab.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/socket_config.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/utils.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/fossa_compat.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/output.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/plugins/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/plugins/base.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/plugins/formatters/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/plugins/formatters/slack.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/plugins/jira.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/plugins/manager.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/plugins/slack.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/plugins/teams.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/plugins/webhook.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/core/conftest.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/core/create_diff_input.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/core/test_diff_alerts.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/core/test_diff_generation.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/core/test_facts_compression.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/core/test_has_manifest_files.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/core/test_package_and_alerts.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/core/test_sdk_methods.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/core/test_supporting_methods.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/create_response.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/diff/stream_diff.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/head_scan/metadata.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/new_scan/metadata.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/repos/repo_info_error.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/repos/repo_info_no_head.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/repos/repo_info_success.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/settings/security-policy.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/e2e/fixtures/simple-npm/index.js +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/e2e/fixtures/simple-npm/package.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/e2e/fixtures/simple-pypi/requirements.txt +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/e2e/validate-gitlab.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/e2e/validate-json.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/e2e/validate-reachability.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/e2e/validate-sarif.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/e2e/validate-scan.sh +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/fixtures/fossa/README.md +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/fixtures/fossa/fossa-analyze-empty.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/fixtures/fossa/fossa-analyze-populated.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/fixtures/fossa/fossa-sbom-empty-deep.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/fixtures/fossa/fossa-sbom-populated.json +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/__init__.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_alert_selection.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_cli_config.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_client.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_dependency_overview.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_disable_ignore.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_fossa_compat.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_fossa_parity.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_gitlab_auth.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_gitlab_auth_fallback.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_gitlab_commit_status.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_gitlab_format.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_ignore_telemetry_filtering.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_output.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_slack_plugin.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/unit/test_socketcli.py +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/workflows/bitbucket-pipelines.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/workflows/buildkite.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/workflows/github-actions.yml +0 -0
- {socketsecurity-2.4.1 → socketsecurity-2.4.2}/workflows/gitlab-ci.yml +0 -0
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.4.2
|
|
4
|
+
|
|
5
|
+
### Added: reachability flag and Coana environment alignment with the Node CLI
|
|
6
|
+
|
|
7
|
+
- New `--reach-disable-external-tool-checks` flag (passes `--disable-external-tool-checks`
|
|
8
|
+
to the Coana CLI).
|
|
9
|
+
- New `--reach-debug` flag to enable Coana debug output (`--debug`) independently of the
|
|
10
|
+
global `--enable-debug`.
|
|
11
|
+
- Node-style `--reach-analysis-timeout` and `--reach-analysis-memory-limit` are now the
|
|
12
|
+
primary flag names; the previous `--reach-timeout` / `--reach-memory-limit` continue to
|
|
13
|
+
work as hidden aliases.
|
|
14
|
+
- The Coana subprocess now receives `SOCKET_CLI_VERSION` and `SOCKET_CALLER_USER_AGENT` so
|
|
15
|
+
calls are attributed to the Python CLI. Proxies continue to work via the inherited
|
|
16
|
+
`HTTPS_PROXY` / `HTTP_PROXY` environment variables, which Coana reads itself.
|
|
17
|
+
- `SOCKET_REPO_NAME` / `SOCKET_BRANCH_NAME` are no longer forwarded to Coana when the repo
|
|
18
|
+
and branch are the default sentinels, avoiding cross-run reachability cache-bucket
|
|
19
|
+
collisions.
|
|
20
|
+
- Tier 1 reachability finalize now retries with exponential backoff instead of giving up on
|
|
21
|
+
the first transient error.
|
|
22
|
+
|
|
3
23
|
## 2.4.1
|
|
4
24
|
|
|
5
25
|
### Added: pyenv in the Docker image
|
|
@@ -139,6 +139,8 @@ class CliConfig:
|
|
|
139
139
|
reach_continue_on_install_errors: bool = False
|
|
140
140
|
reach_continue_on_missing_lock_files: bool = False
|
|
141
141
|
reach_continue_on_no_source_files: bool = False
|
|
142
|
+
reach_debug: bool = False
|
|
143
|
+
reach_disable_external_tool_checks: bool = False
|
|
142
144
|
max_purl_batch_size: int = 5000
|
|
143
145
|
enable_commit_status: bool = False
|
|
144
146
|
legal: bool = False
|
|
@@ -267,6 +269,8 @@ class CliConfig:
|
|
|
267
269
|
'reach_continue_on_install_errors': args.reach_continue_on_install_errors,
|
|
268
270
|
'reach_continue_on_missing_lock_files': args.reach_continue_on_missing_lock_files,
|
|
269
271
|
'reach_continue_on_no_source_files': args.reach_continue_on_no_source_files,
|
|
272
|
+
'reach_debug': args.reach_debug,
|
|
273
|
+
'reach_disable_external_tool_checks': args.reach_disable_external_tool_checks,
|
|
270
274
|
'max_purl_batch_size': args.max_purl_batch_size,
|
|
271
275
|
'enable_commit_status': args.enable_commit_status,
|
|
272
276
|
'legal': args.legal or args.legal_format == "fossa",
|
|
@@ -878,18 +882,32 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
878
882
|
help="Specific version of @coana-tech/cli to use (e.g., '1.2.3')"
|
|
879
883
|
)
|
|
880
884
|
reachability_group.add_argument(
|
|
881
|
-
"--reach-timeout",
|
|
885
|
+
"--reach-analysis-timeout",
|
|
882
886
|
dest="reach_analysis_timeout",
|
|
883
887
|
type=int,
|
|
884
888
|
metavar="<seconds>",
|
|
885
889
|
help="Timeout for reachability analysis in seconds"
|
|
886
890
|
)
|
|
891
|
+
# Backwards-compatible alias for the pre-alignment name. Kept working, hidden from help.
|
|
887
892
|
reachability_group.add_argument(
|
|
888
|
-
"--reach-
|
|
893
|
+
"--reach-timeout",
|
|
894
|
+
dest="reach_analysis_timeout",
|
|
895
|
+
type=int,
|
|
896
|
+
help=argparse.SUPPRESS
|
|
897
|
+
)
|
|
898
|
+
reachability_group.add_argument(
|
|
899
|
+
"--reach-analysis-memory-limit",
|
|
889
900
|
dest="reach_analysis_memory_limit",
|
|
890
901
|
type=int,
|
|
891
902
|
metavar="<mb>",
|
|
892
|
-
help="Memory limit for reachability analysis in MB"
|
|
903
|
+
help="Memory limit for reachability analysis in MB (defaults to the coana CLI's own default, currently 8192)"
|
|
904
|
+
)
|
|
905
|
+
# Backwards-compatible alias for the pre-alignment name. Kept working, hidden from help.
|
|
906
|
+
reachability_group.add_argument(
|
|
907
|
+
"--reach-memory-limit",
|
|
908
|
+
dest="reach_analysis_memory_limit",
|
|
909
|
+
type=int,
|
|
910
|
+
help=argparse.SUPPRESS
|
|
893
911
|
)
|
|
894
912
|
reachability_group.add_argument(
|
|
895
913
|
"--reach-ecosystems",
|
|
@@ -957,7 +975,7 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
957
975
|
dest="reach_concurrency",
|
|
958
976
|
type=int,
|
|
959
977
|
metavar="<number>",
|
|
960
|
-
help="Concurrency level for reachability analysis (must be >= 1)"
|
|
978
|
+
help="Concurrency level for reachability analysis (must be >= 1; defaults to the coana CLI's own default, currently 1)"
|
|
961
979
|
)
|
|
962
980
|
reachability_group.add_argument(
|
|
963
981
|
"--reach-additional-params",
|
|
@@ -1002,6 +1020,20 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
1002
1020
|
action="store_true",
|
|
1003
1021
|
help=argparse.SUPPRESS
|
|
1004
1022
|
)
|
|
1023
|
+
reachability_group.add_argument(
|
|
1024
|
+
"--reach-debug",
|
|
1025
|
+
dest="reach_debug",
|
|
1026
|
+
action="store_true",
|
|
1027
|
+
help="Enable debug output for the reachability analysis (passes --debug to the coana CLI). "
|
|
1028
|
+
"Independent of the global --enable-debug flag."
|
|
1029
|
+
)
|
|
1030
|
+
reachability_group.add_argument(
|
|
1031
|
+
"--reach-disable-external-tool-checks",
|
|
1032
|
+
dest="reach_disable_external_tool_checks",
|
|
1033
|
+
action="store_true",
|
|
1034
|
+
help="Disable coana's external tool availability checks during reachability analysis "
|
|
1035
|
+
"(passes --disable-external-tool-checks to the coana CLI)."
|
|
1036
|
+
)
|
|
1005
1037
|
|
|
1006
1038
|
parser.add_argument(
|
|
1007
1039
|
'--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.
|
|
@@ -549,20 +554,43 @@ class Core:
|
|
|
549
554
|
log.debug(f"Failed to read tier1ReachabilityScanId from {facts_file_path}: {e}")
|
|
550
555
|
return False
|
|
551
556
|
|
|
552
|
-
# Call the SDK to finalize the tier 1 scan
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
557
|
+
# Call the SDK to finalize the tier 1 scan, retrying transient failures with backoff.
|
|
558
|
+
last_error: Optional[Exception] = None
|
|
559
|
+
for attempt in range(1, TIER1_FINALIZE_MAX_ATTEMPTS + 1):
|
|
560
|
+
try:
|
|
561
|
+
success = self.sdk.fullscans.finalize_tier1(
|
|
562
|
+
full_scan_id=full_scan_id,
|
|
563
|
+
tier1_reachability_scan_id=tier1_scan_id,
|
|
564
|
+
)
|
|
558
565
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
566
|
+
if success:
|
|
567
|
+
log.debug(f"Successfully finalized tier 1 scan {tier1_scan_id} for full scan {full_scan_id}")
|
|
568
|
+
return True
|
|
562
569
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
570
|
+
log.debug(
|
|
571
|
+
f"finalize_tier1 returned a falsy result for scan {tier1_scan_id} "
|
|
572
|
+
f"(attempt {attempt}/{TIER1_FINALIZE_MAX_ATTEMPTS})"
|
|
573
|
+
)
|
|
574
|
+
except Exception as e:
|
|
575
|
+
last_error = e
|
|
576
|
+
log.debug(
|
|
577
|
+
f"Unable to finalize tier 1 scan (attempt {attempt}/{TIER1_FINALIZE_MAX_ATTEMPTS}): {e}"
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
if attempt < TIER1_FINALIZE_MAX_ATTEMPTS:
|
|
581
|
+
time.sleep(TIER1_FINALIZE_BACKOFF_SECONDS * (2 ** (attempt - 1)))
|
|
582
|
+
|
|
583
|
+
if last_error is not None:
|
|
584
|
+
log.debug(
|
|
585
|
+
f"Giving up finalizing tier 1 scan {tier1_scan_id} after "
|
|
586
|
+
f"{TIER1_FINALIZE_MAX_ATTEMPTS} attempts: {last_error}"
|
|
587
|
+
)
|
|
588
|
+
else:
|
|
589
|
+
log.debug(
|
|
590
|
+
f"Giving up finalizing tier 1 scan {tier1_scan_id} after "
|
|
591
|
+
f"{TIER1_FINALIZE_MAX_ATTEMPTS} attempts"
|
|
592
|
+
)
|
|
593
|
+
return False
|
|
566
594
|
|
|
567
595
|
@staticmethod
|
|
568
596
|
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
|
|
@@ -384,8 +395,8 @@ def main_code():
|
|
|
384
395
|
enable_analysis_splitting=config.reach_enable_analysis_splitting or False,
|
|
385
396
|
detailed_analysis_log_file=config.reach_detailed_analysis_log_file or False,
|
|
386
397
|
lazy_mode=config.reach_lazy_mode or False,
|
|
387
|
-
repo_name=config.repo,
|
|
388
|
-
branch_name=config.branch,
|
|
398
|
+
repo_name=None if repo_defaulted else config.repo,
|
|
399
|
+
branch_name=None if branch_defaulted else config.branch,
|
|
389
400
|
version=config.reach_version,
|
|
390
401
|
concurrency=config.reach_concurrency,
|
|
391
402
|
additional_params=config.reach_additional_params,
|
|
@@ -396,6 +407,8 @@ def main_code():
|
|
|
396
407
|
continue_on_install_errors=config.reach_continue_on_install_errors,
|
|
397
408
|
continue_on_missing_lock_files=config.reach_continue_on_missing_lock_files,
|
|
398
409
|
continue_on_no_source_files=config.reach_continue_on_no_source_files,
|
|
410
|
+
reach_debug=config.reach_debug,
|
|
411
|
+
disable_external_tool_checks=config.reach_disable_external_tool_checks,
|
|
399
412
|
)
|
|
400
413
|
|
|
401
414
|
log.info("Reachability analysis completed successfully")
|
|
@@ -166,6 +166,55 @@ class TestCliConfigValidation:
|
|
|
166
166
|
assert config.sarif_reachability == "reachable"
|
|
167
167
|
|
|
168
168
|
|
|
169
|
+
class TestReachAlignmentFlags:
|
|
170
|
+
"""Tests for the reachability flag/default alignment with the Node CLI."""
|
|
171
|
+
|
|
172
|
+
BASE_ARGS = ["--api-token", "test-token", "--repo", "test-repo"]
|
|
173
|
+
|
|
174
|
+
def test_reach_defaults_are_unset_and_delegated_to_coana(self):
|
|
175
|
+
"""memory-limit/concurrency/timeout are not hardcoded; omitted so coana applies its
|
|
176
|
+
own defaults (8192 MB / concurrency 1 / 600s), which already match what we'd set."""
|
|
177
|
+
config = CliConfig.from_args(self.BASE_ARGS + ["--reach"])
|
|
178
|
+
assert config.reach_analysis_memory_limit is None
|
|
179
|
+
assert config.reach_concurrency is None
|
|
180
|
+
assert config.reach_analysis_timeout is None
|
|
181
|
+
|
|
182
|
+
def test_reach_node_style_name_aliases(self):
|
|
183
|
+
"""G8: Node-style primary names map to the same dests."""
|
|
184
|
+
config = CliConfig.from_args(
|
|
185
|
+
self.BASE_ARGS
|
|
186
|
+
+ ["--reach", "--reach-analysis-timeout", "300", "--reach-analysis-memory-limit", "2048"]
|
|
187
|
+
)
|
|
188
|
+
assert config.reach_analysis_timeout == 300
|
|
189
|
+
assert config.reach_analysis_memory_limit == 2048
|
|
190
|
+
|
|
191
|
+
def test_reach_legacy_name_aliases_still_work(self):
|
|
192
|
+
"""G8: pre-alignment names keep working (hidden aliases)."""
|
|
193
|
+
config = CliConfig.from_args(
|
|
194
|
+
self.BASE_ARGS + ["--reach", "--reach-timeout", "111", "--reach-memory-limit", "512"]
|
|
195
|
+
)
|
|
196
|
+
assert config.reach_analysis_timeout == 111
|
|
197
|
+
assert config.reach_analysis_memory_limit == 512
|
|
198
|
+
|
|
199
|
+
def test_reach_debug_flag(self):
|
|
200
|
+
"""G9: dedicated --reach-debug flag, independent of --enable-debug."""
|
|
201
|
+
config = CliConfig.from_args(self.BASE_ARGS + ["--reach", "--reach-debug"])
|
|
202
|
+
assert config.reach_debug is True
|
|
203
|
+
assert config.enable_debug is False
|
|
204
|
+
|
|
205
|
+
def test_reach_disable_external_tool_checks_flag(self):
|
|
206
|
+
"""G1: --reach-disable-external-tool-checks parses to its dest."""
|
|
207
|
+
config = CliConfig.from_args(
|
|
208
|
+
self.BASE_ARGS + ["--reach", "--reach-disable-external-tool-checks"]
|
|
209
|
+
)
|
|
210
|
+
assert config.reach_disable_external_tool_checks is True
|
|
211
|
+
|
|
212
|
+
def test_reach_new_flags_default_false(self):
|
|
213
|
+
config = CliConfig.from_args(self.BASE_ARGS + ["--reach"])
|
|
214
|
+
assert config.reach_debug is False
|
|
215
|
+
assert config.reach_disable_external_tool_checks is False
|
|
216
|
+
|
|
217
|
+
|
|
169
218
|
def test_pyproject_requires_python_matches_tomllib_usage():
|
|
170
219
|
pyproject = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
|
|
171
220
|
requires_python = pyproject["project"]["requires-python"]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Tests for the reachability coana-CLI command/env construction (Node alignment).
|
|
2
|
+
|
|
3
|
+
These cover the arg-builder and environment wiring in
|
|
4
|
+
``socketsecurity.core.tools.reachability.ReachabilityAnalyzer`` without actually
|
|
5
|
+
invoking npm/npx/coana: ``_ensure_coana_cli_installed`` and ``subprocess.run`` are mocked.
|
|
6
|
+
"""
|
|
7
|
+
from unittest.mock import MagicMock
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from socketsecurity import __version__
|
|
12
|
+
from socketsecurity.core.tools import reachability
|
|
13
|
+
from socketsecurity.core.tools.reachability import (
|
|
14
|
+
ReachabilityAnalyzer,
|
|
15
|
+
_build_caller_user_agent,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def analyzer():
|
|
21
|
+
return ReachabilityAnalyzer(MagicMock(), "test-api-token")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _run(analyzer, mocker, **kwargs):
|
|
25
|
+
"""Invoke run_reachability_analysis with npm/npx/coana mocked; return (cmd, env)."""
|
|
26
|
+
mocker.patch.object(analyzer, "_ensure_coana_cli_installed", return_value="@coana-tech/cli")
|
|
27
|
+
mocker.patch.object(analyzer, "_extract_scan_id", return_value="scan-123")
|
|
28
|
+
completed = MagicMock()
|
|
29
|
+
completed.returncode = 0
|
|
30
|
+
run_mock = mocker.patch.object(reachability.subprocess, "run", return_value=completed)
|
|
31
|
+
|
|
32
|
+
analyzer.run_reachability_analysis(org_slug="my-org", target_directory=".", **kwargs)
|
|
33
|
+
|
|
34
|
+
cmd = run_mock.call_args.args[0]
|
|
35
|
+
env = run_mock.call_args.kwargs["env"]
|
|
36
|
+
return cmd, env
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_build_caller_user_agent_shape():
|
|
40
|
+
ua = _build_caller_user_agent()
|
|
41
|
+
parts = ua.split(" ")
|
|
42
|
+
assert parts[0] == f"socket/{__version__}"
|
|
43
|
+
assert parts[1].startswith("python/")
|
|
44
|
+
assert "/" in parts[2] # platform/arch
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_reach_debug_appends_debug_long_flag(analyzer, mocker):
|
|
48
|
+
"""G9: --reach-debug -> coana --debug; does not emit the global -d."""
|
|
49
|
+
cmd, _ = _run(analyzer, mocker, reach_debug=True)
|
|
50
|
+
assert "--debug" in cmd
|
|
51
|
+
assert "-d" not in cmd
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_enable_debug_still_emits_short_d(analyzer, mocker):
|
|
55
|
+
"""G9: existing global --enable-debug -> -d behavior is unchanged."""
|
|
56
|
+
cmd, _ = _run(analyzer, mocker, enable_debug=True)
|
|
57
|
+
assert "-d" in cmd
|
|
58
|
+
assert "--debug" not in cmd
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_disable_external_tool_checks(analyzer, mocker):
|
|
62
|
+
"""G1: --reach-disable-external-tool-checks -> coana --disable-external-tool-checks."""
|
|
63
|
+
cmd, _ = _run(analyzer, mocker, disable_external_tool_checks=True)
|
|
64
|
+
assert "--disable-external-tool-checks" in cmd
|
|
65
|
+
|
|
66
|
+
cmd2, _ = _run(analyzer, mocker)
|
|
67
|
+
assert "--disable-external-tool-checks" not in cmd2
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_concurrency_and_memory_args(analyzer, mocker):
|
|
71
|
+
"""G7: explicit concurrency/memory propagate as coana args."""
|
|
72
|
+
cmd, _ = _run(analyzer, mocker, concurrency=1, memory_limit=8192)
|
|
73
|
+
assert "--concurrency" in cmd and cmd[cmd.index("--concurrency") + 1] == "1"
|
|
74
|
+
assert "--memory-limit" in cmd and cmd[cmd.index("--memory-limit") + 1] == "8192"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_env_identifies_python_cli(analyzer, mocker):
|
|
78
|
+
"""G5: SOCKET_CLI_VERSION + SOCKET_CALLER_USER_AGENT forwarded to coana."""
|
|
79
|
+
_, env = _run(analyzer, mocker)
|
|
80
|
+
assert env["SOCKET_CLI_VERSION"] == __version__
|
|
81
|
+
assert env["SOCKET_CALLER_USER_AGENT"].startswith("socket/")
|
|
82
|
+
assert env["SOCKET_ORG_SLUG"] == "my-org"
|
|
83
|
+
assert env["SOCKET_CLI_API_TOKEN"] == "test-api-token"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_no_proxy_env_set_by_default(analyzer, mocker, monkeypatch):
|
|
87
|
+
"""coana inherits HTTPS_PROXY/HTTP_PROXY from the passed env; we don't set
|
|
88
|
+
SOCKET_CLI_API_PROXY ourselves (that's reserved for a future explicit --proxy flag)."""
|
|
89
|
+
monkeypatch.delenv("SOCKET_CLI_API_PROXY", raising=False)
|
|
90
|
+
monkeypatch.setenv("HTTPS_PROXY", "http://envproxy:3128")
|
|
91
|
+
_, env = _run(analyzer, mocker)
|
|
92
|
+
# Even with HTTPS_PROXY set, we don't copy it into SOCKET_CLI_API_PROXY (coana reads it itself).
|
|
93
|
+
assert "SOCKET_CLI_API_PROXY" not in env
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_repo_branch_env_present_when_supplied(analyzer, mocker):
|
|
97
|
+
_, env = _run(analyzer, mocker, repo_name="acme/widget", branch_name="main")
|
|
98
|
+
assert env["SOCKET_REPO_NAME"] == "acme/widget"
|
|
99
|
+
assert env["SOCKET_BRANCH_NAME"] == "main"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_repo_branch_env_absent_when_none(analyzer, mocker):
|
|
103
|
+
"""G6: caller passes None for default sentinels -> env keys omitted (cache hygiene)."""
|
|
104
|
+
_, env = _run(analyzer, mocker, repo_name=None, branch_name=None)
|
|
105
|
+
assert "SOCKET_REPO_NAME" not in env
|
|
106
|
+
assert "SOCKET_BRANCH_NAME" not in env
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Tests for tier1 reachability finalize retry/backoff (G11, Node parity)."""
|
|
2
|
+
import json
|
|
3
|
+
from unittest.mock import MagicMock
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from socketsecurity.core import TIER1_FINALIZE_MAX_ATTEMPTS, Core
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def core_with_mock_sdk():
|
|
12
|
+
# Build a Core without running org setup; we only exercise finalize_tier1_scan.
|
|
13
|
+
core = Core.__new__(Core)
|
|
14
|
+
core.sdk = MagicMock()
|
|
15
|
+
return core
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.fixture
|
|
19
|
+
def facts_file(tmp_path):
|
|
20
|
+
path = tmp_path / ".socket.facts.json"
|
|
21
|
+
path.write_text(json.dumps({"tier1ReachabilityScanId": "tier1-abc"}), encoding="utf-8")
|
|
22
|
+
return str(path)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture(autouse=True)
|
|
26
|
+
def no_sleep(mocker):
|
|
27
|
+
return mocker.patch("socketsecurity.core.time.sleep")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_finalize_succeeds_first_try(core_with_mock_sdk, facts_file, no_sleep):
|
|
31
|
+
core_with_mock_sdk.sdk.fullscans.finalize_tier1.return_value = True
|
|
32
|
+
|
|
33
|
+
assert core_with_mock_sdk.finalize_tier1_scan("full-1", facts_file) is True
|
|
34
|
+
assert core_with_mock_sdk.sdk.fullscans.finalize_tier1.call_count == 1
|
|
35
|
+
no_sleep.assert_not_called()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_finalize_retries_then_succeeds(core_with_mock_sdk, facts_file, no_sleep):
|
|
39
|
+
core_with_mock_sdk.sdk.fullscans.finalize_tier1.side_effect = [
|
|
40
|
+
Exception("transient"),
|
|
41
|
+
Exception("transient"),
|
|
42
|
+
True,
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
assert core_with_mock_sdk.finalize_tier1_scan("full-1", facts_file) is True
|
|
46
|
+
assert core_with_mock_sdk.sdk.fullscans.finalize_tier1.call_count == 3
|
|
47
|
+
assert no_sleep.call_count == 2 # backoff between the 3 attempts
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_finalize_exhausts_on_persistent_exception(core_with_mock_sdk, facts_file, no_sleep):
|
|
51
|
+
core_with_mock_sdk.sdk.fullscans.finalize_tier1.side_effect = Exception("down")
|
|
52
|
+
|
|
53
|
+
# Never raises; returns False after exhausting attempts.
|
|
54
|
+
assert core_with_mock_sdk.finalize_tier1_scan("full-1", facts_file) is False
|
|
55
|
+
assert core_with_mock_sdk.sdk.fullscans.finalize_tier1.call_count == TIER1_FINALIZE_MAX_ATTEMPTS
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_finalize_exhausts_on_persistent_falsy(core_with_mock_sdk, facts_file, no_sleep):
|
|
59
|
+
core_with_mock_sdk.sdk.fullscans.finalize_tier1.return_value = False
|
|
60
|
+
|
|
61
|
+
assert core_with_mock_sdk.finalize_tier1_scan("full-1", facts_file) is False
|
|
62
|
+
assert core_with_mock_sdk.sdk.fullscans.finalize_tier1.call_count == TIER1_FINALIZE_MAX_ATTEMPTS
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_finalize_returns_false_when_no_scan_id(core_with_mock_sdk, tmp_path):
|
|
66
|
+
path = tmp_path / ".socket.facts.json"
|
|
67
|
+
path.write_text(json.dumps({"components": []}), encoding="utf-8")
|
|
68
|
+
|
|
69
|
+
assert core_with_mock_sdk.finalize_tier1_scan("full-1", str(path)) is False
|
|
70
|
+
core_with_mock_sdk.sdk.fullscans.finalize_tier1.assert_not_called()
|
|
@@ -1270,7 +1270,7 @@ wheels = [
|
|
|
1270
1270
|
|
|
1271
1271
|
[[package]]
|
|
1272
1272
|
name = "socketsecurity"
|
|
1273
|
-
version = "2.4.
|
|
1273
|
+
version = "2.4.2"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{socketsecurity-2.4.1 → socketsecurity-2.4.2}/socketsecurity/core/helper/socket_facts_loader.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/diff/stream_diff_full.json
RENAMED
|
File without changes
|
|
File without changes
|
{socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/head_scan/stream_scan.json
RENAMED
|
File without changes
|
{socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/head_scan/stream_scan_full.json
RENAMED
|
File without changes
|
|
File without changes
|
{socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/data/fullscans/new_scan/stream_scan.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/e2e/fixtures/simple-pypi/requirements.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/fixtures/fossa/fossa-analyze-populated.json
RENAMED
|
File without changes
|
{socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/fixtures/fossa/fossa-sbom-empty-deep.json
RENAMED
|
File without changes
|
{socketsecurity-2.4.1 → socketsecurity-2.4.2}/tests/fixtures/fossa/fossa-sbom-populated.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|