socketsecurity 2.4.11__tar.gz → 2.4.12__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 (151) hide show
  1. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/CHANGELOG.md +13 -0
  2. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/PKG-INFO +1 -1
  3. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/docs/cli-reference.md +4 -3
  4. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/pyproject.toml +1 -1
  5. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/__init__.py +1 -1
  6. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/tools/reachability.py +32 -6
  7. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_reachability.py +48 -0
  8. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/uv.lock +1 -1
  9. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/CODEOWNERS +0 -0
  10. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
  11. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  12. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
  13. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  14. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/actions/setup-docker/action.yml +0 -0
  15. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/actions/setup-hatch/action.yml +0 -0
  16. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/actions/setup-sfw/action.yml +0 -0
  17. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/dependabot.yml +0 -0
  18. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/workflows/dependency-review.yml +0 -0
  19. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/workflows/docker-stable.yml +0 -0
  20. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/workflows/e2e-test.yml +0 -0
  21. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/workflows/pr-preview.yml +0 -0
  22. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/workflows/python-tests.yml +0 -0
  23. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/workflows/release.yml +0 -0
  24. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/workflows/version-check.yml +0 -0
  25. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.github/zizmor.yml +0 -0
  26. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.gitignore +0 -0
  27. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.hooks/sync_version.py +0 -0
  28. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.pre-commit-config.yaml +0 -0
  29. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/.python-version +0 -0
  30. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/Dockerfile +0 -0
  31. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/LICENSE +0 -0
  32. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/Makefile +0 -0
  33. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/README.md +0 -0
  34. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/docs/ci-cd.md +0 -0
  35. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/docs/development.md +0 -0
  36. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/docs/troubleshooting.md +0 -0
  37. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/examples/config/sarif-dashboard-parity.json +0 -0
  38. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/examples/config/sarif-dashboard-parity.toml +0 -0
  39. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/examples/config/sarif-diff-ci-cd.json +0 -0
  40. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/examples/config/sarif-diff-ci-cd.toml +0 -0
  41. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/examples/config/sarif-instance-detail.json +0 -0
  42. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/examples/config/sarif-instance-detail.toml +0 -0
  43. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/instructions/gitlab-commit-status/uat.md +0 -0
  44. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/pytest.ini +0 -0
  45. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/scripts/build_container.sh +0 -0
  46. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/scripts/build_container_flexible.sh +0 -0
  47. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/scripts/deploy-test-docker.sh +0 -0
  48. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/scripts/deploy-test-pypi.sh +0 -0
  49. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/scripts/docker-entrypoint.sh +0 -0
  50. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/scripts/run.sh +0 -0
  51. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/session.md +0 -0
  52. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socket.yml +0 -0
  53. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/config.py +0 -0
  54. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/__init__.py +0 -0
  55. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/alert_selection.py +0 -0
  56. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/classes.py +0 -0
  57. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/cli_client.py +0 -0
  58. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/cli_run.py +0 -0
  59. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/exceptions.py +0 -0
  60. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/git_interface.py +0 -0
  61. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/helper/__init__.py +0 -0
  62. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
  63. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/lazy_file_loader.py +0 -0
  64. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/log_uploader.py +0 -0
  65. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/logging.py +0 -0
  66. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/messages.py +0 -0
  67. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/resource_utils.py +0 -0
  68. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/scm/__init__.py +0 -0
  69. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/scm/base.py +0 -0
  70. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/scm/client.py +0 -0
  71. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/scm/github.py +0 -0
  72. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/scm/gitlab.py +0 -0
  73. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/scm_comments.py +0 -0
  74. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/socket_config.py +0 -0
  75. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/streaming.py +0 -0
  76. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/core/utils.py +0 -0
  77. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/fossa_compat.py +0 -0
  78. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/output.py +0 -0
  79. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/plugins/__init__.py +0 -0
  80. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/plugins/base.py +0 -0
  81. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/plugins/formatters/__init__.py +0 -0
  82. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/plugins/formatters/slack.py +0 -0
  83. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/plugins/jira.py +0 -0
  84. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/plugins/manager.py +0 -0
  85. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/plugins/slack.py +0 -0
  86. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/plugins/teams.py +0 -0
  87. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/plugins/webhook.py +0 -0
  88. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/socketsecurity/socketcli.py +0 -0
  89. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/__init__.py +0 -0
  90. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/core/conftest.py +0 -0
  91. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/core/create_diff_input.json +0 -0
  92. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/core/test_diff_alerts.py +0 -0
  93. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/core/test_diff_generation.py +0 -0
  94. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/core/test_facts_compression.py +0 -0
  95. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/core/test_has_manifest_files.py +0 -0
  96. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/core/test_package_and_alerts.py +0 -0
  97. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/core/test_sdk_methods.py +0 -0
  98. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/core/test_supporting_methods.py +0 -0
  99. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/fullscans/create_response.json +0 -0
  100. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/fullscans/diff/stream_diff.json +0 -0
  101. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
  102. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/fullscans/head_scan/metadata.json +0 -0
  103. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
  104. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
  105. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/fullscans/new_scan/metadata.json +0 -0
  106. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
  107. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/repos/repo_info_error.json +0 -0
  108. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/repos/repo_info_no_head.json +0 -0
  109. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/repos/repo_info_success.json +0 -0
  110. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/data/settings/security-policy.json +0 -0
  111. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/e2e/fixtures/simple-npm/index.js +0 -0
  112. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/e2e/fixtures/simple-npm/package.json +0 -0
  113. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/e2e/fixtures/simple-pypi/requirements.txt +0 -0
  114. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/e2e/validate-gitlab.sh +0 -0
  115. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/e2e/validate-json.sh +0 -0
  116. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/e2e/validate-reachability.sh +0 -0
  117. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/e2e/validate-sarif.sh +0 -0
  118. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/e2e/validate-scan.sh +0 -0
  119. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/fixtures/fossa/README.md +0 -0
  120. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/fixtures/fossa/fossa-analyze-empty.json +0 -0
  121. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/fixtures/fossa/fossa-analyze-populated.json +0 -0
  122. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/fixtures/fossa/fossa-sbom-empty-deep.json +0 -0
  123. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/fixtures/fossa/fossa-sbom-populated.json +0 -0
  124. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/__init__.py +0 -0
  125. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_alert_selection.py +0 -0
  126. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_cli_config.py +0 -0
  127. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_cli_run.py +0 -0
  128. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_client.py +0 -0
  129. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_config.py +0 -0
  130. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_dependency_overview.py +0 -0
  131. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_disable_ignore.py +0 -0
  132. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_exclude_paths.py +0 -0
  133. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_fossa_compat.py +0 -0
  134. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_fossa_parity.py +0 -0
  135. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_full_scan_retry.py +0 -0
  136. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_gitlab_auth.py +0 -0
  137. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_gitlab_auth_fallback.py +0 -0
  138. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_gitlab_commit_status.py +0 -0
  139. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_gitlab_format.py +0 -0
  140. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_ignore_telemetry_filtering.py +0 -0
  141. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_include_dirs.py +0 -0
  142. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_log_uploader.py +0 -0
  143. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_output.py +0 -0
  144. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_slack_plugin.py +0 -0
  145. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_socketcli.py +0 -0
  146. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_streaming.py +0 -0
  147. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/tests/unit/test_tier1_finalize.py +0 -0
  148. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/workflows/bitbucket-pipelines.yml +0 -0
  149. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/workflows/buildkite.yml +0 -0
  150. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/workflows/github-actions.yml +0 -0
  151. {socketsecurity-2.4.11 → socketsecurity-2.4.12}/workflows/gitlab-ci.yml +0 -0
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.4.12
4
+
5
+ ### Changed: consolidated coana launcher env vars into `SOCKET_CLI_COANA_LAUNCHER`
6
+
7
+ - The reachability launcher is now tuned via a single `SOCKET_CLI_COANA_LAUNCHER`
8
+ environment variable (mirroring the Socket Node CLI): `auto` (default when unset; try
9
+ `npx` first, fall back to `npm install` + `node` on launcher-level failures),
10
+ `npm-install` (skip `npx` entirely), or `npx` (never fall back). An unrecognized value
11
+ logs a warning and behaves as `auto`.
12
+ - The legacy `SOCKET_CLI_COANA_FORCE_NPM_INSTALL` and `SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK`
13
+ variables remain supported for back-compat when `SOCKET_CLI_COANA_LAUNCHER` is unset, but
14
+ are deprecated and no longer documented.
15
+
3
16
  ## 2.4.11
