socketsecurity 2.2.71__tar.gz → 2.2.75__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.75/.github/CODEOWNERS +1 -0
- socketsecurity-2.2.75/.github/workflows/e2e-test.yml +109 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.github/workflows/pr-preview.yml +2 -1
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.github/workflows/release.yml +2 -1
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/PKG-INFO +20 -11
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/README.md +18 -9
- socketsecurity-2.2.75/instructions/gitlab-commit-status/uat.md +54 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/pyproject.toml +2 -2
- socketsecurity-2.2.75/socket.yml +4 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/__init__.py +1 -1
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/config.py +22 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/scm/gitlab.py +67 -1
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/socketcli.py +17 -1
- socketsecurity-2.2.75/tests/e2e/fixtures/simple-npm/index.js +13 -0
- socketsecurity-2.2.75/tests/e2e/fixtures/simple-npm/package.json +15 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/unit/test_cli_config.py +22 -1
- socketsecurity-2.2.75/tests/unit/test_gitlab_commit_status.py +184 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/uv.lock +54 -54
- socketsecurity-2.2.71/.github/CODEOWNERS +0 -1
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.github/workflows/docker-stable.yml +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.github/workflows/version-check.yml +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.gitignore +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.hooks/sync_version.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.pre-commit-config.yaml +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/.python-version +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/CHANGELOG.md +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/Dockerfile +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/LICENSE +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/Makefile +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/docs/README.md +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/pytest.ini +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/scripts/build_container.sh +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/scripts/build_container_flexible.sh +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/scripts/deploy-test-docker.sh +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/scripts/deploy-test-pypi.sh +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/scripts/docker-entrypoint.sh +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/scripts/run.sh +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/session.md +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/__init__.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/cli_client.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/helper/__init__.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/lazy_file_loader.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/logging.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/messages.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/resource_utils.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/scm/__init__.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/scm/base.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/scm/client.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/scm/github.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/socket_config.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/tools/reachability.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/core/utils.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/output.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/plugins/__init__.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/plugins/base.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/plugins/formatters/__init__.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/plugins/formatters/slack.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/plugins/jira.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/plugins/manager.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/plugins/slack.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/plugins/teams.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/socketsecurity/plugins/webhook.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/__init__.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/core/conftest.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/core/create_diff_input.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/core/test_diff_alerts.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/core/test_diff_generation.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/core/test_package_and_alerts.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/core/test_sdk_methods.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/core/test_supporting_methods.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/fullscans/create_response.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/fullscans/diff/stream_diff.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/fullscans/head_scan/metadata.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/fullscans/new_scan/metadata.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/repos/repo_info_error.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/repos/repo_info_no_head.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/repos/repo_info_success.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/data/settings/security-policy.json +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/unit/__init__.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/unit/test_client.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/unit/test_config.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/unit/test_gitlab_auth.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/unit/test_gitlab_auth_fallback.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/unit/test_gitlab_format.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/tests/unit/test_output.py +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/workflows/bitbucket-pipelines.yml +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/workflows/github-actions.yml +0 -0
- {socketsecurity-2.2.71 → socketsecurity-2.2.75}/workflows/gitlab-ci.yml +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @SocketDev/customer-engineering
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: E2E Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
e2e-scan:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871
|
|
13
|
+
with:
|
|
14
|
+
fetch-depth: 0
|
|
15
|
+
|
|
16
|
+
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
|
|
17
|
+
with:
|
|
18
|
+
python-version: '3.12'
|
|
19
|
+
|
|
20
|
+
- name: Install CLI from local repo
|
|
21
|
+
run: |
|
|
22
|
+
python -m pip install --upgrade pip
|
|
23
|
+
pip install .
|
|
24
|
+
|
|
25
|
+
- name: Run Socket CLI scan
|
|
26
|
+
env:
|
|
27
|
+
SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_CLI_API_TOKEN }}
|
|
28
|
+
run: |
|
|
29
|
+
set -o pipefail
|
|
30
|
+
socketcli \
|
|
31
|
+
--target-path tests/e2e/fixtures/simple-npm \
|
|
32
|
+
--disable-blocking \
|
|
33
|
+
--enable-debug \
|
|
34
|
+
2>&1 | tee /tmp/scan-output.log
|
|
35
|
+
|
|
36
|
+
- name: Verify scan produced a report
|
|
37
|
+
run: |
|
|
38
|
+
if grep -q "Full scan report URL: https://socket.dev/" /tmp/scan-output.log; then
|
|
39
|
+
echo "PASS: Full scan report URL found"
|
|
40
|
+
grep "Full scan report URL:" /tmp/scan-output.log
|
|
41
|
+
elif grep -q "Diff Url: https://socket.dev/" /tmp/scan-output.log; then
|
|
42
|
+
echo "PASS: Diff URL found"
|
|
43
|
+
grep "Diff Url:" /tmp/scan-output.log
|
|
44
|
+
else
|
|
45
|
+
echo "FAIL: No report URL found in scan output"
|
|
46
|
+
cat /tmp/scan-output.log
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
e2e-reachability:
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871
|
|
54
|
+
with:
|
|
55
|
+
fetch-depth: 0
|
|
56
|
+
|
|
57
|
+
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
|
|
58
|
+
with:
|
|
59
|
+
python-version: '3.12'
|
|
60
|
+
|
|
61
|
+
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
|
62
|
+
with:
|
|
63
|
+
node-version: '20'
|
|
64
|
+
|
|
65
|
+
- name: Install CLI from local repo
|
|
66
|
+
run: |
|
|
67
|
+
python -m pip install --upgrade pip
|
|
68
|
+
pip install .
|
|
69
|
+
|
|
70
|
+
- name: Install uv
|
|
71
|
+
run: pip install uv
|
|
72
|
+
|
|
73
|
+
- name: Run Socket CLI with reachability
|
|
74
|
+
env:
|
|
75
|
+
SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_CLI_API_TOKEN }}
|
|
76
|
+
run: |
|
|
77
|
+
set -o pipefail
|
|
78
|
+
socketcli \
|
|
79
|
+
--target-path tests/e2e/fixtures/simple-npm \
|
|
80
|
+
--reach \
|
|
81
|
+
--disable-blocking \
|
|
82
|
+
--enable-debug \
|
|
83
|
+
2>&1 | tee /tmp/reach-output.log
|
|
84
|
+
|
|
85
|
+
- name: Verify reachability analysis completed
|
|
86
|
+
run: |
|
|
87
|
+
if grep -q "Reachability analysis completed successfully" /tmp/reach-output.log; then
|
|
88
|
+
echo "PASS: Reachability analysis completed"
|
|
89
|
+
grep "Reachability analysis completed successfully" /tmp/reach-output.log
|
|
90
|
+
grep "Results written to:" /tmp/reach-output.log || true
|
|
91
|
+
else
|
|
92
|
+
echo "FAIL: Reachability analysis did not complete successfully"
|
|
93
|
+
cat /tmp/reach-output.log
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
- name: Verify scan produced a report
|
|
98
|
+
run: |
|
|
99
|
+
if grep -q "Full scan report URL: https://socket.dev/" /tmp/reach-output.log; then
|
|
100
|
+
echo "PASS: Full scan report URL found"
|
|
101
|
+
grep "Full scan report URL:" /tmp/reach-output.log
|
|
102
|
+
elif grep -q "Diff Url: https://socket.dev/" /tmp/reach-output.log; then
|
|
103
|
+
echo "PASS: Diff URL found"
|
|
104
|
+
grep "Diff Url:" /tmp/reach-output.log
|
|
105
|
+
else
|
|
106
|
+
echo "FAIL: No report URL found in scan output"
|
|
107
|
+
cat /tmp/reach-output.log
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
@@ -16,12 +16,13 @@ jobs:
|
|
|
16
16
|
fetch-depth: 0
|
|
17
17
|
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
|
|
18
18
|
with:
|
|
19
|
-
python-version: '3.
|
|
19
|
+
python-version: '3.13'
|
|
20
20
|
|
|
21
21
|
# Install all dependencies from pyproject.toml
|
|
22
22
|
- name: Install dependencies
|
|
23
23
|
run: |
|
|
24
24
|
python -m pip install --upgrade pip
|
|
25
|
+
pip install "virtualenv<20.36"
|
|
25
26
|
pip install hatchling==1.27.0 hatch==1.14.0
|
|
26
27
|
|
|
27
28
|
- name: Inject full dynamic version
|
|
@@ -15,12 +15,13 @@ jobs:
|
|
|
15
15
|
fetch-depth: 0
|
|
16
16
|
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
|
|
17
17
|
with:
|
|
18
|
-
python-version: '3.
|
|
18
|
+
python-version: '3.13'
|
|
19
19
|
|
|
20
20
|
# Install all dependencies from pyproject.toml
|
|
21
21
|
- name: Install dependencies
|
|
22
22
|
run: |
|
|
23
23
|
python -m pip install --upgrade pip
|
|
24
|
+
pip install "virtualenv<20.36"
|
|
24
25
|
pip install hatchling==1.27.0 hatch==1.14.0
|
|
25
26
|
|
|
26
27
|
- name: Get Version
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: socketsecurity
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.75
|
|
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>
|
|
@@ -41,7 +41,7 @@ Requires-Dist: packaging
|
|
|
41
41
|
Requires-Dist: prettytable
|
|
42
42
|
Requires-Dist: python-dotenv
|
|
43
43
|
Requires-Dist: requests
|
|
44
|
-
Requires-Dist: socketdev<4.0.0,>=3.0.
|
|
44
|
+
Requires-Dist: socketdev<4.0.0,>=3.0.32
|
|
45
45
|
Provides-Extra: dev
|
|
46
46
|
Requires-Dist: hatch; extra == 'dev'
|
|
47
47
|
Requires-Dist: pre-commit; extra == 'dev'
|
|
@@ -101,6 +101,8 @@ These examples are production-ready and include best practices for each platform
|
|
|
101
101
|
|
|
102
102
|
## Monorepo Workspace Support
|
|
103
103
|
|
|
104
|
+
> **Note:** If you're looking to associate a scan with a named Socket workspace (e.g. because your repo is identified as `org/repo`), see the [`--workspace` flag](#repository) instead. The `--workspace-name` flag described in this section is an unrelated monorepo feature.
|
|
105
|
+
|
|
104
106
|
The Socket CLI supports scanning specific workspaces within monorepo structures while preserving git context from the repository root. This is useful for organizations that maintain multiple applications or services in a single repository.
|
|
105
107
|
|
|
106
108
|
### Key Features
|
|
@@ -172,7 +174,7 @@ This will simultaneously generate:
|
|
|
172
174
|
## Usage
|
|
173
175
|
|
|
174
176
|
```` shell
|
|
175
|
-
socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--repo-is-public] [--branch BRANCH] [--integration {api,github,gitlab,azure,bitbucket}]
|
|
177
|
+
socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--workspace WORKSPACE] [--repo-is-public] [--branch BRANCH] [--integration {api,github,gitlab,azure,bitbucket}]
|
|
176
178
|
[--owner OWNER] [--pr-number PR_NUMBER] [--commit-message COMMIT_MESSAGE] [--commit-sha COMMIT_SHA] [--committers [COMMITTERS ...]]
|
|
177
179
|
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--license-file-name LICENSE_FILE_NAME] [--save-submitted-files-list SAVE_SUBMITTED_FILES_LIST]
|
|
178
180
|
[--save-manifest-tar SAVE_MANIFEST_TAR] [--files FILES] [--sub-path SUB_PATH] [--workspace-name WORKSPACE_NAME]
|
|
@@ -196,14 +198,21 @@ If you don't want to provide the Socket API Token every time then you can use th
|
|
|
196
198
|
| --api-token | False | | Socket Security API token (can also be set via SOCKET_SECURITY_API_TOKEN env var) |
|
|
197
199
|
|
|
198
200
|
#### Repository
|
|
199
|
-
| Parameter | Required | Default | Description
|
|
200
|
-
|
|
201
|
-
| --repo | False | *auto* | Repository name in owner/repo format (auto-detected from git remote)
|
|
202
|
-
| --
|
|
203
|
-
| --
|
|
204
|
-
| --
|
|
205
|
-
| --
|
|
206
|
-
| --
|
|
201
|
+
| Parameter | Required | Default | Description |
|
|
202
|
+
|:-----------------|:---------|:--------|:------------------------------------------------------------------------------------------------------------------|
|
|
203
|
+
| --repo | False | *auto* | Repository name in owner/repo format (auto-detected from git remote) |
|
|
204
|
+
| --workspace | False | | The Socket workspace to associate the scan with (e.g. `my-org` in `my-org/my-repo`). See note below. |
|
|
205
|
+
| --repo-is-public | False | False | If set, flags a new repository creation as public. Defaults to false. |
|
|
206
|
+
| --integration | False | api | Integration type (api, github, gitlab, azure, bitbucket) |
|
|
207
|
+
| --owner | False | | Name of the integration owner, defaults to the socket organization slug |
|
|
208
|
+
| --branch | False | *auto* | Branch name (auto-detected from git) |
|
|
209
|
+
| --committers | False | *auto* | Committer(s) to filter by (auto-detected from git commit) |
|
|
210
|
+
|
|
211
|
+
> **`--workspace` vs `--workspace-name`** — these are two distinct flags for different purposes:
|
|
212
|
+
>
|
|
213
|
+
> - **`--workspace <string>`** maps to the Socket API's `workspace` query parameter on `CreateOrgFullScan`. Use it when your repository belongs to a named Socket workspace (e.g. an org with multiple workspace groups). Example: `--repo my-repo --workspace my-org`. Without this flag, scans are created without workspace context and may not appear under the correct workspace in the Socket dashboard.
|
|
214
|
+
>
|
|
215
|
+
> - **`--workspace-name <string>`** is a monorepo feature. It appends a suffix to the repository slug to create a unique name in Socket (e.g. `my-repo-frontend`). It must always be paired with `--sub-path` and has nothing to do with the API `workspace` field. See [Monorepo Workspace Support](#monorepo-workspace-support) below.
|
|
207
216
|
|
|
208
217
|
#### Pull Request and Commit
|
|
209
218
|
| Parameter | Required | Default | Description |
|
|
@@ -43,6 +43,8 @@ These examples are production-ready and include best practices for each platform
|
|
|
43
43
|
|
|
44
44
|
## Monorepo Workspace Support
|
|
45
45
|
|
|
46
|
+
> **Note:** If you're looking to associate a scan with a named Socket workspace (e.g. because your repo is identified as `org/repo`), see the [`--workspace` flag](#repository) instead. The `--workspace-name` flag described in this section is an unrelated monorepo feature.
|
|
47
|
+
|
|
46
48
|
The Socket CLI supports scanning specific workspaces within monorepo structures while preserving git context from the repository root. This is useful for organizations that maintain multiple applications or services in a single repository.
|
|
47
49
|
|
|
48
50
|
### Key Features
|
|
@@ -114,7 +116,7 @@ This will simultaneously generate:
|
|
|
114
116
|
## Usage
|
|
115
117
|
|
|
116
118
|
```` shell
|
|
117
|
-
socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--repo-is-public] [--branch BRANCH] [--integration {api,github,gitlab,azure,bitbucket}]
|
|
119
|
+
socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--workspace WORKSPACE] [--repo-is-public] [--branch BRANCH] [--integration {api,github,gitlab,azure,bitbucket}]
|
|
118
120
|
[--owner OWNER] [--pr-number PR_NUMBER] [--commit-message COMMIT_MESSAGE] [--commit-sha COMMIT_SHA] [--committers [COMMITTERS ...]]
|
|
119
121
|
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--license-file-name LICENSE_FILE_NAME] [--save-submitted-files-list SAVE_SUBMITTED_FILES_LIST]
|
|
120
122
|
[--save-manifest-tar SAVE_MANIFEST_TAR] [--files FILES] [--sub-path SUB_PATH] [--workspace-name WORKSPACE_NAME]
|
|
@@ -138,14 +140,21 @@ If you don't want to provide the Socket API Token every time then you can use th
|
|
|
138
140
|
| --api-token | False | | Socket Security API token (can also be set via SOCKET_SECURITY_API_TOKEN env var) |
|
|
139
141
|
|
|
140
142
|
#### Repository
|
|
141
|
-
| Parameter | Required | Default | Description
|
|
142
|
-
|
|
143
|
-
| --repo | False | *auto* | Repository name in owner/repo format (auto-detected from git remote)
|
|
144
|
-
| --
|
|
145
|
-
| --
|
|
146
|
-
| --
|
|
147
|
-
| --
|
|
148
|
-
| --
|
|
143
|
+
| Parameter | Required | Default | Description |
|
|
144
|
+
|:-----------------|:---------|:--------|:------------------------------------------------------------------------------------------------------------------|
|
|
145
|
+
| --repo | False | *auto* | Repository name in owner/repo format (auto-detected from git remote) |
|
|
146
|
+
| --workspace | False | | The Socket workspace to associate the scan with (e.g. `my-org` in `my-org/my-repo`). See note below. |
|
|
147
|
+
| --repo-is-public | False | False | If set, flags a new repository creation as public. Defaults to false. |
|
|
148
|
+
| --integration | False | api | Integration type (api, github, gitlab, azure, bitbucket) |
|
|
149
|
+
| --owner | False | | Name of the integration owner, defaults to the socket organization slug |
|
|
150
|
+
| --branch | False | *auto* | Branch name (auto-detected from git) |
|
|
151
|
+
| --committers | False | *auto* | Committer(s) to filter by (auto-detected from git commit) |
|
|
152
|
+
|
|
153
|
+
> **`--workspace` vs `--workspace-name`** — these are two distinct flags for different purposes:
|
|
154
|
+
>
|
|
155
|
+
> - **`--workspace <string>`** maps to the Socket API's `workspace` query parameter on `CreateOrgFullScan`. Use it when your repository belongs to a named Socket workspace (e.g. an org with multiple workspace groups). Example: `--repo my-repo --workspace my-org`. Without this flag, scans are created without workspace context and may not appear under the correct workspace in the Socket dashboard.
|
|
156
|
+
>
|
|
157
|
+
> - **`--workspace-name <string>`** is a monorepo feature. It appends a suffix to the repository slug to create a unique name in Socket (e.g. `my-repo-frontend`). It must always be paired with `--sub-path` and has nothing to do with the API `workspace` field. See [Monorepo Workspace Support](#monorepo-workspace-support) below.
|
|
149
158
|
|
|
150
159
|
#### Pull Request and Commit
|
|
151
160
|
| Parameter | Required | Default | Description |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# UAT: GitLab Commit Status Integration
|
|
2
|
+
|
|
3
|
+
## Feature
|
|
4
|
+
`--enable-commit-status` posts a commit status (`success`/`failed`) to GitLab after scan completes. Repo admins can then require `socket-security` as a status check on protected branches.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
- GitLab project with CI/CD configured
|
|
8
|
+
- `GITLAB_TOKEN` with `api` scope (or `CI_JOB_TOKEN` with sufficient permissions)
|
|
9
|
+
- Merge request pipeline (so `CI_MERGE_REQUEST_PROJECT_ID` is set)
|
|
10
|
+
|
|
11
|
+
## Test Cases
|
|
12
|
+
|
|
13
|
+
### 1. Pass scenario (no blocking alerts)
|
|
14
|
+
1. Create MR with no dependency changes (or only safe ones)
|
|
15
|
+
2. Run: `socketcli --scm gitlab --enable-commit-status`
|
|
16
|
+
3. **Expected**: Commit status `socket-security` = `success`, description = "No blocking issues"
|
|
17
|
+
4. Verify in GitLab: **Repository > Commits > (sha) > Pipelines** or **MR > Pipeline > External** tab
|
|
18
|
+
|
|
19
|
+
### 2. Fail scenario (blocking alerts)
|
|
20
|
+
1. Create MR adding a package with known blocking alerts
|
|
21
|
+
2. Run: `socketcli --scm gitlab --enable-commit-status`
|
|
22
|
+
3. **Expected**: Commit status = `failed`, description = "N blocking alert(s) found"
|
|
23
|
+
|
|
24
|
+
### 3. Flag omitted (default off)
|
|
25
|
+
1. Run: `socketcli --scm gitlab` (no `--enable-commit-status`)
|
|
26
|
+
2. **Expected**: No commit status posted
|
|
27
|
+
|
|
28
|
+
### 4. Non-MR pipeline (push event without MR)
|
|
29
|
+
1. Trigger pipeline on a push (no MR context)
|
|
30
|
+
2. Run: `socketcli --scm gitlab --enable-commit-status`
|
|
31
|
+
3. **Expected**: Commit status skipped (no `mr_project_id`), no error
|
|
32
|
+
|
|
33
|
+
### 5. API failure is non-fatal
|
|
34
|
+
1. Use an invalid/revoked `GITLAB_TOKEN`
|
|
35
|
+
2. Run: `socketcli --scm gitlab --enable-commit-status`
|
|
36
|
+
3. **Expected**: Error logged ("Failed to set commit status: ..."), scan still completes with correct exit code
|
|
37
|
+
|
|
38
|
+
### 6. Non-GitLab SCM
|
|
39
|
+
1. Run: `socketcli --scm github --enable-commit-status`
|
|
40
|
+
2. **Expected**: Flag is accepted but commit status is not posted (GitHub not yet supported)
|
|
41
|
+
|
|
42
|
+
## Blocking Merges on Failure
|
|
43
|
+
|
|
44
|
+
### Option A: Pipelines must succeed (all GitLab tiers)
|
|
45
|
+
Since `socketcli` exits with code 1 when blocking alerts are found, the pipeline fails automatically.
|
|
46
|
+
1. Go to **Settings > General > Merge requests**
|
|
47
|
+
2. Under **Merge checks**, enable **"Pipelines must succeed"**
|
|
48
|
+
3. Save — GitLab will now prevent merging when the pipeline fails
|
|
49
|
+
|
|
50
|
+
### Option B: External status checks (GitLab Ultimate only)
|
|
51
|
+
Use the `socket-security` commit status as a required external check.
|
|
52
|
+
1. Go to **Settings > General > Merge requests > Status checks**
|
|
53
|
+
2. Add an external status check with name `socket-security`
|
|
54
|
+
3. MRs will require Socket's `success` status to merge
|
|
@@ -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.75"
|
|
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.32,<4.0.0",
|
|
20
20
|
"bs4>=0.0.2",
|
|
21
21
|
"markdown>=3.10",
|
|
22
22
|
]
|
|
@@ -66,6 +66,7 @@ class CliConfig:
|
|
|
66
66
|
save_manifest_tar: Optional[str] = None
|
|
67
67
|
sub_paths: List[str] = field(default_factory=list)
|
|
68
68
|
workspace_name: Optional[str] = None
|
|
69
|
+
workspace: Optional[str] = None
|
|
69
70
|
# Reachability Flags
|
|
70
71
|
reach: bool = False
|
|
71
72
|
reach_version: Optional[str] = None
|
|
@@ -86,6 +87,7 @@ class CliConfig:
|
|
|
86
87
|
only_facts_file: bool = False
|
|
87
88
|
reach_use_only_pregenerated_sboms: bool = False
|
|
88
89
|
max_purl_batch_size: int = 5000
|
|
90
|
+
enable_commit_status: bool = False
|
|
89
91
|
|
|
90
92
|
@classmethod
|
|
91
93
|
def from_args(cls, args_list: Optional[List[str]] = None) -> 'CliConfig':
|
|
@@ -144,6 +146,7 @@ class CliConfig:
|
|
|
144
146
|
'save_manifest_tar': args.save_manifest_tar,
|
|
145
147
|
'sub_paths': args.sub_paths or [],
|
|
146
148
|
'workspace_name': args.workspace_name,
|
|
149
|
+
'workspace': args.workspace,
|
|
147
150
|
'slack_webhook': args.slack_webhook,
|
|
148
151
|
'reach': args.reach,
|
|
149
152
|
'reach_version': args.reach_version,
|
|
@@ -164,6 +167,7 @@ class CliConfig:
|
|
|
164
167
|
'only_facts_file': args.only_facts_file,
|
|
165
168
|
'reach_use_only_pregenerated_sboms': args.reach_use_only_pregenerated_sboms,
|
|
166
169
|
'max_purl_batch_size': args.max_purl_batch_size,
|
|
170
|
+
'enable_commit_status': args.enable_commit_status,
|
|
167
171
|
'version': __version__
|
|
168
172
|
}
|
|
169
173
|
try:
|
|
@@ -254,6 +258,12 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
254
258
|
help="Repository name in owner/repo format",
|
|
255
259
|
required=False
|
|
256
260
|
)
|
|
261
|
+
repo_group.add_argument(
|
|
262
|
+
"--workspace",
|
|
263
|
+
metavar="<string>",
|
|
264
|
+
help="The workspace in the Socket Organization that the repository is in to associate with the full scan.",
|
|
265
|
+
required=False
|
|
266
|
+
)
|
|
257
267
|
repo_group.add_argument(
|
|
258
268
|
"--repo-is-public",
|
|
259
269
|
dest="repo_is_public",
|
|
@@ -512,6 +522,18 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
512
522
|
action="store_true",
|
|
513
523
|
help=argparse.SUPPRESS
|
|
514
524
|
)
|
|
525
|
+
output_group.add_argument(
|
|
526
|
+
"--enable-commit-status",
|
|
527
|
+
dest="enable_commit_status",
|
|
528
|
+
action="store_true",
|
|
529
|
+
help="Report scan result as a commit status on GitLab (requires GitLab SCM)"
|
|
530
|
+
)
|
|
531
|
+
output_group.add_argument(
|
|
532
|
+
"--enable_commit_status",
|
|
533
|
+
dest="enable_commit_status",
|
|
534
|
+
action="store_true",
|
|
535
|
+
help=argparse.SUPPRESS
|
|
536
|
+
)
|
|
515
537
|
|
|
516
538
|
# Plugin Configuration
|
|
517
539
|
plugin_group = parser.add_argument_group('Plugin Configuration')
|
|
@@ -47,8 +47,15 @@ class GitlabConfig:
|
|
|
47
47
|
# Determine which authentication pattern to use
|
|
48
48
|
headers = cls._get_auth_headers(token)
|
|
49
49
|
|
|
50
|
+
# Prefer source branch SHA (real commit) over CI_COMMIT_SHA which
|
|
51
|
+
# may be a synthetic merge-result commit in merged-results pipelines.
|
|
52
|
+
commit_sha = (
|
|
53
|
+
os.getenv('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') or
|
|
54
|
+
os.getenv('CI_COMMIT_SHA', '')
|
|
55
|
+
)
|
|
56
|
+
|
|
50
57
|
return cls(
|
|
51
|
-
commit_sha=
|
|
58
|
+
commit_sha=commit_sha,
|
|
52
59
|
api_url=os.getenv('CI_API_V4_URL', ''),
|
|
53
60
|
project_dir=os.getenv('CI_PROJECT_DIR', ''),
|
|
54
61
|
mr_source_branch=mr_source_branch,
|
|
@@ -260,6 +267,65 @@ class Gitlab:
|
|
|
260
267
|
log.debug("No Previous version of Security Issue comment, posting")
|
|
261
268
|
self.post_comment(security_comment)
|
|
262
269
|
|
|
270
|
+
def enable_merge_pipeline_check(self) -> None:
|
|
271
|
+
"""Enable 'only_allow_merge_if_pipeline_succeeds' on the MR target project."""
|
|
272
|
+
if not self.config.mr_project_id:
|
|
273
|
+
return
|
|
274
|
+
url = f"{self.config.api_url}/projects/{self.config.mr_project_id}"
|
|
275
|
+
try:
|
|
276
|
+
resp = requests.put(
|
|
277
|
+
url,
|
|
278
|
+
json={"only_allow_merge_if_pipeline_succeeds": True},
|
|
279
|
+
headers=self.config.headers,
|
|
280
|
+
)
|
|
281
|
+
if resp.status_code == 401:
|
|
282
|
+
fallback = self._get_fallback_headers(self.config.headers)
|
|
283
|
+
if fallback:
|
|
284
|
+
resp = requests.put(
|
|
285
|
+
url,
|
|
286
|
+
json={"only_allow_merge_if_pipeline_succeeds": True},
|
|
287
|
+
headers=fallback,
|
|
288
|
+
)
|
|
289
|
+
if resp.status_code >= 400:
|
|
290
|
+
log.error(f"GitLab enable merge check API {resp.status_code}: {resp.text}")
|
|
291
|
+
else:
|
|
292
|
+
log.info("Enabled 'pipelines must succeed' merge check on project")
|
|
293
|
+
except Exception as e:
|
|
294
|
+
log.error(f"Failed to enable merge pipeline check: {e}")
|
|
295
|
+
|
|
296
|
+
def set_commit_status(self, state: str, description: str, target_url: str = '') -> None:
|
|
297
|
+
"""Post a commit status to GitLab. state should be 'success' or 'failed'.
|
|
298
|
+
|
|
299
|
+
Uses requests.post with json= directly because CliClient.request sends
|
|
300
|
+
data= (form-encoded) which GitLab's commit status endpoint rejects.
|
|
301
|
+
"""
|
|
302
|
+
if not self.config.mr_project_id:
|
|
303
|
+
log.debug("No mr_project_id, skipping commit status")
|
|
304
|
+
return
|
|
305
|
+
url = f"{self.config.api_url}/projects/{self.config.mr_project_id}/statuses/{self.config.commit_sha}"
|
|
306
|
+
payload = {
|
|
307
|
+
"state": state,
|
|
308
|
+
"context": "socket-security-commit-status",
|
|
309
|
+
"description": description,
|
|
310
|
+
}
|
|
311
|
+
if self.config.mr_source_branch:
|
|
312
|
+
payload["ref"] = self.config.mr_source_branch
|
|
313
|
+
if target_url:
|
|
314
|
+
payload["target_url"] = target_url
|
|
315
|
+
try:
|
|
316
|
+
log.debug(f"Posting commit status to {url}")
|
|
317
|
+
resp = requests.post(url, json=payload, headers=self.config.headers)
|
|
318
|
+
if resp.status_code == 401:
|
|
319
|
+
fallback = self._get_fallback_headers(self.config.headers)
|
|
320
|
+
if fallback:
|
|
321
|
+
resp = requests.post(url, json=payload, headers=fallback)
|
|
322
|
+
if resp.status_code >= 400:
|
|
323
|
+
log.error(f"GitLab commit status API {resp.status_code}: {resp.text}")
|
|
324
|
+
resp.raise_for_status()
|
|
325
|
+
log.info(f"Commit status set to '{state}' on {self.config.commit_sha[:8]}")
|
|
326
|
+
except Exception as e:
|
|
327
|
+
log.error(f"Failed to set commit status: {e}")
|
|
328
|
+
|
|
263
329
|
def remove_comment_alerts(self, comments: dict):
|
|
264
330
|
security_alert = comments.get("security")
|
|
265
331
|
if security_alert is not None:
|
|
@@ -464,7 +464,8 @@ def main_code():
|
|
|
464
464
|
make_default_branch=is_default_branch,
|
|
465
465
|
set_as_pending_head=is_default_branch,
|
|
466
466
|
tmp=False,
|
|
467
|
-
scan_type='socket_tier1' if config.reach else 'socket'
|
|
467
|
+
scan_type='socket_tier1' if config.reach else 'socket',
|
|
468
|
+
workspace=config.workspace or None,
|
|
468
469
|
)
|
|
469
470
|
|
|
470
471
|
params.include_license_details = not config.exclude_license_details
|
|
@@ -641,6 +642,21 @@ def main_code():
|
|
|
641
642
|
log.debug("Temporarily enabling disable_blocking due to no supported manifest files")
|
|
642
643
|
config.disable_blocking = True
|
|
643
644
|
|
|
645
|
+
# Post commit status to GitLab if enabled
|
|
646
|
+
if config.enable_commit_status and scm is not None:
|
|
647
|
+
from socketsecurity.core.scm.gitlab import Gitlab
|
|
648
|
+
if isinstance(scm, Gitlab) and scm.config.mr_project_id:
|
|
649
|
+
scm.enable_merge_pipeline_check()
|
|
650
|
+
passed = output_handler.report_pass(diff)
|
|
651
|
+
state = "success" if passed else "failed"
|
|
652
|
+
blocking_count = sum(1 for a in diff.new_alerts if a.error)
|
|
653
|
+
if passed:
|
|
654
|
+
description = "No blocking issues"
|
|
655
|
+
else:
|
|
656
|
+
description = f"{blocking_count} blocking alert(s) found"
|
|
657
|
+
target_url = diff.report_url or diff.diff_url or ""
|
|
658
|
+
scm.set_commit_status(state, description, target_url)
|
|
659
|
+
|
|
644
660
|
sys.exit(output_handler.return_exit_code(diff))
|
|
645
661
|
|
|
646
662
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const express = require('express')
|
|
2
|
+
const lodash = require('lodash')
|
|
3
|
+
|
|
4
|
+
const app = express()
|
|
5
|
+
|
|
6
|
+
app.get('/', (req, res) => {
|
|
7
|
+
const data = lodash.pick(req.query, ['name', 'age'])
|
|
8
|
+
res.json(data)
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
app.listen(3000, () => {
|
|
12
|
+
console.log(`Test fixture ${__filename} running on port 3000`)
|
|
13
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "reach-test-fixture",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Test fixture for reachability analysis",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"lodash": "4.17.23",
|
|
8
|
+
"express": "4.22.0",
|
|
9
|
+
"axios": "1.13.5"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"typescript": "5.0.4",
|
|
13
|
+
"jest": "29.5.0"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -60,4 +60,25 @@ class TestCliConfig:
|
|
|
60
60
|
"--disable-blocking"
|
|
61
61
|
])
|
|
62
62
|
assert config.strict_blocking is True
|
|
63
|
-
assert config.disable_blocking is True
|
|
63
|
+
assert config.disable_blocking is True
|
|
64
|
+
|
|
65
|
+
def test_workspace_flag(self):
|
|
66
|
+
"""Test that --workspace is parsed and stored correctly."""
|
|
67
|
+
config = CliConfig.from_args(["--api-token", "test", "--workspace", "my-workspace"])
|
|
68
|
+
assert config.workspace == "my-workspace"
|
|
69
|
+
|
|
70
|
+
def test_workspace_default_is_none(self):
|
|
71
|
+
"""Test that workspace defaults to None when not supplied."""
|
|
72
|
+
config = CliConfig.from_args(["--api-token", "test"])
|
|
73
|
+
assert config.workspace is None
|
|
74
|
+
|
|
75
|
+
def test_workspace_is_independent_of_workspace_name(self):
|
|
76
|
+
"""--workspace and --workspace-name are distinct flags with distinct purposes."""
|
|
77
|
+
config = CliConfig.from_args([
|
|
78
|
+
"--api-token", "test",
|
|
79
|
+
"--workspace", "my-workspace",
|
|
80
|
+
"--sub-path", ".",
|
|
81
|
+
"--workspace-name", "monorepo-suffix",
|
|
82
|
+
])
|
|
83
|
+
assert config.workspace == "my-workspace"
|
|
84
|
+
assert config.workspace_name == "monorepo-suffix"
|