socketsecurity 2.2.80__tar.gz → 2.2.83__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.83/.github/workflows/e2e-test.yml +99 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/CHANGELOG.md +4 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/PKG-INFO +1 -1
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/docs/cli-reference.md +34 -4
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/pyproject.toml +1 -1
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/__init__.py +1 -1
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/config.py +15 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/__init__.py +65 -2
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/git_interface.py +6 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/messages.py +61 -16
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/socketcli.py +24 -9
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/e2e/fixtures/simple-npm/package.json +2 -2
- socketsecurity-2.2.83/tests/e2e/fixtures/simple-pypi/requirements.txt +3 -0
- socketsecurity-2.2.83/tests/e2e/validate-gitlab.sh +63 -0
- socketsecurity-2.2.83/tests/e2e/validate-json.sh +33 -0
- socketsecurity-2.2.83/tests/e2e/validate-reachability.sh +65 -0
- socketsecurity-2.2.83/tests/e2e/validate-sarif.sh +19 -0
- socketsecurity-2.2.83/tests/e2e/validate-scan.sh +16 -0
- socketsecurity-2.2.83/tests/unit/test_disable_ignore.py +138 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_gitlab_format.py +175 -2
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/uv.lock +1 -1
- socketsecurity-2.2.80/.github/workflows/e2e-test.yml +0 -201
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/CODEOWNERS +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/workflows/docker-stable.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/workflows/pr-preview.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/workflows/python-tests.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/workflows/release.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/workflows/version-check.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/zizmor.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.gitignore +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.hooks/sync_version.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.pre-commit-config.yaml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.python-version +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/Dockerfile +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/LICENSE +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/Makefile +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/README.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/docs/ci-cd.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/docs/development.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/docs/troubleshooting.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-dashboard-parity.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-dashboard-parity.toml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-diff-ci-cd.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-diff-ci-cd.toml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-instance-detail.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-instance-detail.toml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/instructions/gitlab-commit-status/uat.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/pytest.ini +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/build_container.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/build_container_flexible.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/deploy-test-docker.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/deploy-test-pypi.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/docker-entrypoint.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/run.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/session.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socket.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/alert_selection.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/cli_client.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/helper/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/lazy_file_loader.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/logging.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/resource_utils.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm/base.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm/client.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm/github.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm/gitlab.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/socket_config.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/tools/reachability.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/utils.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/output.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/base.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/formatters/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/formatters/slack.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/jira.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/manager.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/slack.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/teams.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/webhook.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/conftest.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/create_diff_input.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_diff_alerts.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_diff_generation.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_has_manifest_files.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_package_and_alerts.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_sdk_methods.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_supporting_methods.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/create_response.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/diff/stream_diff.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/head_scan/metadata.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/new_scan/metadata.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/repos/repo_info_error.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/repos/repo_info_no_head.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/repos/repo_info_success.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/settings/security-policy.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/e2e/fixtures/simple-npm/index.js +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_alert_selection.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_cli_config.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_client.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_config.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_gitlab_auth.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_gitlab_auth_fallback.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_gitlab_commit_status.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_output.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_slack_plugin.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/workflows/bitbucket-pipelines.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/workflows/buildkite.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/workflows/github-actions.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.83}/workflows/gitlab-ci.yml +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
name: E2E Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
e2e:
|
|
14
|
+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
strategy:
|
|
17
|
+
fail-fast: false
|
|
18
|
+
matrix:
|
|
19
|
+
include:
|
|
20
|
+
- name: scan
|
|
21
|
+
args: >-
|
|
22
|
+
--target-path tests/e2e/fixtures/simple-npm
|
|
23
|
+
--disable-blocking
|
|
24
|
+
--enable-debug
|
|
25
|
+
validate: tests/e2e/validate-scan.sh
|
|
26
|
+
|
|
27
|
+
- name: sarif
|
|
28
|
+
args: >-
|
|
29
|
+
--target-path tests/e2e/fixtures/simple-npm
|
|
30
|
+
--sarif-file /tmp/results.sarif
|
|
31
|
+
--disable-blocking
|
|
32
|
+
validate: tests/e2e/validate-sarif.sh
|
|
33
|
+
|
|
34
|
+
- name: reachability
|
|
35
|
+
args: >-
|
|
36
|
+
--target-path tests/e2e/fixtures/simple-npm
|
|
37
|
+
--reach
|
|
38
|
+
--disable-blocking
|
|
39
|
+
--enable-debug
|
|
40
|
+
validate: tests/e2e/validate-reachability.sh
|
|
41
|
+
setup-node: "true"
|
|
42
|
+
|
|
43
|
+
- name: gitlab
|
|
44
|
+
args: >-
|
|
45
|
+
--target-path tests/e2e/fixtures/simple-npm
|
|
46
|
+
--enable-gitlab-security
|
|
47
|
+
--disable-blocking
|
|
48
|
+
validate: tests/e2e/validate-gitlab.sh
|
|
49
|
+
|
|
50
|
+
- name: json
|
|
51
|
+
args: >-
|
|
52
|
+
--target-path tests/e2e/fixtures/simple-npm
|
|
53
|
+
--enable-json
|
|
54
|
+
--disable-blocking
|
|
55
|
+
validate: tests/e2e/validate-json.sh
|
|
56
|
+
|
|
57
|
+
- name: pypi
|
|
58
|
+
args: >-
|
|
59
|
+
--target-path tests/e2e/fixtures/simple-pypi
|
|
60
|
+
--disable-blocking
|
|
61
|
+
--enable-debug
|
|
62
|
+
validate: tests/e2e/validate-scan.sh
|
|
63
|
+
|
|
64
|
+
name: e2e-${{ matrix.name }}
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871
|
|
67
|
+
with:
|
|
68
|
+
fetch-depth: 0
|
|
69
|
+
persist-credentials: false
|
|
70
|
+
|
|
71
|
+
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
|
|
72
|
+
with:
|
|
73
|
+
python-version: '3.12'
|
|
74
|
+
|
|
75
|
+
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
|
76
|
+
if: matrix.setup-node == 'true'
|
|
77
|
+
with:
|
|
78
|
+
node-version: '20'
|
|
79
|
+
|
|
80
|
+
- name: Install CLI from local repo
|
|
81
|
+
run: |
|
|
82
|
+
python -m pip install --upgrade pip
|
|
83
|
+
pip install .
|
|
84
|
+
|
|
85
|
+
- name: Install uv
|
|
86
|
+
if: matrix.setup-node == 'true'
|
|
87
|
+
run: pip install uv
|
|
88
|
+
|
|
89
|
+
- name: Run Socket CLI
|
|
90
|
+
env:
|
|
91
|
+
SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_CLI_API_TOKEN }}
|
|
92
|
+
run: |
|
|
93
|
+
set -o pipefail
|
|
94
|
+
socketcli ${{ matrix.args }} 2>&1 | tee /tmp/e2e-output.log
|
|
95
|
+
|
|
96
|
+
- name: Validate results
|
|
97
|
+
env:
|
|
98
|
+
SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_CLI_API_TOKEN }}
|
|
99
|
+
run: bash ${{ matrix.validate }}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.83
|
|
4
|
+
|
|
5
|
+
- Fixed branch detection in detached-HEAD CI checkouts. When `git name-rev --name-only HEAD` returned an output with a suffix operator (e.g. `remotes/origin/master~1`, `master^0`), the `~N`/`^N` was previously passed through as the branch name and rejected by the Socket API as an invalid Git ref. The suffix is now stripped before the prefix split, producing the bare branch name.
|
|
6
|
+
|
|
3
7
|
## 2.2.71
|
|
4
8
|
|
|
5
9
|
- Added `strace` to the Docker image for debugging purposes.
|
|
@@ -151,7 +151,7 @@ socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--workspace WORKSPACE] [--
|
|
|
151
151
|
[--excluded-ecosystems EXCLUDED_ECOSYSTEMS] [--default-branch] [--pending-head] [--generate-license] [--enable-debug]
|
|
152
152
|
[--enable-json] [--enable-sarif] [--sarif-file <path>] [--sarif-scope {diff,full}] [--sarif-grouping {instance,alert}] [--sarif-reachability {all,reachable,potentially,reachable-or-potentially}] [--enable-gitlab-security] [--gitlab-security-file <path>]
|
|
153
153
|
[--disable-overview] [--exclude-license-details] [--allow-unverified] [--disable-security-issue]
|
|
154
|
-
[--ignore-commit-files] [--disable-blocking] [--enable-diff] [--scm SCM] [--timeout TIMEOUT] [--include-module-folders]
|
|
154
|
+
[--ignore-commit-files] [--disable-blocking] [--disable-ignore] [--enable-diff] [--scm SCM] [--timeout TIMEOUT] [--include-module-folders]
|
|
155
155
|
[--reach] [--reach-version REACH_VERSION] [--reach-timeout REACH_ANALYSIS_TIMEOUT]
|
|
156
156
|
[--reach-memory-limit REACH_ANALYSIS_MEMORY_LIMIT] [--reach-ecosystems REACH_ECOSYSTEMS] [--reach-exclude-paths REACH_EXCLUDE_PATHS]
|
|
157
157
|
[--reach-min-severity {low,medium,high,critical}] [--reach-skip-cache] [--reach-disable-analytics] [--reach-output-file REACH_OUTPUT_FILE]
|
|
@@ -306,6 +306,7 @@ The CLI will automatically install `@coana-tech/cli` if not present. Use `--reac
|
|
|
306
306
|
|:-------------------------|:---------|:--------|:----------------------------------------------------------------------|
|
|
307
307
|
| `--ignore-commit-files` | False | False | Ignore commit files |
|
|
308
308
|
| `--disable-blocking` | False | False | Disable blocking mode |
|
|
309
|
+
| `--disable-ignore` | False | False | Disable support for `@SocketSecurity ignore` commands in PR comments. When set, alerts cannot be suppressed via comments and ignore instructions are hidden from comment output. |
|
|
309
310
|
| `--strict-blocking` | False | False | Fail on ANY security policy violations (blocking severity), not just new ones. Only works in diff mode. See [Strict Blocking Mode](#strict-blocking-mode) for details. |
|
|
310
311
|
| `--enable-diff` | False | False | Enable diff mode even when using `--integration api` (forces diff mode without SCM integration) |
|
|
311
312
|
| `--scm` | False | api | Source control management type |
|
|
@@ -700,17 +701,44 @@ The GitLab report includes **actionable security alerts** based on your Socket p
|
|
|
700
701
|
|
|
701
702
|
All alert types are included in the GitLab report if they're marked as `error` or `warn` by your Socket Security policy, ensuring the Security Dashboard shows only actionable findings.
|
|
702
703
|
|
|
704
|
+
### Alert Population: GitLab vs JSON/SARIF
|
|
705
|
+
|
|
706
|
+
The GitLab Security Dashboard report and the JSON/SARIF diff outputs use different alert selection strategies, reflecting their distinct purposes:
|
|
707
|
+
|
|
708
|
+
| Output Format | Default Alerts | With `--strict-blocking` |
|
|
709
|
+
|:---|:---|:---|
|
|
710
|
+
| `--enable-gitlab-security` | **All** alerts (new + existing) | All alerts (same) |
|
|
711
|
+
| `--enable-json` | New alerts only | New + existing alerts |
|
|
712
|
+
| `--enable-sarif` (diff scope) | New alerts only | New + existing alerts |
|
|
713
|
+
|
|
714
|
+
**Why the difference?** GitLab's Security Dashboard is designed to present the full security posture of a project. An empty dashboard on a scan with no dependency changes would be misleading -- the vulnerabilities still exist, they just didn't change. By contrast, JSON and SARIF in diff scope are designed to answer "what changed?" and only include existing alerts when `--strict-blocking` explicitly requests it.
|
|
715
|
+
|
|
716
|
+
> **Tip:** If you use `--enable-json` alongside `--enable-gitlab-security`, the GitLab report may contain more vulnerabilities than the JSON output. This is expected. To make JSON output match, add `--strict-blocking`.
|
|
717
|
+
|
|
718
|
+
### Alert Ignoring via PR/MR Comments
|
|
719
|
+
|
|
720
|
+
When using the CLI with SCM integration (`--scm github` or `--scm gitlab`), users can ignore specific alerts by reacting to Socket's PR/MR comments. Ignored alerts are removed from `--enable-json`, `--enable-sarif`, and console output.
|
|
721
|
+
|
|
722
|
+
However, the GitLab Security Dashboard report includes **all** alerts matching your security policy (new and existing), regardless of comment-based ignores. This ensures the Security Dashboard always reflects the full set of known issues. To suppress a vulnerability from the GitLab report, adjust the alert's policy in Socket's dashboard rather than ignoring it via a PR comment.
|
|
723
|
+
|
|
703
724
|
### Report Schema
|
|
704
725
|
|
|
705
|
-
Socket CLI generates reports compliant with [GitLab Dependency Scanning schema version 15.0.0](https://
|
|
726
|
+
Socket CLI generates reports compliant with [GitLab Dependency Scanning schema version 15.0.0](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/v15.0.0/dist/dependency-scanning-report-format.json). The reports include:
|
|
706
727
|
|
|
707
|
-
- **Scan metadata**: Analyzer and scanner information
|
|
728
|
+
- **Scan metadata**: Analyzer and scanner information with ISO 8601 timestamps
|
|
708
729
|
- **Vulnerabilities**: Detailed vulnerability data with:
|
|
709
730
|
- Unique deterministic UUIDs for tracking
|
|
710
731
|
- Package location and dependency information
|
|
711
732
|
- Severity levels mapped from Socket's analysis
|
|
712
733
|
- Socket-specific alert types and CVE identifiers
|
|
713
734
|
- Links to Socket.dev for detailed analysis
|
|
735
|
+
- **Dependency files**: Manifest files and their dependencies discovered during the scan
|
|
736
|
+
|
|
737
|
+
**Schema compatibility:** The v15.0.0 schema is supported across all GitLab versions 12.0+ (both self-hosted and cloud). The report includes the `dependency_files` field, which is required by v15.0.0 and accepted as an optional extra by newer schema versions, ensuring maximum compatibility across GitLab instances.
|
|
738
|
+
|
|
739
|
+
### Performance Notes
|
|
740
|
+
|
|
741
|
+
When `--enable-gitlab-security` (or `--enable-json` / `--enable-sarif`) is used with a full scan (non-diff mode), the CLI fetches package and alert data from the scan results to populate the report. This adds time proportional to the number of packages in the scan. Without these output flags, no additional data is fetched and scan performance is unchanged.
|
|
714
742
|
|
|
715
743
|
### Requirements
|
|
716
744
|
|
|
@@ -726,7 +754,9 @@ Socket CLI generates reports compliant with [GitLab Dependency Scanning schema v
|
|
|
726
754
|
- Ensure the report file follows the correct schema format
|
|
727
755
|
|
|
728
756
|
**Empty vulnerabilities array:**
|
|
729
|
-
-
|
|
757
|
+
- The GitLab report includes both new and existing alerts, so repeated scans of the same repo should still populate the report as long as Socket detects actionable issues
|
|
758
|
+
- If the report is empty, verify the Socket dashboard shows alerts for the scanned packages -- an empty report means no error/warn-level alerts exist
|
|
759
|
+
- For full scans (non-diff mode), ensure you are using `--enable-gitlab-security` so alert data is fetched
|
|
730
760
|
- Check Socket.dev dashboard for full analysis details
|
|
731
761
|
|
|
732
762
|
## Development
|
|
@@ -91,6 +91,7 @@ class CliConfig:
|
|
|
91
91
|
files: str = None
|
|
92
92
|
ignore_commit_files: bool = False
|
|
93
93
|
disable_blocking: bool = False
|
|
94
|
+
disable_ignore: bool = False
|
|
94
95
|
strict_blocking: bool = False
|
|
95
96
|
integration_type: IntegrationType = "api"
|
|
96
97
|
integration_org_slug: Optional[str] = None
|
|
@@ -201,6 +202,7 @@ class CliConfig:
|
|
|
201
202
|
'files': args.files,
|
|
202
203
|
'ignore_commit_files': args.ignore_commit_files,
|
|
203
204
|
'disable_blocking': args.disable_blocking,
|
|
205
|
+
'disable_ignore': args.disable_ignore,
|
|
204
206
|
'strict_blocking': args.strict_blocking,
|
|
205
207
|
'integration_type': args.integration,
|
|
206
208
|
'pending_head': args.pending_head,
|
|
@@ -693,6 +695,19 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
693
695
|
action="store_true",
|
|
694
696
|
help=argparse.SUPPRESS
|
|
695
697
|
)
|
|
698
|
+
advanced_group.add_argument(
|
|
699
|
+
"--disable-ignore",
|
|
700
|
+
dest="disable_ignore",
|
|
701
|
+
action="store_true",
|
|
702
|
+
help="Disable support for @SocketSecurity ignore commands in PR comments. "
|
|
703
|
+
"Alerts cannot be suppressed via comments when this flag is set."
|
|
704
|
+
)
|
|
705
|
+
advanced_group.add_argument(
|
|
706
|
+
"--disable_ignore",
|
|
707
|
+
dest="disable_ignore",
|
|
708
|
+
action="store_true",
|
|
709
|
+
help=argparse.SUPPRESS
|
|
710
|
+
)
|
|
696
711
|
advanced_group.add_argument(
|
|
697
712
|
"--strict-blocking",
|
|
698
713
|
dest="strict_blocking",
|
|
@@ -659,9 +659,48 @@ class Core:
|
|
|
659
659
|
diff.report_url = f"{base_socket}/{self.config.org_slug}/sbom/{new_full_scan.id}"
|
|
660
660
|
diff.diff_url = diff.report_url
|
|
661
661
|
diff.id = new_full_scan.id
|
|
662
|
-
diff.packages = {}
|
|
663
662
|
|
|
664
|
-
|
|
663
|
+
needs_alerts = (
|
|
664
|
+
self.cli_config is not None
|
|
665
|
+
and (
|
|
666
|
+
self.cli_config.enable_gitlab_security
|
|
667
|
+
or self.cli_config.enable_json
|
|
668
|
+
or self.cli_config.enable_sarif
|
|
669
|
+
)
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
if needs_alerts:
|
|
673
|
+
log.info("Output format requires alerts, fetching SBOM data for full scan")
|
|
674
|
+
sbom_start = time.time()
|
|
675
|
+
sbom_artifacts_dict = self.get_sbom_data(new_full_scan.id)
|
|
676
|
+
sbom_artifacts = self.get_sbom_data_list(sbom_artifacts_dict)
|
|
677
|
+
packages = self._create_packages_dict_without_license_text(sbom_artifacts)
|
|
678
|
+
diff.packages = packages
|
|
679
|
+
|
|
680
|
+
all_alerts_collection: Dict[str, List[Issue]] = {}
|
|
681
|
+
for package_id, package in packages.items():
|
|
682
|
+
self.add_package_alerts_to_collection(
|
|
683
|
+
package=package,
|
|
684
|
+
alerts_collection=all_alerts_collection,
|
|
685
|
+
packages=packages
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
consolidated: Set[str] = set()
|
|
689
|
+
for alert_key, alerts in all_alerts_collection.items():
|
|
690
|
+
for alert in alerts:
|
|
691
|
+
alert_str = f"{alert.purl},{alert.type}"
|
|
692
|
+
if (alert.error or alert.warn) and alert_str not in consolidated:
|
|
693
|
+
diff.new_alerts.append(alert)
|
|
694
|
+
consolidated.add(alert_str)
|
|
695
|
+
|
|
696
|
+
sbom_end = time.time()
|
|
697
|
+
log.info(
|
|
698
|
+
f"Fetched {len(packages)} packages and {len(diff.new_alerts)} alerts "
|
|
699
|
+
f"in {sbom_end - sbom_start:.2f}s"
|
|
700
|
+
)
|
|
701
|
+
else:
|
|
702
|
+
diff.packages = {}
|
|
703
|
+
|
|
665
704
|
return diff
|
|
666
705
|
|
|
667
706
|
def get_full_scan(self, full_scan_id: str) -> FullScan:
|
|
@@ -712,6 +751,30 @@ class Core:
|
|
|
712
751
|
|
|
713
752
|
return packages
|
|
714
753
|
|
|
754
|
+
@staticmethod
|
|
755
|
+
def _create_packages_dict_without_license_text(
|
|
756
|
+
sbom_artifacts: list[SocketArtifact],
|
|
757
|
+
) -> dict[str, Package]:
|
|
758
|
+
"""Like create_packages_dict but skips the license-metadata API call.
|
|
759
|
+
|
|
760
|
+
Used when we only need packages for alert extraction (e.g. populating
|
|
761
|
+
GitLab/JSON/SARIF reports from a full scan) and don't need license text.
|
|
762
|
+
"""
|
|
763
|
+
packages: dict[str, Package] = {}
|
|
764
|
+
top_level_count: dict[str, int] = {}
|
|
765
|
+
for artifact in sbom_artifacts:
|
|
766
|
+
package = Package.from_socket_artifact(asdict(artifact))
|
|
767
|
+
if package.id not in packages:
|
|
768
|
+
packages[package.id] = package
|
|
769
|
+
if package.topLevelAncestors:
|
|
770
|
+
for top_id in package.topLevelAncestors:
|
|
771
|
+
top_level_count[top_id] = top_level_count.get(top_id, 0) + 1
|
|
772
|
+
|
|
773
|
+
for package_id, package in packages.items():
|
|
774
|
+
package.transitives = top_level_count.get(package_id, 0)
|
|
775
|
+
|
|
776
|
+
return packages
|
|
777
|
+
|
|
715
778
|
def get_package_license_text(self, package: Package) -> str:
|
|
716
779
|
"""
|
|
717
780
|
Gets the license text for a package if available.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import re
|
|
1
2
|
import urllib.parse
|
|
2
3
|
import os
|
|
3
4
|
|
|
@@ -108,6 +109,11 @@ class Git:
|
|
|
108
109
|
# Try git name-rev first (most reliable for detached HEAD)
|
|
109
110
|
result = self.repo.git.name_rev('--name-only', 'HEAD')
|
|
110
111
|
if result and result != 'undefined':
|
|
112
|
+
# Strip name-rev suffix operators (~N, ^N, or combinations
|
|
113
|
+
# like master~3^2). These characters are forbidden in git
|
|
114
|
+
# ref names, so cutting at the first occurrence can never
|
|
115
|
+
# truncate a real branch name.
|
|
116
|
+
result = re.split(r'[~^]', result, maxsplit=1)[0]
|
|
111
117
|
# Clean up the result (remove any prefixes like 'remotes/origin/')
|
|
112
118
|
git_detected_branch = result.split('/')[-1]
|
|
113
119
|
log.debug(f"Branch detected from git name-rev: {git_detected_branch}")
|
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
5
|
import uuid
|
|
6
|
-
from datetime import datetime
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from mdutils import MdUtils
|
|
9
9
|
from prettytable import PrettyTable
|
|
@@ -593,6 +593,20 @@ class Messages:
|
|
|
593
593
|
output["new_alerts"].append(json.loads(str(alert)))
|
|
594
594
|
return output
|
|
595
595
|
|
|
596
|
+
@staticmethod
|
|
597
|
+
def _pkg_type_to_package_manager(pkg_type: str) -> str:
|
|
598
|
+
"""Map Socket pkg_type to GitLab package_manager name for dependency_files."""
|
|
599
|
+
mapping = {
|
|
600
|
+
"npm": "npm",
|
|
601
|
+
"pypi": "pip",
|
|
602
|
+
"go": "go",
|
|
603
|
+
"maven": "maven",
|
|
604
|
+
"gem": "bundler",
|
|
605
|
+
"nuget": "nuget",
|
|
606
|
+
"cargo": "cargo",
|
|
607
|
+
}
|
|
608
|
+
return mapping.get(pkg_type, pkg_type or "unknown")
|
|
609
|
+
|
|
596
610
|
@staticmethod
|
|
597
611
|
def map_socket_severity_to_gitlab(severity: str) -> str:
|
|
598
612
|
"""
|
|
@@ -743,15 +757,18 @@ class Messages:
|
|
|
743
757
|
}
|
|
744
758
|
},
|
|
745
759
|
"type": "dependency_scanning",
|
|
746
|
-
"start_time": datetime.
|
|
747
|
-
"end_time": datetime.
|
|
760
|
+
"start_time": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
|
|
761
|
+
"end_time": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
|
|
748
762
|
"status": "success"
|
|
749
763
|
},
|
|
750
|
-
"vulnerabilities": []
|
|
764
|
+
"vulnerabilities": [],
|
|
765
|
+
"dependency_files": []
|
|
751
766
|
}
|
|
752
767
|
|
|
753
|
-
|
|
754
|
-
|
|
768
|
+
dep_files_map: dict = {}
|
|
769
|
+
|
|
770
|
+
all_alerts = list(diff.new_alerts) + list(getattr(diff, 'unchanged_alerts', []))
|
|
771
|
+
for alert in all_alerts:
|
|
755
772
|
vulnerability = {
|
|
756
773
|
"id": Messages.generate_uuid_from_alert_gitlab(alert),
|
|
757
774
|
"category": "dependency_scanning",
|
|
@@ -764,12 +781,29 @@ class Messages:
|
|
|
764
781
|
"location": Messages.extract_location_gitlab(alert)
|
|
765
782
|
}
|
|
766
783
|
|
|
767
|
-
# Add solution if available
|
|
768
784
|
if hasattr(alert, 'suggestion') and alert.suggestion:
|
|
769
785
|
vulnerability["solution"] = alert.suggestion
|
|
770
786
|
|
|
771
787
|
gitlab_report["vulnerabilities"].append(vulnerability)
|
|
772
788
|
|
|
789
|
+
file_path = vulnerability["location"]["file"]
|
|
790
|
+
if file_path != "unknown":
|
|
791
|
+
pkg_manager = Messages._pkg_type_to_package_manager(
|
|
792
|
+
alert.pkg_type if hasattr(alert, 'pkg_type') else ""
|
|
793
|
+
)
|
|
794
|
+
if file_path not in dep_files_map:
|
|
795
|
+
dep_files_map[file_path] = {
|
|
796
|
+
"path": file_path,
|
|
797
|
+
"package_manager": pkg_manager,
|
|
798
|
+
"dependencies": []
|
|
799
|
+
}
|
|
800
|
+
dep_files_map[file_path]["dependencies"].append({
|
|
801
|
+
"package": {"name": alert.pkg_name},
|
|
802
|
+
"version": alert.pkg_version
|
|
803
|
+
})
|
|
804
|
+
|
|
805
|
+
gitlab_report["dependency_files"] = list(dep_files_map.values())
|
|
806
|
+
|
|
773
807
|
return gitlab_report
|
|
774
808
|
|
|
775
809
|
@staticmethod
|
|
@@ -816,6 +850,8 @@ class Messages:
|
|
|
816
850
|
<tbody>
|
|
817
851
|
"""
|
|
818
852
|
|
|
853
|
+
show_ignore = not (config and getattr(config, 'disable_ignore', False))
|
|
854
|
+
|
|
819
855
|
# Loop through security alerts (non-license), dynamically generating rows
|
|
820
856
|
for alert in security_alerts:
|
|
821
857
|
severity_icon = Messages.get_severity_icon(alert.severity)
|
|
@@ -824,6 +860,12 @@ class Messages:
|
|
|
824
860
|
# Generate proper manifest URL
|
|
825
861
|
manifest_url = Messages.get_manifest_file_url(diff, alert.manifests, config)
|
|
826
862
|
# Generate a table row for each alert
|
|
863
|
+
ignore_html = (
|
|
864
|
+
f"<p><em>Mark as acceptable risk:</em> To ignore this alert only in this pull request, reply with:<br/>"
|
|
865
|
+
f"<code>@SocketSecurity ignore {alert.pkg_name}@{alert.pkg_version}</code><br/>"
|
|
866
|
+
f"Or ignore all future alerts with:<br/>"
|
|
867
|
+
f"<code>@SocketSecurity ignore-all</code></p>"
|
|
868
|
+
) if show_ignore else ""
|
|
827
869
|
comment += f"""
|
|
828
870
|
<!-- start-socket-alert-{alert.pkg_name}@{alert.pkg_version} -->
|
|
829
871
|
<tr>
|
|
@@ -836,16 +878,13 @@ class Messages:
|
|
|
836
878
|
<summary>{alert.pkg_name}@{alert.pkg_version} - {alert.title}</summary>
|
|
837
879
|
<p><strong>Note:</strong> {alert.description}</p>
|
|
838
880
|
<p><strong>Source:</strong> <a href="{manifest_url}">Manifest File</a></p>
|
|
839
|
-
<p>ℹ️ Read more on:
|
|
840
|
-
<a href="{alert.purl}">This package</a> |
|
|
841
|
-
<a href="{alert.url}">This alert</a> |
|
|
881
|
+
<p>ℹ️ Read more on:
|
|
882
|
+
<a href="{alert.purl}">This package</a> |
|
|
883
|
+
<a href="{alert.url}">This alert</a> |
|
|
842
884
|
<a href="https://socket.dev/alerts/malware">What is known malware?</a></p>
|
|
843
885
|
<blockquote>
|
|
844
886
|
<p><em>Suggestion:</em> {alert.suggestion}</p>
|
|
845
|
-
|
|
846
|
-
<code>@SocketSecurity ignore {alert.pkg_name}@{alert.pkg_version}</code><br/>
|
|
847
|
-
Or ignore all future alerts with:<br/>
|
|
848
|
-
<code>@SocketSecurity ignore-all</code></p>
|
|
887
|
+
{ignore_html}
|
|
849
888
|
</blockquote>
|
|
850
889
|
</details>
|
|
851
890
|
</td>
|
|
@@ -883,14 +922,20 @@ class Messages:
|
|
|
883
922
|
|
|
884
923
|
# Generate proper manifest URL for license violations
|
|
885
924
|
license_manifest_url = Messages.get_manifest_file_url(diff, first_alert.manifests, config)
|
|
886
|
-
|
|
925
|
+
|
|
926
|
+
license_ignore_html = (
|
|
927
|
+
f"<p><em>Mark the package as acceptable risk:</em> To ignore this alert only in this pull request, reply with the comment "
|
|
928
|
+
f"<code>@SocketSecurity ignore {first_alert.pkg_name}@{first_alert.pkg_version}</code>. "
|
|
929
|
+
f"You can also ignore all packages with <code>@SocketSecurity ignore-all</code>. "
|
|
930
|
+
f"To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.</p>"
|
|
931
|
+
) if show_ignore else ""
|
|
887
932
|
comment += f""" </ul>
|
|
888
933
|
<p><strong>From:</strong> <a href="{license_manifest_url}">Manifest File</a></p>
|
|
889
934
|
<p>ℹ️ Read more on: <a href="{first_alert.purl}">This package</a> | <a href="https://socket.dev/alerts/license">What is a license policy violation?</a></p>
|
|
890
935
|
<blockquote>
|
|
891
936
|
<p><em>Next steps:</em> Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at <strong>support@socket.dev</strong>.</p>
|
|
892
937
|
<p><em>Suggestion:</em> Find a package that does not violate your license policy or adjust your policy to allow this package's license.</p>
|
|
893
|
-
|
|
938
|
+
{license_ignore_html}
|
|
894
939
|
</blockquote>
|
|
895
940
|
</details>
|
|
896
941
|
</td>
|
|
@@ -486,10 +486,13 @@ def main_code():
|
|
|
486
486
|
# 3. Updates the comment to remove ignored alerts
|
|
487
487
|
# This is completely separate from the main scanning functionality
|
|
488
488
|
log.info("Comment initiated flow")
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
489
|
+
|
|
490
|
+
if not config.disable_ignore:
|
|
491
|
+
comments = scm.get_comments_for_pr()
|
|
492
|
+
log.debug("Removing comment alerts")
|
|
493
|
+
scm.remove_comment_alerts(comments)
|
|
494
|
+
else:
|
|
495
|
+
log.info("Ignore commands disabled (--disable-ignore), skipping comment processing")
|
|
493
496
|
|
|
494
497
|
elif scm is not None and scm.check_event_type() != "comment" and not force_api_mode:
|
|
495
498
|
log.info("Push initiated flow")
|
|
@@ -497,10 +500,13 @@ def main_code():
|
|
|
497
500
|
log.info("Starting comment logic for PR/MR event")
|
|
498
501
|
diff = core.create_new_diff(scan_paths, params, no_change=should_skip_scan, save_files_list_path=config.save_submitted_files_list, save_manifest_tar_path=config.save_manifest_tar, base_paths=base_paths, explicit_files=sbom_files_to_submit)
|
|
499
502
|
comments = scm.get_comments_for_pr()
|
|
500
|
-
|
|
501
|
-
|
|
503
|
+
|
|
502
504
|
# FIXME: this overwrites diff.new_alerts, which was previously populated by Core.create_issue_alerts
|
|
503
|
-
|
|
505
|
+
if not config.disable_ignore:
|
|
506
|
+
log.debug("Removing comment alerts")
|
|
507
|
+
diff.new_alerts = Comments.remove_alerts(comments, diff.new_alerts)
|
|
508
|
+
else:
|
|
509
|
+
log.info("Ignore commands disabled (--disable-ignore), all alerts will be reported")
|
|
504
510
|
log.debug("Creating Dependency Overview Comment")
|
|
505
511
|
|
|
506
512
|
overview_comment = Messages.dependency_overview_template(diff)
|
|
@@ -649,11 +655,20 @@ def main_code():
|
|
|
649
655
|
scm.enable_merge_pipeline_check()
|
|
650
656
|
passed = output_handler.report_pass(diff)
|
|
651
657
|
state = "success" if passed else "failed"
|
|
652
|
-
|
|
658
|
+
new_blocking = sum(1 for a in diff.new_alerts if a.error)
|
|
659
|
+
unchanged_blocking = 0
|
|
660
|
+
if config.strict_blocking and hasattr(diff, 'unchanged_alerts'):
|
|
661
|
+
unchanged_blocking = sum(1 for a in diff.unchanged_alerts if a.error)
|
|
662
|
+
blocking_count = new_blocking + unchanged_blocking
|
|
653
663
|
if passed:
|
|
654
664
|
description = "No blocking issues"
|
|
655
665
|
else:
|
|
656
|
-
|
|
666
|
+
parts = []
|
|
667
|
+
if new_blocking:
|
|
668
|
+
parts.append(f"{new_blocking} new")
|
|
669
|
+
if unchanged_blocking:
|
|
670
|
+
parts.append(f"{unchanged_blocking} existing")
|
|
671
|
+
description = f"{blocking_count} blocking alert(s) found ({', '.join(parts)})"
|
|
657
672
|
target_url = diff.report_url or diff.diff_url or ""
|
|
658
673
|
scm.set_commit_status(state, description, target_url)
|
|
659
674
|
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
"description": "Test fixture for reachability analysis",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"lodash": "4.
|
|
7
|
+
"lodash": "4.18.1",
|
|
8
8
|
"express": "4.22.0",
|
|
9
|
-
"axios": "1.
|
|
9
|
+
"axios": "1.15.0"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"typescript": "5.0.4",
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
REPORT="gl-dependency-scanning-report.json"
|
|
5
|
+
|
|
6
|
+
if [ ! -f "$REPORT" ]; then
|
|
7
|
+
echo "FAIL: GitLab report not found at $REPORT"
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
python3 -c "
|
|
12
|
+
import json, re, sys
|
|
13
|
+
|
|
14
|
+
with open('$REPORT') as f:
|
|
15
|
+
data = json.load(f)
|
|
16
|
+
|
|
17
|
+
errors = []
|
|
18
|
+
|
|
19
|
+
# v15.0.0 required root-level keys
|
|
20
|
+
for key in ('version', 'scan', 'vulnerabilities', 'dependency_files'):
|
|
21
|
+
if key not in data:
|
|
22
|
+
errors.append(f'Missing required root key: {key}')
|
|
23
|
+
|
|
24
|
+
if 'scan' in data:
|
|
25
|
+
scan = data['scan']
|
|
26
|
+
|
|
27
|
+
# Timestamp format: YYYY-MM-DDTHH:MM:SS (no microseconds, no trailing Z)
|
|
28
|
+
ts_pattern = re.compile(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$')
|
|
29
|
+
for field in ('start_time', 'end_time'):
|
|
30
|
+
val = scan.get(field, '')
|
|
31
|
+
if not ts_pattern.match(val):
|
|
32
|
+
errors.append(f'scan.{field} \"{val}\" does not match pattern YYYY-MM-DDTHH:MM:SS')
|
|
33
|
+
|
|
34
|
+
if scan.get('type') != 'dependency_scanning':
|
|
35
|
+
errors.append(f'scan.type is \"{scan.get(\"type\")}\" expected \"dependency_scanning\"')
|
|
36
|
+
|
|
37
|
+
analyzer_id = scan.get('analyzer', {}).get('id', '')
|
|
38
|
+
if analyzer_id != 'socket-security':
|
|
39
|
+
errors.append(f'scan.analyzer.id is \"{analyzer_id}\" expected \"socket-security\"')
|
|
40
|
+
|
|
41
|
+
scanner_id = scan.get('scanner', {}).get('id', '')
|
|
42
|
+
if scanner_id != 'socket-cli':
|
|
43
|
+
errors.append(f'scan.scanner.id is \"{scanner_id}\" expected \"socket-cli\"')
|
|
44
|
+
|
|
45
|
+
if scan.get('status') != 'success':
|
|
46
|
+
errors.append(f'scan.status is \"{scan.get(\"status\")}\" expected \"success\"')
|
|
47
|
+
|
|
48
|
+
# dependency_files structure check
|
|
49
|
+
if 'dependency_files' in data:
|
|
50
|
+
for i, df in enumerate(data['dependency_files']):
|
|
51
|
+
for req in ('path', 'package_manager', 'dependencies'):
|
|
52
|
+
if req not in df:
|
|
53
|
+
errors.append(f'dependency_files[{i}] missing required key: {req}')
|
|
54
|
+
|
|
55
|
+
if errors:
|
|
56
|
+
for e in errors:
|
|
57
|
+
print(f'FAIL: {e}')
|
|
58
|
+
sys.exit(1)
|
|
59
|
+
|
|
60
|
+
vuln_count = len(data.get('vulnerabilities', []))
|
|
61
|
+
dep_file_count = len(data.get('dependency_files', []))
|
|
62
|
+
print(f'PASS: Valid GitLab v15.0.0 report with {vuln_count} vulnerability(ies) and {dep_file_count} dependency file(s)')
|
|
63
|
+
"
|