feat(intel): major intel scan improvements + Review Queue integration
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
Backend:
- intel_service: remove 50-technique limit (scan all techniques), improve
pattern matching with word boundaries (\bT1059\b), raise min name length
to 8 chars to reduce false positives, skip entries with empty titles
- technique_query_service: add intel_items to get_technique_detail() so
the technique page now shows recent threat intel articles (last 20)
- New GET /intel/items endpoint with optional technique_id filter
Frontend:
- New api/intel.ts with listIntelItems()
- ReviewQueuePage: complete redesign
* Expandable rows — click a technique to see its intel articles inline
* IntelPanel component fetches articles per technique on expand
* 'Create Template from Intel' button opens pre-filled modal:
name (from article title), source_url (article link), technique_id
User reads the article and fills the attack procedure
* Updated explanation text: lists all 3 reasons a technique can be flagged
(MITRE update / intel scan / new template or detection rule)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
54
backend/app/routers/intel.py
Normal file
54
backend/app/routers/intel.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""Intel items endpoints — list and manage threat intelligence items."""
|
||||
|
||||
import uuid
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.database import get_db
|
||||
from app.dependencies.auth import get_current_user
|
||||
from app.models.intel import IntelItem
|
||||
from app.models.user import User
|
||||
|
||||
router = APIRouter(prefix="/intel", tags=["intel"])
|
||||
|
||||
|
||||
class IntelItemOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
technique_id: Optional[uuid.UUID] = None
|
||||
url: str
|
||||
title: Optional[str] = None
|
||||
source: Optional[str] = None
|
||||
detected_at: Optional[str] = None
|
||||
reviewed: bool
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
@router.get("/items", response_model=list[IntelItemOut])
|
||||
def list_intel_items(
|
||||
technique_id: Optional[uuid.UUID] = Query(None, description="Filter by technique"),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""List threat intelligence items, optionally filtered by technique."""
|
||||
query = db.query(IntelItem).order_by(IntelItem.detected_at.desc())
|
||||
if technique_id:
|
||||
query = query.filter(IntelItem.technique_id == technique_id)
|
||||
items = query.limit(limit).all()
|
||||
return [
|
||||
IntelItemOut(
|
||||
id=item.id,
|
||||
technique_id=item.technique_id,
|
||||
url=item.url,
|
||||
title=item.title,
|
||||
source=item.source,
|
||||
detected_at=item.detected_at.isoformat() if item.detected_at else None,
|
||||
reviewed=item.reviewed,
|
||||
)
|
||||
for item in items
|
||||
]
|
||||
Reference in New Issue
Block a user