socketsecurity 1.0.34__tar.gz → 1.0.35__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {socketsecurity-1.0.34/socketsecurity.egg-info → socketsecurity-1.0.35}/PKG-INFO +5 -9
- socketsecurity-1.0.35/pyproject.toml +48 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/__init__.py +1 -1
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/core/__init__.py +116 -118
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/core/github.py +28 -25
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/socketcli.py +7 -18
- {socketsecurity-1.0.34 → socketsecurity-1.0.35/socketsecurity.egg-info}/PKG-INFO +5 -9
- socketsecurity-1.0.35/socketsecurity.egg-info/requires.txt +6 -0
- socketsecurity-1.0.34/pyproject.toml +0 -130
- socketsecurity-1.0.34/socketsecurity.egg-info/requires.txt +0 -11
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/LICENSE +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/README.md +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/setup.cfg +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/core/gitlab.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/core/issues.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/core/licenses.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/core/messages.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity.egg-info/SOURCES.txt +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity.egg-info/dependency_links.txt +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity.egg-info/entry_points.txt +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: socketsecurity
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.35
|
|
4
4
|
Summary: Socket Security CLI for CI/CD
|
|
5
5
|
Author-email: Douglas Coburn <douglas@socket.dev>
|
|
6
6
|
Maintainer-email: Douglas Coburn <douglas@socket.dev>
|
|
@@ -15,16 +15,12 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
15
15
|
Requires-Python: >=3.9
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: requests
|
|
19
|
-
Requires-Dist: mdutils
|
|
18
|
+
Requires-Dist: requests
|
|
19
|
+
Requires-Dist: mdutils
|
|
20
20
|
Requires-Dist: prettytable
|
|
21
21
|
Requires-Dist: argparse
|
|
22
|
-
Requires-Dist: GitPython
|
|
23
|
-
Requires-Dist: packaging
|
|
24
|
-
Requires-Dist: python-dotenv>=1.0.1
|
|
25
|
-
Requires-Dist: socket-sdk-python>=1.0.14
|
|
26
|
-
Provides-Extra: dev
|
|
27
|
-
Requires-Dist: ruff>=0.3.0; extra == "dev"
|
|
22
|
+
Requires-Dist: GitPython
|
|
23
|
+
Requires-Dist: packaging
|
|
28
24
|
|
|
29
25
|
# Socket Security CLI
|
|
30
26
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "socketsecurity"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
requires-python = ">= 3.9"
|
|
9
|
+
dependencies = [
|
|
10
|
+
'requests',
|
|
11
|
+
'mdutils',
|
|
12
|
+
'prettytable',
|
|
13
|
+
'argparse',
|
|
14
|
+
'GitPython',
|
|
15
|
+
'packaging'
|
|
16
|
+
]
|
|
17
|
+
readme = "README.md"
|
|
18
|
+
description = "Socket Security CLI for CI/CD"
|
|
19
|
+
keywords = ["socketsecurity", "socket.dev", "sca", "oss", "security"]
|
|
20
|
+
authors = [
|
|
21
|
+
{name = "Douglas Coburn", email = "douglas@socket.dev"}
|
|
22
|
+
]
|
|
23
|
+
maintainers = [
|
|
24
|
+
{name = "Douglas Coburn", email = "douglas@socket.dev"}
|
|
25
|
+
]
|
|
26
|
+
classifiers = [
|
|
27
|
+
"Development Status :: 4 - Beta",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"Programming Language :: Python :: 3.9",
|
|
30
|
+
"Programming Language :: Python :: 3.10",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
socketcli = "socketsecurity.socketcli:cli"
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://socket.dev"
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.packages.find]
|
|
42
|
+
include = [
|
|
43
|
+
"socketsecurity",
|
|
44
|
+
"socketsecurity.core"
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.dynamic]
|
|
48
|
+
version = {attr = "socketsecurity.__version__"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__author__ = 'socket.dev'
|
|
2
|
-
__version__ = '1.0.
|
|
2
|
+
__version__ = '1.0.35'
|
|
@@ -2,9 +2,9 @@ import logging
|
|
|
2
2
|
from pathlib import PurePath
|
|
3
3
|
|
|
4
4
|
import requests
|
|
5
|
+
from urllib.parse import urlencode
|
|
5
6
|
import base64
|
|
6
7
|
import json
|
|
7
|
-
from socketdev import socketdev
|
|
8
8
|
from socketsecurity.core.exceptions import (
|
|
9
9
|
APIFailure, APIKeyMissing, APIAccessDenied, APIInsufficientQuota, APIResourceNotFound, APICloudflareError
|
|
10
10
|
)
|
|
@@ -12,6 +12,7 @@ from socketsecurity import __version__
|
|
|
12
12
|
from socketsecurity.core.licenses import Licenses
|
|
13
13
|
from socketsecurity.core.issues import AllIssues
|
|
14
14
|
from socketsecurity.core.classes import (
|
|
15
|
+
Report,
|
|
15
16
|
Issue,
|
|
16
17
|
Package,
|
|
17
18
|
Alert,
|
|
@@ -225,53 +226,34 @@ def do_request(
|
|
|
225
226
|
|
|
226
227
|
|
|
227
228
|
class Core:
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
token: str
|
|
230
|
+
base_api_url: str
|
|
231
|
+
request_timeout: int
|
|
232
|
+
reports: list
|
|
230
233
|
|
|
231
|
-
def __init__(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
@classmethod
|
|
235
|
-
def initialize(
|
|
236
|
-
cls,
|
|
234
|
+
def __init__(
|
|
235
|
+
self,
|
|
237
236
|
token: str,
|
|
238
237
|
base_api_url: str = None,
|
|
239
238
|
request_timeout: int = None,
|
|
240
239
|
enable_all_alerts: bool = False,
|
|
241
240
|
allow_unverified: bool = False
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
if cls._initialized:
|
|
245
|
-
return
|
|
246
|
-
|
|
247
|
-
global allow_unverified_ssl, all_new_alerts
|
|
248
|
-
|
|
241
|
+
):
|
|
242
|
+
global allow_unverified_ssl
|
|
249
243
|
allow_unverified_ssl = allow_unverified
|
|
250
|
-
|
|
251
|
-
encode_key(token
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
244
|
+
self.token = token + ":"
|
|
245
|
+
encode_key(self.token)
|
|
246
|
+
self.socket_date_format = "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
247
|
+
self.base_api_url = base_api_url
|
|
248
|
+
if self.base_api_url is not None:
|
|
249
|
+
Core.set_api_url(self.base_api_url)
|
|
250
|
+
self.request_timeout = request_timeout
|
|
251
|
+
if self.request_timeout is not None:
|
|
252
|
+
Core.set_timeout(self.request_timeout)
|
|
259
253
|
if enable_all_alerts:
|
|
254
|
+
global all_new_alerts
|
|
260
255
|
all_new_alerts = True
|
|
261
|
-
|
|
262
|
-
cls._initialized = True
|
|
263
|
-
cls.set_org_vars()
|
|
264
|
-
|
|
265
|
-
@classmethod
|
|
266
|
-
def _initialize_sdk(cls, token: str) -> None:
|
|
267
|
-
if cls._sdk is None:
|
|
268
|
-
cls._sdk = socketdev(token=token)
|
|
269
|
-
|
|
270
|
-
@classmethod
|
|
271
|
-
def get_sdk(cls) -> socketdev:
|
|
272
|
-
if not cls._initialized:
|
|
273
|
-
raise RuntimeError("Core not initialized - call Core.initialize() first")
|
|
274
|
-
return cls._sdk
|
|
256
|
+
Core.set_org_vars()
|
|
275
257
|
|
|
276
258
|
@staticmethod
|
|
277
259
|
def enable_debug_log(level: int):
|
|
@@ -315,50 +297,39 @@ class Core:
|
|
|
315
297
|
def get_org_id_slug() -> (str, str):
|
|
316
298
|
"""
|
|
317
299
|
Gets the Org ID and Org Slug for the API Token
|
|
318
|
-
:return:
|
|
300
|
+
:return:
|
|
319
301
|
"""
|
|
302
|
+
path = "organizations"
|
|
303
|
+
response = do_request(path)
|
|
304
|
+
data = response.json()
|
|
305
|
+
organizations = data.get("organizations")
|
|
320
306
|
new_org_id = None
|
|
321
307
|
new_org_slug = None
|
|
322
|
-
|
|
323
|
-
sdk = Core.get_sdk()
|
|
324
|
-
data = sdk.org.get()
|
|
325
|
-
organizations = data.get("organizations")
|
|
326
|
-
|
|
327
308
|
if len(organizations) == 1:
|
|
328
309
|
for key in organizations:
|
|
329
310
|
new_org_id = key
|
|
330
311
|
new_org_slug = organizations[key].get('slug')
|
|
331
|
-
|
|
332
312
|
return new_org_id, new_org_slug
|
|
333
313
|
|
|
334
314
|
@staticmethod
|
|
335
|
-
def get_sbom_data(full_scan_id: str) ->
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
315
|
+
def get_sbom_data(full_scan_id: str) -> list:
|
|
316
|
+
path = f"orgs/{org_slug}/full-scans/{full_scan_id}"
|
|
317
|
+
response = do_request(path)
|
|
318
|
+
results = []
|
|
319
|
+
try:
|
|
320
|
+
data = response.json()
|
|
321
|
+
results = data.get("sbom_artifacts") or []
|
|
322
|
+
except Exception as error:
|
|
323
|
+
log.debug("Failed with old style full-scan API using new format")
|
|
324
|
+
log.debug(error)
|
|
325
|
+
data = response.text
|
|
342
326
|
data.strip('"')
|
|
343
327
|
data.strip()
|
|
344
|
-
|
|
345
328
|
for line in data.split("\n"):
|
|
346
329
|
if line != '"' and line != "" and line is not None:
|
|
347
330
|
item = json.loads(line)
|
|
348
331
|
results.append(item)
|
|
349
|
-
|
|
350
|
-
return results
|
|
351
|
-
|
|
352
|
-
keys_to_remove = ["success", "status"]
|
|
353
|
-
for key in keys_to_remove:
|
|
354
|
-
response_dict.pop(key, None)
|
|
355
|
-
|
|
356
|
-
for key in response_dict:
|
|
357
|
-
value = response_dict.get(key)
|
|
358
|
-
response_dict[key] = Package(**value)
|
|
359
|
-
|
|
360
|
-
return response_dict
|
|
361
|
-
|
|
332
|
+
return results
|
|
362
333
|
|
|
363
334
|
@staticmethod
|
|
364
335
|
def get_security_policy() -> dict:
|
|
@@ -366,21 +337,23 @@ class Core:
|
|
|
366
337
|
Get the Security policy and determine the effective Org security policy
|
|
367
338
|
:return:
|
|
368
339
|
"""
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
340
|
+
path = "settings"
|
|
341
|
+
payload = [
|
|
342
|
+
{
|
|
343
|
+
"organization": org_id
|
|
344
|
+
}
|
|
345
|
+
]
|
|
346
|
+
response = do_request(path, payload=json.dumps(payload), method="POST")
|
|
347
|
+
data = response.json()
|
|
373
348
|
defaults = data.get("defaults")
|
|
374
349
|
default_rules = defaults.get("issueRules")
|
|
375
350
|
entries = data.get("entries")
|
|
376
351
|
org_rules = {}
|
|
377
|
-
|
|
378
352
|
for org_set in entries:
|
|
379
353
|
settings = org_set.get("settings")
|
|
380
354
|
if settings is not None:
|
|
381
355
|
org_details = settings.get("organization")
|
|
382
356
|
org_rules = org_details.get("issueRules")
|
|
383
|
-
|
|
384
357
|
for default in default_rules:
|
|
385
358
|
if default not in org_rules:
|
|
386
359
|
action = default_rules[default]["action"]
|
|
@@ -389,6 +362,10 @@ class Core:
|
|
|
389
362
|
}
|
|
390
363
|
return org_rules
|
|
391
364
|
|
|
365
|
+
# @staticmethod
|
|
366
|
+
# def get_supported_file_types() -> dict:
|
|
367
|
+
# path = "report/supported"
|
|
368
|
+
|
|
392
369
|
@staticmethod
|
|
393
370
|
def get_manifest_files(package: Package, packages: dict) -> str:
|
|
394
371
|
if package.direct:
|
|
@@ -411,16 +388,15 @@ class Core:
|
|
|
411
388
|
|
|
412
389
|
@staticmethod
|
|
413
390
|
def create_sbom_output(diff: Diff) -> dict:
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
391
|
+
base_path = f"orgs/{org_slug}/export/cdx"
|
|
392
|
+
path = f"{base_path}/{diff.id}"
|
|
393
|
+
result = do_request(path=path)
|
|
394
|
+
try:
|
|
395
|
+
sbom = result.json()
|
|
396
|
+
except Exception as error:
|
|
418
397
|
log.error(f"Unable to get CycloneDX Output for {diff.id}")
|
|
419
|
-
log.error(
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
sbom.pop("success", None)
|
|
423
|
-
|
|
398
|
+
log.error(error)
|
|
399
|
+
sbom = {}
|
|
424
400
|
return sbom
|
|
425
401
|
|
|
426
402
|
@staticmethod
|
|
@@ -431,7 +407,7 @@ class Core:
|
|
|
431
407
|
patterns = socket_globs[ecosystem]
|
|
432
408
|
for file_name in patterns:
|
|
433
409
|
pattern = patterns[file_name]["pattern"]
|
|
434
|
-
|
|
410
|
+
# path_pattern = f"**/{pattern}"
|
|
435
411
|
for file in files:
|
|
436
412
|
if "\\" in file:
|
|
437
413
|
file = file.replace("\\", "/")
|
|
@@ -449,12 +425,11 @@ class Core:
|
|
|
449
425
|
:param path: Str - path to where the manifest files are located
|
|
450
426
|
:return:
|
|
451
427
|
"""
|
|
452
|
-
|
|
428
|
+
log.debug("Starting Find Files")
|
|
453
429
|
start_time = time.time()
|
|
454
430
|
files = set()
|
|
455
431
|
for ecosystem in socket_globs:
|
|
456
432
|
patterns = socket_globs[ecosystem]
|
|
457
|
-
|
|
458
433
|
for file_name in patterns:
|
|
459
434
|
pattern = patterns[file_name]["pattern"]
|
|
460
435
|
file_path = f"{path}/**/{pattern}"
|
|
@@ -462,15 +437,14 @@ class Core:
|
|
|
462
437
|
log.debug(f"Globbing {file_path}")
|
|
463
438
|
glob_start = time.time()
|
|
464
439
|
glob_files = glob(file_path, recursive=True)
|
|
465
|
-
|
|
466
440
|
for glob_file in glob_files:
|
|
467
441
|
if glob_file not in files:
|
|
468
442
|
files.add(glob_file)
|
|
469
|
-
|
|
470
443
|
glob_end = time.time()
|
|
471
444
|
glob_total_time = glob_end - glob_start
|
|
472
445
|
log.debug(f"Glob for pattern {file_path} took {glob_total_time:.2f} seconds")
|
|
473
446
|
|
|
447
|
+
log.debug("Finished Find Files")
|
|
474
448
|
end_time = time.time()
|
|
475
449
|
total_time = end_time - start_time
|
|
476
450
|
log.info(f"Found {len(files)} in {total_time:.2f} seconds")
|
|
@@ -487,7 +461,7 @@ class Core:
|
|
|
487
461
|
"""
|
|
488
462
|
send_files = []
|
|
489
463
|
create_full_start = time.time()
|
|
490
|
-
|
|
464
|
+
log.debug("Creating new full scan")
|
|
491
465
|
for file in files:
|
|
492
466
|
if platform.system() == "Windows":
|
|
493
467
|
file = file.replace("\\", "/")
|
|
@@ -511,12 +485,10 @@ class Core:
|
|
|
511
485
|
)
|
|
512
486
|
)
|
|
513
487
|
send_files.append(payload)
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
results = sdk.fullscans.post(files=files, params=params_dict)
|
|
519
|
-
|
|
488
|
+
query_params = urlencode(params.__dict__)
|
|
489
|
+
full_uri = f"{full_scan_path}?{query_params}"
|
|
490
|
+
response = do_request(full_uri, method="POST", files=send_files)
|
|
491
|
+
results = response.json()
|
|
520
492
|
full_scan = FullScan(**results)
|
|
521
493
|
full_scan.sbom_artifacts = Core.get_sbom_data(full_scan.id)
|
|
522
494
|
create_full_end = time.time()
|
|
@@ -541,13 +513,26 @@ class Core:
|
|
|
541
513
|
:param repo_slug: Str - Repo slug for the repository that is being diffed
|
|
542
514
|
:return:
|
|
543
515
|
"""
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
results =
|
|
516
|
+
repo_path = f"{repository_path}/{repo_slug}"
|
|
517
|
+
response = do_request(repo_path)
|
|
518
|
+
results = response.json()
|
|
547
519
|
repository = Repository(**results)
|
|
548
|
-
|
|
549
520
|
return repository.head_full_scan_id
|
|
550
521
|
|
|
522
|
+
@staticmethod
|
|
523
|
+
def get_full_scan(full_scan_id: str) -> FullScan:
|
|
524
|
+
"""
|
|
525
|
+
Get the specified full scan and return a FullScan object
|
|
526
|
+
:param full_scan_id: str - ID of the full scan to pull
|
|
527
|
+
:return:
|
|
528
|
+
"""
|
|
529
|
+
full_scan_url = f"{full_scan_path}/{full_scan_id}"
|
|
530
|
+
response = do_request(full_scan_url)
|
|
531
|
+
results = response.json()
|
|
532
|
+
full_scan = FullScan(**results)
|
|
533
|
+
full_scan.sbom_artifacts = Core.get_sbom_data(full_scan.id)
|
|
534
|
+
return full_scan
|
|
535
|
+
|
|
551
536
|
@staticmethod
|
|
552
537
|
def create_new_diff(
|
|
553
538
|
path: str,
|
|
@@ -570,13 +555,11 @@ class Core:
|
|
|
570
555
|
diff = Diff()
|
|
571
556
|
diff.id = "no_diff_id"
|
|
572
557
|
return diff
|
|
573
|
-
|
|
574
558
|
files = Core.find_files(path)
|
|
575
559
|
if files is None or len(files) == 0:
|
|
576
560
|
diff = Diff()
|
|
577
561
|
diff.id = "no_diff_id"
|
|
578
562
|
return diff
|
|
579
|
-
|
|
580
563
|
try:
|
|
581
564
|
head_full_scan_id = Core.get_head_scan_for_repo(params.repo)
|
|
582
565
|
if head_full_scan_id is None or head_full_scan_id == "":
|
|
@@ -587,35 +570,29 @@ class Core:
|
|
|
587
570
|
head_end = time.time()
|
|
588
571
|
total_head_time = head_end - head_start
|
|
589
572
|
log.info(f"Total time to get head full-scan {total_head_time: .2f}")
|
|
590
|
-
|
|
591
573
|
except APIResourceNotFound:
|
|
592
574
|
head_full_scan_id = None
|
|
593
575
|
head_full_scan = []
|
|
594
|
-
|
|
595
576
|
new_scan_start = time.time()
|
|
596
577
|
new_full_scan = Core.create_full_scan(files, params, workspace)
|
|
597
|
-
new_full_scan.packages = new_full_scan.sbom_artifacts
|
|
578
|
+
new_full_scan.packages = Core.create_sbom_dict(new_full_scan.sbom_artifacts)
|
|
598
579
|
new_scan_end = time.time()
|
|
599
|
-
|
|
600
580
|
total_new_time = new_scan_end - new_scan_start
|
|
601
581
|
log.info(f"Total time to get new full-scan {total_new_time: .2f}")
|
|
602
|
-
|
|
603
582
|
diff_report = Core.compare_sboms(new_full_scan.sbom_artifacts, head_full_scan)
|
|
604
583
|
diff_report.packages = new_full_scan.packages
|
|
605
|
-
|
|
584
|
+
# Set the diff ID and URLs
|
|
606
585
|
base_socket = "https://socket.dev/dashboard/org"
|
|
607
586
|
diff_report.id = new_full_scan.id
|
|
608
587
|
diff_report.report_url = f"{base_socket}/{org_slug}/sbom/{diff_report.id}"
|
|
609
|
-
|
|
610
588
|
if head_full_scan_id is not None:
|
|
611
589
|
diff_report.diff_url = f"{base_socket}/{org_slug}/diff/{diff_report.id}/{head_full_scan_id}"
|
|
612
590
|
else:
|
|
613
591
|
diff_report.diff_url = diff_report.report_url
|
|
614
|
-
|
|
615
592
|
return diff_report
|
|
616
593
|
|
|
617
594
|
@staticmethod
|
|
618
|
-
def compare_sboms(new_scan:
|
|
595
|
+
def compare_sboms(new_scan: list, head_scan: list) -> Diff:
|
|
619
596
|
"""
|
|
620
597
|
compare the SBOMs of the new full Scan and the head full scan. Return a Diff report with new packages,
|
|
621
598
|
removed packages, and new alerts for the new full scan compared to the head.
|
|
@@ -623,33 +600,28 @@ class Core:
|
|
|
623
600
|
:param head_scan: FullScan - Current head FullScan for the repository
|
|
624
601
|
:return:
|
|
625
602
|
"""
|
|
626
|
-
diff: Diff
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
603
|
+
diff: Diff
|
|
604
|
+
diff = Diff()
|
|
605
|
+
new_packages = Core.create_sbom_dict(new_scan)
|
|
606
|
+
head_packages = Core.create_sbom_dict(head_scan)
|
|
630
607
|
new_scan_alerts = {}
|
|
631
608
|
head_scan_alerts = {}
|
|
632
609
|
consolidated = set()
|
|
633
|
-
|
|
634
610
|
for package_id in new_packages:
|
|
635
611
|
purl, package = Core.create_purl(package_id, new_packages)
|
|
636
612
|
base_purl = f"{purl.ecosystem}/{purl.name}@{purl.version}"
|
|
637
|
-
|
|
638
613
|
if package_id not in head_packages and package.direct and base_purl not in consolidated:
|
|
639
614
|
diff.new_packages.append(purl)
|
|
640
615
|
consolidated.add(base_purl)
|
|
641
616
|
new_scan_alerts = Core.create_issue_alerts(package, new_scan_alerts, new_packages)
|
|
642
|
-
|
|
643
617
|
for package_id in head_packages:
|
|
644
618
|
purl, package = Core.create_purl(package_id, head_packages)
|
|
645
619
|
if package_id not in new_packages and package.direct:
|
|
646
620
|
diff.removed_packages.append(purl)
|
|
647
621
|
head_scan_alerts = Core.create_issue_alerts(package, head_scan_alerts, head_packages)
|
|
648
|
-
|
|
649
622
|
diff.new_alerts = Core.compare_issue_alerts(new_scan_alerts, head_scan_alerts, diff.new_alerts)
|
|
650
623
|
diff.new_capabilities = Core.compare_capabilities(new_packages, head_packages)
|
|
651
624
|
diff = Core.add_capabilities_to_purl(diff)
|
|
652
|
-
|
|
653
625
|
return diff
|
|
654
626
|
|
|
655
627
|
@staticmethod
|
|
@@ -855,6 +827,32 @@ class Core:
|
|
|
855
827
|
)
|
|
856
828
|
return purl, package
|
|
857
829
|
|
|
830
|
+
@staticmethod
|
|
831
|
+
def create_sbom_dict(sbom: list) -> dict:
|
|
832
|
+
"""
|
|
833
|
+
Converts the SBOM Artifacts from the FulLScan into a Dictionary for parsing
|
|
834
|
+
:param sbom: list - Raw artifacts for the SBOM
|
|
835
|
+
:return:
|
|
836
|
+
"""
|
|
837
|
+
packages = {}
|
|
838
|
+
top_level_count = {}
|
|
839
|
+
for item in sbom:
|
|
840
|
+
package = Package(**item)
|
|
841
|
+
if package.id in packages:
|
|
842
|
+
print("Duplicate package?")
|
|
843
|
+
else:
|
|
844
|
+
package = Core.get_license_details(package)
|
|
845
|
+
packages[package.id] = package
|
|
846
|
+
for top_id in package.topLevelAncestors:
|
|
847
|
+
if top_id not in top_level_count:
|
|
848
|
+
top_level_count[top_id] = 1
|
|
849
|
+
else:
|
|
850
|
+
top_level_count[top_id] += 1
|
|
851
|
+
if len(top_level_count) > 0:
|
|
852
|
+
for package_id in top_level_count:
|
|
853
|
+
packages[package_id].transitives = top_level_count[package_id]
|
|
854
|
+
return packages
|
|
855
|
+
|
|
858
856
|
@staticmethod
|
|
859
857
|
def save_file(file_name: str, content: str) -> None:
|
|
860
858
|
file = open(file_name, "w")
|
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
from socketsecurity.core import log, do_request
|
|
4
|
+
import requests
|
|
4
5
|
from socketsecurity.core.classes import Comment
|
|
5
6
|
from socketsecurity.core.scm_comments import Comments
|
|
6
7
|
import sys
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
github_sha
|
|
10
|
-
github_api_url
|
|
11
|
-
github_ref_type
|
|
12
|
-
github_event_name
|
|
13
|
-
github_workspace
|
|
14
|
-
github_repository
|
|
15
|
-
github_ref_name
|
|
16
|
-
github_actor
|
|
17
|
-
default_branch
|
|
18
|
-
github_env
|
|
19
|
-
pr_number
|
|
20
|
-
pr_name
|
|
21
|
-
is_default_branch
|
|
22
|
-
commit_message
|
|
23
|
-
committer
|
|
24
|
-
gh_api_token
|
|
25
|
-
github_repository_owner
|
|
26
|
-
event_action
|
|
10
|
+
global github_sha
|
|
11
|
+
global github_api_url
|
|
12
|
+
global github_ref_type
|
|
13
|
+
global github_event_name
|
|
14
|
+
global github_workspace
|
|
15
|
+
global github_repository
|
|
16
|
+
global github_ref_name
|
|
17
|
+
global github_actor
|
|
18
|
+
global default_branch
|
|
19
|
+
global github_env
|
|
20
|
+
global pr_number
|
|
21
|
+
global pr_name
|
|
22
|
+
global is_default_branch
|
|
23
|
+
global commit_message
|
|
24
|
+
global committer
|
|
25
|
+
global gh_api_token
|
|
26
|
+
global github_repository_owner
|
|
27
|
+
global event_action
|
|
27
28
|
|
|
28
29
|
github_variables = [
|
|
29
30
|
"GITHUB_SHA",
|
|
@@ -46,14 +47,16 @@ github_variables = [
|
|
|
46
47
|
|
|
47
48
|
for env in github_variables:
|
|
48
49
|
var_name = env.lower()
|
|
49
|
-
|
|
50
|
-
globals()[var_name] = value
|
|
51
|
-
|
|
50
|
+
globals()[var_name] = os.getenv(env) or None
|
|
52
51
|
if var_name == "default_branch":
|
|
53
|
-
is_default_branch
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
global is_default_branch
|
|
53
|
+
if default_branch is None or default_branch.lower() == "false":
|
|
54
|
+
is_default_branch = False
|
|
55
|
+
else:
|
|
56
|
+
is_default_branch = True
|
|
57
|
+
if var_name != "gh_api_token":
|
|
58
|
+
value = globals()[var_name] = os.getenv(env) or None
|
|
59
|
+
log.debug(f"{env}={value}")
|
|
57
60
|
|
|
58
61
|
headers = {
|
|
59
62
|
'Authorization': f"Bearer {gh_api_token}",
|
|
@@ -167,15 +167,12 @@ def output_console_comments(diff_report: Diff, sbom_file_name: str = None) -> No
|
|
|
167
167
|
console_security_comment = Messages.create_console_security_alert_table(diff_report)
|
|
168
168
|
save_sbom_file(diff_report, sbom_file_name)
|
|
169
169
|
log.info(f"Socket Full Scan ID: {diff_report.id}")
|
|
170
|
-
|
|
171
170
|
if len(diff_report.new_alerts) > 0:
|
|
172
171
|
log.info("Security issues detected by Socket Security")
|
|
173
172
|
msg = f"\n{console_security_comment}"
|
|
174
173
|
log.info(msg)
|
|
175
|
-
|
|
176
174
|
if not report_pass(diff_report) and not blocking_disabled:
|
|
177
175
|
sys.exit(1)
|
|
178
|
-
|
|
179
176
|
else:
|
|
180
177
|
# Means only warning alerts with no blocked
|
|
181
178
|
if not blocking_disabled:
|
|
@@ -191,7 +188,7 @@ def output_console_json(diff_report: Diff, sbom_file_name: str = None) -> None:
|
|
|
191
188
|
print(json.dumps(console_security_comment))
|
|
192
189
|
if not report_pass(diff_report) and not blocking_disabled:
|
|
193
190
|
sys.exit(1)
|
|
194
|
-
|
|
191
|
+
elif len(diff_report.new_alerts) > 0 and not blocking_disabled:
|
|
195
192
|
# Means only warning alerts with no blocked
|
|
196
193
|
sys.exit(5)
|
|
197
194
|
|
|
@@ -255,7 +252,6 @@ def main_code():
|
|
|
255
252
|
ignore_commit_files = arguments.ignore_commit_files
|
|
256
253
|
disable_blocking = arguments.disable_blocking
|
|
257
254
|
allow_unverified = arguments.allow_unverified
|
|
258
|
-
|
|
259
255
|
if disable_blocking:
|
|
260
256
|
global blocking_disabled
|
|
261
257
|
blocking_disabled = True
|
|
@@ -312,20 +308,13 @@ def main_code():
|
|
|
312
308
|
default_branch = scm.is_default_branch
|
|
313
309
|
|
|
314
310
|
base_api_url = os.getenv("BASE_API_URL") or None
|
|
315
|
-
|
|
316
|
-
Core.initialize(
|
|
317
|
-
token=api_token,
|
|
318
|
-
request_timeout=1200,
|
|
319
|
-
base_api_url=base_api_url,
|
|
320
|
-
allow_unverified=allow_unverified
|
|
321
|
-
)
|
|
322
|
-
|
|
311
|
+
core = Core(token=api_token, request_timeout=1200, base_api_url=base_api_url, allow_unverified=allow_unverified)
|
|
323
312
|
no_change = True
|
|
324
313
|
if ignore_commit_files:
|
|
325
314
|
no_change = False
|
|
326
315
|
elif is_repo and files is not None and len(files) > 0:
|
|
327
316
|
log.info(files)
|
|
328
|
-
no_change =
|
|
317
|
+
no_change = core.match_supported_files(files)
|
|
329
318
|
|
|
330
319
|
set_as_pending_head = False
|
|
331
320
|
if default_branch:
|
|
@@ -355,7 +344,7 @@ def main_code():
|
|
|
355
344
|
log.info("No manifest files changes, skipping scan")
|
|
356
345
|
# log.info("No dependency changes")
|
|
357
346
|
elif scm.check_event_type() == "diff":
|
|
358
|
-
diff =
|
|
347
|
+
diff = core.create_new_diff(target_path, params, workspace=target_path, no_change=no_change)
|
|
359
348
|
log.info("Starting comment logic for PR/MR event")
|
|
360
349
|
log.debug(f"Getting comments for Repo {scm.repository} for PR {scm.pr_number}")
|
|
361
350
|
comments = scm.get_comments_for_pr(repo, str(pr_number))
|
|
@@ -399,7 +388,7 @@ def main_code():
|
|
|
399
388
|
)
|
|
400
389
|
else:
|
|
401
390
|
log.info("Starting non-PR/MR flow")
|
|
402
|
-
diff =
|
|
391
|
+
diff = core.create_new_diff(target_path, params, workspace=target_path, no_change=no_change)
|
|
403
392
|
if enable_json:
|
|
404
393
|
log.debug("Outputting JSON Results")
|
|
405
394
|
output_console_json(diff, sbom_file)
|
|
@@ -408,7 +397,7 @@ def main_code():
|
|
|
408
397
|
else:
|
|
409
398
|
log.info("API Mode")
|
|
410
399
|
diff: Diff
|
|
411
|
-
diff =
|
|
400
|
+
diff = core.create_new_diff(target_path, params, workspace=target_path, no_change=no_change)
|
|
412
401
|
if enable_json:
|
|
413
402
|
output_console_json(diff, sbom_file)
|
|
414
403
|
else:
|
|
@@ -429,7 +418,7 @@ def main_code():
|
|
|
429
418
|
"license_text": package.license_text
|
|
430
419
|
}
|
|
431
420
|
all_packages[package_id] = output
|
|
432
|
-
|
|
421
|
+
core.save_file(license_file, json.dumps(all_packages))
|
|
433
422
|
|
|
434
423
|
|
|
435
424
|
if __name__ == '__main__':
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: socketsecurity
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.35
|
|
4
4
|
Summary: Socket Security CLI for CI/CD
|
|
5
5
|
Author-email: Douglas Coburn <douglas@socket.dev>
|
|
6
6
|
Maintainer-email: Douglas Coburn <douglas@socket.dev>
|
|
@@ -15,16 +15,12 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
15
15
|
Requires-Python: >=3.9
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: requests
|
|
19
|
-
Requires-Dist: mdutils
|
|
18
|
+
Requires-Dist: requests
|
|
19
|
+
Requires-Dist: mdutils
|
|
20
20
|
Requires-Dist: prettytable
|
|
21
21
|
Requires-Dist: argparse
|
|
22
|
-
Requires-Dist: GitPython
|
|
23
|
-
Requires-Dist: packaging
|
|
24
|
-
Requires-Dist: python-dotenv>=1.0.1
|
|
25
|
-
Requires-Dist: socket-sdk-python>=1.0.14
|
|
26
|
-
Provides-Extra: dev
|
|
27
|
-
Requires-Dist: ruff>=0.3.0; extra == "dev"
|
|
22
|
+
Requires-Dist: GitPython
|
|
23
|
+
Requires-Dist: packaging
|
|
28
24
|
|
|
29
25
|
# Socket Security CLI
|
|
30
26
|
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools >= 61.0"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "socketsecurity"
|
|
7
|
-
dynamic = ["version"]
|
|
8
|
-
requires-python = ">= 3.9"
|
|
9
|
-
dependencies = [
|
|
10
|
-
'requests >= 2.32.0',
|
|
11
|
-
'mdutils ~= 1.6.0',
|
|
12
|
-
'prettytable',
|
|
13
|
-
'argparse',
|
|
14
|
-
'GitPython >= 3.1.43',
|
|
15
|
-
'packaging >= 24.1',
|
|
16
|
-
'python-dotenv >= 1.0.1',
|
|
17
|
-
'socket-sdk-python >= 1.0.14'
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
readme = "README.md"
|
|
22
|
-
description = "Socket Security CLI for CI/CD"
|
|
23
|
-
keywords = ["socketsecurity", "socket.dev", "sca", "oss", "security"]
|
|
24
|
-
authors = [
|
|
25
|
-
{name = "Douglas Coburn", email = "douglas@socket.dev"}
|
|
26
|
-
]
|
|
27
|
-
maintainers = [
|
|
28
|
-
{name = "Douglas Coburn", email = "douglas@socket.dev"}
|
|
29
|
-
]
|
|
30
|
-
classifiers = [
|
|
31
|
-
"Development Status :: 4 - Beta",
|
|
32
|
-
"Intended Audience :: Developers",
|
|
33
|
-
"Programming Language :: Python :: 3.9",
|
|
34
|
-
"Programming Language :: Python :: 3.10",
|
|
35
|
-
"Programming Language :: Python :: 3.11",
|
|
36
|
-
"Programming Language :: Python :: 3.12",
|
|
37
|
-
]
|
|
38
|
-
|
|
39
|
-
# modern, faster linter and language server. install with `pip install -e ".[dev]"`
|
|
40
|
-
[project.optional-dependencies]
|
|
41
|
-
dev = [
|
|
42
|
-
"ruff>=0.3.0",
|
|
43
|
-
]
|
|
44
|
-
|
|
45
|
-
[project.scripts]
|
|
46
|
-
socketcli = "socketsecurity.socketcli:cli"
|
|
47
|
-
|
|
48
|
-
[project.urls]
|
|
49
|
-
Homepage = "https://socket.dev"
|
|
50
|
-
|
|
51
|
-
[tool.setuptools.packages.find]
|
|
52
|
-
include = [
|
|
53
|
-
"socketsecurity",
|
|
54
|
-
"socketsecurity.core"
|
|
55
|
-
]
|
|
56
|
-
|
|
57
|
-
[tool.setuptools.dynamic]
|
|
58
|
-
version = {attr = "socketsecurity.__version__"}
|
|
59
|
-
|
|
60
|
-
[tool.ruff]
|
|
61
|
-
# Exclude a variety of commonly ignored directories.
|
|
62
|
-
exclude = [
|
|
63
|
-
".bzr",
|
|
64
|
-
".direnv",
|
|
65
|
-
".eggs",
|
|
66
|
-
".git",
|
|
67
|
-
".git-rewrite",
|
|
68
|
-
".hg",
|
|
69
|
-
".ipynb_checkpoints",
|
|
70
|
-
".mypy_cache",
|
|
71
|
-
".nox",
|
|
72
|
-
".pants.d",
|
|
73
|
-
".pyenv",
|
|
74
|
-
".pytest_cache",
|
|
75
|
-
".pytype",
|
|
76
|
-
".ruff_cache",
|
|
77
|
-
".svn",
|
|
78
|
-
".tox",
|
|
79
|
-
".venv",
|
|
80
|
-
".vscode",
|
|
81
|
-
"__pypackages__",
|
|
82
|
-
"_build",
|
|
83
|
-
"buck-out",
|
|
84
|
-
"build",
|
|
85
|
-
"dist",
|
|
86
|
-
"node_modules",
|
|
87
|
-
"site-packages",
|
|
88
|
-
"venv",
|
|
89
|
-
]
|
|
90
|
-
|
|
91
|
-
[tool.ruff.lint]
|
|
92
|
-
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
|
93
|
-
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
|
|
94
|
-
# McCabe complexity (`C901`) by default.
|
|
95
|
-
select = ["E4", "E7", "E9", "F"]
|
|
96
|
-
ignore = []
|
|
97
|
-
|
|
98
|
-
# Allow fix for all enabled rules (when `--fix`) is provided.
|
|
99
|
-
fixable = ["ALL"]
|
|
100
|
-
unfixable = []
|
|
101
|
-
|
|
102
|
-
# Allow unused variables when underscore-prefixed.
|
|
103
|
-
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
104
|
-
|
|
105
|
-
[tool.ruff.format]
|
|
106
|
-
# Like Black, use double quotes for strings.
|
|
107
|
-
quote-style = "double"
|
|
108
|
-
|
|
109
|
-
# Like Black, indent with spaces, rather than tabs.
|
|
110
|
-
indent-style = "space"
|
|
111
|
-
|
|
112
|
-
# Like Black, respect magic trailing commas.
|
|
113
|
-
skip-magic-trailing-comma = false
|
|
114
|
-
|
|
115
|
-
# Like Black, automatically detect the appropriate line ending.
|
|
116
|
-
line-ending = "auto"
|
|
117
|
-
|
|
118
|
-
# Enable auto-formatting of code examples in docstrings. Markdown,
|
|
119
|
-
# reStructuredText code/literal blocks and doctests are all supported.
|
|
120
|
-
#
|
|
121
|
-
# This is currently disabled by default, but it is planned for this
|
|
122
|
-
# to be opt-out in the future.
|
|
123
|
-
docstring-code-format = false
|
|
124
|
-
|
|
125
|
-
# Set the line length limit used when formatting code snippets in
|
|
126
|
-
# docstrings.
|
|
127
|
-
#
|
|
128
|
-
# This only has an effect when the `docstring-code-format` setting is
|
|
129
|
-
# enabled.
|
|
130
|
-
docstring-code-line-length = "dynamic"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{socketsecurity-1.0.34 → socketsecurity-1.0.35}/socketsecurity.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|