refactor(backend/api): extract ScreeningService (Step 4 — file 8 of 18)
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>
This commit is contained in:
62
backend-compliance/compliance/schemas/screening.py
Normal file
62
backend-compliance/compliance/schemas/screening.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
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",
|
||||
]
|
||||
Reference in New Issue
Block a user