socketsecurity 1.0.34__tar.gz → 1.0.37__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.37}/PKG-INFO +5 -10
- socketsecurity-1.0.37/pyproject.toml +48 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/__init__.py +1 -1
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/core/__init__.py +119 -118
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/core/github.py +28 -25
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/socketcli.py +7 -18
- {socketsecurity-1.0.34 → socketsecurity-1.0.37/socketsecurity.egg-info}/PKG-INFO +5 -10
- socketsecurity-1.0.37/socketsecurity.egg-info/requires.txt +5 -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.37}/LICENSE +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/README.md +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/setup.cfg +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/core/gitlab.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/core/issues.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/core/licenses.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/core/messages.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity.egg-info/SOURCES.txt +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity.egg-info/dependency_links.txt +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/socketsecurity.egg-info/entry_points.txt +0 -0
- {socketsecurity-1.0.34 → socketsecurity-1.0.37}/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.37
|
|
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,11 @@ 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
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist: packaging>=24.1
|
|
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"
|
|
21
|
+
Requires-Dist: GitPython
|
|
22
|
+
Requires-Dist: packaging
|
|
28
23
|
|
|
29
24
|
# Socket Security CLI
|
|
30
25
|
|
|
@@ -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
|
+
'GitPython',
|
|
14
|
+
'packaging',
|
|
15
|
+
]
|
|
16
|
+
readme = "README.md"
|
|
17
|
+
description = "Socket Security CLI for CI/CD"
|
|
18
|
+
keywords = ["socketsecurity", "socket.dev", "sca", "oss", "security"]
|
|
19
|
+
authors = [
|
|
20
|
+
{name = "Douglas Coburn", email = "douglas@socket.dev"}
|
|
21
|
+
]
|
|
22
|
+
maintainers = [
|
|
23
|
+
{name = "Douglas Coburn", email = "douglas@socket.dev"}
|
|
24
|
+
]
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Development Status :: 4 - Beta",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"Programming Language :: Python :: 3.9",
|
|
29
|
+
"Programming Language :: Python :: 3.10",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
]
|
|
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.37'
|
|
@@ -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,
|
|
@@ -115,6 +116,9 @@ socket_globs = {
|
|
|
115
116
|
},
|
|
116
117
|
"setup.py": {
|
|
117
118
|
"pattern": "setup.py"
|
|
119
|
+
},
|
|
120
|
+
"pipfile.lock": {
|
|
121
|
+
"pattern": "pipfile.lock"
|
|
118
122
|
}
|
|
119
123
|
},
|
|
120
124
|
"golang": {
|
|
@@ -225,53 +229,34 @@ def do_request(
|
|
|
225
229
|
|
|
226
230
|
|
|
227
231
|
class Core:
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
raise NotImplementedError("Use Core.initialize() instead of instantiation")
|
|
232
|
+
token: str
|
|
233
|
+
base_api_url: str
|
|
234
|
+
request_timeout: int
|
|
235
|
+
reports: list
|
|
233
236
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
cls,
|
|
237
|
+
def __init__(
|
|
238
|
+
self,
|
|
237
239
|
token: str,
|
|
238
240
|
base_api_url: str = None,
|
|
239
241
|
request_timeout: int = None,
|
|
240
242
|
enable_all_alerts: bool = False,
|
|
241
243
|
allow_unverified: bool = False
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
if cls._initialized:
|
|
245
|
-
return
|
|
246
|
-
|
|
247
|
-
global allow_unverified_ssl, all_new_alerts
|
|
248
|
-
|
|
244
|
+
):
|
|
245
|
+
global allow_unverified_ssl
|
|
249
246
|
allow_unverified_ssl = allow_unverified
|
|
250
|
-
|
|
251
|
-
encode_key(token
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
247
|
+
self.token = token + ":"
|
|
248
|
+
encode_key(self.token)
|
|
249
|
+
self.socket_date_format = "%Y-%m-%dT%H:%M:%S.%fZ"
|
|
250
|
+
self.base_api_url = base_api_url
|
|
251
|
+
if self.base_api_url is not None:
|
|
252
|
+
Core.set_api_url(self.base_api_url)
|
|
253
|
+
self.request_timeout = request_timeout
|
|
254
|
+
if self.request_timeout is not None:
|
|
255
|
+
Core.set_timeout(self.request_timeout)
|
|
259
256
|
if enable_all_alerts:
|
|
257
|
+
global all_new_alerts
|
|
260
258
|
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
|
|
259
|
+
Core.set_org_vars()
|
|
275
260
|
|
|
276
261
|
@staticmethod
|
|
277
262
|
def enable_debug_log(level: int):
|
|
@@ -315,50 +300,39 @@ class Core:
|
|
|
315
300
|
def get_org_id_slug() -> (str, str):
|
|
316
301
|
"""
|
|
317
302
|
Gets the Org ID and Org Slug for the API Token
|
|
318
|
-
:return:
|
|
303
|
+
:return:
|
|
319
304
|
"""
|
|
305
|
+
path = "organizations"
|
|
306
|
+
response = do_request(path)
|
|
307
|
+
data = response.json()
|
|
308
|
+
organizations = data.get("organizations")
|
|
320
309
|
new_org_id = None
|
|
321
310
|
new_org_slug = None
|
|
322
|
-
|
|
323
|
-
sdk = Core.get_sdk()
|
|
324
|
-
data = sdk.org.get()
|
|
325
|
-
organizations = data.get("organizations")
|
|
326
|
-
|
|
327
311
|
if len(organizations) == 1:
|
|
328
312
|
for key in organizations:
|
|
329
313
|
new_org_id = key
|
|
330
314
|
new_org_slug = organizations[key].get('slug')
|
|
331
|
-
|
|
332
315
|
return new_org_id, new_org_slug
|
|
333
316
|
|
|
334
317
|
@staticmethod
|
|
335
|
-
def get_sbom_data(full_scan_id: str) ->
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
318
|
+
def get_sbom_data(full_scan_id: str) -> list:
|
|
319
|
+
path = f"orgs/{org_slug}/full-scans/{full_scan_id}"
|
|
320
|
+
response = do_request(path)
|
|
321
|
+
results = []
|
|
322
|
+
try:
|
|
323
|
+
data = response.json()
|
|
324
|
+
results = data.get("sbom_artifacts") or []
|
|
325
|
+
except Exception as error:
|
|
326
|
+
log.debug("Failed with old style full-scan API using new format")
|
|
327
|
+
log.debug(error)
|
|
328
|
+
data = response.text
|
|
342
329
|
data.strip('"')
|
|
343
330
|
data.strip()
|
|
344
|
-
|
|
345
331
|
for line in data.split("\n"):
|
|
346
332
|
if line != '"' and line != "" and line is not None:
|
|
347
333
|
item = json.loads(line)
|
|
348
334
|
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
|
-
|
|
335
|
+
return results
|
|
362
336
|
|
|
363
337
|
@staticmethod
|
|
364
338
|
def get_security_policy() -> dict:
|
|
@@ -366,21 +340,23 @@ class Core:
|
|
|
366
340
|
Get the Security policy and determine the effective Org security policy
|
|
367
341
|
:return:
|
|
368
342
|
"""
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
343
|
+
path = "settings"
|
|
344
|
+
payload = [
|
|
345
|
+
{
|
|
346
|
+
"organization": org_id
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
response = do_request(path, payload=json.dumps(payload), method="POST")
|
|
350
|
+
data = response.json()
|
|
373
351
|
defaults = data.get("defaults")
|
|
374
352
|
default_rules = defaults.get("issueRules")
|
|
375
353
|
entries = data.get("entries")
|
|
376
354
|
org_rules = {}
|
|
377
|
-
|
|
378
355
|
for org_set in entries:
|
|
379
356
|
settings = org_set.get("settings")
|
|
380
357
|
if settings is not None:
|
|
381
358
|
org_details = settings.get("organization")
|
|
382
359
|
org_rules = org_details.get("issueRules")
|
|
383
|
-
|
|
384
360
|
for default in default_rules:
|
|
385
361
|
if default not in org_rules:
|
|
386
362
|
action = default_rules[default]["action"]
|
|
@@ -389,6 +365,10 @@ class Core:
|
|
|
389
365
|
}
|
|
390
366
|
return org_rules
|
|
391
367
|
|
|
368
|
+
# @staticmethod
|
|
369
|
+
# def get_supported_file_types() -> dict:
|
|
370
|
+
# path = "report/supported"
|
|
371
|
+
|
|
392
372
|
@staticmethod
|
|
393
373
|
def get_manifest_files(package: Package, packages: dict) -> str:
|
|
394
374
|
if package.direct:
|
|
@@ -411,16 +391,15 @@ class Core:
|
|
|
411
391
|
|
|
412
392
|
@staticmethod
|
|
413
393
|
def create_sbom_output(diff: Diff) -> dict:
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
394
|
+
base_path = f"orgs/{org_slug}/export/cdx"
|
|
395
|
+
path = f"{base_path}/{diff.id}"
|
|
396
|
+
result = do_request(path=path)
|
|
397
|
+
try:
|
|
398
|
+
sbom = result.json()
|
|
399
|
+
except Exception as error:
|
|
418
400
|
log.error(f"Unable to get CycloneDX Output for {diff.id}")
|
|
419
|
-
log.error(
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
sbom.pop("success", None)
|
|
423
|
-
|
|
401
|
+
log.error(error)
|
|
402
|
+
sbom = {}
|
|
424
403
|
return sbom
|
|
425
404
|
|
|
426
405
|
@staticmethod
|
|
@@ -431,7 +410,7 @@ class Core:
|
|
|
431
410
|
patterns = socket_globs[ecosystem]
|
|
432
411
|
for file_name in patterns:
|
|
433
412
|
pattern = patterns[file_name]["pattern"]
|
|
434
|
-
|
|
413
|
+
# path_pattern = f"**/{pattern}"
|
|
435
414
|
for file in files:
|
|
436
415
|
if "\\" in file:
|
|
437
416
|
file = file.replace("\\", "/")
|
|
@@ -449,12 +428,11 @@ class Core:
|
|
|
449
428
|
:param path: Str - path to where the manifest files are located
|
|
450
429
|
:return:
|
|
451
430
|
"""
|
|
452
|
-
|
|
431
|
+
log.debug("Starting Find Files")
|
|
453
432
|
start_time = time.time()
|
|
454
433
|
files = set()
|
|
455
434
|
for ecosystem in socket_globs:
|
|
456
435
|
patterns = socket_globs[ecosystem]
|
|
457
|
-
|
|
458
436
|
for file_name in patterns:
|
|
459
437
|
pattern = patterns[file_name]["pattern"]
|
|
460
438
|
file_path = f"{path}/**/{pattern}"
|
|
@@ -462,15 +440,14 @@ class Core:
|
|
|
462
440
|
log.debug(f"Globbing {file_path}")
|
|
463
441
|
glob_start = time.time()
|
|
464
442
|
glob_files = glob(file_path, recursive=True)
|
|
465
|
-
|
|
466
443
|
for glob_file in glob_files:
|
|
467
444
|
if glob_file not in files:
|
|
468
445
|
files.add(glob_file)
|
|
469
|
-
|
|
470
446
|
glob_end = time.time()
|
|
471
447
|
glob_total_time = glob_end - glob_start
|
|
472
448
|
log.debug(f"Glob for pattern {file_path} took {glob_total_time:.2f} seconds")
|
|
473
449
|
|
|
450
|
+
log.debug("Finished Find Files")
|
|
474
451
|
end_time = time.time()
|
|
475
452
|
total_time = end_time - start_time
|
|
476
453
|
log.info(f"Found {len(files)} in {total_time:.2f} seconds")
|
|
@@ -487,7 +464,7 @@ class Core:
|
|
|
487
464
|
"""
|
|
488
465
|
send_files = []
|
|
489
466
|
create_full_start = time.time()
|
|
490
|
-
|
|
467
|
+
log.debug("Creating new full scan")
|
|
491
468
|
for file in files:
|
|
492
469
|
if platform.system() == "Windows":
|
|
493
470
|
file = file.replace("\\", "/")
|
|
@@ -511,12 +488,10 @@ class Core:
|
|
|
511
488
|
)
|
|
512
489
|
)
|
|
513
490
|
send_files.append(payload)
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
results = sdk.fullscans.post(files=files, params=params_dict)
|
|
519
|
-
|
|
491
|
+
query_params = urlencode(params.__dict__)
|
|
492
|
+
full_uri = f"{full_scan_path}?{query_params}"
|
|
493
|
+
response = do_request(full_uri, method="POST", files=send_files)
|
|
494
|
+
results = response.json()
|
|
520
495
|
full_scan = FullScan(**results)
|
|
521
496
|
full_scan.sbom_artifacts = Core.get_sbom_data(full_scan.id)
|
|
522
497
|
create_full_end = time.time()
|
|
@@ -541,13 +516,26 @@ class Core:
|
|
|
541
516
|
:param repo_slug: Str - Repo slug for the repository that is being diffed
|
|
542
517
|
:return:
|
|
543
518
|
"""
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
results =
|
|
519
|
+
repo_path = f"{repository_path}/{repo_slug}"
|
|
520
|
+
response = do_request(repo_path)
|
|
521
|
+
results = response.json()
|
|
547
522
|
repository = Repository(**results)
|
|
548
|
-
|
|
549
523
|
return repository.head_full_scan_id
|
|
550
524
|
|
|
525
|
+
@staticmethod
|
|
526
|
+
def get_full_scan(full_scan_id: str) -> FullScan:
|
|
527
|
+
"""
|
|
528
|
+
Get the specified full scan and return a FullScan object
|
|
529
|
+
:param full_scan_id: str - ID of the full scan to pull
|
|
530
|
+
:return:
|
|
531
|
+
"""
|
|
532
|
+
full_scan_url = f"{full_scan_path}/{full_scan_id}"
|
|
533
|
+
response = do_request(full_scan_url)
|
|
534
|
+
results = response.json()
|
|
535
|
+
full_scan = FullScan(**results)
|
|
536
|
+
full_scan.sbom_artifacts = Core.get_sbom_data(full_scan.id)
|
|
537
|
+
return full_scan
|
|
538
|
+
|
|
551
539
|
@staticmethod
|
|
552
540
|
def create_new_diff(
|
|
553
541
|
path: str,
|
|
@@ -570,13 +558,11 @@ class Core:
|
|
|
570
558
|
diff = Diff()
|
|
571
559
|
diff.id = "no_diff_id"
|
|
572
560
|
return diff
|
|
573
|
-
|
|
574
561
|
files = Core.find_files(path)
|
|
575
562
|
if files is None or len(files) == 0:
|
|
576
563
|
diff = Diff()
|
|
577
564
|
diff.id = "no_diff_id"
|
|
578
565
|
return diff
|
|
579
|
-
|
|
580
566
|
try:
|
|
581
567
|
head_full_scan_id = Core.get_head_scan_for_repo(params.repo)
|
|
582
568
|
if head_full_scan_id is None or head_full_scan_id == "":
|
|
@@ -587,35 +573,29 @@ class Core:
|
|
|
587
573
|
head_end = time.time()
|
|
588
574
|
total_head_time = head_end - head_start
|
|
589
575
|
log.info(f"Total time to get head full-scan {total_head_time: .2f}")
|
|
590
|
-
|
|
591
576
|
except APIResourceNotFound:
|
|
592
577
|
head_full_scan_id = None
|
|
593
578
|
head_full_scan = []
|
|
594
|
-
|
|
595
579
|
new_scan_start = time.time()
|
|
596
580
|
new_full_scan = Core.create_full_scan(files, params, workspace)
|
|
597
|
-
new_full_scan.packages = new_full_scan.sbom_artifacts
|
|
581
|
+
new_full_scan.packages = Core.create_sbom_dict(new_full_scan.sbom_artifacts)
|
|
598
582
|
new_scan_end = time.time()
|
|
599
|
-
|
|
600
583
|
total_new_time = new_scan_end - new_scan_start
|
|
601
584
|
log.info(f"Total time to get new full-scan {total_new_time: .2f}")
|
|
602
|
-
|
|
603
585
|
diff_report = Core.compare_sboms(new_full_scan.sbom_artifacts, head_full_scan)
|
|
604
586
|
diff_report.packages = new_full_scan.packages
|
|
605
|
-
|
|
587
|
+
# Set the diff ID and URLs
|
|
606
588
|
base_socket = "https://socket.dev/dashboard/org"
|
|
607
589
|
diff_report.id = new_full_scan.id
|
|
608
590
|
diff_report.report_url = f"{base_socket}/{org_slug}/sbom/{diff_report.id}"
|
|
609
|
-
|
|
610
591
|
if head_full_scan_id is not None:
|
|
611
592
|
diff_report.diff_url = f"{base_socket}/{org_slug}/diff/{diff_report.id}/{head_full_scan_id}"
|
|
612
593
|
else:
|
|
613
594
|
diff_report.diff_url = diff_report.report_url
|
|
614
|
-
|
|
615
595
|
return diff_report
|
|
616
596
|
|
|
617
597
|
@staticmethod
|
|
618
|
-
def compare_sboms(new_scan:
|
|
598
|
+
def compare_sboms(new_scan: list, head_scan: list) -> Diff:
|
|
619
599
|
"""
|
|
620
600
|
compare the SBOMs of the new full Scan and the head full scan. Return a Diff report with new packages,
|
|
621
601
|
removed packages, and new alerts for the new full scan compared to the head.
|
|
@@ -623,33 +603,28 @@ class Core:
|
|
|
623
603
|
:param head_scan: FullScan - Current head FullScan for the repository
|
|
624
604
|
:return:
|
|
625
605
|
"""
|
|
626
|
-
diff: Diff
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
606
|
+
diff: Diff
|
|
607
|
+
diff = Diff()
|
|
608
|
+
new_packages = Core.create_sbom_dict(new_scan)
|
|
609
|
+
head_packages = Core.create_sbom_dict(head_scan)
|
|
630
610
|
new_scan_alerts = {}
|
|
631
611
|
head_scan_alerts = {}
|
|
632
612
|
consolidated = set()
|
|
633
|
-
|
|
634
613
|
for package_id in new_packages:
|
|
635
614
|
purl, package = Core.create_purl(package_id, new_packages)
|
|
636
615
|
base_purl = f"{purl.ecosystem}/{purl.name}@{purl.version}"
|
|
637
|
-
|
|
638
616
|
if package_id not in head_packages and package.direct and base_purl not in consolidated:
|
|
639
617
|
diff.new_packages.append(purl)
|
|
640
618
|
consolidated.add(base_purl)
|
|
641
619
|
new_scan_alerts = Core.create_issue_alerts(package, new_scan_alerts, new_packages)
|
|
642
|
-
|
|
643
620
|
for package_id in head_packages:
|
|
644
621
|
purl, package = Core.create_purl(package_id, head_packages)
|
|
645
622
|
if package_id not in new_packages and package.direct:
|
|
646
623
|
diff.removed_packages.append(purl)
|
|
647
624
|
head_scan_alerts = Core.create_issue_alerts(package, head_scan_alerts, head_packages)
|
|
648
|
-
|
|
649
625
|
diff.new_alerts = Core.compare_issue_alerts(new_scan_alerts, head_scan_alerts, diff.new_alerts)
|
|
650
626
|
diff.new_capabilities = Core.compare_capabilities(new_packages, head_packages)
|
|
651
627
|
diff = Core.add_capabilities_to_purl(diff)
|
|
652
|
-
|
|
653
628
|
return diff
|
|
654
629
|
|
|
655
630
|
@staticmethod
|
|
@@ -855,6 +830,32 @@ class Core:
|
|
|
855
830
|
)
|
|
856
831
|
return purl, package
|
|
857
832
|
|
|
833
|
+
@staticmethod
|
|
834
|
+
def create_sbom_dict(sbom: list) -> dict:
|
|
835
|
+
"""
|
|
836
|
+
Converts the SBOM Artifacts from the FulLScan into a Dictionary for parsing
|
|
837
|
+
:param sbom: list - Raw artifacts for the SBOM
|
|
838
|
+
:return:
|
|
839
|
+
"""
|
|
840
|
+
packages = {}
|
|
841
|
+
top_level_count = {}
|
|
842
|
+
for item in sbom:
|
|
843
|
+
package = Package(**item)
|
|
844
|
+
if package.id in packages:
|
|
845
|
+
print("Duplicate package?")
|
|
846
|
+
else:
|
|
847
|
+
package = Core.get_license_details(package)
|
|
848
|
+
packages[package.id] = package
|
|
849
|
+
for top_id in package.topLevelAncestors:
|
|
850
|
+
if top_id not in top_level_count:
|
|
851
|
+
top_level_count[top_id] = 1
|
|
852
|
+
else:
|
|
853
|
+
top_level_count[top_id] += 1
|
|
854
|
+
if len(top_level_count) > 0:
|
|
855
|
+
for package_id in top_level_count:
|
|
856
|
+
packages[package_id].transitives = top_level_count[package_id]
|
|
857
|
+
return packages
|
|
858
|
+
|
|
858
859
|
@staticmethod
|
|
859
860
|
def save_file(file_name: str, content: str) -> None:
|
|
860
861
|
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.37
|
|
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,11 @@ 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
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist: packaging>=24.1
|
|
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"
|
|
21
|
+
Requires-Dist: GitPython
|
|
22
|
+
Requires-Dist: packaging
|
|
28
23
|
|
|
29
24
|
# Socket Security CLI
|
|
30
25
|
|
|
@@ -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.37}/socketsecurity.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|