Skip to content

Commit de87c12

Browse files
committed
Merge remote-tracking branch 'origin/main' into martin/get-rid-of-tier-naming-convention
# Conflicts: # CHANGELOG.md # pyproject.toml # socketsecurity/__init__.py # uv.lock
2 parents b996097 + f59513f commit de87c12

4 files changed

Lines changed: 97 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@
1313
old→new name mapping.
1414
- Bumped the pinned `@coana-tech/cli` version to `15.5.7`.
1515

16+
## 2.4.12
17+
18+
### Changed: consolidated coana launcher env vars into `SOCKET_CLI_COANA_LAUNCHER`
19+
20+
- The reachability launcher is now tuned via a single `SOCKET_CLI_COANA_LAUNCHER`
21+
environment variable (mirroring the Socket Node CLI): `auto` (default when unset; try
22+
`npx` first, fall back to `npm install` + `node` on launcher-level failures),
23+
`npm-install` (skip `npx` entirely), or `npx` (never fall back). An unrecognized value
24+
logs a warning and behaves as `auto`.
25+
- The legacy `SOCKET_CLI_COANA_FORCE_NPM_INSTALL` and `SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK`
26+
variables remain supported for back-compat when `SOCKET_CLI_COANA_LAUNCHER` is unset, but
27+
are deprecated and no longer documented.
28+
1629
## 2.4.11
1730

1831
### Changed: units for `--reach-analysis-timeout` and `--reach-analysis-memory-limit`

docs/cli-reference.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,9 +325,10 @@ For CI-specific examples and guidance, see [`ci-cd.md`](ci-cd.md).
325325

326326
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.
327327

328-
The launcher fallback can be tuned via environment variables:
329-
- `SOCKET_CLI_COANA_FORCE_NPM_INSTALL` — skip `npx` entirely and always use the `npm install` + `node` path (useful where `npx` is known-broken).
330-
- `SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK` — never fall back; surface the `npx` failure directly.
328+
The launcher can be tuned via the `SOCKET_CLI_COANA_LAUNCHER` environment variable:
329+
- `auto` (default when unset) — try `npx` first; fall back to `npm install` + `node` if the launcher fails before the engine starts.
330+
- `npm-install` — skip `npx` entirely and always use the `npm install` + `node` path (useful where `npx` is known-broken).
331+
- `npx` — never fall back; surface the `npx` failure directly.
331332

332333
#### Advanced Configuration
333334
| Parameter | Required | Default | Description |

socketsecurity/core/tools/reachability.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,31 @@ def _npx_launcher_failed_before_coana(returncode: int) -> bool:
304304
"""
305305
return returncode < 0 or returncode >= 128
306306

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+
307332
def _spawn_coana(
308333
self,
309334
coana_args: List[str],
@@ -324,14 +349,15 @@ def _spawn_coana(
324349
325350
Fallback path: if npx is missing, or its launcher dies before coana starts, install
326351
@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).
329355
"""
330356
effective_version = self._resolve_coana_version(version)
331357
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()
333359

334-
if os.environ.get("SOCKET_CLI_COANA_FORCE_NPM_INSTALL"):
360+
if launcher_mode == "npm-install":
335361
return self._spawn_coana_via_npm_install(coana_args, effective_version, coana_env, cwd)
336362

337363
package_spec = f"@coana-tech/cli@{effective_version}"
@@ -347,15 +373,15 @@ def _spawn_coana(
347373
)
348374
except FileNotFoundError:
349375
# npx is not on PATH: the launcher provably never started coana.
350-
if disable_fallback:
376+
if launcher_mode == "npx":
351377
raise
352378
log.warning("npx not found on PATH; retrying reachability analysis via `npm install` + `node`.")
353379
return self._spawn_coana_via_npm_install(coana_args, effective_version, coana_env, cwd)
354380

355381
if result.returncode == 0:
356382
return 0
357383

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):
359385
log.warning(
360386
f"npx launcher failed (exit {result.returncode}) before coana started; "
361387
"retrying reachability analysis via `npm install` + `node`."

tests/unit/test_reachability.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,54 @@ def fake_run(argv, **_kw):
317317
assert all(c[:2] != ["npm", "install"] for c in calls)
318318

319319

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+
320368
def test_fallback_installs_once_per_version(analyzer, mocker):
321369
"""A second in-process fallback for the same version reuses the install (no re-install)."""
322370
mocker.patch.object(analyzer, "_extract_scan_id", return_value="scan-123")

0 commit comments

Comments
 (0)