Skip to content

Commit 0c4524e

Browse files
committed
feat(output): show patched versions in security findings
1 parent 0d3937d commit 0c4524e

2 files changed

Lines changed: 98 additions & 0 deletions

File tree

socketsecurity/core/messages.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import re
55
import uuid
66
from datetime import datetime, timezone
7+
from html import escape
78
from pathlib import Path
9+
810
from mdutils import MdUtils
911
from prettytable import PrettyTable
1012

@@ -14,6 +16,13 @@
1416

1517
class Messages:
1618

19+
@staticmethod
20+
def get_patched_version(alert: Issue) -> str:
21+
"""Return the first patched version exposed by an alert, if any."""
22+
props = getattr(alert, "props", {}) or {}
23+
value = props.get("firstPatchedVersionIdentifier")
24+
return str(value) if value not in (None, "") else ""
25+
1726
@staticmethod
1827
def map_severity_to_sarif(severity: str) -> str:
1928
"""
@@ -857,6 +866,11 @@ def security_comment_template(diff: Diff, config=None) -> str:
857866
severity_icon = Messages.get_severity_icon(alert.severity)
858867
action = "Block" if alert.error else "Warn"
859868
details_open = ""
869+
patched_version = Messages.get_patched_version(alert)
870+
patched_version_html = (
871+
f"<p><strong>Patched version:</strong> <code>{escape(patched_version)}</code></p>"
872+
if patched_version else ""
873+
)
860874
# Generate proper manifest URL
861875
manifest_url = Messages.get_manifest_file_url(diff, alert.manifests, config)
862876
# Generate a table row for each alert
@@ -877,6 +891,7 @@ def security_comment_template(diff: Diff, config=None) -> str:
877891
<details {details_open}>
878892
<summary>{alert.pkg_name}@{alert.pkg_version} - {alert.title}</summary>
879893
<p><strong>Note:</strong> {alert.description}</p>
894+
{patched_version_html}
880895
<p><strong>Source:</strong> <a href="{manifest_url}">Manifest File</a></p>
881896
<p>ℹ️ Read more on:
882897
<a href="{alert.purl}">This package</a> |
@@ -1247,6 +1262,7 @@ def create_console_security_alert_table(diff: Diff) -> PrettyTable:
12471262
[
12481263
"Alert",
12491264
"Package",
1265+
"Patched Version",
12501266
"url",
12511267
"Introduced by",
12521268
"Manifest File",
@@ -1267,6 +1283,7 @@ def create_console_security_alert_table(diff: Diff) -> PrettyTable:
12671283
row = [
12681284
alert.title,
12691285
alert.purl,
1286+
Messages.get_patched_version(alert),
12701287
alert.url,
12711288
source_str,
12721289
manifest_str,

tests/unit/test_messages.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from socketsecurity.core.classes import Diff, Issue
2+
from socketsecurity.core.messages import Messages
3+
4+
5+
def _issue(**kwargs):
6+
values = {
7+
"pkg_type": "npm",
8+
"pkg_name": "example-lib",
9+
"pkg_version": "1.4.2",
10+
"type": "highCVE",
11+
"severity": "high",
12+
"title": "High CVE",
13+
"description": "A vulnerable dependency.",
14+
"suggestion": "Upgrade to a patched release.",
15+
"purl": "pkg:npm/example-lib@1.4.2",
16+
"url": "https://socket.dev/npm/package/example-lib/overview/1.4.2",
17+
"manifests": "package-lock.json",
18+
"introduced_by": [["example-lib", "package-lock.json"]],
19+
"error": True,
20+
}
21+
values.update(kwargs)
22+
return Issue(**values)
23+
24+
25+
def test_console_security_alert_table_includes_patched_version():
26+
diff = Diff(
27+
new_alerts=[
28+
_issue(props={"firstPatchedVersionIdentifier": "1.5.0"}),
29+
]
30+
)
31+
32+
table = Messages.create_console_security_alert_table(diff)
33+
34+
assert table.field_names == [
35+
"Alert",
36+
"Package",
37+
"Patched Version",
38+
"url",
39+
"Introduced by",
40+
"Manifest File",
41+
"CI Status",
42+
]
43+
assert table.rows[0][2] == "1.5.0"
44+
45+
46+
def test_console_security_alert_table_leaves_missing_patched_version_blank():
47+
diff = Diff(
48+
new_alerts=[
49+
_issue(),
50+
_issue(props={}),
51+
_issue(props={"firstPatchedVersionIdentifier": None}),
52+
]
53+
)
54+
55+
table = Messages.create_console_security_alert_table(diff)
56+
57+
assert [row[2] for row in table.rows] == ["", "", ""]
58+
59+
60+
def test_security_comment_includes_patched_version_when_available():
61+
diff = Diff(
62+
new_alerts=[
63+
_issue(props={"firstPatchedVersionIdentifier": "1.5.0"}),
64+
],
65+
diff_url="https://socket.dev/dashboard/org/acme/diff/before/after",
66+
)
67+
68+
comment = Messages.security_comment_template(diff)
69+
70+
assert "<strong>Patched version:</strong> <code>1.5.0</code>" in comment
71+
72+
73+
def test_security_comment_omits_missing_patched_version():
74+
diff = Diff(
75+
new_alerts=[_issue(props={})],
76+
diff_url="https://socket.dev/dashboard/org/acme/diff/before/after",
77+
)
78+
79+
comment = Messages.security_comment_template(diff)
80+
81+
assert "Patched version:" not in comment

0 commit comments

Comments
 (0)