4
17
 
5
18
  ### Changed: units for `--reach-analysis-timeout` and `--reach-analysis-memory-limit`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socketsecurity
3
- Version: 2.4.11
3
+ Version: 2.4.12
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>
@@ -317,9 +317,10 @@ For CI-specific examples and guidance, see [`ci-cd.md`](ci-cd.md).
317
317
 
318
318
  The CLI runs a pinned `@coana-tech/cli` version via `npx --yes --force` (the same flags the Socket Node CLI passes for coana); it does **not** auto-update the engine or install it globally. `--yes` skips npx's interactive install prompt so non-interactive/CI runs don't hang. If the `npx` launcher is unavailable or fails before the engine starts, the CLI falls back to `npm install`-ing the pinned version into a temp directory and running it via `node`. Pass `--reach-version latest` to opt into the newest published version. Use `--reach` to enable reachability analysis during a full scan, or add `--only-facts-file` (with `--reach`) to submit only the reachability facts file (`.socket.facts.json`) when creating the full scan.
319
319
 
320
- The launcher fallback can be tuned via environment variables:
321
- - `SOCKET_CLI_COANA_FORCE_NPM_INSTALL` — skip `npx` entirely and always use the `npm install` + `node` path (useful where `npx` is known-broken).
322
- - `SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK` — never fall back; surface the `npx` failure directly.
320
+ The launcher can be tuned via the `SOCKET_CLI_COANA_LAUNCHER` environment variable:
321
+ - `auto` (default when unset) try `npx` first; fall back to `npm install` + `node` if the launcher fails before the engine starts.
322
+ - `npm-install` — skip `npx` entirely and always use the `npm install` + `node` path (useful where `npx` is known-broken).
323
+ - `npx` — never fall back; surface the `npx` failure directly.
323
324
 
