socketsecurity 2.2.71__tar.gz → 2.2.74__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.
Files changed (101) hide show
  1. socketsecurity-2.2.74/.github/CODEOWNERS +1 -0
  2. socketsecurity-2.2.74/.github/workflows/e2e-test.yml +109 -0
  3. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.github/workflows/pr-preview.yml +2 -1
  4. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.github/workflows/release.yml +2 -1
  5. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/PKG-INFO +20 -11
  6. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/README.md +18 -9
  7. socketsecurity-2.2.74/instructions/gitlab-commit-status/uat.md +54 -0
  8. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/pyproject.toml +2 -2
  9. socketsecurity-2.2.74/socket.yml +4 -0
  10. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/__init__.py +1 -1
  11. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/config.py +22 -0
  12. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/scm/gitlab.py +67 -1
  13. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/socketcli.py +17 -1
  14. socketsecurity-2.2.74/tests/e2e/fixtures/simple-npm/index.js +13 -0
  15. socketsecurity-2.2.74/tests/e2e/fixtures/simple-npm/package.json +15 -0
  16. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/unit/test_cli_config.py +22 -1
  17. socketsecurity-2.2.74/tests/unit/test_gitlab_commit_status.py +184 -0
  18. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/uv.lock +54 -54
  19. socketsecurity-2.2.71/.github/CODEOWNERS +0 -1
  20. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
  21. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  22. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
  23. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  24. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.github/workflows/docker-stable.yml +0 -0
  25. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.github/workflows/version-check.yml +0 -0
  26. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.gitignore +0 -0
  27. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.hooks/sync_version.py +0 -0
  28. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.pre-commit-config.yaml +0 -0
  29. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/.python-version +0 -0
  30. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/CHANGELOG.md +0 -0
  31. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/Dockerfile +0 -0
  32. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/LICENSE +0 -0
  33. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/Makefile +0 -0
  34. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/docs/README.md +0 -0
  35. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/pytest.ini +0 -0
  36. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/scripts/build_container.sh +0 -0
  37. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/scripts/build_container_flexible.sh +0 -0
  38. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/scripts/deploy-test-docker.sh +0 -0
  39. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/scripts/deploy-test-pypi.sh +0 -0
  40. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/scripts/docker-entrypoint.sh +0 -0
  41. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/scripts/run.sh +0 -0
  42. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/session.md +0 -0
  43. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/__init__.py +0 -0
  44. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/classes.py +0 -0
  45. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/cli_client.py +0 -0
  46. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/exceptions.py +0 -0
  47. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/git_interface.py +0 -0
  48. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/helper/__init__.py +0 -0
  49. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
  50. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/lazy_file_loader.py +0 -0
  51. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/logging.py +0 -0
  52. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/messages.py +0 -0
  53. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/resource_utils.py +0 -0
  54. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/scm/__init__.py +0 -0
  55. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/scm/base.py +0 -0
  56. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/scm/client.py +0 -0
  57. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/scm/github.py +0 -0
  58. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/scm_comments.py +0 -0
  59. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/socket_config.py +0 -0
  60. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/tools/reachability.py +0 -0
  61. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/core/utils.py +0 -0
  62. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/output.py +0 -0
  63. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/plugins/__init__.py +0 -0
  64. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/plugins/base.py +0 -0
  65. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/plugins/formatters/__init__.py +0 -0
  66. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/plugins/formatters/slack.py +0 -0
  67. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/plugins/jira.py +0 -0
  68. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/plugins/manager.py +0 -0
  69. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/plugins/slack.py +0 -0
  70. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/plugins/teams.py +0 -0
  71. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/socketsecurity/plugins/webhook.py +0 -0
  72. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/__init__.py +0 -0
  73. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/core/conftest.py +0 -0
  74. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/core/create_diff_input.json +0 -0
  75. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/core/test_diff_alerts.py +0 -0
  76. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/core/test_diff_generation.py +0 -0
  77. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/core/test_package_and_alerts.py +0 -0
  78. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/core/test_sdk_methods.py +0 -0
  79. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/core/test_supporting_methods.py +0 -0
  80. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/fullscans/create_response.json +0 -0
  81. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/fullscans/diff/stream_diff.json +0 -0
  82. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
  83. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/fullscans/head_scan/metadata.json +0 -0
  84. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
  85. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
  86. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/fullscans/new_scan/metadata.json +0 -0
  87. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
  88. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/repos/repo_info_error.json +0 -0
  89. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/repos/repo_info_no_head.json +0 -0
  90. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/repos/repo_info_success.json +0 -0
  91. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/data/settings/security-policy.json +0 -0
  92. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/unit/__init__.py +0 -0
  93. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/unit/test_client.py +0 -0
  94. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/unit/test_config.py +0 -0
  95. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/unit/test_gitlab_auth.py +0 -0
  96. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/unit/test_gitlab_auth_fallback.py +0 -0
  97. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/unit/test_gitlab_format.py +0 -0
  98. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/tests/unit/test_output.py +0 -0
  99. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/workflows/bitbucket-pipelines.yml +0 -0
  100. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/workflows/github-actions.yml +0 -0
  101. {socketsecurity-2.2.71 → socketsecurity-2.2.74}/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.x'
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.x'
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.71
3
+ Version: 2.2.74
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.29
44
+ Requires-Dist: socketdev<4.0.0,>=3.0.31
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
- | --repo-is-public | False | False | If set, flags a new repository creation as public. Defaults to false. |
203
- | --integration | False | api | Integration type (api, github, gitlab, azure, bitbucket) |
204
- | --owner | False | | Name of the integration owner, defaults to the socket organization slug |
205
- | --branch | False | *auto* | Branch name (auto-detected from git) |
206
- | --committers | False | *auto* | Committer(s) to filter by (auto-detected from git commit) |
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
- | --repo-is-public | False | False | If set, flags a new repository creation as public. Defaults to false. |
145
- | --integration | False | api | Integration type (api, github, gitlab, azure, bitbucket) |
146
- | --owner | False | | Name of the integration owner, defaults to the socket organization slug |
147
- | --branch | False | *auto* | Branch name (auto-detected from git) |
148
- | --committers | False | *auto* | Committer(s) to filter by (auto-detected from git commit) |
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.71"
9
+ version = "2.2.74"
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.29,<4.0.0",
19
+ "socketdev>=3.0.31,<4.0.0",
20
20
  "bs4>=0.0.2",
21
21
  "markdown>=3.10",
22
22
  ]
@@ -0,0 +1,4 @@
1
+ version: 2
2
+
3
+ projectIgnorePaths:
4
+ - "tests/e2e/fixtures/"
@@ -1,3 +1,3 @@
1
1
  __author__ = 'socket.dev'
2
- __version__ = '2.2.71'
2
+ __version__ = '2.2.74'
3
3
  USER_AGENT = f'SocketPythonCLI/{__version__}'
@@ -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=os.getenv('CI_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"