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.
Files changed (124) hide show
  1. socketsecurity-2.2.83/.github/workflows/e2e-test.yml +99 -0
  2. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/CHANGELOG.md +4 -0
  3. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/PKG-INFO +1 -1
  4. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/docs/cli-reference.md +34 -4
  5. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/pyproject.toml +1 -1
  6. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/__init__.py +1 -1
  7. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/config.py +15 -0
  8. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/__init__.py +65 -2
  9. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/git_interface.py +6 -0
  10. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/messages.py +61 -16
  11. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/socketcli.py +24 -9
  12. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/e2e/fixtures/simple-npm/package.json +2 -2
  13. socketsecurity-2.2.83/tests/e2e/fixtures/simple-pypi/requirements.txt +3 -0
  14. socketsecurity-2.2.83/tests/e2e/validate-gitlab.sh +63 -0
  15. socketsecurity-2.2.83/tests/e2e/validate-json.sh +33 -0
  16. socketsecurity-2.2.83/tests/e2e/validate-reachability.sh +65 -0
  17. socketsecurity-2.2.83/tests/e2e/validate-sarif.sh +19 -0
  18. socketsecurity-2.2.83/tests/e2e/validate-scan.sh +16 -0
  19. socketsecurity-2.2.83/tests/unit/test_disable_ignore.py +138 -0
  20. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_gitlab_format.py +175 -2
  21. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/uv.lock +1 -1
  22. socketsecurity-2.2.80/.github/workflows/e2e-test.yml +0 -201
  23. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/CODEOWNERS +0 -0
  24. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/PULL_REQUEST_TEMPLATE/bug-fix.md +0 -0
  25. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/PULL_REQUEST_TEMPLATE/feature.md +0 -0
  26. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/PULL_REQUEST_TEMPLATE/improvement.md +0 -0
  27. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  28. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/workflows/docker-stable.yml +0 -0
  29. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/workflows/pr-preview.yml +0 -0
  30. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/workflows/python-tests.yml +0 -0
  31. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/workflows/release.yml +0 -0
  32. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/workflows/version-check.yml +0 -0
  33. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.github/zizmor.yml +0 -0
  34. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.gitignore +0 -0
  35. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.hooks/sync_version.py +0 -0
  36. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.pre-commit-config.yaml +0 -0
  37. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/.python-version +0 -0
  38. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/Dockerfile +0 -0
  39. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/LICENSE +0 -0
  40. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/Makefile +0 -0
  41. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/README.md +0 -0
  42. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/docs/ci-cd.md +0 -0
  43. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/docs/development.md +0 -0
  44. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/docs/troubleshooting.md +0 -0
  45. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-dashboard-parity.json +0 -0
  46. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-dashboard-parity.toml +0 -0
  47. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-diff-ci-cd.json +0 -0
  48. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-diff-ci-cd.toml +0 -0
  49. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-instance-detail.json +0 -0
  50. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/examples/config/sarif-instance-detail.toml +0 -0
  51. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/instructions/gitlab-commit-status/uat.md +0 -0
  52. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/pytest.ini +0 -0
  53. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/build_container.sh +0 -0
  54. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/build_container_flexible.sh +0 -0
  55. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/deploy-test-docker.sh +0 -0
  56. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/deploy-test-pypi.sh +0 -0
  57. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/docker-entrypoint.sh +0 -0
  58. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/scripts/run.sh +0 -0
  59. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/session.md +0 -0
  60. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socket.yml +0 -0
  61. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/alert_selection.py +0 -0
  62. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/classes.py +0 -0
  63. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/cli_client.py +0 -0
  64. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/exceptions.py +0 -0
  65. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/helper/__init__.py +0 -0
  66. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/helper/socket_facts_loader.py +0 -0
  67. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/lazy_file_loader.py +0 -0
  68. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/logging.py +0 -0
  69. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/resource_utils.py +0 -0
  70. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm/__init__.py +0 -0
  71. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm/base.py +0 -0
  72. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm/client.py +0 -0
  73. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm/github.py +0 -0
  74. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm/gitlab.py +0 -0
  75. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/scm_comments.py +0 -0
  76. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/socket_config.py +0 -0
  77. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/tools/reachability.py +0 -0
  78. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/core/utils.py +0 -0
  79. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/output.py +0 -0
  80. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/__init__.py +0 -0
  81. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/base.py +0 -0
  82. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/formatters/__init__.py +0 -0
  83. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/formatters/slack.py +0 -0
  84. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/jira.py +0 -0
  85. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/manager.py +0 -0
  86. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/slack.py +0 -0
  87. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/teams.py +0 -0
  88. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/socketsecurity/plugins/webhook.py +0 -0
  89. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/__init__.py +0 -0
  90. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/conftest.py +0 -0
  91. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/create_diff_input.json +0 -0
  92. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_diff_alerts.py +0 -0
  93. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_diff_generation.py +0 -0
  94. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_has_manifest_files.py +0 -0
  95. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_package_and_alerts.py +0 -0
  96. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_sdk_methods.py +0 -0
  97. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/core/test_supporting_methods.py +0 -0
  98. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/create_response.json +0 -0
  99. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/diff/stream_diff.json +0 -0
  100. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/diff/stream_diff_full.json +0 -0
  101. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/head_scan/metadata.json +0 -0
  102. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/head_scan/stream_scan.json +0 -0
  103. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/head_scan/stream_scan_full.json +0 -0
  104. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/new_scan/metadata.json +0 -0
  105. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/fullscans/new_scan/stream_scan.json +0 -0
  106. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/repos/repo_info_error.json +0 -0
  107. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/repos/repo_info_no_head.json +0 -0
  108. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/repos/repo_info_success.json +0 -0
  109. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/data/settings/security-policy.json +0 -0
  110. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/e2e/fixtures/simple-npm/index.js +0 -0
  111. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/__init__.py +0 -0
  112. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_alert_selection.py +0 -0
  113. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_cli_config.py +0 -0
  114. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_client.py +0 -0
  115. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_config.py +0 -0
  116. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_gitlab_auth.py +0 -0
  117. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_gitlab_auth_fallback.py +0 -0
  118. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_gitlab_commit_status.py +0 -0
  119. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_output.py +0 -0
  120. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/tests/unit/test_slack_plugin.py +0 -0
  121. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/workflows/bitbucket-pipelines.yml +0 -0
  122. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/workflows/buildkite.yml +0 -0
  123. {socketsecurity-2.2.80 → socketsecurity-2.2.83}/workflows/github-actions.yml +0 -0
  124. {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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socketsecurity
3
- Version: 2.2.80
3
+ Version: 2.2.83
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>
@@ -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://docs.gitlab.com/ee/development/integrations/secure.html). The reports include:
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
- - This is normal if no new security issues were detected
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
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "socketsecurity"
9
- version = "2.2.80"
9
+ version = "2.2.83"
10
10
  requires-python = ">= 3.11"
11
11
  license = {"file" = "LICENSE"}
12
12
  dependencies = [
@@ -1,3 +1,3 @@
1
1
  __author__ = 'socket.dev'
2
- __version__ = '2.2.80'
2
+ __version__ = '2.2.83'
3
3
  USER_AGENT = f'SocketPythonCLI/{__version__}'
@@ -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
- # Return result in the format expected by the user
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.utcnow().isoformat() + "Z",
747
- "end_time": datetime.utcnow().isoformat() + "Z",
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
- # Process each alert
754
- for alert in diff.new_alerts:
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
- <p><em>Mark as acceptable risk:</em> To ignore this alert only in this pull request, reply with:<br/>
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
- <p><em>Mark the package as acceptable risk:</em> To ignore this alert only in this pull request, reply with the comment <code>@SocketSecurity ignore {first_alert.pkg_name}@{first_alert.pkg_version}</code>. You can also ignore all packages with <code>@SocketSecurity ignore-all</code>. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.</p>
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
- comments = scm.get_comments_for_pr()
491
- log.debug("Removing comment alerts")
492
- scm.remove_comment_alerts(comments)
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
- log.debug("Removing comment alerts")
501
-
503
+
502
504
  # FIXME: this overwrites diff.new_alerts, which was previously populated by Core.create_issue_alerts
503
- diff.new_alerts = Comments.remove_alerts(comments, diff.new_alerts)
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
- blocking_count = sum(1 for a in diff.new_alerts if a.error)
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
- description = f"{blocking_count} blocking alert(s) found"
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.17.23",
7
+ "lodash": "4.18.1",
8
8
  "express": "4.22.0",
9
- "axios": "1.13.5"
9
+ "axios": "1.15.0"
10
10
  },
11
11
  "devDependencies": {
12
12
  "typescript": "5.0.4",
@@ -0,0 +1,3 @@
1
+ requests==2.31.0
2
+ flask==3.0.0
3
+ pyyaml==6.0.1
@@ -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
+ "