Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ run_container.sh
bin
scripts/*.py
*.json
*.sarif
!tests/**/*.json
markdown_overview_temp.md
markdown_security_temp.md
Expand Down
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,27 @@ This will:
- Save to `gl-dependency-scanning-report.json`
- Include all actionable security alerts (error/warn level)

**Save SARIF report to file (e.g. for GitHub Code Scanning, SonarQube, or VS Code):**
```bash
socketcli --sarif-file results.sarif \
--repo owner/repo \
--target-path .
```

**Multiple output formats:**
```bash
socketcli --enable-json \
--enable-sarif \
--sarif-file results.sarif \
--enable-gitlab-security \
--repo owner/repo
```

This will simultaneously generate:
- JSON output to console
- SARIF format to console
- GitLab Security Dashboard report to file
- SARIF report to `results.sarif` (and stdout)
- GitLab Security Dashboard report to `gl-dependency-scanning-report.json`

> **Note:** `--enable-sarif` prints SARIF to stdout only. Use `--sarif-file <path>` to save to a file (this also implies `--enable-sarif`). These are independent from `--enable-gitlab-security`, which produces a separate GitLab-specific Dependency Scanning report.

### Requirements

Expand All @@ -121,7 +130,7 @@ socketcli [-h] [--api-token API_TOKEN] [--repo REPO] [--workspace WORKSPACE] [--
[--target-path TARGET_PATH] [--sbom-file SBOM_FILE] [--license-file-name LICENSE_FILE_NAME] [--save-submitted-files-list SAVE_SUBMITTED_FILES_LIST]
[--save-manifest-tar SAVE_MANIFEST_TAR] [--files FILES] [--sub-path SUB_PATH] [--workspace-name WORKSPACE_NAME]
[--excluded-ecosystems EXCLUDED_ECOSYSTEMS] [--default-branch] [--pending-head] [--generate-license] [--enable-debug]
[--enable-json] [--enable-sarif] [--enable-gitlab-security] [--gitlab-security-file <path>]
[--enable-json] [--enable-sarif] [--sarif-file <path>] [--enable-gitlab-security] [--gitlab-security-file <path>]
[--disable-overview] [--exclude-license-details] [--allow-unverified] [--disable-security-issue]
[--ignore-commit-files] [--disable-blocking] [--enable-diff] [--scm SCM] [--timeout TIMEOUT] [--include-module-folders]
[--reach] [--reach-version REACH_VERSION] [--reach-analysis-timeout REACH_ANALYSIS_TIMEOUT]
Expand Down Expand Up @@ -189,7 +198,8 @@ If you don't want to provide the Socket API Token every time then you can use th
| --generate-license | False | False | Generate license information |
| --enable-debug | False | False | Enable debug logging |
| --enable-json | False | False | Output in JSON format |
| --enable-sarif | False | False | Enable SARIF output of results instead of table or JSON format |
| --enable-sarif | False | False | Enable SARIF output of results instead of table or JSON format (prints to stdout) |
| --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 |
| --enable-gitlab-security | False | False | Enable GitLab Security Dashboard output format (Dependency Scanning report) |
| --gitlab-security-file | False | gl-dependency-scanning-report.json | Output file path for GitLab Security report |
| --disable-overview | False | False | Disable overview output |
Expand Down Expand Up @@ -725,13 +735,13 @@ socketcli --enable-gitlab-security --gitlab-security-file custom-path.json
GitLab security reports can be generated alongside other output formats:

```bash
socketcli --enable-json --enable-gitlab-security --enable-sarif
socketcli --enable-json --enable-gitlab-security --sarif-file results.sarif
```

This command will:
- Output JSON format to console
- Save GitLab Security Dashboard report to `gl-dependency-scanning-report.json`
- Save SARIF report (if configured)
- Save SARIF report to `results.sarif`

### Security Dashboard Features

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build-backend = "hatchling.build"

[project]
name = "socketsecurity"
version = "2.2.74"
version = "2.2.75"
requires-python = ">= 3.10"
license = {"file" = "LICENSE"}
dependencies = [
Expand Down
2 changes: 1 addition & 1 deletion socketsecurity/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__author__ = 'socket.dev'
__version__ = '2.2.74'
__version__ = '2.2.75'
USER_AGENT = f'SocketPythonCLI/{__version__}'
13 changes: 13 additions & 0 deletions socketsecurity/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class CliConfig:
allow_unverified: bool = False
enable_json: bool = False
enable_sarif: bool = False
sarif_file: Optional[str] = None
enable_gitlab_security: bool = False
gitlab_security_file: Optional[str] = None
disable_overview: bool = False
Expand Down Expand Up @@ -103,6 +104,10 @@ def from_args(cls, args_list: Optional[List[str]] = None) -> 'CliConfig':
args.api_token
)

# --sarif-file implies --enable-sarif
if args.sarif_file:
args.enable_sarif = True

# Strip quotes from commit message if present
commit_message = args.commit_message
if commit_message and commit_message.startswith('"') and commit_message.endswith('"'):
Expand All @@ -126,6 +131,7 @@ def from_args(cls, args_list: Optional[List[str]] = None) -> 'CliConfig':
'allow_unverified': args.allow_unverified,
'enable_json': args.enable_json,
'enable_sarif': args.enable_sarif,
'sarif_file': args.sarif_file,
'enable_gitlab_security': args.enable_gitlab_security,
'gitlab_security_file': args.gitlab_security_file,
'disable_overview': args.disable_overview,
Expand Down Expand Up @@ -471,6 +477,13 @@ def create_argument_parser() -> argparse.ArgumentParser:
action="store_true",
help="Enable SARIF output of results instead of table or JSON format"
)
output_group.add_argument(
"--sarif-file",
dest="sarif_file",
metavar="<path>",
default=None,
help="Output file path for SARIF report (implies --enable-sarif)"
)
output_group.add_argument(
"--enable-gitlab-security",
dest="enable_gitlab_security",
Expand Down
9 changes: 9 additions & 0 deletions socketsecurity/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def output_console_json(self, diff_report: Diff, sbom_file_name: Optional[str] =
def output_console_sarif(self, diff_report: Diff, sbom_file_name: Optional[str] = None) -> None:
"""
Generate SARIF output from the diff report and print to console.
If --sarif-file is configured, also save to file.
"""
if diff_report.id != "NO_DIFF_RAN":
# Generate the SARIF structure using Messages
Expand All @@ -147,6 +148,14 @@ def output_console_sarif(self, diff_report: Diff, sbom_file_name: Optional[str]
# Print the SARIF output to the console in JSON format
print(json.dumps(console_security_comment, indent=2))

# Save to file if --sarif-file is specified
if self.config.sarif_file:
sarif_path = Path(self.config.sarif_file)
sarif_path.parent.mkdir(parents=True, exist_ok=True)
with open(sarif_path, "w") as f:
json.dump(console_security_comment, f, indent=2)
self.logger.info(f"SARIF report saved to {self.config.sarif_file}")

def report_pass(self, diff_report: Diff) -> bool:
"""Determines if the report passes security checks"""
# Priority 1: --disable-blocking always passes
Expand Down
85 changes: 84 additions & 1 deletion tests/unit/test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,87 @@ def test_disable_blocking_overrides_strict_blocking(self):
diff.unchanged_alerts = [Issue(error=True, warn=False)]

# Should pass because disable_blocking takes precedence
assert handler.report_pass(diff)
assert handler.report_pass(diff)

def test_sarif_file_output(self, tmp_path):
"""Test that --sarif-file writes SARIF report to a file"""
from socketsecurity.config import CliConfig
from unittest.mock import Mock

sarif_path = tmp_path / "report.sarif"

config = Mock(spec=CliConfig)
config.sarif_file = str(sarif_path)
config.sbom_file = None

handler = OutputHandler(config, Mock())

diff = Diff()
diff.id = "test-scan-id"
diff.new_alerts = [Issue(
pkg_name="test-package",
pkg_version="1.0.0",
severity="high",
title="Test Vulnerability",
description="Test description",
type="malware",
url="https://socket.dev/test",
manifests="package.json",
pkg_type="npm",
key="test-key",
purl="pkg:npm/test-package@1.0.0",
error=True,
)]

handler.output_console_sarif(diff)

assert sarif_path.exists()
with open(sarif_path) as f:
sarif_data = json.load(f)
assert sarif_data["version"] == "2.1.0"
assert "$schema" in sarif_data
assert len(sarif_data["runs"]) == 1

def test_sarif_no_file_when_not_configured(self, tmp_path):
"""Test that no file is written when --sarif-file is not set"""
from socketsecurity.config import CliConfig
from unittest.mock import Mock

config = Mock(spec=CliConfig)
config.sarif_file = None
config.sbom_file = None

handler = OutputHandler(config, Mock())

diff = Diff()
diff.id = "test-scan-id"
diff.new_alerts = []

handler.output_console_sarif(diff)

# No files should be created in tmp_path
assert list(tmp_path.iterdir()) == []

def test_sarif_file_nested_directory(self, tmp_path):
"""Test that --sarif-file creates parent directories if needed"""
from socketsecurity.config import CliConfig
from unittest.mock import Mock

sarif_path = tmp_path / "nested" / "dir" / "report.sarif"

config = Mock(spec=CliConfig)
config.sarif_file = str(sarif_path)
config.sbom_file = None

handler = OutputHandler(config, Mock())

diff = Diff()
diff.id = "test-scan-id"
diff.new_alerts = []

handler.output_console_sarif(diff)

assert sarif_path.exists()
with open(sarif_path) as f:
sarif_data = json.load(f)
assert sarif_data["version"] == "2.1.0"
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading