socketsecurity 2.1.33__tar.gz → 2.2.0__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.1.33 → socketsecurity-2.2.0}/.github/workflows/docker-stable.yml +8 -7
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.github/workflows/pr-preview.yml +7 -7
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.github/workflows/release.yml +6 -6
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/PKG-INFO +5 -2
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/README.md +3 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/pyproject.toml +2 -2
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/requirements.txt +1 -1
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/__init__.py +1 -1
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/config.py +7 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/__init__.py +72 -43
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/git_interface.py +83 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/messages.py +58 -4
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/socketcli.py +28 -1
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/unit/test_cli_config.py +14 -2
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.github/CODEOWNERS +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.github/workflows/version-check.yml +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.gitignore +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.hooks/sync_version.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.pre-commit-config.yaml +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/.python-version +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/Dockerfile +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/LICENSE +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/Makefile +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/Pipfile.lock +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/docs/README.md +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/pytest.ini +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/requirements-dev.lock +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/requirements-dev.txt +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/requirements.lock +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/scripts/build_container.sh +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/scripts/deploy-test-docker.sh +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/scripts/deploy-test-pypi.sh +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/scripts/run.sh +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/cli_client.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/helper/__init__.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/lazy_file_loader.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/logging.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/resource_utils.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/scm/__init__.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/scm/base.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/scm/client.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/scm/github.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/scm/gitlab.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/socket_config.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/core/utils.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/output.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/plugins/__init__.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/plugins/base.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/plugins/jira.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/plugins/manager.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/plugins/slack.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/plugins/teams.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/socketsecurity/plugins/webhook.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/__init__.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/core/conftest.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/core/create_diff_input.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/core/test_diff_generation.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/core/test_package_and_alerts.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/core/test_sdk_methods.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/core/test_supporting_methods.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/create_response.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/diff/stream_diff.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/head_scan/metadata.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/new_scan/metadata.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/repos/repo_info_error.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/repos/repo_info_no_head.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/repos/repo_info_success.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/settings/security-policy.json +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/unit/__init__.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/unit/test_client.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/unit/test_config.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/unit/test_output.py +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/workflows/bitbucket-pipelines.yml +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/workflows/github-actions.yml +0 -0
- {socketsecurity-2.1.33 → socketsecurity-2.2.0}/workflows/gitlab-ci.yml +0 -0
|
@@ -21,18 +21,18 @@ jobs:
|
|
|
21
21
|
fi
|
|
22
22
|
echo "Version ${{ inputs.version }} found on PyPI - proceeding with release"
|
|
23
23
|
|
|
24
|
-
- name: Login to Docker Hub
|
|
25
|
-
uses: docker/login-action@v3
|
|
26
|
-
with:
|
|
27
|
-
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
28
|
-
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
29
|
-
|
|
30
24
|
- name: Set up QEMU
|
|
31
25
|
uses: docker/setup-qemu-action@v3
|
|
32
26
|
|
|
33
27
|
- name: Set up Docker Buildx
|
|
34
28
|
uses: docker/setup-buildx-action@v3
|
|
35
29
|
|
|
30
|
+
- name: Login to Docker Hub with Organization Token
|
|
31
|
+
uses: docker/login-action@v3
|
|
32
|
+
with:
|
|
33
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
34
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
35
|
+
|
|
36
36
|
- name: Build & Push Stable Docker
|
|
37
37
|
uses: docker/build-push-action@v5
|
|
38
38
|
with:
|
|
@@ -40,4 +40,5 @@ jobs:
|
|
|
40
40
|
platforms: linux/amd64,linux/arm64
|
|
41
41
|
tags: socketdev/cli:stable
|
|
42
42
|
build-args: |
|
|
43
|
-
CLI_VERSION=${{ inputs.version }}
|
|
43
|
+
CLI_VERSION=${{ inputs.version }}
|
|
44
|
+
|
|
@@ -119,19 +119,19 @@ jobs:
|
|
|
119
119
|
echo "success=false" >> $GITHUB_OUTPUT
|
|
120
120
|
exit 1
|
|
121
121
|
|
|
122
|
-
- name: Login to Docker Hub
|
|
123
|
-
if: steps.verify_package.outputs.success == 'true'
|
|
124
|
-
uses: docker/login-action@v3
|
|
125
|
-
with:
|
|
126
|
-
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
127
|
-
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
128
|
-
|
|
129
122
|
- name: Set up QEMU
|
|
130
123
|
uses: docker/setup-qemu-action@v3
|
|
131
124
|
|
|
132
125
|
- name: Set up Docker Buildx
|
|
133
126
|
uses: docker/setup-buildx-action@v3
|
|
134
127
|
|
|
128
|
+
- name: Login to Docker Hub with Organization Token
|
|
129
|
+
if: steps.verify_package.outputs.success == 'true'
|
|
130
|
+
uses: docker/login-action@v3
|
|
131
|
+
with:
|
|
132
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
133
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
134
|
+
|
|
135
135
|
- name: Build & Push Docker Preview
|
|
136
136
|
if: steps.verify_package.outputs.success == 'true'
|
|
137
137
|
uses: docker/build-push-action@v5
|
|
@@ -68,18 +68,18 @@ jobs:
|
|
|
68
68
|
if: steps.version_check.outputs.pypi_exists != 'true'
|
|
69
69
|
uses: pypa/gh-action-pypi-publish@v1.12.4
|
|
70
70
|
|
|
71
|
-
- name: Login to Docker Hub
|
|
72
|
-
uses: docker/login-action@v3
|
|
73
|
-
with:
|
|
74
|
-
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
75
|
-
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
76
|
-
|
|
77
71
|
- name: Set up QEMU
|
|
78
72
|
uses: docker/setup-qemu-action@v3
|
|
79
73
|
|
|
80
74
|
- name: Set up Docker Buildx
|
|
81
75
|
uses: docker/setup-buildx-action@v3
|
|
82
76
|
|
|
77
|
+
- name: Login to Docker Hub with Organization Token
|
|
78
|
+
uses: docker/login-action@v3
|
|
79
|
+
with:
|
|
80
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
81
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
82
|
+
|
|
83
83
|
- name: Verify package is installable
|
|
84
84
|
id: verify_package
|
|
85
85
|
env:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: socketsecurity
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
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>
|
|
@@ -39,7 +39,7 @@ Requires-Dist: packaging
|
|
|
39
39
|
Requires-Dist: prettytable
|
|
40
40
|
Requires-Dist: python-dotenv
|
|
41
41
|
Requires-Dist: requests
|
|
42
|
-
Requires-Dist: socket-sdk-python<3,>=2.1.
|
|
42
|
+
Requires-Dist: socket-sdk-python<3,>=2.1.8
|
|
43
43
|
Provides-Extra: dev
|
|
44
44
|
Requires-Dist: hatch; extra == 'dev'
|
|
45
45
|
Requires-Dist: pip-tools>=7.4.0; extra == 'dev'
|
|
@@ -172,6 +172,7 @@ If you don't want to provide the Socket API Token every time then you can use th
|
|
|
172
172
|
|:-------------------------|:---------|:--------|:----------------------------------------------------------------------|
|
|
173
173
|
| --ignore-commit-files | False | False | Ignore commit files |
|
|
174
174
|
| --disable-blocking | False | False | Disable blocking mode |
|
|
175
|
+
| --enable-diff | False | False | Enable diff mode even when using --integration api (forces diff mode without SCM integration) |
|
|
175
176
|
| --scm | False | api | Source control management type |
|
|
176
177
|
| --timeout | False | | Timeout in seconds for API requests |
|
|
177
178
|
| --include-module-folders | False | False | If enabled will include manifest files from folders like node_modules |
|
|
@@ -261,6 +262,7 @@ The CLI determines which files to scan based on the following logic:
|
|
|
261
262
|
- **Differential Mode**: When manifest files are detected in changes, performs a diff scan with PR/MR comment integration
|
|
262
263
|
- **API Mode**: When no manifest files are in changes, creates a full scan report without PR comments but still scans the entire repository
|
|
263
264
|
- **Force Mode**: With `--ignore-commit-files`, always performs a full scan regardless of changes
|
|
265
|
+
- **Forced Diff Mode**: With `--enable-diff`, forces differential mode even when using `--integration api` (without SCM integration)
|
|
264
266
|
|
|
265
267
|
### Examples
|
|
266
268
|
|
|
@@ -268,6 +270,7 @@ The CLI determines which files to scan based on the following logic:
|
|
|
268
270
|
- **Commit without manifest files**: If your commit only changes non-manifest files (like `.github/workflows/socket.yaml`), the CLI automatically switches to API mode and performs a full repository scan.
|
|
269
271
|
- **Using `--files`**: If you specify `--files '["package.json"]'`, the CLI will check if this file exists and is a manifest file before determining scan type.
|
|
270
272
|
- **Using `--ignore-commit-files`**: This forces a full scan of all manifest files in the target path, regardless of what's in your commit.
|
|
273
|
+
- **Using `--enable-diff`**: Forces diff mode without SCM integration - useful when you want differential scanning but are using `--integration api`. For example: `socketcli --integration api --enable-diff --target-path /path/to/repo`
|
|
271
274
|
- **Auto-detection**: Most CI/CD scenarios now work with just `socketcli --target-path /path/to/repo --scm github --pr-number $PR_NUM`
|
|
272
275
|
|
|
273
276
|
## Debugging and Troubleshooting
|
|
@@ -116,6 +116,7 @@ If you don't want to provide the Socket API Token every time then you can use th
|
|
|
116
116
|
|:-------------------------|:---------|:--------|:----------------------------------------------------------------------|
|
|
117
117
|
| --ignore-commit-files | False | False | Ignore commit files |
|
|
118
118
|
| --disable-blocking | False | False | Disable blocking mode |
|
|
119
|
+
| --enable-diff | False | False | Enable diff mode even when using --integration api (forces diff mode without SCM integration) |
|
|
119
120
|
| --scm | False | api | Source control management type |
|
|
120
121
|
| --timeout | False | | Timeout in seconds for API requests |
|
|
121
122
|
| --include-module-folders | False | False | If enabled will include manifest files from folders like node_modules |
|
|
@@ -205,6 +206,7 @@ The CLI determines which files to scan based on the following logic:
|
|
|
205
206
|
- **Differential Mode**: When manifest files are detected in changes, performs a diff scan with PR/MR comment integration
|
|
206
207
|
- **API Mode**: When no manifest files are in changes, creates a full scan report without PR comments but still scans the entire repository
|
|
207
208
|
- **Force Mode**: With `--ignore-commit-files`, always performs a full scan regardless of changes
|
|
209
|
+
- **Forced Diff Mode**: With `--enable-diff`, forces differential mode even when using `--integration api` (without SCM integration)
|
|
208
210
|
|
|
209
211
|
### Examples
|
|
210
212
|
|
|
@@ -212,6 +214,7 @@ The CLI determines which files to scan based on the following logic:
|
|
|
212
214
|
- **Commit without manifest files**: If your commit only changes non-manifest files (like `.github/workflows/socket.yaml`), the CLI automatically switches to API mode and performs a full repository scan.
|
|
213
215
|
- **Using `--files`**: If you specify `--files '["package.json"]'`, the CLI will check if this file exists and is a manifest file before determining scan type.
|
|
214
216
|
- **Using `--ignore-commit-files`**: This forces a full scan of all manifest files in the target path, regardless of what's in your commit.
|
|
217
|
+
- **Using `--enable-diff`**: Forces diff mode without SCM integration - useful when you want differential scanning but are using `--integration api`. For example: `socketcli --integration api --enable-diff --target-path /path/to/repo`
|
|
215
218
|
- **Auto-detection**: Most CI/CD scenarios now work with just `socketcli --target-path /path/to/repo --scm github --pr-number $PR_NUM`
|
|
216
219
|
|
|
217
220
|
## Debugging and Troubleshooting
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "socketsecurity"
|
|
9
|
-
version = "2.
|
|
9
|
+
version = "2.2.0"
|
|
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
|
-
'socket-sdk-python>=2.1.
|
|
19
|
+
'socket-sdk-python>=2.1.8,<3'
|
|
20
20
|
]
|
|
21
21
|
readme = "README.md"
|
|
22
22
|
description = "Socket Security CLI for CI/CD"
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__author__ = 'socket.dev'
|
|
2
|
-
__version__ = '2.
|
|
2
|
+
__version__ = '2.2.0'
|
|
@@ -48,6 +48,7 @@ class CliConfig:
|
|
|
48
48
|
integration_type: IntegrationType = "api"
|
|
49
49
|
integration_org_slug: Optional[str] = None
|
|
50
50
|
pending_head: bool = False
|
|
51
|
+
enable_diff: bool = False
|
|
51
52
|
timeout: Optional[int] = 1200
|
|
52
53
|
exclude_license_details: bool = False
|
|
53
54
|
include_module_folders: bool = False
|
|
@@ -421,6 +422,12 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
421
422
|
action="store_true",
|
|
422
423
|
help=argparse.SUPPRESS
|
|
423
424
|
)
|
|
425
|
+
advanced_group.add_argument(
|
|
426
|
+
"--enable-diff",
|
|
427
|
+
dest="enable_diff",
|
|
428
|
+
action="store_true",
|
|
429
|
+
help="Enable diff mode even when using --integration api (forces diff mode without SCM integration)"
|
|
430
|
+
)
|
|
424
431
|
advanced_group.add_argument(
|
|
425
432
|
"--scm",
|
|
426
433
|
metavar="<type>",
|
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
import sys
|
|
4
4
|
import tarfile
|
|
5
|
+
import tempfile
|
|
5
6
|
import time
|
|
6
7
|
import io
|
|
7
8
|
import json
|
|
@@ -30,7 +31,6 @@ from socketsecurity.core.exceptions import APIResourceNotFound
|
|
|
30
31
|
from .socket_config import SocketConfig
|
|
31
32
|
from .utils import socket_globs
|
|
32
33
|
from .resource_utils import check_file_count_against_ulimit
|
|
33
|
-
from .lazy_file_loader import load_files_for_sending_lazy
|
|
34
34
|
import importlib
|
|
35
35
|
logging_std = importlib.import_module("logging")
|
|
36
36
|
|
|
@@ -338,10 +338,10 @@ class Core:
|
|
|
338
338
|
ulimit_check = check_file_count_against_ulimit(file_count)
|
|
339
339
|
if ulimit_check["can_check"]:
|
|
340
340
|
if ulimit_check["would_exceed"]:
|
|
341
|
-
log.
|
|
342
|
-
log.
|
|
343
|
-
log.
|
|
344
|
-
log.
|
|
341
|
+
log.debug(f"Found {file_count} manifest files, which may exceed the file descriptor limit (ulimit -n = {ulimit_check['soft_limit']})")
|
|
342
|
+
log.debug(f"Available file descriptors: {ulimit_check['available_fds']} (after {ulimit_check['buffer_size']} buffer)")
|
|
343
|
+
log.debug(f"Recommendation: {ulimit_check['recommendation']}")
|
|
344
|
+
log.debug("This may cause 'Too many open files' errors during processing")
|
|
345
345
|
else:
|
|
346
346
|
log.debug(f"File count ({file_count}) is within file descriptor limit ({ulimit_check['soft_limit']})")
|
|
347
347
|
else:
|
|
@@ -434,37 +434,29 @@ class Core:
|
|
|
434
434
|
return ''.join(f'[{char.lower()}{char.upper()}]' if char.isalpha() else char for char in input_string)
|
|
435
435
|
|
|
436
436
|
@staticmethod
|
|
437
|
-
def empty_head_scan_file() ->
|
|
438
|
-
# Create an empty file for when no head full scan so that the diff endpoint can always be used
|
|
439
|
-
empty_file_obj = io.BytesIO(b"")
|
|
440
|
-
empty_filename = "initial_head_scan"
|
|
441
|
-
empty_full_scan_file = [(empty_filename, (empty_filename, empty_file_obj))]
|
|
442
|
-
return empty_full_scan_file
|
|
443
|
-
|
|
444
|
-
@staticmethod
|
|
445
|
-
def load_files_for_sending(files: List[str], workspace: str) -> List[Tuple[str, Tuple[str, BinaryIO]]]:
|
|
437
|
+
def empty_head_scan_file() -> List[str]:
|
|
446
438
|
"""
|
|
447
|
-
|
|
439
|
+
Creates a temporary empty file for baseline scans when no head scan exists.
|
|
448
440
|
|
|
449
|
-
This version uses lazy file loading to prevent "Too many open files" errors
|
|
450
|
-
when processing large numbers of manifest files.
|
|
451
|
-
|
|
452
|
-
Args:
|
|
453
|
-
files: List of file paths from find_files()
|
|
454
|
-
workspace: Base directory path to make paths relative to
|
|
455
|
-
|
|
456
441
|
Returns:
|
|
457
|
-
List
|
|
458
|
-
[(field_name, (filename, file_object)), ...]
|
|
442
|
+
List containing path to a temporary empty file
|
|
459
443
|
"""
|
|
460
|
-
|
|
444
|
+
# Create a temporary empty file
|
|
445
|
+
temp_fd, temp_path = tempfile.mkstemp(suffix='.empty', prefix='socket_baseline_')
|
|
446
|
+
|
|
447
|
+
# Close the file descriptor since we just need the path
|
|
448
|
+
# The file is already created and empty
|
|
449
|
+
os.close(temp_fd)
|
|
450
|
+
|
|
451
|
+
log.debug(f"Created temporary empty file for baseline scan: {temp_path}")
|
|
452
|
+
return [temp_path]
|
|
461
453
|
|
|
462
|
-
def create_full_scan(self, files:
|
|
454
|
+
def create_full_scan(self, files: List[str], params: FullScanParams) -> FullScan:
|
|
463
455
|
"""
|
|
464
456
|
Creates a new full scan via the Socket API.
|
|
465
457
|
|
|
466
458
|
Args:
|
|
467
|
-
files: List of
|
|
459
|
+
files: List of file paths to scan
|
|
468
460
|
params: Parameters for the full scan
|
|
469
461
|
|
|
470
462
|
Returns:
|
|
@@ -473,7 +465,7 @@ class Core:
|
|
|
473
465
|
log.info("Creating new full scan")
|
|
474
466
|
create_full_start = time.time()
|
|
475
467
|
|
|
476
|
-
res = self.sdk.fullscans.post(files, params, use_types=True)
|
|
468
|
+
res = self.sdk.fullscans.post(files, params, use_types=True, use_lazy_loading=True, max_open_files=50)
|
|
477
469
|
if not res.success:
|
|
478
470
|
log.error(f"Error creating full scan: {res.message}, status: {res.status}")
|
|
479
471
|
raise Exception(f"Error creating full scan: {res.message}, status: {res.status}")
|
|
@@ -525,14 +517,13 @@ class Core:
|
|
|
525
517
|
if save_manifest_tar_path and files:
|
|
526
518
|
self.save_manifest_tar(files, save_manifest_tar_path, path)
|
|
527
519
|
|
|
528
|
-
files_for_sending = self.load_files_for_sending(files, path)
|
|
529
520
|
if not files:
|
|
530
521
|
return diff
|
|
531
522
|
|
|
532
523
|
try:
|
|
533
524
|
# Create new scan
|
|
534
525
|
new_scan_start = time.time()
|
|
535
|
-
new_full_scan = self.create_full_scan(
|
|
526
|
+
new_full_scan = self.create_full_scan(files, params)
|
|
536
527
|
new_scan_end = time.time()
|
|
537
528
|
log.info(f"Total time to create new full scan: {new_scan_end - new_scan_start:.2f}")
|
|
538
529
|
except APIFailure as e:
|
|
@@ -779,7 +770,15 @@ class Core:
|
|
|
779
770
|
log.info(f"Comparing scans - Head scan ID: {head_full_scan_id}, New scan ID: {new_full_scan_id}")
|
|
780
771
|
diff_start = time.time()
|
|
781
772
|
try:
|
|
782
|
-
diff_report =
|
|
773
|
+
diff_report = (
|
|
774
|
+
self.sdk.fullscans.stream_diff
|
|
775
|
+
(
|
|
776
|
+
self.config.org_slug,
|
|
777
|
+
head_full_scan_id,
|
|
778
|
+
new_full_scan_id,
|
|
779
|
+
use_types=True
|
|
780
|
+
).data
|
|
781
|
+
)
|
|
783
782
|
except APIFailure as e:
|
|
784
783
|
log.error(f"API Error: {e}")
|
|
785
784
|
sys.exit(1)
|
|
@@ -877,7 +876,6 @@ class Core:
|
|
|
877
876
|
if save_manifest_tar_path and files:
|
|
878
877
|
self.save_manifest_tar(files, save_manifest_tar_path, path)
|
|
879
878
|
|
|
880
|
-
files_for_sending = self.load_files_for_sending(files, path)
|
|
881
879
|
if not files:
|
|
882
880
|
return Diff(id="NO_DIFF_RAN", diff_url="", report_url="")
|
|
883
881
|
|
|
@@ -887,7 +885,9 @@ class Core:
|
|
|
887
885
|
except APIResourceNotFound:
|
|
888
886
|
head_full_scan_id = None
|
|
889
887
|
|
|
888
|
+
# If no head scan exists, create an empty baseline scan
|
|
890
889
|
if head_full_scan_id is None:
|
|
890
|
+
log.info("No previous scan found - creating empty baseline scan")
|
|
891
891
|
new_params = copy.deepcopy(params.__dict__)
|
|
892
892
|
new_params.pop('include_license_details')
|
|
893
893
|
tmp_params = FullScanParams(**new_params)
|
|
@@ -895,13 +895,34 @@ class Core:
|
|
|
895
895
|
tmp_params.tmp = True
|
|
896
896
|
tmp_params.set_as_pending_head = False
|
|
897
897
|
tmp_params.make_default_branch = False
|
|
898
|
-
|
|
899
|
-
|
|
898
|
+
|
|
899
|
+
# Create baseline scan with empty file
|
|
900
|
+
empty_files = Core.empty_head_scan_file()
|
|
901
|
+
try:
|
|
902
|
+
head_full_scan = self.create_full_scan(empty_files, tmp_params)
|
|
903
|
+
head_full_scan_id = head_full_scan.id
|
|
904
|
+
log.debug(f"Created empty baseline scan: {head_full_scan_id}")
|
|
905
|
+
|
|
906
|
+
# Clean up the temporary empty file
|
|
907
|
+
for temp_file in empty_files:
|
|
908
|
+
try:
|
|
909
|
+
os.unlink(temp_file)
|
|
910
|
+
log.debug(f"Cleaned up temporary file: {temp_file}")
|
|
911
|
+
except OSError as e:
|
|
912
|
+
log.warning(f"Failed to clean up temporary file {temp_file}: {e}")
|
|
913
|
+
except Exception as e:
|
|
914
|
+
# Clean up temp files even if scan creation fails
|
|
915
|
+
for temp_file in empty_files:
|
|
916
|
+
try:
|
|
917
|
+
os.unlink(temp_file)
|
|
918
|
+
except OSError:
|
|
919
|
+
pass
|
|
920
|
+
raise e
|
|
900
921
|
|
|
901
922
|
# Create new scan
|
|
902
923
|
try:
|
|
903
924
|
new_scan_start = time.time()
|
|
904
|
-
new_full_scan = self.create_full_scan(
|
|
925
|
+
new_full_scan = self.create_full_scan(files, params)
|
|
905
926
|
new_scan_end = time.time()
|
|
906
927
|
log.info(f"Total time to create new full scan: {new_scan_end - new_scan_start:.2f}")
|
|
907
928
|
except APIFailure as e:
|
|
@@ -913,6 +934,7 @@ class Core:
|
|
|
913
934
|
log.error(f"Stack trace:\n{traceback.format_exc()}")
|
|
914
935
|
raise
|
|
915
936
|
|
|
937
|
+
# Handle diff generation - now we always have both scans
|
|
916
938
|
scans_ready = self.check_full_scans_status(head_full_scan_id, new_full_scan.id)
|
|
917
939
|
if scans_ready is False:
|
|
918
940
|
log.error(f"Full scans did not complete within {self.config.timeout} seconds")
|
|
@@ -1134,6 +1156,12 @@ class Core:
|
|
|
1134
1156
|
alert = Alert(**alert_item)
|
|
1135
1157
|
props = getattr(self.config.all_issues, alert.type, default_props)
|
|
1136
1158
|
introduced_by = self.get_source_data(package, packages)
|
|
1159
|
+
|
|
1160
|
+
# Handle special case for license policy violations
|
|
1161
|
+
title = props.title
|
|
1162
|
+
if alert.type == "licenseSpdxDisj" and not title:
|
|
1163
|
+
title = "License Policy Violation"
|
|
1164
|
+
|
|
1137
1165
|
issue_alert = Issue(
|
|
1138
1166
|
pkg_type=package.type,
|
|
1139
1167
|
pkg_name=package.name,
|
|
@@ -1144,7 +1172,7 @@ class Core:
|
|
|
1144
1172
|
type=alert.type,
|
|
1145
1173
|
severity=alert.severity,
|
|
1146
1174
|
description=props.description,
|
|
1147
|
-
title=
|
|
1175
|
+
title=title,
|
|
1148
1176
|
suggestion=props.suggestion,
|
|
1149
1177
|
next_step_title=props.nextStepTitle,
|
|
1150
1178
|
introduced_by=introduced_by,
|
|
@@ -1156,11 +1184,10 @@ class Core:
|
|
|
1156
1184
|
action = self.config.security_policy[alert.type]['action']
|
|
1157
1185
|
setattr(issue_alert, action, True)
|
|
1158
1186
|
|
|
1159
|
-
if issue_alert.
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
alerts_collection[issue_alert.key].append(issue_alert)
|
|
1187
|
+
if issue_alert.key not in alerts_collection:
|
|
1188
|
+
alerts_collection[issue_alert.key] = [issue_alert]
|
|
1189
|
+
else:
|
|
1190
|
+
alerts_collection[issue_alert.key].append(issue_alert)
|
|
1164
1191
|
|
|
1165
1192
|
return alerts_collection
|
|
1166
1193
|
|
|
@@ -1232,7 +1259,8 @@ class Core:
|
|
|
1232
1259
|
if alert_key not in removed_package_alerts:
|
|
1233
1260
|
new_alerts = added_package_alerts[alert_key]
|
|
1234
1261
|
for alert in new_alerts:
|
|
1235
|
-
|
|
1262
|
+
# Consolidate by package and alert type, not by manifest details
|
|
1263
|
+
alert_str = f"{alert.purl},{alert.type}"
|
|
1236
1264
|
|
|
1237
1265
|
if alert.error or alert.warn:
|
|
1238
1266
|
if alert_str not in consolidated_alerts:
|
|
@@ -1243,7 +1271,8 @@ class Core:
|
|
|
1243
1271
|
removed_alerts = removed_package_alerts[alert_key]
|
|
1244
1272
|
|
|
1245
1273
|
for alert in new_alerts:
|
|
1246
|
-
|
|
1274
|
+
# Consolidate by package and alert type, not by manifest details
|
|
1275
|
+
alert_str = f"{alert.purl},{alert.type}"
|
|
1247
1276
|
|
|
1248
1277
|
# Only add if:
|
|
1249
1278
|
# 1. Alert isn't in removed packages (or we're not ignoring readded alerts)
|
|
@@ -12,6 +12,7 @@ class Git:
|
|
|
12
12
|
|
|
13
13
|
def __init__(self, path: str):
|
|
14
14
|
self.path = path
|
|
15
|
+
self.ensure_safe_directory(path)
|
|
15
16
|
self.repo = Repo(path)
|
|
16
17
|
assert self.repo
|
|
17
18
|
self.head = self.repo.head
|
|
@@ -318,6 +319,67 @@ class Git:
|
|
|
318
319
|
"""Return commit SHA as a string"""
|
|
319
320
|
return self.commit.hexsha
|
|
320
321
|
|
|
322
|
+
def get_formatted_committer(self) -> str:
|
|
323
|
+
"""
|
|
324
|
+
Get the committer in the preferred order:
|
|
325
|
+
1. CLI --committers (handled in socketcli.py)
|
|
326
|
+
2. CI/CD SCM username (GitHub/GitLab/BitBucket environment variables)
|
|
327
|
+
3. Git username (extracted from email patterns like GitHub noreply)
|
|
328
|
+
4. Git email address
|
|
329
|
+
5. Git author name (fallback)
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
Formatted committer string
|
|
333
|
+
"""
|
|
334
|
+
# Check for CI/CD environment usernames first
|
|
335
|
+
# GitHub Actions
|
|
336
|
+
github_actor = os.getenv('GITHUB_ACTOR')
|
|
337
|
+
if github_actor:
|
|
338
|
+
log.debug(f"Using GitHub actor as committer: {github_actor}")
|
|
339
|
+
return github_actor
|
|
340
|
+
|
|
341
|
+
# GitLab CI
|
|
342
|
+
gitlab_user_login = os.getenv('GITLAB_USER_LOGIN')
|
|
343
|
+
if gitlab_user_login:
|
|
344
|
+
log.debug(f"Using GitLab user login as committer: {gitlab_user_login}")
|
|
345
|
+
return gitlab_user_login
|
|
346
|
+
|
|
347
|
+
# Bitbucket Pipelines
|
|
348
|
+
bitbucket_step_triggerer_uuid = os.getenv('BITBUCKET_STEP_TRIGGERER_UUID')
|
|
349
|
+
if bitbucket_step_triggerer_uuid:
|
|
350
|
+
log.debug(f"Using Bitbucket step triggerer as committer: {bitbucket_step_triggerer_uuid}")
|
|
351
|
+
return bitbucket_step_triggerer_uuid
|
|
352
|
+
|
|
353
|
+
# Fall back to commit author/committer details
|
|
354
|
+
# Priority 3: Try to extract git username from email patterns first
|
|
355
|
+
if self.author and self.author.email and self.author.email.strip():
|
|
356
|
+
email = self.author.email.strip()
|
|
357
|
+
|
|
358
|
+
# If it's a GitHub noreply email, try to extract username
|
|
359
|
+
if email.endswith('@users.noreply.github.com'):
|
|
360
|
+
# Pattern: number+username@users.noreply.github.com
|
|
361
|
+
email_parts = email.split('@')[0]
|
|
362
|
+
if '+' in email_parts:
|
|
363
|
+
username = email_parts.split('+')[1]
|
|
364
|
+
log.debug(f"Extracted GitHub username from noreply email: {username}")
|
|
365
|
+
return username
|
|
366
|
+
|
|
367
|
+
# Priority 4: Use email if available
|
|
368
|
+
if self.author and self.author.email and self.author.email.strip():
|
|
369
|
+
email = self.author.email.strip()
|
|
370
|
+
log.debug(f"Using commit author email as committer: {email}")
|
|
371
|
+
return email
|
|
372
|
+
|
|
373
|
+
# Priority 5: Fall back to author name as last resort
|
|
374
|
+
if self.author and self.author.name and self.author.name.strip():
|
|
375
|
+
name = self.author.name.strip()
|
|
376
|
+
log.debug(f"Using commit author name as fallback committer: {name}")
|
|
377
|
+
return name
|
|
378
|
+
|
|
379
|
+
# Ultimate fallback
|
|
380
|
+
log.debug("Using fallback committer: unknown")
|
|
381
|
+
return "unknown"
|
|
382
|
+
|
|
321
383
|
def get_default_branch_name(self) -> str:
|
|
322
384
|
"""
|
|
323
385
|
Get the default branch name from the remote origin.
|
|
@@ -409,3 +471,24 @@ class Git:
|
|
|
409
471
|
except Exception as error:
|
|
410
472
|
log.debug(f"Error checking if on default branch: {error}")
|
|
411
473
|
return False
|
|
474
|
+
|
|
475
|
+
@staticmethod
|
|
476
|
+
def ensure_safe_directory(path: str) -> None:
|
|
477
|
+
# Ensure the repo is marked as safe for git (prevents SHA empty/dubious ownership errors)
|
|
478
|
+
try :
|
|
479
|
+
import subprocess
|
|
480
|
+
abs_path = os.path.abspath(path)
|
|
481
|
+
# Get all safe directories
|
|
482
|
+
result = subprocess.run([
|
|
483
|
+
"git", "config", "--global", "--get-all", "safe.directory"
|
|
484
|
+
], capture_output=True, text=True)
|
|
485
|
+
safe_dirs = result.stdout.splitlines() if result.returncode == 0 else []
|
|
486
|
+
if abs_path not in safe_dirs:
|
|
487
|
+
subprocess.run([
|
|
488
|
+
"git", "config", "--global", "--add", "safe.directory", abs_path
|
|
489
|
+
], check=True)
|
|
490
|
+
log.debug(f"Added {abs_path} to git safe.directory config.")
|
|
491
|
+
else:
|
|
492
|
+
log.debug(f"{abs_path} already present in git safe.directory config.")
|
|
493
|
+
except Exception as safe_error:
|
|
494
|
+
log.debug(f"Failed to set safe.directory for git: {safe_error}")
|
|
@@ -309,13 +309,26 @@ class Messages:
|
|
|
309
309
|
:param diff: Diff - Contains the detected vulnerabilities and warnings.
|
|
310
310
|
:return: str - The formatted Markdown/HTML string.
|
|
311
311
|
"""
|
|
312
|
+
# Group license policy violations by PURL (ecosystem/package@version)
|
|
313
|
+
license_groups = {}
|
|
314
|
+
security_alerts = []
|
|
315
|
+
|
|
316
|
+
for alert in diff.new_alerts:
|
|
317
|
+
if alert.type == "licenseSpdxDisj":
|
|
318
|
+
purl_key = f"{alert.pkg_type}/{alert.pkg_name}@{alert.pkg_version}"
|
|
319
|
+
if purl_key not in license_groups:
|
|
320
|
+
license_groups[purl_key] = []
|
|
321
|
+
license_groups[purl_key].append(alert)
|
|
322
|
+
else:
|
|
323
|
+
security_alerts.append(alert)
|
|
324
|
+
|
|
312
325
|
# Start of the comment
|
|
313
326
|
comment = """<!-- socket-security-comment-actions -->
|
|
314
327
|
|
|
315
328
|
> **❗️ Caution**
|
|
316
329
|
> **Review the following alerts detected in dependencies.**
|
|
317
330
|
>
|
|
318
|
-
> According to your organization
|
|
331
|
+
> According to your organization's Security Policy, you **must** resolve all **"Block"** alerts before proceeding. It's recommended to resolve **"Warn"** alerts too.
|
|
319
332
|
> Learn more about [Socket for GitHub](https://socket.dev?utm_medium=gh).
|
|
320
333
|
|
|
321
334
|
<!-- start-socket-updated-alerts-table -->
|
|
@@ -330,8 +343,8 @@ class Messages:
|
|
|
330
343
|
<tbody>
|
|
331
344
|
"""
|
|
332
345
|
|
|
333
|
-
# Loop through alerts, dynamically generating rows
|
|
334
|
-
for alert in
|
|
346
|
+
# Loop through security alerts (non-license), dynamically generating rows
|
|
347
|
+
for alert in security_alerts:
|
|
335
348
|
severity_icon = Messages.get_severity_icon(alert.severity)
|
|
336
349
|
action = "Block" if alert.error else "Warn"
|
|
337
350
|
details_open = ""
|
|
@@ -365,7 +378,48 @@ class Messages:
|
|
|
365
378
|
<!-- end-socket-alert-{alert.pkg_name}@{alert.pkg_version} -->
|
|
366
379
|
"""
|
|
367
380
|
|
|
368
|
-
#
|
|
381
|
+
# Add license policy violation entries grouped by PURL
|
|
382
|
+
for purl_key, alerts in license_groups.items():
|
|
383
|
+
action = "Block" if any(alert.error for alert in alerts) else "Warn"
|
|
384
|
+
first_alert = alerts[0]
|
|
385
|
+
|
|
386
|
+
# Use orange diamond for license policy violations
|
|
387
|
+
license_icon = "🔶"
|
|
388
|
+
|
|
389
|
+
# Build license findings list
|
|
390
|
+
license_findings = []
|
|
391
|
+
for alert in alerts:
|
|
392
|
+
license_findings.append(alert.title)
|
|
393
|
+
|
|
394
|
+
comment += f"""
|
|
395
|
+
<!-- start-socket-alert-{first_alert.pkg_name}@{first_alert.pkg_version} -->
|
|
396
|
+
<tr>
|
|
397
|
+
<td><strong>{action}</strong></td>
|
|
398
|
+
<td align="center">{license_icon}</td>
|
|
399
|
+
<td>
|
|
400
|
+
<details>
|
|
401
|
+
<summary>{first_alert.pkg_name}@{first_alert.pkg_version} has a License Policy Violation.</summary>
|
|
402
|
+
<p><strong>License findings:</strong></p>
|
|
403
|
+
<ul>
|
|
404
|
+
"""
|
|
405
|
+
for finding in license_findings:
|
|
406
|
+
comment += f" <li>{finding}</li>\n"
|
|
407
|
+
|
|
408
|
+
comment += f""" </ul>
|
|
409
|
+
<p><strong>From:</strong> {first_alert.manifests}</p>
|
|
410
|
+
<p>ℹ️ Read more on: <a href="{first_alert.purl}">This package</a> | <a href="https://socket.dev/alerts/license">What is a license policy violation?</a></p>
|
|
411
|
+
<blockquote>
|
|
412
|
+
<p><em>Next steps:</em> Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at <strong>support@socket.dev</strong>.</p>
|
|
413
|
+
<p><em>Suggestion:</em> Find a package that does not violate your license policy or adjust your policy to allow this package's license.</p>
|
|
414
|
+
<p><em>Mark the package as acceptable risk:</em> To ignore this alert only in this pull request, reply with the comment <code>@SocketSecurity ignore {first_alert.pkg_name}@{first_alert.pkg_version}</code>. You can also ignore all packages with <code>@SocketSecurity ignore-all</code>. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.</p>
|
|
415
|
+
</blockquote>
|
|
416
|
+
</details>
|
|
417
|
+
</td>
|
|
418
|
+
</tr>
|
|
419
|
+
<!-- end-socket-alert-{first_alert.pkg_name}@{first_alert.pkg_version} -->
|
|
420
|
+
"""
|
|
421
|
+
|
|
422
|
+
# Close table
|
|
369
423
|
comment += """
|
|
370
424
|
</tbody>
|
|
371
425
|
</table>
|
|
@@ -125,7 +125,7 @@ def main_code():
|
|
|
125
125
|
if not config.branch:
|
|
126
126
|
config.branch = git_repo.branch
|
|
127
127
|
if not config.committers:
|
|
128
|
-
config.committers = [git_repo.
|
|
128
|
+
config.committers = [git_repo.get_formatted_committer()]
|
|
129
129
|
if not config.commit_message:
|
|
130
130
|
config.commit_message = git_repo.commit_message
|
|
131
131
|
except InvalidGitRepositoryError:
|
|
@@ -320,6 +320,33 @@ def main_code():
|
|
|
320
320
|
diff = core.create_new_diff(config.target_path, params, no_change=should_skip_scan, save_files_list_path=config.save_submitted_files_list, save_manifest_tar_path=config.save_manifest_tar)
|
|
321
321
|
|
|
322
322
|
output_handler.handle_output(diff)
|
|
323
|
+
|
|
324
|
+
elif config.enable_diff and not force_api_mode:
|
|
325
|
+
# New logic: --enable-diff forces diff mode even with --integration api (no SCM)
|
|
326
|
+
log.info("Diff mode enabled without SCM integration")
|
|
327
|
+
diff = core.create_new_diff(config.target_path, params, no_change=should_skip_scan, save_files_list_path=config.save_submitted_files_list, save_manifest_tar_path=config.save_manifest_tar)
|
|
328
|
+
output_handler.handle_output(diff)
|
|
329
|
+
|
|
330
|
+
elif config.enable_diff and force_api_mode:
|
|
331
|
+
# User requested diff mode but no manifest files were detected
|
|
332
|
+
log.warning("--enable-diff was specified but no supported manifest files were detected in the changed files. Falling back to full scan mode.")
|
|
333
|
+
log.info("Creating Socket Report (full scan)")
|
|
334
|
+
serializable_params = {
|
|
335
|
+
key: value if isinstance(value, (int, float, str, list, dict, bool, type(None))) else str(value)
|
|
336
|
+
for key, value in params.__dict__.items()
|
|
337
|
+
}
|
|
338
|
+
log.debug(f"params={serializable_params}")
|
|
339
|
+
diff = core.create_full_scan_with_report_url(
|
|
340
|
+
config.target_path,
|
|
341
|
+
params,
|
|
342
|
+
no_change=should_skip_scan,
|
|
343
|
+
save_files_list_path=config.save_submitted_files_list,
|
|
344
|
+
save_manifest_tar_path=config.save_manifest_tar
|
|
345
|
+
)
|
|
346
|
+
log.info(f"Full scan created with ID: {diff.id}")
|
|
347
|
+
log.info(f"Full scan report URL: {diff.report_url}")
|
|
348
|
+
output_handler.handle_output(diff)
|
|
349
|
+
|
|
323
350
|
else:
|
|
324
351
|
if force_api_mode:
|
|
325
352
|
log.info("No Manifest files changed, creating Socket Report")
|
|
@@ -24,8 +24,20 @@ class TestCliConfig:
|
|
|
24
24
|
@pytest.mark.parametrize("flag,attr", [
|
|
25
25
|
("--enable-debug", "enable_debug"),
|
|
26
26
|
("--disable-blocking", "disable_blocking"),
|
|
27
|
-
("--allow-unverified", "allow_unverified")
|
|
27
|
+
("--allow-unverified", "allow_unverified"),
|
|
28
|
+
("--enable-diff", "enable_diff")
|
|
28
29
|
])
|
|
29
30
|
def test_boolean_flags(self, flag, attr):
|
|
30
31
|
config = CliConfig.from_args(["--api-token", "test", flag])
|
|
31
|
-
assert getattr(config, attr) is True
|
|
32
|
+
assert getattr(config, attr) is True
|
|
33
|
+
|
|
34
|
+
def test_enable_diff_default_false(self):
|
|
35
|
+
"""Test that enable_diff defaults to False"""
|
|
36
|
+
config = CliConfig.from_args(["--api-token", "test"])
|
|
37
|
+
assert config.enable_diff is False
|
|
38
|
+
|
|
39
|
+
def test_enable_diff_with_integration_api(self):
|
|
40
|
+
"""Test that enable_diff can be used with integration api"""
|
|
41
|
+
config = CliConfig.from_args(["--api-token", "test", "--integration", "api", "--enable-diff"])
|
|
42
|
+
assert config.enable_diff is True
|
|
43
|
+
assert config.integration_type == "api"
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/diff/stream_diff_full.json
RENAMED
|
File without changes
|
|
File without changes
|
{socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/head_scan/stream_scan.json
RENAMED
|
File without changes
|
{socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/head_scan/stream_scan_full.json
RENAMED
|
File without changes
|
|
File without changes
|
{socketsecurity-2.1.33 → socketsecurity-2.2.0}/tests/data/fullscans/new_scan/stream_scan.json
RENAMED
|
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
|