socketsecurity 1.0.32__tar.gz → 1.0.34__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.32/socketsecurity.egg-info → socketsecurity-1.0.34}/PKG-INFO +9 -5
- socketsecurity-1.0.34/pyproject.toml +130 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/__init__.py +1 -1
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/core/__init__.py +118 -116
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/core/github.py +25 -28
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/socketcli.py +18 -7
- {socketsecurity-1.0.32 → socketsecurity-1.0.34/socketsecurity.egg-info}/PKG-INFO +9 -5
- socketsecurity-1.0.34/socketsecurity.egg-info/requires.txt +11 -0
- socketsecurity-1.0.32/pyproject.toml +0 -48
- socketsecurity-1.0.32/socketsecurity.egg-info/requires.txt +0 -6
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/LICENSE +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/README.md +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/setup.cfg +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/core/gitlab.py +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/core/issues.py +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/core/licenses.py +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/core/messages.py +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity.egg-info/SOURCES.txt +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity.egg-info/dependency_links.txt +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/socketsecurity.egg-info/entry_points.txt +0 -0
- {socketsecurity-1.0.32 → socketsecurity-1.0.34}/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.34
|
|
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,12 +15,16 @@ 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>=2.32.0
|
|
19
|
+
Requires-Dist: mdutils~=1.6.0
|
|
20
20
|
Requires-Dist: prettytable
|
|
21
21
|
Requires-Dist: argparse
|
|
22
|
-
Requires-Dist: GitPython
|
|
23
|
-
Requires-Dist: packaging
|
|
22
|
+
Requires-Dist: GitPython>=3.1.43
|
|
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"
|
|
24
28
|
|
|
25
29
|
# Socket Security CLI
|
|
26
30
|
|
|
@@ -0,0 +1,130 @@
|
|
|
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"
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__author__ = 'socket.dev'
|
|
2
|
-
__version__ = '1.0.
|
|
2
|
+
__version__ = '1.0.34'
|
|
@@ -2,9 +2,9 @@ import logging
|
|
|
2
2
|
from pathlib import PurePath
|
|
3
3
|
|
|
4
4
|
import requests
|
|
5
|
-
from urllib.parse import urlencode
|
|
6
5
|
import base64
|
|
7
6
|
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,7 +12,6 @@ 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,
|
|
16
15
|
Issue,
|
|
17
16
|
Package,
|
|
18
17
|
Alert,
|
|
@@ -226,34 +225,53 @@ def do_request(
|
|
|
226
225
|
|
|
227
226
|
|
|
228
227
|
class Core:
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
request_timeout: int
|
|
232
|
-
reports: list
|
|
228
|
+
_sdk = None
|
|
229
|
+
_initialized = False
|
|
233
230
|
|
|
234
|
-
def __init__(
|
|
235
|
-
|
|
231
|
+
def __init__(self):
|
|
232
|
+
raise NotImplementedError("Use Core.initialize() instead of instantiation")
|
|
233
|
+
|
|
234
|
+
@classmethod
|
|
235
|
+
def initialize(
|
|
236
|
+
cls,
|
|
236
237
|
token: str,
|
|
237
238
|
base_api_url: str = None,
|
|
238
239
|
request_timeout: int = None,
|
|
239
240
|
enable_all_alerts: bool = False,
|
|
240
241
|
allow_unverified: bool = False
|
|
241
|
-
):
|
|
242
|
-
global
|
|
242
|
+
) -> None:
|
|
243
|
+
"""Initialize the Core class and set up global configuration"""
|
|
244
|
+
if cls._initialized:
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
global allow_unverified_ssl, all_new_alerts
|
|
248
|
+
|
|
243
249
|
allow_unverified_ssl = allow_unverified
|
|
244
|
-
|
|
245
|
-
encode_key(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
250
|
+
cls._initialize_sdk(token)
|
|
251
|
+
encode_key(token + ":")
|
|
252
|
+
|
|
253
|
+
if base_api_url is not None:
|
|
254
|
+
cls.set_api_url(base_api_url)
|
|
255
|
+
|
|
256
|
+
if request_timeout is not None:
|
|
257
|
+
cls.set_timeout(request_timeout)
|
|
258
|
+
|
|
253
259
|
if enable_all_alerts:
|
|
254
|
-
global all_new_alerts
|
|
255
260
|
all_new_alerts = True
|
|
256
|
-
|
|
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
|
|
257
275
|
|
|
258
276
|
@staticmethod
|
|
259
277
|
def enable_debug_log(level: int):
|
|
@@ -297,39 +315,50 @@ class Core:
|
|
|
297
315
|
def get_org_id_slug() -> (str, str):
|
|
298
316
|
"""
|
|
299
317
|
Gets the Org ID and Org Slug for the API Token
|
|
300
|
-
:return:
|
|
318
|
+
:return: Tuple of Org ID and Org Slug
|
|
301
319
|
"""
|
|
302
|
-
path = "organizations"
|
|
303
|
-
response = do_request(path)
|
|
304
|
-
data = response.json()
|
|
305
|
-
organizations = data.get("organizations")
|
|
306
320
|
new_org_id = None
|
|
307
321
|
new_org_slug = None
|
|
322
|
+
|
|
323
|
+
sdk = Core.get_sdk()
|
|
324
|
+
data = sdk.org.get()
|
|
325
|
+
organizations = data.get("organizations")
|
|
326
|
+
|
|
308
327
|
if len(organizations) == 1:
|
|
309
328
|
for key in organizations:
|
|
310
329
|
new_org_id = key
|
|
311
330
|
new_org_slug = organizations[key].get('slug')
|
|
331
|
+
|
|
312
332
|
return new_org_id, new_org_slug
|
|
313
333
|
|
|
314
334
|
@staticmethod
|
|
315
|
-
def get_sbom_data(full_scan_id: str) ->
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
|
335
|
+
def get_sbom_data(full_scan_id: str) -> dict:
|
|
336
|
+
sdk = Core.get_sdk()
|
|
337
|
+
response_dict = sdk.fullscans.stream(org_slug, full_scan_id)
|
|
338
|
+
|
|
339
|
+
if not response_dict.get("success"):
|
|
340
|
+
results = []
|
|
341
|
+
data = response_dict.get("message")
|
|
326
342
|
data.strip('"')
|
|
327
343
|
data.strip()
|
|
344
|
+
|
|
328
345
|
for line in data.split("\n"):
|
|
329
346
|
if line != '"' and line != "" and line is not None:
|
|
330
347
|
item = json.loads(line)
|
|
331
348
|
results.append(item)
|
|
332
|
-
|
|
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
|
+
|
|
333
362
|
|
|
334
363
|
@staticmethod
|
|
335
364
|
def get_security_policy() -> dict:
|
|
@@ -337,23 +366,21 @@ class Core:
|
|
|
337
366
|
Get the Security policy and determine the effective Org security policy
|
|
338
367
|
:return:
|
|
339
368
|
"""
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
]
|
|
346
|
-
response = do_request(path, payload=json.dumps(payload), method="POST")
|
|
347
|
-
data = response.json()
|
|
369
|
+
|
|
370
|
+
sdk = Core.get_sdk()
|
|
371
|
+
data = sdk.settings.get(org_id)
|
|
372
|
+
|
|
348
373
|
defaults = data.get("defaults")
|
|
349
374
|
default_rules = defaults.get("issueRules")
|
|
350
375
|
entries = data.get("entries")
|
|
351
376
|
org_rules = {}
|
|
377
|
+
|
|
352
378
|
for org_set in entries:
|
|
353
379
|
settings = org_set.get("settings")
|
|
354
380
|
if settings is not None:
|
|
355
381
|
org_details = settings.get("organization")
|
|
356
382
|
org_rules = org_details.get("issueRules")
|
|
383
|
+
|
|
357
384
|
for default in default_rules:
|
|
358
385
|
if default not in org_rules:
|
|
359
386
|
action = default_rules[default]["action"]
|
|
@@ -362,10 +389,6 @@ class Core:
|
|
|
362
389
|
}
|
|
363
390
|
return org_rules
|
|
364
391
|
|
|
365
|
-
# @staticmethod
|
|
366
|
-
# def get_supported_file_types() -> dict:
|
|
367
|
-
# path = "report/supported"
|
|
368
|
-
|
|
369
392
|
@staticmethod
|
|
370
393
|
def get_manifest_files(package: Package, packages: dict) -> str:
|
|
371
394
|
if package.direct:
|
|
@@ -388,15 +411,16 @@ class Core:
|
|
|
388
411
|
|
|
389
412
|
@staticmethod
|
|
390
413
|
def create_sbom_output(diff: Diff) -> dict:
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
sbom = result.json()
|
|
396
|
-
except Exception as error:
|
|
414
|
+
sdk = Core.get_sdk()
|
|
415
|
+
sbom = sdk.export.cdx_bom(org_slug, diff.id)
|
|
416
|
+
|
|
417
|
+
if not sbom.get("success"):
|
|
397
418
|
log.error(f"Unable to get CycloneDX Output for {diff.id}")
|
|
398
|
-
log.error(
|
|
399
|
-
|
|
419
|
+
log.error(sbom.get("message"))
|
|
420
|
+
return {}
|
|
421
|
+
|
|
422
|
+
sbom.pop("success", None)
|
|
423
|
+
|
|
400
424
|
return sbom
|
|
401
425
|
|
|
402
426
|
@staticmethod
|
|
@@ -407,7 +431,7 @@ class Core:
|
|
|
407
431
|
patterns = socket_globs[ecosystem]
|
|
408
432
|
for file_name in patterns:
|
|
409
433
|
pattern = patterns[file_name]["pattern"]
|
|
410
|
-
|
|
434
|
+
|
|
411
435
|
for file in files:
|
|
412
436
|
if "\\" in file:
|
|
413
437
|
file = file.replace("\\", "/")
|
|
@@ -425,11 +449,12 @@ class Core:
|
|
|
425
449
|
:param path: Str - path to where the manifest files are located
|
|
426
450
|
:return:
|
|
427
451
|
"""
|
|
428
|
-
|
|
452
|
+
|
|
429
453
|
start_time = time.time()
|
|
430
454
|
files = set()
|
|
431
455
|
for ecosystem in socket_globs:
|
|
432
456
|
patterns = socket_globs[ecosystem]
|
|
457
|
+
|
|
433
458
|
for file_name in patterns:
|
|
434
459
|
pattern = patterns[file_name]["pattern"]
|
|
435
460
|
file_path = f"{path}/**/{pattern}"
|
|
@@ -437,14 +462,15 @@ class Core:
|
|
|
437
462
|
log.debug(f"Globbing {file_path}")
|
|
438
463
|
glob_start = time.time()
|
|
439
464
|
glob_files = glob(file_path, recursive=True)
|
|
465
|
+
|
|
440
466
|
for glob_file in glob_files:
|
|
441
467
|
if glob_file not in files:
|
|
442
468
|
files.add(glob_file)
|
|
469
|
+
|
|
443
470
|
glob_end = time.time()
|
|
444
471
|
glob_total_time = glob_end - glob_start
|
|
445
472
|
log.debug(f"Glob for pattern {file_path} took {glob_total_time:.2f} seconds")
|
|
446
473
|
|
|
447
|
-
log.debug("Finished Find Files")
|
|
448
474
|
end_time = time.time()
|
|
449
475
|
total_time = end_time - start_time
|
|
450
476
|
log.info(f"Found {len(files)} in {total_time:.2f} seconds")
|
|
@@ -461,7 +487,7 @@ class Core:
|
|
|
461
487
|
"""
|
|
462
488
|
send_files = []
|
|
463
489
|
create_full_start = time.time()
|
|
464
|
-
|
|
490
|
+
|
|
465
491
|
for file in files:
|
|
466
492
|
if platform.system() == "Windows":
|
|
467
493
|
file = file.replace("\\", "/")
|
|
@@ -485,10 +511,12 @@ class Core:
|
|
|
485
511
|
)
|
|
486
512
|
)
|
|
487
513
|
send_files.append(payload)
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
514
|
+
|
|
515
|
+
sdk = Core.get_sdk()
|
|
516
|
+
params_dict = params.__dict__
|
|
517
|
+
params_dict["org_slug"] = org_slug
|
|
518
|
+
results = sdk.fullscans.post(files=files, params=params_dict)
|
|
519
|
+
|
|
492
520
|
full_scan = FullScan(**results)
|
|
493
521
|
full_scan.sbom_artifacts = Core.get_sbom_data(full_scan.id)
|
|
494
522
|
create_full_end = time.time()
|
|
@@ -513,25 +541,12 @@ class Core:
|
|
|
513
541
|
:param repo_slug: Str - Repo slug for the repository that is being diffed
|
|
514
542
|
:return:
|
|
515
543
|
"""
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
results =
|
|
544
|
+
|
|
545
|
+
sdk = Core.get_sdk()
|
|
546
|
+
results = sdk.repos.repo(org_slug, repo_slug)
|
|
519
547
|
repository = Repository(**results)
|
|
520
|
-
return repository.head_full_scan_id
|
|
521
548
|
|
|
522
|
-
|
|
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
|
|
549
|
+
return repository.head_full_scan_id
|
|
535
550
|
|
|
536
551
|
@staticmethod
|
|
537
552
|
def create_new_diff(
|
|
@@ -555,11 +570,13 @@ class Core:
|
|
|
555
570
|
diff = Diff()
|
|
556
571
|
diff.id = "no_diff_id"
|
|
557
572
|
return diff
|
|
573
|
+
|
|
558
574
|
files = Core.find_files(path)
|
|
559
575
|
if files is None or len(files) == 0:
|
|
560
576
|
diff = Diff()
|
|
561
577
|
diff.id = "no_diff_id"
|
|
562
578
|
return diff
|
|
579
|
+
|
|
563
580
|
try:
|
|
564
581
|
head_full_scan_id = Core.get_head_scan_for_repo(params.repo)
|
|
565
582
|
if head_full_scan_id is None or head_full_scan_id == "":
|
|
@@ -570,29 +587,35 @@ class Core:
|
|
|
570
587
|
head_end = time.time()
|
|
571
588
|
total_head_time = head_end - head_start
|
|
572
589
|
log.info(f"Total time to get head full-scan {total_head_time: .2f}")
|
|
590
|
+
|
|
573
591
|
except APIResourceNotFound:
|
|
574
592
|
head_full_scan_id = None
|
|
575
593
|
head_full_scan = []
|
|
594
|
+
|
|
576
595
|
new_scan_start = time.time()
|
|
577
596
|
new_full_scan = Core.create_full_scan(files, params, workspace)
|
|
578
|
-
new_full_scan.packages =
|
|
597
|
+
new_full_scan.packages = new_full_scan.sbom_artifacts
|
|
579
598
|
new_scan_end = time.time()
|
|
599
|
+
|
|
580
600
|
total_new_time = new_scan_end - new_scan_start
|
|
581
601
|
log.info(f"Total time to get new full-scan {total_new_time: .2f}")
|
|
602
|
+
|
|
582
603
|
diff_report = Core.compare_sboms(new_full_scan.sbom_artifacts, head_full_scan)
|
|
583
604
|
diff_report.packages = new_full_scan.packages
|
|
584
|
-
|
|
605
|
+
|
|
585
606
|
base_socket = "https://socket.dev/dashboard/org"
|
|
586
607
|
diff_report.id = new_full_scan.id
|
|
587
608
|
diff_report.report_url = f"{base_socket}/{org_slug}/sbom/{diff_report.id}"
|
|
609
|
+
|
|
588
610
|
if head_full_scan_id is not None:
|
|
589
611
|
diff_report.diff_url = f"{base_socket}/{org_slug}/diff/{diff_report.id}/{head_full_scan_id}"
|
|
590
612
|
else:
|
|
591
613
|
diff_report.diff_url = diff_report.report_url
|
|
614
|
+
|
|
592
615
|
return diff_report
|
|
593
616
|
|
|
594
617
|
@staticmethod
|
|
595
|
-
def compare_sboms(new_scan:
|
|
618
|
+
def compare_sboms(new_scan: dict, head_scan: dict) -> Diff:
|
|
596
619
|
"""
|
|
597
620
|
compare the SBOMs of the new full Scan and the head full scan. Return a Diff report with new packages,
|
|
598
621
|
removed packages, and new alerts for the new full scan compared to the head.
|
|
@@ -600,28 +623,33 @@ class Core:
|
|
|
600
623
|
:param head_scan: FullScan - Current head FullScan for the repository
|
|
601
624
|
:return:
|
|
602
625
|
"""
|
|
603
|
-
diff: Diff
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
626
|
+
diff: Diff = Diff()
|
|
627
|
+
new_packages = new_scan
|
|
628
|
+
head_packages = head_scan
|
|
629
|
+
|
|
607
630
|
new_scan_alerts = {}
|
|
608
631
|
head_scan_alerts = {}
|
|
609
632
|
consolidated = set()
|
|
633
|
+
|
|
610
634
|
for package_id in new_packages:
|
|
611
635
|
purl, package = Core.create_purl(package_id, new_packages)
|
|
612
636
|
base_purl = f"{purl.ecosystem}/{purl.name}@{purl.version}"
|
|
637
|
+
|
|
613
638
|
if package_id not in head_packages and package.direct and base_purl not in consolidated:
|
|
614
639
|
diff.new_packages.append(purl)
|
|
615
640
|
consolidated.add(base_purl)
|
|
616
641
|
new_scan_alerts = Core.create_issue_alerts(package, new_scan_alerts, new_packages)
|
|
642
|
+
|
|
617
643
|
for package_id in head_packages:
|
|
618
644
|
purl, package = Core.create_purl(package_id, head_packages)
|
|
619
645
|
if package_id not in new_packages and package.direct:
|
|
620
646
|
diff.removed_packages.append(purl)
|
|
621
647
|
head_scan_alerts = Core.create_issue_alerts(package, head_scan_alerts, head_packages)
|
|
648
|
+
|
|
622
649
|
diff.new_alerts = Core.compare_issue_alerts(new_scan_alerts, head_scan_alerts, diff.new_alerts)
|
|
623
650
|
diff.new_capabilities = Core.compare_capabilities(new_packages, head_packages)
|
|
624
651
|
diff = Core.add_capabilities_to_purl(diff)
|
|
652
|
+
|
|
625
653
|
return diff
|
|
626
654
|
|
|
627
655
|
@staticmethod
|
|
@@ -827,32 +855,6 @@ class Core:
|
|
|
827
855
|
)
|
|
828
856
|
return purl, package
|
|
829
857
|
|
|
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
|
-
|
|
856
858
|
@staticmethod
|
|
857
859
|
def save_file(file_name: str, content: str) -> None:
|
|
858
860
|
file = open(file_name, "w")
|
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
from socketsecurity.core import log, do_request
|
|
4
|
-
import requests
|
|
5
4
|
from socketsecurity.core.classes import Comment
|
|
6
5
|
from socketsecurity.core.scm_comments import Comments
|
|
7
6
|
import sys
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
9
|
+
github_sha = None
|
|
10
|
+
github_api_url = None
|
|
11
|
+
github_ref_type = None
|
|
12
|
+
github_event_name = None
|
|
13
|
+
github_workspace = None
|
|
14
|
+
github_repository = None
|
|
15
|
+
github_ref_name = None
|
|
16
|
+
github_actor = None
|
|
17
|
+
default_branch = None
|
|
18
|
+
github_env = None
|
|
19
|
+
pr_number = None
|
|
20
|
+
pr_name = None
|
|
21
|
+
is_default_branch = False
|
|
22
|
+
commit_message = None
|
|
23
|
+
committer = None
|
|
24
|
+
gh_api_token = None
|
|
25
|
+
github_repository_owner = None
|
|
26
|
+
event_action = None
|
|
28
27
|
|
|
29
28
|
github_variables = [
|
|
30
29
|
"GITHUB_SHA",
|
|
@@ -47,16 +46,14 @@ github_variables = [
|
|
|
47
46
|
|
|
48
47
|
for env in github_variables:
|
|
49
48
|
var_name = env.lower()
|
|
50
|
-
|
|
49
|
+
value = os.getenv(env)
|
|
50
|
+
globals()[var_name] = value
|
|
51
|
+
|
|
51
52
|
if var_name == "default_branch":
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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}")
|
|
53
|
+
is_default_branch = bool(value and value.lower() != "false")
|
|
54
|
+
|
|
55
|
+
if var_name != "gh_api_token":
|
|
56
|
+
log.debug(f"{env}={value}")
|
|
60
57
|
|
|
61
58
|
headers = {
|
|
62
59
|
'Authorization': f"Bearer {gh_api_token}",
|
|
@@ -167,12 +167,15 @@ 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
|
+
|
|
170
171
|
if len(diff_report.new_alerts) > 0:
|
|
171
172
|
log.info("Security issues detected by Socket Security")
|
|
172
173
|
msg = f"\n{console_security_comment}"
|
|
173
174
|
log.info(msg)
|
|
175
|
+
|
|
174
176
|
if not report_pass(diff_report) and not blocking_disabled:
|
|
175
177
|
sys.exit(1)
|
|
178
|
+
|
|
176
179
|
else:
|
|
177
180
|
# Means only warning alerts with no blocked
|
|
178
181
|
if not blocking_disabled:
|
|
@@ -188,7 +191,7 @@ def output_console_json(diff_report: Diff, sbom_file_name: str = None) -> None:
|
|
|
188
191
|
print(json.dumps(console_security_comment))
|
|
189
192
|
if not report_pass(diff_report) and not blocking_disabled:
|
|
190
193
|
sys.exit(1)
|
|
191
|
-
|
|
194
|
+
if len(diff_report.new_alerts) > 0 and not blocking_disabled:
|
|
192
195
|
# Means only warning alerts with no blocked
|
|
193
196
|
sys.exit(5)
|
|
194
197
|
|
|
@@ -252,6 +255,7 @@ def main_code():
|
|
|
252
255
|
ignore_commit_files = arguments.ignore_commit_files
|
|
253
256
|
disable_blocking = arguments.disable_blocking
|
|
254
257
|
allow_unverified = arguments.allow_unverified
|
|
258
|
+
|
|
255
259
|
if disable_blocking:
|
|
256
260
|
global blocking_disabled
|
|
257
261
|
blocking_disabled = True
|
|
@@ -308,13 +312,20 @@ def main_code():
|
|
|
308
312
|
default_branch = scm.is_default_branch
|
|
309
313
|
|
|
310
314
|
base_api_url = os.getenv("BASE_API_URL") or None
|
|
311
|
-
|
|
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
|
+
|
|
312
323
|
no_change = True
|
|
313
324
|
if ignore_commit_files:
|
|
314
325
|
no_change = False
|
|
315
326
|
elif is_repo and files is not None and len(files) > 0:
|
|
316
327
|
log.info(files)
|
|
317
|
-
no_change =
|
|
328
|
+
no_change = Core.match_supported_files(files)
|
|
318
329
|
|
|
319
330
|
set_as_pending_head = False
|
|
320
331
|
if default_branch:
|
|
@@ -344,7 +355,7 @@ def main_code():
|
|
|
344
355
|
log.info("No manifest files changes, skipping scan")
|
|
345
356
|
# log.info("No dependency changes")
|
|
346
357
|
elif scm.check_event_type() == "diff":
|
|
347
|
-
diff =
|
|
358
|
+
diff = Core.create_new_diff(target_path, params, workspace=target_path, no_change=no_change)
|
|
348
359
|
log.info("Starting comment logic for PR/MR event")
|
|
349
360
|
log.debug(f"Getting comments for Repo {scm.repository} for PR {scm.pr_number}")
|
|
350
361
|
comments = scm.get_comments_for_pr(repo, str(pr_number))
|
|
@@ -388,7 +399,7 @@ def main_code():
|
|
|
388
399
|
)
|
|
389
400
|
else:
|
|
390
401
|
log.info("Starting non-PR/MR flow")
|
|
391
|
-
diff =
|
|
402
|
+
diff = Core.create_new_diff(target_path, params, workspace=target_path, no_change=no_change)
|
|
392
403
|
if enable_json:
|
|
393
404
|
log.debug("Outputting JSON Results")
|
|
394
405
|
output_console_json(diff, sbom_file)
|
|
@@ -397,7 +408,7 @@ def main_code():
|
|
|
397
408
|
else:
|
|
398
409
|
log.info("API Mode")
|
|
399
410
|
diff: Diff
|
|
400
|
-
diff =
|
|
411
|
+
diff = Core.create_new_diff(target_path, params, workspace=target_path, no_change=no_change)
|
|
401
412
|
if enable_json:
|
|
402
413
|
output_console_json(diff, sbom_file)
|
|
403
414
|
else:
|
|
@@ -418,7 +429,7 @@ def main_code():
|
|
|
418
429
|
"license_text": package.license_text
|
|
419
430
|
}
|
|
420
431
|
all_packages[package_id] = output
|
|
421
|
-
|
|
432
|
+
Core.save_file(license_file, json.dumps(all_packages))
|
|
422
433
|
|
|
423
434
|
|
|
424
435
|
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.34
|
|
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,12 +15,16 @@ 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>=2.32.0
|
|
19
|
+
Requires-Dist: mdutils~=1.6.0
|
|
20
20
|
Requires-Dist: prettytable
|
|
21
21
|
Requires-Dist: argparse
|
|
22
|
-
Requires-Dist: GitPython
|
|
23
|
-
Requires-Dist: packaging
|
|
22
|
+
Requires-Dist: GitPython>=3.1.43
|
|
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"
|
|
24
28
|
|
|
25
29
|
# Socket Security CLI
|
|
26
30
|
|
|
@@ -1,48 +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',
|
|
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__"}
|
|
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.32 → socketsecurity-1.0.34}/socketsecurity.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|