Skip to content

Commit 9476cea

Browse files
committed
fix(fullscans): skip artifacts without usable ids
1 parent ff78408 commit 9476cea

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

socketdev/fullscans/__init__.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,18 @@ def stream(self, org_slug: str, full_scan_id: str, use_types: bool = False) -> U
955955
stream_str.append(item)
956956
stream_deduped = Dedupe.dedupe(stream_str, batched=False)
957957
for batch in stream_deduped:
958-
artifacts[batch["id"]] = batch
958+
try:
959+
artifact_id = batch["id"]
960+
if not isinstance(artifact_id, str) or not artifact_id:
961+
raise TypeError("artifact id must be a non-empty string")
962+
artifacts[artifact_id] = batch
963+
except (KeyError, TypeError):
964+
# A malformed artifact should not discard valid stream results
965+
# before FullScanStreamResponse can parse them individually.
966+
log.warning(
967+
"Skipping artifact without a usable id",
968+
exc_info=True,
969+
)
959970
if use_types:
960971
return FullScanStreamResponse.from_dict({"success": True, "status": 200, "artifacts": artifacts})
961972
return artifacts

tests/unit/test_socket_purl_type.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
parse instead of discarding the entire response.
1818
"""
1919

20+
import json
2021
import logging
2122
import unittest
2223

2324
from socketdev.fullscans import (
25+
FullScans,
2426
FullScanStreamResponse,
2527
SocketArtifact,
2628
SocketPURL,
@@ -106,6 +108,34 @@ def test_malformed_artifact_is_skipped_not_fatal(self):
106108
f"expected a warning about the skipped artifact, got: {captured.output}",
107109
)
108110

111+
def test_full_scans_stream_skips_artifact_without_id(self):
112+
class Response:
113+
status_code = 200
114+
text = "\n".join(
115+
json.dumps(artifact)
116+
for artifact in (
117+
_artifact_payload("good", "npm"),
118+
{"type": "npm", "name": "bad", "alerts": []},
119+
)
120+
)
121+
122+
class API:
123+
def do_request(self, **kwargs):
124+
return Response()
125+
126+
with self.assertLogs("socketdev", level=logging.WARNING) as captured:
127+
response = FullScans(API()).stream("org", "scan", use_types=True)
128+
129+
self.assertTrue(response.success)
130+
self.assertEqual(list(response.artifacts), ["good"])
131+
self.assertTrue(
132+
any(
133+
"Skipping artifact without a usable id" in message
134+
for message in captured.output
135+
),
136+
f"expected a warning about the skipped artifact, got: {captured.output}",
137+
)
138+
109139

110140
if __name__ == "__main__":
111141
unittest.main()

0 commit comments

Comments
 (0)