- New model: ProspectSecurityAudit with score, grade, findings_json, severity counts, has_https, has_valid_ssl, missing_headers, exposed files, technologies, scan_error - Add last_security_audit_at timestamp to Prospect model - Add security_audit 1:1 relationship on Prospect Part of Phase 1: Security Audit in Enrichment Pipeline. Service, constants, migration, endpoints, and frontend to follow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
60 lines
2.3 KiB
Python
60 lines
2.3 KiB
Python
# 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")
|