socketsecurity 2.2.0__tar.gz → 2.2.4__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.0 → socketsecurity-2.2.4}/Dockerfile +1 -1
- socketsecurity-2.2.4/Makefile +62 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/PKG-INFO +85 -13
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/README.md +82 -10
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/pyproject.toml +3 -3
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/scripts/deploy-test-docker.sh +1 -1
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/__init__.py +1 -1
- socketsecurity-2.2.4/socketsecurity/core/scm/client.py +84 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/scm/gitlab.py +112 -8
- socketsecurity-2.2.4/tests/unit/test_gitlab_auth.py +116 -0
- socketsecurity-2.2.4/tests/unit/test_gitlab_auth_fallback.py +148 -0
- socketsecurity-2.2.4/uv.lock +1388 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/workflows/gitlab-ci.yml +3 -0
- socketsecurity-2.2.0/Makefile +0 -69
- socketsecurity-2.2.0/requirements-dev.lock +0 -73
- socketsecurity-2.2.0/requirements-dev.txt +0 -73
- socketsecurity-2.2.0/requirements.lock +0 -71
- socketsecurity-2.2.0/requirements.txt +0 -71
- socketsecurity-2.2.0/socketsecurity/core/scm/client.py +0 -41
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.github/CODEOWNERS +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.github/workflows/docker-stable.yml +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.github/workflows/pr-preview.yml +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.github/workflows/release.yml +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.github/workflows/version-check.yml +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.gitignore +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.hooks/sync_version.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.pre-commit-config.yaml +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/.python-version +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/LICENSE +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/Pipfile.lock +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/docs/README.md +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/pytest.ini +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/scripts/build_container.sh +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/scripts/deploy-test-pypi.sh +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/scripts/run.sh +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/config.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/__init__.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/cli_client.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/helper/__init__.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/lazy_file_loader.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/logging.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/messages.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/resource_utils.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/scm/__init__.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/scm/base.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/scm/github.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/socket_config.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/core/utils.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/output.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/plugins/__init__.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/plugins/base.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/plugins/jira.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/plugins/manager.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/plugins/slack.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/plugins/teams.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/plugins/webhook.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/socketsecurity/socketcli.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/__init__.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/core/conftest.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/core/create_diff_input.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/core/test_diff_generation.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/core/test_package_and_alerts.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/core/test_sdk_methods.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/core/test_supporting_methods.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/fullscans/create_response.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/fullscans/diff/stream_diff.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/fullscans/head_scan/metadata.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/fullscans/new_scan/metadata.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/repos/repo_info_error.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/repos/repo_info_no_head.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/repos/repo_info_success.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/data/settings/security-policy.json +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/unit/__init__.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/unit/test_cli_config.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/unit/test_client.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/unit/test_config.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/tests/unit/test_output.py +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/workflows/bitbucket-pipelines.yml +0 -0
- {socketsecurity-2.2.0 → socketsecurity-2.2.4}/workflows/github-actions.yml +0 -0
|
@@ -18,5 +18,5 @@ RUN for i in $(seq 1 10); do \
|
|
|
18
18
|
sleep 30; \
|
|
19
19
|
done && \
|
|
20
20
|
if [ ! -z "$SDK_VERSION" ]; then \
|
|
21
|
-
pip install --index-url ${PIP_INDEX_URL} --extra-index-url ${PIP_EXTRA_INDEX_URL}
|
|
21
|
+
pip install --index-url ${PIP_INDEX_URL} --extra-index-url ${PIP_EXTRA_INDEX_URL} socketdev==${SDK_VERSION}; \
|
|
22
22
|
fi
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
.PHONY: setup sync clean test lint update-lock local-dev first-time-setup dev-setup sync-all first-time-local-setup
|
|
2
|
+
|
|
3
|
+
# Environment variable for local SDK path (optional)
|
|
4
|
+
SOCKET_SDK_PATH ?= ../socketdev
|
|
5
|
+
|
|
6
|
+
# Environment variable to control local development mode
|
|
7
|
+
USE_LOCAL_SDK ?= false
|
|
8
|
+
|
|
9
|
+
# === High-level workflow targets ===
|
|
10
|
+
|
|
11
|
+
# First-time repo setup after cloning (using PyPI packages)
|
|
12
|
+
first-time-setup: clean setup
|
|
13
|
+
|
|
14
|
+
# First-time setup for local development (using local SDK)
|
|
15
|
+
first-time-local-setup:
|
|
16
|
+
$(MAKE) clean
|
|
17
|
+
$(MAKE) USE_LOCAL_SDK=true dev-setup
|
|
18
|
+
|
|
19
|
+
# Update lock file after changing pyproject.toml
|
|
20
|
+
update-lock:
|
|
21
|
+
uv lock
|
|
22
|
+
|
|
23
|
+
# Setup for local development
|
|
24
|
+
dev-setup: clean local-dev setup
|
|
25
|
+
|
|
26
|
+
# Sync all dependencies after pulling changes
|
|
27
|
+
sync-all: sync
|
|
28
|
+
|
|
29
|
+
# === Implementation targets ===
|
|
30
|
+
|
|
31
|
+
# Installs dependencies needed for local development
|
|
32
|
+
# Currently: socketdev from test PyPI or local path
|
|
33
|
+
local-dev:
|
|
34
|
+
ifeq ($(USE_LOCAL_SDK),true)
|
|
35
|
+
uv add --editable $(SOCKET_SDK_PATH)
|
|
36
|
+
endif
|
|
37
|
+
|
|
38
|
+
# Creates virtual environment and installs dependencies from uv.lock
|
|
39
|
+
setup: update-lock
|
|
40
|
+
uv sync --all-extras
|
|
41
|
+
ifeq ($(USE_LOCAL_SDK),true)
|
|
42
|
+
uv add --editable $(SOCKET_SDK_PATH)
|
|
43
|
+
endif
|
|
44
|
+
|
|
45
|
+
# Installs exact versions from uv.lock into your virtual environment
|
|
46
|
+
sync:
|
|
47
|
+
uv sync --all-extras
|
|
48
|
+
ifeq ($(USE_LOCAL_SDK),true)
|
|
49
|
+
uv add --editable $(SOCKET_SDK_PATH)
|
|
50
|
+
endif
|
|
51
|
+
|
|
52
|
+
# Removes virtual environment and cache files
|
|
53
|
+
clean:
|
|
54
|
+
rm -rf .venv
|
|
55
|
+
find . -type d -name "__pycache__" -exec rm -rf {} +
|
|
56
|
+
|
|
57
|
+
test:
|
|
58
|
+
uv run pytest
|
|
59
|
+
|
|
60
|
+
lint:
|
|
61
|
+
uv run ruff check .
|
|
62
|
+
uv run ruff format --check .
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: socketsecurity
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.4
|
|
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,13 +39,13 @@ Requires-Dist: packaging
|
|
|
39
39
|
Requires-Dist: prettytable
|
|
40
40
|
Requires-Dist: python-dotenv
|
|
41
41
|
Requires-Dist: requests
|
|
42
|
-
Requires-Dist:
|
|
42
|
+
Requires-Dist: socketdev<4.0.0,>=3.0.0
|
|
43
43
|
Provides-Extra: dev
|
|
44
44
|
Requires-Dist: hatch; extra == 'dev'
|
|
45
|
-
Requires-Dist: pip-tools>=7.4.0; extra == 'dev'
|
|
46
45
|
Requires-Dist: pre-commit; extra == 'dev'
|
|
47
46
|
Requires-Dist: ruff>=0.3.0; extra == 'dev'
|
|
48
47
|
Requires-Dist: twine; extra == 'dev'
|
|
48
|
+
Requires-Dist: uv>=0.1.0; extra == 'dev'
|
|
49
49
|
Provides-Extra: test
|
|
50
50
|
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'test'
|
|
51
51
|
Requires-Dist: pytest-cov>=4.1.0; extra == 'test'
|
|
@@ -235,6 +235,74 @@ The CLI uses intelligent default branch detection with the following priority:
|
|
|
235
235
|
|
|
236
236
|
Both `--default-branch` and `--pending-head` parameters are automatically synchronized to ensure consistent behavior.
|
|
237
237
|
|
|
238
|
+
## GitLab Token Configuration
|
|
239
|
+
|
|
240
|
+
The CLI supports GitLab integration with automatic authentication pattern detection for different token types.
|
|
241
|
+
|
|
242
|
+
### Supported Token Types
|
|
243
|
+
|
|
244
|
+
GitLab API supports two authentication methods, and the CLI automatically detects which one to use:
|
|
245
|
+
|
|
246
|
+
1. **Bearer Token Authentication** (`Authorization: Bearer <token>`)
|
|
247
|
+
- GitLab CI Job Tokens (`$CI_JOB_TOKEN`)
|
|
248
|
+
- Personal Access Tokens with `glpat-` prefix
|
|
249
|
+
- OAuth 2.0 tokens (long alphanumeric tokens)
|
|
250
|
+
|
|
251
|
+
2. **Private Token Authentication** (`PRIVATE-TOKEN: <token>`)
|
|
252
|
+
- Legacy personal access tokens
|
|
253
|
+
- Custom tokens that don't match Bearer patterns
|
|
254
|
+
|
|
255
|
+
### Token Detection Logic
|
|
256
|
+
|
|
257
|
+
The CLI automatically determines the authentication method using this logic:
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
if token == $CI_JOB_TOKEN:
|
|
261
|
+
use Bearer authentication
|
|
262
|
+
elif token starts with "glpat-":
|
|
263
|
+
use Bearer authentication
|
|
264
|
+
elif token is long (>40 chars) and alphanumeric:
|
|
265
|
+
use Bearer authentication
|
|
266
|
+
else:
|
|
267
|
+
use PRIVATE-TOKEN authentication
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Automatic Fallback
|
|
271
|
+
|
|
272
|
+
If the initial authentication method fails with a 401 error, the CLI automatically retries with the alternative method:
|
|
273
|
+
|
|
274
|
+
- **Bearer → PRIVATE-TOKEN**: If Bearer authentication fails, retry with PRIVATE-TOKEN
|
|
275
|
+
- **PRIVATE-TOKEN → Bearer**: If PRIVATE-TOKEN fails, retry with Bearer authentication
|
|
276
|
+
|
|
277
|
+
This ensures maximum compatibility across different GitLab configurations and token types.
|
|
278
|
+
|
|
279
|
+
### Environment Variables
|
|
280
|
+
|
|
281
|
+
| Variable | Description | Example |
|
|
282
|
+
|:---------|:------------|:--------|
|
|
283
|
+
| `GITLAB_TOKEN` | GitLab API token (required for GitLab integration) | `glpat-xxxxxxxxxxxxxxxxxxxx` |
|
|
284
|
+
| `CI_JOB_TOKEN` | GitLab CI job token (automatically used in GitLab CI) | Automatically provided by GitLab CI |
|
|
285
|
+
|
|
286
|
+
### Usage Examples
|
|
287
|
+
|
|
288
|
+
**GitLab CI with job token (recommended):**
|
|
289
|
+
```yaml
|
|
290
|
+
variables:
|
|
291
|
+
GITLAB_TOKEN: $CI_JOB_TOKEN
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**GitLab CI with personal access token:**
|
|
295
|
+
```yaml
|
|
296
|
+
variables:
|
|
297
|
+
GITLAB_TOKEN: $GITLAB_PERSONAL_ACCESS_TOKEN # Set in GitLab project/group variables
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Local development:**
|
|
301
|
+
```bash
|
|
302
|
+
export GITLAB_TOKEN="glpat-your-personal-access-token"
|
|
303
|
+
socketcli --integration gitlab --repo owner/repo --pr-number 123
|
|
304
|
+
```
|
|
305
|
+
|
|
238
306
|
### Scan Behavior
|
|
239
307
|
|
|
240
308
|
The CLI determines scanning behavior intelligently:
|
|
@@ -359,9 +427,9 @@ make first-time-setup
|
|
|
359
427
|
2. Local Development Setup (for SDK development):
|
|
360
428
|
```bash
|
|
361
429
|
pyenv local 3.11 # Ensure correct Python version
|
|
362
|
-
SOCKET_SDK_PATH=~/path/to/
|
|
430
|
+
SOCKET_SDK_PATH=~/path/to/socketdev make first-time-local-setup
|
|
363
431
|
```
|
|
364
|
-
The default SDK path is `../
|
|
432
|
+
The default SDK path is `../socketdev` if not specified.
|
|
365
433
|
|
|
366
434
|
#### Ongoing Development Tasks
|
|
367
435
|
|
|
@@ -380,20 +448,24 @@ make sync-all
|
|
|
380
448
|
High-level workflows:
|
|
381
449
|
- `make first-time-setup`: Complete setup using PyPI packages
|
|
382
450
|
- `make first-time-local-setup`: Complete setup for local SDK development
|
|
383
|
-
- `make update-
|
|
451
|
+
- `make update-lock`: Update uv.lock file after changing pyproject.toml
|
|
384
452
|
- `make sync-all`: Sync dependencies after pulling changes
|
|
385
453
|
- `make dev-setup`: Setup for local development (included in first-time-local-setup)
|
|
386
454
|
|
|
387
455
|
Implementation targets:
|
|
388
|
-
- `make init-tools`: Creates virtual environment and installs pip-tools
|
|
389
456
|
- `make local-dev`: Installs dependencies needed for local development
|
|
390
|
-
- `make
|
|
391
|
-
- `make
|
|
392
|
-
- `make sync-deps`: Installs exact versions from requirements.txt
|
|
457
|
+
- `make setup`: Creates virtual environment and installs dependencies from uv.lock
|
|
458
|
+
- `make sync`: Installs exact versions from uv.lock
|
|
393
459
|
- `make clean`: Removes virtual environment and cache files
|
|
394
|
-
- `make test`: Runs pytest suite
|
|
395
|
-
- `make lint`: Runs ruff for code formatting and linting
|
|
460
|
+
- `make test`: Runs pytest suite using uv run
|
|
461
|
+
- `make lint`: Runs ruff for code formatting and linting using uv run
|
|
396
462
|
|
|
397
463
|
### Environment Variables
|
|
398
464
|
|
|
399
|
-
|
|
465
|
+
#### Core Configuration
|
|
466
|
+
- `SOCKET_SECURITY_API_KEY`: Socket Security API token (alternative to --api-token parameter)
|
|
467
|
+
- `SOCKET_SDK_PATH`: Path to local socketdev repository (default: ../socketdev)
|
|
468
|
+
|
|
469
|
+
#### GitLab Integration
|
|
470
|
+
- `GITLAB_TOKEN`: GitLab API token for GitLab integration (supports both Bearer and PRIVATE-TOKEN authentication)
|
|
471
|
+
- `CI_JOB_TOKEN`: GitLab CI job token (automatically provided in GitLab CI environments)
|
|
@@ -179,6 +179,74 @@ The CLI uses intelligent default branch detection with the following priority:
|
|
|
179
179
|
|
|
180
180
|
Both `--default-branch` and `--pending-head` parameters are automatically synchronized to ensure consistent behavior.
|
|
181
181
|
|
|
182
|
+
## GitLab Token Configuration
|
|
183
|
+
|
|
184
|
+
The CLI supports GitLab integration with automatic authentication pattern detection for different token types.
|
|
185
|
+
|
|
186
|
+
### Supported Token Types
|
|
187
|
+
|
|
188
|
+
GitLab API supports two authentication methods, and the CLI automatically detects which one to use:
|
|
189
|
+
|
|
190
|
+
1. **Bearer Token Authentication** (`Authorization: Bearer <token>`)
|
|
191
|
+
- GitLab CI Job Tokens (`$CI_JOB_TOKEN`)
|
|
192
|
+
- Personal Access Tokens with `glpat-` prefix
|
|
193
|
+
- OAuth 2.0 tokens (long alphanumeric tokens)
|
|
194
|
+
|
|
195
|
+
2. **Private Token Authentication** (`PRIVATE-TOKEN: <token>`)
|
|
196
|
+
- Legacy personal access tokens
|
|
197
|
+
- Custom tokens that don't match Bearer patterns
|
|
198
|
+
|
|
199
|
+
### Token Detection Logic
|
|
200
|
+
|
|
201
|
+
The CLI automatically determines the authentication method using this logic:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
if token == $CI_JOB_TOKEN:
|
|
205
|
+
use Bearer authentication
|
|
206
|
+
elif token starts with "glpat-":
|
|
207
|
+
use Bearer authentication
|
|
208
|
+
elif token is long (>40 chars) and alphanumeric:
|
|
209
|
+
use Bearer authentication
|
|
210
|
+
else:
|
|
211
|
+
use PRIVATE-TOKEN authentication
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Automatic Fallback
|
|
215
|
+
|
|
216
|
+
If the initial authentication method fails with a 401 error, the CLI automatically retries with the alternative method:
|
|
217
|
+
|
|
218
|
+
- **Bearer → PRIVATE-TOKEN**: If Bearer authentication fails, retry with PRIVATE-TOKEN
|
|
219
|
+
- **PRIVATE-TOKEN → Bearer**: If PRIVATE-TOKEN fails, retry with Bearer authentication
|
|
220
|
+
|
|
221
|
+
This ensures maximum compatibility across different GitLab configurations and token types.
|
|
222
|
+
|
|
223
|
+
### Environment Variables
|
|
224
|
+
|
|
225
|
+
| Variable | Description | Example |
|
|
226
|
+
|:---------|:------------|:--------|
|
|
227
|
+
| `GITLAB_TOKEN` | GitLab API token (required for GitLab integration) | `glpat-xxxxxxxxxxxxxxxxxxxx` |
|
|
228
|
+
| `CI_JOB_TOKEN` | GitLab CI job token (automatically used in GitLab CI) | Automatically provided by GitLab CI |
|
|
229
|
+
|
|
230
|
+
### Usage Examples
|
|
231
|
+
|
|
232
|
+
**GitLab CI with job token (recommended):**
|
|
233
|
+
```yaml
|
|
234
|
+
variables:
|
|
235
|
+
GITLAB_TOKEN: $CI_JOB_TOKEN
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**GitLab CI with personal access token:**
|
|
239
|
+
```yaml
|
|
240
|
+
variables:
|
|
241
|
+
GITLAB_TOKEN: $GITLAB_PERSONAL_ACCESS_TOKEN # Set in GitLab project/group variables
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Local development:**
|
|
245
|
+
```bash
|
|
246
|
+
export GITLAB_TOKEN="glpat-your-personal-access-token"
|
|
247
|
+
socketcli --integration gitlab --repo owner/repo --pr-number 123
|
|
248
|
+
```
|
|
249
|
+
|
|
182
250
|
### Scan Behavior
|
|
183
251
|
|
|
184
252
|
The CLI determines scanning behavior intelligently:
|
|
@@ -303,9 +371,9 @@ make first-time-setup
|
|
|
303
371
|
2. Local Development Setup (for SDK development):
|
|
304
372
|
```bash
|
|
305
373
|
pyenv local 3.11 # Ensure correct Python version
|
|
306
|
-
SOCKET_SDK_PATH=~/path/to/
|
|
374
|
+
SOCKET_SDK_PATH=~/path/to/socketdev make first-time-local-setup
|
|
307
375
|
```
|
|
308
|
-
The default SDK path is `../
|
|
376
|
+
The default SDK path is `../socketdev` if not specified.
|
|
309
377
|
|
|
310
378
|
#### Ongoing Development Tasks
|
|
311
379
|
|
|
@@ -324,20 +392,24 @@ make sync-all
|
|
|
324
392
|
High-level workflows:
|
|
325
393
|
- `make first-time-setup`: Complete setup using PyPI packages
|
|
326
394
|
- `make first-time-local-setup`: Complete setup for local SDK development
|
|
327
|
-
- `make update-
|
|
395
|
+
- `make update-lock`: Update uv.lock file after changing pyproject.toml
|
|
328
396
|
- `make sync-all`: Sync dependencies after pulling changes
|
|
329
397
|
- `make dev-setup`: Setup for local development (included in first-time-local-setup)
|
|
330
398
|
|
|
331
399
|
Implementation targets:
|
|
332
|
-
- `make init-tools`: Creates virtual environment and installs pip-tools
|
|
333
400
|
- `make local-dev`: Installs dependencies needed for local development
|
|
334
|
-
- `make
|
|
335
|
-
- `make
|
|
336
|
-
- `make sync-deps`: Installs exact versions from requirements.txt
|
|
401
|
+
- `make setup`: Creates virtual environment and installs dependencies from uv.lock
|
|
402
|
+
- `make sync`: Installs exact versions from uv.lock
|
|
337
403
|
- `make clean`: Removes virtual environment and cache files
|
|
338
|
-
- `make test`: Runs pytest suite
|
|
339
|
-
- `make lint`: Runs ruff for code formatting and linting
|
|
404
|
+
- `make test`: Runs pytest suite using uv run
|
|
405
|
+
- `make lint`: Runs ruff for code formatting and linting using uv run
|
|
340
406
|
|
|
341
407
|
### Environment Variables
|
|
342
408
|
|
|
343
|
-
|
|
409
|
+
#### Core Configuration
|
|
410
|
+
- `SOCKET_SECURITY_API_KEY`: Socket Security API token (alternative to --api-token parameter)
|
|
411
|
+
- `SOCKET_SDK_PATH`: Path to local socketdev repository (default: ../socketdev)
|
|
412
|
+
|
|
413
|
+
#### GitLab Integration
|
|
414
|
+
- `GITLAB_TOKEN`: GitLab API token for GitLab integration (supports both Bearer and PRIVATE-TOKEN authentication)
|
|
415
|
+
- `CI_JOB_TOKEN`: GitLab CI job token (automatically provided in GitLab CI environments)
|
|
@@ -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.4"
|
|
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
|
-
'
|
|
19
|
+
'socketdev>=3.0.0,<4.0.0'
|
|
20
20
|
]
|
|
21
21
|
readme = "README.md"
|
|
22
22
|
description = "Socket Security CLI for CI/CD"
|
|
@@ -45,7 +45,7 @@ test = [
|
|
|
45
45
|
dev = [
|
|
46
46
|
"ruff>=0.3.0",
|
|
47
47
|
"twine", # for building
|
|
48
|
-
"
|
|
48
|
+
"uv>=0.1.0", # for dependency management
|
|
49
49
|
"pre-commit",
|
|
50
50
|
"hatch"
|
|
51
51
|
]
|
|
@@ -29,7 +29,7 @@ fi
|
|
|
29
29
|
|
|
30
30
|
if [ -z "$SDK_VERSION" ]; then
|
|
31
31
|
echo "No SDK version specified, checking TestPyPI for latest version..."
|
|
32
|
-
SDK_VERSION=$(get_latest_version "
|
|
32
|
+
SDK_VERSION=$(get_latest_version "socketdev")
|
|
33
33
|
echo "Latest SDK version on TestPyPI is: $SDK_VERSION"
|
|
34
34
|
fi
|
|
35
35
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__author__ = 'socket.dev'
|
|
2
|
-
__version__ = '2.2.
|
|
2
|
+
__version__ = '2.2.4'
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from typing import Dict
|
|
3
|
+
|
|
4
|
+
from ..cli_client import CliClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ScmClient(CliClient):
|
|
8
|
+
def __init__(self, token: str, api_url: str):
|
|
9
|
+
self.token = token
|
|
10
|
+
self.api_url = api_url
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def get_headers(self) -> Dict:
|
|
14
|
+
"""Each SCM implements its own auth headers"""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
def request(self, path: str, **kwargs):
|
|
18
|
+
"""Override base request to use SCM-specific headers and base_url"""
|
|
19
|
+
headers = kwargs.pop('headers', None) or self.get_headers()
|
|
20
|
+
return super().request(
|
|
21
|
+
path=path,
|
|
22
|
+
headers=headers,
|
|
23
|
+
base_url=self.api_url,
|
|
24
|
+
**kwargs
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
class GithubClient(ScmClient):
|
|
28
|
+
def get_headers(self) -> Dict:
|
|
29
|
+
return {
|
|
30
|
+
'Authorization': f"Bearer {self.token}",
|
|
31
|
+
'User-Agent': 'SocketPythonScript/0.0.1',
|
|
32
|
+
"accept": "application/json"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class GitlabClient(ScmClient):
|
|
36
|
+
def get_headers(self) -> Dict:
|
|
37
|
+
"""
|
|
38
|
+
Determine the appropriate authentication headers for GitLab API.
|
|
39
|
+
Uses the same logic as GitlabConfig._get_auth_headers()
|
|
40
|
+
"""
|
|
41
|
+
return self._get_gitlab_auth_headers(self.token)
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def _get_gitlab_auth_headers(token: str) -> dict:
|
|
45
|
+
"""
|
|
46
|
+
Determine the appropriate authentication headers for GitLab API.
|
|
47
|
+
|
|
48
|
+
GitLab supports two authentication patterns:
|
|
49
|
+
1. Bearer token (OAuth 2.0 tokens, personal access tokens with api scope)
|
|
50
|
+
2. Private token (personal access tokens)
|
|
51
|
+
"""
|
|
52
|
+
import os
|
|
53
|
+
|
|
54
|
+
base_headers = {
|
|
55
|
+
'User-Agent': 'SocketPythonScript/0.0.1',
|
|
56
|
+
"accept": "application/json"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Check if this is a GitLab CI job token
|
|
60
|
+
if token == os.getenv('CI_JOB_TOKEN'):
|
|
61
|
+
return {
|
|
62
|
+
**base_headers,
|
|
63
|
+
'Authorization': f"Bearer {token}"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Check for personal access token pattern
|
|
67
|
+
if token.startswith('glpat-'):
|
|
68
|
+
return {
|
|
69
|
+
**base_headers,
|
|
70
|
+
'Authorization': f"Bearer {token}"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Check for OAuth token pattern (typically longer and alphanumeric)
|
|
74
|
+
if len(token) > 40 and token.isalnum():
|
|
75
|
+
return {
|
|
76
|
+
**base_headers,
|
|
77
|
+
'Authorization': f"Bearer {token}"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Default to PRIVATE-TOKEN for other token types
|
|
81
|
+
return {
|
|
82
|
+
**base_headers,
|
|
83
|
+
'PRIVATE-TOKEN': f"{token}"
|
|
84
|
+
}
|
|
@@ -42,6 +42,9 @@ class GitlabConfig:
|
|
|
42
42
|
mr_source_branch = os.getenv('CI_MERGE_REQUEST_SOURCE_BRANCH_NAME')
|
|
43
43
|
default_branch = os.getenv('CI_DEFAULT_BRANCH', '')
|
|
44
44
|
|
|
45
|
+
# Determine which authentication pattern to use
|
|
46
|
+
headers = cls._get_auth_headers(token)
|
|
47
|
+
|
|
45
48
|
return cls(
|
|
46
49
|
commit_sha=os.getenv('CI_COMMIT_SHA', ''),
|
|
47
50
|
api_url=os.getenv('CI_API_V4_URL', ''),
|
|
@@ -57,18 +60,119 @@ class GitlabConfig:
|
|
|
57
60
|
token=token,
|
|
58
61
|
repository=project_name,
|
|
59
62
|
is_default_branch=(mr_source_branch == default_branch if mr_source_branch else False),
|
|
60
|
-
headers=
|
|
61
|
-
'Authorization': f"Bearer {token}",
|
|
62
|
-
'User-Agent': 'SocketPythonScript/0.0.1',
|
|
63
|
-
"accept": "application/json"
|
|
64
|
-
}
|
|
63
|
+
headers=headers
|
|
65
64
|
)
|
|
66
65
|
|
|
66
|
+
@staticmethod
|
|
67
|
+
def _get_auth_headers(token: str) -> dict:
|
|
68
|
+
"""
|
|
69
|
+
Determine the appropriate authentication headers for GitLab API.
|
|
70
|
+
|
|
71
|
+
GitLab supports two authentication patterns:
|
|
72
|
+
1. Bearer token (OAuth 2.0 tokens, personal access tokens with api scope)
|
|
73
|
+
2. Private token (personal access tokens)
|
|
74
|
+
|
|
75
|
+
Logic for token type determination:
|
|
76
|
+
- CI_JOB_TOKEN: Always use Bearer (GitLab CI job token)
|
|
77
|
+
- Tokens starting with 'glpat-': Personal access tokens, try Bearer first
|
|
78
|
+
- OAuth tokens: Use Bearer
|
|
79
|
+
- Other tokens: Use PRIVATE-TOKEN as fallback
|
|
80
|
+
"""
|
|
81
|
+
base_headers = {
|
|
82
|
+
'User-Agent': 'SocketPythonScript/0.0.1',
|
|
83
|
+
"accept": "application/json"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Check if this is a GitLab CI job token
|
|
87
|
+
if token == os.getenv('CI_JOB_TOKEN'):
|
|
88
|
+
log.debug("Using Bearer authentication for GitLab CI job token")
|
|
89
|
+
return {
|
|
90
|
+
**base_headers,
|
|
91
|
+
'Authorization': f"Bearer {token}"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Check for personal access token pattern
|
|
95
|
+
if token.startswith('glpat-'):
|
|
96
|
+
log.debug("Using Bearer authentication for GitLab personal access token")
|
|
97
|
+
return {
|
|
98
|
+
**base_headers,
|
|
99
|
+
'Authorization': f"Bearer {token}"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Check for OAuth token pattern (typically longer and alphanumeric)
|
|
103
|
+
if len(token) > 40 and token.isalnum():
|
|
104
|
+
log.debug("Using Bearer authentication for potential OAuth token")
|
|
105
|
+
return {
|
|
106
|
+
**base_headers,
|
|
107
|
+
'Authorization': f"Bearer {token}"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Default to PRIVATE-TOKEN for other token types
|
|
111
|
+
log.debug("Using PRIVATE-TOKEN authentication for GitLab token")
|
|
112
|
+
return {
|
|
113
|
+
**base_headers,
|
|
114
|
+
'PRIVATE-TOKEN': f"{token}"
|
|
115
|
+
}
|
|
116
|
+
|
|
67
117
|
class Gitlab:
|
|
68
118
|
def __init__(self, client: CliClient, config: Optional[GitlabConfig] = None):
|
|
69
119
|
self.config = config or GitlabConfig.from_env()
|
|
70
120
|
self.client = client
|
|
71
121
|
|
|
122
|
+
def _request_with_fallback(self, **kwargs):
|
|
123
|
+
"""
|
|
124
|
+
Make a request with automatic fallback between Bearer and PRIVATE-TOKEN authentication.
|
|
125
|
+
This provides robustness when the initial token type detection is incorrect.
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
# Try the initial request with the configured headers
|
|
129
|
+
return self.client.request(**kwargs)
|
|
130
|
+
except Exception as e:
|
|
131
|
+
# Check if this is an authentication error (401)
|
|
132
|
+
if hasattr(e, 'response') and e.response and e.response.status_code == 401:
|
|
133
|
+
log.debug(f"Authentication failed with initial headers, trying fallback method")
|
|
134
|
+
|
|
135
|
+
# Determine the fallback headers
|
|
136
|
+
original_headers = kwargs.get('headers', self.config.headers)
|
|
137
|
+
fallback_headers = self._get_fallback_headers(original_headers)
|
|
138
|
+
|
|
139
|
+
if fallback_headers and fallback_headers != original_headers:
|
|
140
|
+
log.debug("Retrying request with fallback authentication method")
|
|
141
|
+
kwargs['headers'] = fallback_headers
|
|
142
|
+
return self.client.request(**kwargs)
|
|
143
|
+
|
|
144
|
+
# Re-raise the original exception if it's not an auth error or fallback failed
|
|
145
|
+
raise
|
|
146
|
+
|
|
147
|
+
def _get_fallback_headers(self, original_headers: dict) -> dict:
|
|
148
|
+
"""
|
|
149
|
+
Generate fallback authentication headers.
|
|
150
|
+
If using Bearer, fallback to PRIVATE-TOKEN and vice versa.
|
|
151
|
+
"""
|
|
152
|
+
base_headers = {
|
|
153
|
+
'User-Agent': 'SocketPythonScript/0.0.1',
|
|
154
|
+
"accept": "application/json"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# If currently using Bearer, try PRIVATE-TOKEN
|
|
158
|
+
if 'Authorization' in original_headers and 'Bearer' in original_headers['Authorization']:
|
|
159
|
+
log.debug("Falling back from Bearer to PRIVATE-TOKEN authentication")
|
|
160
|
+
return {
|
|
161
|
+
**base_headers,
|
|
162
|
+
'PRIVATE-TOKEN': f"{self.config.token}"
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# If currently using PRIVATE-TOKEN, try Bearer
|
|
166
|
+
elif 'PRIVATE-TOKEN' in original_headers:
|
|
167
|
+
log.debug("Falling back from PRIVATE-TOKEN to Bearer authentication")
|
|
168
|
+
return {
|
|
169
|
+
**base_headers,
|
|
170
|
+
'Authorization': f"Bearer {self.config.token}"
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# No fallback available
|
|
174
|
+
return None
|
|
175
|
+
|
|
72
176
|
def check_event_type(self) -> str:
|
|
73
177
|
pipeline_source = self.config.pipeline_source.lower()
|
|
74
178
|
if pipeline_source in ["web", 'merge_request_event', "push", "api"]:
|
|
@@ -84,7 +188,7 @@ class Gitlab:
|
|
|
84
188
|
def post_comment(self, body: str) -> None:
|
|
85
189
|
path = f"projects/{self.config.mr_project_id}/merge_requests/{self.config.mr_iid}/notes"
|
|
86
190
|
payload = {"body": body}
|
|
87
|
-
self.
|
|
191
|
+
self._request_with_fallback(
|
|
88
192
|
path=path,
|
|
89
193
|
payload=payload,
|
|
90
194
|
method="POST",
|
|
@@ -95,7 +199,7 @@ class Gitlab:
|
|
|
95
199
|
def update_comment(self, body: str, comment_id: str) -> None:
|
|
96
200
|
path = f"projects/{self.config.mr_project_id}/merge_requests/{self.config.mr_iid}/notes/{comment_id}"
|
|
97
201
|
payload = {"body": body}
|
|
98
|
-
self.
|
|
202
|
+
self._request_with_fallback(
|
|
99
203
|
path=path,
|
|
100
204
|
payload=payload,
|
|
101
205
|
method="PUT",
|
|
@@ -106,7 +210,7 @@ class Gitlab:
|
|
|
106
210
|
def get_comments_for_pr(self) -> dict:
|
|
107
211
|
log.debug(f"Getting Gitlab comments for Repo {self.config.repository} for PR {self.config.mr_iid}")
|
|
108
212
|
path = f"projects/{self.config.mr_project_id}/merge_requests/{self.config.mr_iid}/notes"
|
|
109
|
-
response = self.
|
|
213
|
+
response = self._request_with_fallback(
|
|
110
214
|
path=path,
|
|
111
215
|
headers=self.config.headers,
|
|
112
216
|
base_url=self.config.api_url
|