Skip to content

Commit 8193af4

Browse files
committed
Expose scan references in JSON reports
Include comparison and full-scan identities and canonical report links for JSON consumers. Preserve existing fields and null values. Assisted-by: Codex:GPT-6
1 parent d3b0c22 commit 8193af4

4 files changed

Lines changed: 150 additions & 1 deletion

File tree

‎docs/cli-reference.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,19 @@ If you don't want to provide the Socket API Token every time then you can use th
328328
| `--exclude-license-details` | False | False | Exclude license details from the diff report (boosts performance for large repos) |
329329
| `--version` | False | False | Show program's version number and exit |
330330
331+
#### Standard Socket JSON scan references
332+
333+
Standard Socket JSON output adds `diff_scan_id`, `old_scan_id`, `new_scan_id`,
334+
`old_scan_url`, and `new_scan_url`. These fields are JSON null when the
335+
corresponding reference is unavailable, including fallback comparisons,
336+
full scans, and no-run results. `full_scan_id` retains its legacy meaning: the
337+
new full-scan ID (or the existing no-run sentinel), not the comparison ID.
338+
339+
`new_scan_url` is the canonical server URL for the new full scan. It can differ
340+
from `report_url`, which is the display link and may include output options such
341+
as license-detail settings. These additions apply to standard Socket JSON, not
342+
FOSSA compatibility output.
343+
331344
#### Security Configuration
332345
| Parameter | Required | Default | Description |
333346
|:-------------------------|:---------|:--------|:------------------------------|

‎socketsecurity/core/report_output.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ class ReportReferences:
1616
report_url: Optional[str]
1717
diff_url: Optional[str]
1818
is_full_scan: bool
19+
diff_scan_id: Optional[str] = None
20+
old_scan_id: Optional[str] = None
21+
new_scan_id: Optional[str] = None
22+
old_scan_url: Optional[str] = None
23+
new_scan_url: Optional[str] = None
1924

2025
def console_link(self) -> Optional[tuple[str, str]]:
2126
if self.is_full_scan:
@@ -37,6 +42,11 @@ def build_report_references(diff_report: Diff) -> ReportReferences:
3742
report_url=getattr(diff_report, "report_url", None),
3843
diff_url=getattr(diff_report, "diff_url", ""),
3944
is_full_scan=getattr(diff_report, "is_full_scan", False),
45+
diff_scan_id=getattr(diff_report, "diff_scan_id", None),
46+
old_scan_id=getattr(diff_report, "old_scan_id", None),
47+
new_scan_id=getattr(diff_report, "new_scan_id", None),
48+
old_scan_url=getattr(diff_report, "old_scan_url", None),
49+
new_scan_url=getattr(diff_report, "new_scan_url", None),
4050
)
4151

4252

