Updates to new format #1
No files matched your search
@@ -45,8 +45,9 @@ project page is left out. The report has these parts:
|
||||
|
||||
1. the cover and a table of contents,
|
||||
2. a summary and the most critical issues across all sources,
|
||||
3. the dependency vulnerabilities from the VDR,
|
||||
4. one section per SARIF run, named after its tool (e.g. CipherCell Lynkeus, CipherCell SAST),
|
||||
3. the dependency vulnerabilities from the VDR, as CipherCell SCA,
|
||||
4. one section per SARIF run, named after its tool: CipherCell Lynkeus, then CipherCell SAST, then
|
||||
any other tool in the order its run appears,
|
||||
5. at the end, *False Positives and Suppressed Findings*: every result Polygraph classified as a
|
||||
false positive or that carries a SARIF suppression (such as the areas Lynkeus ruled out), with
|
||||
the reason.
|
||||
|
||||
+52
-47
@@ -44,6 +44,13 @@ BARS = 10
|
||||
|
||||
SEVERITY_ORDER = ("Critical", "High", "Medium", "Low")
|
||||
|
||||
# What the dependency check is called wherever the report names its source.
|
||||
SCA = "CipherCell SCA"
|
||||
|
||||
# The order the sources are reported in, everywhere: the dependency check first, then the SARIF
|
||||
# tools in this order. A tool not named here follows them, in the order its run appears.
|
||||
TOOL_ORDER = ("CipherCell Lynkeus", "CipherCell SAST")
|
||||
|
||||
# A full git commit SHA.
|
||||
_COMMIT = re.compile(r"[0-9a-f]{40}")
|
||||
|
||||
@@ -77,7 +84,8 @@ def build_context(
|
||||
context: dict[str, Any] = {
|
||||
"cover": _cover(sources, metadata, tools, sca, project=project, manufacturer=manufacturer),
|
||||
"summary": _summary(tools, sca, documents.get("metrics")),
|
||||
"sections": [_tool_section(name, report) for name, report in tools],
|
||||
"sections": ([_sca_section(sca)] if sca is not None else [])
|
||||
+ [_tool_section(name, report) for name, report in tools],
|
||||
}
|
||||
|
||||
# Without the pipeline's metadata or cloc's count this page would be nothing but gaps.
|
||||
@@ -89,9 +97,6 @@ def build_context(
|
||||
if critical["table"]["rows"]:
|
||||
context["critical"] = critical
|
||||
|
||||
if sca is not None:
|
||||
context["sections"].insert(0, _sca_section(sca))
|
||||
|
||||
set_aside = _set_aside_section(tools)
|
||||
|
||||
if set_aside is not None:
|
||||
@@ -109,7 +114,9 @@ def _tools(sarif: Any) -> list[tuple[str, SarifReport]]: # noqa: ANN401
|
||||
|
||||
runs = [run for run in sarif.get("runs") or [] if isinstance(run, dict)]
|
||||
|
||||
return [(_tool_name(run), _summarize(run)) for run in runs]
|
||||
tools = [(_tool_name(run), _summarize(run)) for run in runs]
|
||||
|
||||
return sorted(tools, key=lambda pair: TOOL_ORDER.index(pair[0]) if pair[0] in TOOL_ORDER else len(TOOL_ORDER))
|
||||
|
||||
|
||||
def _summarize(run: dict[str, Any]) -> SarifReport:
|
||||
@@ -182,7 +189,7 @@ def _cover(
|
||||
manufacturer: str | None,
|
||||
) -> dict[str, Any]:
|
||||
"""The first page."""
|
||||
technologies = [name for name, _ in tools] + (["CipherCell SCA"] if sca is not None else [])
|
||||
technologies = ([SCA] if sca is not None else []) + [name for name, _ in tools]
|
||||
|
||||
return {
|
||||
"scan_id": str(sources.scan_id),
|
||||
@@ -277,7 +284,7 @@ def _summary(
|
||||
"""The counts, and where they concentrate."""
|
||||
sca_total = sca.total if sca else 0
|
||||
raw = sum(report.total for _, report in tools) + sca_total
|
||||
true_issues = [f"{len(_kept(report))} {name}" for name, report in tools] + ([f"{sca_total} SCA"] if sca else [])
|
||||
true_issues = ([f"{sca_total} {SCA}"] if sca else []) + [f"{len(_kept(report))} {name}" for name, report in tools]
|
||||
|
||||
summary = {
|
||||
"id": "summary",
|
||||
@@ -386,18 +393,30 @@ def _remedy(value: dict[str, Any]) -> str | None:
|
||||
|
||||
def _issue_types(tools: list[tuple[str, SarifReport]], sca: VdrReport | None) -> list[str]:
|
||||
"""How many distinct kinds of true issue each tool, and the dependency check, found."""
|
||||
counted = [f"{len({one.rule_id for one in _kept(report)})} {name} rules" for name, report in tools]
|
||||
counted = [f"{sca.component_count} {SCA} affected components"] if sca is not None else []
|
||||
|
||||
if sca is not None:
|
||||
counted.append(f"{sca.component_count} affected components")
|
||||
|
||||
return counted
|
||||
return counted + [f"{len({one.rule_id for one in _kept(report)})} {name} rules" for name, report in tools]
|
||||
|
||||
|
||||
def _distributions(tools: list[tuple[str, SarifReport]], sca: VdrReport | None) -> list[dict[str, Any]]:
|
||||
"""Where the issues concentrate: by rule for each SARIF tool, by package for SCA."""
|
||||
"""Where the issues concentrate: by package for SCA, by rule for each SARIF tool."""
|
||||
blocks = []
|
||||
|
||||
if sca and sca.vulnerabilities:
|
||||
blocks.append(
|
||||
{
|
||||
"title": f"Most affected components, true {SCA} issues ({sca.component_count})",
|
||||
**_bars(
|
||||
_group(
|
||||
[(one, name) for one in sca.vulnerabilities for name in one.component_names],
|
||||
lambda pair: pair[1],
|
||||
lambda pair: _vdr_severity(pair[0]),
|
||||
),
|
||||
mono=True,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
for name, report in tools:
|
||||
findings = _kept(report)
|
||||
|
||||
@@ -409,21 +428,6 @@ def _distributions(tools: list[tuple[str, SarifReport]], sca: VdrReport | None)
|
||||
}
|
||||
)
|
||||
|
||||
if sca and sca.vulnerabilities:
|
||||
blocks.append(
|
||||
{
|
||||
"title": f"Most affected components, true SCA issues ({sca.component_count})",
|
||||
**_bars(
|
||||
_group(
|
||||
[(one, name) for one in sca.vulnerabilities for name in one.component_names],
|
||||
lambda pair: pair[1],
|
||||
lambda pair: _vdr_severity(pair[0]),
|
||||
),
|
||||
mono=True,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
return blocks
|
||||
|
||||
|
||||
@@ -498,6 +502,23 @@ def _critical(tools: list[tuple[str, SarifReport]], sca: VdrReport | None) -> di
|
||||
"""The handful worth reading first, whichever tool found them."""
|
||||
rows: list[tuple[str, dict[str, Any]]] = []
|
||||
|
||||
for vulnerability in sca.vulnerabilities if sca else []:
|
||||
severity = _vdr_severity(vulnerability)
|
||||
rows.append(
|
||||
(
|
||||
severity,
|
||||
{
|
||||
"cells": [
|
||||
SCA,
|
||||
{"value": severity, "kind": "severity"},
|
||||
_stated(vulnerability.id),
|
||||
{"value": _stated(", ".join(vulnerability.component_names)), "kind": "mono"},
|
||||
_stated(vulnerability.description),
|
||||
]
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
for name, report in tools:
|
||||
for finding in _kept(report):
|
||||
severity = _finding_severity(finding)
|
||||
@@ -516,24 +537,8 @@ def _critical(tools: list[tuple[str, SarifReport]], sca: VdrReport | None) -> di
|
||||
)
|
||||
)
|
||||
|
||||
for vulnerability in sca.vulnerabilities if sca else []:
|
||||
severity = _vdr_severity(vulnerability)
|
||||
rows.append(
|
||||
(
|
||||
severity,
|
||||
{
|
||||
"cells": [
|
||||
"SCA",
|
||||
{"value": severity, "kind": "severity"},
|
||||
_stated(vulnerability.id),
|
||||
{"value": _stated(", ".join(vulnerability.component_names)), "kind": "mono"},
|
||||
_stated(vulnerability.description),
|
||||
]
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
# Sorted is stable, so within a severity each tool keeps its own order -- the rank, where it has one.
|
||||
# Sorted is stable, so within a severity the sources keep the report's order, and each tool its own
|
||||
# -- the rank, where it has one.
|
||||
rows.sort(key=lambda pair: SEVERITY_ORDER.index(pair[0]))
|
||||
|
||||
return {
|
||||
@@ -634,7 +639,7 @@ def _sca_section(sca: VdrReport) -> dict[str, Any]:
|
||||
"""The dependency findings."""
|
||||
return {
|
||||
"id": "sca",
|
||||
"title": "Dependency Vulnerabilities (SBOM / SCA)",
|
||||
"title": f"{SCA} — Dependency Vulnerabilities",
|
||||
"new_page": True,
|
||||
"groups": [
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user