c753797019
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.
54 lines
2.4 KiB
Python
54 lines
2.4 KiB
Python
"""SQLAlchemy model for operator-proposed test templates, pending lead review.
|
|
|
|
Leads create templates directly (see TestTemplateCreate router). An
|
|
operator (red_tech/blue_tech) gets the same "create template" action, but
|
|
their submission lands here instead of the live catalog — a lead on their
|
|
team reviews it, optionally edits any field, and either approves it (which
|
|
creates the real TestTemplate) or discards it.
|
|
"""
|
|
|
|
import uuid
|
|
|
|
from sqlalchemy import Column, DateTime, ForeignKey, String, Text, func
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
from sqlalchemy.orm import relationship
|
|
|
|
from app.database import Base
|
|
|
|
|
|
class TemplateSuggestion(Base):
|
|
"""A proposed new TestTemplate submitted by an operator, pending lead review."""
|
|
|
|
__tablename__ = "template_suggestions"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
# Proposed TestTemplate fields — mirrors TestTemplateCreate.
|
|
mitre_technique_id = Column(String, nullable=False)
|
|
name = Column(String, nullable=False)
|
|
description = Column(Text, nullable=True)
|
|
source = Column(String, nullable=False, default="custom", server_default="custom")
|
|
source_url = Column(String, nullable=True)
|
|
attack_procedure = Column(Text, nullable=True)
|
|
expected_detection = Column(Text, nullable=True)
|
|
platform = Column(String, nullable=True)
|
|
tool_suggested = Column(String, nullable=True)
|
|
severity = Column(String, nullable=True)
|
|
atomic_test_id = Column(String, nullable=True)
|
|
suggested_remediation = Column(Text, nullable=True)
|
|
|
|
# "red" or "blue" — derived from the submitter's role, determines which
|
|
# lead reviews it (admins can review both).
|
|
team = Column(String(10), nullable=False)
|
|
submitted_by = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=True)
|
|
|
|
status = Column(String(10), nullable=False, default="pending", server_default="pending") # pending/approved/rejected
|
|
reviewed_by = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=True)
|
|
reviewed_at = Column(DateTime, nullable=True)
|
|
created_template_id = Column(UUID(as_uuid=True), ForeignKey("test_templates.id"), nullable=True)
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
submitter = relationship("User", foreign_keys=[submitted_by])
|
|
reviewer = relationship("User", foreign_keys=[reviewed_by])
|
|
created_template = relationship("TestTemplate", foreign_keys=[created_template_id])
|