Skip to content

Commit b28a035

Browse files
committed
cover FOSSA-compatible legal outputs/sample scenarios
Signed-off-by: lelia <2418071+lelia@users.noreply.github.com>
1 parent d2bc914 commit b28a035

4 files changed

Lines changed: 295 additions & 2 deletions

File tree

tests/unit/test_cli_config.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,15 @@ def test_api_token_from_env(self, monkeypatch):
77
config = CliConfig.from_args([]) # Empty args list
88
assert config.api_token == "test-token"
99

10-
def test_required_args(self):
10+
def test_required_args(self, monkeypatch):
1111
"""Test that api token is required if not in environment"""
12+
for env_var in (
13+
"SOCKET_SECURITY_API_KEY",
14+
"SOCKET_SECURITY_API_TOKEN",
15+
"SOCKET_API_KEY",
16+
"SOCKET_API_TOKEN",
17+
):
18+
monkeypatch.delenv(env_var, raising=False)
1219
with pytest.raises(ValueError, match="API token is required"):
1320
config = CliConfig.from_args([])
1421
if not config.api_token:
@@ -86,6 +93,7 @@ def test_workspace_is_independent_of_workspace_name(self):
8693
def test_legal_flag_sets_default_artifact_files(self):
8794
config = CliConfig.from_args(["--api-token", "test", "--legal"])
8895
assert config.legal is True
96+
assert config.legal_format == "socket"
8997
assert config.generate_license is True
9098
assert config.json_file == "socket-report.json"
9199
assert config.summary_file == "socket-summary.txt"
@@ -108,3 +116,14 @@ def test_legal_flag_preserves_explicit_file_paths(self):
108116
assert config.report_link_file == "custom-link.txt"
109117
assert config.sbom_file == "custom-sbom.json"
110118
assert config.license_file_name == "custom-license.json"
119+
120+
def test_fossa_legal_format_enables_legal_defaults(self):
121+
config = CliConfig.from_args(["--api-token", "test", "--legal-format", "fossa"])
122+
assert config.legal is True
123+
assert config.legal_format == "fossa"
124+
assert config.generate_license is True
125+
assert config.json_file == "fossa-analyze.json"
126+
assert config.summary_file == "fossa-test.txt"
127+
assert config.report_link_file == "fossa-link.txt"
128+
assert config.sbom_file is None
129+
assert config.license_file_name == "fossa-sbom.json"

tests/unit/test_fossa_compat.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import json
2+
from pathlib import Path
3+
4+
from socketsecurity.config import CliConfig
5+
from socketsecurity.core.classes import Diff, Issue, Package
6+
from socketsecurity.fossa_compat import build_fossa_report_payload
7+
8+
9+
FIXTURE_DIR = Path("/Users/lelia/github/fossa/DependencyScan/Fossa/validation-pipeline")
10+
11+
12+
def test_fossa_report_payload_matches_sample_top_level_shape():
13+
sample = json.loads(
14+
(FIXTURE_DIR / "fossa-analyze-11464165-job-011e1ec8-6569-5e69-4f06-baf193d1351e_03172026132742.json").read_text()
15+
)
16+
17+
config = CliConfig.from_args(["--api-token", "test", "--legal-format", "fossa"])
18+
diff = Diff(id="scan-123", report_url="https://socket.dev/report/123")
19+
20+
payload = build_fossa_report_payload(diff, config)
21+
22+
assert list(payload.keys()) == list(sample.keys())
23+
assert sorted(payload["project"].keys()) == sorted(sample["project"].keys())
24+
assert payload["vulnerability"] == []
25+
assert payload["licensing"] == []
26+
assert payload["quality"] == []
27+
28+
29+
def test_fossa_report_payload_vulnerability_keys_cover_sample_shape():
30+
sample = json.loads(
31+
(FIXTURE_DIR / "fossa-analyze-11464165-job-7f33e5bd-7764-5d8a-ba2e-506e078b9c3f_03172026132955.json").read_text()
32+
)
33+
sample_vulnerability = sample["vulnerability"][0]
34+
35+
config = CliConfig.from_args([
36+
"--api-token", "test",
37+
"--legal-format", "fossa",
38+
"--repo", "owner/repo",
39+
"--branch", "refs/heads/main",
40+
])
41+
diff = Diff(id="scan-123", report_url="https://socket.dev/report/123")
42+
diff.packages = {
43+
"pkg-1": Package(
44+
id="pkg-1",
45+
name="requests",
46+
version="2.31.0",
47+
type="pypi",
48+
score={},
49+
alerts=[],
50+
direct=True,
51+
url="https://requests.readthedocs.io/",
52+
license="Apache-2.0",
53+
purl="pkg:pypi/requests@2.31.0",
54+
)
55+
}
56+
diff.new_alerts = [
57+
Issue(
58+
title="Insufficiently Protected Credentials",
59+
severity="medium",
60+
description="Requests may leak credentials for crafted URLs.",
61+
error=True,
62+
key="GHSA-9hjg-9r4m-mvj7",
63+
type="vulnerability",
64+
pkg_type="pypi",
65+
pkg_name="requests",
66+
pkg_version="2.31.0",
67+
pkg_id="pkg-1",
68+
purl="pkg:pypi/requests@2.31.0",
69+
url="https://socket.dev/pypi/package/requests/alerts/2.31.0",
70+
props={
71+
"id": 11088938,
72+
"createdAt": "2025-10-08T10:41:05.933Z",
73+
"ghsaId": "GHSA-9hjg-9r4m-mvj7",
74+
"cveId": "CVE-2024-47081",
75+
"cvssScore": 5.3,
76+
"fixedVersion": "2.32.4",
77+
"partialFixDistance": "MINOR",
78+
"completeFixDistance": "MINOR",
79+
"attackVector": "Network",
80+
"attackComplexity": "High",
81+
"privilegesRequired": "None",
82+
"userInteraction": "Required",
83+
"scope": "Unchanged",
84+
"confidentialityImpact": "High",
85+
"integrityImpact": "None",
86+
"availabilityImpact": "None",
87+
"cveStatus": "COMPLETED",
88+
"cwes": ["CWE-522"],
89+
"published": "2025-06-09T19:06:08Z",
90+
"affectedVersionRanges": ["<2.32.4"],
91+
"patchedVersionRanges": ["2.32.4"],
92+
"references": ["https://github.com/advisories/GHSA-9hjg-9r4m-mvj7"],
93+
"cvssVector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N",
94+
"exploitability": "UNKNOWN",
95+
"epssScore": 0.00154,
96+
"epssPercentile": 0.35957,
97+
"cpes": [],
98+
},
99+
)
100+
]
101+
102+
payload = build_fossa_report_payload(diff, config)
103+
generated_vulnerability = payload["vulnerability"][0]
104+
105+
assert sorted(generated_vulnerability.keys()) == sorted(sample_vulnerability.keys())
106+
assert generated_vulnerability["source"]["packageManager"] == sample_vulnerability["source"]["packageManager"]
107+
assert sorted(generated_vulnerability["source"].keys()) == sorted(sample_vulnerability["source"].keys())
108+
assert sorted(generated_vulnerability["depths"].keys()) == sorted(sample_vulnerability["depths"].keys())
109+
assert sorted(generated_vulnerability["statuses"].keys()) == sorted(sample_vulnerability["statuses"].keys())
110+
assert sorted(generated_vulnerability["remediation"].keys()) == sorted(sample_vulnerability["remediation"].keys())
111+
assert sorted(generated_vulnerability["epss"].keys()) == sorted(sample_vulnerability["epss"].keys())

