Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## 2.11.4

### Fixed: find the reachability facts file when `--target-path` isn't the working directory

- `--only-facts-file` and `--reach-use-only-pregenerated-sboms` now submit the
facts file, and `--reach` no longer warns `Facts file not found`.

### Changed: update the Docker image's PyPy to 3.12

- The amd64 Docker image now ships PyPy `8.0.1` (Python 3.12), up from PyPy
Expand Down
9 changes: 5 additions & 4 deletions socketsecurity/core/tools/reachability.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def run_reachability_analysis(
org_slug: Socket organization slug
target_directory: Directory to analyze
tar_hash: Tar hash from manifest upload or existing scan (optional)
output_path: Output file path for results
output_path: Output file path for results, relative to target_directory unless absolute
timeout: Analysis timeout, forwarded verbatim to coana --analysis-timeout
(coana parses the units, e.g. '90s', '10m', '1h'; a bare number is seconds)
memory_limit: Memory limit, forwarded verbatim to coana --memory-limit
Expand Down Expand Up @@ -282,16 +282,17 @@ def run_reachability_analysis(
log.error(f"Reachability analysis failed with exit code {returncode}")
raise Exception(f"Reachability analysis failed with exit code {returncode}")

# Extract scan ID from output file
scan_id = self._extract_scan_id(output_path)
# Coana runs in target_directory, so a relative output_path lands there.
report_path = os.path.join(target_directory, output_path)
scan_id = self._extract_scan_id(report_path)

log.info("Reachability analysis completed successfully")
if scan_id:
log.info(f"Scan ID: {scan_id}")

return {
"scan_id": scan_id,
"report_path": output_path,
"report_path": report_path,
"tar_hash_used": tar_hash
}

Expand Down
7 changes: 3 additions & 4 deletions socketsecurity/socketcli.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ def main_code():

# If only-facts-file mode, mark the facts file for submission
if config.only_facts_file:
facts_file_to_submit = os.path.abspath(output_path)
facts_file_to_submit = os.path.abspath(result['report_path'])
log.info(f"Only-facts-file mode: will submit only {facts_file_to_submit}")

# If reach-use-only-pregenerated-sboms mode, submit CDX, SPDX, and facts file
Expand All @@ -570,9 +570,8 @@ def main_code():
sbom_files_to_submit = []
for scan_path in scan_paths:
sbom_files_to_submit.extend(core.find_sbom_files(scan_path))
# Use relative path for facts file
if os.path.exists(output_path):
sbom_files_to_submit.append(output_path)
if os.path.exists(result['report_path']):
sbom_files_to_submit.append(result['report_path'])
log.info(f"Pre-generated SBOMs mode: will submit {len(sbom_files_to_submit)} files (CDX, SPDX, and facts file)")

except Exception as e:
Expand Down
25 changes: 24 additions & 1 deletion tests/unit/test_reachability.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@

VERSION = "1.2.3"

# Records what it was run with in $FAKE_COANA_RUN, or fails when passed --fail.
# Records what it was run with in $FAKE_COANA_RUN, or fails when passed --fail. Like Coana, it
# writes the facts file to the --socket-mode path, resolved against its cwd.
FAKE_CLI_WRAPPER = """
import { writeFileSync } from 'node:fs'
const args = process.argv.slice(2)
if (args.includes('--fail')) process.exit(1)
writeFileSync(process.env.FAKE_COANA_RUN, JSON.stringify({ args, cwd: process.cwd(), env: process.env }))
writeFileSync(args[args.indexOf('--socket-mode') + 1], JSON.stringify({ tier1ReachabilityScanId: 'reach-scan-id' }))
"""


Expand Down Expand Up @@ -84,6 +86,27 @@ def test_runs_the_cached_cli_wrapper_with_the_args_and_cwd(fake_run_path):
assert run["cwd"] == str(fake_run_path.parent)


@pytest.mark.parametrize("absolute", [False, True])
def test_report_path_is_where_coana_wrote_the_facts_file(fake_run_path, tmp_path, monkeypatch, absolute):
target = tmp_path / "project"
target.mkdir()
monkeypatch.chdir(tmp_path)
output_path = str(tmp_path / "out" / "facts.json") if absolute else ".socket.facts.json"
if absolute:
(tmp_path / "out").mkdir()

result = _analyzer().run_reachability_analysis(
org_slug="my-org",
target_directory=str(target),
tar_hash="tar-hash-abc123",
output_path=output_path,
version=VERSION,
)

assert result["report_path"] == (output_path if absolute else str(target / ".socket.facts.json"))
assert result["scan_id"] == "reach-scan-id"


def test_reach_debug_appends_debug_long_flag(fake_run_path):
"""G9: --reach-debug -> coana --debug; does not emit the global -d."""
args = _run(fake_run_path, reach_debug=True)["args"]
Expand Down
Loading