feat: guarddog-nexus — webhook-based PyPI scanner with web UI
This commit is contained in:
49
guarddog_nexus/api/findings.py
Normal file
49
guarddog_nexus/api/findings.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""REST API for findings (across all scans)."""
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from guarddog_nexus.database import get_session
|
||||
from guarddog_nexus.models import Finding
|
||||
|
||||
router = APIRouter(prefix="/api/v1/findings", tags=["findings"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def list_findings(
|
||||
limit: int = Query(50, le=200),
|
||||
offset: int = Query(0, ge=0),
|
||||
rule: str | None = Query(None),
|
||||
severity: str | None = Query(None),
|
||||
scan_id: int | None = Query(None),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
):
|
||||
q = select(Finding)
|
||||
if rule:
|
||||
q = q.where(Finding.rule == rule)
|
||||
if severity:
|
||||
q = q.where(Finding.severity == severity)
|
||||
if scan_id:
|
||||
q = q.where(Finding.scan_id == scan_id)
|
||||
|
||||
total = await session.scalar(select(func.count()).select_from(q.subquery()))
|
||||
findings = (await session.execute(q.offset(offset).limit(limit))).scalars().all()
|
||||
|
||||
return {
|
||||
"total": total,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
"findings": [
|
||||
{
|
||||
"id": f.id,
|
||||
"scan_id": f.scan_id,
|
||||
"rule": f.rule,
|
||||
"severity": f.severity,
|
||||
"message": f.message,
|
||||
"location": f.location,
|
||||
"created_at": f.created_at.isoformat() if f.created_at else None,
|
||||
}
|
||||
for f in findings
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user