Skip to content

Commit 88732aa

Browse files
committed
Merge origin/main into lelia/cli-api-error-exit-handling
Resolve version-bump conflicts (pyproject.toml, socketsecurity/__init__.py, uv.lock) in favor of 2.3.0, which supersedes main's 2.2.92 release (#208). Auto-merged #208's alert-title fallback changes in core/__init__.py. Signed-off-by: lelia <2418071+lelia@users.noreply.github.com>
2 parents b1054e5 + cc24682 commit 88732aa

2 files changed

Lines changed: 108 additions & 4 deletions

File tree

socketsecurity/core/__init__.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
import os
3+
import re
34
import sys
45
import tarfile
56
import tempfile
@@ -44,6 +45,26 @@
4445
version = __version__
4546
log = logging.getLogger("socketdev")
4647

48+
_ALERT_TYPE_TITLE_OVERRIDES = {
49+
"gptDidYouMean": "Possible typosquat attack (GPT)",
50+
}
51+
52+
_HUMANIZE_BOUNDARY = re.compile(r"(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])")
53+
54+
55+
def _humanize_alert_type(alert_type: str) -> str:
56+
"""Convert a camelCase/PascalCase alert type into a Title-Cased label.
57+
58+
Used as a last-resort fallback when the SDK does not have metadata for an
59+
alert type and there is no explicit override. Adjacent capitals are kept
60+
together so acronyms like 'SQL' survive ('SQLInjection' -> 'SQL Injection').
61+
"""
62+
if not alert_type:
63+
return ""
64+
parts = _HUMANIZE_BOUNDARY.split(alert_type)
65+
return " ".join(part[:1].upper() + part[1:] for part in parts if part)
66+
67+
4768
class Core:
4869
"""Main class for interacting with Socket Security API and processing scan results."""
4970

@@ -1407,11 +1428,19 @@ def add_package_alerts_to_collection(self, package: Package, alerts_collection:
14071428
alert = Alert(**alert_item)
14081429
props = getattr(self.config.all_issues, alert.type, default_props)
14091430
introduced_by = self.get_source_data(package, packages)
1410-
1411-
# Handle special case for license policy violations
1431+
1432+
# Title resolution order:
1433+
# 1. SDK-provided title (props.title) if non-empty
1434+
# 2. Explicit override for known-but-unmapped alert types (e.g. gptDidYouMean)
1435+
# 3. Hard-coded special cases (e.g. licenseSpdxDisj)
1436+
# 4. Humanized alert.type as last-resort fallback
14121437
title = props.title
1413-
if alert.type == "licenseSpdxDisj" and not title:
1438+
if not title:
1439+
title = _ALERT_TYPE_TITLE_OVERRIDES.get(alert.type, "")
1440+
if not title and alert.type == "licenseSpdxDisj":
14141441
title = "License Policy Violation"
1442+
if not title:
1443+
title = _humanize_alert_type(alert.type)
14151444

14161445
issue_alert = Issue(
14171446
pkg_type=package.type,

tests/core/test_package_and_alerts.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import pytest
55
from socketdev import socketdev
66

7-
from socketsecurity.core import Core
7+
from socketsecurity.core import Core, _humanize_alert_type
88
from socketsecurity.core.classes import Issue, Package
99
from socketsecurity.core.socket_config import SocketConfig
1010

@@ -166,6 +166,62 @@ def test_add_package_alerts_basic(self, core):
166166
assert alert.type == "networkAccess"
167167
assert alert.severity == "high"
168168

169+
def test_gpt_did_you_mean_gets_typosquat_title(self, core):
170+
"""gptDidYouMean alerts must render a non-empty title (CUS2-2)."""
171+
package = self.make_package(
172+
alerts=[{
173+
"type": "gptDidYouMean",
174+
"key": "gpt-did-you-mean-alert",
175+
"severity": "middle",
176+
}],
177+
topLevelAncestors=[],
178+
)
179+
180+
result = core.add_package_alerts_to_collection(
181+
package, alerts_collection={}, packages={package.id: package}
182+
)
183+
184+
alert = result["gpt-did-you-mean-alert"][0]
185+
assert alert.type == "gptDidYouMean"
186+
assert alert.title, "title should not be empty for gptDidYouMean"
187+
assert "typosquat" in alert.title.lower()
188+
189+
def test_unknown_alert_type_falls_back_to_humanized_title(self, core):
190+
"""Any alert type not present in the SDK should still render a non-empty title."""
191+
package = self.make_package(
192+
alerts=[{
193+
"type": "someBrandNewAlertType",
194+
"key": "future-alert",
195+
"severity": "low",
196+
}],
197+
topLevelAncestors=[],
198+
)
199+
200+
result = core.add_package_alerts_to_collection(
201+
package, alerts_collection={}, packages={package.id: package}
202+
)
203+
204+
alert = result["future-alert"][0]
205+
assert alert.title == "Some Brand New Alert Type"
206+
207+
def test_license_spdx_disj_keeps_explicit_title(self, core):
208+
"""licenseSpdxDisj must keep its hard-coded fallback (regression guard for CUS2-2 fix)."""
209+
package = self.make_package(
210+
alerts=[{
211+
"type": "licenseSpdxDisj",
212+
"key": "license-alert",
213+
"severity": "high",
214+
}],
215+
topLevelAncestors=[],
216+
)
217+
218+
result = core.add_package_alerts_to_collection(
219+
package, alerts_collection={}, packages={package.id: package}
220+
)
221+
222+
alert = result["license-alert"][0]
223+
assert alert.title == "License Policy Violation"
224+
169225

170226

171227
def test_get_capabilities_for_added_packages(self, core):
@@ -266,3 +322,22 @@ def test_get_license_text_via_purl_uses_org_scoped_endpoint(self, core, mock_sdk
266322
)
267323
assert result["npm/lodash@4.18.1"].licenseAttrib == [{"name": "MIT"}]
268324
assert result["npm/lodash@4.18.1"].licenseDetails == [{"license": "MIT"}]
325+
326+
327+
class TestHumanizeAlertType:
328+
def test_humanizes_camel_case(self):
329+
assert _humanize_alert_type("gptDidYouMean") == "Gpt Did You Mean"
330+
331+
def test_humanizes_single_word(self):
332+
assert _humanize_alert_type("malware") == "Malware"
333+
334+
def test_humanizes_pascal_case(self):
335+
assert _humanize_alert_type("UnsafeShellAccess") == "Unsafe Shell Access"
336+
337+
def test_empty_input_returns_empty_string(self):
338+
assert _humanize_alert_type("") == ""
339+
340+
def test_handles_acronyms_conservatively(self):
341+
"""Adjacent capitals are kept together: SQLInjection -> 'SQL Injection'."""
342+
assert _humanize_alert_type("SQLInjection") == "SQL Injection"
343+

0 commit comments

Comments
 (0)