1
- import base64
2
- import json
3
1
import logging
4
2
import time
5
3
from dataclasses import asdict
6
4
from glob import glob
7
5
from pathlib import PurePath
8
- from typing import BinaryIO , Dict , List , Optional , Tuple
9
-
6
+ from typing import BinaryIO , Dict , List , Tuple
10
7
from socketdev import socketdev
11
8
from socketdev .fullscans import (
12
9
FullScanParams ,
13
- SocketArtifact ,
14
- DiffArtifact ,
10
+ SocketArtifact
15
11
)
16
12
from socketdev .org import Organization
17
13
from socketdev .repos import RepositoryInfo
27
23
Purl ,
28
24
)
29
25
from socketsecurity .core .exceptions import (
30
- APIResourceNotFound ,
26
+ APIResourceNotFound
31
27
)
28
+ from socketdev .exceptions import APIFailure
32
29
from socketsecurity .core .licenses import Licenses
33
30
34
31
from .socket_config import SocketConfig
@@ -216,7 +213,7 @@ def load_files_for_sending(files: List[str], workspace: str) -> List[Tuple[str,
216
213
217
214
return send_files
218
215
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 :
220
217
"""
221
218
Creates a new full scan via the Socket API.
222
219
@@ -236,10 +233,10 @@ def create_full_scan(self, files: List[str], params: FullScanParams) -> FullScan
236
233
raise Exception (f"Error creating full scan: { res .message } , status: { res .status } " )
237
234
238
235
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 )
243
240
244
241
create_full_end = time .time ()
245
242
total_time = create_full_end - create_full_start
@@ -317,24 +314,37 @@ def get_package_license_text(self, package: Package) -> str:
317
314
318
315
return ""
319
316
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 :
321
318
"""
322
319
Gets repository information from the Socket API.
323
320
324
321
Args:
325
322
repo_slug: Repository slug to get info for
323
+ default_branch: Default branch string to use if the repo doesn't exist
326
324
327
325
Returns:
328
326
RepositoryInfo object
329
327
330
328
Raises:
331
329
Exception: If API request fails
332
330
"""
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
338
348
return response .data
339
349
340
350
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:
350
360
repo_info = self .get_repo_info (repo_slug )
351
361
return repo_info .head_full_scan_id if repo_info .head_full_scan_id else None
352
362
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 ]]:
354
374
"""
355
375
Get packages that were added and removed between scans.
356
376
357
377
Args:
358
378
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
360
380
361
381
Returns:
362
382
Tuple of (added_packages, removed_packages) dictionaries
363
383
"""
364
- if head_full_scan is None :
384
+ if head_full_scan_id is None :
365
385
log .info (f"No head scan found. New scan ID: { new_full_scan .id } " )
366
386
return new_full_scan .packages , {}
367
387
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" )
371
393
log .info (f"Diff report artifact counts:" )
372
394
log .info (f"Added: { len (diff_report .artifacts .added )} " )
373
395
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
384
406
for artifact in added_artifacts :
385
407
try :
386
408
pkg = Package .from_diff_artifact (asdict (artifact ))
409
+ pkg = Core .update_package_values (pkg )
387
410
added_packages [artifact .id ] = pkg
388
411
except KeyError :
389
412
log .error (f"KeyError: Could not create package from added artifact { artifact .id } " )
390
413
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" )
398
415
399
416
for artifact in removed_artifacts :
400
417
try :
401
418
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 } "
402
422
removed_packages [artifact .id ] = pkg
403
423
except KeyError :
404
424
log .error (f"KeyError: Could not create package from removed artifact { artifact .id } " )
405
425
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" )
413
427
414
428
return added_packages , removed_packages
415
429
@@ -439,32 +453,33 @@ def create_new_diff(
439
453
if not files :
440
454
return Diff (id = "no_diff_id" )
441
455
442
- head_full_scan_id = None
443
-
444
456
try :
445
457
# Get head scan ID
446
458
head_full_scan_id = self .get_head_scan_for_repo (params .repo )
459
+ has_head_scan = True
447
460
except APIResourceNotFound :
448
461
head_full_scan_id = None
462
+ has_head_scan = False
449
463
450
464
# Create new scan
465
+ params .include_license_details = False
451
466
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 )
453
468
new_scan_end = time .time ()
454
469
log .info (f"Total time to create new full scan: { new_scan_end - new_scan_start :.2f} " )
455
470
456
471
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)
460
475
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 )
462
477
463
478
diff = self .create_diff_report (added_packages , removed_packages )
464
479
465
480
base_socket = "https://socket.dev/dashboard/org"
466
481
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 "
468
483
if head_full_scan_id is not None :
469
484
diff .diff_url = f"{ base_socket } /{ self .config .org_slug } /diff/{ diff .id } /{ head_full_scan_id } "
470
485
else :
@@ -609,7 +624,8 @@ def get_source_data(package: Package, packages: dict) -> list:
609
624
source = (top_purl , manifests )
610
625
introduced_by .append (source )
611
626
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}")
613
629
return introduced_by
614
630
615
631
@staticmethod
0 commit comments