Skip to content

Commit 95385df

Browse files
committed
fix: tolerate unknown SocketCategory values in SocketAlert.from_dict
The Socket API can emit category values the SDK does not yet know about (e.g. "other"). Strict enum construction in SocketAlert.from_dict turned that into a hard failure that propagated up through stream_diff and crashed any consumer that happened to receive such an alert. Fall back to SocketCategory.MISCELLANEOUS and log a warning when the value is unrecognized, so the SDK stays forward-compatible with new server-side categories without needing a coordinated release. Fixes #78.
1 parent c8efa8f commit 95385df

2 files changed

Lines changed: 73 additions & 1 deletion

File tree

socketdev/fullscans/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,11 +443,20 @@ def to_dict(self):
443443

444444
@classmethod
445445
def from_dict(cls, data: dict) -> "SocketAlert":
446+
try:
447+
category = SocketCategory(data["category"])
448+
except ValueError:
449+
log.warning(
450+
"Unknown SocketCategory %r; falling back to MISCELLANEOUS. "
451+
"Upgrade socketdev to pick up newer categories.",
452+
data["category"],
453+
)
454+
category = SocketCategory.MISCELLANEOUS
446455
return cls(
447456
key=data["key"],
448457
type=data["type"],
449458
severity=SocketIssueSeverity(data["severity"]),
450-
category=SocketCategory(data["category"]),
459+
category=category,
451460
file=data.get("file"),
452461
start=data.get("start"),
453462
end=data.get("end"),
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""
2+
Unit tests for lenient SocketCategory parsing in SocketAlert.from_dict.
3+
4+
Regression coverage for
5+
https://github.com/SocketDev/socket-sdk-python/issues/78: the Socket API can
6+
emit category values the SDK does not yet know about (e.g. ``"other"``). Strict
7+
enum parsing turned that into a hard failure that took down every consumer
8+
(notably socketsecurity CI runs) whenever a diff included one of those alerts.
9+
10+
These tests pin the fallback behavior so the SDK stays forward-compatible with
11+
new server-side categories.
12+
"""
13+
14+
import logging
15+
import unittest
16+
17+
from socketdev.fullscans import SocketAlert, SocketCategory, SocketIssueSeverity
18+
19+
20+
class TestSocketAlertCategoryParsing(unittest.TestCase):
21+
"""SocketAlert.from_dict should tolerate unknown category values."""
22+
23+
def _base_payload(self, category: str) -> dict:
24+
return {
25+
"key": "alert-key",
26+
"type": "someAlertType",
27+
"severity": "low",
28+
"category": category,
29+
}
30+
31+
def test_known_category_is_preserved(self):
32+
alert = SocketAlert.from_dict(self._base_payload("supplyChainRisk"))
33+
self.assertEqual(alert.category, SocketCategory.SUPPLY_CHAIN_RISK)
34+
self.assertEqual(alert.severity, SocketIssueSeverity.LOW)
35+
36+
def test_unknown_category_falls_back_to_miscellaneous(self):
37+
alert = SocketAlert.from_dict(self._base_payload("other"))
38+
self.assertEqual(alert.category, SocketCategory.MISCELLANEOUS)
39+
40+
def test_unknown_category_does_not_raise(self):
41+
# Explicit regression assertion: no ValueError for brand-new categories.
42+
try:
43+
SocketAlert.from_dict(self._base_payload("somethingCompletelyNew"))
44+
except ValueError as exc:
45+
self.fail(f"SocketAlert.from_dict raised ValueError for unknown category: {exc}")
46+
47+
def test_unknown_category_emits_warning(self):
48+
with self.assertLogs("socketdev", level=logging.WARNING) as captured:
49+
SocketAlert.from_dict(self._base_payload("other"))
50+
self.assertTrue(
51+
any("Unknown SocketCategory" in message for message in captured.output),
52+
f"expected a warning about the unknown category, got: {captured.output}",
53+
)
54+
55+
def test_every_known_category_round_trips(self):
56+
for category in SocketCategory:
57+
with self.subTest(category=category):
58+
alert = SocketAlert.from_dict(self._base_payload(category.value))
59+
self.assertEqual(alert.category, category)
60+
61+
62+
if __name__ == "__main__":
63+
unittest.main()

0 commit comments

Comments
 (0)