139 lines
4.0 KiB
Python
139 lines
4.0 KiB
Python
"""OSINT enrichment endpoints — view, review, and trigger enrichment of
|
|
OSINT items (CVEs, advisories, etc.) linked to techniques.
|
|
"""
|
|
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, Depends, Query, HTTPException, status
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.database import get_db
|
|
from app.dependencies.auth import get_current_user, require_any_role
|
|
from app.models.user import User
|
|
from app.services.osint_enrichment_service import (
|
|
enrich_technique_with_cves,
|
|
get_osint_items_for_technique,
|
|
get_osint_summary,
|
|
get_technique_or_raise,
|
|
list_osint_items as service_list_osint_items,
|
|
mark_osint_reviewed,
|
|
)
|
|
|
|
router = APIRouter(prefix="/osint", tags=["osint"])
|
|
|
|
|
|
# ── Schemas ──────────────────────────────────────────────────────────
|
|
|
|
|
|
class OsintItemOut(BaseModel):
|
|
id: str
|
|
technique_id: str
|
|
source_type: str
|
|
source_url: str
|
|
title: str
|
|
description: str | None
|
|
severity: str | None
|
|
discovered_at: str | None
|
|
reviewed: bool
|
|
metadata_: dict | None = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# ── Endpoints ────────────────────────────────────────────────────────
|
|
|
|
|
|
@router.get("/items")
|
|
def list_osint_items(
|
|
technique_id: UUID | None = Query(None),
|
|
source_type: str | None = Query(None),
|
|
reviewed: bool | None = Query(None),
|
|
offset: int = Query(0, ge=0),
|
|
limit: int = Query(50, ge=1, le=200),
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""List OSINT items with optional filters."""
|
|
return service_list_osint_items(
|
|
db,
|
|
technique_id=technique_id,
|
|
source_type=source_type,
|
|
reviewed=reviewed,
|
|
offset=offset,
|
|
limit=limit,
|
|
)
|
|
|
|
|
|
@router.get("/summary")
|
|
def osint_summary(
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""Summary statistics for OSINT items."""
|
|
return get_osint_summary(db)
|
|
|
|
|
|
@router.post("/items/{item_id}/review")
|
|
def review_osint_item(
|
|
item_id: UUID,
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""Mark an OSINT item as reviewed."""
|
|
item = mark_osint_reviewed(db, str(item_id))
|
|
if not item:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="OSINT item not found",
|
|
)
|
|
return {"id": str(item.id), "reviewed": True}
|
|
|
|
|
|
@router.post("/enrich/{technique_id}")
|
|
def trigger_technique_enrichment(
|
|
technique_id: UUID,
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(require_any_role("red_lead", "blue_lead")),
|
|
):
|
|
"""Manually trigger OSINT enrichment for a single technique."""
|
|
technique = get_technique_or_raise(db, technique_id)
|
|
count = enrich_technique_with_cves(db, technique)
|
|
return {
|
|
"technique_id": str(technique.id),
|
|
"mitre_id": technique.mitre_id,
|
|
"new_items": count,
|
|
}
|
|
|
|
|
|
@router.get("/technique/{technique_id}")
|
|
def get_technique_osint(
|
|
technique_id: UUID,
|
|
source_type: str | None = Query(None),
|
|
reviewed: bool | None = Query(None),
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""Get all OSINT items for a specific technique."""
|
|
items = get_osint_items_for_technique(
|
|
db,
|
|
str(technique_id),
|
|
source_type=source_type,
|
|
reviewed=reviewed,
|
|
)
|
|
return [
|
|
{
|
|
"id": str(item.id),
|
|
"source_type": item.source_type,
|
|
"source_url": item.source_url,
|
|
"title": item.title,
|
|
"description": item.description,
|
|
"severity": item.severity,
|
|
"discovered_at": item.discovered_at.isoformat() if item.discovered_at else None,
|
|
"reviewed": item.reviewed,
|
|
"metadata": item.metadata_,
|
|
}
|
|
for item in items
|
|
]
|