"""ThreatActor and ThreatActorTechnique models. Stores profiles of APT groups and their associated MITRE ATT&CK techniques, imported from MITRE CTI (STIX 2.0). """ import uuid from sqlalchemy import ( Column, String, Text, Boolean, DateTime, ForeignKey, Index, UniqueConstraint, func, ) from sqlalchemy.dialects.postgresql import UUID, JSONB from sqlalchemy.orm import relationship from app.database import Base class ThreatActor(Base): """ Threat actor / APT group profile. Imported from MITRE CTI ``intrusion-set`` STIX objects. """ __tablename__ = "threat_actors" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) mitre_id = Column(String, unique=True, nullable=True) # e.g. "G0016" (APT29) name = Column(String, nullable=False) aliases = Column(JSONB, nullable=True, default=[]) # ["Cozy Bear", "The Dukes", ...] description = Column(Text, nullable=True) country = Column(String, nullable=True) target_sectors = Column(JSONB, nullable=True, default=[]) # ["government", "defense", ...] target_regions = Column(JSONB, nullable=True, default=[]) # ["north-america", "europe", ...] motivation = Column(String, nullable=True) # espionage / financial / destruction / ... sophistication = Column(String, nullable=True) # low / medium / high / advanced first_seen = Column(String, nullable=True) last_seen = Column(String, nullable=True) references = Column(JSONB, nullable=True, default=[]) # [{"url": "...", "description": "..."}] mitre_url = Column(String, nullable=True) is_active = Column(Boolean, default=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) # Relationships techniques = relationship( "ThreatActorTechnique", back_populates="threat_actor", cascade="all, delete-orphan", ) __table_args__ = ( Index('ix_threat_actors_country', 'country'), Index('ix_threat_actors_motivation', 'motivation'), ) class ThreatActorTechnique(Base): """ Association between a threat actor and a MITRE ATT&CK technique. Stores additional context about how the actor uses the technique (from the STIX ``relationship`` ``uses`` objects). """ __tablename__ = "threat_actor_techniques" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) threat_actor_id = Column( UUID(as_uuid=True), ForeignKey("threat_actors.id", ondelete="CASCADE"), nullable=False, ) technique_id = Column( UUID(as_uuid=True), ForeignKey("techniques.id", ondelete="CASCADE"), nullable=False, ) usage_description = Column(Text, nullable=True) first_seen_using = Column(String, nullable=True) # Relationships threat_actor = relationship("ThreatActor", back_populates="techniques") technique = relationship("Technique") __table_args__ = ( Index('ix_threat_actor_techniques_actor', 'threat_actor_id'), Index('ix_threat_actor_techniques_technique', 'technique_id'), UniqueConstraint( 'threat_actor_id', 'technique_id', name='uq_actor_technique', ), )