feat(test-catalog): add template creation and lead-approval workflow
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
Leads get a Create Template button that adds directly to the catalog (existing POST /test-templates). Operators (red_tech/blue_tech) get the same button, but their proposal now lands in a new template_suggestions review queue instead — a lead on their team can approve it as-is, edit fields before approving, or discard it. Modeled on the existing ProcedureSuggestion review workflow.
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
"""Pydantic schemas for the TemplateSuggestion (operator template proposal) workflow."""
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class TemplateSuggestionCreate(BaseModel):
|
||||
"""Payload for an operator proposing a new test template."""
|
||||
|
||||
mitre_technique_id: str
|
||||
name: str
|
||||
description: str | None = None
|
||||
source: str = "custom"
|
||||
source_url: str | None = None
|
||||
attack_procedure: str | None = None
|
||||
expected_detection: str | None = None
|
||||
platform: str | None = None
|
||||
tool_suggested: str | None = None
|
||||
severity: str | None = None
|
||||
atomic_test_id: str | None = None
|
||||
suggested_remediation: str | None = None
|
||||
|
||||
|
||||
class TemplateSuggestionApprove(BaseModel):
|
||||
"""Optional field overrides a lead can apply while approving a suggestion.
|
||||
|
||||
Any field left unset falls back to what the operator originally
|
||||
submitted — the lead only needs to pass the fields they want to change.
|
||||
"""
|
||||
|
||||
mitre_technique_id: str | None = None
|
||||
name: str | None = None
|
||||
description: str | None = None
|
||||
source: str | None = None
|
||||
source_url: str | None = None
|
||||
attack_procedure: str | None = None
|
||||
expected_detection: str | None = None
|
||||
platform: str | None = None
|
||||
tool_suggested: str | None = None
|
||||
severity: str | None = None
|
||||
atomic_test_id: str | None = None
|
||||
suggested_remediation: str | None = None
|
||||
|
||||
|
||||
class TemplateSuggestionOut(BaseModel):
|
||||
"""Full representation of a pending/reviewed template suggestion."""
|
||||
|
||||
id: uuid.UUID
|
||||
mitre_technique_id: str
|
||||
name: str
|
||||
description: str | None = None
|
||||
source: str
|
||||
source_url: str | None = None
|
||||
attack_procedure: str | None = None
|
||||
expected_detection: str | None = None
|
||||
platform: str | None = None
|
||||
tool_suggested: str | None = None
|
||||
severity: str | None = None
|
||||
atomic_test_id: str | None = None
|
||||
suggested_remediation: str | None = None
|
||||
team: str
|
||||
submitted_by: uuid.UUID | None = None
|
||||
submitter_name: str | None = None
|
||||
status: str
|
||||
reviewed_by: uuid.UUID | None = None
|
||||
reviewed_at: datetime | None = None
|
||||
created_template_id: uuid.UUID | None = None
|
||||
created_at: datetime | None = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
Reference in New Issue
Block a user