55from dataclasses import asdict
66from glob import glob
77from pathlib import PurePath
8- from typing import BinaryIO , Dict , List , Tuple
9-
8+ from typing import BinaryIO , Dict , List , Tuple , Set
9+ import re
1010from socketdev import socketdev
1111from socketdev .exceptions import APIFailure
1212from socketdev .fullscans import FullScanParams , SocketArtifact
@@ -123,19 +123,50 @@ def create_sbom_output(self, diff: Diff) -> dict:
123123 log .error (result .get ("message" , "No error message provided" ))
124124 return {}
125125
126+ @staticmethod
127+ def to_case_insensitive_regex (pattern : str ) -> str :
128+ """
129+ Converts a pattern to a case-insensitive regex (optional step).
130+ If `pattern` is a glob pattern, this step should be removed.
131+ """
132+ return pattern # Remove if unnecessary
133+
134+ @staticmethod
135+ def expand_brace_pattern (pattern : str ) -> List [str ]:
136+ """
137+ Expands brace expressions (e.g., {a,b,c}) into separate patterns.
138+ """
139+ brace_regex = re .compile (r"\{([^{}]+)\}" )
140+
141+ # Expand all brace groups
142+ expanded_patterns = [pattern ]
143+ while any ("{" in p for p in expanded_patterns ):
144+ new_patterns = []
145+ for pat in expanded_patterns :
146+ match = brace_regex .search (pat )
147+ if match :
148+ options = match .group (1 ).split ("," ) # Extract values inside {}
149+ prefix , suffix = pat [:match .start ()], pat [match .end ():]
150+ new_patterns .extend ([prefix + opt + suffix for opt in options ])
151+ else :
152+ new_patterns .append (pat )
153+ expanded_patterns = new_patterns
154+
155+ return expanded_patterns
156+
126157 def find_files (self , path : str ) -> List [str ]:
127158 """
128159 Finds supported manifest files in the given path.
129160
130161 Args:
131- path: Path to search for manifest files
162+ path: Path to search for manifest files.
132163
133164 Returns:
134- List of found manifest file paths
165+ List of found manifest file paths.
135166 """
136167 log .debug ("Starting Find Files" )
137168 start_time = time .time ()
138- files = set ()
169+ files : Set [ str ] = set ()
139170
140171 # Get supported patterns from the API
141172 try :
@@ -149,28 +180,28 @@ def find_files(self, path: str) -> List[str]:
149180 for ecosystem in patterns :
150181 ecosystem_patterns = patterns [ecosystem ]
151182 for file_name in ecosystem_patterns :
152- pattern = Core . to_case_insensitive_regex ( ecosystem_patterns [file_name ]["pattern" ])
153- file_path = f" { path } /**/ { pattern } "
154- #log.debug(f"Globbing {file_path}")
155- glob_start = time . time ( )
156- glob_files = glob ( file_path , recursive = True )
157- for glob_file in glob_files :
158- # Only add if it's a file, not a directory
159- if glob_file not in files and os .path .isfile ( glob_file ):
160- files . add ( glob_file )
161- glob_end = time . time ( )
162- glob_total_time = glob_end - glob_start
163- #log.debug(f"Glob for pattern {file_path} took {glob_total_time:.2f} seconds" )
164-
165- log . debug ( "Finished Find Files" )
166- end_time = time . time ()
167- total_time = end_time - start_time
168- files_list = list ( files )
169- if len ( files_list ) > 5 :
170- log .debug (f"{ len ( files_list ) } Files found ( { total_time :.2f } s): { ', ' . join ( files_list [: 5 ]) } , ... " )
171- else :
172- log .debug (f"{ len ( files_list ) } Files found ( { total_time :.2f } s): { ', ' . join ( files_list )} " )
173- return list (files )
183+ original_pattern = ecosystem_patterns [file_name ]["pattern" ]
184+
185+ # Expand brace patterns
186+ expanded_patterns = Core . expand_brace_pattern ( original_pattern )
187+
188+ for pattern in expanded_patterns :
189+ case_insensitive_pattern = Core . to_case_insensitive_regex ( pattern )
190+ file_path = os .path .join ( path , "**" , case_insensitive_pattern )
191+
192+ log . debug ( f"Globbing { file_path } " )
193+ glob_start = time . time ()
194+ glob_files = glob ( file_path , recursive = True )
195+
196+ for glob_file in glob_files :
197+ if os . path . isfile ( glob_file ):
198+ files . add ( glob_file )
199+
200+ glob_end = time . time ()
201+ log .debug (f"Globbing took { glob_end - glob_start :.4f } seconds " )
202+
203+ log .debug (f"Total files found: { len ( files )} " )
204+ return sorted (files )
174205
175206 def get_supported_patterns (self ) -> Dict :
176207 """
0 commit comments