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
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.10.11"
version = "2.10.12"
requires-python = ">= 3.11"
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.10.11'
__version__ = '2.10.12'
USER_AGENT = f'SocketPythonCLI/{__version__}'
140 changes: 140 additions & 0 deletions socketsecurity/core/report_output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
from dataclasses import dataclass
from typing import Optional

from socketsecurity.config import CliConfig
from socketsecurity.core.alert_selection import (
clone_diff_with_selected_alerts,
select_diff_alerts,
)
from socketsecurity.core.classes import Diff
from socketsecurity.core.messages import Messages


@dataclass(frozen=True)
class ReportReferences:
full_scan_id: str
report_url: Optional[str]
diff_url: Optional[str]
is_full_scan: bool

def console_link(self) -> Optional[tuple[str, str]]:
if self.is_full_scan:
if self.report_url:
return "Report Url", self.report_url
if self.diff_url:
return "Report Url", self.diff_url
else:
if self.diff_url:
return "Diff Url", self.diff_url
if self.report_url:
return "Report Url", self.report_url
return None


def build_report_references(diff_report: Diff) -> ReportReferences:
return ReportReferences(
full_scan_id=getattr(diff_report, "id", ""),
report_url=getattr(diff_report, "report_url", None),
diff_url=getattr(diff_report, "diff_url", ""),
is_full_scan=getattr(diff_report, "is_full_scan", False),
)


def build_console_summary(diff_report: Diff, config: CliConfig) -> str:
references = build_report_references(diff_report)
is_full_scan = references.is_full_scan
link = references.console_link()

if is_full_scan and not getattr(diff_report, "alerts_fetched", False):
lines = ["Full scan completed. Findings were not fetched for console output."]
if link:
lines.append(f"{link[0]}: {link[1]}")
return "\n".join(lines)

selected_alerts = select_diff_alerts(
diff_report,
strict_blocking=config.strict_blocking,
)
has_new_alerts = len(selected_alerts) > 0
has_unchanged_alerts = (
config.strict_blocking
and hasattr(diff_report, "unchanged_alerts")
and len(diff_report.unchanged_alerts) > 0
)

if not has_new_alerts and not has_unchanged_alerts:
if link:
return f"No issues found\n{link[0]}: {link[1]}"
return "No issues found"

new_blocking = sum(1 for issue in diff_report.new_alerts if issue.error)
new_warning = sum(1 for issue in diff_report.new_alerts if issue.warn)

unchanged_blocking = 0
unchanged_warning = 0
if has_unchanged_alerts:
unchanged_blocking = sum(
1 for issue in diff_report.unchanged_alerts if issue.error
)
unchanged_warning = sum(
1 for issue in diff_report.unchanged_alerts if issue.warn
)

selected_diff = clone_diff_with_selected_alerts(diff_report, selected_alerts)
console_security_comment = Messages.create_console_security_alert_table(
selected_diff
)

blocking_label = "Blocking issues" if is_full_scan else "NEW blocking issues"
warning_label = "Warning issues" if is_full_scan else "NEW warning issues"

lines = ["Security issues detected by Socket Security:"]
if new_blocking > 0:
lines.append(f" - {blocking_label}: {new_blocking}")
if new_warning > 0:
lines.append(f" - {warning_label}: {new_warning}")
if unchanged_blocking > 0:
lines.append(
f" - EXISTING blocking issues: {unchanged_blocking} "
"(causing failure due to --strict-blocking)"
)
if unchanged_warning > 0:
lines.append(f" - EXISTING warning issues: {unchanged_warning}")
if is_full_scan:
lines.append(
" Reported against the whole repository, with no baseline to compare "
"against, so these do not affect the exit code."
)

if link:
lines.append(f"{link[0]}: {link[1]}")
lines.append("")
lines.append(str(console_security_comment))
return "\n".join(lines)


def build_socket_json_report(
diff_report: Diff,
config: CliConfig,
) -> dict[str, object]:
references = build_report_references(diff_report)
selected_alerts = select_diff_alerts(
diff_report,
strict_blocking=config.strict_blocking,
)
selected_diff = clone_diff_with_selected_alerts(diff_report, selected_alerts)
report = Messages.create_security_comment_json(selected_diff)
legal_flag = getattr(config, "legal", False)
repo = getattr(config, "repo", None)
branch = getattr(config, "branch", None)
commit_sha = getattr(config, "commit_sha", None)
report["full_scan_id"] = references.full_scan_id
report["diff_url"] = references.diff_url
report["report_url"] = references.report_url
report["repo"] = repo if isinstance(repo, str) or repo is None else None
report["branch"] = branch if isinstance(branch, str) or branch is None else None
report["commit_sha"] = (
commit_sha if isinstance(commit_sha, str) or commit_sha is None else None
)
report["legal_mode"] = legal_flag if isinstance(legal_flag, bool) else False
return report
80 changes: 3 additions & 77 deletions socketsecurity/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .config import CliConfig
from .core.classes import Diff, Issue
from .core.messages import Messages
from .core.report_output import build_console_summary, build_socket_json_report
from .fossa_compat import build_fossa_report_payload