324
325
  #### Advanced Configuration
325
326
  | Parameter | Required | Default | Description |
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "socketsecurity"
9
- version = "2.4.11"
9
+ version = "2.4.12"
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.11'
2
+ __version__ = '2.4.12'
3
3
  USER_AGENT = f'SocketPythonCLI/{__version__}'
@@ -304,6 +304,31 @@ class ReachabilityAnalyzer:
304
304
  """
305
305
  return returncode < 0 or returncode >= 128
306
306
 
307
+ @staticmethod
308
+ def _resolve_coana_launcher_mode() -> str:
309
+ """Resolve the coana launcher mode: ``auto``, ``npx``, or ``npm-install``.
310
+
311
+ ``SOCKET_CLI_COANA_LAUNCHER`` wins when set to a recognized value; an unrecognized
312
+ value warns and behaves as ``auto``. Only when it is unset/empty do the legacy vars
313
+ apply: ``SOCKET_CLI_COANA_FORCE_NPM_INSTALL`` -> ``npm-install``, else
314
+ ``SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK`` -> ``npx``. Mirrors the Node CLI.
315
+ """
316
+ raw = os.environ.get("SOCKET_CLI_COANA_LAUNCHER", "")
317
+ mode = raw.strip().lower()
318
+ if mode in ("auto", "npx", "npm-install"):
319
+ return mode
320
+ if mode:
321
+ log.warning(
322
+ f'Ignoring unrecognized SOCKET_CLI_COANA_LAUNCHER value "{raw}"; '
323
+ 'expected "auto", "npm-install", or "npx".'
324
+ )
325
+ return "auto"
326
+ if os.environ.get("SOCKET_CLI_COANA_FORCE_NPM_INSTALL"):
327
+ return "npm-install"
328
+ if os.environ.get("SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK"):
329
+ return "npx"
330
+ return "auto"
331
+
307
332
  def _spawn_coana(
308
333
  self,
309
334
  coana_args: List[str],
@@ -324,14 +349,15 @@ class ReachabilityAnalyzer:
324
349
 
325
350
  Fallback path: if npx is missing, or its launcher dies before coana starts, install
326
351
  @coana-tech/cli into a temp dir via ``npm install`` and run it directly via ``node``.
327
- Toggle with ``SOCKET_CLI_COANA_FORCE_NPM_INSTALL`` (use the fallback as the primary
328
- path) and ``SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK`` (never fall back).
352
+ Tune with ``SOCKET_CLI_COANA_LAUNCHER``: ``auto`` (default; npx with the npm-install
353
+ fallback), ``npm-install`` (skip npx, always use the fallback path), or ``npx``
354
+ (never fall back).
329
355
  """
330
356
  effective_version = self._resolve_coana_version(version)
331
357
  coana_env = self._sanitize_coana_env(env)
332
- disable_fallback = bool(os.environ.get("SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK"))
358
+ launcher_mode = self._resolve_coana_launcher_mode()
333
359
 
334
- if os.environ.get("SOCKET_CLI_COANA_FORCE_NPM_INSTALL"):
360
+ if launcher_mode == "npm-install":
335
361
  return self._spawn_coana_via_npm_install(coana_args, effective_version, coana_env, cwd)
336
362
 
337
363
  package_spec = f"@coana-tech/cli@{effective_version}"
@@ -347,7 +373,7 @@ class ReachabilityAnalyzer:
347
373
  )
348
374
  except FileNotFoundError:
349
375
  # npx is not on PATH: the launcher provably never started coana.
350
- if disable_fallback:
376
+ if launcher_mode == "npx":
351
377
  raise
