sec-report-kit 0.2.6__tar.gz → 0.2.7__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.
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/CHANGELOG.md +8 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/PKG-INFO +1 -1
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/pyproject.toml +1 -1
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/requirements.txt +1 -1
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/__init__.py +1 -1
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/tests/test_cli.py +72 -1
- sec_report_kit-0.2.7/tests/test_html_renderer.py +166 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/tests/test_parsers.py +73 -0
- sec_report_kit-0.2.6/tests/test_html_renderer.py +0 -83
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/.gitattributes +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/.github/workflows/pip-audit.yml +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/.github/workflows/publish-pypi.yml +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/.github/workflows/publish-testpypi.yml +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/.github/workflows/trivy-fs-audit.yml +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/.github/workflows/unit-test-coverage-gate.yml +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/.gitignore +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/README.md +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/bandit-gaps-vs-sast-open-source-tools.md +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/check_assets.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/download_safety_db.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/download_safety_db.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/download_safety_db.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/install_external_clis.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/install_tools.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/install_tools.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_bandit_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_bandit_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_checkov_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_checkov_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_codeql_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_codeql_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_consolidated_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_consolidated_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_gitleaks_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_gitleaks_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_osv_scanner_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_osv_scanner_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_pip_audit_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_pip_audit_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_safety_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_safety_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_semgrep_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_semgrep_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_tfsec_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_tfsec_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_trivy_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_trivy_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_trufflehog_html.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/render_trufflehog_html.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_bandit.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_bandit.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_checkov.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_checkov.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_codeql.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_codeql.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_gitleaks.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_gitleaks.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_osv_scanner.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_osv_scanner.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_pip_audit.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_pip_audit.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_safety.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_safety.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_semgrep.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_semgrep.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_tfsec.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_tfsec.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_trivy.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_trivy.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_trufflehog.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_trufflehog.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_unit_tests_with_coverage.bat +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/scripts/run_unit_tests_with_coverage.sh +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/__main__.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/cli.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/generate_consolidated_security_report.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/mcp/__init__.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/mcp/server.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/models.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/__init__.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/bandit.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/checkov.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/codeql.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/gitleaks.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/osv_scanner.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/pip_audit.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/safety.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/semgrep.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/tfsec.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/trivy.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/parsers/trufflehog.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/report/__init__.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/report/html_renderer.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/services/__init__.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/services/normalize.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/src/sec_report_kit/services/summarize.py +0 -0
- {sec_report_kit-0.2.6 → sec_report_kit-0.2.7}/tests/test_mcp_server.py +0 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.2.7 - 2026-05-17
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Test coverage now includes the consolidated date-filter helpers, Safety parser fallback branches, and consolidated HTML dashboard visibility paths, bringing enforced suite coverage to 100%.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- `requirements.txt` now pins `urllib3==2.7.0` to resolve the Trivy-reported CVEs affecting `2.6.3`.
|
|
12
|
+
|
|
5
13
|
## 0.2.6 - 2026-05-17
|
|
6
14
|
|
|
7
15
|
### Added
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sec-report-kit"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.7"
|
|
8
8
|
description = "Generate HTML security reports from Trivy, pip-audit, Safety, Bandit, and Gitleaks JSON"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -8,7 +8,7 @@ from unittest.mock import patch
|
|
|
8
8
|
import pytest
|
|
9
9
|
from typer.testing import CliRunner
|
|
10
10
|
|
|
11
|
-
from sec_report_kit.cli import _write_report, app
|
|
11
|
+
from sec_report_kit.cli import _parse_modified_since, _parse_modified_until, _write_report, app
|
|
12
12
|
|
|
13
13
|
runner = CliRunner()
|
|
14
14
|
|
|
@@ -666,6 +666,77 @@ def test_generate_consolidated_wrapper_invokes_cli(monkeypatch, tmp_path):
|
|
|
666
666
|
)
|
|
667
667
|
|
|
668
668
|
|
|
669
|
+
def test_parse_modified_since_supports_named_ranges():
|
|
670
|
+
today = _parse_modified_since("today")
|
|
671
|
+
yesterday = _parse_modified_since("yesterday")
|
|
672
|
+
last_week = _parse_modified_since("last-week")
|
|
673
|
+
|
|
674
|
+
assert today.hour == 0
|
|
675
|
+
assert today.minute == 0
|
|
676
|
+
assert yesterday.hour == 0
|
|
677
|
+
assert yesterday.minute == 0
|
|
678
|
+
assert yesterday < today
|
|
679
|
+
assert last_week < today
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
def test_parse_modified_since_supports_iso_inputs_and_raises_for_invalid():
|
|
683
|
+
naive = _parse_modified_since("2026-05-10")
|
|
684
|
+
aware = _parse_modified_since("2026-05-10T09:00:00Z")
|
|
685
|
+
|
|
686
|
+
assert naive.year == 2026
|
|
687
|
+
assert naive.month == 5
|
|
688
|
+
assert naive.day == 10
|
|
689
|
+
assert naive.tzinfo is not None
|
|
690
|
+
assert aware.tzinfo is not None
|
|
691
|
+
|
|
692
|
+
import typer
|
|
693
|
+
|
|
694
|
+
with pytest.raises(typer.BadParameter):
|
|
695
|
+
_parse_modified_since("not-a-date")
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
def test_parse_modified_until_supports_named_and_date_only_inputs():
|
|
699
|
+
today = _parse_modified_until("today")
|
|
700
|
+
yesterday = _parse_modified_until("yesterday")
|
|
701
|
+
date_only = _parse_modified_until("2026-05-10")
|
|
702
|
+
datetime_value = _parse_modified_until("2026-05-10T09:15:00")
|
|
703
|
+
|
|
704
|
+
assert today.hour == 23
|
|
705
|
+
assert today.minute == 59
|
|
706
|
+
assert today.second == 59
|
|
707
|
+
assert today.microsecond == 999999
|
|
708
|
+
assert yesterday.hour == 23
|
|
709
|
+
assert yesterday.minute == 59
|
|
710
|
+
assert date_only.hour == 23
|
|
711
|
+
assert date_only.minute == 59
|
|
712
|
+
assert datetime_value.hour == 9
|
|
713
|
+
assert datetime_value.minute == 15
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
def test_generate_consolidated_wrapper_module_main(monkeypatch, tmp_path):
|
|
717
|
+
input_dir = tmp_path / "reports"
|
|
718
|
+
output_dir = tmp_path / "out"
|
|
719
|
+
input_dir.mkdir()
|
|
720
|
+
|
|
721
|
+
monkeypatch.setattr(
|
|
722
|
+
"sys.argv",
|
|
723
|
+
[
|
|
724
|
+
"generate_consolidated_security_report.py",
|
|
725
|
+
"--input",
|
|
726
|
+
str(input_dir),
|
|
727
|
+
"--output",
|
|
728
|
+
str(output_dir),
|
|
729
|
+
],
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
with patch("sec_report_kit.cli.render_consolidated") as mock_render:
|
|
733
|
+
with pytest.raises(SystemExit) as exc:
|
|
734
|
+
runpy.run_module("sec_report_kit.generate_consolidated_security_report", run_name="__main__")
|
|
735
|
+
|
|
736
|
+
assert exc.value.code == 0
|
|
737
|
+
mock_render.assert_called_once()
|
|
738
|
+
|
|
739
|
+
|
|
669
740
|
def test_consolidated_helper_scripts_exist():
|
|
670
741
|
assert Path("scripts/render_consolidated_html.bat").exists()
|
|
671
742
|
assert Path("scripts/render_consolidated_html.sh").exists()
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from sec_report_kit.models import Finding
|
|
2
|
+
from sec_report_kit.report.html_renderer import render_consolidated_dashboard_report, render_html_report
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
EMPTY_COUNTS = {"CRITICAL": 0, "HIGH": 0, "MEDIUM": 0, "LOW": 0, "UNKNOWN": 0}
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _finding(severity="HIGH", primary_url="https://example.com"):
|
|
9
|
+
return Finding(
|
|
10
|
+
source_type="test",
|
|
11
|
+
target="myimage:1",
|
|
12
|
+
severity=severity,
|
|
13
|
+
vulnerability_id="CVE-2024-0001",
|
|
14
|
+
package="openssl",
|
|
15
|
+
installed_version="1.0",
|
|
16
|
+
fixed_version="1.1",
|
|
17
|
+
title="Test vuln",
|
|
18
|
+
primary_url=primary_url,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_render_html_with_finding_with_url():
|
|
23
|
+
counts = {**EMPTY_COUNTS, "HIGH": 1}
|
|
24
|
+
html = render_html_report(
|
|
25
|
+
target_ref="myimage:1",
|
|
26
|
+
source_label="trivy",
|
|
27
|
+
findings=[_finding(primary_url="https://example.com")],
|
|
28
|
+
counts=counts,
|
|
29
|
+
)
|
|
30
|
+
assert "CVE-2024-0001" in html
|
|
31
|
+
assert "https://example.com" in html
|
|
32
|
+
assert "openssl" in html
|
|
33
|
+
assert "This report is generated by" in html
|
|
34
|
+
assert ">sec-report-kit<" in html
|
|
35
|
+
assert "Connect with the developer:" in html
|
|
36
|
+
assert ">Shan Konduru<" in html
|
|
37
|
+
assert "https://www.linkedin.com/in/shankonduru/" in html
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_render_html_with_finding_no_url():
|
|
41
|
+
counts = {**EMPTY_COUNTS, "HIGH": 1}
|
|
42
|
+
html = render_html_report(
|
|
43
|
+
target_ref="myimage:1",
|
|
44
|
+
source_label="trivy",
|
|
45
|
+
findings=[_finding(primary_url="")],
|
|
46
|
+
counts=counts,
|
|
47
|
+
)
|
|
48
|
+
assert "CVE-2024-0001" in html
|
|
49
|
+
# no anchor tag when url is empty
|
|
50
|
+
assert 'href=""' not in html
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_render_html_no_findings():
|
|
54
|
+
html = render_html_report(
|
|
55
|
+
target_ref="myimage:1",
|
|
56
|
+
source_label="trivy",
|
|
57
|
+
findings=[],
|
|
58
|
+
counts=EMPTY_COUNTS,
|
|
59
|
+
)
|
|
60
|
+
assert "No vulnerabilities found" in html
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_render_html_escapes_special_chars():
|
|
64
|
+
finding = Finding(
|
|
65
|
+
source_type="test",
|
|
66
|
+
target='target<script>alert(1)</script>',
|
|
67
|
+
severity="LOW",
|
|
68
|
+
vulnerability_id="CVE-X",
|
|
69
|
+
package='pkg"quoted"',
|
|
70
|
+
installed_version="1.0",
|
|
71
|
+
fixed_version="-",
|
|
72
|
+
title="<b>bold</b>",
|
|
73
|
+
primary_url="",
|
|
74
|
+
)
|
|
75
|
+
counts = {**EMPTY_COUNTS, "LOW": 1}
|
|
76
|
+
html = render_html_report(
|
|
77
|
+
target_ref="ref",
|
|
78
|
+
source_label="test",
|
|
79
|
+
findings=[finding],
|
|
80
|
+
counts=counts,
|
|
81
|
+
)
|
|
82
|
+
assert "<script>alert(1)</script>" not in html
|
|
83
|
+
assert "<script>alert(1)</script>" in html
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_render_consolidated_dashboard_with_available_and_missing_reports(tmp_path):
|
|
87
|
+
reports_dir = tmp_path / "reports"
|
|
88
|
+
output_dir = tmp_path / "out"
|
|
89
|
+
reports_dir.mkdir()
|
|
90
|
+
output_dir.mkdir()
|
|
91
|
+
|
|
92
|
+
(reports_dir / "bandit-report.html").write_text("<html>bandit</html>", encoding="utf-8")
|
|
93
|
+
(output_dir / "trivy-report.html").write_text("<html>trivy</html>", encoding="utf-8")
|
|
94
|
+
|
|
95
|
+
findings = [
|
|
96
|
+
Finding(
|
|
97
|
+
source_type="bandit",
|
|
98
|
+
target="src/app.py",
|
|
99
|
+
severity="HIGH",
|
|
100
|
+
vulnerability_id="B101",
|
|
101
|
+
package="-",
|
|
102
|
+
installed_version="-",
|
|
103
|
+
fixed_version="-",
|
|
104
|
+
title="Assert used",
|
|
105
|
+
primary_url="",
|
|
106
|
+
),
|
|
107
|
+
Finding(
|
|
108
|
+
source_type="trivy",
|
|
109
|
+
target="requirements.txt",
|
|
110
|
+
severity="CRITICAL",
|
|
111
|
+
vulnerability_id="CVE-1",
|
|
112
|
+
package="urllib3",
|
|
113
|
+
installed_version="2.6.3",
|
|
114
|
+
fixed_version="2.7.0",
|
|
115
|
+
title="Critical vuln",
|
|
116
|
+
primary_url="",
|
|
117
|
+
),
|
|
118
|
+
Finding(
|
|
119
|
+
source_type="pip-audit",
|
|
120
|
+
target="requirements.txt",
|
|
121
|
+
severity="MEDIUM",
|
|
122
|
+
vulnerability_id="GHSA-1",
|
|
123
|
+
package="requests",
|
|
124
|
+
installed_version="2.0.0",
|
|
125
|
+
fixed_version="2.1.0",
|
|
126
|
+
title="Dependency finding",
|
|
127
|
+
primary_url="",
|
|
128
|
+
),
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
html = render_consolidated_dashboard_report(
|
|
132
|
+
target_ref="repo-root",
|
|
133
|
+
findings=findings,
|
|
134
|
+
counts={"CRITICAL": 1, "HIGH": 1, "MEDIUM": 1, "LOW": 0, "UNKNOWN": 0},
|
|
135
|
+
reports_dir=reports_dir,
|
|
136
|
+
output_dir=output_dir,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
assert 'href="../reports/bandit-report.html"' in html
|
|
140
|
+
assert 'src="trivy-report.html"' in html
|
|
141
|
+
assert "Open Full Report" in html
|
|
142
|
+
assert "Generate this report first using scripts/render_pip_audit_html.sh or scripts/render_pip_audit_html.bat." in html
|
|
143
|
+
assert "scripts/render_pip_audit_html.sh" in html
|
|
144
|
+
assert "Bandit Report" in html
|
|
145
|
+
assert "Trivy Report" in html
|
|
146
|
+
assert "Reports available:" in html
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def test_render_consolidated_dashboard_shows_all_tools_when_no_visible_tools(tmp_path):
|
|
150
|
+
reports_dir = tmp_path / "reports"
|
|
151
|
+
output_dir = tmp_path / "out"
|
|
152
|
+
reports_dir.mkdir()
|
|
153
|
+
output_dir.mkdir()
|
|
154
|
+
|
|
155
|
+
html = render_consolidated_dashboard_report(
|
|
156
|
+
target_ref="repo-root",
|
|
157
|
+
findings=[],
|
|
158
|
+
counts={"CRITICAL": 0, "HIGH": 0, "MEDIUM": 0, "LOW": 0, "UNKNOWN": 0},
|
|
159
|
+
reports_dir=reports_dir,
|
|
160
|
+
output_dir=output_dir,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
assert "pip-audit Report" in html
|
|
164
|
+
assert "GitLeaks Report" in html
|
|
165
|
+
assert "TruffleHog Report" in html
|
|
166
|
+
assert "Generate this report first using scripts/render_trivy_html.sh or scripts/render_trivy_html.bat." in html
|
|
@@ -88,6 +88,79 @@ def test_parse_safety_json_basic():
|
|
|
88
88
|
assert findings[0].fixed_version == "1.26.19"
|
|
89
89
|
|
|
90
90
|
|
|
91
|
+
def test_parse_safety_json_list_payload_and_helper_fallbacks():
|
|
92
|
+
payload = [
|
|
93
|
+
{
|
|
94
|
+
"id": "CVE-2026-1",
|
|
95
|
+
"package": "requests",
|
|
96
|
+
"installed_version": "2.0.0",
|
|
97
|
+
"fixed_version": "2.1.0",
|
|
98
|
+
"description": "Fallback description",
|
|
99
|
+
"url": "https://example.com/safety/url",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"CVE": "CVE-2026-2",
|
|
103
|
+
"package_name": "urllib3",
|
|
104
|
+
"analyzed_version": "1.0.0",
|
|
105
|
+
"fix_versions": ["1.1.0", "1.2.0"],
|
|
106
|
+
"summary": "Summary title",
|
|
107
|
+
"references": [{"url": "https://example.com/safety/ref"}],
|
|
108
|
+
"severity": {"name": "medium"},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"vulnerability_id": "CVE-2026-3",
|
|
112
|
+
"package_name": "idna",
|
|
113
|
+
"analyzed_version": "3.0",
|
|
114
|
+
"fixed_versions": [],
|
|
115
|
+
"references": ["https://example.com/safety/list-ref"],
|
|
116
|
+
"severity": {"value": "low"},
|
|
117
|
+
},
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
findings = parse_safety_json(payload)
|
|
121
|
+
|
|
122
|
+
assert len(findings) == 3
|
|
123
|
+
assert findings[0].severity == "UNKNOWN"
|
|
124
|
+
assert findings[0].fixed_version == "2.1.0"
|
|
125
|
+
assert findings[0].title == "Fallback description"
|
|
126
|
+
assert findings[0].primary_url == "https://example.com/safety/url"
|
|
127
|
+
assert findings[1].severity == "MEDIUM"
|
|
128
|
+
assert findings[1].vulnerability_id == "CVE-2026-2"
|
|
129
|
+
assert findings[1].fixed_version == "1.1.0, 1.2.0"
|
|
130
|
+
assert findings[1].title == "Summary title"
|
|
131
|
+
assert findings[1].primary_url == "https://example.com/safety/ref"
|
|
132
|
+
assert findings[2].severity == "LOW"
|
|
133
|
+
assert findings[2].fixed_version == "-"
|
|
134
|
+
assert findings[2].title == "Safety finding"
|
|
135
|
+
assert findings[2].primary_url == "https://example.com/safety/list-ref"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_parse_safety_json_dict_filters_non_dict_items_and_handles_unknown_severity():
|
|
139
|
+
payload = {
|
|
140
|
+
"vulnerabilities": [
|
|
141
|
+
"skip-me",
|
|
142
|
+
{"package_name": "pkg", "severity": {"other": "none"}},
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
findings = parse_safety_json(payload)
|
|
147
|
+
|
|
148
|
+
assert len(findings) == 1
|
|
149
|
+
assert findings[0].severity == "UNKNOWN"
|
|
150
|
+
assert findings[0].vulnerability_id == "-"
|
|
151
|
+
assert findings[0].package == "pkg"
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_detect_source_type_safety_from_scanned_packages_metadata():
|
|
155
|
+
payload = {"scanned_packages": [], "vulnerabilities": []}
|
|
156
|
+
assert detect_source_type(payload) == "safety"
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def test_detect_source_type_safety_from_cve_sample():
|
|
160
|
+
payload = {"vulnerabilities": [{"CVE": "CVE-2026-1000"}]}
|
|
161
|
+
assert detect_source_type(payload) == "safety"
|
|
162
|
+
|
|
163
|
+
|
|
91
164
|
def test_count_by_severity_unknown_when_missing():
|
|
92
165
|
payload = {
|
|
93
166
|
"dependencies": [
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
from sec_report_kit.models import Finding
|
|
2
|
-
from sec_report_kit.report.html_renderer import render_html_report
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
EMPTY_COUNTS = {"CRITICAL": 0, "HIGH": 0, "MEDIUM": 0, "LOW": 0, "UNKNOWN": 0}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def _finding(severity="HIGH", primary_url="https://example.com"):
|
|
9
|
-
return Finding(
|
|
10
|
-
source_type="test",
|
|
11
|
-
target="myimage:1",
|
|
12
|
-
severity=severity,
|
|
13
|
-
vulnerability_id="CVE-2024-0001",
|
|
14
|
-
package="openssl",
|
|
15
|
-
installed_version="1.0",
|
|
16
|
-
fixed_version="1.1",
|
|
17
|
-
title="Test vuln",
|
|
18
|
-
primary_url=primary_url,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def test_render_html_with_finding_with_url():
|
|
23
|
-
counts = {**EMPTY_COUNTS, "HIGH": 1}
|
|
24
|
-
html = render_html_report(
|
|
25
|
-
target_ref="myimage:1",
|
|
26
|
-
source_label="trivy",
|
|
27
|
-
findings=[_finding(primary_url="https://example.com")],
|
|
28
|
-
counts=counts,
|
|
29
|
-
)
|
|
30
|
-
assert "CVE-2024-0001" in html
|
|
31
|
-
assert "https://example.com" in html
|
|
32
|
-
assert "openssl" in html
|
|
33
|
-
assert "This report is generated by" in html
|
|
34
|
-
assert ">sec-report-kit<" in html
|
|
35
|
-
assert "Connect with the developer:" in html
|
|
36
|
-
assert ">Shan Konduru<" in html
|
|
37
|
-
assert "https://www.linkedin.com/in/shankonduru/" in html
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def test_render_html_with_finding_no_url():
|
|
41
|
-
counts = {**EMPTY_COUNTS, "HIGH": 1}
|
|
42
|
-
html = render_html_report(
|
|
43
|
-
target_ref="myimage:1",
|
|
44
|
-
source_label="trivy",
|
|
45
|
-
findings=[_finding(primary_url="")],
|
|
46
|
-
counts=counts,
|
|
47
|
-
)
|
|
48
|
-
assert "CVE-2024-0001" in html
|
|
49
|
-
# no anchor tag when url is empty
|
|
50
|
-
assert 'href=""' not in html
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def test_render_html_no_findings():
|
|
54
|
-
html = render_html_report(
|
|
55
|
-
target_ref="myimage:1",
|
|
56
|
-
source_label="trivy",
|
|
57
|
-
findings=[],
|
|
58
|
-
counts=EMPTY_COUNTS,
|
|
59
|
-
)
|
|
60
|
-
assert "No vulnerabilities found" in html
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def test_render_html_escapes_special_chars():
|
|
64
|
-
finding = Finding(
|
|
65
|
-
source_type="test",
|
|
66
|
-
target='target<script>alert(1)</script>',
|
|
67
|
-
severity="LOW",
|
|
68
|
-
vulnerability_id="CVE-X",
|
|
69
|
-
package='pkg"quoted"',
|
|
70
|
-
installed_version="1.0",
|
|
71
|
-
fixed_version="-",
|
|
72
|
-
title="<b>bold</b>",
|
|
73
|
-
primary_url="",
|
|
74
|
-
)
|
|
75
|
-
counts = {**EMPTY_COUNTS, "LOW": 1}
|
|
76
|
-
html = render_html_report(
|
|
77
|
-
target_ref="ref",
|
|
78
|
-
source_label="test",
|
|
79
|
-
findings=[finding],
|
|
80
|
-
counts=counts,
|
|
81
|
-
)
|
|
82
|
-
assert "<script>alert(1)</script>" not in html
|
|
83
|
-
assert "<script>alert(1)</script>" in html
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|