tests/unit/test_output.py

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22
from socketsecurity.output import OutputHandler
3-
from socketsecurity.core.classes import Diff, Issue
3+
from socketsecurity.core.classes import Diff, Issue, Package
44
import json
55

66
class TestOutputHandler:
@@ -144,6 +144,7 @@ def test_json_file_saving(self, tmp_path):
144144
config.report_link_file = None
145145
config.sbom_file = None
146146
config.legal = True
147+
config.legal_format = "socket"
147148
config.repo = "owner/repo"
148149
config.branch = "main"
149150
config.commit_sha = "abc123"
@@ -199,6 +200,7 @@ def test_summary_and_report_link_files_are_written(self, tmp_path):
199200
config.report_link_file = str(report_link_path)
200201
config.sbom_file = None
201202
config.legal = False
203+
config.legal_format = "socket"
202204
config.repo = None
203205
config.branch = ""
204206
config.commit_sha = ""
@@ -235,6 +237,105 @@ def test_summary_and_report_link_files_are_written(self, tmp_path):
235237
assert "Security issues detected by Socket Security:" in summary_path.read_text()
236238
assert report_link_path.read_text().strip() == "https://socket.dev/report/123"
237239

240+
def test_json_file_saving_in_fossa_format(self, tmp_path):
241+
from socketsecurity.config import CliConfig
242+
from unittest.mock import Mock
243+
244+
json_path = tmp_path / "fossa-report.json"
245+
246+
config = Mock(spec=CliConfig)
247+
config.disable_blocking = False
248+
config.strict_blocking = False
249+
config.json_file = str(json_path)
250+
config.summary_file = None
251+
config.report_link_file = None
252+
config.sbom_file = None
253+
config.legal = True
254+
config.legal_format = "fossa"
255+
config.repo = "owner/repo"
256+
config.branch = "refs/heads/main"
257+
config.commit_sha = "abc123"
258+
config.enable_json = False
259+
config.enable_sarif = False
260+
config.enable_gitlab_security = False
261+
config.enable_debug = False
262+
263+
handler = OutputHandler(config, Mock())
264+
265+
diff = Diff()
266+
diff.id = "scan-123"
267+
diff.report_url = "https://socket.dev/report/123"
268+
diff.packages = {
269+
"pkg-1": Package(
270+
id="pkg-1",
271+
name="requests",
272+
version="2.31.0",
273+
type="pypi",
274+
score={},
275+
alerts=[],
276+
direct=True,
277+
url="https://socket.dev/pypi/package/requests/overview/2.31.0",
278+
license="Apache-2.0",
279+
purl="pkg:pypi/requests@2.31.0",
280+
)
281+
}
282+
diff.new_alerts = [
283+
Issue(
284+
title="Prototype Pollution",
285+
severity="high",
286+
description="Upgrade to a fixed version.",
287+
error=True,
288+
key="alert-1",
289+
type="vulnerability",
290+
pkg_type="pypi",
291+
pkg_name="requests",
292+
pkg_version="2.31.0",
293+
pkg_id="pkg-1",
294+
purl="pkg:pypi/requests@2.31.0",
295+
url="https://socket.dev/npm/package/requests/alerts/2.31.0",
296+
props={
297+
"ghsaId": "GHSA-1234",
298+
"cveId": "CVE-2026-1234",
299+
"cvssScore": 8.2,
300+
"fixedVersion": "2.31.1",
301+
"references": ["https://github.com/advisories/GHSA-1234"],
302+
},
303+
),
304+
Issue(
305+
title="License Policy Violation",
306+
severity="medium",
307+
description="Package license violates policy.",
308+
warn=True,
309+
key="license-1",
310+
type="licenseSpdxDisj",
311+
pkg_type="pypi",
312+
pkg_name="requests",
313+
pkg_version="2.31.0",
314+
pkg_id="pkg-1",
315+
purl="pkg:pypi/requests@2.31.0",
316+
url="https://socket.dev/pypi/package/requests/license/2.31.0",
317+
),
318+
]
319+
320+
handler.save_json_file(diff, str(json_path))
321+
322+
saved = json.loads(json_path.read_text())
323+
assert saved["project"] == {
324+
"branch": "refs/heads/main",
325+
"id": "owner/repo-scan-123",
326+
"project": "owner/repo",
327+
"projectId": "owner/repo",
328+
"revision": "scan-123",
329+
"url": "https://socket.dev/report/123",
330+
}
331+
assert len(saved["vulnerability"]) == 1
332+
assert saved["vulnerability"][0]["vulnId"] == "GHSA-1234"
333+
assert saved["vulnerability"][0]["cve"] == "CVE-2026-1234"
334+
assert saved["vulnerability"][0]["source"]["packageManager"] == "pip"
335+
assert saved["vulnerability"][0]["remediation"]["completeFix"] == "2.31.1"
336+
assert saved["licensing"][0]["type"] == "policy_conflict"
337+
assert saved["quality"] == []
338+
238339
def test_report_pass_with_strict_blocking_new_alerts(self):
239340
"""Test that strict-blocking fails on new blocking alerts"""
240341
from socketsecurity.config import CliConfig

