Files
strix/tests/test_reporting_fields.py
T

1016 lines
34 KiB
Python

"""Tests for restored report fields, SCA tool, and report formatting guidance."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
import pytest
from strix.report.dedupe import (
_check_dependency_duplicate,
_prepare_report_for_comparison,
check_duplicate,
)
from strix.report.state import ReportState, set_global_report_state
from strix.tools.finish.tool import finish_scan
from strix.tools.reporting.tool import (
_do_create,
_do_create_dependency,
create_dependency_report,
create_vulnerability_report,
)
if TYPE_CHECKING:
from pathlib import Path
_CVSS = {
"attack_vector": "N",
"attack_complexity": "L",
"privileges_required": "N",
"user_interaction": "N",
"scope": "U",
"confidentiality": "H",
"integrity": "H",
"availability": "H",
}
@pytest.fixture
def report_state(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> ReportState:
monkeypatch.chdir(tmp_path)
state = ReportState(run_name="test-run")
set_global_report_state(state)
return state
async def test_create_report_persists_new_fields(report_state: ReportState) -> None:
result = await _do_create(
title="Reflected XSS in search",
description="q reflects unencoded input.",
impact="Session theft.",
target="https://app.example.com",
technical_analysis="Input interpolated into HTML.",
poc_description="1. open /search?q=<payload>",
poc_script_code="GET /search?q=<script>alert(1)</script>",
remediation_steps="Context-encode output.",
evidence="Response echoes the payload verbatim.",
assumptions="Assumes a victim opens a crafted link.",
counterevidence="No output encoding or CSP observed on this response.",
confidence="HIGH",
severity_change_conditions="A strict CSP would lower the severity.",
fix_effort="LOW",
cvss_breakdown=_CVSS,
endpoint="/search",
method="GET",
cve=None,
cwe="CWE-79",
code_locations=None,
fix_pr_body="## Fix\nEncode output.",
)
assert result["success"] is True
report = report_state.vulnerability_reports[0]
assert report["evidence"] == "Response echoes the payload verbatim."
assert report["assumptions"] == "Assumes a victim opens a crafted link."
assert report["fix_effort"] == "low"
assert report["fix_pr_body"] == "## Fix\nEncode output."
assert report["finding_class"] == "dynamic"
assert report["counterevidence"] == "No output encoding or CSP observed on this response."
assert report["confidence"] == "high"
assert report["severity_change_conditions"] == "A strict CSP would lower the severity."
async def test_create_report_requires_evidence_and_assumptions(
report_state: ReportState,
) -> None:
result = await _do_create(
title="X",
description="d",
impact="i",
target="t",
technical_analysis="ta",
poc_description="p",
poc_script_code="c",
remediation_steps="r",
evidence=" ",
assumptions="",
counterevidence="none found",
confidence="high",
severity_change_conditions="n/a",
fix_effort="low",
cvss_breakdown=_CVSS,
endpoint=None,
method=None,
cve=None,
cwe=None,
code_locations=None,
)
assert result["success"] is False
joined = " ".join(result["errors"])
assert "Evidence" in joined
assert "Assumptions" in joined
assert not report_state.vulnerability_reports
async def test_create_report_rejects_invalid_fix_effort(report_state: ReportState) -> None:
result = await _do_create(
title="X",
description="d",
impact="i",
target="t",
technical_analysis="ta",
poc_description="p",
poc_script_code="c",
remediation_steps="r",
evidence="e",
assumptions="a",
counterevidence="none found",
confidence="high",
severity_change_conditions="n/a",
fix_effort="enormous",
cvss_breakdown=_CVSS,
endpoint=None,
method=None,
cve=None,
cwe=None,
code_locations=None,
)
assert result["success"] is False
assert any("fix_effort" in e for e in result["errors"])
assert not report_state.vulnerability_reports
async def _create_with(report_state: ReportState, **overrides: object) -> dict[str, Any]:
kwargs: dict[str, object] = {
"title": "X",
"description": "d",
"impact": "i",
"target": "t",
"technical_analysis": "ta",
"poc_description": "p",
"poc_script_code": "c",
"remediation_steps": "r",
"evidence": "e",
"assumptions": "a",
"counterevidence": "No guard found on this path.",
"confidence": "high",
"severity_change_conditions": "Proof of internet exposure would raise it.",
"fix_effort": "low",
"cvss_breakdown": _CVSS,
"endpoint": None,
"method": None,
"cve": None,
"cwe": None,
"code_locations": None,
}
kwargs.update(overrides)
assert report_state is not None
return await _do_create(**kwargs) # type: ignore[arg-type]
async def test_create_report_requires_counterevidence(report_state: ReportState) -> None:
result = await _create_with(report_state, counterevidence=" ")
assert result["success"] is False
assert any("Counterevidence" in e for e in result["errors"])
assert not report_state.vulnerability_reports
async def test_create_report_requires_severity_change_conditions(
report_state: ReportState,
) -> None:
result = await _create_with(report_state, severity_change_conditions="")
assert result["success"] is False
assert any("severity_change_conditions" in e for e in result["errors"])
assert not report_state.vulnerability_reports
async def test_create_report_rejects_invalid_confidence(report_state: ReportState) -> None:
result = await _create_with(report_state, confidence="pretty sure")
assert result["success"] is False
assert any("confidence" in e for e in result["errors"])
assert not report_state.vulnerability_reports
async def test_create_report_requires_rationale_when_confidence_not_high(
report_state: ReportState,
) -> None:
result = await _create_with(report_state, confidence="medium")
assert result["success"] is False
assert any("confidence_rationale" in e for e in result["errors"])
assert not report_state.vulnerability_reports
async def test_create_report_accepts_medium_confidence_with_rationale(
report_state: ReportState,
) -> None:
result = await _create_with(
report_state,
confidence="medium",
confidence_rationale="Static-only trace; could not stand up the service.",
)
assert result["success"] is True
report = report_state.vulnerability_reports[0]
assert report["confidence"] == "medium"
assert report["confidence_rationale"] == "Static-only trace; could not stand up the service."
async def test_dependency_report_sets_class_and_metadata(report_state: ReportState) -> None:
result = await _do_create_dependency(
title="CVE-2021-23337 in lodash 4.17.20",
description="Command injection via template.",
target="repo/package.json",
cve="CVE-2021-23337",
package_name="lodash",
installed_version="4.17.20",
impact="Arbitrary command execution.",
remediation_steps="Upgrade to 4.17.21.",
assumptions="Assumes the template sink is reachable.",
package_ecosystem="npm",
manifest_path="package-lock.json",
fixed_version="4.17.21",
cwe="CWE-94",
advisory_cvss=7.2,
technical_analysis=None,
fix_effort="trivial",
)
assert result["success"] is True
report = report_state.vulnerability_reports[0]
assert report["finding_class"] == "dependency_cve"
assert report["cve"] == "CVE-2021-23337"
assert report["severity"] == "high"
assert report["evidence"] == (
"**Advisory evidence:** `CVE-2021-23337` applies to `lodash` "
"at installed version `4.17.20`. The advisory is fixed in `4.17.21`."
)
assert report["dependency_metadata"] == {
"package_name": "lodash",
"installed_version": "4.17.20",
"package_ecosystem": "npm",
"manifest_path": "package-lock.json",
"fixed_version": "4.17.21",
}
async def test_dependency_report_records_transitive_chain(report_state: ReportState) -> None:
result = await _do_create_dependency(
title="CVE-2022-24999 in qs 6.10.2",
description="Prototype pollution in qs parsing.",
target="repo/package.json",
cve="CVE-2022-24999",
package_name="qs",
installed_version="6.10.2",
impact="Denial of service via crafted query strings.",
remediation_steps="Upgrade express to 4.18.2, which resolves qs 6.11.0.",
assumptions="qs parses all incoming query strings by default.",
package_ecosystem="npm",
manifest_path="package-lock.json",
fixed_version="6.10.3",
cwe="CWE-1321",
advisory_cvss=7.5,
technical_analysis=None,
fix_effort="trivial",
introduced_by="express@4.18.1",
dependency_path="express@4.18.1 > body-parser@1.20.0 > qs@6.10.2",
)
assert result["success"] is True
report = report_state.vulnerability_reports[0]
assert report["dependency_metadata"]["introduced_by"] == "express@4.18.1"
assert (
report["dependency_metadata"]["dependency_path"]
== "express@4.18.1 > body-parser@1.20.0 > qs@6.10.2"
)
assert (
"**Transitive dependency:** introduced by the direct dependency `express@4.18.1`."
in report["evidence"]
)
assert (
"**Dependency chain:** `express@4.18.1 > body-parser@1.20.0 > qs@6.10.2`"
in report["evidence"]
)
async def test_dependency_report_omits_blank_chain_fields(report_state: ReportState) -> None:
result = await _do_create_dependency(
title="CVE-2024-0001 in sample 1.0.0",
description="Published advisory affects the pinned version.",
target="repo/package.json",
cve="CVE-2024-0001",
package_name="sample",
installed_version="1.0.0",
impact="Impact.",
remediation_steps="Upgrade.",
assumptions="Assumptions.",
package_ecosystem="npm",
manifest_path="package-lock.json",
fixed_version=None,
cwe=None,
advisory_cvss=5.0,
technical_analysis=None,
fix_effort="trivial",
introduced_by=" ",
dependency_path=None,
)
assert result["success"] is True
report = report_state.vulnerability_reports[0]
assert "introduced_by" not in report["dependency_metadata"]
assert "dependency_path" not in report["dependency_metadata"]
async def test_dependency_report_with_zero_cvss_remains_low_severity(
report_state: ReportState,
) -> None:
result = await _do_create_dependency(
title="CVE-2024-0001 in sample 1.0.0",
description="Published advisory affects the pinned version.",
target="repo/package.json",
cve="CVE-2024-0001",
package_name="sample",
installed_version="1.0.0",
impact="Low-impact dependency advisory.",
remediation_steps="Upgrade to 1.0.1.",
assumptions="Assumes the package is included in deployed builds.",
package_ecosystem="npm",
manifest_path="package-lock.json",
fixed_version="1.0.1",
cwe=None,
advisory_cvss=0.0,
technical_analysis=None,
fix_effort="low",
)
assert result["success"] is True
assert result["severity"] == "low"
report = report_state.vulnerability_reports[0]
assert report["severity"] == "low"
assert report["cvss"] == 0.0
async def test_dependency_report_records_reachability(report_state: ReportState) -> None:
result = await _do_create_dependency(
title="CVE-2021-23337 in lodash 4.17.20",
description="Command injection via template.",
target="repo/package.json",
cve="CVE-2021-23337",
package_name="lodash",
installed_version="4.17.20",
impact="Command injection where template is used.",
remediation_steps="Upgrade to 4.17.21.",
assumptions="Assumes the template sink is reachable.",
package_ecosystem="npm",
manifest_path="package-lock.json",
fixed_version="4.17.21",
cwe=None,
advisory_cvss=7.2,
technical_analysis=None,
fix_effort="low",
reachability="vulnerable_symbol_used",
reachability_evidence="src/render.ts:14 calls `_.template()`.",
)
assert result["success"] is True
report = report_state.vulnerability_reports[0]
assert report["dependency_metadata"]["reachability"] == "vulnerable_symbol_used"
assert (
report["dependency_metadata"]["reachability_evidence"]
== "src/render.ts:14 calls `_.template()`."
)
assert "**Usage analysis:**" in report["evidence"]
assert "not a proof of exploitability or of safety" in report["evidence"]
# The level must never influence the rating — that stays advisory_cvss only.
assert report["severity"] == "high"
async def test_dependency_report_rejects_reachability_without_evidence(
report_state: ReportState,
) -> None:
result = await _do_create_dependency(
title="CVE-2024-0001 in sample 1.0.0",
description="Published advisory affects the pinned version.",
target="repo/package.json",
cve="CVE-2024-0001",
package_name="sample",
installed_version="1.0.0",
impact="Impact.",
remediation_steps="Upgrade.",
assumptions="Assumptions.",
package_ecosystem="npm",
manifest_path="package-lock.json",
fixed_version="1.0.1",
cwe=None,
advisory_cvss=5.0,
technical_analysis=None,
fix_effort="low",
reachability="not_imported",
)
assert result["success"] is False
assert any("reachability_evidence is required" in e for e in result["errors"])
assert not report_state.vulnerability_reports
async def test_dependency_report_rejects_unknown_reachability_level(
report_state: ReportState,
) -> None:
result = await _do_create_dependency(
title="CVE-2024-0001 in sample 1.0.0",
description="Published advisory affects the pinned version.",
target="repo/package.json",
cve="CVE-2024-0001",
package_name="sample",
installed_version="1.0.0",
impact="Impact.",
remediation_steps="Upgrade.",
assumptions="Assumptions.",
package_ecosystem="npm",
manifest_path="package-lock.json",
fixed_version="1.0.1",
cwe=None,
advisory_cvss=5.0,
technical_analysis=None,
fix_effort="low",
reachability="not_exploitable",
reachability_evidence="vibes",
)
assert result["success"] is False
assert any("Invalid reachability" in e for e in result["errors"])
assert not report_state.vulnerability_reports
async def test_dependency_report_omits_unknown_reachability(report_state: ReportState) -> None:
result = await _do_create_dependency(
title="CVE-2024-0001 in sample 1.0.0",
description="Published advisory affects the pinned version.",
target="repo/package.json",
cve="CVE-2024-0001",
package_name="sample",
installed_version="1.0.0",
impact="Impact.",
remediation_steps="Upgrade.",
assumptions="Analysis was inconclusive.",
package_ecosystem="npm",
manifest_path="package-lock.json",
fixed_version="1.0.1",
cwe=None,
advisory_cvss=5.0,
technical_analysis=None,
fix_effort="low",
)
assert result["success"] is True
metadata = report_state.vulnerability_reports[0]["dependency_metadata"]
assert "reachability" not in metadata
assert "reachability_evidence" not in metadata
async def test_dependency_report_requires_advisory_cvss(report_state: ReportState) -> None:
result = await _do_create_dependency(
title="CVE-2024-0001 in sample 1.0.0",
description="Published advisory affects the pinned version.",
target="repo/package.json",
cve="CVE-2024-0001",
package_name="sample",
installed_version="1.0.0",
impact="Some impact.",
remediation_steps="Upgrade to 1.0.1.",
assumptions="Assumes the package ships in deployed builds.",
package_ecosystem="npm",
manifest_path="package-lock.json",
fixed_version="1.0.1",
cwe=None,
advisory_cvss=None,
technical_analysis=None,
fix_effort="low",
)
assert result["success"] is False
assert any("advisory_cvss is required" in e for e in result["errors"])
assert not report_state.vulnerability_reports
async def test_dependency_report_dedupe_candidate_includes_dependency_metadata(
report_state: ReportState,
monkeypatch: pytest.MonkeyPatch,
) -> None:
captured: dict[str, object] = {}
async def fake_check_duplicate(
candidate: dict[str, object],
existing: list[dict[str, object]],
) -> dict[str, object]:
captured["candidate"] = candidate
captured["existing"] = existing
return {"is_duplicate": False}
monkeypatch.setattr("strix.report.dedupe.check_duplicate", fake_check_duplicate)
report_state.vulnerability_reports.append(
{
"id": "vuln-0001",
"title": "CVE-2024-0001 in other 1.0.0",
"severity": "low",
"timestamp": "2026-01-01 00:00:00 UTC",
"description": "Existing dependency finding.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "other",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
},
}
)
result = await _do_create_dependency(
title="CVE-2024-0001 in sample 1.0.0",
description="Published advisory affects the pinned version.",
target="repo/package.json",
cve="CVE-2024-0001",
package_name="sample",
installed_version="1.0.0",
impact="Low-impact dependency advisory.",
remediation_steps="Upgrade to 1.0.1.",
assumptions="Assumes the package is included in deployed builds.",
package_ecosystem="npm",
manifest_path="package-lock.json",
fixed_version="1.0.1",
cwe=None,
advisory_cvss=0.0,
technical_analysis=None,
fix_effort="low",
)
assert result["success"] is True
assert captured["candidate"] == {
"title": "CVE-2024-0001 in sample 1.0.0",
"description": "Published advisory affects the pinned version.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
"manifest_path": "package-lock.json",
"fixed_version": "1.0.1",
},
"technical_analysis": None,
}
async def test_dependency_report_rejects_bad_cve(report_state: ReportState) -> None:
result = await _do_create_dependency(
title="bad",
description="d",
target="t",
cve="not-a-cve",
package_name="pkg",
installed_version="1.0.0",
impact="i",
remediation_steps="r",
assumptions="a",
package_ecosystem="npm",
manifest_path="package-lock.json",
fixed_version=None,
cwe=None,
advisory_cvss=None,
technical_analysis=None,
fix_effort="low",
)
assert result["success"] is False
assert not report_state.vulnerability_reports
async def test_dependency_report_requires_ecosystem(report_state: ReportState) -> None:
result = await _do_create_dependency(
title="CVE-2024-0001 in sample 1.0.0",
description="Published advisory affects the pinned version.",
target="repo/package.json",
cve="CVE-2024-0001",
package_name="sample",
installed_version="1.0.0",
impact="Low-impact dependency advisory.",
remediation_steps="Upgrade to 1.0.1.",
assumptions="Assumes the package is included in deployed builds.",
package_ecosystem="",
manifest_path="package-lock.json",
fixed_version="1.0.1",
cwe=None,
advisory_cvss=0.0,
technical_analysis=None,
fix_effort="low",
)
assert result["success"] is False
assert any("package_ecosystem" in error for error in result["errors"])
assert not report_state.vulnerability_reports
async def test_dependency_report_requires_manifest_path(report_state: ReportState) -> None:
result = await _do_create_dependency(
title="CVE-2024-0001 in sample 1.0.0",
description="Published advisory affects the pinned version.",
target="repo/package.json",
cve="CVE-2024-0001",
package_name="sample",
installed_version="1.0.0",
impact="Low-impact dependency advisory.",
remediation_steps="Upgrade to 1.0.1.",
assumptions="Assumes the package is included in deployed builds.",
package_ecosystem="npm",
manifest_path=None,
fixed_version="1.0.1",
cwe=None,
advisory_cvss=5.0,
technical_analysis=None,
fix_effort="low",
)
assert result["success"] is False
assert any("manifest_path is required" in error for error in result["errors"])
assert not report_state.vulnerability_reports
@pytest.mark.parametrize(
"bad_path",
["/etc/passwd", "..\\pom.xml", "services/../pom.xml", "./package.json", "C:/repo/pom.xml"],
)
async def test_dependency_report_rejects_unsafe_manifest_path(
report_state: ReportState, bad_path: str
) -> None:
result = await _do_create_dependency(
title="CVE-2024-0001 in sample 1.0.0",
description="Published advisory affects the pinned version.",
target="repo/package.json",
cve="CVE-2024-0001",
package_name="sample",
installed_version="1.0.0",
impact="Low-impact dependency advisory.",
remediation_steps="Upgrade to 1.0.1.",
assumptions="Assumes the package is included in deployed builds.",
package_ecosystem="npm",
manifest_path=bad_path,
fixed_version="1.0.1",
cwe=None,
advisory_cvss=5.0,
technical_analysis=None,
fix_effort="low",
)
assert result["success"] is False
assert any("manifest_path" in error for error in result["errors"])
assert not report_state.vulnerability_reports
def test_dedupe_comparison_preserves_cve_identity() -> None:
cleaned = _prepare_report_for_comparison(
{
"title": "CVE-2021-23337 in lodash",
"description": "Pinned vulnerable dependency.",
"target": "repo/package.json",
"cve": "CVE-2021-23337",
"dependency_metadata": {"package_name": "lodash"},
}
)
assert cleaned["cve"] == "CVE-2021-23337"
assert cleaned["dependency_metadata"] == {"package_name": "lodash"}
async def test_dependency_dedupe_uses_cve_package_identity() -> None:
existing = [
{
"id": "vuln-0001",
"title": "CVE-2024-0001 in other",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "other",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
},
}
]
candidate = {
"title": "CVE-2024-0001 in sample",
"description": "Similar advisory prose.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
},
}
result = await check_duplicate(candidate, existing)
assert result["is_duplicate"] is False
assert result["confidence"] == 1.0
async def test_dependency_dedupe_rejects_same_cve_package_identity() -> None:
existing = [
{
"id": "vuln-0001",
"title": "CVE-2024-0001 in sample",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
},
}
]
candidate = {
"title": "CVE-2024-0001 in sample with different prose",
"description": "Different prose for the same dependency identity.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.1",
"package_ecosystem": "npm",
},
}
result = await check_duplicate(candidate, existing)
assert result["is_duplicate"] is True
assert result["duplicate_id"] == "vuln-0001"
assert result["confidence"] == 1.0
async def test_dependency_dedupe_keeps_findings_from_distinct_manifests() -> None:
existing = [
{
"id": "vuln-0001",
"title": "CVE-2024-0001 in sample",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
"manifest_path": "services/api/package-lock.json",
},
}
]
candidate = {
"title": "CVE-2024-0001 in sample (web)",
"description": "Same advisory observed in a second workspace.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
"manifest_path": "services/web/package-lock.json",
},
}
result = await check_duplicate(candidate, existing)
assert result["is_duplicate"] is False
assert result["confidence"] == 1.0
async def test_dependency_dedupe_rejects_same_manifest_identity() -> None:
existing = [
{
"id": "vuln-0001",
"title": "CVE-2024-0001 in sample",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
"manifest_path": "services/api/package-lock.json",
},
}
]
candidate = {
"title": "CVE-2024-0001 in sample re-reported",
"description": "Same advisory, same manifest.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
"manifest_path": "services/api/package-lock.json",
},
}
result = await check_duplicate(candidate, existing)
assert result["is_duplicate"] is True
assert result["duplicate_id"] == "vuln-0001"
async def test_dependency_dedupe_detects_legacy_same_cve_package() -> None:
existing = [
{
"id": "vuln-0001",
"title": "CVE-2024-0001 in npm sample package",
"description": "Legacy dependency finding without structured metadata.",
"cve": "CVE-2024-0001",
}
]
candidate = {
"title": "CVE-2024-0001 in sample",
"description": "Different prose for the same dependency identity.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.1",
"package_ecosystem": "npm",
},
}
result = await check_duplicate(candidate, existing)
assert result["is_duplicate"] is True
assert result["duplicate_id"] == "vuln-0001"
assert result["confidence"] == 1.0
def test_dependency_dedupe_defers_unclear_legacy_same_cve() -> None:
existing = [
{
"id": "vuln-0001",
"title": "CVE-2024-0001 dependency finding",
"description": "Legacy dependency finding without package identity.",
"cve": "CVE-2024-0001",
}
]
candidate = {
"title": "CVE-2024-0001 in sample",
"description": "Candidate dependency finding.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.1",
"package_ecosystem": "npm",
},
}
assert _check_dependency_duplicate(candidate, existing) is None
def test_dependency_dedupe_defers_legacy_package_substring_match() -> None:
existing = [
{
"id": "vuln-0001",
"title": "CVE-2024-0001 in sample-package",
"description": "Legacy dependency finding for a different package.",
"cve": "CVE-2024-0001",
}
]
candidate = {
"title": "CVE-2024-0001 in sample",
"description": "Candidate dependency finding.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.1",
"package_ecosystem": "npm",
},
}
assert _check_dependency_duplicate(candidate, existing) is None
def test_dependency_dedupe_defers_legacy_ecosystem_mismatch() -> None:
existing = [
{
"id": "vuln-0001",
"title": "CVE-2024-0001 in npm sample",
"description": "Legacy dependency finding for a different ecosystem.",
"cve": "CVE-2024-0001",
}
]
candidate = {
"title": "CVE-2024-0001 in sample",
"description": "Candidate dependency finding.",
"target": "repo/requirements.txt",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.1",
"package_ecosystem": "pypi",
},
}
assert _check_dependency_duplicate(candidate, existing) is None
def test_dependency_dedupe_matches_structured_missing_ecosystem() -> None:
existing = [
{
"id": "vuln-0001",
"title": "CVE-2024-0001 in sample",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
},
}
]
candidate = {
"title": "CVE-2024-0001 in sample",
"description": "Candidate dependency finding.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.1",
"package_ecosystem": "npm",
},
}
result = _check_dependency_duplicate(candidate, existing)
assert result is not None
assert result["is_duplicate"] is True
assert result["duplicate_id"] == "vuln-0001"
def test_tool_descriptions_include_formatting_guidance() -> None:
vuln_desc = create_vulnerability_report.description
assert "markdown" in vuln_desc.lower()
assert "fenced code" in vuln_desc.lower()
finish_desc = finish_scan.description
assert "markdown" in finish_desc.lower()
assert "# Executive Summary" in finish_desc
dep_desc = create_dependency_report.description
assert "cve" in dep_desc.lower()
assert "reachab" in dep_desc.lower()
def test_vuln_tool_exposes_new_params() -> None:
props = create_vulnerability_report.params_json_schema["properties"]
for field in ("evidence", "assumptions", "fix_effort", "fix_pr_body"):
assert field in props
dep_props = create_dependency_report.params_json_schema["properties"]
for field in ("package_name", "installed_version", "cve", "advisory_cvss"):
assert field in dep_props
for field in ("reachability", "reachability_evidence", "manifest_path"):
assert field in dep_props
dep_required = create_dependency_report.params_json_schema["required"]
assert "package_ecosystem" in dep_required
assert "advisory_cvss" in dep_required
_FIX_LOCATION = {
"file": "app/views.py",
"start_line": 10,
"end_line": 12,
"fix_before": 'query = f"SELECT * FROM t WHERE id={uid}"',
"fix_after": 'query = "SELECT * FROM t WHERE id=%s"',
}
_INFO_LOCATION = {
"file": "app/views.py",
"start_line": 10,
"end_line": 12,
"snippet": 'query = f"SELECT * FROM t WHERE id={uid}"',
}
async def test_fix_after_requires_verification(report_state: ReportState) -> None:
result = await _create_with(report_state, code_locations=[_FIX_LOCATION])
assert result["success"] is False
assert any("fix_verification" in e for e in result["errors"])
assert not report_state.vulnerability_reports
async def test_fix_after_with_verification_persists(report_state: ReportState) -> None:
verification = (
"Re-ran the PoC against the patched handler: the payload is now bound as a "
"parameter and returns no extra rows. Checked the two sibling call sites of "
"the same helper and the admin export path; both already parameterized. "
"Legitimate numeric ids still resolve and the 404 path is unchanged. "
"Ran the focused view tests and ruff."
)
result = await _create_with(
report_state,
code_locations=[_FIX_LOCATION],
fix_verification=verification,
)
assert result["success"] is True
assert report_state.vulnerability_reports[0]["fix_verification"] == verification
async def test_informational_location_needs_no_verification(report_state: ReportState) -> None:
result = await _create_with(report_state, code_locations=[_INFO_LOCATION])
assert result["success"] is True
assert "fix_verification" not in report_state.vulnerability_reports[0]
def test_vuln_tool_exposes_fix_verification() -> None:
assert "fix_verification" in create_vulnerability_report.params_json_schema["properties"]