socketsecurity 1.0.31__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.31/socketsecurity.egg-info → socketsecurity-1.0.34}/PKG-INFO +9 -5
- socketsecurity-1.0.34/pyproject.toml +130 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/__init__.py +1 -1
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/core/__init__.py +131 -116
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/core/github.py +25 -28
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/socketcli.py +18 -7
- {socketsecurity-1.0.31 → 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.31/pyproject.toml +0 -48
- socketsecurity-1.0.31/socketsecurity.egg-info/requires.txt +0 -6
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/LICENSE +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/README.md +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/setup.cfg +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/core/gitlab.py +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/core/issues.py +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/core/licenses.py +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/core/messages.py +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity.egg-info/SOURCES.txt +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity.egg-info/dependency_links.txt +0 -0
- {socketsecurity-1.0.31 → socketsecurity-1.0.34}/socketsecurity.egg-info/entry_points.txt +0 -0
- {socketsecurity-1.0.31 → 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,
|
|
@@ -50,6 +49,19 @@ log = logging.getLogger("socketdev")
|
|
|
50
49
|
log.addHandler(logging.NullHandler())
|
|
51
50
|
|
|
52
51
|
socket_globs = {
|
|
52
|
+
"spdx": {
|
|
53
|
+
"spdx.json": {
|
|
54
|
+
"pattern": "*[-.]spdx.json"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"cdx": {
|
|
58
|
+
"cyclonedx.json": {
|
|
59
|
+
"pattern": "{bom,*[-.]c{yclone,}dx}.json"
|
|
60
|
+
},
|
|
61
|
+
"xml": {
|
|
62
|
+
"pattern": "{bom,*[-.]c{yclone,}dx}.xml"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
53
65
|
"npm": {
|
|
54
66
|
"package.json": {
|
|
55
67
|
"pattern": "package.json"
|
|
@@ -213,34 +225,53 @@ def do_request(
|
|
|
213
225
|
|
|
214
226
|
|
|
215
227
|
class Core:
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
request_timeout: int
|
|
219
|
-
reports: list
|
|
228
|
+
_sdk = None
|
|
229
|
+
_initialized = False
|
|
220
230
|
|
|
221
|
-
def __init__(
|
|
222
|
-
|
|
231
|
+
def __init__(self):
|
|
232
|
+
raise NotImplementedError("Use Core.initialize() instead of instantiation")
|
|
233
|
+
|
|
234
|
+
@classmethod
|
|
235
|
+
def initialize(
|
|
236
|
+
cls,
|
|
223
237
|
token: str,
|
|
224
238
|
base_api_url: str = None,
|
|
225
239
|
request_timeout: int = None,
|
|
226
240
|
enable_all_alerts: bool = False,
|
|
227
241
|
allow_unverified: bool = False
|
|
228
|
-
):
|
|
229
|
-
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
|
+
|
|
230
249
|
allow_unverified_ssl = allow_unverified
|
|
231
|
-
|
|
232
|
-
encode_key(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
+
|
|
240
259
|
if enable_all_alerts:
|
|
241
|
-
global all_new_alerts
|
|
242
260
|
all_new_alerts = True
|
|
243
|
-
|
|
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
|
|
244
275
|
|
|
245
276
|
@staticmethod
|
|
246
277
|
def enable_debug_log(level: int):
|
|
@@ -284,39 +315,50 @@ class Core:
|
|
|
284
315
|
def get_org_id_slug() -> (str, str):
|
|
285
316
|
"""
|
|
286
317
|
Gets the Org ID and Org Slug for the API Token
|
|
287
|
-
:return:
|
|
318
|
+
:return: Tuple of Org ID and Org Slug
|
|
288
319
|
"""
|
|
289
|
-
path = "organizations"
|
|
290
|
-
response = do_request(path)
|
|
291
|
-
data = response.json()
|
|
292
|
-
organizations = data.get("organizations")
|
|
293
320
|
new_org_id = None
|
|
294
321
|
new_org_slug = None
|
|
322
|
+
|
|
323
|
+
sdk = Core.get_sdk()
|
|
324
|
+
data = sdk.org.get()
|
|
325
|
+
organizations = data.get("organizations")
|
|
326
|
+
|
|
295
327
|
if len(organizations) == 1:
|
|
296
328
|
for key in organizations:
|
|
297
329
|
new_org_id = key
|
|
298
330
|
new_org_slug = organizations[key].get('slug')
|
|
331
|
+
|
|
299
332
|
return new_org_id, new_org_slug
|
|
300
333
|
|
|
301
334
|
@staticmethod
|
|
302
|
-
def get_sbom_data(full_scan_id: str) ->
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
except Exception as error:
|
|
310
|
-
log.debug("Failed with old style full-scan API using new format")
|
|
311
|
-
log.debug(error)
|
|
312
|
-
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")
|
|
313
342
|
data.strip('"')
|
|
314
343
|
data.strip()
|
|
344
|
+
|
|
315
345
|
for line in data.split("\n"):
|
|
316
346
|
if line != '"' and line != "" and line is not None:
|
|
317
347
|
item = json.loads(line)
|
|
318
348
|
results.append(item)
|
|
319
|
-
|
|
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
|
+
|
|
320
362
|
|
|
321
363
|
@staticmethod
|
|
322
364
|
def get_security_policy() -> dict:
|
|
@@ -324,23 +366,21 @@ class Core:
|
|
|
324
366
|
Get the Security policy and determine the effective Org security policy
|
|
325
367
|
:return:
|
|
326
368
|
"""
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
]
|
|
333
|
-
response = do_request(path, payload=json.dumps(payload), method="POST")
|
|
334
|
-
data = response.json()
|
|
369
|
+
|
|
370
|
+
sdk = Core.get_sdk()
|
|
371
|
+
data = sdk.settings.get(org_id)
|
|
372
|
+
|
|
335
373
|
defaults = data.get("defaults")
|
|
336
374
|
default_rules = defaults.get("issueRules")
|
|
337
375
|
entries = data.get("entries")
|
|
338
376
|
org_rules = {}
|
|
377
|
+
|
|
339
378
|
for org_set in entries:
|
|
340
379
|
settings = org_set.get("settings")
|
|
341
380
|
if settings is not None:
|
|
342
381
|
org_details = settings.get("organization")
|
|
343
382
|
org_rules = org_details.get("issueRules")
|
|
383
|
+
|
|
344
384
|
for default in default_rules:
|
|
345
385
|
if default not in org_rules:
|
|
346
386
|
action = default_rules[default]["action"]
|
|
@@ -349,10 +389,6 @@ class Core:
|
|
|
349
389
|
}
|
|
350
390
|
return org_rules
|
|
351
391
|
|
|
352
|
-
# @staticmethod
|
|
353
|
-
# def get_supported_file_types() -> dict:
|
|
354
|
-
# path = "report/supported"
|
|
355
|
-
|
|
356
392
|
@staticmethod
|
|
357
393
|
def get_manifest_files(package: Package, packages: dict) -> str:
|
|
358
394
|
if package.direct:
|
|
@@ -375,15 +411,16 @@ class Core:
|
|
|
375
411
|
|
|
376
412
|
@staticmethod
|
|
377
413
|
def create_sbom_output(diff: Diff) -> dict:
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
sbom = result.json()
|
|
383
|
-
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"):
|
|
384
418
|
log.error(f"Unable to get CycloneDX Output for {diff.id}")
|
|
385
|
-
log.error(
|
|
386
|
-
|
|
419
|
+
log.error(sbom.get("message"))
|
|
420
|
+
return {}
|
|
421
|
+
|
|
422
|
+
sbom.pop("success", None)
|
|
423
|
+
|
|
387
424
|
return sbom
|
|
388
425
|
|
|
389
426
|
@staticmethod
|
|
@@ -394,7 +431,7 @@ class Core:
|
|
|
394
431
|
patterns = socket_globs[ecosystem]
|
|
395
432
|
for file_name in patterns:
|
|
396
433
|
pattern = patterns[file_name]["pattern"]
|
|
397
|
-
|
|
434
|
+
|
|
398
435
|
for file in files:
|
|
399
436
|
if "\\" in file:
|
|
400
437
|
file = file.replace("\\", "/")
|
|
@@ -412,11 +449,12 @@ class Core:
|
|
|
412
449
|
:param path: Str - path to where the manifest files are located
|
|
413
450
|
:return:
|
|
414
451
|
"""
|
|
415
|
-
|
|
452
|
+
|
|
416
453
|
start_time = time.time()
|
|
417
454
|
files = set()
|
|
418
455
|
for ecosystem in socket_globs:
|
|
419
456
|
patterns = socket_globs[ecosystem]
|
|
457
|
+
|
|
420
458
|
for file_name in patterns:
|
|
421
459
|
pattern = patterns[file_name]["pattern"]
|
|
422
460
|
file_path = f"{path}/**/{pattern}"
|
|
@@ -424,14 +462,15 @@ class Core:
|
|
|
424
462
|
log.debug(f"Globbing {file_path}")
|
|
425
463
|
glob_start = time.time()
|
|
426
464
|
glob_files = glob(file_path, recursive=True)
|
|
465
|
+
|
|
427
466
|
for glob_file in glob_files:
|
|
428
467
|
if glob_file not in files:
|
|
429
468
|
files.add(glob_file)
|
|
469
|
+
|
|
430
470
|
glob_end = time.time()
|
|
431
471
|
glob_total_time = glob_end - glob_start
|
|
432
472
|
log.debug(f"Glob for pattern {file_path} took {glob_total_time:.2f} seconds")
|
|
433
473
|
|
|
434
|
-
log.debug("Finished Find Files")
|
|
435
474
|
end_time = time.time()
|
|
436
475
|
total_time = end_time - start_time
|
|
437
476
|
log.info(f"Found {len(files)} in {total_time:.2f} seconds")
|
|
@@ -448,7 +487,7 @@ class Core:
|
|
|
448
487
|
"""
|
|
449
488
|
send_files = []
|
|
450
489
|
create_full_start = time.time()
|
|
451
|
-
|
|
490
|
+
|
|
452
491
|
for file in files:
|
|
453
492
|
if platform.system() == "Windows":
|
|
454
493
|
file = file.replace("\\", "/")
|
|
@@ -472,10 +511,12 @@ class Core:
|
|
|
472
511
|
)
|
|
473
512
|
)
|
|
474
513
|
send_files.append(payload)
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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
|
+
|
|
479
520
|
full_scan = FullScan(**results)
|
|
480
521
|
full_scan.sbom_artifacts = Core.get_sbom_data(full_scan.id)
|
|
481
522
|
create_full_end = time.time()
|
|
@@ -500,25 +541,12 @@ class Core:
|
|
|
500
541
|
:param repo_slug: Str - Repo slug for the repository that is being diffed
|
|
501
542
|
:return:
|
|
502
543
|
"""
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
results =
|
|
544
|
+
|
|
545
|
+
sdk = Core.get_sdk()
|
|
546
|
+
results = sdk.repos.repo(org_slug, repo_slug)
|
|
506
547
|
repository = Repository(**results)
|
|
507
|
-
return repository.head_full_scan_id
|
|
508
548
|
|
|
509
|
-
|
|
510
|
-
def get_full_scan(full_scan_id: str) -> FullScan:
|
|
511
|
-
"""
|
|
512
|
-
Get the specified full scan and return a FullScan object
|
|
513
|
-
:param full_scan_id: str - ID of the full scan to pull
|
|
514
|
-
:return:
|
|
515
|
-
"""
|
|
516
|
-
full_scan_url = f"{full_scan_path}/{full_scan_id}"
|
|
517
|
-
response = do_request(full_scan_url)
|
|
518
|
-
results = response.json()
|
|
519
|
-
full_scan = FullScan(**results)
|
|
520
|
-
full_scan.sbom_artifacts = Core.get_sbom_data(full_scan.id)
|
|
521
|
-
return full_scan
|
|
549
|
+
return repository.head_full_scan_id
|
|
522
550
|
|
|
523
551
|
@staticmethod
|
|
524
552
|
def create_new_diff(
|
|
@@ -542,11 +570,13 @@ class Core:
|
|
|
542
570
|
diff = Diff()
|
|
543
571
|
diff.id = "no_diff_id"
|
|
544
572
|
return diff
|
|
573
|
+
|
|
545
574
|
files = Core.find_files(path)
|
|
546
575
|
if files is None or len(files) == 0:
|
|
547
576
|
diff = Diff()
|
|
548
577
|
diff.id = "no_diff_id"
|
|
549
578
|
return diff
|
|
579
|
+
|
|
550
580
|
try:
|
|
551
581
|
head_full_scan_id = Core.get_head_scan_for_repo(params.repo)
|
|
552
582
|
if head_full_scan_id is None or head_full_scan_id == "":
|
|
@@ -557,29 +587,35 @@ class Core:
|
|
|
557
587
|
head_end = time.time()
|
|
558
588
|
total_head_time = head_end - head_start
|
|
559
589
|
log.info(f"Total time to get head full-scan {total_head_time: .2f}")
|
|
590
|
+
|
|
560
591
|
except APIResourceNotFound:
|
|
561
592
|
head_full_scan_id = None
|
|
562
593
|
head_full_scan = []
|
|
594
|
+
|
|
563
595
|
new_scan_start = time.time()
|
|
564
596
|
new_full_scan = Core.create_full_scan(files, params, workspace)
|
|
565
|
-
new_full_scan.packages =
|
|
597
|
+
new_full_scan.packages = new_full_scan.sbom_artifacts
|
|
566
598
|
new_scan_end = time.time()
|
|
599
|
+
|
|
567
600
|
total_new_time = new_scan_end - new_scan_start
|
|
568
601
|
log.info(f"Total time to get new full-scan {total_new_time: .2f}")
|
|
602
|
+
|
|
569
603
|
diff_report = Core.compare_sboms(new_full_scan.sbom_artifacts, head_full_scan)
|
|
570
604
|
diff_report.packages = new_full_scan.packages
|
|
571
|
-
|
|
605
|
+
|
|
572
606
|
base_socket = "https://socket.dev/dashboard/org"
|
|
573
607
|
diff_report.id = new_full_scan.id
|
|
574
608
|
diff_report.report_url = f"{base_socket}/{org_slug}/sbom/{diff_report.id}"
|
|
609
|
+
|
|
575
610
|
if head_full_scan_id is not None:
|
|
576
611
|
diff_report.diff_url = f"{base_socket}/{org_slug}/diff/{diff_report.id}/{head_full_scan_id}"
|
|
577
612
|
else:
|
|
578
613
|
diff_report.diff_url = diff_report.report_url
|
|
614
|
+
|
|
579
615
|
return diff_report
|
|
580
616
|
|
|
581
617
|
@staticmethod
|
|
582
|
-
def compare_sboms(new_scan:
|
|
618
|
+
def compare_sboms(new_scan: dict, head_scan: dict) -> Diff:
|
|
583
619
|
"""
|
|
584
620
|
compare the SBOMs of the new full Scan and the head full scan. Return a Diff report with new packages,
|
|
585
621
|
removed packages, and new alerts for the new full scan compared to the head.
|
|
@@ -587,28 +623,33 @@ class Core:
|
|
|
587
623
|
:param head_scan: FullScan - Current head FullScan for the repository
|
|
588
624
|
:return:
|
|
589
625
|
"""
|
|
590
|
-
diff: Diff
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
626
|
+
diff: Diff = Diff()
|
|
627
|
+
new_packages = new_scan
|
|
628
|
+
head_packages = head_scan
|
|
629
|
+
|
|
594
630
|
new_scan_alerts = {}
|
|
595
631
|
head_scan_alerts = {}
|
|
596
632
|
consolidated = set()
|
|
633
|
+
|
|
597
634
|
for package_id in new_packages:
|
|
598
635
|
purl, package = Core.create_purl(package_id, new_packages)
|
|
599
636
|
base_purl = f"{purl.ecosystem}/{purl.name}@{purl.version}"
|
|
637
|
+
|
|
600
638
|
if package_id not in head_packages and package.direct and base_purl not in consolidated:
|
|
601
639
|
diff.new_packages.append(purl)
|
|
602
640
|
consolidated.add(base_purl)
|
|
603
641
|
new_scan_alerts = Core.create_issue_alerts(package, new_scan_alerts, new_packages)
|
|
642
|
+
|
|
604
643
|
for package_id in head_packages:
|
|
605
644
|
purl, package = Core.create_purl(package_id, head_packages)
|
|
606
645
|
if package_id not in new_packages and package.direct:
|
|
607
646
|
diff.removed_packages.append(purl)
|
|
608
647
|
head_scan_alerts = Core.create_issue_alerts(package, head_scan_alerts, head_packages)
|
|
648
|
+
|
|
609
649
|
diff.new_alerts = Core.compare_issue_alerts(new_scan_alerts, head_scan_alerts, diff.new_alerts)
|
|
610
650
|
diff.new_capabilities = Core.compare_capabilities(new_packages, head_packages)
|
|
611
651
|
diff = Core.add_capabilities_to_purl(diff)
|
|
652
|
+
|
|
612
653
|
return diff
|
|
613
654
|
|
|
614
655
|
@staticmethod
|
|
@@ -814,32 +855,6 @@ class Core:
|
|
|
814
855
|
)
|
|
815
856
|
return purl, package
|
|
816
857
|
|
|
817
|
-
@staticmethod
|
|
818
|
-
def create_sbom_dict(sbom: list) -> dict:
|
|
819
|
-
"""
|
|
820
|
-
Converts the SBOM Artifacts from the FulLScan into a Dictionary for parsing
|
|
821
|
-
:param sbom: list - Raw artifacts for the SBOM
|
|
822
|
-
:return:
|
|
823
|
-
"""
|
|
824
|
-
packages = {}
|
|
825
|
-
top_level_count = {}
|
|
826
|
-
for item in sbom:
|
|
827
|
-
package = Package(**item)
|
|
828
|
-
if package.id in packages:
|
|
829
|
-
print("Duplicate package?")
|
|
830
|
-
else:
|
|
831
|
-
package = Core.get_license_details(package)
|
|
832
|
-
packages[package.id] = package
|
|
833
|
-
for top_id in package.topLevelAncestors:
|
|
834
|
-
if top_id not in top_level_count:
|
|
835
|
-
top_level_count[top_id] = 1
|
|
836
|
-
else:
|
|
837
|
-
top_level_count[top_id] += 1
|
|
838
|
-
if len(top_level_count) > 0:
|
|
839
|
-
for package_id in top_level_count:
|
|
840
|
-
packages[package_id].transitives = top_level_count[package_id]
|
|
841
|
-
return packages
|
|
842
|
-
|
|
843
858
|
@staticmethod
|
|
844
859
|
def save_file(file_name: str, content: str) -> None:
|
|
845
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.31 → socketsecurity-1.0.34}/socketsecurity.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|