Skip to content

Latest commit

 

History

History
450 lines (373 loc) · 16.9 KB

File metadata and controls

450 lines (373 loc) · 16.9 KB

AIris Security — System Architecture

Version 2.1.0 | Last Updated: March 2026


📐 System Overview

AIris Security is a full-stack vulnerability assessment platform built around four scanner integrations, ML-assisted risk analysis, and a ReportLab-based PDF generator. The design follows a service-oriented pattern: a Next.js frontend communicates with the FastAPI backend via REST; scan execution and ML analysis run in backend background flow.


🏗️ High-Level Architecture

┌────────────────────────────────────────────────────────────────────┐
│                      USER INTERFACE LAYER                          │
│                       (Next.js 16.1.6)                             │
│  ┌─────────────┐  ┌──────────────┐  ┌──────────────────────────┐  │
│  │  Auth pages │  │  Scan page   │  │  Results / Report pages  │  │
│  │  login /    │  │  target +    │  │  findings · ML analysis  │  │
│  │  register   │  │  live logs   │  │  charts · PDF download   │  │
│  └─────────────┘  └──────────────┘  └──────────────────────────┘  │
└──────────────────────────┬─────────────────────────────────────────┘
                           │ REST / Axios (polling every 3 s)
┌──────────────────────────▼─────────────────────────────────────────┐
│                     APPLICATION LAYER                              │
│                    (FastAPI 0.110.1 + Uvicorn)                     │
│  ┌──────────────┐  ┌──────────────────┐  ┌───────────────────┐    │
│  │  Auth API    │  │  Scan API        │  │  Report API       │    │
│  │  /api/auth/* │  │  /api/scan/*     │  │  /api/scan/       │    │
│  │  JWT, bcrypt │  │  start·status·   │  │  report/{id}/pdf  │    │
│  └──────────────┘  │  results·health  │  └───────────────────┘    │
│                    └──────────────────┘                            │
└──────┬─────────────────┬───────────────────┬────────────────────────┘
       │                 │                   │
┌──────▼──────┐  ┌───────▼────────┐  ┌───────▼──────────────────────┐
│  Database   │  │ Scanner Layer  │  │  ML Engine                   │
│  (MongoDB)  │  │  (subprocess + │  │  (scikit-learn + NLTK)       │
│  Motor      │  │   executor)    │  │                              │
│             │  │                │  │  ┌──────────────────────┐    │
│  • users    │  │  • Nmap        │  │  │ XGBoost              │    │
│  • scans    │  │  • Nikto       │  │  │ attack predictor     │    │
│  • reports  │  │  • SSLScan     │  │  ├──────────────────────┤    │
│             │  │  • DirSearch   │  │  │ NLP payload          │    │
└─────────────┘  └────────────────┘  │  │ classifier (TF-IDF)  │    │
                                     │  ├──────────────────────┤    │
                                     │  │ Hybrid risk scoring  │    │
                                     │  │ + scanner boosts     │    │
                                     │  └──────────────────────┘    │
                                     └──────────────────────────────┘

🔧 Component Architecture

1. Frontend Layer (Next.js 16)

Technology Stack

Library Version Role
Next.js 16.1.6 React framework + routing
React 18.2.0 UI rendering
TailwindCSS 3.4.19 Utility-first styling
Framer Motion 12.29.0 Animations
Axios 1.6.x HTTP client

Pages

pages/
├── index.js         Landing page with feature overview
├── login.js         JWT login form
├── register.js      Account creation
├── scan.js          Main scanning interface (target input, live logs, results)
├── history.js       Per-user scan history
├── results/
│   └── index.js     Redirects to report view
└── report/
    └── [id].js      Full results: ML analysis, findings table, PDF download

Components

components/
├── PredictionCard.jsx Active ML analysis card in report view
├── LogTerminal.jsx    Placeholder file (currently empty)
├── RiskMeter.jsx      Placeholder file (currently empty)
└── VulnTable.jsx      Placeholder file (currently empty)

Real-Time Status Polling

// Poll every 3 seconds until status === "completed"
const pollInterval = setInterval(async () => {
    const status = await getScanStatus(scanId);
    setScanProgress(status.progress);   // 0–100 %
    setScanLogs(status.logs);           // string[]
    if (status.status === "completed") {
        router.push(`/report/${scanId}`);
        clearInterval(pollInterval);
    }
}, 3000);

2. Backend Layer (FastAPI)

Technology Stack

Library Version Role
FastAPI 0.110.1 REST API framework
Uvicorn 0.25.0 ASGI server
Motor 3.3.1 Async MongoDB driver
PyJWT 2.10.1 JWT token handling
bcrypt 4.1.2 Password hashing
ReportLab 4.0.7 PDF generation
matplotlib 3.x Chart generation (embedded in PDF)
scikit-learn 1.7.2 ML inference

Application Structure

backend/
├── server.py                     FastAPI app + Windows event-loop policy
└── app/
    ├── core/
    │   ├── config.py             Environment config (pydantic Settings)
    │   └── security.py           JWT encode/decode, password hashing
    ├── db/
    │   └── mongodb.py            Motor client + collection helpers
    ├── models/
    │   ├── user.py               User document model
    │   ├── scan.py               Scan document model
    │   └── report.py             Report document model
    ├── schemas/                  Pydantic request/response schemas
    ├── api/
    │   ├── auth.py               POST /api/auth/register, /login; GET /api/auth/me
    │   └── report.py             GET  /api/report/{id}
    ├── routes/
    │   └── scan.py               POST /api/scan/start
    │                             GET  /api/scan/status/{id}
    │                             GET  /api/scan/results/{id}
    │                             GET  /api/scan/health
    │                             GET  /api/scan/scanners
    ├── scanners/
    │   ├── __init__.py           Auto-detection registry
    │   ├── base.py               Abstract BaseScanner
    │   ├── nmap_scanner.py       Nmap XML parser
    │   ├── nikto_scanner.py      Nikto text parser
    │   ├── sslscan_scanner.py    SSLScan TLS/cipher/cert parser
    │   └── dirsearch_scanner.py  DirSearch plain-format parser
    └── services/
        ├── scan_service.py       Scan lifecycle management
        ├── scanner_orchestrator.py  Legacy/simplified orchestrator
        ├── ml_service.py         Hybrid ML risk scoring
        ├── pdf_report.py         PDF builder (reportlab + matplotlib)
        ├── aggregator_service.py Finding aggregation
        ├── vulnerability_aggregator.py  Deduplication
        └── auth_service.py       User management

API Endpoints

Method Path Auth Description
POST /api/auth/register No Create user account
POST /api/auth/login No Return JWT token
GET /api/auth/me JWT Return authenticated user profile
GET /api/scan/health No System status + scanner list
GET /api/scan/scanners No Available scanner names
GET /api/scan/history JWT User scan history
POST /api/scan/start JWT Initiate comprehensive scan
GET /api/scan/status/{id} JWT Progress + live logs
GET /api/scan/results/{id} JWT Findings + ML analysis
GET /api/scan/report/{id}/pdf JWT Download PDF report
GET /api/report/{id} JWT Return stored report payload

3. Scanner Layer

Auto-Detection Registry

At startup app/scanners/__init__.py instantiates each scanner class and checks its is_available property. Only scanners whose binary/script is found are added to AVAILABLE_SCANNERS:

_ALL_SCANNER_CLASSES = {
    "nmap": NmapScanner,
    "nikto": NiktoScanner,
    "sslscan": SSLScanner,
    "dirsearch": DirSearchScanner,
}

AVAILABLE_SCANNERS: dict = {}
for _name, _cls in _ALL_SCANNER_CLASSES.items():
    try:
        _instance = _cls()
        if hasattr(_instance, "is_available") and not _instance.is_available:
            continue
        AVAILABLE_SCANNERS[_name] = _cls
    except RuntimeError as _e:
        print(f"[SCANNER] {_name} unavailable: {_e}")

Binary Discovery Order

Each scanner searches for its tool in this order:

  1. backend/security_tools/tool_paths.txt — explicit path overrides
  2. Common Windows install paths (e.g. C:\Program Files (x86)\Nmap\nmap.exe)
  3. System PATH (shutil.which)

Abstract Base Class

class BaseScannerInterface:
    async def scan(self, target: str) -> Dict[str, Any]: ...

Scanner Output Shapes

Nmap

{
    "tool": "nmap", "status": "completed", "target": "...",
    "findings": [...],   # standardised finding dicts
    "raw_output": "..."
}

Nikto

{
    "tool": "nikto", "status": "completed", "target": "...",
    "findings": [...],
    "raw_output": "..."
}

SSLScan

{
    "tool": "sslscan", "status": "completed", "target": "...",
    "tls_versions": ["TLSv1.0"],          # deprecated protocols found enabled
    "weak_ciphers": ["RC4-SHA"],          # weak cipher strings
    "certificate_info": {
        "subject": "...", "issuer": "...",
        "valid_from": "...", "valid_until": "...",
        "days_until_expiry": 42, "self_signed": False
    },
    "findings": [...],
    "raw_output": "..."
}

DirSearch

{
    "tool": "dirsearch", "status": "completed", "target": "...",
    "exposed_paths": ["[200] /admin/", "[403] /.git/"],
    "findings": [...],
    "summary": "DirSearch completed for ...",
    "raw_output": "..."
}

4. ML Engine

Pipeline

findings + scanner_results
        │
        ▼
ml_service.analyze_scan(findings, scanner_results)
        │
    ├── Build scan_input for ml/src/inference.py
    │      payload + scanner blocks (nmap, nikto, sslscan, dirsearch)
    │
    ├── predict_attack(scan_input)
    │      payload classifier + attack predictor + graph risk helper
    │
    ├── Return mapped backend shape
    │      risk_score, attack_type, confidence,
    │      payload_prediction, ml_powered
        │
    └── On failure: fallback output with neutral defaults

Model Files

File Location Description
payload_classifier.joblib ml/models/ TF-IDF + LogReg payload classifier
attack_predictor.joblib ml/models/ Multi-class attack predictor
attack_label_encoder.joblib ml/models/ Label encoder for attack types

Note: legacy backend RF/scaler placeholders exist in service code but active inference path uses ml/src/inference.py model artifacts.


5. PDF Report Generation

ReportLab handles layout; matplotlib generates charts which are embedded as in-memory PNG images — no temp files.

Report Sections

  1. Scan Info — target, scan type, timestamps, scanner list
  2. ML Analysis — risk gauge chart, expanded ML table (attack type, hybrid/RF/NLP scores, confidence), predicted attack vectors, CVE context, remediation callout box (green, keyed to attack_type)
  3. Findings — severity pie chart, per-severity grouped tables with green-highlighted Mitigation: row sourced from REMEDIATION_MAP
  4. Raw Output — full scanner stdout on a new page

Remediation Knowledge Base

REMEDIATION_MAP in pdf_report.py contains 48 entries keyed by attack type and finding type. _lookup_remediation(key) normalises underscores, hyphens, and case, then does exact → substring → reverse-substring matching.


6. Database Layer (MongoDB)

// users collection
{
    "_id": ObjectId,
    "email": "user@example.com",
    "hashed_password": "$2b$12$...",
    "created_at": ISODate
}

// scans collection
{
    "scan_id": "scan_20260303_120000_all",
    "user_email": "user@example.com",
    "target": "scanme.nmap.org",
    "status": "completed",          // initializing|scanning|analyzing|completed|error
    "progress": 100,
    "scanners": ["nmap", "nikto", "sslscan", "dirsearch"],
    "started_at": ISODate,
    "completed_at": ISODate,
    "findings": [...],
    "ml_analysis": { "risk_score": 62, "attack_type": "XSS", ... },
    "raw_output": "...",
    "logs": [...]
}

🔄 Complete Scan Data Flow

1. User submits target on /scan page
   └─ POST /api/scan/start { target }
         │
         ▼
2. Backend validates target, generates scan_id, stores initial record in MongoDB
   └─ Starts background asyncio task: run_all_scanners()
   └─ Returns { scan_id, status: "started" }
         │
         ▼
3. Frontend polls GET /api/scan/status/{scan_id} every 3 s
    └─ Updates progress bar and inline log panel
         │
         ▼
4. Background task executes scanners sequentially
   ├─ Nmap   → open_ports, findings, raw
   ├─ Nikto  → findings, raw
   ├─ SSLScan → tls_versions, weak_ciphers, cert_info, findings, raw
   └─ DirSearch → exposed_paths, findings, raw
         │
         ▼
5. analyze_scan() calls `predict_attack(scan_input)`
    └─ Returns risk_score, attack_type, confidence, payload_prediction
         │
         ▼
6. Scan record updated: status="completed", findings, ml_analysis, raw_output
         │
         ▼
7. Frontend detects "completed" and routes to `/report/{scan_id}`
    └─ Report page renders PredictionCard, findings table, raw output, PDF action
         │
         ▼
8. User clicks Download PDF
   └─ GET /api/scan/report/{id}/pdf
   └─ PDFReportGenerator.generate_scan_report(scan_data) → bytes
   └─ Browser downloads scan_report_{id}_{date}.pdf

🔐 Security Architecture

JWT Authentication

Register  →  bcrypt hash password (12 rounds)  →  store in MongoDB
Login     →  verify bcrypt  →  issue JWT (default 24h expiry, HS256)
Requests  →  Axios Authorization: Bearer <token>  →  FastAPI Depends(verify_token)

Scan Ethics Controls

  • Target validation rejects obviously malformed inputs
  • Timeouts on every scanner subprocess prevent runaway processes
  • All subprocess calls use argument lists (not shell=True) to prevent injection

📊 Performance Metrics

Component Typical Duration
Nmap scan 20–40 s
Nikto scan 5–10 min
SSLScan 30–90 s
DirSearch 1–5 min
ML inference < 1 s
PDF generation 1–3 s

🔧 Technology Stack Summary

Layer Technology Version
Frontend Next.js 16.1.6
Frontend TailwindCSS 3.4.19
Frontend Framer Motion 12.29.0
Backend FastAPI 0.110.1
Backend Uvicorn 0.25.0
Database MongoDB 4.4+
DB Driver Motor 3.3.1
Auth PyJWT + bcrypt 2.10.1 / 4.1.2
ML scikit-learn 1.7.2
Charts matplotlib 3.x
PDF ReportLab 4.0.7
Scanners Nmap · Nikto · SSLScan · DirSearch Latest

Document Version: 2.1.0 · Last Updated: March 2026