Expand Down Expand Up @@ -229,88 +230,13 @@ def save_sbom_file(self, diff_report: Diff, sbom_file_name: Optional[str] = None

def build_summary_text(self, diff_report: Diff) -> str:
"""Render the console summary text for stdout and file output."""
is_full_scan = getattr(diff_report, "is_full_scan", False)
if is_full_scan and not getattr(diff_report, "alerts_fetched", False):
lines = ["Full scan completed. Findings were not fetched for console output."]
report_link = getattr(diff_report, "report_url", "") or getattr(
diff_report, "diff_url", ""
)
if report_link:
lines.append(f"Report Url: {report_link}")
return "\n".join(lines)

selected_alerts = select_diff_alerts(diff_report, strict_blocking=self.config.strict_blocking)
has_new_alerts = len(selected_alerts) > 0
has_unchanged_alerts = (
self.config.strict_blocking and
hasattr(diff_report, 'unchanged_alerts') and
len(diff_report.unchanged_alerts) > 0
)

if not has_new_alerts and not has_unchanged_alerts:
return "No issues found"

new_blocking = sum(1 for issue in diff_report.new_alerts if issue.error)
new_warning = sum(1 for issue in diff_report.new_alerts if issue.warn)

unchanged_blocking = 0
unchanged_warning = 0
if has_unchanged_alerts:
unchanged_blocking = sum(1 for issue in diff_report.unchanged_alerts if issue.error)
unchanged_warning = sum(1 for issue in diff_report.unchanged_alerts if issue.warn)

selected_diff = clone_diff_with_selected_alerts(diff_report, selected_alerts)
console_security_comment = Messages.create_console_security_alert_table(selected_diff)

# A full scan has no baseline, so everything it carries is a finding in the
# repository rather than one the change introduced. Calling those NEW would
# also contradict the exit code, which stays 0 because no finding can be
# attributed to the change.
blocking_label = "Blocking issues" if is_full_scan else "NEW blocking issues"
warning_label = "Warning issues" if is_full_scan else "NEW warning issues"
link_label = "Report Url" if is_full_scan else "Diff Url"

lines = ["Security issues detected by Socket Security:"]
if new_blocking > 0:
lines.append(f" - {blocking_label}: {new_blocking}")
if new_warning > 0:
lines.append(f" - {warning_label}: {new_warning}")
if unchanged_blocking > 0:
lines.append(
f" - EXISTING blocking issues: {unchanged_blocking} (causing failure due to --strict-blocking)"
)
if unchanged_warning > 0:
lines.append(f" - EXISTING warning issues: {unchanged_warning}")
if is_full_scan:
lines.append(
" Reported against the whole repository, with no baseline to compare "
"against, so these do not affect the exit code."
)

report_link = getattr(diff_report, "report_url", "") or getattr(diff_report, "diff_url", "")
lines.append(f"{link_label}: {report_link}")
lines.append("")
lines.append(str(console_security_comment))
return "\n".join(lines)
return build_console_summary(diff_report, self.config)

def build_json_report(self, diff_report: Diff) -> dict:
"""Build the JSON report payload for stdout and file output."""
if getattr(self.config, "legal_format", "socket") == "fossa":
return build_fossa_report_payload(diff_report, self.config)

selected_alerts = select_diff_alerts(diff_report, strict_blocking=self.config.strict_blocking)
selected_diff = clone_diff_with_selected_alerts(diff_report, selected_alerts)
report = Messages.create_security_comment_json(selected_diff)
legal_flag = getattr(self.config, "legal", False)
repo = getattr(self.config, "repo", None)
branch = getattr(self.config, "branch", None)
commit_sha = getattr(self.config, "commit_sha", None)
report["report_url"] = getattr(diff_report, "report_url", None)
report["repo"] = repo if isinstance(repo, str) or repo is None else None
report["branch"] = branch if isinstance(branch, str) or branch is None else None
report["commit_sha"] = commit_sha if isinstance(commit_sha, str) or commit_sha is None else None
report["legal_mode"] = legal_flag if isinstance(legal_flag, bool) else False
return report
return build_socket_json_report(diff_report, self.config)

def save_json_file(self, diff_report: Diff, json_file_name: Optional[str] = None) -> None:
if not json_file_name:
Expand Down
Loading
Loading