@@ -41,16 +41,13 @@ def find_line_in_file(packagename: str, packageversion: str, manifest_file: str)
41
41
Supports:
42
42
1) JSON-based manifest files (package-lock.json, Pipfile.lock, composer.lock)
43
43
- Locates a dictionary entry with the matching package & version
44
- - Does a rough line-based search (by matching the key) in the raw text
44
+ - Searches the raw text for the key
45
45
2) Text-based (requirements.txt, package.json, yarn.lock, etc.)
46
- - Uses compiled regex patterns to detect a match line by line
46
+ - Uses regex patterns to detect a match line by line
47
47
"""
48
48
file_type = Path (manifest_file ).name
49
49
logging .debug ("Processing file for line lookup: %s" , manifest_file )
50
50
51
- # ----------------------------------------------------
52
- # 1) JSON-based manifest files
53
- # ----------------------------------------------------
54
51
if file_type in ["package-lock.json" , "Pipfile.lock" , "composer.lock" ]:
55
52
try :
56
53
with open (manifest_file , "r" , encoding = "utf-8" ) as f :
@@ -67,21 +64,19 @@ def find_line_in_file(packagename: str, packageversion: str, manifest_file: str)
67
64
found_key = None
68
65
found_info = None
69
66
for key , value in packages_dict .items ():
70
- # For NPM package-lock, keys might look like "node_modules/axios"
71
67
if key .endswith (packagename ) and "version" in value :
72
68
if value ["version" ] == packageversion :
73
69
found_key = key
74
70
found_info = value
75
71
break
76
72
77
73
if found_key and found_info :
78
- # Only use the found key to locate the line
79
74
needle_key = f'"{ found_key } ":'
80
75
lines = raw_text .splitlines ()
81
76
logging .debug ("Total lines in %s: %d" , manifest_file , len (lines ))
82
77
for i , line in enumerate (lines , start = 1 ):
83
78
if needle_key in line :
84
- logging .debug ("Match found at line %d in %s: %s" , i , manifest_file , line .strip ())
79
+ logging .debug ("Found match at line %d in %s: %s" , i , manifest_file , line .strip ())
85
80
return i , line .strip ()
86
81
return 1 , f'"{ found_key } ": { found_info } '
87
82
else :
@@ -90,9 +85,7 @@ def find_line_in_file(packagename: str, packageversion: str, manifest_file: str)
90
85
logging .error ("Error reading %s: %s" , manifest_file , e )
91
86
return 1 , f"Error reading { manifest_file } "
92
87
93
- # ----------------------------------------------------
94
- # 2) Text-based / line-based manifests
95
- # ----------------------------------------------------
88
+ # Text-based manifests
96
89
search_patterns = {
97
90
"package.json" : rf'"{ packagename } ":\s*"[\^~]?{ re .escape (packageversion )} "' ,
98
91
"yarn.lock" : rf'{ packagename } @{ packageversion } ' ,
@@ -174,7 +167,8 @@ def create_security_comment_sarif(diff) -> dict:
174
167
175
168
This function now:
176
169
- Accepts multiple manifest files from alert.introduced_by or alert.manifests.
177
- - Generates one SARIF location per manifest file.
170
+ - Generates an individual SARIF result for each manifest file.
171
+ - Appends the manifest file name to the alert name to make each result unique.
178
172
- Does NOT fall back to 'requirements.txt' if no manifest file is provided.
179
173
- Adds detailed logging to validate our assumptions.
180
174
"""
@@ -204,11 +198,11 @@ def create_security_comment_sarif(diff) -> dict:
204
198
for alert in diff .new_alerts :
205
199
pkg_name = alert .pkg_name
206
200
pkg_version = alert .pkg_version
207
- rule_id = f"{ pkg_name } =={ pkg_version } "
201
+ base_rule_id = f"{ pkg_name } =={ pkg_version } "
208
202
severity = alert .severity
209
203
210
204
# Log raw alert data for manifest extraction.
211
- logging .debug ("Alert %s - introduced_by: %s, manifests: %s" , rule_id , alert .introduced_by , getattr (alert , 'manifests' , None ))
205
+ logging .debug ("Alert %s - introduced_by: %s, manifests: %s" , base_rule_id , alert .introduced_by , getattr (alert , 'manifests' , None ))
212
206
213
207
manifest_files = []
214
208
if alert .introduced_by and isinstance (alert .introduced_by , list ):
@@ -221,56 +215,57 @@ def create_security_comment_sarif(diff) -> dict:
221
215
elif hasattr (alert , 'manifests' ) and alert .manifests :
222
216
manifest_files = [mf .strip () for mf in alert .manifests .split (";" ) if mf .strip ()]
223
217
224
- logging .debug ("Alert %s - extracted manifest_files: %s" , rule_id , manifest_files )
225
-
218
+ logging .debug ("Alert %s - extracted manifest_files: %s" , base_rule_id , manifest_files )
226
219
if not manifest_files :
227
- logging .error ("Alert %s: No manifest file found; cannot determine file location." , rule_id )
220
+ logging .error ("Alert %s: No manifest file found; cannot determine file location." , base_rule_id )
228
221
continue
229
222
230
- logging .debug ("Alert %s - using manifest_files for processing: %s" , rule_id , manifest_files )
231
- # Use the first manifest for URL generation.
232
- logging .debug ("Alert %s - Using file for URL generation: %s" , rule_id , manifest_files [0 ])
233
- socket_url = Messages .get_manifest_type_url (manifest_files [0 ], pkg_name , pkg_version )
234
- short_desc = (f"{ alert .props .get ('note' , '' )} <br/><br/>Suggested Action:<br/>{ alert .suggestion } "
235
- f"<br/><a href=\" { socket_url } \" >{ socket_url } </a>" )
236
- full_desc = "{} - {}" .format (alert .title , alert .description .replace ('\r \n ' , '<br/>' ))
237
-
238
- if rule_id not in rules_map :
239
- rules_map [rule_id ] = {
240
- "id" : rule_id ,
241
- "name" : f"{ pkg_name } =={ pkg_version } " ,
242
- "shortDescription" : {"text" : f"Alert generated for { rule_id } by Socket Security" },
243
- "fullDescription" : {"text" : full_desc },
244
- "helpUri" : socket_url ,
245
- "defaultConfiguration" : {
246
- "level" : Messages .map_severity_to_sarif (severity )
247
- },
248
- }
223
+ logging .debug ("Alert %s - using manifest_files for processing: %s" , base_rule_id , manifest_files )
249
224
250
- # Create a SARIF location for each manifest file.
251
- locations = []
225
+ # For each manifest file, create an individual SARIF result.
252
226
for mf in manifest_files :
253
- logging .debug ("Alert %s - Processing manifest file: %s" , rule_id , mf )
227
+ logging .debug ("Alert %s - Processing manifest file: %s" , base_rule_id , mf )
228
+ socket_url = Messages .get_manifest_type_url (mf , pkg_name , pkg_version )
254
229
line_number , line_content = Messages .find_line_in_file (pkg_name , pkg_version , mf )
255
230
if line_number < 1 :
256
231
line_number = 1
257
- logging .debug ("Alert %s: Manifest %s, line %d: %s" , rule_id , mf , line_number , line_content )
258
- locations .append ({
259
- "physicalLocation" : {
260
- "artifactLocation" : {"uri" : mf },
261
- "region" : {
262
- "startLine" : line_number ,
263
- "snippet" : {"text" : line_content },
232
+ logging .debug ("Alert %s: Manifest %s, line %d: %s" , base_rule_id , mf , line_number , line_content )
233
+
234
+ # Create a unique rule id and name by appending the file prefix.
235
+ unique_rule_id = f"{ base_rule_id } ({ mf } )"
236
+ rule_name = unique_rule_id
237
+
238
+ short_desc = (f"{ alert .props .get ('note' , '' )} <br/><br/>Suggested Action:<br/>{ alert .suggestion } "
239
+ f"<br/><a href=\" { socket_url } \" >{ socket_url } </a>" )
240
+ full_desc = "{} - {}" .format (alert .title , alert .description .replace ('\r \n ' , '<br/>' ))
241
+
242
+ # Add the rule if not already defined.
243
+ if unique_rule_id not in rules_map :
244
+ rules_map [unique_rule_id ] = {
245
+ "id" : unique_rule_id ,
246
+ "name" : rule_name ,
247
+ "shortDescription" : {"text" : f"Alert generated for { unique_rule_id } by Socket Security" },
248
+ "fullDescription" : {"text" : full_desc },
249
+ "helpUri" : socket_url ,
250
+ "defaultConfiguration" : {
251
+ "level" : Messages .map_severity_to_sarif (severity )
264
252
},
265
253
}
266
- })
267
-
268
- result_obj = {
269
- "ruleId" : rule_id ,
270
- "message" : {"text" : short_desc },
271
- "locations" : locations ,
272
- }
273
- results_list .append (result_obj )
254
+
255
+ result_obj = {
256
+ "ruleId" : unique_rule_id ,
257
+ "message" : {"text" : short_desc },
258
+ "locations" : [{
259
+ "physicalLocation" : {
260
+ "artifactLocation" : {"uri" : mf },
261
+ "region" : {
262
+ "startLine" : line_number ,
263
+ "snippet" : {"text" : line_content },
264
+ },
265
+ }
266
+ }]
267
+ }
268
+ results_list .append (result_obj )
274
269
275
270
sarif_data ["runs" ][0 ]["tool" ]["driver" ]["rules" ] = list (rules_map .values ())
276
271
sarif_data ["runs" ][0 ]["results" ] = results_list
0 commit comments