socketsecurity 2.2.75__tar.gz → 2.2.77__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.github/workflows/e2e-test.yml +86 -0
- socketsecurity-2.2.77/.github/workflows/python-tests.yml +52 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.gitignore +1 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/PKG-INFO +19 -8
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/README.md +18 -7
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/pyproject.toml +1 -1
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/__init__.py +1 -1
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/config.py +26 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/__init__.py +10 -13
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/classes.py +44 -9
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/output.py +32 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/plugins/slack.py +24 -14
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/core/conftest.py +11 -4
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/core/test_diff_generation.py +8 -24
- socketsecurity-2.2.77/tests/core/test_has_manifest_files.py +68 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/core/test_package_and_alerts.py +44 -14
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/core/test_sdk_methods.py +25 -19
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/core/test_supporting_methods.py +30 -20
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/repos/repo_info_no_head.json +2 -1
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/repos/repo_info_success.json +2 -1
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/unit/test_config.py +24 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/unit/test_gitlab_auth_fallback.py +2 -0
- socketsecurity-2.2.77/tests/unit/test_output.py +338 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/uv.lock +5 -5
- socketsecurity-2.2.75/tests/unit/test_output.py +0 -159
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.github/CODEOWNERS +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.github/workflows/docker-stable.yml +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.github/workflows/pr-preview.yml +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.github/workflows/release.yml +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.github/workflows/version-check.yml +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.hooks/sync_version.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.pre-commit-config.yaml +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/.python-version +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/CHANGELOG.md +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/Dockerfile +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/LICENSE +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/Makefile +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/docs/README.md +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/instructions/gitlab-commit-status/uat.md +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/pytest.ini +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/scripts/build_container.sh +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/scripts/build_container_flexible.sh +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/scripts/deploy-test-docker.sh +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/scripts/deploy-test-pypi.sh +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/scripts/docker-entrypoint.sh +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/scripts/run.sh +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/session.md +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socket.yml +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/cli_client.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/exceptions.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/git_interface.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/helper/__init__.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/lazy_file_loader.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/logging.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/messages.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/resource_utils.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/scm/__init__.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/scm/base.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/scm/client.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/scm/github.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/scm/gitlab.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/scm_comments.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/socket_config.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/tools/reachability.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/core/utils.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/plugins/__init__.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/plugins/base.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/plugins/formatters/__init__.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/plugins/formatters/slack.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/plugins/jira.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/plugins/manager.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/plugins/teams.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/plugins/webhook.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/socketsecurity/socketcli.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/__init__.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/core/create_diff_input.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/core/test_diff_alerts.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/fullscans/create_response.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/fullscans/diff/stream_diff.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/fullscans/head_scan/metadata.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/fullscans/new_scan/metadata.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/repos/repo_info_error.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/data/settings/security-policy.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/e2e/fixtures/simple-npm/index.js +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/e2e/fixtures/simple-npm/package.json +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/unit/__init__.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/unit/test_cli_config.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/unit/test_client.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/unit/test_gitlab_auth.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/unit/test_gitlab_commit_status.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/tests/unit/test_gitlab_format.py +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/workflows/bitbucket-pipelines.yml +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/workflows/github-actions.yml +0 -0
- {socketsecurity-2.2.75 → socketsecurity-2.2.77}/workflows/gitlab-ci.yml +0 -0
|
@@ -47,6 +47,54 @@ jobs:
|
|
|
47
47
|
exit 1
|
|
48
48
|
fi
|
|
49
49
|
|
|
50
|
+
e2e-sarif:
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871
|
|
54
|
+
with:
|
|
55
|
+
fetch-depth: 0
|
|
56
|
+
|
|
57
|
+
- uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
|
|
58
|
+
with:
|
|
59
|
+
python-version: '3.12'
|
|
60
|
+
|
|
61
|
+
- name: Install CLI from local repo
|
|
62
|
+
run: |
|
|
63
|
+
python -m pip install --upgrade pip
|
|
64
|
+
pip install .
|
|
65
|
+
|
|
66
|
+
- name: Verify --sarif-reachable-only without --reach exits non-zero
|
|
67
|
+
run: |
|
|
68
|
+
if socketcli --sarif-reachable-only --api-token dummy 2>&1; then
|
|
69
|
+
echo "FAIL: Expected non-zero exit"
|
|
70
|
+
exit 1
|
|
71
|
+
else
|
|
72
|
+
echo "PASS: Exited non-zero as expected"
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
- name: Run Socket CLI scan with --sarif-file
|
|
76
|
+
env:
|
|
77
|
+
SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_CLI_API_TOKEN }}
|
|
78
|
+
run: |
|
|
79
|
+
set -o pipefail
|
|
80
|
+
socketcli \
|
|
81
|
+
--target-path tests/e2e/fixtures/simple-npm \
|
|
82
|
+
--sarif-file /tmp/results.sarif \
|
|
83
|
+
--disable-blocking \
|
|
84
|
+
2>&1 | tee /tmp/sarif-output.log
|
|
85
|
+
|
|
86
|
+
- name: Verify SARIF file is valid
|
|
87
|
+
run: |
|
|
88
|
+
python3 -c "
|
|
89
|
+
import json, sys
|
|
90
|
+
with open('/tmp/results.sarif') as f:
|
|
91
|
+
data = json.load(f)
|
|
92
|
+
assert data['version'] == '2.1.0', f'Invalid version: {data[\"version\"]}'
|
|
93
|
+
assert '\$schema' in data, 'Missing \$schema'
|
|
94
|
+
count = len(data['runs'][0]['results'])
|
|
95
|
+
print(f'PASS: Valid SARIF 2.1.0 with {count} result(s)')
|
|
96
|
+
"
|
|
97
|
+
|
|
50
98
|
e2e-reachability:
|
|
51
99
|
runs-on: ubuntu-latest
|
|
52
100
|
steps:
|
|
@@ -107,3 +155,41 @@ jobs:
|
|
|
107
155
|
cat /tmp/reach-output.log
|
|
108
156
|
exit 1
|
|
109
157
|
fi
|
|
158
|
+
|
|
159
|
+
- name: Run scan with --sarif-file (all results)
|
|
160
|
+
env:
|
|
161
|
+
SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_CLI_API_TOKEN }}
|
|
162
|
+
run: |
|
|
163
|
+
socketcli \
|
|
164
|
+
--target-path tests/e2e/fixtures/simple-npm \
|
|
165
|
+
--reach \
|
|
166
|
+
--sarif-file /tmp/sarif-all.sarif \
|
|
167
|
+
--disable-blocking \
|
|
168
|
+
2>/dev/null || true
|
|
169
|
+
|
|
170
|
+
- name: Run scan with --sarif-file --sarif-reachable-only (filtered results)
|
|
171
|
+
env:
|
|
172
|
+
SOCKET_SECURITY_API_KEY: ${{ secrets.SOCKET_CLI_API_TOKEN }}
|
|
173
|
+
run: |
|
|
174
|
+
socketcli \
|
|
175
|
+
--target-path tests/e2e/fixtures/simple-npm \
|
|
176
|
+
--reach \
|
|
177
|
+
--sarif-file /tmp/sarif-reachable.sarif \
|
|
178
|
+
--sarif-reachable-only \
|
|
179
|
+
--disable-blocking \
|
|
180
|
+
2>/dev/null || true
|
|
181
|
+
|
|
182
|
+
- name: Verify reachable-only results are a subset of all results
|
|
183
|
+
run: |
|
|
184
|
+
python3 -c "
|
|
185
|
+
import json
|
|
186
|
+
with open('/tmp/sarif-all.sarif') as f:
|
|
187
|
+
all_data = json.load(f)
|
|
188
|
+
with open('/tmp/sarif-reachable.sarif') as f:
|
|
189
|
+
reach_data = json.load(f)
|
|
190
|
+
all_count = len(all_data['runs'][0]['results'])
|
|
191
|
+
reach_count = len(reach_data['runs'][0]['results'])
|
|
192
|
+
print(f'All results: {all_count}, Reachable-only results: {reach_count}')
|
|
193
|
+
assert reach_count <= all_count, f'FAIL: reachable ({reach_count}) > all ({all_count})'
|
|
194
|
+
print('PASS: Reachable-only results is a subset of all results')
|
|
195
|
+
"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: Unit Tests
|
|
2
|
+
|
|
3
|
+
env:
|
|
4
|
+
PYTHON_VERSION: "3.12"
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
paths:
|
|
10
|
+
- "socketsecurity/**/*.py"
|
|
11
|
+
- "tests/unit/**/*.py"
|
|
12
|
+
- "tests/core/**/*.py"
|
|
13
|
+
- "pyproject.toml"
|
|
14
|
+
- "uv.lock"
|
|
15
|
+
- ".github/workflows/python-tests.yml"
|
|
16
|
+
pull_request:
|
|
17
|
+
paths:
|
|
18
|
+
- "socketsecurity/**/*.py"
|
|
19
|
+
- "tests/unit/**/*.py"
|
|
20
|
+
- "tests/core/**/*.py"
|
|
21
|
+
- "pyproject.toml"
|
|
22
|
+
- "uv.lock"
|
|
23
|
+
- ".github/workflows/python-tests.yml"
|
|
24
|
+
workflow_dispatch:
|
|
25
|
+
|
|
26
|
+
permissions:
|
|
27
|
+
contents: read
|
|
28
|
+
|
|
29
|
+
concurrency:
|
|
30
|
+
group: python-tests-${{ github.ref }}
|
|
31
|
+
cancel-in-progress: true
|
|
32
|
+
|
|
33
|
+
jobs:
|
|
34
|
+
python-tests:
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
timeout-minutes: 20
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871
|
|
39
|
+
with:
|
|
40
|
+
fetch-depth: 1
|
|
41
|
+
persist-credentials: false
|
|
42
|
+
- name: 🐍 setup python
|
|
43
|
+
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3
|
|
44
|
+
with:
|
|
45
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
46
|
+
- name: 🛠️ install deps
|
|
47
|
+
run: |
|
|
48
|
+
python -m pip install --upgrade pip
|
|
49
|
+
pip install uv
|
|
50
|
+
uv sync --extra test
|
|
51
|
+
- name: 🧪 run tests
|
|
52
|
+
run: uv run pytest -q tests/unit/ tests/core/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: socketsecurity
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.77
|
|
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>
|
|
@@ -152,18 +152,27 @@ This will:
|
|
|
152
152
|
- Save to `gl-dependency-scanning-report.json`
|
|
153
153
|
- Include all actionable security alerts (error/warn level)
|
|
154
154
|
|
|
155
|
+
**Save SARIF report to file (e.g. for GitHub Code Scanning, SonarQube, or VS Code):**
|
|
156
|
+
```bash
|
|
157
|
+
socketcli --sarif-file results.sarif \
|
|
158
|
+
--repo owner/repo \
|
|
159
|
+
--target-path .
|
|
160
|
+
```
|
|
161
|
+
|
|
155
162
|
**Multiple output formats:**
|
|
156
163
|
```bash
|
|
157
164
|
socketcli --enable-json \
|
|
158
|
-
--
|
|
165
|
+
--sarif-file results.sarif \
|
|
159
166
|
--enable-gitlab-security \
|
|
160
167
|
--repo owner/repo
|
|
161
168
|
```
|
|
162
169
|
|
|
163
170
|
This will simultaneously generate:
|
|
164
171
|
- JSON output to console
|
|
165
|
-
- SARIF
|
|
166
|
-
- GitLab Security Dashboard report to
|
|
172
|
+
- SARIF report to `results.sarif` (and stdout)
|
|
173
|
+
- GitLab Security Dashboard report to `gl-dependency-scanning-report.json`
|
|
174
|
+
|
|
175
|
+
> **Note:** `--enable-sarif` prints SARIF to stdout only. Use `--sarif-file <path>` to save to a file (this also implies `--enable-sarif`). Add `--sarif-reachable-only` (requires `--reach`) to filter results down to only reachable findings — useful for uploading to GitHub Code Scanning without noisy alerts on unreachable vulns. These flags are independent from `--enable-gitlab-security`, which produces a separate GitLab-specific Dependency Scanning report.
|
|
167
176
|
|
|
168
177
|
### Requirements
|
|
169
178
|
|
|
@@ -179,7 +188,7 @@ socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--workspace WORKSPACE] [--
|
|
|
179
188
|
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--license-file-name LICENSE_FILE_NAME] [--save-submitted-files-list SAVE_SUBMITTED_FILES_LIST]
|
|
180
189
|
[--save-manifest-tar SAVE_MANIFEST_TAR] [--files FILES] [--sub-path SUB_PATH] [--workspace-name WORKSPACE_NAME]
|
|
181
190
|
[--excluded-ecosystems EXCLUDED_ECOSYSTEMS] [--default-branch] [--pending-head] [--generate-license] [--enable-debug]
|
|
182
|
-
[--enable-json] [--enable-sarif] [--enable-gitlab-security] [--gitlab-security-file <path>]
|
|
191
|
+
[--enable-json] [--enable-sarif] [--sarif-file <path>] [--sarif-reachable-only] [--enable-gitlab-security] [--gitlab-security-file <path>]
|
|
183
192
|
[--disable-overview] [--exclude-license-details] [--allow-unverified] [--disable-security-issue]
|
|
184
193
|
[--ignore-commit-files] [--disable-blocking] [--enable-diff] [--scm SCM] [--timeout TIMEOUT] [--include-module-folders]
|
|
185
194
|
[--reach] [--reach-version REACH_VERSION] [--reach-analysis-timeout REACH_ANALYSIS_TIMEOUT]
|
|
@@ -247,7 +256,9 @@ If you don't want to provide the Socket API Token every time then you can use th
|
|
|
247
256
|
| --generate-license | False | False | Generate license information |
|
|
248
257
|
| --enable-debug | False | False | Enable debug logging |
|
|
249
258
|
| --enable-json | False | False | Output in JSON format |
|
|
250
|
-
| --enable-sarif | False | False | Enable SARIF output of results instead of table or JSON format
|
|
259
|
+
| --enable-sarif | False | False | Enable SARIF output of results instead of table or JSON format (prints to stdout) |
|
|
260
|
+
| --sarif-file | False | | Output file path for SARIF report (implies --enable-sarif). Use this to save SARIF output to a file for upload to GitHub Code Scanning, SonarQube, VS Code, or other SARIF-compatible tools |
|
|
261
|
+
| --sarif-reachable-only | False | False | Filter SARIF output to only include reachable findings (requires --reach) |
|
|
251
262
|
| --enable-gitlab-security | False | False | Enable GitLab Security Dashboard output format (Dependency Scanning report) |
|
|
252
263
|
| --gitlab-security-file | False | gl-dependency-scanning-report.json | Output file path for GitLab Security report |
|
|
253
264
|
| --disable-overview | False | False | Disable overview output |
|
|
@@ -783,13 +794,13 @@ socketcli --enable-gitlab-security --gitlab-security-file custom-path.json
|
|
|
783
794
|
GitLab security reports can be generated alongside other output formats:
|
|
784
795
|
|
|
785
796
|
```bash
|
|
786
|
-
socketcli --enable-json --enable-gitlab-security --
|
|
797
|
+
socketcli --enable-json --enable-gitlab-security --sarif-file results.sarif
|
|
787
798
|
```
|
|
788
799
|
|
|
789
800
|
This command will:
|
|
790
801
|
- Output JSON format to console
|
|
791
802
|
- Save GitLab Security Dashboard report to `gl-dependency-scanning-report.json`
|
|
792
|
-
- Save SARIF report
|
|
803
|
+
- Save SARIF report to `results.sarif`
|
|
793
804
|
|
|
794
805
|
### Security Dashboard Features
|
|
795
806
|
|
|
@@ -94,18 +94,27 @@ This will:
|
|
|
94
94
|
- Save to `gl-dependency-scanning-report.json`
|
|
95
95
|
- Include all actionable security alerts (error/warn level)
|
|
96
96
|
|
|
97
|
+
**Save SARIF report to file (e.g. for GitHub Code Scanning, SonarQube, or VS Code):**
|
|
98
|
+
```bash
|
|
99
|
+
socketcli --sarif-file results.sarif \
|
|
100
|
+
--repo owner/repo \
|
|
101
|
+
--target-path .
|
|
102
|
+
```
|
|
103
|
+
|
|
97
104
|
**Multiple output formats:**
|
|
98
105
|
```bash
|
|
99
106
|
socketcli --enable-json \
|
|
100
|
-
--
|
|
107
|
+
--sarif-file results.sarif \
|
|
101
108
|
--enable-gitlab-security \
|
|
102
109
|
--repo owner/repo
|
|
103
110
|
```
|
|
104
111
|
|
|
105
112
|
This will simultaneously generate:
|
|
106
113
|
- JSON output to console
|
|
107
|
-
- SARIF
|
|
108
|
-
- GitLab Security Dashboard report to
|
|
114
|
+
- SARIF report to `results.sarif` (and stdout)
|
|
115
|
+
- GitLab Security Dashboard report to `gl-dependency-scanning-report.json`
|
|
116
|
+
|
|
117
|
+
> **Note:** `--enable-sarif` prints SARIF to stdout only. Use `--sarif-file <path>` to save to a file (this also implies `--enable-sarif`). Add `--sarif-reachable-only` (requires `--reach`) to filter results down to only reachable findings — useful for uploading to GitHub Code Scanning without noisy alerts on unreachable vulns. These flags are independent from `--enable-gitlab-security`, which produces a separate GitLab-specific Dependency Scanning report.
|
|
109
118
|
|
|
110
119
|
### Requirements
|
|
111
120
|
|
|
@@ -121,7 +130,7 @@ socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--workspace WORKSPACE] [--
|
|
|
121
130
|
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--license-file-name LICENSE_FILE_NAME] [--save-submitted-files-list SAVE_SUBMITTED_FILES_LIST]
|
|
122
131
|
[--save-manifest-tar SAVE_MANIFEST_TAR] [--files FILES] [--sub-path SUB_PATH] [--workspace-name WORKSPACE_NAME]
|
|
123
132
|
[--excluded-ecosystems EXCLUDED_ECOSYSTEMS] [--default-branch] [--pending-head] [--generate-license] [--enable-debug]
|
|
124
|
-
[--enable-json] [--enable-sarif] [--enable-gitlab-security] [--gitlab-security-file <path>]
|
|
133
|
+
[--enable-json] [--enable-sarif] [--sarif-file <path>] [--sarif-reachable-only] [--enable-gitlab-security] [--gitlab-security-file <path>]
|
|
125
134
|
[--disable-overview] [--exclude-license-details] [--allow-unverified] [--disable-security-issue]
|
|
126
135
|
[--ignore-commit-files] [--disable-blocking] [--enable-diff] [--scm SCM] [--timeout TIMEOUT] [--include-module-folders]
|
|
127
136
|
[--reach] [--reach-version REACH_VERSION] [--reach-analysis-timeout REACH_ANALYSIS_TIMEOUT]
|
|
@@ -189,7 +198,9 @@ If you don't want to provide the Socket API Token every time then you can use th
|
|
|
189
198
|
| --generate-license | False | False | Generate license information |
|
|
190
199
|
| --enable-debug | False | False | Enable debug logging |
|
|
191
200
|
| --enable-json | False | False | Output in JSON format |
|
|
192
|
-
| --enable-sarif | False | False | Enable SARIF output of results instead of table or JSON format
|
|
201
|
+
| --enable-sarif | False | False | Enable SARIF output of results instead of table or JSON format (prints to stdout) |
|
|
202
|
+
| --sarif-file | False | | Output file path for SARIF report (implies --enable-sarif). Use this to save SARIF output to a file for upload to GitHub Code Scanning, SonarQube, VS Code, or other SARIF-compatible tools |
|
|
203
|
+
| --sarif-reachable-only | False | False | Filter SARIF output to only include reachable findings (requires --reach) |
|
|
193
204
|
| --enable-gitlab-security | False | False | Enable GitLab Security Dashboard output format (Dependency Scanning report) |
|
|
194
205
|
| --gitlab-security-file | False | gl-dependency-scanning-report.json | Output file path for GitLab Security report |
|
|
195
206
|
| --disable-overview | False | False | Disable overview output |
|
|
@@ -725,13 +736,13 @@ socketcli --enable-gitlab-security --gitlab-security-file custom-path.json
|
|
|
725
736
|
GitLab security reports can be generated alongside other output formats:
|
|
726
737
|
|
|
727
738
|
```bash
|
|
728
|
-
socketcli --enable-json --enable-gitlab-security --
|
|
739
|
+
socketcli --enable-json --enable-gitlab-security --sarif-file results.sarif
|
|
729
740
|
```
|
|
730
741
|
|
|
731
742
|
This command will:
|
|
732
743
|
- Output JSON format to console
|
|
733
744
|
- Save GitLab Security Dashboard report to `gl-dependency-scanning-report.json`
|
|
734
|
-
- Save SARIF report
|
|
745
|
+
- Save SARIF report to `results.sarif`
|
|
735
746
|
|
|
736
747
|
### Security Dashboard Features
|
|
737
748
|
|
|
@@ -40,6 +40,8 @@ class CliConfig:
|
|
|
40
40
|
allow_unverified: bool = False
|
|
41
41
|
enable_json: bool = False
|
|
42
42
|
enable_sarif: bool = False
|
|
43
|
+
sarif_file: Optional[str] = None
|
|
44
|
+
sarif_reachable_only: bool = False
|
|
43
45
|
enable_gitlab_security: bool = False
|
|
44
46
|
gitlab_security_file: Optional[str] = None
|
|
45
47
|
disable_overview: bool = False
|
|
@@ -103,6 +105,10 @@ class CliConfig:
|
|
|
103
105
|
args.api_token
|
|
104
106
|
)
|
|
105
107
|
|
|
108
|
+
# --sarif-file implies --enable-sarif
|
|
109
|
+
if args.sarif_file:
|
|
110
|
+
args.enable_sarif = True
|
|
111
|
+
|
|
106
112
|
# Strip quotes from commit message if present
|
|
107
113
|
commit_message = args.commit_message
|
|
108
114
|
if commit_message and commit_message.startswith('"') and commit_message.endswith('"'):
|
|
@@ -126,6 +132,8 @@ class CliConfig:
|
|
|
126
132
|
'allow_unverified': args.allow_unverified,
|
|
127
133
|
'enable_json': args.enable_json,
|
|
128
134
|
'enable_sarif': args.enable_sarif,
|
|
135
|
+
'sarif_file': args.sarif_file,
|
|
136
|
+
'sarif_reachable_only': args.sarif_reachable_only,
|
|
129
137
|
'enable_gitlab_security': args.enable_gitlab_security,
|
|
130
138
|
'gitlab_security_file': args.gitlab_security_file,
|
|
131
139
|
'disable_overview': args.disable_overview,
|
|
@@ -204,6 +212,11 @@ class CliConfig:
|
|
|
204
212
|
logging.error("--workspace-name requires --sub-path to be specified")
|
|
205
213
|
exit(1)
|
|
206
214
|
|
|
215
|
+
# Validate that sarif_reachable_only requires reach
|
|
216
|
+
if args.sarif_reachable_only and not args.reach:
|
|
217
|
+
logging.error("--sarif-reachable-only requires --reach to be specified")
|
|
218
|
+
exit(1)
|
|
219
|
+
|
|
207
220
|
# Validate that only_facts_file requires reach
|
|
208
221
|
if args.only_facts_file and not args.reach:
|
|
209
222
|
logging.error("--only-facts-file requires --reach to be specified")
|
|
@@ -471,6 +484,19 @@ def create_argument_parser() -> argparse.ArgumentParser:
|
|
|
471
484
|
action="store_true",
|
|
472
485
|
help="Enable SARIF output of results instead of table or JSON format"
|
|
473
486
|
)
|
|
487
|
+
output_group.add_argument(
|
|
488
|
+
"--sarif-file",
|
|
489
|
+
dest="sarif_file",
|
|
490
|
+
metavar="<path>",
|
|
491
|
+
default=None,
|
|
492
|
+
help="Output file path for SARIF report (implies --enable-sarif)"
|
|
493
|
+
)
|
|
494
|
+
output_group.add_argument(
|
|
495
|
+
"--sarif-reachable-only",
|
|
496
|
+
dest="sarif_reachable_only",
|
|
497
|
+
action="store_true",
|
|
498
|
+
help="Filter SARIF output to only include reachable findings (requires --reach)"
|
|
499
|
+
)
|
|
474
500
|
output_group.add_argument(
|
|
475
501
|
"--enable-gitlab-security",
|
|
476
502
|
dest="enable_gitlab_security",
|
|
@@ -88,19 +88,16 @@ class Core:
|
|
|
88
88
|
return org_id, organizations[org_id]['slug']
|
|
89
89
|
return None, None
|
|
90
90
|
|
|
91
|
-
def get_sbom_data(self, full_scan_id: str) ->
|
|
92
|
-
"""Returns
|
|
91
|
+
def get_sbom_data(self, full_scan_id: str) -> Dict[str, SocketArtifact]:
|
|
92
|
+
"""Returns SBOM artifacts for a full scan keyed by artifact ID."""
|
|
93
93
|
response = self.sdk.fullscans.stream(self.config.org_slug, full_scan_id, use_types=True)
|
|
94
|
-
artifacts: List[SocketArtifact] = []
|
|
95
94
|
if not response.success:
|
|
96
95
|
log.debug(f"Failed to get SBOM data for full-scan {full_scan_id}")
|
|
97
96
|
log.debug(response.message)
|
|
98
97
|
return {}
|
|
99
98
|
if not hasattr(response, "artifacts") or not response.artifacts:
|
|
100
|
-
return
|
|
101
|
-
|
|
102
|
-
artifacts.append(response.artifacts[artifact_id])
|
|
103
|
-
return artifacts
|
|
99
|
+
return {}
|
|
100
|
+
return response.artifacts
|
|
104
101
|
|
|
105
102
|
def get_sbom_data_list(self, artifacts_dict: Dict[str, SocketArtifact]) -> list[SocketArtifact]:
|
|
106
103
|
"""Converts artifacts dictionary to a list."""
|
|
@@ -414,15 +411,15 @@ class Core:
|
|
|
414
411
|
# Expand brace patterns for each manifest pattern
|
|
415
412
|
expanded_patterns = Core.expand_brace_pattern(pattern_str)
|
|
416
413
|
for exp_pat in expanded_patterns:
|
|
417
|
-
# If pattern doesn't contain '/', prepend '**/' to match files in any subdirectory
|
|
418
|
-
# This ensures patterns like '*requirements.txt' match '.test/requirements.txt'
|
|
419
|
-
if '/' not in exp_pat:
|
|
420
|
-
exp_pat = f"**/{exp_pat}"
|
|
421
|
-
|
|
422
414
|
for file in norm_files:
|
|
423
|
-
#
|
|
415
|
+
# Match the pattern as-is first (handles root-level files
|
|
416
|
+
# like "package.json" matching pattern "package.json")
|
|
424
417
|
if PurePath(file).match(exp_pat):
|
|
425
418
|
return True
|
|
419
|
+
# Also try with **/ prefix to match files in subdirectories
|
|
420
|
+
# (e.g. "src/requirements.txt" matching "*requirements.txt")
|
|
421
|
+
if '/' not in exp_pat and PurePath(file).match(f"**/{exp_pat}"):
|
|
422
|
+
return True
|
|
426
423
|
return False
|
|
427
424
|
|
|
428
425
|
def check_file_count_limit(self, file_count: int) -> dict:
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from dataclasses import dataclass, field
|
|
3
|
-
from typing import Dict, List,
|
|
3
|
+
from typing import Dict, List, Optional, TypedDict
|
|
4
4
|
|
|
5
|
-
from socketdev.fullscans import
|
|
5
|
+
from socketdev.fullscans import (
|
|
6
|
+
FullScanMetadata,
|
|
7
|
+
SocketAlert,
|
|
8
|
+
SocketArtifact,
|
|
9
|
+
SocketArtifactLink,
|
|
10
|
+
SocketManifestReference,
|
|
11
|
+
SocketScore,
|
|
12
|
+
)
|
|
6
13
|
|
|
7
14
|
__all__ = [
|
|
8
15
|
"Report",
|
|
@@ -109,8 +116,8 @@ class Package():
|
|
|
109
116
|
type: str
|
|
110
117
|
name: str
|
|
111
118
|
version: str
|
|
112
|
-
release: str
|
|
113
|
-
diffType: str
|
|
119
|
+
release: Optional[str] = None
|
|
120
|
+
diffType: Optional[str] = None
|
|
114
121
|
id: str
|
|
115
122
|
author: List[str] = field(default_factory=list)
|
|
116
123
|
score: SocketScore
|
|
@@ -158,6 +165,8 @@ class Package():
|
|
|
158
165
|
name=data["name"],
|
|
159
166
|
version=data["version"],
|
|
160
167
|
type=data["type"],
|
|
168
|
+
release=data.get("release"),
|
|
169
|
+
diffType=data.get("diffType"),
|
|
161
170
|
score=data["score"],
|
|
162
171
|
alerts=data["alerts"],
|
|
163
172
|
author=data.get("author", []),
|
|
@@ -187,10 +196,36 @@ class Package():
|
|
|
187
196
|
Raises:
|
|
188
197
|
ValueError: If reference data cannot be found in DiffArtifact
|
|
189
198
|
"""
|
|
199
|
+
diff_type = data.get("diffType")
|
|
200
|
+
if hasattr(diff_type, "value"):
|
|
201
|
+
diff_type = diff_type.value
|
|
202
|
+
|
|
203
|
+
# Newer API responses may provide flattened diff artifacts without refs.
|
|
204
|
+
if "topLevelAncestors" in data or (not data.get("head") and not data.get("base")):
|
|
205
|
+
return cls(
|
|
206
|
+
id=data["id"],
|
|
207
|
+
name=data["name"],
|
|
208
|
+
version=data["version"],
|
|
209
|
+
type=data["type"],
|
|
210
|
+
score=data.get("score", data.get("scores", {})),
|
|
211
|
+
alerts=data.get("alerts", []),
|
|
212
|
+
author=data.get("author", []),
|
|
213
|
+
size=data.get("size"),
|
|
214
|
+
license=data.get("license"),
|
|
215
|
+
topLevelAncestors=data.get("topLevelAncestors", []),
|
|
216
|
+
direct=data.get("direct", True),
|
|
217
|
+
manifestFiles=data.get("manifestFiles", []),
|
|
218
|
+
dependencies=data.get("dependencies"),
|
|
219
|
+
artifact=data.get("artifact"),
|
|
220
|
+
namespace=data.get("namespace"),
|
|
221
|
+
release=data.get("release"),
|
|
222
|
+
diffType=diff_type,
|
|
223
|
+
)
|
|
224
|
+
|
|
190
225
|
ref = None
|
|
191
|
-
if
|
|
226
|
+
if diff_type in ["added", "updated", "unchanged"] and data.get("head"):
|
|
192
227
|
ref = data["head"][0]
|
|
193
|
-
elif
|
|
228
|
+
elif diff_type in ["removed", "replaced"] and data.get("base"):
|
|
194
229
|
ref = data["base"][0]
|
|
195
230
|
|
|
196
231
|
if not ref:
|
|
@@ -201,8 +236,8 @@ class Package():
|
|
|
201
236
|
name=data["name"],
|
|
202
237
|
version=data["version"],
|
|
203
238
|
type=data["type"],
|
|
204
|
-
score=data
|
|
205
|
-
alerts=data
|
|
239
|
+
score=data.get("score", data.get("scores", {})),
|
|
240
|
+
alerts=data.get("alerts", []),
|
|
206
241
|
author=data.get("author", []),
|
|
207
242
|
size=data.get("size"),
|
|
208
243
|
license=data.get("license"),
|
|
@@ -213,7 +248,7 @@ class Package():
|
|
|
213
248
|
artifact=ref.get("artifact"),
|
|
214
249
|
namespace=data.get('namespace', None),
|
|
215
250
|
release=ref.get("release", None),
|
|
216
|
-
diffType=ref.get("diffType",
|
|
251
|
+
diffType=ref.get("diffType", diff_type),
|
|
217
252
|
)
|
|
218
253
|
|
|
219
254
|
class Issue:
|
|
@@ -58,12 +58,20 @@ class OutputHandler:
|
|
|
58
58
|
slack_url = "Not configured"
|
|
59
59
|
if self.config.slack_plugin.config and self.config.slack_plugin.config.get("url"):
|
|
60
60
|
slack_url = self.config.slack_plugin.config.get("url")
|
|
61
|
+
slack_mode = (self.config.slack_plugin.config or {}).get("mode", "webhook")
|
|
62
|
+
bot_token = os.getenv("SOCKET_SLACK_BOT_TOKEN")
|
|
63
|
+
bot_token_status = "Set" if bot_token else "Not set"
|
|
61
64
|
self.logger.debug("=== Slack Webhook Debug Information ===")
|
|
62
65
|
self.logger.debug(f"Slack Plugin Enabled: {self.config.slack_plugin.enabled}")
|
|
66
|
+
self.logger.debug(f"Slack Mode: {slack_mode}")
|
|
63
67
|
self.logger.debug(f"SOCKET_SLACK_ENABLED environment variable: {slack_enabled_env}")
|
|
64
68
|
self.logger.debug(f"SOCKET_SLACK_CONFIG_JSON environment variable: {slack_config_env}")
|
|
65
69
|
self.logger.debug(f"Slack Webhook URL: {slack_url}")
|
|
70
|
+
self.logger.debug(f"SOCKET_SLACK_BOT_TOKEN: {bot_token_status}")
|
|
66
71
|
self.logger.debug(f"Slack Alert Levels: {self.config.slack_plugin.levels}")
|
|
72
|
+
if self.config.reach:
|
|
73
|
+
facts_path = os.path.join(self.config.target_path or ".", self.config.reach_output_file or ".socket.facts.json")
|
|
74
|
+
self.logger.debug(f"Reachability facts file: {facts_path} (exists: {os.path.exists(facts_path)})")
|
|
67
75
|
self.logger.debug("=====================================")
|
|
68
76
|
|
|
69
77
|
if self.config.slack_plugin.enabled:
|
|
@@ -139,14 +147,38 @@ class OutputHandler:
|
|
|
139
147
|
def output_console_sarif(self, diff_report: Diff, sbom_file_name: Optional[str] = None) -> None:
|
|
140
148
|
"""
|
|
141
149
|
Generate SARIF output from the diff report and print to console.
|
|
150
|
+
If --sarif-file is configured, also save to file.
|
|
151
|
+
If --sarif-reachable-only is set, filters to blocking (reachable) alerts only.
|
|
142
152
|
"""
|
|
143
153
|
if diff_report.id != "NO_DIFF_RAN":
|
|
154
|
+
# When --sarif-reachable-only is set, filter to error=True alerts only.
|
|
155
|
+
# This mirrors the Slack plugin's reachability_alerts_only behaviou:
|
|
156
|
+
# when --reach is used, error=True reflects Socket's reachability-aware policy.
|
|
157
|
+
if self.config.sarif_reachable_only:
|
|
158
|
+
filtered_alerts = [a for a in diff_report.new_alerts if getattr(a, "error", False)]
|
|
159
|
+
diff_report = Diff(
|
|
160
|
+
new_alerts=filtered_alerts,
|
|
161
|
+
diff_url=getattr(diff_report, "diff_url", ""),
|
|
162
|
+
new_packages=getattr(diff_report, "new_packages", []),
|
|
163
|
+
removed_packages=getattr(diff_report, "removed_packages", []),
|
|
164
|
+
packages=getattr(diff_report, "packages", {}),
|
|
165
|
+
)
|
|
166
|
+
diff_report.id = "filtered"
|
|
167
|
+
|
|
144
168
|
# Generate the SARIF structure using Messages
|
|
145
169
|
console_security_comment = Messages.create_security_comment_sarif(diff_report)
|
|
146
170
|
self.save_sbom_file(diff_report, sbom_file_name)
|
|
147
171
|
# Print the SARIF output to the console in JSON format
|
|
148
172
|
print(json.dumps(console_security_comment, indent=2))
|
|
149
173
|
|
|
174
|
+
# Save to file if --sarif-file is specified
|
|
175
|
+
if self.config.sarif_file:
|
|
176
|
+
sarif_path = Path(self.config.sarif_file)
|
|
177
|
+
sarif_path.parent.mkdir(parents=True, exist_ok=True)
|
|
178
|
+
with open(sarif_path, "w") as f:
|
|
179
|
+
json.dump(console_security_comment, f, indent=2)
|
|
180
|
+
self.logger.info(f"SARIF report saved to {self.config.sarif_file}")
|
|
181
|
+
|
|
150
182
|
def report_pass(self, diff_report: Diff) -> bool:
|
|
151
183
|
"""Determines if the report passes security checks"""
|
|
152
184
|
# Priority 1: --disable-blocking always passes
|
|
@@ -135,18 +135,20 @@ class SlackPlugin(Plugin):
|
|
|
135
135
|
if not bot_token:
|
|
136
136
|
logger.error("SOCKET_SLACK_BOT_TOKEN environment variable not set for bot mode.")
|
|
137
137
|
return
|
|
138
|
-
|
|
138
|
+
|
|
139
139
|
if not bot_token.startswith("xoxb-"):
|
|
140
140
|
logger.error("SOCKET_SLACK_BOT_TOKEN must start with 'xoxb-' (Bot User OAuth Token).")
|
|
141
141
|
return
|
|
142
|
-
|
|
142
|
+
|
|
143
|
+
logger.debug("SOCKET_SLACK_BOT_TOKEN: Set (valid xoxb- format)")
|
|
144
|
+
|
|
143
145
|
# Get bot_configs from configuration
|
|
144
146
|
bot_configs = self.config.get("bot_configs", [])
|
|
145
|
-
|
|
147
|
+
|
|
146
148
|
if not bot_configs:
|
|
147
149
|
logger.warning("No bot_configs configured for bot mode.")
|
|
148
150
|
return
|
|
149
|
-
|
|
151
|
+
|
|
150
152
|
logger.debug("Slack Plugin Enabled (bot mode)")
|
|
151
153
|
logger.debug("Alert levels: %s", self.config.get("levels"))
|
|
152
154
|
logger.debug(f"Number of bot_configs: {len(bot_configs)}")
|
|
@@ -212,29 +214,35 @@ class SlackPlugin(Plugin):
|
|
|
212
214
|
"""Send reachability alerts using bot mode with Slack API."""
|
|
213
215
|
# Construct path to socket facts file
|
|
214
216
|
facts_file_path = os.path.join(config.target_path or ".", f"{config.reach_output_file}")
|
|
215
|
-
|
|
216
|
-
|
|
217
|
+
facts_file_exists = os.path.exists(facts_file_path)
|
|
218
|
+
logger.debug(f"Loading reachability data from {facts_file_path} (exists: {facts_file_exists})")
|
|
219
|
+
|
|
220
|
+
if not facts_file_exists:
|
|
221
|
+
logger.error(f"Reachability facts file not found: {facts_file_path} — was --reach run successfully?")
|
|
222
|
+
return
|
|
223
|
+
|
|
217
224
|
# Load socket facts file
|
|
218
225
|
facts_data = load_socket_facts(facts_file_path)
|
|
219
|
-
|
|
226
|
+
|
|
220
227
|
if not facts_data:
|
|
221
|
-
logger.
|
|
228
|
+
logger.error(f"Failed to load or parse reachability facts file: {facts_file_path}")
|
|
222
229
|
return
|
|
223
|
-
|
|
230
|
+
|
|
224
231
|
# Get components with vulnerabilities
|
|
225
232
|
components_with_vulns = get_components_with_vulnerabilities(facts_data)
|
|
226
|
-
|
|
233
|
+
logger.debug(f"Components with vulnerabilities in facts file: {len(components_with_vulns) if components_with_vulns else 0}")
|
|
234
|
+
|
|
227
235
|
if not components_with_vulns:
|
|
228
236
|
logger.debug("No components with vulnerabilities found in .socket.facts.json")
|
|
229
237
|
return
|
|
230
|
-
|
|
238
|
+
|
|
231
239
|
# Convert to alerts format
|
|
232
240
|
components_with_alerts = convert_to_alerts(components_with_vulns)
|
|
233
|
-
|
|
241
|
+
|
|
234
242
|
if not components_with_alerts:
|
|
235
243
|
logger.debug("No alerts generated from .socket.facts.json")
|
|
236
244
|
return
|
|
237
|
-
|
|
245
|
+
|
|
238
246
|
logger.debug(f"Found {len(components_with_alerts)} components with reachability alerts")
|
|
239
247
|
|
|
240
248
|
# Send to each configured bot_config with filtering
|
|
@@ -265,10 +273,12 @@ class SlackPlugin(Plugin):
|
|
|
265
273
|
filtered_component['alerts'] = filtered_component_alerts
|
|
266
274
|
filtered_components.append(filtered_component)
|
|
267
275
|
|
|
276
|
+
logger.debug(f"Bot config '{name}': {len(filtered_components)} components after severity filter {bot_config.get('severities', '(all)')}")
|
|
277
|
+
|
|
268
278
|
if not filtered_components:
|
|
269
279
|
logger.debug(f"No reachability alerts match filter criteria for bot_config '{name}'. Skipping.")
|
|
270
280
|
continue
|
|
271
|
-
|
|
281
|
+
|
|
272
282
|
# Format for Slack using the formatter (max 45 blocks for findings + 5 for header/footer)
|
|
273
283
|
slack_notifications = format_socket_facts_for_slack(
|
|
274
284
|
filtered_components,
|