@@ -131,6 +141,11 @@ def build_socket_json_report(
131141
report["full_scan_id"] = references.full_scan_id
132142
report["diff_url"] = references.diff_url
133143
report["report_url"] = references.report_url
144+
report["diff_scan_id"] = references.diff_scan_id
145+
report["old_scan_id"] = references.old_scan_id
146+
report["new_scan_id"] = references.new_scan_id
147+
report["old_scan_url"] = references.old_scan_url
148+
report["new_scan_url"] = references.new_scan_url
134149
report["repo"] = repo if isinstance(repo, str) or repo is None else None
135150
report["branch"] = branch if isinstance(branch, str) or branch is None else None
136151
report["commit_sha"] = (

‎tests/core/test_diff_identity_transport.py‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from socketsecurity.config import CliConfig
1313
from socketsecurity.core import Core
14+
from socketsecurity.core.report_output import build_socket_json_report
1415
from socketsecurity.core.socket_config import SocketConfig
1516
from tests.support.socket_api_fixture import (
1617
SocketApiFixture,
@@ -426,6 +427,24 @@ def create_full_scan(_request: SocketRequest) -> SocketResponse:
426427
)
427428
assert diff.new_scan_url == expected_new_scan_url
428429
assert diff.report_url == expected_report_url
430+
report = build_socket_json_report(
431+
diff,
432+
CliConfig(
433+
api_token="fictional-local-only",
434+
repo="fixture-repo",
435+
legal=False,
436+
),
437+
)
438+
assert report["full_scan_id"] == "new-full-scan-id"
439+
assert report["diff_scan_id"] == (
440+
"new-comparison-id" if comparison_succeeds else None
441+
)
442+
assert report["old_scan_id"] == "temporary-baseline-id"
443+
assert report["new_scan_id"] == "new-full-scan-id"
444+
assert report["diff_url"] == diff.diff_url
445+
assert report["old_scan_url"] == diff.old_scan_url
446+
assert report["new_scan_url"] == expected_new_scan_url
447+
assert report["report_url"] == expected_report_url
429448
if comparison_succeeds and not null_urls:
430449
assert diff.diff_url == "https://reports.example.invalid/diff?canonical=1#comparison"
431450
assert diff.old_scan_url == "https://reports.example.invalid/before?source=get"
@@ -539,6 +558,17 @@ def test_full_scan_identity_is_preserved_with_findings_disabled_or_enabled(
539558
assert result.new_scan_url == "https://socket.dev/scan/full-scan-id"
540559
assert result.old_scan_url is None
541560
assert result.report_url == result.diff_url == result.new_scan_url
561+
report = build_socket_json_report(
562+
result,
563+
cli_config
564+
or CliConfig(api_token="fictional-local-only", repo="fixture-repo"),
565+
)
566+
assert report["full_scan_id"] == "full-scan-id"
567+
assert report["diff_scan_id"] is None
568+
assert report["old_scan_id"] is None
569+
assert report["new_scan_id"] == "full-scan-id"
570+
assert report["old_scan_url"] is None
571+
assert report["new_scan_url"] == "https://socket.dev/scan/full-scan-id"
542572
if enable_json:
543573
assert result.packages["artifact-fixture-id"].purl == "npm/sample-package@1.0.0"
544574
findings_requests = [
@@ -567,4 +597,13 @@ def test_no_change_paths_keep_sentinels_without_transport_calls(
567597
assert result.diff_scan_id is None
568598
assert result.old_scan_url is None
569599
assert result.new_scan_url is None
600+
report = build_socket_json_report(
601+
result, CliConfig(api_token="fictional-local-only", repo="fixture-repo")
602+
)
603+
assert report["diff_scan_id"] is None
604+
assert report["old_scan_id"] is None
605+
assert report["new_scan_id"] is None
606+
assert report["old_scan_url"] is None
607+
assert report["new_scan_url"] is None
608+
assert report["full_scan_id"] == result.id
570609
assert len(socket_api.requests) == request_count_after_setup

‎tests/unit/test_report_output.py‎

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,23 @@ def _diff(
5858
diff_url: str | None = "https://socket.dev/orgs/fictional/diffs/diff-456",
5959
is_full_scan: bool = False,
6060
alerts_fetched: bool = True,
61+
diff_scan_id: str | None = None,
62+
old_scan_id: str | None = None,
63+
new_scan_id: str | None = "full-123",
64+
old_scan_url: str | None = None,
65+
new_scan_url: str | None = None,
6166
) -> Diff:
6267
return Diff(
6368
id="full-123",
64-
new_scan_id="full-123",
6569
report_url=report_url,
6670
diff_url=diff_url,
6771
is_full_scan=is_full_scan,
6872
alerts_fetched=alerts_fetched,
73+
diff_scan_id=diff_scan_id,
74+
old_scan_id=old_scan_id,
75+
new_scan_id=new_scan_id,
76+
old_scan_url=old_scan_url,
77+
new_scan_url=new_scan_url,
6978
new_alerts=list(new_alerts or []),
7079
unchanged_alerts=list(unchanged_alerts or []),
7180
)
@@ -245,6 +254,11 @@ def test_default_socket_json_key_set_and_metadata_remain_unchanged():
245254
"branch",
246255
"commit_sha",
247256
"legal_mode",
257+
"diff_scan_id",
258+
"old_scan_id",
259+
"new_scan_id",
260+
"old_scan_url",
261+
"new_scan_url",
248262
}
249263
assert report == {
250264
"scan_failed": False,
@@ -256,9 +270,77 @@ def test_default_socket_json_key_set_and_metadata_remain_unchanged():
256270
"branch": "feature/example",
257271
"commit_sha": "abc123",
258272
"legal_mode": True,
273+
"diff_scan_id": None,
274+
"old_scan_id": None,
275+
"new_scan_id": "full-123",
276+
"old_scan_url": None,
277+
"new_scan_url": None,
259278
}
260279

261280

281+
def test_scan_references_are_distinct_nullable_and_preserve_input():
282+
diff = _diff(
283+
diff_scan_id="comparison-789",
284+
old_scan_id="baseline-456",
285+
new_scan_id="full-123",
286+
old_scan_url="https://reports.example.test/before?x=1#base",
287+
new_scan_url="https://reports.example.test/after?x=2#new",
288+
)
289+
original = copy.deepcopy(diff.__dict__)
290+
291+
report = build_socket_json_report(diff, _config())
292+
293+
assert report == {
294+
"scan_failed": False,
295+
"new_alerts": [],
296+
"full_scan_id": "full-123",
297+
"diff_url": "https://socket.dev/orgs/fictional/diffs/diff-456",
298+
"report_url": "https://socket.dev/orgs/fictional/reports/full-123",
299+
"repo": "fictional-repo",
300+
"branch": "",
301+
"commit_sha": "",
302+
"legal_mode": False,
303+
"diff_scan_id": "comparison-789",
304+
"old_scan_id": "baseline-456",
305+
"new_scan_id": "full-123",
306+
"old_scan_url": "https://reports.example.test/before?x=1#base",
307+
"new_scan_url": "https://reports.example.test/after?x=2#new",
308+
}
309+
assert json.loads(json.dumps(report))["old_scan_id"] == "baseline-456"
310+
assert diff.__dict__ == original
311+
312+
313+
def test_missing_references_remain_null_without_url_inference():
314+
diff = _diff(
315+
report_url="https://reports.example.test/display",
316+
diff_url=None,
317+
diff_scan_id=None,
318+
old_scan_id=None,
319+
new_scan_id=None,
320+
old_scan_url=None,
321+
new_scan_url=None,
322+
)
323+
324+
report = build_socket_json_report(diff, _config())
325+
326+
assert report["diff_scan_id"] is None
327+
assert report["new_scan_id"] is None
328+
assert report["new_scan_url"] is None
329+
assert report["diff_url"] is None
330+
assert report["report_url"] == "https://reports.example.test/display"
331+
332+
333+
def test_canonical_scan_url_is_distinct_from_display_report_url():
334+
canonical_url = "https://reports.example.test/scan?id=full-123#result"
335+
display_url = "https://reports.example.test/scan?id=full-123&include_license_details=false#result"
336+
diff = _diff(report_url=display_url, new_scan_url=canonical_url)
337+
338+
report = build_socket_json_report(diff, _config())
339+
340+
assert report["new_scan_url"] == canonical_url
341+
assert report["report_url"] == display_url
342+
343+
262344
def test_missing_reference_fields_keep_existing_json_defaults():
263345
diff = Diff(new_alerts=[])
264346

0 commit comments

Comments
 (0)