# app/modules/prospecting/models/security_audit.py """ Security audit results for a prospect's website. Stores findings from passive security checks (HTTPS, headers, exposed files, cookies, server info, technology detection). Follows the same 1:1 pattern as ProspectTechProfile and ProspectPerformanceProfile. """ from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text from sqlalchemy.orm import relationship from app.core.database import Base from models.database.base import TimestampMixin class ProspectSecurityAudit(Base, TimestampMixin): """Security audit results for a prospect's website.""" __tablename__ = "prospect_security_audits" id = Column(Integer, primary_key=True, index=True) prospect_id = Column( Integer, ForeignKey("prospects.id", ondelete="CASCADE"), nullable=False, unique=True, ) # Overall score and grade score = Column(Integer, nullable=False, default=0) # 0-100 grade = Column(String(2), nullable=False, default="F") # A+, A, B, C, D, F # Detected language for bilingual reports detected_language = Column(String(5), nullable=True, default="en") # Findings stored as JSON (variable structure per check) findings_json = Column(Text, nullable=True) # JSON list of finding dicts # Denormalized severity counts (for dashboard queries without JSON parsing) findings_count_critical = Column(Integer, nullable=False, default=0) findings_count_high = Column(Integer, nullable=False, default=0) findings_count_medium = Column(Integer, nullable=False, default=0) findings_count_low = Column(Integer, nullable=False, default=0) findings_count_info = Column(Integer, nullable=False, default=0) # Key results (denormalized for quick access) has_https = Column(Boolean, nullable=True) has_valid_ssl = Column(Boolean, nullable=True) ssl_expires_at = Column(DateTime, nullable=True) missing_headers_json = Column(Text, nullable=True) # JSON list of header names exposed_files_json = Column(Text, nullable=True) # JSON list of exposed paths technologies_json = Column(Text, nullable=True) # JSON list of detected techs # Scan metadata scan_error = Column(Text, nullable=True) # Relationships prospect = relationship("Prospect", back_populates="security_audit")