352
378
  log.warning("npx not found on PATH; retrying reachability analysis via `npm install` + `node`.")
353
379
  return self._spawn_coana_via_npm_install(coana_args, effective_version, coana_env, cwd)
@@ -355,7 +381,7 @@ class ReachabilityAnalyzer:
355
381
  if result.returncode == 0:
356
382
  return 0
357
383
 
358
- if not disable_fallback and self._npx_launcher_failed_before_coana(result.returncode):
384
+ if launcher_mode != "npx" and self._npx_launcher_failed_before_coana(result.returncode):
359
385
  log.warning(
360
386
  f"npx launcher failed (exit {result.returncode}) before coana started; "
361
387
  "retrying reachability analysis via `npm install` + `node`."
@@ -317,6 +317,54 @@ def test_disable_fallback_propagates_npx_failure(analyzer, mocker, monkeypatch):
317
317
  assert all(c[:2] != ["npm", "install"] for c in calls)
318
318
 
319
319
 
320
+ def test_launcher_npm_install_skips_npx(analyzer, mocker, monkeypatch):
321
+ """SOCKET_CLI_COANA_LAUNCHER=npm-install routes straight to npm install + node."""
322
+ monkeypatch.setenv("SOCKET_CLI_COANA_LAUNCHER", "npm-install")
323
+ calls = _capture_spawns(analyzer, mocker, npx_behavior=0)
324
+ assert all(c[0] != "npx" for c in calls)
325
+ assert calls[0][:2] == ["npm", "install"]
326
+ assert calls[1][0] == "node"
327
+
328
+
329
+ def test_launcher_npx_propagates_npx_failure(analyzer, mocker, monkeypatch):
330
+ """SOCKET_CLI_COANA_LAUNCHER=npx: a launcher failure is NOT retried via npm."""
331
+ monkeypatch.setenv("SOCKET_CLI_COANA_LAUNCHER", "npx")
332
+ mocker.patch.object(analyzer, "_extract_scan_id", return_value=None)
333
+ calls = []
334
+
335
+ def fake_run(argv, **_kw):
336
+ calls.append(argv)
337
+ m = MagicMock()
338
+ m.returncode = 137
339
+ return m
340
+
341
+ mocker.patch.object(reachability.subprocess, "run", side_effect=fake_run)
342
+ with pytest.raises(Exception):
343
+ analyzer.run_reachability_analysis(org_slug="my-org", target_directory=".")
344
+ assert calls[0][0] == "npx"
345
+ assert all(c[:2] != ["npm", "install"] for c in calls)
346
+
347
+
348
+ def test_launcher_overrides_legacy_vars(analyzer, mocker, monkeypatch):
349
+ """A recognized SOCKET_CLI_COANA_LAUNCHER wins; legacy vars are ignored entirely."""
350
+ monkeypatch.setenv("SOCKET_CLI_COANA_LAUNCHER", "auto")
351
+ monkeypatch.setenv("SOCKET_CLI_COANA_FORCE_NPM_INSTALL", "1")
352
+ monkeypatch.setenv("SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK", "1")
353
+ calls = _capture_spawns(analyzer, mocker, npx_behavior=137)
354
+ assert calls[0][0] == "npx" # force-npm-install ignored: npx still attempted
355
+ assert calls[1][:2] == ["npm", "install"] # disable-fallback ignored: fallback runs
356
+ assert calls[2][0] == "node"
357
+
358
+
359
+ def test_launcher_unrecognized_value_behaves_as_auto(analyzer, mocker, monkeypatch):
360
+ """An unrecognized SOCKET_CLI_COANA_LAUNCHER value warns and behaves as auto."""
361
+ monkeypatch.setenv("SOCKET_CLI_COANA_LAUNCHER", "bogus")
362
+ calls = _capture_spawns(analyzer, mocker, npx_behavior=137)
363
+ assert calls[0][0] == "npx"
364
+ assert calls[1][:2] == ["npm", "install"]
365
+ assert calls[2][0] == "node"
366
+
367
+
320
368
  def test_fallback_installs_once_per_version(analyzer, mocker):
321
369
  """A second in-process fallback for the same version reuses the install (no re-install)."""
322
370
  mocker.patch.object(analyzer, "_extract_scan_id", return_value="scan-123")
@@ -1283,7 +1283,7 @@ wheels = [
1283
1283
 
1284
1284
  [[package]]
1285
1285
  name = "socketsecurity"
1286
- version = "2.4.11"
1286
+ version = "2.4.12"
1287
1287
  source = { editable = "." }
1288
1288
  dependencies = [
1289
1289
  { name = "brotli", marker = "platform_python_implementation == 'CPython'" },
File without changes