tests/unit/test_socketcli.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,65 @@ def test_build_license_artifact_payload_serializes_package_fields():
4545
"purl": "requests@2.31.0",
4646
}
4747
}
48+
49+
50+
def test_build_license_artifact_payload_fossa_format_without_packages():
51+
class Config:
52+
repo = "owner/repo"
53+
branch = "main"
54+
55+
diff = Diff(id="scan-1", report_url="https://socket.dev/report/1")
56+
57+
payload = build_license_artifact_payload(diff, legal_format="fossa", config=Config())
58+
59+
assert payload == {
60+
"project": {
61+
"branch": "main",
62+
"id": "owner/repo-scan-1",
63+
"project": "owner/repo",
64+
"projectId": "owner/repo",
65+
"revision": "scan-1",
66+
"url": "https://socket.dev/report/1",
67+
},
68+
"dependencies": [],
69+
}
70+
71+
72+
def test_build_license_artifact_payload_fossa_format_serializes_dependencies():
73+
class Config:
74+
repo = "owner/repo"
75+
branch = "main"
76+
77+
diff = Diff(id="scan-1", report_url="https://socket.dev/report/1")
78+
diff.packages = {
79+
"pkg:pypi/requests@2.31.0": Package(
80+
id="pkg-1",
81+
name="requests",
82+
version="2.31.0",
83+
type="pypi",
84+
score={},
85+
alerts=[],
86+
direct=True,
87+
url="https://socket.dev/pypi/package/requests/overview/2.31.0",
88+
license="Apache-2.0",
89+
licenseDetails=[{"id": "Apache-2.0"}],
90+
licenseAttrib=[{"id": "Apache-2.0"}],
91+
purl="pkg:pypi/requests@2.31.0",
92+
)
93+
}
94+
95+
payload = build_license_artifact_payload(diff, legal_format="fossa", config=Config())
96+
97+
assert payload["project"]["projectId"] == "owner/repo"
98+
assert payload["dependencies"] == [{
99+
"id": "pkg-1",
100+
"name": "requests",
101+
"version": "2.31.0",
102+
"ecosystem": "pip",
103+
"direct": True,
104+
"url": "https://socket.dev/pypi/package/requests/overview/2.31.0",
105+
"purl": "pkg:pypi/requests@2.31.0",
106+
"declaredLicense": "Apache-2.0",
107+
"licenseDetails": [{"id": "Apache-2.0"}],
108+
"licenseAttrib": [{"id": "Apache-2.0"}],
109+
}]

0 commit comments

Comments
 (0)