@@ -41,13 +41,16 @@ 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
- - Searches the raw text for the key
45
- 2) Text-based (requirements.txt, package.json, yarn.lock, etc.)
46
- - Uses regex patterns to detect a match line by line
44
+ - Searches the raw text for the dependency key
45
+ 2) Text-based (requirements.txt, package.json, yarn.lock, pnpm-lock.yaml, etc.)
46
+ - Uses compiled 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
+ # ----------------------------------------------------
51
54
if file_type in ["package-lock.json" , "Pipfile.lock" , "composer.lock" ]:
52
55
try :
53
56
with open (manifest_file , "r" , encoding = "utf-8" ) as f :
@@ -85,11 +88,16 @@ def find_line_in_file(packagename: str, packageversion: str, manifest_file: str)
85
88
logging .error ("Error reading %s: %s" , manifest_file , e )
86
89
return 1 , f"Error reading { manifest_file } "
87
90
88
- # Text-based manifests
91
+ # ----------------------------------------------------
92
+ # 2) Text-based / line-based manifests
93
+ # ----------------------------------------------------
94
+ # Updated search patterns; note the new pattern for pnpm-lock.yaml.
89
95
search_patterns = {
90
96
"package.json" : rf'"{ packagename } ":\s*"[\^~]?{ re .escape (packageversion )} "' ,
91
97
"yarn.lock" : rf'{ packagename } @{ packageversion } ' ,
92
- "pnpm-lock.yaml" : rf'"{ re .escape (packagename )} "\s*:\s*\{{[^}}]*"version":\s*"{ re .escape (packageversion )} "' ,
98
+ # For pnpm-lock.yaml, look for a line in the packages section like:
99
+ # /bitget-main/19.4.9:
100
+ "pnpm-lock.yaml" : rf'^/{ re .escape (packagename )} /{ re .escape (packageversion )} :' ,
93
101
"requirements.txt" : rf'^{ re .escape (packagename )} \s*(?:==|===|!=|>=|<=|~=|\s+)?\s*{ re .escape (packageversion )} (?:\s*;.*)?$' ,
94
102
"pyproject.toml" : rf'{ packagename } \s*=\s*"{ packageversion } "' ,
95
103
"Pipfile" : rf'"{ packagename } "\s*=\s*"{ packageversion } "' ,
@@ -168,7 +176,7 @@ def create_security_comment_sarif(diff) -> dict:
168
176
This function now:
169
177
- Accepts multiple manifest files from alert.introduced_by or alert.manifests.
170
178
- Generates an individual SARIF result for each manifest file.
171
- - Appends the manifest file name to the alert name to make each result unique.
179
+ - Appends the manifest file name to the alert name (and rule ID) to make each result unique.
172
180
- Does NOT fall back to 'requirements.txt' if no manifest file is provided.
173
181
- Adds detailed logging to validate our assumptions.
174
182
"""
@@ -201,7 +209,6 @@ def create_security_comment_sarif(diff) -> dict:
201
209
base_rule_id = f"{ pkg_name } =={ pkg_version } "
202
210
severity = alert .severity
203
211
204
- # Log raw alert data for manifest extraction.
205
212
logging .debug ("Alert %s - introduced_by: %s, manifests: %s" , base_rule_id , alert .introduced_by , getattr (alert , 'manifests' , None ))
206
213
207
214
manifest_files = []
@@ -216,42 +223,42 @@ def create_security_comment_sarif(diff) -> dict:
216
223
manifest_files = [mf .strip () for mf in alert .manifests .split (";" ) if mf .strip ()]
217
224
218
225
logging .debug ("Alert %s - extracted manifest_files: %s" , base_rule_id , manifest_files )
226
+
219
227
if not manifest_files :
220
228
logging .error ("Alert %s: No manifest file found; cannot determine file location." , base_rule_id )
221
229
continue
222
230
223
231
logging .debug ("Alert %s - using manifest_files for processing: %s" , base_rule_id , manifest_files )
224
232
225
- # For each manifest file, create an individual SARIF result.
233
+ # For each manifest file, generate a separate result
226
234
for mf in manifest_files :
227
235
logging .debug ("Alert %s - Processing manifest file: %s" , base_rule_id , mf )
228
236
socket_url = Messages .get_manifest_type_url (mf , pkg_name , pkg_version )
229
237
line_number , line_content = Messages .find_line_in_file (pkg_name , pkg_version , mf )
230
238
if line_number < 1 :
231
239
line_number = 1
232
240
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.
241
+
242
+ # Create a unique rule id and name by appending the manifest file name
235
243
unique_rule_id = f"{ base_rule_id } ({ mf } )"
236
- rule_name = unique_rule_id
237
-
244
+ rule_name = f"Alert { base_rule_id } ( { mf } )"
245
+
238
246
short_desc = (f"{ alert .props .get ('note' , '' )} <br/><br/>Suggested Action:<br/>{ alert .suggestion } "
239
247
f"<br/><a href=\" { socket_url } \" >{ socket_url } </a>" )
240
248
full_desc = "{} - {}" .format (alert .title , alert .description .replace ('\r \n ' , '<br/>' ))
241
-
242
- # Add the rule if not already defined.
249
+
243
250
if unique_rule_id not in rules_map :
244
251
rules_map [unique_rule_id ] = {
245
252
"id" : unique_rule_id ,
246
253
"name" : rule_name ,
247
- "shortDescription" : {"text" : f"Alert generated for { unique_rule_id } by Socket Security" },
254
+ "shortDescription" : {"text" : rule_name },
248
255
"fullDescription" : {"text" : full_desc },
249
256
"helpUri" : socket_url ,
250
257
"defaultConfiguration" : {
251
258
"level" : Messages .map_severity_to_sarif (severity )
252
259
},
253
260
}
254
-
261
+
255
262
result_obj = {
256
263
"ruleId" : unique_rule_id ,
257
264
"message" : {"text" : short_desc },
0 commit comments