larxius's picture
Update backend_structured/models.py
ee7b305 verified
Raw
History Blame Contribute Delete
18.6 kB
import sys
import os
sys.path.insert(0, os.path.abspath('backend'))
from bs4 import BeautifulSoup
from celery import Celery
from celery.schedules import crontab
from collections import defaultdict
from scanners.base_scanner import (
active_scan_logs, add_log, get_scan_logs, parse_domain,
cleanup_scan_logs, schedule_log_cleanup, emit_scan_progress
)
from scanners import get_pipeline, get_phases, build_scanner, apply_scan_options
try:
from backend.utils.fuzzer_engine import ContextAwareFuzzer
except ImportError:
from utils.fuzzer_engine import ContextAwareFuzzer
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from datetime import datetime, timezone
from datetime import datetime, timezone, timedelta
from datetime import datetime, timezone, timezone
from dotenv import load_dotenv
load_dotenv()
import stripe
from flask import Blueprint, request, jsonify, current_app, send_from_directory
from werkzeug.utils import secure_filename
from flask import Blueprint, send_file, jsonify, request
from flask import Flask
from flask import jsonify
from flask import render_template
from flask import request, abort, g, Response, make_response
from flask_cors import CORS
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from flask_socketio import SocketIO, emit, join_room, leave_room
from flask_sqlalchemy import SQLAlchemy
from functools import wraps
from markupsafe import escape # always available with Flask
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.pdfgen import canvas
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, Image, Flowable
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.charts.barcharts import VerticalBarChart
from sqlalchemy import event
from sqlalchemy import func
from sqlalchemy import inspect, text
from sqlalchemy import text
from sqlalchemy.engine import Engine
from typing import Any
from typing import Any, Callable
from typing import Callable
from typing import Literal
from urllib.parse import urljoin, urlparse
from urllib.parse import urlparse
import base64
import bcrypt
import concurrent.futures
from backend.utils.email_service import (
send_welcome_email,
send_scan_started,
send_scan_completed,
send_scan_failed,
send_critical_alert
)
import hashlib
import html
import io
import itertools
import json
import jwt
import math
import os
import re
import re, time, ipaddress, os, hashlib, threading
import requests
import socket
import sqlite3
import ssl
import statistics
import threading
import time
import traceback
import urllib.error
import urllib.parse
import urllib.request
import urllib3
import uuid
import ipaddress
from .extensions import db, celery, socketio, limiter
# --- From models.py ---
class Organization(db.Model):
__tablename__ = 'organizations'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
name = db.Column(db.String(150), nullable=False)
subscription_tier = db.Column(db.String(50), nullable=False, default='free')
status = db.Column(db.String(50), default='active')
api_key = db.Column(db.String(100), unique=True, nullable=True)
webhook_url = db.Column(db.String(500), nullable=True)
report_logo_url = db.Column(db.String(500), nullable=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
users = db.relationship('User', backref='organization', lazy=True)
scans = db.relationship('Scan', backref='organization', lazy=True, cascade="all, delete-orphan")
class Role(db.Model):
__tablename__ = 'roles'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
name = db.Column(db.String(50), unique=True, nullable=False)
class User(db.Model):
__tablename__ = 'users'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(128), nullable=False)
first_name = db.Column(db.String(100), nullable=True)
last_name = db.Column(db.String(100), nullable=True)
role = db.Column(db.String(50), nullable=False, default='org_admin') # super_admin, support_engineer, org_admin, soc_analyst, executive, read_only
org_id = db.Column(db.String(36), db.ForeignKey('organizations.id'), nullable=True)
failed_login_attempts = db.Column(db.Integer, default=0)
locked_until = db.Column(db.DateTime, nullable=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
# Policy Consent & Security Fields
terms_accepted_at = db.Column(db.DateTime, nullable=True)
privacy_policy_agreed_at = db.Column(db.DateTime, nullable=True)
policy_version_agreed = db.Column(db.String(20), default='v1.0')
mfa_enabled = db.Column(db.Boolean, default=False)
mfa_secret = db.Column(db.String(100), nullable=True)
reset_token = db.Column(db.String(255), nullable=True)
reset_token_expires = db.Column(db.DateTime, nullable=True)
email_verified = db.Column(db.Boolean, default=False)
scans = db.relationship('Scan', backref='user', lazy=True, cascade="all, delete-orphan")
alert_settings = db.relationship('AlertSettings', backref='user', uselist=False, lazy=True, cascade="all, delete-orphan")
@property
def subscription_tier(self):
if self.organization:
return self.organization.subscription_tier
return 'free'
@subscription_tier.setter
def subscription_tier(self, value):
if self.organization:
self.organization.subscription_tier = value
def set_password(self, password):
salt = bcrypt.gensalt()
self.password_hash = bcrypt.hashpw(password.encode('utf-8'), salt).decode('utf-8')
def check_password(self, password):
if not self.password_hash or not password:
return False
try:
return bcrypt.checkpw(password.encode('utf-8'), self.password_hash.encode('utf-8'))
except (ValueError, TypeError, AttributeError):
return False
class SubscriptionTier(db.Model):
__tablename__ = 'subscription_tiers'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(50), primary_key=True)
name = db.Column(db.String(100), nullable=False)
monthly_price = db.Column(db.Integer, nullable=False, default=0)
yearly_price = db.Column(db.Integer, nullable=False, default=0)
class AuditLog(db.Model):
__tablename__ = 'audit_logs'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
admin_id = db.Column(db.String(36), nullable=False)
action = db.Column(db.String(255), nullable=False)
target_id = db.Column(db.String(100), nullable=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
class Payment(db.Model):
__tablename__ = 'payments'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
org_id = db.Column(db.String(36), db.ForeignKey('organizations.id', ondelete='CASCADE'), nullable=True)
user_id = db.Column(db.String(36), db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False)
razorpay_payment_id = db.Column(db.String(100), unique=True, nullable=True)
razorpay_order_id = db.Column(db.String(100), nullable=True)
stripe_session_id = db.Column(db.String(100), unique=True, nullable=True)
stripe_payment_id = db.Column(db.String(100), unique=True, nullable=True)
tier_id = db.Column(db.String(50), nullable=False)
amount = db.Column(db.Integer, nullable=False)
currency = db.Column(db.String(10), default='USD')
status = db.Column(db.String(50), default='successful')
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))
class Scan(db.Model):
__tablename__ = 'scans'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
org_id = db.Column(db.String(36), db.ForeignKey('organizations.id'), nullable=False)
user_id = db.Column(db.String(36), db.ForeignKey('users.id'), nullable=False)
target_url = db.Column(db.String(500), nullable=False)
scan_type = db.Column(db.String(50), nullable=False, default='Full') # Full, Port, SSL, OWASP
status = db.Column(db.String(50), nullable=False, default='queued') # queued, scanning, completed, failed
security_score = db.Column(db.Integer, nullable=True) # 0 to 100
started_at = db.Column(db.DateTime, default=datetime.utcnow)
completed_at = db.Column(db.DateTime, nullable=True)
auth_headers = db.Column(db.JSON, nullable=True) # For authenticated scanning
scan_options = db.Column(db.JSON, nullable=True) # crawl_depth, exclude_paths, enable_red_team
ssl_info = db.Column(db.JSON, nullable=True) # Cached SSL certificate info
duration_seconds = db.Column(db.Integer, nullable=True)
error_message = db.Column(db.Text, nullable=True)
critical_count = db.Column(db.Integer, default=0)
high_count = db.Column(db.Integer, default=0)
medium_count = db.Column(db.Integer, default=0)
low_count = db.Column(db.Integer, default=0)
vulnerabilities = db.relationship('Vulnerability', backref='scan', lazy=True, cascade="all, delete-orphan")
def __init__(self, org_id, user_id, target_url, scan_type='Full', status='queued', security_score=None, started_at=None, completed_at=None, auth_headers=None, scan_options=None, id=None):
if id: self.id = id
self.org_id = org_id
self.user_id = user_id
self.target_url = target_url
self.scan_type = scan_type
self.status = status
self.security_score = security_score
self.auth_headers = auth_headers
self.scan_options = scan_options
if started_at: self.started_at = started_at
if completed_at: self.completed_at = completed_at
class Vulnerability(db.Model):
__tablename__ = 'vulnerabilities'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
scan_id = db.Column(db.String(36), db.ForeignKey('scans.id'), nullable=False)
title = db.Column(db.String(200), nullable=False)
severity = db.Column(db.String(50), nullable=False) # Critical, High, Medium, Low
category = db.Column(db.String(100), nullable=False) # SSL/TLS, Port, Security Headers, Injection
description = db.Column(db.Text, nullable=False)
remediation = db.Column(db.Text, nullable=False)
cvss_score = db.Column(db.Float, nullable=False)
detected_at = db.Column(db.DateTime, default=datetime.utcnow)
evidence = db.Column(db.Text, default="")
payload = db.Column(db.Text, default="")
request_details = db.Column(db.Text, default="")
response_details = db.Column(db.Text, default="")
is_false_positive = db.Column(db.Boolean, default=False)
cwe_ids = db.Column(db.JSON, nullable=True)
owasp_category = db.Column(db.String(100), nullable=True)
exploit_poc = db.Column(db.JSON, nullable=True)
remediation_code = db.Column(db.Text, nullable=True)
status = db.Column(db.String(50), nullable=False, default='open') # open, in_progress, resolved, ignored
remediated_at = db.Column(db.DateTime, nullable=True)
remediated_by = db.Column(db.String(36), nullable=True)
def __init__(self, scan_id, title, severity, category, description, remediation, cvss_score, detected_at=None, id=None, evidence="", payload="", request_details="", response_details="", cwe_ids=None, owasp_category=None, exploit_poc=None, remediation_code=None):
if id: self.id = id
self.scan_id = scan_id
self.title = title
self.severity = severity
self.category = category
self.description = description
self.remediation = remediation
self.cvss_score = cvss_score
if detected_at: self.detected_at = detected_at
self.evidence = evidence
self.payload = payload
self.request_details = request_details
self.response_details = response_details
self.cwe_ids = cwe_ids
self.owasp_category = owasp_category
self.exploit_poc = exploit_poc
self.remediation_code = remediation_code
class DemoBooking(db.Model):
__tablename__ = 'demo_bookings'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
email = db.Column(db.String(255), nullable=False)
company_size = db.Column(db.String(100), nullable=False)
meeting_date = db.Column(db.String(100), nullable=False)
meeting_time = db.Column(db.String(50), nullable=False)
status = db.Column(db.String(50), nullable=False, default='pending')
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def __init__(self, email, company_size, meeting_date, meeting_time, status='pending', id=None):
if id: self.id = id
self.email = email
self.company_size = company_size
self.meeting_date = meeting_date
self.meeting_time = meeting_time
self.status = status
def to_dict(self):
return {
'id': self.id,
'email': self.email,
'company_size': self.company_size,
'meeting_date': self.meeting_date,
'meeting_time': self.meeting_time,
'status': self.status,
'created_at': self.created_at.isoformat() + 'Z' if self.created_at else None
}
class EmailLog(db.Model):
__tablename__ = 'email_logs'
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
recipient = db.Column(db.String(255), nullable=False)
subject = db.Column(db.String(255), nullable=False)
status = db.Column(db.String(50), nullable=False, default='sent')
error_message = db.Column(db.Text, nullable=True)
sent_at = db.Column(db.DateTime, default=datetime.utcnow)
def __init__(self, recipient, subject, status='sent', error_message=None, id=None):
if id: self.id = id
self.recipient = recipient
self.subject = subject
self.status = status
self.error_message = error_message
class ScheduledScan(db.Model):
__tablename__ = 'scheduled_scans'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
org_id = db.Column(db.String(36), db.ForeignKey('organizations.id'), nullable=False)
user_id = db.Column(db.String(36), db.ForeignKey('users.id'), nullable=False)
target_url = db.Column(db.String(500), nullable=False)
scan_type = db.Column(db.String(50), nullable=False, default='Full')
frequency = db.Column(db.String(50), nullable=False, default='daily') # daily, weekly, monthly, once
schedule_time = db.Column(db.String(5), nullable=True) # HH:MM format like "20:00"
day_of_week = db.Column(db.String(20), nullable=True) # monday, tuesday, etc.
day_of_month = db.Column(db.Integer, nullable=True) # 1..31
specific_date = db.Column(db.String(20), nullable=True) # YYYY-MM-DD
is_active = db.Column(db.Boolean, default=True)
auth_headers = db.Column(db.JSON, nullable=True) # For authenticated scanning
created_at = db.Column(db.DateTime, default=datetime.utcnow)
last_run_at = db.Column(db.DateTime, nullable=True)
def __init__(self, org_id, user_id, target_url, scan_type='Full', frequency='daily', schedule_time=None, day_of_week=None, day_of_month=None, specific_date=None, is_active=True, last_run_at=None, auth_headers=None, id=None):
if id: self.id = id
self.org_id = org_id
self.user_id = user_id
self.target_url = target_url
self.scan_type = scan_type
self.frequency = frequency
self.schedule_time = schedule_time
self.day_of_week = day_of_week
self.day_of_month = day_of_month
self.specific_date = specific_date
self.is_active = is_active
self.auth_headers = auth_headers
if last_run_at: self.last_run_at = last_run_at
class AlertSettings(db.Model):
__tablename__ = 'alert_settings'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
user_id = db.Column(db.String(36), db.ForeignKey('users.id'), nullable=False)
email_notifications = db.Column(db.Boolean, default=True)
webhook_url = db.Column(db.String(500), nullable=True)
severity_threshold = db.Column(db.String(50), default='Medium') # Low, Medium, High, Critical
class OrganizationScanQuota(db.Model):
__tablename__ = 'organization_scan_quotas'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
org_id = db.Column(db.String(36), db.ForeignKey('organizations.id'), nullable=False)
scan_type = db.Column(db.String(50), nullable=False)
allocated_count = db.Column(db.Integer, nullable=False, default=0)
used_count = db.Column(db.Integer, nullable=False, default=0)
__table_args__ = (db.UniqueConstraint('org_id', 'scan_type', name='uix_org_scan_type'),)
class Report(db.Model):
__tablename__ = 'reports'
def __init__(self, **kwargs): super().__init__(**kwargs)
id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
scan_id = db.Column(db.String(36), db.ForeignKey('scans.id', ondelete='CASCADE'), nullable=False)
org_id = db.Column(db.String(36), db.ForeignKey('organizations.id', ondelete='CASCADE'), nullable=False)
report_type = db.Column(db.String(50), nullable=False) # pdf, executive_summary, compliance
file_path = db.Column(db.String(500), nullable=True)
generated_at = db.Column(db.DateTime, default=datetime.utcnow)