1- import base64
2- import json
31import logging
42import time
53from dataclasses import asdict
64from glob import glob
75from pathlib import PurePath
8- from typing import BinaryIO , Dict , List , Optional , Tuple
9-
6+ from typing import BinaryIO , Dict , List , Tuple
107from socketdev import socketdev
118from socketdev .fullscans import (
129 FullScanParams ,
13- SocketArtifact ,
14- DiffArtifact ,
10+ SocketArtifact
1511)
1612from socketdev .org import Organization
1713from socketdev .repos import RepositoryInfo
2723 Purl ,
2824)
2925from socketsecurity .core .exceptions import (
30- APIResourceNotFound ,
26+ APIResourceNotFound
3127)
28+ from socketdev .exceptions import APIFailure
3229from socketsecurity .core .licenses import Licenses
3330
3431from .socket_config import SocketConfig
@@ -216,7 +213,7 @@ def load_files_for_sending(files: List[str], workspace: str) -> List[Tuple[str,
216213
217214 return send_files
218215
219- def create_full_scan (self , files : List [str ], params : FullScanParams ) -> FullScan :
216+ def create_full_scan (self , files : List [str ], params : FullScanParams , has_head_scan : bool = False ) -> FullScan :
220217 """
221218 Creates a new full scan via the Socket API.
222219
@@ -236,10 +233,10 @@ def create_full_scan(self, files: List[str], params: FullScanParams) -> FullScan
236233 raise Exception (f"Error creating full scan: { res .message } , status: { res .status } " )
237234
238235 full_scan = FullScan (** asdict (res .data ))
239-
240- full_scan_artifacts_dict = self .get_sbom_data (full_scan .id )
241- full_scan .sbom_artifacts = self .get_sbom_data_list (full_scan_artifacts_dict )
242- full_scan .packages = self .create_packages_dict (full_scan .sbom_artifacts )
236+ if not has_head_scan :
237+ full_scan_artifacts_dict = self .get_sbom_data (full_scan .id )
238+ full_scan .sbom_artifacts = self .get_sbom_data_list (full_scan_artifacts_dict )
239+ full_scan .packages = self .create_packages_dict (full_scan .sbom_artifacts )
243240
244241 create_full_end = time .time ()
245242 total_time = create_full_end - create_full_start
@@ -317,24 +314,37 @@ def get_package_license_text(self, package: Package) -> str:
317314
318315 return ""
319316
320- def get_repo_info (self , repo_slug : str ) -> RepositoryInfo :
317+ def get_repo_info (self , repo_slug : str , default_branch : str = "socket-default-branch" ) -> RepositoryInfo :
321318 """
322319 Gets repository information from the Socket API.
323320
324321 Args:
325322 repo_slug: Repository slug to get info for
323+ default_branch: Default branch string to use if the repo doesn't exist
326324
327325 Returns:
328326 RepositoryInfo object
329327
330328 Raises:
331329 Exception: If API request fails
332330 """
333- response = self .sdk .repos .repo (self .config .org_slug , repo_slug )
334- if not response .success :
335- log .error (f"Failed to get repository: { response .status } " )
336- log .error (response .message )
337- raise Exception (f"Failed to get repository info: { response .status } , message: { response .message } " )
331+ try :
332+ response = self .sdk .repos .repo (self .config .org_slug , repo_slug )
333+ if not response .success :
334+ log .error (f"Failed to get repository: { response .status } " )
335+ log .error (response .message )
336+ # raise Exception(f"Failed to get repository info: {response.status}, message: {response.message}")
337+ except APIFailure :
338+ log .warning (f"Failed to get repository { repo_slug } , attempting to create it" )
339+ create_response = self .sdk .repos .post (self .config .org_slug , name = repo_slug , default_branch = default_branch )
340+ if not create_response .success :
341+ log .error (f"Failed to create repository: { create_response .status } " )
342+ log .error (create_response .message )
343+ raise Exception (
344+ f"Failed to create repository: { create_response .status } , message: { create_response .message } "
345+ )
346+ else :
347+ return create_response .data
338348 return response .data
339349
340350 def get_head_scan_for_repo (self , repo_slug : str ) -> str :
@@ -350,24 +360,36 @@ def get_head_scan_for_repo(self, repo_slug: str) -> str:
350360 repo_info = self .get_repo_info (repo_slug )
351361 return repo_info .head_full_scan_id if repo_info .head_full_scan_id else None
352362
353- def get_added_and_removed_packages (self , head_full_scan : Optional [FullScan ], new_full_scan : FullScan ) -> Tuple [Dict [str , Package ], Dict [str , Package ]]:
363+ @staticmethod
364+ def update_package_values (pkg : Package ) -> Package :
365+ pkg .purl = f"{ pkg .name } @{ pkg .version } "
366+ pkg .url = f"https://socket.dev/{ pkg .type } /package"
367+ if pkg .namespace :
368+ pkg .purl = f"{ pkg .namespace } /{ pkg .purl } "
369+ pkg .url += f"/{ pkg .namespace } "
370+ pkg .url += f"/{ pkg .name } /overview/{ pkg .version } "
371+ return pkg
372+
373+ def get_added_and_removed_packages (self , head_full_scan_id : str , new_full_scan : FullScan ) -> Tuple [Dict [str , Package ], Dict [str , Package ]]:
354374 """
355375 Get packages that were added and removed between scans.
356376
357377 Args:
358378 head_full_scan: Previous scan (may be None if first scan)
359- new_full_scan : New scan just created
379+ head_full_scan_id : New scan just created
360380
361381 Returns:
362382 Tuple of (added_packages, removed_packages) dictionaries
363383 """
364- if head_full_scan is None :
384+ if head_full_scan_id is None :
365385 log .info (f"No head scan found. New scan ID: { new_full_scan .id } " )
366386 return new_full_scan .packages , {}
367387
368- log .info (f"Comparing scans - Head scan ID: { head_full_scan .id } , New scan ID: { new_full_scan .id } " )
369- diff_report = self .sdk .fullscans .stream_diff (self .config .org_slug , head_full_scan .id , new_full_scan .id ).data
370-
388+ log .info (f"Comparing scans - Head scan ID: { head_full_scan_id } , New scan ID: { new_full_scan .id } " )
389+ diff_start = time .time ()
390+ diff_report = self .sdk .fullscans .stream_diff (self .config .org_slug , head_full_scan_id , new_full_scan .id ).data
391+ diff_end = time .time ()
392+ log .info (f"Diff Report Gathered in { diff_end - diff_start :.2f} seconds" )
371393 log .info (f"Diff report artifact counts:" )
372394 log .info (f"Added: { len (diff_report .artifacts .added )} " )
373395 log .info (f"Removed: { len (diff_report .artifacts .removed )} " )
@@ -384,32 +406,24 @@ def get_added_and_removed_packages(self, head_full_scan: Optional[FullScan], new
384406 for artifact in added_artifacts :
385407 try :
386408 pkg = Package .from_diff_artifact (asdict (artifact ))
409+ pkg = Core .update_package_values (pkg )
387410 added_packages [artifact .id ] = pkg
388411 except KeyError :
389412 log .error (f"KeyError: Could not create package from added artifact { artifact .id } " )
390413 log .error (f"Artifact details - name: { artifact .name } , version: { artifact .version } " )
391- matches = [p for p in new_full_scan .packages .values () if p .name == artifact .name and p .version == artifact .version ]
392- if matches :
393- log .error (f"Found { len (matches )} packages with matching name/version:" )
394- for m in matches :
395- log .error (f" ID: { m .id } , name: { m .name } , version: { m .version } " )
396- else :
397- log .error ("No matching packages found in new_full_scan" )
414+ log .error ("No matching packages found in new_full_scan" )
398415
399416 for artifact in removed_artifacts :
400417 try :
401418 pkg = Package .from_diff_artifact (asdict (artifact ))
419+ pkg = Core .update_package_values (pkg )
420+ if pkg .namespace :
421+ pkg .purl += f"{ pkg .namespace } /{ pkg .purl } "
402422 removed_packages [artifact .id ] = pkg
403423 except KeyError :
404424 log .error (f"KeyError: Could not create package from removed artifact { artifact .id } " )
405425 log .error (f"Artifact details - name: { artifact .name } , version: { artifact .version } " )
406- matches = [p for p in head_full_scan .packages .values () if p .name == artifact .name and p .version == artifact .version ]
407- if matches :
408- log .error (f"Found { len (matches )} packages with matching name/version:" )
409- for m in matches :
410- log .error (f" ID: { m .id } , name: { m .name } , version: { m .version } " )
411- else :
412- log .error ("No matching packages found in head_full_scan" )
426+ log .error ("No matching packages found in head_full_scan" )
413427
414428 return added_packages , removed_packages
415429
@@ -439,32 +453,33 @@ def create_new_diff(
439453 if not files :
440454 return Diff (id = "no_diff_id" )
441455
442- head_full_scan_id = None
443-
444456 try :
445457 # Get head scan ID
446458 head_full_scan_id = self .get_head_scan_for_repo (params .repo )
459+ has_head_scan = True
447460 except APIResourceNotFound :
448461 head_full_scan_id = None
462+ has_head_scan = False
449463
450464 # Create new scan
465+ params .include_license_details = False
451466 new_scan_start = time .time ()
452- new_full_scan = self .create_full_scan (files_for_sending , params )
467+ new_full_scan = self .create_full_scan (files_for_sending , params , has_head_scan )
453468 new_scan_end = time .time ()
454469 log .info (f"Total time to create new full scan: { new_scan_end - new_scan_start :.2f} " )
455470
456471
457- head_full_scan = None
458- if head_full_scan_id :
459- head_full_scan = self .get_full_scan (head_full_scan_id )
472+ # head_full_scan = None
473+ # if head_full_scan_id:
474+ # head_full_scan = self.get_full_scan(head_full_scan_id)
460475
461- added_packages , removed_packages = self .get_added_and_removed_packages (head_full_scan , new_full_scan )
476+ added_packages , removed_packages = self .get_added_and_removed_packages (head_full_scan_id , new_full_scan )
462477
463478 diff = self .create_diff_report (added_packages , removed_packages )
464479
465480 base_socket = "https://socket.dev/dashboard/org"
466481 diff .id = new_full_scan .id
467- diff .report_url = f"{ base_socket } /{ self .config .org_slug } /sbom/{ diff .id } "
482+ diff .report_url = f"{ base_socket } /{ self .config .org_slug } /sbom/{ diff .id } ?include_license_details=false "
468483 if head_full_scan_id is not None :
469484 diff .diff_url = f"{ base_socket } /{ self .config .org_slug } /diff/{ diff .id } /{ head_full_scan_id } "
470485 else :
@@ -609,7 +624,8 @@ def get_source_data(package: Package, packages: dict) -> list:
609624 source = (top_purl , manifests )
610625 introduced_by .append (source )
611626 else :
612- log .debug (f"Unable to get top level package info for { top_id } " )
627+ pass
628+ # log.debug(f"Unable to get top level package info for {top_id}")
613629 return introduced_by
614630
615631 @staticmethod
0 commit comments