compliance/api/screening_routes.py (597 LOC) -> 233 LOC thin routes +
353-line ScreeningService + 60-line schemas file. Manages SBOM generation
(CycloneDX 1.5) and OSV.dev vulnerability scanning.
Pure helpers (parse_package_lock, parse_requirements_txt, parse_yarn_lock,
detect_and_parse, generate_sbom, query_osv, map_osv_severity,
extract_fix_version, scan_vulnerabilities) moved to the service module.
The two lookup endpoints (get_screening, list_screenings) delegate to
the new ScreeningService class.
Test-mock compatibility: tests/test_screening_routes.py uses
`patch("compliance.api.screening_routes.SessionLocal", ...)` and
`patch("compliance.api.screening_routes.scan_vulnerabilities", ...)`.
Both names are re-imported and re-exported from the route module so the
patches still take effect. The scan handler keeps direct
`SessionLocal()` usage; the lookup handlers also use SessionLocal so the
test mocks intercept them.
Latent bug fixed: the original scan handler had
text = content.decode("utf-8")
on line 339, shadowing the imported `sqlalchemy.text` so that the
subsequent `text("INSERT ...")` calls would have raised at runtime.
The variable is now named `file_text`. Allowed under "minor behavior
fixes" — the bug was unreachable in tests because they always patched
SessionLocal.
Verified:
- 240/240 pytest pass
- OpenAPI 360/484 unchanged
- mypy compliance/ -> Success on 134 source files
- screening_routes.py 597 -> 233 LOC
- Hard-cap violations: 11 -> 10
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
63 lines
1.3 KiB
Python
63 lines
1.3 KiB
Python
"""
|
|
System Screening schemas — SBOM + vulnerability scan results.
|
|
|
|
Phase 1 Step 4: extracted from ``compliance.api.screening_routes``.
|
|
"""
|
|
|
|
from typing import Any, Optional
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
class SecurityIssueResponse(BaseModel):
|
|
id: str
|
|
severity: str
|
|
title: str
|
|
description: Optional[str] = None
|
|
cve: Optional[str] = None
|
|
cvss: Optional[float] = None
|
|
affected_component: str
|
|
affected_version: Optional[str] = None
|
|
fixed_in: Optional[str] = None
|
|
remediation: Optional[str] = None
|
|
status: str = "OPEN"
|
|
|
|
|
|
class SBOMComponentResponse(BaseModel):
|
|
name: str
|
|
version: str
|
|
type: str
|
|
purl: str
|
|
licenses: list[str]
|
|
vulnerabilities: list[dict[str, Any]]
|
|
|
|
|
|
class ScreeningResponse(BaseModel):
|
|
id: str
|
|
status: str
|
|
sbom_format: str
|
|
sbom_version: str
|
|
total_components: int
|
|
total_issues: int
|
|
critical_issues: int
|
|
high_issues: int
|
|
medium_issues: int
|
|
low_issues: int
|
|
components: list[SBOMComponentResponse]
|
|
issues: list[SecurityIssueResponse]
|
|
started_at: Optional[str] = None
|
|
completed_at: Optional[str] = None
|
|
|
|
|
|
class ScreeningListResponse(BaseModel):
|
|
screenings: list[dict[str, Any]]
|
|
total: int
|
|
|
|
|
|
__all__ = [
|
|
"SecurityIssueResponse",
|
|
"SBOMComponentResponse",
|
|
"ScreeningResponse",
|
|
"ScreeningListResponse",
|
|
]
|