socketsecurity 2.2.80__tar.gz → 2.2.81__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.81/.github/workflows/e2e-test.yml +99 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/PKG-INFO +1 -1
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/docs/cli-reference.md +32 -3
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/pyproject.toml +1 -1
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/__init__.py +1 -1
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/__init__.py +65 -2
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/messages.py +41 -7
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/socketcli.py +11 -2
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/e2e/fixtures/simple-npm/package.json +2 -2
- socketsecurity-2.2.81/tests/e2e/fixtures/simple-pypi/requirements.txt +3 -0
- socketsecurity-2.2.81/tests/e2e/validate-gitlab.sh +63 -0
- socketsecurity-2.2.81/tests/e2e/validate-json.sh +33 -0
- socketsecurity-2.2.81/tests/e2e/validate-reachability.sh +65 -0
- socketsecurity-2.2.81/tests/e2e/validate-sarif.sh +19 -0
- socketsecurity-2.2.81/tests/e2e/validate-scan.sh +16 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/unit/test_gitlab_format.py +175 -2
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/uv.lock +1 -1
- socketsecurity-2.2.80/.github/workflows/e2e-test.yml +0 -201
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.github/CODEOWNERS +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.github/workflows/docker-stable.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.github/workflows/pr-preview.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.github/workflows/python-tests.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.github/workflows/release.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.github/workflows/version-check.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.github/zizmor.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.gitignore +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.hooks/sync_version.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.pre-commit-config.yaml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/.python-version +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/CHANGELOG.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/Dockerfile +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/LICENSE +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/Makefile +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/README.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/docs/ci-cd.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/docs/development.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/docs/troubleshooting.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/examples/config/sarif-dashboard-parity.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/examples/config/sarif-dashboard-parity.toml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/examples/config/sarif-diff-ci-cd.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/examples/config/sarif-diff-ci-cd.toml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/examples/config/sarif-instance-detail.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/examples/config/sarif-instance-detail.toml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/instructions/gitlab-commit-status/uat.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/pytest.ini +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/scripts/build_container.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/scripts/build_container_flexible.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/scripts/deploy-test-docker.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/scripts/deploy-test-pypi.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/scripts/docker-entrypoint.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/scripts/run.sh +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/session.md +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socket.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/config.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/alert_selection.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/classes.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/cli_client.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/helper/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/lazy_file_loader.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/logging.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/resource_utils.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/scm/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/scm/base.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/scm/client.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/scm/github.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/scm/gitlab.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/socket_config.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/tools/reachability.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/core/utils.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/output.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/plugins/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/plugins/base.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/plugins/formatters/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/plugins/formatters/slack.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/plugins/jira.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/plugins/manager.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/plugins/slack.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/plugins/teams.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/socketsecurity/plugins/webhook.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/core/conftest.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/core/create_diff_input.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/core/test_diff_alerts.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/core/test_diff_generation.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/core/test_has_manifest_files.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/core/test_package_and_alerts.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/core/test_sdk_methods.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/core/test_supporting_methods.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/fullscans/create_response.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/fullscans/diff/stream_diff.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/fullscans/head_scan/metadata.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/fullscans/new_scan/metadata.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/repos/repo_info_error.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/repos/repo_info_no_head.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/repos/repo_info_success.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/data/settings/security-policy.json +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/e2e/fixtures/simple-npm/index.js +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/unit/__init__.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/unit/test_alert_selection.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/unit/test_cli_config.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/unit/test_client.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/unit/test_config.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/unit/test_gitlab_auth.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/unit/test_gitlab_auth_fallback.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/unit/test_gitlab_commit_status.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/unit/test_output.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/tests/unit/test_slack_plugin.py +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/workflows/bitbucket-pipelines.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/workflows/buildkite.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/workflows/github-actions.yml +0 -0
- {socketsecurity-2.2.80 → socketsecurity-2.2.81}/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 }}
|
|
@@ -700,17 +700,44 @@ The GitLab report includes **actionable security alerts** based on your Socket p
|
|
|
700
700
|
|
|
701
701
|
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
702
|
|
|
703
|
+
### Alert Population: GitLab vs JSON/SARIF
|
|
704
|
+
|
|
705
|
+
The GitLab Security Dashboard report and the JSON/SARIF diff outputs use different alert selection strategies, reflecting their distinct purposes:
|
|
706
|
+
|
|
707
|
+
| Output Format | Default Alerts | With `--strict-blocking` |
|
|
708
|
+
|:---|:---|:---|
|
|
709
|
+
| `--enable-gitlab-security` | **All** alerts (new + existing) | All alerts (same) |
|
|
710
|
+
| `--enable-json` | New alerts only | New + existing alerts |
|
|
711
|
+
| `--enable-sarif` (diff scope) | New alerts only | New + existing alerts |
|
|
712
|
+
|
|
713
|
+
**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.
|
|
714
|
+
|
|
715
|
+
> **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`.
|
|
716
|
+
|
|
717
|
+
### Alert Ignoring via PR/MR Comments
|
|
718
|
+
|
|
719
|
+
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.
|
|
720
|
+
|
|
721
|
+
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.
|
|
722
|
+
|
|
703
723
|
### Report Schema
|
|
704
724
|
|
|
705
|
-
Socket CLI generates reports compliant with [GitLab Dependency Scanning schema version 15.0.0](https://
|
|
725
|
+
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
726
|
|
|
707
|
-
- **Scan metadata**: Analyzer and scanner information
|
|
727
|
+
- **Scan metadata**: Analyzer and scanner information with ISO 8601 timestamps
|
|
708
728
|
- **Vulnerabilities**: Detailed vulnerability data with:
|
|
709
729
|
- Unique deterministic UUIDs for tracking
|
|
710
730
|
- Package location and dependency information
|
|
711
731
|
- Severity levels mapped from Socket's analysis
|
|
712
732
|
- Socket-specific alert types and CVE identifiers
|
|
713
733
|
- Links to Socket.dev for detailed analysis
|
|
734
|
+
- **Dependency files**: Manifest files and their dependencies discovered during the scan
|
|
735
|
+
|
|
736
|
+
**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.
|
|
737
|
+
|
|
738
|
+
### Performance Notes
|
|
739
|
+
|
|
740
|
+
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
741
|
|
|
715
742
|
### Requirements
|
|
716
743
|
|
|
@@ -726,7 +753,9 @@ Socket CLI generates reports compliant with [GitLab Dependency Scanning schema v
|
|
|
726
753
|
- Ensure the report file follows the correct schema format
|
|
727
754
|
|
|
728
755
|
**Empty vulnerabilities array:**
|
|
729
|
-
-
|
|
756
|
+
- 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
|
|
757
|
+
- 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
|
|
758
|
+
- For full scans (non-diff mode), ensure you are using `--enable-gitlab-security` so alert data is fetched
|
|
730
759
|
- Check Socket.dev dashboard for full analysis details
|
|
731
760
|
|
|
732
761
|
## Development
|
|
@@ -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.
|
|
@@ -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
|
|
@@ -649,11 +649,20 @@ def main_code():
|
|
|
649
649
|
scm.enable_merge_pipeline_check()
|
|
650
650
|
passed = output_handler.report_pass(diff)
|
|
651
651
|
state = "success" if passed else "failed"
|
|
652
|
-
|
|
652
|
+
new_blocking = sum(1 for a in diff.new_alerts if a.error)
|
|
653
|
+
unchanged_blocking = 0
|
|
654
|
+
if config.strict_blocking and hasattr(diff, 'unchanged_alerts'):
|
|
655
|
+
unchanged_blocking = sum(1 for a in diff.unchanged_alerts if a.error)
|
|
656
|
+
blocking_count = new_blocking + unchanged_blocking
|
|
653
657
|
if passed:
|
|
654
658
|
description = "No blocking issues"
|
|
655
659
|
else:
|
|
656
|
-
|
|
660
|
+
parts = []
|
|
661
|
+
if new_blocking:
|
|
662
|
+
parts.append(f"{new_blocking} new")
|
|
663
|
+
if unchanged_blocking:
|
|
664
|
+
parts.append(f"{unchanged_blocking} existing")
|
|
665
|
+
description = f"{blocking_count} blocking alert(s) found ({', '.join(parts)})"
|
|
657
666
|
target_url = diff.report_url or diff.diff_url or ""
|
|
658
667
|
scm.set_commit_status(state, description, target_url)
|
|
659
668
|
|
|
@@ -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
|
+
"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
LOG="/tmp/e2e-output.log"
|
|
5
|
+
|
|
6
|
+
python3 -c "
|
|
7
|
+
import json, sys
|
|
8
|
+
|
|
9
|
+
# The JSON output may be prefixed with a logger timestamp (e.g. '2026-04-08 22:46:50,580: {...}').
|
|
10
|
+
# Try parsing the full line first, then from the first '{' character.
|
|
11
|
+
found = False
|
|
12
|
+
with open('$LOG') as f:
|
|
13
|
+
for line in f:
|
|
14
|
+
line = line.strip()
|
|
15
|
+
if not line or '{' not in line:
|
|
16
|
+
continue
|
|
17
|
+
# Try full line first, then from the first brace
|
|
18
|
+
for candidate in (line, line[line.index('{'):]):
|
|
19
|
+
try:
|
|
20
|
+
data = json.loads(candidate)
|
|
21
|
+
if isinstance(data, dict):
|
|
22
|
+
found = True
|
|
23
|
+
print(f'PASS: Valid JSON output with {len(data)} top-level key(s)')
|
|
24
|
+
break
|
|
25
|
+
except json.JSONDecodeError:
|
|
26
|
+
continue
|
|
27
|
+
if found:
|
|
28
|
+
break
|
|
29
|
+
|
|
30
|
+
if not found:
|
|
31
|
+
print('FAIL: No valid JSON object found in output')
|
|
32
|
+
sys.exit(1)
|
|
33
|
+
"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
LOG="/tmp/e2e-output.log"
|
|
5
|
+
|
|
6
|
+
# 1. Verify reachability analysis completed
|
|
7
|
+
if grep -q "Reachability analysis completed successfully" "$LOG"; then
|
|
8
|
+
echo "PASS: Reachability analysis completed"
|
|
9
|
+
grep "Reachability analysis completed successfully" "$LOG"
|
|
10
|
+
grep "Results written to:" "$LOG" || true
|
|
11
|
+
else
|
|
12
|
+
echo "FAIL: Reachability analysis did not complete successfully"
|
|
13
|
+
cat "$LOG"
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# 2. Verify scan produced a report URL
|
|
18
|
+
if grep -q "Full scan report URL: https://socket.dev/" "$LOG"; then
|
|
19
|
+
echo "PASS: Full scan report URL found"
|
|
20
|
+
grep "Full scan report URL:" "$LOG"
|
|
21
|
+
elif grep -q "Diff Url: https://socket.dev/" "$LOG"; then
|
|
22
|
+
echo "PASS: Diff URL found"
|
|
23
|
+
grep "Diff Url:" "$LOG"
|
|
24
|
+
else
|
|
25
|
+
echo "FAIL: No report URL found in scan output"
|
|
26
|
+
cat "$LOG"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# 3. Run SARIF with --sarif-reachability all
|
|
31
|
+
socketcli \
|
|
32
|
+
--target-path tests/e2e/fixtures/simple-npm \
|
|
33
|
+
--reach \
|
|
34
|
+
--sarif-file /tmp/sarif-all.sarif \
|
|
35
|
+
--sarif-scope full \
|
|
36
|
+
--sarif-reachability all \
|
|
37
|
+
--disable-blocking \
|
|
38
|
+
2>/dev/null
|
|
39
|
+
|
|
40
|
+
# 4. Run SARIF with --sarif-reachability reachable (filtered)
|
|
41
|
+
socketcli \
|
|
42
|
+
--target-path tests/e2e/fixtures/simple-npm \
|
|
43
|
+
--reach \
|
|
44
|
+
--sarif-file /tmp/sarif-reachable.sarif \
|
|
45
|
+
--sarif-scope full \
|
|
46
|
+
--sarif-reachability reachable \
|
|
47
|
+
--disable-blocking \
|
|
48
|
+
2>/dev/null
|
|
49
|
+
|
|
50
|
+
# 5. Verify reachable-only results are a subset of all results
|
|
51
|
+
test -f /tmp/sarif-all.sarif
|
|
52
|
+
test -f /tmp/sarif-reachable.sarif
|
|
53
|
+
|
|
54
|
+
python3 -c "
|
|
55
|
+
import json
|
|
56
|
+
with open('/tmp/sarif-all.sarif') as f:
|
|
57
|
+
all_data = json.load(f)
|
|
58
|
+
with open('/tmp/sarif-reachable.sarif') as f:
|
|
59
|
+
reach_data = json.load(f)
|
|
60
|
+
all_count = len(all_data['runs'][0]['results'])
|
|
61
|
+
reach_count = len(reach_data['runs'][0]['results'])
|
|
62
|
+
print(f'All results: {all_count}, Reachable-only results: {reach_count}')
|
|
63
|
+
assert reach_count <= all_count, f'FAIL: reachable ({reach_count}) > all ({all_count})'
|
|
64
|
+
print('PASS: Reachable-only results is a subset of all results')
|
|
65
|
+
"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SARIF="/tmp/results.sarif"
|
|
5
|
+
|
|
6
|
+
if [ ! -f "$SARIF" ]; then
|
|
7
|
+
echo "FAIL: SARIF file not found at $SARIF"
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
python3 -c "
|
|
12
|
+
import json, sys
|
|
13
|
+
with open('$SARIF') as f:
|
|
14
|
+
data = json.load(f)
|
|
15
|
+
assert data['version'] == '2.1.0', f'Invalid version: {data[\"version\"]}'
|
|
16
|
+
assert '\$schema' in data, 'Missing \$schema'
|
|
17
|
+
count = len(data['runs'][0]['results'])
|
|
18
|
+
print(f'PASS: Valid SARIF 2.1.0 with {count} result(s)')
|
|
19
|
+
"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
LOG="/tmp/e2e-output.log"
|
|
5
|
+
|
|
6
|
+
if grep -q "Full scan report URL: https://socket.dev/" "$LOG"; then
|
|
7
|
+
echo "PASS: Full scan report URL found"
|
|
8
|
+
grep "Full scan report URL:" "$LOG"
|
|
9
|
+
elif grep -q "Diff Url: https://socket.dev/" "$LOG"; then
|
|
10
|
+
echo "PASS: Diff URL found"
|
|
11
|
+
grep "Diff Url:" "$LOG"
|
|
12
|
+
else
|
|
13
|
+
echo "FAIL: No report URL found in scan output"
|
|
14
|
+
cat "$LOG"
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|