@@ -133,40 +133,25 @@ def create_sbom_output(self, diff: Diff) -> dict:
133133 @staticmethod
134134 def expand_brace_pattern (pattern : str ) -> List [str ]:
135135 """
136- Recursively expands brace expressions (e.g., {a,b,c}) into separate patterns, supporting nested braces.
137- """
138- def recursive_expand (pat : str ) -> List [str ]:
139- stack = []
140- for i , c in enumerate (pat ):
141- if c == '{' :
142- stack .append (i )
143- elif c == '}' and stack :
144- start = stack .pop ()
145- if not stack :
146- # Found the outermost pair
147- before = pat [:start ]
148- after = pat [i + 1 :]
149- inner = pat [start + 1 :i ]
150- # Split on commas not inside nested braces
151- options = []
152- depth = 0
153- last = 0
154- for j , ch in enumerate (inner ):
155- if ch == '{' :
156- depth += 1
157- elif ch == '}' :
158- depth -= 1
159- elif ch == ',' and depth == 0 :
160- options .append (inner [last :j ])
161- last = j + 1
162- options .append (inner [last :])
163- results = []
164- for opt in options :
165- expanded = before + opt + after
166- results .extend (recursive_expand (expanded ))
167- return results
168- return [pat ]
169- return recursive_expand (pattern )
136+ Expands brace expressions (e.g., {a,b,c}) into separate patterns.
137+ """
138+ brace_regex = re .compile (r"\{([^{}]+)\}" )
139+
140+ # Expand all brace groups
141+ expanded_patterns = [pattern ]
142+ while any ("{" in p for p in expanded_patterns ):
143+ new_patterns = []
144+ for pat in expanded_patterns :
145+ match = brace_regex .search (pat )
146+ if match :
147+ options = match .group (1 ).split ("," ) # Extract values inside {}
148+ prefix , suffix = pat [:match .start ()], pat [match .end ():]
149+ new_patterns .extend ([prefix + opt + suffix for opt in options ])
150+ else :
151+ new_patterns .append (pat )
152+ expanded_patterns = new_patterns
153+
154+ return expanded_patterns
170155
171156 @staticmethod
172157 def is_excluded (file_path : str , excluded_dirs : Set [str ]) -> bool :
@@ -191,7 +176,13 @@ def find_files(self, path: str) -> List[str]:
191176 files : Set [str ] = set ()
192177
193178 # Get supported patterns from the API
194- patterns = self .get_supported_patterns ()
179+ try :
180+ patterns = self .get_supported_patterns ()
181+ except Exception as e :
182+ log .error (f"Error getting supported patterns from API: { e } " )
183+ log .warning ("Falling back to local patterns" )
184+ from .utils import socket_globs as fallback_patterns
185+ patterns = fallback_patterns
195186
196187 for ecosystem in patterns :
197188 if ecosystem in self .config .excluded_ecosystems :
@@ -434,7 +425,7 @@ def create_packages_dict(self, sbom_artifacts: list[SocketArtifact]) -> dict[str
434425 packages = {}
435426 top_level_count = {}
436427 for artifact in sbom_artifacts :
437- package = Package .from_socket_artifact ( asdict ( artifact ) )
428+ package = Package .from_diff_artifact ( artifact . __dict__ )
438429 if package .id in packages :
439430 print ("Duplicate package?" )
440431 else :
@@ -543,22 +534,44 @@ def update_package_values(pkg: Package) -> Package:
543534 pkg .url += f"/{ pkg .name } /overview/{ pkg .version } "
544535 return pkg
545536
546- def get_added_and_removed_packages (self , head_full_scan_id : str , new_full_scan_id : str ) -> Tuple [Dict [str , Package ], Dict [str , Package ]]:
537+ def get_added_and_removed_packages (
538+ self ,
539+ head_full_scan_id : str ,
540+ new_full_scan_id : str ,
541+ merge : bool = False ,
542+ external_href : str = None ,
543+ ) -> Tuple [Dict [str , Package ], Dict [str , Package ], str , str ]:
547544 """
548545 Get packages that were added and removed between scans.
549546
550547 Args:
551- head_full_scan: Previous scan (may be None if first scan)
552- head_full_scan_id: New scan just created
553-
548+ head_full_scan_id: Previous scan
549+ new_full_scan_id: New scan just created
550+ merge: Whether the scan is merged into the default branch
551+ external_href: External reference
554552 Returns:
555553 Tuple of (added_packages, removed_packages) dictionaries
556554 """
557555
558556 log .info (f"Comparing scans - Head scan ID: { head_full_scan_id } , New scan ID: { new_full_scan_id } " )
559557 diff_start = time .time ()
560558 try :
561- diff_report = self .sdk .fullscans .stream_diff (self .config .org_slug , head_full_scan_id , new_full_scan_id , use_types = True ).data
559+ params = {
560+ "before" : head_full_scan_id ,
561+ "after" : new_full_scan_id ,
562+ "description" : f"Diff scan between head { head_full_scan_id } and new { new_full_scan_id } scans" ,
563+ "merge" : merge ,
564+ }
565+ if external_href :
566+ params ["external_href" ] = external_href
567+ new_diff_scan = self .sdk .diffscans .create_from_ids (self .config .org_slug , params )
568+ data = new_diff_scan .get ("diff_scan" , {})
569+ diff_scan_id = data .get ("id" )
570+ if not diff_scan_id :
571+ log .error (f"Failed to get diff scan ID for { new_full_scan_id } " )
572+ log .error (new_diff_scan )
573+ sys .exit (1 )
574+ diff_report = self .sdk .diffscans .get (self .config .org_slug , diff_scan_id )
562575 except APIFailure as e :
563576 log .error (f"API Error: { e } " )
564577 sys .exit (1 )
@@ -568,44 +581,63 @@ def get_added_and_removed_packages(self, head_full_scan_id: str, new_full_scan_i
568581 log .error (f"Stack trace:\n { traceback .format_exc ()} " )
569582 raise
570583
584+ diff_data = diff_report .get ("diff_scan" , {})
571585 diff_end = time .time ()
586+ diff_url = diff_data .get ("html_url" )
587+ after_data = diff_data .get ("after_full_scan" )
588+ if after_data :
589+ new_full_scan_url = after_data .get ("html_url" )
590+ else :
591+ new_full_scan_url = ""
592+ artifacts = diff_data .get ("artifacts" , {})
593+ added = artifacts .get ("added" , [])
594+ removed = artifacts .get ("removed" , [])
595+ unchanged = artifacts .get ("unchanged" , [])
596+ replaced = artifacts .get ("replaced" , [])
597+ updated = artifacts .get ("updated" , [])
572598 log .info (f"Diff Report Gathered in { diff_end - diff_start :.2f} seconds" )
573599 log .info ("Diff report artifact counts:" )
574- log .info (f"Added: { len (diff_report . artifacts . added )} " )
575- log .info (f"Removed: { len (diff_report . artifacts . removed )} " )
576- log .info (f"Unchanged: { len (diff_report . artifacts . unchanged )} " )
577- log .info (f"Replaced: { len (diff_report . artifacts . replaced )} " )
578- log .info (f"Updated: { len (diff_report . artifacts . updated )} " )
600+ log .info (f"Added: { len (added )} " )
601+ log .info (f"Removed: { len (removed )} " )
602+ log .info (f"Unchanged: { len (unchanged )} " )
603+ log .info (f"Replaced: { len (replaced )} " )
604+ log .info (f"Updated: { len (updated )} " )
579605
580- added_artifacts = diff_report . artifacts . added + diff_report . artifacts . updated
581- removed_artifacts = diff_report . artifacts . removed + diff_report . artifacts . replaced
606+ added_artifacts = added + updated
607+ removed_artifacts = removed
582608
583609 added_packages : Dict [str , Package ] = {}
584610 removed_packages : Dict [str , Package ] = {}
585611
586612 for artifact in added_artifacts :
613+ artifact_id = artifact .get ("id" )
614+ artifact_name = artifact .get ("name" )
615+ artifact_version = artifact .get ("version" )
587616 try :
588- pkg = Package .from_diff_artifact (asdict ( artifact ) )
617+ pkg = Package .from_diff_artifact (artifact )
589618 pkg = Core .update_package_values (pkg )
590- added_packages [artifact .id ] = pkg
619+ added_packages [pkg .id ] = pkg
591620 except KeyError :
592- log .error (f"KeyError: Could not create package from added artifact { artifact . id } " )
593- log .error (f"Artifact details - name: { artifact . name } , version: { artifact . version } " )
621+ log .error (f"KeyError: Could not create package from added artifact { artifact_id } " )
622+ log .error (f"Artifact details - name: { artifact_name } , version: { artifact_version } " )
594623 log .error ("No matching packages found in new_full_scan" )
595624
596625 for artifact in removed_artifacts :
626+ artifact_id = artifact .get ("id" )
627+ artifact_name = artifact .get ("name" )
628+ artifact_version = artifact .get ("version" )
597629 try :
598- pkg = Package .from_diff_artifact (asdict ( artifact ) )
630+ pkg = Package .from_diff_artifact (artifact )
599631 pkg = Core .update_package_values (pkg )
600632 if pkg .namespace :
601633 pkg .purl += f"{ pkg .namespace } /{ pkg .purl } "
602- removed_packages [artifact .id ] = pkg
634+ removed_packages [pkg .id ] = pkg
603635 except KeyError :
604- log .error (f"KeyError: Could not create package from removed artifact { artifact . id } " )
605- log .error (f"Artifact details - name: { artifact . name } , version: { artifact . version } " )
636+ log .error (f"KeyError: Could not create package from removed artifact { artifact_id } " )
637+ log .error (f"Artifact details - name: { artifact_name } , version: { artifact_version } " )
606638 log .error ("No matching packages found in head_full_scan" )
607639
608- return added_packages , removed_packages
640+ return added_packages , removed_packages , diff_url , new_full_scan_url
609641
610642 def create_new_diff (
611643 self ,
@@ -651,7 +683,6 @@ def create_new_diff(
651683 try :
652684 new_scan_start = time .time ()
653685 new_full_scan = self .create_full_scan (files_for_sending , params )
654- new_full_scan .sbom_artifacts = self .get_sbom_data (new_full_scan .id )
655686 new_scan_end = time .time ()
656687 log .info (f"Total time to create new full scan: { new_scan_end - new_scan_start :.2f} " )
657688 except APIFailure as e :
@@ -663,26 +694,15 @@ def create_new_diff(
663694 log .error (f"Stack trace:\n { traceback .format_exc ()} " )
664695 raise
665696
666- scans_ready = self .check_full_scans_status ( head_full_scan_id , new_full_scan . id )
667- if scans_ready is False :
668- log . error ( f"Full scans did not complete within { self . config . timeout } seconds" )
669- added_packages , removed_packages = self . get_added_and_removed_packages ( head_full_scan_id , new_full_scan . id )
697+ added_packages , removed_packages , diff_url , report_url = self .get_added_and_removed_packages (
698+ head_full_scan_id ,
699+ new_full_scan . id
700+ )
670701
671702 diff = self .create_diff_report (added_packages , removed_packages )
672-
673- base_socket = "https://socket.dev/dashboard/org"
674703 diff .id = new_full_scan .id
675-
676- report_url = f"{ base_socket } /{ self .config .org_slug } /sbom/{ diff .id } "
677- if not params .include_license_details :
678- report_url += "?include_license_details=false"
679704 diff .report_url = report_url
680-
681- if head_full_scan_id is not None :
682- diff .diff_url = f"{ base_socket } /{ self .config .org_slug } /diff/{ head_full_scan_id } /{ diff .id } "
683- else :
684- diff .diff_url = diff .report_url
685-
705+ diff .diff_url = diff_url
686706 return diff
687707
688708 def create_diff_report (
0 commit comments