Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| 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") | |
| def subscription_tier(self): | |
| if self.organization: | |
| return self.organization.subscription_tier | |
| return 'free' | |
| 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) | |