socketsecurity 2.2.40__tar.gz → 2.2.51__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-2.2.40 → socketsecurity-2.2.51}/PKG-INFO +3 -3
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/README.md +1 -1
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/pyproject.toml +8 -2
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/__init__.py +1 -1
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/config.py +13 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/__init__.py +49 -27
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/messages.py +3 -3
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/socket_config.py +0 -8
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/tools/reachability.py +8 -3
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/socketcli.py +36 -11
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/core/conftest.py +1 -11
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/core/test_package_and_alerts.py +2 -31
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/unit/test_config.py +1 -12
- socketsecurity-2.2.51/uv.lock +1541 -0
- socketsecurity-2.2.40/uv.lock +0 -1428
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.github/CODEOWNERS +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.github/workflows/docker-stable.yml +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.github/workflows/pr-preview.yml +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.github/workflows/release.yml +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.github/workflows/version-check.yml +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.gitignore +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.hooks/sync_version.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.pre-commit-config.yaml +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/.python-version +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/Dockerfile +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/LICENSE +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/Makefile +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/docs/README.md +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/pytest.ini +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/scripts/build_container.sh +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/scripts/build_container_flexible.sh +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/scripts/deploy-test-docker.sh +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/scripts/deploy-test-pypi.sh +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/scripts/docker-entrypoint.sh +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/scripts/run.sh +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/cli_client.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/helper/__init__.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/lazy_file_loader.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/logging.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/resource_utils.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/scm/__init__.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/scm/base.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/scm/client.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/scm/github.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/scm/gitlab.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/core/utils.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/output.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/plugins/__init__.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/plugins/base.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/plugins/jira.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/plugins/manager.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/plugins/slack.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/plugins/teams.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/socketsecurity/plugins/webhook.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/__init__.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/core/create_diff_input.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/core/test_diff_generation.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/core/test_sdk_methods.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/core/test_supporting_methods.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/fullscans/create_response.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/fullscans/diff/stream_diff.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/fullscans/head_scan/metadata.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/fullscans/new_scan/metadata.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/repos/repo_info_error.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/repos/repo_info_no_head.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/repos/repo_info_success.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/data/settings/security-policy.json +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/unit/__init__.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/unit/test_cli_config.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/unit/test_client.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/unit/test_gitlab_auth.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/unit/test_gitlab_auth_fallback.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/tests/unit/test_output.py +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/workflows/bitbucket-pipelines.yml +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/workflows/github-actions.yml +0 -0
- {socketsecurity-2.2.40 → socketsecurity-2.2.51}/workflows/gitlab-ci.yml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: socketsecurity
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.51
|
|
4
4
|
Summary: Socket Security CLI for CI/CD
|
|
5
5
|
Project-URL: Homepage, https://socket.dev
|
|
6
6
|
Author-email: Douglas Coburn <douglas@socket.dev>
|
|
@@ -40,7 +40,7 @@ Requires-Dist: packaging
|
|
|
40
40
|
Requires-Dist: prettytable
|
|
41
41
|
Requires-Dist: python-dotenv
|
|
42
42
|
Requires-Dist: requests
|
|
43
|
-
Requires-Dist: socketdev<4.0.0,>=3.0.
|
|
43
|
+
Requires-Dist: socketdev<4.0.0,>=3.0.22
|
|
44
44
|
Provides-Extra: dev
|
|
45
45
|
Requires-Dist: hatch; extra == 'dev'
|
|
46
46
|
Requires-Dist: pre-commit; extra == 'dev'
|
|
@@ -57,7 +57,7 @@ Description-Content-Type: text/markdown
|
|
|
57
57
|
|
|
58
58
|
# Socket Security CLI
|
|
59
59
|
|
|
60
|
-
The Socket Security CLI was created to enable integrations with other tools like GitHub Actions, GitLab, BitBucket, local use cases and more. The tool will get the head scan for the provided repo from Socket, create a new one, and then report any new alerts detected. If there are new alerts
|
|
60
|
+
The Socket Security CLI was created to enable integrations with other tools like GitHub Actions, GitLab, BitBucket, local use cases and more. The tool will get the head scan for the provided repo from Socket, create a new one, and then report any new alerts detected. If there are new alerts with blocking actions it'll exit with a non-Zero exit code.
|
|
61
61
|
|
|
62
62
|
## Quick Start
|
|
63
63
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Socket Security CLI
|
|
2
2
|
|
|
3
|
-
The Socket Security CLI was created to enable integrations with other tools like GitHub Actions, GitLab, BitBucket, local use cases and more. The tool will get the head scan for the provided repo from Socket, create a new one, and then report any new alerts detected. If there are new alerts
|
|
3
|
+
The Socket Security CLI was created to enable integrations with other tools like GitHub Actions, GitLab, BitBucket, local use cases and more. The tool will get the head scan for the provided repo from Socket, create a new one, and then report any new alerts detected. If there are new alerts with blocking actions it'll exit with a non-Zero exit code.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "socketsecurity"
|
|
9
|
-
version = "2.2.
|
|
9
|
+
version = "2.2.51"
|
|
10
10
|
requires-python = ">= 3.10"
|
|
11
11
|
license = {"file" = "LICENSE"}
|
|
12
12
|
dependencies = [
|
|
@@ -16,7 +16,7 @@ dependencies = [
|
|
|
16
16
|
'GitPython',
|
|
17
17
|
'packaging',
|
|
18
18
|
'python-dotenv',
|
|
19
|
-
'socketdev>=3.0.
|
|
19
|
+
'socketdev>=3.0.22,<4.0.0',
|
|
20
20
|
"bs4>=0.0.2",
|
|
21
21
|
]
|
|
22
22
|
readme = "README.md"
|
|
@@ -160,3 +160,9 @@ docstring-code-line-length = "dynamic"
|
|
|
160
160
|
|
|
161
161
|
[tool.hatch.build.targets.wheel]
|
|
162
162
|
include = ["socketsecurity", "LICENSE"]
|
|
163
|
+
|
|
164
|
+
[dependency-groups]
|
|
165
|
+
dev = [
|
|
166
|
+
"pre-commit>=4.3.0",
|
|
167
|
+
]
|
|
168
|
+
|
|
@@ -77,6 +77,7 @@ class CliConfig:
|
|
|
77
77
|
reach_concurrency: Optional[int] = None
|
|
78
78
|
reach_additional_params: Optional[List[str]] = None
|
|
79
79
|
only_facts_file: bool = False
|
|
80
|
+
reach_use_only_pregenerated_sboms: bool = False
|
|
80
81
|
|
|
81
82
|
@classmethod
|
|
82
83
|
def from_args(cls, args_list: Optional[List[str]] = None) -> 'CliConfig':
|
|
@@ -139,6 +140,7 @@ class CliConfig:
|
|
|
139
140
|
'reach_concurrency': args.reach_concurrency,
|
|
140
141
|
'reach_additional_params': args.reach_additional_params,
|
|
141
142
|
'only_facts_file': args.only_facts_file,
|
|
143
|
+
'reach_use_only_pregenerated_sboms': args.reach_use_only_pregenerated_sboms,
|
|
142
144
|
'version': __version__
|
|
143
145
|
}
|
|
144
146
|
try:
|
|
@@ -175,6 +177,11 @@ class CliConfig:
|
|
|
175
177
|
logging.error("--only-facts-file requires --reach to be specified")
|
|
176
178
|
exit(1)
|
|
177
179
|
|
|
180
|
+
# Validate that reach_use_only_pregenerated_sboms requires reach
|
|
181
|
+
if args.reach_use_only_pregenerated_sboms and not args.reach:
|
|
182
|
+
logging.error("--reach-use-only-pregenerated-sboms requires --reach to be specified")
|
|
183
|
+
exit(1)
|
|
184
|
+
|
|
178
185
|
# Validate reach_concurrency is >= 1 if provided
|
|
179
186
|
if args.reach_concurrency is not None and args.reach_concurrency < 1:
|
|
180
187
|
logging.error("--reach-concurrency must be >= 1")
|
|
@@ -602,6 +609,12 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
602
609
|
action="store_true",
|
|
603
610
|
help="Submit only the .socket.facts.json file when creating full scan (requires --reach)"
|
|
604
611
|
)
|
|
612
|
+
reachability_group.add_argument(
|
|
613
|
+
"--reach-use-only-pregenerated-sboms",
|
|
614
|
+
dest="reach_use_only_pregenerated_sboms",
|
|
615
|
+
action="store_true",
|
|
616
|
+
help="When using this option, the scan is created based only on pre-generated CDX and SPDX files in your project. (requires --reach)"
|
|
617
|
+
)
|
|
605
618
|
|
|
606
619
|
parser.add_argument(
|
|
607
620
|
'--version',
|
|
@@ -19,7 +19,6 @@ from socketdev.exceptions import APIFailure
|
|
|
19
19
|
from socketdev.fullscans import FullScanParams, SocketArtifact
|
|
20
20
|
from socketdev.org import Organization
|
|
21
21
|
from socketdev.repos import RepositoryInfo
|
|
22
|
-
from socketdev.settings import SecurityPolicyRule
|
|
23
22
|
import copy
|
|
24
23
|
from socketsecurity import __version__, USER_AGENT
|
|
25
24
|
from socketsecurity.core.classes import (
|
|
@@ -82,8 +81,6 @@ class Core:
|
|
|
82
81
|
self.config.full_scan_path = f"{base_path}/full-scans"
|
|
83
82
|
self.config.repository_path = f"{base_path}/repos"
|
|
84
83
|
|
|
85
|
-
self.config.security_policy = self.get_security_policy()
|
|
86
|
-
|
|
87
84
|
def get_org_id_slug(self) -> Tuple[str, str]:
|
|
88
85
|
"""Gets the Org ID and Org Slug for the API Token."""
|
|
89
86
|
response = self.sdk.org.get(use_types=True)
|
|
@@ -112,16 +109,7 @@ class Core:
|
|
|
112
109
|
"""Converts artifacts dictionary to a list."""
|
|
113
110
|
return list(artifacts_dict.values())
|
|
114
111
|
|
|
115
|
-
def get_security_policy(self) -> Dict[str, SecurityPolicyRule]:
|
|
116
|
-
"""Gets the organization's security policy."""
|
|
117
|
-
response = self.sdk.settings.get(self.config.org_slug, use_types=True)
|
|
118
|
-
|
|
119
|
-
if not response.success:
|
|
120
|
-
log.error(f"Failed to get security policy: {response.status}")
|
|
121
|
-
log.error(response.message)
|
|
122
|
-
raise Exception(f"Failed to get security policy: {response.status}, message: {response.message}")
|
|
123
112
|
|
|
124
|
-
return response.securityPolicyRules
|
|
125
113
|
|
|
126
114
|
def create_sbom_output(self, diff: Diff) -> dict:
|
|
127
115
|
"""Creates CycloneDX output for a given diff."""
|
|
@@ -293,12 +281,13 @@ class Core:
|
|
|
293
281
|
except Exception as e:
|
|
294
282
|
log.error(f"Failed to save manifest tar.gz to {output_path}: {e}")
|
|
295
283
|
|
|
296
|
-
def find_files(self, path: str) -> List[str]:
|
|
284
|
+
def find_files(self, path: str, ecosystems: Optional[List[str]] = None) -> List[str]:
|
|
297
285
|
"""
|
|
298
286
|
Finds supported manifest files in the given path.
|
|
299
287
|
|
|
300
288
|
Args:
|
|
301
289
|
path: Path to search for manifest files.
|
|
290
|
+
ecosystems: Optional list of ecosystems to include. If None, all ecosystems are included.
|
|
302
291
|
|
|
303
292
|
Returns:
|
|
304
293
|
List of found manifest file paths.
|
|
@@ -311,6 +300,9 @@ class Core:
|
|
|
311
300
|
patterns = self.get_supported_patterns()
|
|
312
301
|
|
|
313
302
|
for ecosystem in patterns:
|
|
303
|
+
# If ecosystems filter is provided, only include specified ecosystems
|
|
304
|
+
if ecosystems is not None and ecosystem not in ecosystems:
|
|
305
|
+
continue
|
|
314
306
|
if ecosystem in self.config.excluded_ecosystems:
|
|
315
307
|
continue
|
|
316
308
|
log.debug(f'Scanning ecosystem: {ecosystem}')
|
|
@@ -355,6 +347,23 @@ class Core:
|
|
|
355
347
|
|
|
356
348
|
return file_list
|
|
357
349
|
|
|
350
|
+
def find_sbom_files(self, path: str) -> List[str]:
|
|
351
|
+
"""
|
|
352
|
+
Finds only pre-generated SBOM files (CDX and SPDX) in the given path.
|
|
353
|
+
|
|
354
|
+
This is used with --reach-use-only-pregenerated-sboms to find only
|
|
355
|
+
pre-computed CycloneDX and SPDX manifest files.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
path: Path to search for SBOM files.
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
List of found CDX and SPDX file paths.
|
|
362
|
+
"""
|
|
363
|
+
log.debug("Starting Find SBOM Files (CDX and SPDX only)")
|
|
364
|
+
sbom_ecosystems = ['cdx', 'spdx']
|
|
365
|
+
return self.find_files(path, ecosystems=sbom_ecosystems)
|
|
366
|
+
|
|
358
367
|
def get_supported_patterns(self) -> Dict:
|
|
359
368
|
"""
|
|
360
369
|
Gets supported file patterns from the Socket API.
|
|
@@ -559,7 +568,8 @@ class Core:
|
|
|
559
568
|
no_change: bool = False,
|
|
560
569
|
save_files_list_path: Optional[str] = None,
|
|
561
570
|
save_manifest_tar_path: Optional[str] = None,
|
|
562
|
-
base_paths: Optional[List[str]] = None
|
|
571
|
+
base_paths: Optional[List[str]] = None,
|
|
572
|
+
explicit_files: Optional[List[str]] = None
|
|
563
573
|
) -> Diff:
|
|
564
574
|
"""Create a new full scan and return with html_report_url.
|
|
565
575
|
|
|
@@ -570,6 +580,7 @@ class Core:
|
|
|
570
580
|
save_files_list_path: Optional path to save submitted files list for debugging
|
|
571
581
|
save_manifest_tar_path: Optional path to save manifest files tar.gz archive
|
|
572
582
|
base_paths: List of base paths for the scan (optional)
|
|
583
|
+
explicit_files: Optional list of explicit files to use instead of discovering files
|
|
573
584
|
|
|
574
585
|
Returns:
|
|
575
586
|
Dict with full scan data including html_report_url
|
|
@@ -583,11 +594,15 @@ class Core:
|
|
|
583
594
|
if no_change:
|
|
584
595
|
return diff
|
|
585
596
|
|
|
586
|
-
#
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
files
|
|
590
|
-
|
|
597
|
+
# Use explicit files if provided, otherwise find manifest files from all paths
|
|
598
|
+
if explicit_files is not None:
|
|
599
|
+
all_files = explicit_files
|
|
600
|
+
log.debug(f"Using {len(all_files)} explicit files instead of discovering files")
|
|
601
|
+
else:
|
|
602
|
+
all_files = []
|
|
603
|
+
for path in paths:
|
|
604
|
+
files = self.find_files(path)
|
|
605
|
+
all_files.extend(files)
|
|
591
606
|
|
|
592
607
|
# Save submitted files list if requested
|
|
593
608
|
if save_files_list_path and all_files:
|
|
@@ -955,7 +970,8 @@ class Core:
|
|
|
955
970
|
no_change: bool = False,
|
|
956
971
|
save_files_list_path: Optional[str] = None,
|
|
957
972
|
save_manifest_tar_path: Optional[str] = None,
|
|
958
|
-
base_paths: Optional[List[str]] = None
|
|
973
|
+
base_paths: Optional[List[str]] = None,
|
|
974
|
+
explicit_files: Optional[List[str]] = None
|
|
959
975
|
) -> Diff:
|
|
960
976
|
"""Create a new diff using the Socket SDK.
|
|
961
977
|
|
|
@@ -966,16 +982,21 @@ class Core:
|
|
|
966
982
|
save_files_list_path: Optional path to save submitted files list for debugging
|
|
967
983
|
save_manifest_tar_path: Optional path to save manifest files tar.gz archive
|
|
968
984
|
base_paths: List of base paths for the scan (optional)
|
|
985
|
+
explicit_files: Optional list of explicit files to use instead of discovering files
|
|
969
986
|
"""
|
|
970
987
|
log.debug(f"starting create_new_diff with no_change: {no_change}")
|
|
971
988
|
if no_change:
|
|
972
989
|
return Diff(id="NO_DIFF_RAN", diff_url="", report_url="")
|
|
973
990
|
|
|
974
|
-
#
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
files
|
|
978
|
-
|
|
991
|
+
# Use explicit files if provided, otherwise find manifest files from all paths
|
|
992
|
+
if explicit_files is not None:
|
|
993
|
+
all_files = explicit_files
|
|
994
|
+
log.debug(f"Using {len(all_files)} explicit files instead of discovering files")
|
|
995
|
+
else:
|
|
996
|
+
all_files = []
|
|
997
|
+
for path in paths:
|
|
998
|
+
files = self.find_files(path)
|
|
999
|
+
all_files.extend(files)
|
|
979
1000
|
|
|
980
1001
|
# Save submitted files list if requested
|
|
981
1002
|
if save_files_list_path and all_files:
|
|
@@ -1317,8 +1338,9 @@ class Core:
|
|
|
1317
1338
|
url=package.url
|
|
1318
1339
|
)
|
|
1319
1340
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1341
|
+
# Use action from API (from security policy, label policy, triage, etc.)
|
|
1342
|
+
if 'action' in alert_item and alert_item['action']:
|
|
1343
|
+
action = alert_item['action']
|
|
1322
1344
|
setattr(issue_alert, action, True)
|
|
1323
1345
|
|
|
1324
1346
|
if issue_alert.key not in alerts_collection:
|
|
@@ -416,7 +416,7 @@ class Messages:
|
|
|
416
416
|
> **❗️ Caution**
|
|
417
417
|
> **Review the following alerts detected in dependencies.**
|
|
418
418
|
>
|
|
419
|
-
> According to your organization's
|
|
419
|
+
> According to your organization's policies, you **must** resolve all **"Block"** alerts before proceeding. It's recommended to resolve **"Warn"** alerts too.
|
|
420
420
|
> Learn more about [Socket for GitHub](https://socket.dev?utm_medium=gh).
|
|
421
421
|
|
|
422
422
|
<!-- start-socket-updated-alerts-table -->
|
|
@@ -622,7 +622,7 @@ class Messages:
|
|
|
622
622
|
@staticmethod
|
|
623
623
|
def create_security_alert_table(diff: Diff, md: MdUtils) -> tuple[MdUtils, list, dict]:
|
|
624
624
|
"""
|
|
625
|
-
Creates the detected issues table based on the
|
|
625
|
+
Creates the detected issues table based on alert actions from the API
|
|
626
626
|
:param diff: Diff - Diff report with the detected issues
|
|
627
627
|
:param md: MdUtils - Main markdown variable
|
|
628
628
|
:return:
|
|
@@ -794,7 +794,7 @@ class Messages:
|
|
|
794
794
|
@staticmethod
|
|
795
795
|
def create_console_security_alert_table(diff: Diff) -> PrettyTable:
|
|
796
796
|
"""
|
|
797
|
-
Creates the detected issues table based on the
|
|
797
|
+
Creates the detected issues table based on alert actions from the API
|
|
798
798
|
:param diff: Diff - Diff report with the detected issues
|
|
799
799
|
:return:
|
|
800
800
|
"""
|
|
@@ -25,7 +25,6 @@ class SocketConfig:
|
|
|
25
25
|
org_slug: Optional[str] = None
|
|
26
26
|
full_scan_path: Optional[str] = None
|
|
27
27
|
repository_path: Optional[str] = None
|
|
28
|
-
security_policy: Dict = None
|
|
29
28
|
repo_visibility: Optional[str] = 'private'
|
|
30
29
|
all_issues: Optional['AllIssues'] = None
|
|
31
30
|
excluded_dirs: Set[str] = field(default_factory=lambda: default_exclude_dirs)
|
|
@@ -42,10 +41,6 @@ class SocketConfig:
|
|
|
42
41
|
|
|
43
42
|
self._validate_api_url(self.api_url)
|
|
44
43
|
|
|
45
|
-
# Initialize empty dict for security policy if None
|
|
46
|
-
if self.security_policy is None:
|
|
47
|
-
self.security_policy = {}
|
|
48
|
-
|
|
49
44
|
# Initialize AllIssues if None
|
|
50
45
|
if self.all_issues is None:
|
|
51
46
|
self.all_issues = AllIssues()
|
|
@@ -70,6 +65,3 @@ class SocketConfig:
|
|
|
70
65
|
self.full_scan_path = f"{base_path}/full-scans"
|
|
71
66
|
self.repository_path = f"{base_path}/repos"
|
|
72
67
|
|
|
73
|
-
def update_security_policy(self, policy: Dict) -> None:
|
|
74
|
-
"""Update security policy"""
|
|
75
|
-
self.security_policy = policy
|
|
@@ -101,10 +101,11 @@ class ReachabilityAnalyzer:
|
|
|
101
101
|
additional_params: Optional[List[str]] = None,
|
|
102
102
|
allow_unverified: bool = False,
|
|
103
103
|
enable_debug: bool = False,
|
|
104
|
+
use_only_pregenerated_sboms: bool = False,
|
|
104
105
|
) -> Dict[str, Any]:
|
|
105
106
|
"""
|
|
106
107
|
Run reachability analysis.
|
|
107
|
-
|
|
108
|
+
|
|
108
109
|
Args:
|
|
109
110
|
org_slug: Socket organization slug
|
|
110
111
|
target_directory: Directory to analyze
|
|
@@ -125,7 +126,8 @@ class ReachabilityAnalyzer:
|
|
|
125
126
|
additional_params: Additional parameters to pass to coana CLI
|
|
126
127
|
allow_unverified: Disable SSL certificate verification (sets NODE_TLS_REJECT_UNAUTHORIZED=0)
|
|
127
128
|
enable_debug: Enable debug mode (passes -d flag to coana CLI)
|
|
128
|
-
|
|
129
|
+
use_only_pregenerated_sboms: Use only pre-generated CDX and SPDX files for the scan
|
|
130
|
+
|
|
129
131
|
Returns:
|
|
130
132
|
Dict containing scan_id and report_path
|
|
131
133
|
"""
|
|
@@ -179,7 +181,10 @@ class ReachabilityAnalyzer:
|
|
|
179
181
|
|
|
180
182
|
if enable_debug:
|
|
181
183
|
cmd.append("-d")
|
|
182
|
-
|
|
184
|
+
|
|
185
|
+
if use_only_pregenerated_sboms:
|
|
186
|
+
cmd.append("--use-only-pregenerated-sboms")
|
|
187
|
+
|
|
183
188
|
# Add any additional parameters provided by the user
|
|
184
189
|
if additional_params:
|
|
185
190
|
cmd.extend(additional_params)
|
|
@@ -167,6 +167,8 @@ def main_code():
|
|
|
167
167
|
|
|
168
168
|
# Variable to track if we need to override files with facts file
|
|
169
169
|
facts_file_to_submit = None
|
|
170
|
+
# Variable to track SBOM files to submit when using --reach-use-only-pregenerated-sboms
|
|
171
|
+
sbom_files_to_submit = None
|
|
170
172
|
|
|
171
173
|
# Git setup
|
|
172
174
|
is_repo = False
|
|
@@ -230,12 +232,14 @@ def main_code():
|
|
|
230
232
|
# Run reachability analysis if enabled
|
|
231
233
|
if config.reach:
|
|
232
234
|
from socketsecurity.core.tools.reachability import ReachabilityAnalyzer
|
|
233
|
-
|
|
235
|
+
|
|
234
236
|
log.info("Starting reachability analysis...")
|
|
235
|
-
|
|
237
|
+
|
|
236
238
|
# Find manifest files in scan paths (excluding .socket.facts.json to avoid circular dependency)
|
|
237
239
|
log.info("Finding manifest files for reachability analysis...")
|
|
238
240
|
manifest_files = []
|
|
241
|
+
|
|
242
|
+
# Always find all manifest files for the tar hash upload
|
|
239
243
|
for scan_path in scan_paths:
|
|
240
244
|
scan_manifests = core.find_files(scan_path)
|
|
241
245
|
# Filter out .socket.facts.json files from manifest upload
|
|
@@ -289,7 +293,8 @@ def main_code():
|
|
|
289
293
|
concurrency=config.reach_concurrency,
|
|
290
294
|
additional_params=config.reach_additional_params,
|
|
291
295
|
allow_unverified=config.allow_unverified,
|
|
292
|
-
enable_debug=config.enable_debug
|
|
296
|
+
enable_debug=config.enable_debug,
|
|
297
|
+
use_only_pregenerated_sboms=config.reach_use_only_pregenerated_sboms
|
|
293
298
|
)
|
|
294
299
|
|
|
295
300
|
log.info(f"Reachability analysis completed successfully")
|
|
@@ -301,6 +306,17 @@ def main_code():
|
|
|
301
306
|
if config.only_facts_file:
|
|
302
307
|
facts_file_to_submit = os.path.abspath(output_path)
|
|
303
308
|
log.info(f"Only-facts-file mode: will submit only {facts_file_to_submit}")
|
|
309
|
+
|
|
310
|
+
# If reach-use-only-pregenerated-sboms mode, submit CDX, SPDX, and facts file
|
|
311
|
+
if config.reach_use_only_pregenerated_sboms:
|
|
312
|
+
# Find only CDX and SPDX files for the final scan submission
|
|
313
|
+
sbom_files_to_submit = []
|
|
314
|
+
for scan_path in scan_paths:
|
|
315
|
+
sbom_files_to_submit.extend(core.find_sbom_files(scan_path))
|
|
316
|
+
# Use relative path for facts file
|
|
317
|
+
if os.path.exists(output_path):
|
|
318
|
+
sbom_files_to_submit.append(output_path)
|
|
319
|
+
log.info(f"Pre-generated SBOMs mode: will submit {len(sbom_files_to_submit)} files (CDX, SPDX, and facts file)")
|
|
304
320
|
|
|
305
321
|
except Exception as e:
|
|
306
322
|
log.error(f"Reachability analysis failed: {str(e)}")
|
|
@@ -331,6 +347,12 @@ def main_code():
|
|
|
331
347
|
files_explicitly_specified = True
|
|
332
348
|
log.debug(f"Overriding files to only submit facts file: {facts_file_to_submit}")
|
|
333
349
|
|
|
350
|
+
# Override files if reach-use-only-pregenerated-sboms mode is active
|
|
351
|
+
if sbom_files_to_submit:
|
|
352
|
+
specified_files = sbom_files_to_submit
|
|
353
|
+
files_explicitly_specified = True
|
|
354
|
+
log.debug(f"Overriding files to submit only SBOM files (CDX, SPDX, and facts): {sbom_files_to_submit}")
|
|
355
|
+
|
|
334
356
|
# Determine files to check based on the new logic
|
|
335
357
|
files_to_check = []
|
|
336
358
|
force_api_mode = False
|
|
@@ -452,7 +474,7 @@ def main_code():
|
|
|
452
474
|
log.info("Push initiated flow")
|
|
453
475
|
if scm.check_event_type() == "diff":
|
|
454
476
|
log.info("Starting comment logic for PR/MR event")
|
|
455
|
-
diff = core.create_new_diff(scan_paths, params, no_change=should_skip_scan, save_files_list_path=config.save_submitted_files_list, save_manifest_tar_path=config.save_manifest_tar, base_paths=base_paths)
|
|
477
|
+
diff = core.create_new_diff(scan_paths, params, no_change=should_skip_scan, save_files_list_path=config.save_submitted_files_list, save_manifest_tar_path=config.save_manifest_tar, base_paths=base_paths, explicit_files=sbom_files_to_submit)
|
|
456
478
|
comments = scm.get_comments_for_pr()
|
|
457
479
|
log.debug("Removing comment alerts")
|
|
458
480
|
|
|
@@ -505,14 +527,14 @@ def main_code():
|
|
|
505
527
|
)
|
|
506
528
|
else:
|
|
507
529
|
log.info("Starting non-PR/MR flow")
|
|
508
|
-
diff = core.create_new_diff(scan_paths, params, no_change=should_skip_scan, save_files_list_path=config.save_submitted_files_list, save_manifest_tar_path=config.save_manifest_tar, base_paths=base_paths)
|
|
530
|
+
diff = core.create_new_diff(scan_paths, params, no_change=should_skip_scan, save_files_list_path=config.save_submitted_files_list, save_manifest_tar_path=config.save_manifest_tar, base_paths=base_paths, explicit_files=sbom_files_to_submit)
|
|
509
531
|
|
|
510
532
|
output_handler.handle_output(diff)
|
|
511
|
-
|
|
533
|
+
|
|
512
534
|
elif config.enable_diff and not force_api_mode:
|
|
513
535
|
# New logic: --enable-diff forces diff mode even with --integration api (no SCM)
|
|
514
536
|
log.info("Diff mode enabled without SCM integration")
|
|
515
|
-
diff = core.create_new_diff(scan_paths, params, no_change=should_skip_scan, save_files_list_path=config.save_submitted_files_list, save_manifest_tar_path=config.save_manifest_tar, base_paths=base_paths)
|
|
537
|
+
diff = core.create_new_diff(scan_paths, params, no_change=should_skip_scan, save_files_list_path=config.save_submitted_files_list, save_manifest_tar_path=config.save_manifest_tar, base_paths=base_paths, explicit_files=sbom_files_to_submit)
|
|
516
538
|
output_handler.handle_output(diff)
|
|
517
539
|
|
|
518
540
|
elif config.enable_diff and force_api_mode:
|
|
@@ -530,12 +552,13 @@ def main_code():
|
|
|
530
552
|
no_change=should_skip_scan,
|
|
531
553
|
save_files_list_path=config.save_submitted_files_list,
|
|
532
554
|
save_manifest_tar_path=config.save_manifest_tar,
|
|
533
|
-
base_paths=base_paths
|
|
555
|
+
base_paths=base_paths,
|
|
556
|
+
explicit_files=sbom_files_to_submit
|
|
534
557
|
)
|
|
535
558
|
log.info(f"Full scan created with ID: {diff.id}")
|
|
536
559
|
log.info(f"Full scan report URL: {diff.report_url}")
|
|
537
560
|
output_handler.handle_output(diff)
|
|
538
|
-
|
|
561
|
+
|
|
539
562
|
else:
|
|
540
563
|
if force_api_mode:
|
|
541
564
|
log.info("No Manifest files changed, creating Socket Report")
|
|
@@ -550,7 +573,8 @@ def main_code():
|
|
|
550
573
|
no_change=should_skip_scan,
|
|
551
574
|
save_files_list_path=config.save_submitted_files_list,
|
|
552
575
|
save_manifest_tar_path=config.save_manifest_tar,
|
|
553
|
-
base_paths=base_paths
|
|
576
|
+
base_paths=base_paths,
|
|
577
|
+
explicit_files=sbom_files_to_submit
|
|
554
578
|
)
|
|
555
579
|
log.info(f"Full scan created with ID: {diff.id}")
|
|
556
580
|
log.info(f"Full scan report URL: {diff.report_url}")
|
|
@@ -561,7 +585,8 @@ def main_code():
|
|
|
561
585
|
no_change=should_skip_scan,
|
|
562
586
|
save_files_list_path=config.save_submitted_files_list,
|
|
563
587
|
save_manifest_tar_path=config.save_manifest_tar,
|
|
564
|
-
base_paths=base_paths
|
|
588
|
+
base_paths=base_paths,
|
|
589
|
+
explicit_files=sbom_files_to_submit
|
|
565
590
|
)
|
|
566
591
|
output_handler.handle_output(diff)
|
|
567
592
|
|
|
@@ -10,7 +10,6 @@ from socketdev.fullscans import (
|
|
|
10
10
|
StreamDiffResponse,
|
|
11
11
|
)
|
|
12
12
|
from socketdev.repos import GetRepoResponse
|
|
13
|
-
from socketdev.settings import OrgSecurityPolicyResponse
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
@pytest.fixture
|
|
@@ -88,14 +87,7 @@ def stream_diff_response(data_dir, load_json):
|
|
|
88
87
|
})
|
|
89
88
|
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
def security_policy(data_dir, load_json):
|
|
93
|
-
json_data = load_json(data_dir / "settings" / "security-policy.json")
|
|
94
|
-
return OrgSecurityPolicyResponse.from_dict({
|
|
95
|
-
"success": json_data["success"],
|
|
96
|
-
"status": json_data["status"],
|
|
97
|
-
"securityPolicyRules": json_data["securityPolicyRules"]
|
|
98
|
-
})
|
|
90
|
+
|
|
99
91
|
|
|
100
92
|
|
|
101
93
|
@pytest.fixture
|
|
@@ -146,13 +138,11 @@ def mock_sdk_with_responses(
|
|
|
146
138
|
new_scan_metadata,
|
|
147
139
|
new_scan_stream,
|
|
148
140
|
stream_diff_response,
|
|
149
|
-
security_policy,
|
|
150
141
|
create_full_scan_response,
|
|
151
142
|
):
|
|
152
143
|
sdk = mock_socket_sdk.return_value
|
|
153
144
|
|
|
154
145
|
# Simple returns
|
|
155
|
-
sdk.settings.get.return_value = security_policy
|
|
156
146
|
sdk.fullscans.post.return_value = create_full_scan_response
|
|
157
147
|
|
|
158
148
|
# Argument-based returns
|
|
@@ -33,11 +33,10 @@ class TestPackageAndAlerts:
|
|
|
33
33
|
}
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
-
# Set up settings.get() to return empty
|
|
36
|
+
# Set up settings.get() to return empty response
|
|
37
37
|
mock.settings = Mock()
|
|
38
38
|
settings_response = Mock()
|
|
39
39
|
settings_response.success = True
|
|
40
|
-
settings_response.security_policy = {}
|
|
41
40
|
mock.settings.get = Mock(return_value=settings_response)
|
|
42
41
|
|
|
43
42
|
return mock
|
|
@@ -48,7 +47,6 @@ class TestPackageAndAlerts:
|
|
|
48
47
|
api_key="test-key",
|
|
49
48
|
allow_unverified_ssl=False
|
|
50
49
|
)
|
|
51
|
-
config.security_policy = {} # Initialize with empty dict
|
|
52
50
|
return config
|
|
53
51
|
|
|
54
52
|
@pytest.fixture
|
|
@@ -135,34 +133,7 @@ class TestPackageAndAlerts:
|
|
|
135
133
|
assert alert.type == "networkAccess"
|
|
136
134
|
assert alert.severity == "high"
|
|
137
135
|
|
|
138
|
-
|
|
139
|
-
"""Test alerts are properly tagged based on security policy"""
|
|
140
|
-
# Mock security policy in config
|
|
141
|
-
core.config.security_policy = {
|
|
142
|
-
"networkAccess": {"action": "error"}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
package = Package(
|
|
146
|
-
id="pkg:npm/test@1.0.0",
|
|
147
|
-
name="test",
|
|
148
|
-
version="1.0.0",
|
|
149
|
-
type="npm",
|
|
150
|
-
alerts=[{
|
|
151
|
-
"type": "networkAccess",
|
|
152
|
-
"key": "test-alert",
|
|
153
|
-
"severity": "high"
|
|
154
|
-
}],
|
|
155
|
-
topLevelAncestors=[]
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
alerts_collection = {}
|
|
159
|
-
packages = {package.id: package}
|
|
160
|
-
|
|
161
|
-
result = core.add_package_alerts_to_collection(package, alerts_collection, packages)
|
|
162
|
-
|
|
163
|
-
assert len(result) == 1
|
|
164
|
-
alert = result["test-alert"][0]
|
|
165
|
-
assert alert.error is True
|
|
136
|
+
|
|
166
137
|
|
|
167
138
|
def test_get_capabilities_for_added_packages(self, core):
|
|
168
139
|
"""Test capability extraction from package alerts"""
|
|
@@ -7,13 +7,12 @@ def test_config_default_values():
|
|
|
7
7
|
|
|
8
8
|
assert config.api_key == "test_key"
|
|
9
9
|
assert config.api_url == "https://api.socket.dev/v0"
|
|
10
|
-
assert config.timeout ==
|
|
10
|
+
assert config.timeout == 1200
|
|
11
11
|
assert config.allow_unverified_ssl is False
|
|
12
12
|
assert config.org_id is None
|
|
13
13
|
assert config.org_slug is None
|
|
14
14
|
assert config.full_scan_path is None
|
|
15
15
|
assert config.repository_path is None
|
|
16
|
-
assert config.security_policy == {}
|
|
17
16
|
|
|
18
17
|
def test_config_custom_values():
|
|
19
18
|
"""Test that config accepts custom values"""
|
|
@@ -67,14 +66,4 @@ def test_config_update_org_details():
|
|
|
67
66
|
assert config.full_scan_path == "orgs/test-org/full-scans"
|
|
68
67
|
assert config.repository_path == "orgs/test-org/repos"
|
|
69
68
|
|
|
70
|
-
def test_config_update_security_policy():
|
|
71
|
-
"""Test updating security policy"""
|
|
72
|
-
config = SocketConfig(api_key="test_key")
|
|
73
|
-
|
|
74
|
-
test_policy = {
|
|
75
|
-
"rule1": {"action": "block"},
|
|
76
|
-
"rule2": {"action": "warn"}
|
|
77
|
-
}
|
|
78
69
|
|
|
79
|
-
config.security_policy = test_policy
|
|
80
|
-
assert config.security_policy == test_policy
|