socketsecurity 2.0.7__tar.gz → 2.0.9__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.0.7/socketsecurity.egg-info → socketsecurity-2.0.9}/PKG-INFO +29 -12
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/README.md +26 -10
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/pyproject.toml +2 -1
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/__init__.py +1 -1
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/__init__.py +105 -105
- {socketsecurity-2.0.7 → socketsecurity-2.0.9/socketsecurity.egg-info}/PKG-INFO +29 -12
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity.egg-info/requires.txt +2 -1
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/LICENSE +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/setup.cfg +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/config.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/cli_client.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/issues.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/licenses.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/logging.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/messages.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/scm/__init__.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/scm/base.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/scm/client.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/scm/github.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/scm/gitlab.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/socket_config.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/core/utils.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/output.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity/socketcli.py +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity.egg-info/SOURCES.txt +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity.egg-info/dependency_links.txt +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity.egg-info/entry_points.txt +0 -0
- {socketsecurity-2.0.7 → socketsecurity-2.0.9}/socketsecurity.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: socketsecurity
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.9
|
|
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>
|
|
@@ -19,7 +19,7 @@ Requires-Dist: prettytable
|
|
|
19
19
|
Requires-Dist: GitPython
|
|
20
20
|
Requires-Dist: packaging
|
|
21
21
|
Requires-Dist: python-dotenv
|
|
22
|
-
Requires-Dist: socket-sdk-python>=2.0.
|
|
22
|
+
Requires-Dist: socket-sdk-python>=2.0.8
|
|
23
23
|
Provides-Extra: test
|
|
24
24
|
Requires-Dist: pytest>=7.4.0; extra == "test"
|
|
25
25
|
Requires-Dist: pytest-cov>=4.1.0; extra == "test"
|
|
@@ -28,6 +28,7 @@ Requires-Dist: pytest-asyncio>=0.23.0; extra == "test"
|
|
|
28
28
|
Requires-Dist: pytest-watch>=4.2.0; extra == "test"
|
|
29
29
|
Provides-Extra: dev
|
|
30
30
|
Requires-Dist: ruff>=0.3.0; extra == "dev"
|
|
31
|
+
Requires-Dist: twine; extra == "dev"
|
|
31
32
|
Requires-Dist: pip-tools>=7.4.0; extra == "dev"
|
|
32
33
|
|
|
33
34
|
# Socket Security CLI
|
|
@@ -37,10 +38,10 @@ The Socket Security CLI was created to enable integrations with other tools like
|
|
|
37
38
|
## Usage
|
|
38
39
|
|
|
39
40
|
```` shell
|
|
40
|
-
socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--integration {api,github,gitlab}] [--owner OWNER] [--branch BRANCH]
|
|
41
|
-
[--committers [COMMITTERS ...]] [--pr-number PR_NUMBER] [--commit-message COMMIT_MESSAGE] [--commit-sha COMMIT_SHA]
|
|
42
|
-
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--files FILES] [--default-branch] [--pending-head]
|
|
43
|
-
[--generate-license] [--enable-debug] [--enable-json] [--enable-sarif] [--disable-overview] [--disable-security-issue]
|
|
41
|
+
socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--integration {api,github,gitlab}] [--owner OWNER] [--branch BRANCH]
|
|
42
|
+
[--committers [COMMITTERS ...]] [--pr-number PR_NUMBER] [--commit-message COMMIT_MESSAGE] [--commit-sha COMMIT_SHA]
|
|
43
|
+
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--files FILES] [--default-branch] [--pending-head]
|
|
44
|
+
[--generate-license] [--enable-debug] [--enable-json] [--enable-sarif] [--disable-overview] [--disable-security-issue]
|
|
44
45
|
[--allow-unverified] [--ignore-commit-files] [--disable-blocking] [--scm SCM] [--timeout TIMEOUT]
|
|
45
46
|
[--exclude-license-details]
|
|
46
47
|
````
|
|
@@ -107,9 +108,30 @@ If you don't want to provide the Socket API Token every time then you can use th
|
|
|
107
108
|
| --scm | False | api | Source control management type |
|
|
108
109
|
| --timeout | False | | Timeout in seconds for API requests |
|
|
109
110
|
|
|
111
|
+
## File Selection Behavior
|
|
112
|
+
|
|
113
|
+
The CLI determines which files to scan based on the following logic:
|
|
114
|
+
|
|
115
|
+
1. **Git Commit Files**: By default, the CLI checks files changed in the current git commit first. If any of these files match supported manifest patterns (like package.json, requirements.txt, etc.), a scan is triggered.
|
|
116
|
+
|
|
117
|
+
2. **`--files` Parameter**: If no git commit exists, or no manifest files are found in the commit changes, the CLI checks files specified via the `--files` parameter. This parameter accepts a JSON array of file paths.
|
|
118
|
+
|
|
119
|
+
3. **`--ignore-commit-files`**: When this flag is set, git commit files are ignored completely, and only files specified in `--files` are considered. This also forces a scan regardless of whether manifest files are present.
|
|
120
|
+
|
|
121
|
+
4. **No Manifest Files**: If no manifest files are found in either git commit changes or `--files` (and `--ignore-commit-files` is not set), the scan is skipped.
|
|
122
|
+
|
|
123
|
+
> **Note**: The CLI does not scan only the specified files - it uses them to determine whether a scan should be performed. When a scan is triggered, it searches the entire `--target-path` for all supported manifest files.
|
|
124
|
+
|
|
125
|
+
### Examples
|
|
126
|
+
|
|
127
|
+
- **Commit with manifest file**: If your commit includes changes to `package.json`, a scan will be triggered automatically.
|
|
128
|
+
- **Commit without manifest files**: If your commit only changes non-manifest files (like `.github/workflows/socket.yaml`), no scan will be performed unless you use `--files` or `--ignore-commit-files`.
|
|
129
|
+
- **Using `--files`**: If you specify `--files '["package.json"]'`, the CLI will check if this file exists and is a manifest file before triggering a scan.
|
|
130
|
+
- **Using `--ignore-commit-files`**: This forces a scan of all manifest files in the target path, regardless of what's in your commit.
|
|
131
|
+
|
|
110
132
|
## Development
|
|
111
133
|
|
|
112
|
-
This project uses `pyproject.toml` as the primary dependency specification.
|
|
134
|
+
This project uses `pyproject.toml` as the primary dependency specification.
|
|
113
135
|
|
|
114
136
|
### Development Workflows
|
|
115
137
|
|
|
@@ -164,8 +186,3 @@ Implementation targets:
|
|
|
164
186
|
### Environment Variables
|
|
165
187
|
|
|
166
188
|
- `SOCKET_SDK_PATH`: Path to local socket-sdk-python repository (default: ../socket-sdk-python)
|
|
167
|
-
|
|
168
|
-
### Running tests:
|
|
169
|
-
|
|
170
|
-
#### Run all tests:
|
|
171
|
-
```
|
|
@@ -5,10 +5,10 @@ The Socket Security CLI was created to enable integrations with other tools like
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
7
|
```` shell
|
|
8
|
-
socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--integration {api,github,gitlab}] [--owner OWNER] [--branch BRANCH]
|
|
9
|
-
[--committers [COMMITTERS ...]] [--pr-number PR_NUMBER] [--commit-message COMMIT_MESSAGE] [--commit-sha COMMIT_SHA]
|
|
10
|
-
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--files FILES] [--default-branch] [--pending-head]
|
|
11
|
-
[--generate-license] [--enable-debug] [--enable-json] [--enable-sarif] [--disable-overview] [--disable-security-issue]
|
|
8
|
+
socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--integration {api,github,gitlab}] [--owner OWNER] [--branch BRANCH]
|
|
9
|
+
[--committers [COMMITTERS ...]] [--pr-number PR_NUMBER] [--commit-message COMMIT_MESSAGE] [--commit-sha COMMIT_SHA]
|
|
10
|
+
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--files FILES] [--default-branch] [--pending-head]
|
|
11
|
+
[--generate-license] [--enable-debug] [--enable-json] [--enable-sarif] [--disable-overview] [--disable-security-issue]
|
|
12
12
|
[--allow-unverified] [--ignore-commit-files] [--disable-blocking] [--scm SCM] [--timeout TIMEOUT]
|
|
13
13
|
[--exclude-license-details]
|
|
14
14
|
````
|
|
@@ -75,9 +75,30 @@ If you don't want to provide the Socket API Token every time then you can use th
|
|
|
75
75
|
| --scm | False | api | Source control management type |
|
|
76
76
|
| --timeout | False | | Timeout in seconds for API requests |
|
|
77
77
|
|
|
78
|
+
## File Selection Behavior
|
|
79
|
+
|
|
80
|
+
The CLI determines which files to scan based on the following logic:
|
|
81
|
+
|
|
82
|
+
1. **Git Commit Files**: By default, the CLI checks files changed in the current git commit first. If any of these files match supported manifest patterns (like package.json, requirements.txt, etc.), a scan is triggered.
|
|
83
|
+
|
|
84
|
+
2. **`--files` Parameter**: If no git commit exists, or no manifest files are found in the commit changes, the CLI checks files specified via the `--files` parameter. This parameter accepts a JSON array of file paths.
|
|
85
|
+
|
|
86
|
+
3. **`--ignore-commit-files`**: When this flag is set, git commit files are ignored completely, and only files specified in `--files` are considered. This also forces a scan regardless of whether manifest files are present.
|
|
87
|
+
|
|
88
|
+
4. **No Manifest Files**: If no manifest files are found in either git commit changes or `--files` (and `--ignore-commit-files` is not set), the scan is skipped.
|
|
89
|
+
|
|
90
|
+
> **Note**: The CLI does not scan only the specified files - it uses them to determine whether a scan should be performed. When a scan is triggered, it searches the entire `--target-path` for all supported manifest files.
|
|
91
|
+
|
|
92
|
+
### Examples
|
|
93
|
+
|
|
94
|
+
- **Commit with manifest file**: If your commit includes changes to `package.json`, a scan will be triggered automatically.
|
|
95
|
+
- **Commit without manifest files**: If your commit only changes non-manifest files (like `.github/workflows/socket.yaml`), no scan will be performed unless you use `--files` or `--ignore-commit-files`.
|
|
96
|
+
- **Using `--files`**: If you specify `--files '["package.json"]'`, the CLI will check if this file exists and is a manifest file before triggering a scan.
|
|
97
|
+
- **Using `--ignore-commit-files`**: This forces a scan of all manifest files in the target path, regardless of what's in your commit.
|
|
98
|
+
|
|
78
99
|
## Development
|
|
79
100
|
|
|
80
|
-
This project uses `pyproject.toml` as the primary dependency specification.
|
|
101
|
+
This project uses `pyproject.toml` as the primary dependency specification.
|
|
81
102
|
|
|
82
103
|
### Development Workflows
|
|
83
104
|
|
|
@@ -132,8 +153,3 @@ Implementation targets:
|
|
|
132
153
|
### Environment Variables
|
|
133
154
|
|
|
134
155
|
- `SOCKET_SDK_PATH`: Path to local socket-sdk-python repository (default: ../socket-sdk-python)
|
|
135
|
-
|
|
136
|
-
### Running tests:
|
|
137
|
-
|
|
138
|
-
#### Run all tests:
|
|
139
|
-
```
|
|
@@ -13,7 +13,7 @@ dependencies = [
|
|
|
13
13
|
'GitPython',
|
|
14
14
|
'packaging',
|
|
15
15
|
'python-dotenv',
|
|
16
|
-
'socket-sdk-python>=2.0.
|
|
16
|
+
'socket-sdk-python>=2.0.8'
|
|
17
17
|
]
|
|
18
18
|
readme = "README.md"
|
|
19
19
|
description = "Socket Security CLI for CI/CD"
|
|
@@ -41,6 +41,7 @@ test = [
|
|
|
41
41
|
]
|
|
42
42
|
dev = [
|
|
43
43
|
"ruff>=0.3.0",
|
|
44
|
+
"twine", # for building
|
|
44
45
|
"pip-tools>=7.4.0", # for pip-compile
|
|
45
46
|
]
|
|
46
47
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__author__ = 'socket.dev'
|
|
2
|
-
__version__ = '2.0.
|
|
2
|
+
__version__ = '2.0.9'
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
import
|
|
2
|
+
import os
|
|
3
3
|
import sys
|
|
4
|
+
import time
|
|
4
5
|
from dataclasses import asdict
|
|
5
6
|
from glob import glob
|
|
6
7
|
from pathlib import PurePath
|
|
7
8
|
from typing import BinaryIO, Dict, List, Tuple
|
|
9
|
+
|
|
8
10
|
from socketdev import socketdev
|
|
9
|
-
from socketdev.
|
|
10
|
-
|
|
11
|
-
SocketArtifact
|
|
12
|
-
)
|
|
11
|
+
from socketdev.exceptions import APIFailure
|
|
12
|
+
from socketdev.fullscans import FullScanParams, SocketArtifact
|
|
13
13
|
from socketdev.org import Organization
|
|
14
14
|
from socketdev.repos import RepositoryInfo
|
|
15
15
|
from socketdev.settings import SecurityPolicyRule
|
|
@@ -23,10 +23,7 @@ from socketsecurity.core.classes import (
|
|
|
23
23
|
Package,
|
|
24
24
|
Purl,
|
|
25
25
|
)
|
|
26
|
-
from socketsecurity.core.exceptions import
|
|
27
|
-
APIResourceNotFound
|
|
28
|
-
)
|
|
29
|
-
from socketdev.exceptions import APIFailure
|
|
26
|
+
from socketsecurity.core.exceptions import APIResourceNotFound
|
|
30
27
|
from socketsecurity.core.licenses import Licenses
|
|
31
28
|
|
|
32
29
|
from .socket_config import SocketConfig
|
|
@@ -43,11 +40,11 @@ log = logging.getLogger("socketdev")
|
|
|
43
40
|
|
|
44
41
|
class Core:
|
|
45
42
|
"""Main class for interacting with Socket Security API and processing scan results."""
|
|
46
|
-
|
|
43
|
+
|
|
47
44
|
ALERT_TYPE_TO_CAPABILITY = {
|
|
48
45
|
"envVars": "Environment Variables",
|
|
49
46
|
"networkAccess": "Network Access",
|
|
50
|
-
"filesystemAccess": "File System Access",
|
|
47
|
+
"filesystemAccess": "File System Access",
|
|
51
48
|
"shellAccess": "Shell Access",
|
|
52
49
|
"usesEval": "Uses Eval",
|
|
53
50
|
"unsafe": "Unsafe"
|
|
@@ -77,7 +74,7 @@ class Core:
|
|
|
77
74
|
|
|
78
75
|
def get_org_id_slug(self) -> Tuple[str, str]:
|
|
79
76
|
"""Gets the Org ID and Org Slug for the API Token."""
|
|
80
|
-
response = self.sdk.org.get()
|
|
77
|
+
response = self.sdk.org.get(use_types=True)
|
|
81
78
|
organizations: Dict[str, Organization] = response.get("organizations", {})
|
|
82
79
|
|
|
83
80
|
if len(organizations) == 1:
|
|
@@ -87,33 +84,33 @@ class Core:
|
|
|
87
84
|
|
|
88
85
|
def get_sbom_data(self, full_scan_id: str) -> Dict[str, SocketArtifact]:
|
|
89
86
|
"""Returns the list of SBOM artifacts for a full scan."""
|
|
90
|
-
response = self.sdk.fullscans.stream(self.config.org_slug, full_scan_id)
|
|
87
|
+
response = self.sdk.fullscans.stream(self.config.org_slug, full_scan_id, use_types=True)
|
|
91
88
|
if not response.success:
|
|
92
89
|
log.debug(f"Failed to get SBOM data for full-scan {full_scan_id}")
|
|
93
90
|
log.debug(response.message)
|
|
94
91
|
return {}
|
|
95
92
|
|
|
96
93
|
return response.artifacts
|
|
97
|
-
|
|
94
|
+
|
|
98
95
|
def get_sbom_data_list(self, artifacts_dict: Dict[str, SocketArtifact]) -> list[SocketArtifact]:
|
|
99
96
|
"""Converts artifacts dictionary to a list."""
|
|
100
97
|
return list(artifacts_dict.values())
|
|
101
98
|
|
|
102
99
|
def get_security_policy(self) -> Dict[str, SecurityPolicyRule]:
|
|
103
100
|
"""Gets the organization's security policy."""
|
|
104
|
-
response = self.sdk.settings.get(self.config.org_slug)
|
|
105
|
-
|
|
101
|
+
response = self.sdk.settings.get(self.config.org_slug, use_types=True)
|
|
102
|
+
|
|
106
103
|
if not response.success:
|
|
107
104
|
log.error(f"Failed to get security policy: {response.status}")
|
|
108
105
|
log.error(response.message)
|
|
109
106
|
raise Exception(f"Failed to get security policy: {response.status}, message: {response.message}")
|
|
110
|
-
|
|
107
|
+
|
|
111
108
|
return response.securityPolicyRules
|
|
112
109
|
|
|
113
110
|
def create_sbom_output(self, diff: Diff) -> dict:
|
|
114
111
|
"""Creates CycloneDX output for a given diff."""
|
|
115
112
|
try:
|
|
116
|
-
result = self.sdk.export.cdx_bom(self.config.org_slug, diff.id)
|
|
113
|
+
result = self.sdk.export.cdx_bom(self.config.org_slug, diff.id, use_types=True)
|
|
117
114
|
if not result.success:
|
|
118
115
|
log.error(f"Failed to get CycloneDX Output for full-scan {diff.id}")
|
|
119
116
|
log.error(result.message)
|
|
@@ -121,7 +118,7 @@ class Core:
|
|
|
121
118
|
|
|
122
119
|
result.pop("success", None)
|
|
123
120
|
return result
|
|
124
|
-
except Exception
|
|
121
|
+
except Exception:
|
|
125
122
|
log.error(f"Unable to get CycloneDX Output for {diff.id}")
|
|
126
123
|
log.error(result.get("message", "No error message provided"))
|
|
127
124
|
return {}
|
|
@@ -130,17 +127,17 @@ class Core:
|
|
|
130
127
|
def find_files(path: str) -> List[str]:
|
|
131
128
|
"""
|
|
132
129
|
Finds supported manifest files in the given path.
|
|
133
|
-
|
|
130
|
+
|
|
134
131
|
Args:
|
|
135
132
|
path: Path to search for manifest files
|
|
136
|
-
|
|
133
|
+
|
|
137
134
|
Returns:
|
|
138
135
|
List of found manifest file paths
|
|
139
136
|
"""
|
|
140
137
|
log.debug("Starting Find Files")
|
|
141
138
|
start_time = time.time()
|
|
142
139
|
files = set()
|
|
143
|
-
|
|
140
|
+
|
|
144
141
|
for ecosystem in socket_globs:
|
|
145
142
|
patterns = socket_globs[ecosystem]
|
|
146
143
|
for file_name in patterns:
|
|
@@ -150,7 +147,8 @@ class Core:
|
|
|
150
147
|
glob_start = time.time()
|
|
151
148
|
glob_files = glob(file_path, recursive=True)
|
|
152
149
|
for glob_file in glob_files:
|
|
153
|
-
if
|
|
150
|
+
# Only add if it's a file, not a directory
|
|
151
|
+
if glob_file not in files and os.path.isfile(glob_file):
|
|
154
152
|
files.add(glob_file)
|
|
155
153
|
glob_end = time.time()
|
|
156
154
|
glob_total_time = glob_end - glob_start
|
|
@@ -165,18 +163,18 @@ class Core:
|
|
|
165
163
|
else:
|
|
166
164
|
log.debug(f"{len(files_list)} Files found ({total_time:.2f}s): {', '.join(files_list)}")
|
|
167
165
|
return list(files)
|
|
168
|
-
|
|
166
|
+
|
|
169
167
|
@staticmethod
|
|
170
168
|
def to_case_insensitive_regex(input_string: str) -> str:
|
|
171
169
|
"""
|
|
172
170
|
Converts a string into a case-insensitive regex pattern.
|
|
173
|
-
|
|
171
|
+
|
|
174
172
|
Args:
|
|
175
173
|
input_string: String to convert
|
|
176
|
-
|
|
174
|
+
|
|
177
175
|
Returns:
|
|
178
176
|
Case-insensitive regex pattern
|
|
179
|
-
|
|
177
|
+
|
|
180
178
|
Example:
|
|
181
179
|
"pipfile" -> "[Pp][Ii][Pp][Ff][Ii][Ll][Ee]"
|
|
182
180
|
"""
|
|
@@ -186,52 +184,52 @@ class Core:
|
|
|
186
184
|
def load_files_for_sending(files: List[str], workspace: str) -> List[Tuple[str, Tuple[str, BinaryIO]]]:
|
|
187
185
|
"""
|
|
188
186
|
Prepares files for sending to the Socket API.
|
|
189
|
-
|
|
187
|
+
|
|
190
188
|
Args:
|
|
191
189
|
files: List of file paths from find_files()
|
|
192
190
|
workspace: Base directory path to make paths relative to
|
|
193
|
-
|
|
191
|
+
|
|
194
192
|
Returns:
|
|
195
193
|
List of tuples formatted for requests multipart upload:
|
|
196
194
|
[(field_name, (filename, file_object)), ...]
|
|
197
195
|
"""
|
|
198
196
|
send_files = []
|
|
199
|
-
|
|
197
|
+
|
|
200
198
|
for file_path in files:
|
|
201
199
|
if "/" in file_path:
|
|
202
200
|
_, name = file_path.rsplit("/", 1)
|
|
203
201
|
else:
|
|
204
202
|
name = file_path
|
|
205
|
-
|
|
203
|
+
|
|
206
204
|
if file_path.startswith(workspace):
|
|
207
205
|
key = file_path[len(workspace):]
|
|
208
206
|
else:
|
|
209
207
|
key = file_path
|
|
210
|
-
|
|
208
|
+
|
|
211
209
|
key = key.lstrip("/")
|
|
212
210
|
key = key.lstrip("./")
|
|
213
|
-
|
|
211
|
+
|
|
214
212
|
f = open(file_path, 'rb')
|
|
215
213
|
payload = (key, (name, f))
|
|
216
214
|
send_files.append(payload)
|
|
217
|
-
|
|
215
|
+
|
|
218
216
|
return send_files
|
|
219
217
|
|
|
220
218
|
def create_full_scan(self, files: List[str], params: FullScanParams, has_head_scan: bool = False) -> FullScan:
|
|
221
219
|
"""
|
|
222
220
|
Creates a new full scan via the Socket API.
|
|
223
|
-
|
|
221
|
+
|
|
224
222
|
Args:
|
|
225
223
|
files: List of files to scan
|
|
226
224
|
params: Parameters for the full scan
|
|
227
|
-
|
|
225
|
+
|
|
228
226
|
Returns:
|
|
229
227
|
FullScan object with scan results
|
|
230
228
|
"""
|
|
231
229
|
log.debug("Creating new full scan")
|
|
232
230
|
create_full_start = time.time()
|
|
233
231
|
|
|
234
|
-
res = self.sdk.fullscans.post(files, params)
|
|
232
|
+
res = self.sdk.fullscans.post(files, params, use_types=True)
|
|
235
233
|
if not res.success:
|
|
236
234
|
log.error(f"Error creating full scan: {res.message}, status: {res.status}")
|
|
237
235
|
raise Exception(f"Error creating full scan: {res.message}, status: {res.status}")
|
|
@@ -245,20 +243,20 @@ class Core:
|
|
|
245
243
|
create_full_end = time.time()
|
|
246
244
|
total_time = create_full_end - create_full_start
|
|
247
245
|
log.debug(f"New Full Scan created in {total_time:.2f} seconds")
|
|
248
|
-
|
|
246
|
+
|
|
249
247
|
return full_scan
|
|
250
248
|
|
|
251
249
|
def get_full_scan(self, full_scan_id: str) -> FullScan:
|
|
252
250
|
"""
|
|
253
251
|
Get a FullScan object for an existing full scan including sbom_artifacts and packages.
|
|
254
|
-
|
|
252
|
+
|
|
255
253
|
Args:
|
|
256
254
|
full_scan_id: The ID of the full scan to get
|
|
257
|
-
|
|
255
|
+
|
|
258
256
|
Returns:
|
|
259
257
|
The FullScan object with populated artifacts and packages
|
|
260
258
|
"""
|
|
261
|
-
full_scan_metadata = self.sdk.fullscans.metadata(self.config.org_slug, full_scan_id)
|
|
259
|
+
full_scan_metadata = self.sdk.fullscans.metadata(self.config.org_slug, full_scan_id, use_types=True)
|
|
262
260
|
full_scan = FullScan(**asdict(full_scan_metadata.data))
|
|
263
261
|
full_scan_artifacts_dict = self.get_sbom_data(full_scan_id)
|
|
264
262
|
full_scan.sbom_artifacts = self.get_sbom_data_list(full_scan_artifacts_dict)
|
|
@@ -268,10 +266,10 @@ class Core:
|
|
|
268
266
|
def create_packages_dict(self, sbom_artifacts: list[SocketArtifact]) -> dict[str, Package]:
|
|
269
267
|
"""
|
|
270
268
|
Creates a dictionary of Package objects from SBOM artifacts.
|
|
271
|
-
|
|
269
|
+
|
|
272
270
|
Args:
|
|
273
271
|
sbom_artifacts: List of SBOM artifacts from the scan
|
|
274
|
-
|
|
272
|
+
|
|
275
273
|
Returns:
|
|
276
274
|
Dictionary mapping package IDs to Package objects
|
|
277
275
|
"""
|
|
@@ -289,51 +287,51 @@ class Core:
|
|
|
289
287
|
top_level_count[top_id] = 1
|
|
290
288
|
else:
|
|
291
289
|
top_level_count[top_id] += 1
|
|
292
|
-
|
|
290
|
+
|
|
293
291
|
for package_id, package in packages.items():
|
|
294
292
|
package.transitives = top_level_count.get(package_id, 0)
|
|
295
293
|
|
|
296
294
|
return packages
|
|
297
|
-
|
|
295
|
+
|
|
298
296
|
def get_package_license_text(self, package: Package) -> str:
|
|
299
297
|
"""
|
|
300
298
|
Gets the license text for a package if available.
|
|
301
|
-
|
|
299
|
+
|
|
302
300
|
Args:
|
|
303
301
|
package: Package object to get license text for
|
|
304
|
-
|
|
302
|
+
|
|
305
303
|
Returns:
|
|
306
304
|
License text if found, empty string otherwise
|
|
307
305
|
"""
|
|
308
306
|
if package.license is None:
|
|
309
307
|
return ""
|
|
310
|
-
|
|
308
|
+
|
|
311
309
|
license_raw = package.license
|
|
312
310
|
all_licenses = Licenses()
|
|
313
311
|
license_str = Licenses.make_python_safe(license_raw)
|
|
314
|
-
|
|
312
|
+
|
|
315
313
|
if license_str is not None and hasattr(all_licenses, license_str):
|
|
316
314
|
license_obj = getattr(all_licenses, license_str)
|
|
317
315
|
return license_obj.licenseText
|
|
318
|
-
|
|
316
|
+
|
|
319
317
|
return ""
|
|
320
318
|
|
|
321
319
|
def get_repo_info(self, repo_slug: str, default_branch: str = "socket-default-branch") -> RepositoryInfo:
|
|
322
320
|
"""
|
|
323
321
|
Gets repository information from the Socket API.
|
|
324
|
-
|
|
322
|
+
|
|
325
323
|
Args:
|
|
326
324
|
repo_slug: Repository slug to get info for
|
|
327
325
|
default_branch: Default branch string to use if the repo doesn't exist
|
|
328
|
-
|
|
326
|
+
|
|
329
327
|
Returns:
|
|
330
328
|
RepositoryInfo object
|
|
331
|
-
|
|
329
|
+
|
|
332
330
|
Raises:
|
|
333
331
|
Exception: If API request fails
|
|
334
332
|
"""
|
|
335
333
|
try:
|
|
336
|
-
response = self.sdk.repos.repo(self.config.org_slug, repo_slug)
|
|
334
|
+
response = self.sdk.repos.repo(self.config.org_slug, repo_slug, use_types=True)
|
|
337
335
|
if not response.success:
|
|
338
336
|
log.error(f"Failed to get repository: {response.status}")
|
|
339
337
|
log.error(response.message)
|
|
@@ -354,10 +352,10 @@ class Core:
|
|
|
354
352
|
def get_head_scan_for_repo(self, repo_slug: str) -> str:
|
|
355
353
|
"""
|
|
356
354
|
Gets the head scan ID for a repository.
|
|
357
|
-
|
|
355
|
+
|
|
358
356
|
Args:
|
|
359
357
|
repo_slug: Repository slug to get head scan for
|
|
360
|
-
|
|
358
|
+
|
|
361
359
|
Returns:
|
|
362
360
|
Head scan ID if it exists, None otherwise
|
|
363
361
|
"""
|
|
@@ -377,24 +375,34 @@ class Core:
|
|
|
377
375
|
def get_added_and_removed_packages(self, head_full_scan_id: str, new_full_scan: FullScan) -> Tuple[Dict[str, Package], Dict[str, Package]]:
|
|
378
376
|
"""
|
|
379
377
|
Get packages that were added and removed between scans.
|
|
380
|
-
|
|
378
|
+
|
|
381
379
|
Args:
|
|
382
380
|
head_full_scan: Previous scan (may be None if first scan)
|
|
383
381
|
head_full_scan_id: New scan just created
|
|
384
|
-
|
|
382
|
+
|
|
385
383
|
Returns:
|
|
386
384
|
Tuple of (added_packages, removed_packages) dictionaries
|
|
387
385
|
"""
|
|
388
386
|
if head_full_scan_id is None:
|
|
389
387
|
log.info(f"No head scan found. New scan ID: {new_full_scan.id}")
|
|
390
388
|
return new_full_scan.packages, {}
|
|
391
|
-
|
|
389
|
+
|
|
392
390
|
log.info(f"Comparing scans - Head scan ID: {head_full_scan_id}, New scan ID: {new_full_scan.id}")
|
|
393
391
|
diff_start = time.time()
|
|
394
|
-
|
|
392
|
+
try:
|
|
393
|
+
diff_report = self.sdk.fullscans.stream_diff(self.config.org_slug, head_full_scan_id, new_full_scan.id, use_types=True).data
|
|
394
|
+
except APIFailure as e:
|
|
395
|
+
log.error(f"API Error: {e}")
|
|
396
|
+
sys.exit(1)
|
|
397
|
+
except Exception as e:
|
|
398
|
+
import traceback
|
|
399
|
+
log.error(f"Error getting diff report: {str(e)}")
|
|
400
|
+
log.error(f"Stack trace:\n{traceback.format_exc()}")
|
|
401
|
+
raise
|
|
402
|
+
|
|
395
403
|
diff_end = time.time()
|
|
396
404
|
log.info(f"Diff Report Gathered in {diff_end - diff_start:.2f} seconds")
|
|
397
|
-
log.info(
|
|
405
|
+
log.info("Diff report artifact counts:")
|
|
398
406
|
log.info(f"Added: {len(diff_report.artifacts.added)}")
|
|
399
407
|
log.info(f"Removed: {len(diff_report.artifacts.removed)}")
|
|
400
408
|
log.info(f"Unchanged: {len(diff_report.artifacts.unchanged)}")
|
|
@@ -442,7 +450,6 @@ class Core:
|
|
|
442
450
|
Args:
|
|
443
451
|
path: Path to look for manifest files
|
|
444
452
|
params: Query params for the Full Scan endpoint
|
|
445
|
-
|
|
446
453
|
no_change: If True, return empty diff
|
|
447
454
|
"""
|
|
448
455
|
log.debug(f"starting create_new_diff with no_change: {no_change}")
|
|
@@ -464,7 +471,7 @@ class Core:
|
|
|
464
471
|
head_full_scan_id = None
|
|
465
472
|
has_head_scan = False
|
|
466
473
|
|
|
467
|
-
|
|
474
|
+
# Create new scan
|
|
468
475
|
try:
|
|
469
476
|
new_scan_start = time.time()
|
|
470
477
|
new_full_scan = self.create_full_scan(files_for_sending, params, has_head_scan)
|
|
@@ -474,17 +481,12 @@ class Core:
|
|
|
474
481
|
log.error(f"API Error: {e}")
|
|
475
482
|
sys.exit(1)
|
|
476
483
|
except Exception as e:
|
|
477
|
-
|
|
478
|
-
|
|
484
|
+
import traceback
|
|
485
|
+
log.error(f"Error creating new full scan: {str(e)}")
|
|
486
|
+
log.error(f"Stack trace:\n{traceback.format_exc()}")
|
|
487
|
+
raise
|
|
479
488
|
|
|
480
|
-
|
|
481
|
-
added_packages, removed_packages = self.get_added_and_removed_packages(head_full_scan_id, new_full_scan)
|
|
482
|
-
except APIFailure as e:
|
|
483
|
-
log.error(f"API Error: {e}")
|
|
484
|
-
sys.exit(1)
|
|
485
|
-
except Exception as e:
|
|
486
|
-
log.error(f"Unexpected error while comparing packages: {e}")
|
|
487
|
-
sys.exit(1)
|
|
489
|
+
added_packages, removed_packages = self.get_added_and_removed_packages(head_full_scan_id, new_full_scan)
|
|
488
490
|
|
|
489
491
|
diff = self.create_diff_report(added_packages, removed_packages)
|
|
490
492
|
|
|
@@ -495,7 +497,7 @@ class Core:
|
|
|
495
497
|
if not params.include_license_details:
|
|
496
498
|
report_url += "?include_license_details=false"
|
|
497
499
|
diff.report_url = report_url
|
|
498
|
-
|
|
500
|
+
|
|
499
501
|
if head_full_scan_id is not None:
|
|
500
502
|
diff.diff_url = f"{base_socket}/{self.config.org_slug}/diff/{diff.id}/{head_full_scan_id}"
|
|
501
503
|
else:
|
|
@@ -504,25 +506,25 @@ class Core:
|
|
|
504
506
|
return diff
|
|
505
507
|
|
|
506
508
|
def create_diff_report(
|
|
507
|
-
self,
|
|
508
|
-
added_packages: Dict[str, Package],
|
|
509
|
+
self,
|
|
510
|
+
added_packages: Dict[str, Package],
|
|
509
511
|
removed_packages: Dict[str, Package],
|
|
510
512
|
direct_only: bool = True
|
|
511
513
|
) -> Diff:
|
|
512
514
|
"""
|
|
513
515
|
Creates a diff report comparing two sets of packages.
|
|
514
|
-
|
|
516
|
+
|
|
515
517
|
Takes packages that were added and removed between two scans and:
|
|
516
518
|
1. Records new/removed packages (direct only by default)
|
|
517
519
|
2. Collects alerts from both sets of packages
|
|
518
520
|
3. Determines new capabilities introduced
|
|
519
|
-
|
|
521
|
+
|
|
520
522
|
Args:
|
|
521
523
|
added_packages: Dict of packages added in new scan
|
|
522
524
|
removed_packages: Dict of packages removed in new scan
|
|
523
525
|
direct_only: If True, only direct dependencies are included in new/removed lists
|
|
524
526
|
(but alerts are still processed for all packages)
|
|
525
|
-
|
|
527
|
+
|
|
526
528
|
Returns:
|
|
527
529
|
Diff object containing the comparison results
|
|
528
530
|
"""
|
|
@@ -577,11 +579,11 @@ class Core:
|
|
|
577
579
|
def create_purl(package_id: str, packages: dict[str, Package]) -> Purl:
|
|
578
580
|
"""
|
|
579
581
|
Creates the extended PURL data for package identification and tracking.
|
|
580
|
-
|
|
582
|
+
|
|
581
583
|
Args:
|
|
582
584
|
package_id: Package ID to create PURL data for
|
|
583
585
|
packages: Dictionary of all packages for transitive dependency lookup
|
|
584
|
-
|
|
586
|
+
|
|
585
587
|
Returns:
|
|
586
588
|
Purl object containing package metadata and dependency information
|
|
587
589
|
"""
|
|
@@ -606,14 +608,14 @@ class Core:
|
|
|
606
608
|
def get_source_data(package: Package, packages: dict) -> list:
|
|
607
609
|
"""
|
|
608
610
|
Determines how a package was introduced into the dependency tree.
|
|
609
|
-
|
|
611
|
+
|
|
610
612
|
For direct dependencies, records the manifest file.
|
|
611
613
|
For transitive dependencies, records the top-level package that introduced it.
|
|
612
|
-
|
|
614
|
+
|
|
613
615
|
Args:
|
|
614
616
|
package: Package to analyze
|
|
615
617
|
packages: Dictionary of all packages for ancestor lookup
|
|
616
|
-
|
|
618
|
+
|
|
617
619
|
Returns:
|
|
618
620
|
List of tuples containing (source, manifest_file) information
|
|
619
621
|
"""
|
|
@@ -648,7 +650,7 @@ class Core:
|
|
|
648
650
|
def add_purl_capabilities(diff: Diff) -> None:
|
|
649
651
|
"""
|
|
650
652
|
Adds capability information to each package in the diff's new_packages list.
|
|
651
|
-
|
|
653
|
+
|
|
652
654
|
Args:
|
|
653
655
|
diff: Diff object to update with capability information
|
|
654
656
|
"""
|
|
@@ -662,18 +664,18 @@ class Core:
|
|
|
662
664
|
new_packages.append(new_purl)
|
|
663
665
|
else:
|
|
664
666
|
new_packages.append(purl)
|
|
665
|
-
|
|
667
|
+
|
|
666
668
|
diff.new_packages = new_packages
|
|
667
669
|
|
|
668
670
|
def add_package_alerts_to_collection(self, package: Package, alerts_collection: dict, packages: dict) -> dict:
|
|
669
671
|
"""
|
|
670
672
|
Processes alerts from a package and adds them to a shared alerts collection.
|
|
671
|
-
|
|
673
|
+
|
|
672
674
|
Args:
|
|
673
675
|
package: Package to process alerts from
|
|
674
676
|
alerts_collection: Dictionary to store processed alerts
|
|
675
677
|
packages: Dictionary of all packages for dependency lookup
|
|
676
|
-
|
|
678
|
+
|
|
677
679
|
Returns:
|
|
678
680
|
Updated alerts collection dictionary
|
|
679
681
|
"""
|
|
@@ -723,11 +725,11 @@ class Core:
|
|
|
723
725
|
def save_file(file_name: str, content: str) -> None:
|
|
724
726
|
"""
|
|
725
727
|
Saves content to a file, raising an error if the save fails.
|
|
726
|
-
|
|
728
|
+
|
|
727
729
|
Args:
|
|
728
730
|
file_name: Path to save the file
|
|
729
731
|
content: Content to write to the file
|
|
730
|
-
|
|
732
|
+
|
|
731
733
|
Raises:
|
|
732
734
|
IOError: If file cannot be written
|
|
733
735
|
"""
|
|
@@ -742,10 +744,10 @@ class Core:
|
|
|
742
744
|
def has_manifest_files(files: list) -> bool:
|
|
743
745
|
"""
|
|
744
746
|
Checks if any files in the list are supported manifest files.
|
|
745
|
-
|
|
747
|
+
|
|
746
748
|
Args:
|
|
747
749
|
files: List of file paths to check
|
|
748
|
-
|
|
750
|
+
|
|
749
751
|
Returns:
|
|
750
752
|
True if any files match manifest patterns, False otherwise
|
|
751
753
|
"""
|
|
@@ -764,41 +766,41 @@ class Core:
|
|
|
764
766
|
def get_capabilities_for_added_packages(added_packages: Dict[str, Package]) -> Dict[str, List[str]]:
|
|
765
767
|
"""
|
|
766
768
|
Maps added packages to their capabilities based on their alerts.
|
|
767
|
-
|
|
769
|
+
|
|
768
770
|
Args:
|
|
769
771
|
added_packages: Dictionary of packages added in new scan
|
|
770
|
-
|
|
772
|
+
|
|
771
773
|
Returns:
|
|
772
774
|
Dictionary mapping package IDs to their capability lists
|
|
773
775
|
"""
|
|
774
776
|
capabilities: Dict[str, List[str]] = {}
|
|
775
|
-
|
|
777
|
+
|
|
776
778
|
for package_id, package in added_packages.items():
|
|
777
779
|
for alert in package.alerts:
|
|
778
780
|
if alert["type"] in Core.ALERT_TYPE_TO_CAPABILITY:
|
|
779
781
|
value = Core.ALERT_TYPE_TO_CAPABILITY[alert["type"]]
|
|
780
|
-
|
|
782
|
+
|
|
781
783
|
if package_id not in capabilities:
|
|
782
784
|
capabilities[package_id] = [value]
|
|
783
785
|
elif value not in capabilities[package_id]:
|
|
784
786
|
capabilities[package_id].append(value)
|
|
785
|
-
|
|
787
|
+
|
|
786
788
|
return capabilities
|
|
787
789
|
|
|
788
790
|
@staticmethod
|
|
789
791
|
def get_new_alerts(
|
|
790
|
-
added_package_alerts: Dict[str, List[Issue]],
|
|
792
|
+
added_package_alerts: Dict[str, List[Issue]],
|
|
791
793
|
removed_package_alerts: Dict[str, List[Issue]],
|
|
792
794
|
ignore_readded: bool = True
|
|
793
795
|
) -> List[Issue]:
|
|
794
796
|
"""
|
|
795
797
|
Find alerts that are new or changed between added and removed packages.
|
|
796
|
-
|
|
798
|
+
|
|
797
799
|
Args:
|
|
798
800
|
added_package_alerts: Dictionary of alerts from packages that were added
|
|
799
801
|
removed_package_alerts: Dictionary of alerts from packages that were removed
|
|
800
802
|
ignore_readded: If True, don't report alerts that were both removed and added
|
|
801
|
-
|
|
803
|
+
|
|
802
804
|
Returns:
|
|
803
805
|
List of newly found alerts
|
|
804
806
|
"""
|
|
@@ -810,7 +812,7 @@ class Core:
|
|
|
810
812
|
new_alerts = added_package_alerts[alert_key]
|
|
811
813
|
for alert in new_alerts:
|
|
812
814
|
alert_str = f"{alert.purl},{alert.manifests},{alert.type}"
|
|
813
|
-
|
|
815
|
+
|
|
814
816
|
if alert.error or alert.warn:
|
|
815
817
|
if alert_str not in consolidated_alerts:
|
|
816
818
|
alerts.append(alert)
|
|
@@ -818,10 +820,10 @@ class Core:
|
|
|
818
820
|
else:
|
|
819
821
|
new_alerts = added_package_alerts[alert_key]
|
|
820
822
|
removed_alerts = removed_package_alerts[alert_key]
|
|
821
|
-
|
|
823
|
+
|
|
822
824
|
for alert in new_alerts:
|
|
823
825
|
alert_str = f"{alert.purl},{alert.manifests},{alert.type}"
|
|
824
|
-
|
|
826
|
+
|
|
825
827
|
# Only add if:
|
|
826
828
|
# 1. Alert isn't in removed packages (or we're not ignoring readded alerts)
|
|
827
829
|
# 2. We haven't already recorded this alert
|
|
@@ -832,5 +834,3 @@ class Core:
|
|
|
832
834
|
consolidated_alerts.add(alert_str)
|
|
833
835
|
|
|
834
836
|
return alerts
|
|
835
|
-
|
|
836
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: socketsecurity
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.9
|
|
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>
|
|
@@ -19,7 +19,7 @@ Requires-Dist: prettytable
|
|
|
19
19
|
Requires-Dist: GitPython
|
|
20
20
|
Requires-Dist: packaging
|
|
21
21
|
Requires-Dist: python-dotenv
|
|
22
|
-
Requires-Dist: socket-sdk-python>=2.0.
|
|
22
|
+
Requires-Dist: socket-sdk-python>=2.0.8
|
|
23
23
|
Provides-Extra: test
|
|
24
24
|
Requires-Dist: pytest>=7.4.0; extra == "test"
|
|
25
25
|
Requires-Dist: pytest-cov>=4.1.0; extra == "test"
|
|
@@ -28,6 +28,7 @@ Requires-Dist: pytest-asyncio>=0.23.0; extra == "test"
|
|
|
28
28
|
Requires-Dist: pytest-watch>=4.2.0; extra == "test"
|
|
29
29
|
Provides-Extra: dev
|
|
30
30
|
Requires-Dist: ruff>=0.3.0; extra == "dev"
|
|
31
|
+
Requires-Dist: twine; extra == "dev"
|
|
31
32
|
Requires-Dist: pip-tools>=7.4.0; extra == "dev"
|
|
32
33
|
|
|
33
34
|
# Socket Security CLI
|
|
@@ -37,10 +38,10 @@ The Socket Security CLI was created to enable integrations with other tools like
|
|
|
37
38
|
## Usage
|
|
38
39
|
|
|
39
40
|
```` shell
|
|
40
|
-
socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--integration {api,github,gitlab}] [--owner OWNER] [--branch BRANCH]
|
|
41
|
-
[--committers [COMMITTERS ...]] [--pr-number PR_NUMBER] [--commit-message COMMIT_MESSAGE] [--commit-sha COMMIT_SHA]
|
|
42
|
-
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--files FILES] [--default-branch] [--pending-head]
|
|
43
|
-
[--generate-license] [--enable-debug] [--enable-json] [--enable-sarif] [--disable-overview] [--disable-security-issue]
|
|
41
|
+
socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--integration {api,github,gitlab}] [--owner OWNER] [--branch BRANCH]
|
|
42
|
+
[--committers [COMMITTERS ...]] [--pr-number PR_NUMBER] [--commit-message COMMIT_MESSAGE] [--commit-sha COMMIT_SHA]
|
|
43
|
+
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--files FILES] [--default-branch] [--pending-head]
|
|
44
|
+
[--generate-license] [--enable-debug] [--enable-json] [--enable-sarif] [--disable-overview] [--disable-security-issue]
|
|
44
45
|
[--allow-unverified] [--ignore-commit-files] [--disable-blocking] [--scm SCM] [--timeout TIMEOUT]
|
|
45
46
|
[--exclude-license-details]
|
|
46
47
|
````
|
|
@@ -107,9 +108,30 @@ If you don't want to provide the Socket API Token every time then you can use th
|
|
|
107
108
|
| --scm | False | api | Source control management type |
|
|
108
109
|
| --timeout | False | | Timeout in seconds for API requests |
|
|
109
110
|
|
|
111
|
+
## File Selection Behavior
|
|
112
|
+
|
|
113
|
+
The CLI determines which files to scan based on the following logic:
|
|
114
|
+
|
|
115
|
+
1. **Git Commit Files**: By default, the CLI checks files changed in the current git commit first. If any of these files match supported manifest patterns (like package.json, requirements.txt, etc.), a scan is triggered.
|
|
116
|
+
|
|
117
|
+
2. **`--files` Parameter**: If no git commit exists, or no manifest files are found in the commit changes, the CLI checks files specified via the `--files` parameter. This parameter accepts a JSON array of file paths.
|
|
118
|
+
|
|
119
|
+
3. **`--ignore-commit-files`**: When this flag is set, git commit files are ignored completely, and only files specified in `--files` are considered. This also forces a scan regardless of whether manifest files are present.
|
|
120
|
+
|
|
121
|
+
4. **No Manifest Files**: If no manifest files are found in either git commit changes or `--files` (and `--ignore-commit-files` is not set), the scan is skipped.
|
|
122
|
+
|
|
123
|
+
> **Note**: The CLI does not scan only the specified files - it uses them to determine whether a scan should be performed. When a scan is triggered, it searches the entire `--target-path` for all supported manifest files.
|
|
124
|
+
|
|
125
|
+
### Examples
|
|
126
|
+
|
|
127
|
+
- **Commit with manifest file**: If your commit includes changes to `package.json`, a scan will be triggered automatically.
|
|
128
|
+
- **Commit without manifest files**: If your commit only changes non-manifest files (like `.github/workflows/socket.yaml`), no scan will be performed unless you use `--files` or `--ignore-commit-files`.
|
|
129
|
+
- **Using `--files`**: If you specify `--files '["package.json"]'`, the CLI will check if this file exists and is a manifest file before triggering a scan.
|
|
130
|
+
- **Using `--ignore-commit-files`**: This forces a scan of all manifest files in the target path, regardless of what's in your commit.
|
|
131
|
+
|
|
110
132
|
## Development
|
|
111
133
|
|
|
112
|
-
This project uses `pyproject.toml` as the primary dependency specification.
|
|
134
|
+
This project uses `pyproject.toml` as the primary dependency specification.
|
|
113
135
|
|
|
114
136
|
### Development Workflows
|
|
115
137
|
|
|
@@ -164,8 +186,3 @@ Implementation targets:
|
|
|
164
186
|
### Environment Variables
|
|
165
187
|
|
|
166
188
|
- `SOCKET_SDK_PATH`: Path to local socket-sdk-python repository (default: ../socket-sdk-python)
|
|
167
|
-
|
|
168
|
-
### Running tests:
|
|
169
|
-
|
|
170
|
-
#### Run all tests:
|
|
171
|
-
```
|
|
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
|
|
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
|
|
File without changes
|