Skip to content

Commit 065407a

Browse files
authored
fix: tolerate unknown SocketCategory values in SocketAlert.from_dict (#79)
* 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. * chore: bump version to 3.0.33
1 parent c8efa8f commit 065407a

4 files changed

Lines changed: 75 additions & 3 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "socketdev"
7-
version = "3.0.32"
7+
version = "3.0.33"
88
requires-python = ">= 3.9"
99
dependencies = [
1010
'requests',

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"),

socketdev/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "3.0.32"
1+
__version__ = "3.0.33"
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)