@@ -41,13 +41,16 @@ def find_line_in_file(packagename: str, packageversion: str, manifest_file: str)
4141 Supports:
4242 1) JSON-based manifest files (package-lock.json, Pipfile.lock, composer.lock)
4343 - 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
4747 """
4848 file_type = Path (manifest_file ).name
4949 logging .debug ("Processing file for line lookup: %s" , manifest_file )
5050
51+ # ----------------------------------------------------
52+ # 1) JSON-based manifest files
53+ # ----------------------------------------------------
5154 if file_type in ["package-lock.json" , "Pipfile.lock" , "composer.lock" ]:
5255 try :
5356 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)
8588 logging .error ("Error reading %s: %s" , manifest_file , e )
8689 return 1 , f"Error reading { manifest_file } "
8790
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.
8995 search_patterns = {
9096 "package.json" : rf'"{ packagename } ":\s*"[\^~]?{ re .escape (packageversion )} "' ,
9197 "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 )} :' ,
93101 "requirements.txt" : rf'^{ re .escape (packagename )} \s*(?:==|===|!=|>=|<=|~=|\s+)?\s*{ re .escape (packageversion )} (?:\s*;.*)?$' ,
94102 "pyproject.toml" : rf'{ packagename } \s*=\s*"{ packageversion } "' ,
95103 "Pipfile" : rf'"{ packagename } "\s*=\s*"{ packageversion } "' ,
@@ -168,7 +176,7 @@ def create_security_comment_sarif(diff) -> dict:
168176 This function now:
169177 - Accepts multiple manifest files from alert.introduced_by or alert.manifests.
170178 - 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.
172180 - Does NOT fall back to 'requirements.txt' if no manifest file is provided.
173181 - Adds detailed logging to validate our assumptions.
174182 """
@@ -201,7 +209,6 @@ def create_security_comment_sarif(diff) -> dict:
201209 base_rule_id = f"{ pkg_name } =={ pkg_version } "
202210 severity = alert .severity
203211
204- # Log raw alert data for manifest extraction.
205212 logging .debug ("Alert %s - introduced_by: %s, manifests: %s" , base_rule_id , alert .introduced_by , getattr (alert , 'manifests' , None ))
206213
207214 manifest_files = []
@@ -216,42 +223,42 @@ def create_security_comment_sarif(diff) -> dict:
216223 manifest_files = [mf .strip () for mf in alert .manifests .split (";" ) if mf .strip ()]
217224
218225 logging .debug ("Alert %s - extracted manifest_files: %s" , base_rule_id , manifest_files )
226+
219227 if not manifest_files :
220228 logging .error ("Alert %s: No manifest file found; cannot determine file location." , base_rule_id )
221229 continue
222230
223231 logging .debug ("Alert %s - using manifest_files for processing: %s" , base_rule_id , manifest_files )
224232
225- # For each manifest file, create an individual SARIF result.
233+ # For each manifest file, generate a separate result
226234 for mf in manifest_files :
227235 logging .debug ("Alert %s - Processing manifest file: %s" , base_rule_id , mf )
228236 socket_url = Messages .get_manifest_type_url (mf , pkg_name , pkg_version )
229237 line_number , line_content = Messages .find_line_in_file (pkg_name , pkg_version , mf )
230238 if line_number < 1 :
231239 line_number = 1
232240 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
235243 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+
238246 short_desc = (f"{ alert .props .get ('note' , '' )} <br/><br/>Suggested Action:<br/>{ alert .suggestion } "
239247 f"<br/><a href=\" { socket_url } \" >{ socket_url } </a>" )
240248 full_desc = "{} - {}" .format (alert .title , alert .description .replace ('\r \n ' , '<br/>' ))
241-
242- # Add the rule if not already defined.
249+
243250 if unique_rule_id not in rules_map :
244251 rules_map [unique_rule_id ] = {
245252 "id" : unique_rule_id ,
246253 "name" : rule_name ,
247- "shortDescription" : {"text" : f"Alert generated for { unique_rule_id } by Socket Security" },
254+ "shortDescription" : {"text" : rule_name },
248255 "fullDescription" : {"text" : full_desc },
249256 "helpUri" : socket_url ,
250257 "defaultConfiguration" : {
251258 "level" : Messages .map_severity_to_sarif (severity )
252259 },
253260 }
254-
261+
255262 result_obj = {
256263 "ruleId" : unique_rule_id ,
257264 "message" : {"text" : short_desc },
0 commit comments