feat: ethical AVR pipeline — semantic extractor, harness factory, chain analyzer, disclosure vault, Security Research tab
9452af2 verified | """ | |
| Rhodawk AI — Vulnerability Chain Analyzer | |
| ========================================== | |
| Documents how primitive findings (individual assumption gaps + PoC results) | |
| might combine into higher-severity chains. | |
| ETHICAL CONSTRAINTS: | |
| - Chains are THEORETICAL proposals documented for human review | |
| - No chain is automatically executed | |
| - All chain proposals are stored with status PENDING_HUMAN_REVIEW | |
| - Human operator must approve or reject every chain before any further action | |
| Orchestrated by Nous Hermes 3 via OpenRouter. | |
| """ | |
| from __future__ import annotations | |
| import hashlib | |
| import json | |
| import os | |
| import re | |
| import sqlite3 | |
| import time | |
| from typing import Optional | |
| import requests | |
| OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "") | |
| HERMES_MODEL = os.getenv( | |
| "RHODAWK_RESEARCH_MODEL", | |
| "nousresearch/hermes-3-llama-3.1-405b:free", | |
| ) | |
| CHAIN_DB = os.getenv("RHODAWK_CHAIN_DB", "/data/chain_memory.sqlite") | |
| def _init_db() -> None: | |
| os.makedirs(os.path.dirname(CHAIN_DB), exist_ok=True) | |
| conn = sqlite3.connect(CHAIN_DB) | |
| conn.executescript(""" | |
| CREATE TABLE IF NOT EXISTS primitive_findings ( | |
| id TEXT PRIMARY KEY, | |
| repo TEXT NOT NULL, | |
| gap_id TEXT NOT NULL, | |
| severity TEXT, | |
| description TEXT, | |
| triggered INTEGER DEFAULT 0, | |
| confidence TEXT DEFAULT 'UNKNOWN', | |
| created_at REAL, | |
| harness_out TEXT | |
| ); | |
| CREATE TABLE IF NOT EXISTS chains ( | |
| id TEXT PRIMARY KEY, | |
| repo TEXT NOT NULL, | |
| primitive_ids TEXT NOT NULL, | |
| description TEXT, | |
| chained_severity TEXT, | |
| confidence TEXT, | |
| conditions TEXT, | |
| theoretical_impact TEXT, | |
| human_notes TEXT, | |
| status TEXT DEFAULT 'PENDING_HUMAN_REVIEW', | |
| created_at REAL, | |
| human_approved INTEGER DEFAULT 0, | |
| human_reviewer TEXT, | |
| reviewed_at REAL | |
| ); | |
| """) | |
| conn.commit() | |
| conn.close() | |
| def store_primitive( | |
| repo: str, | |
| gap_id: str, | |
| severity: str, | |
| description: str, | |
| triggered: bool, | |
| confidence: str = "UNKNOWN", | |
| harness_result: Optional[dict] = None, | |
| ) -> str: | |
| """Persist a primitive finding from the harness execution.""" | |
| _init_db() | |
| finding_id = hashlib.sha256( | |
| f"{repo}:{gap_id}:{time.time()}".encode() | |
| ).hexdigest()[:16] | |
| conn = sqlite3.connect(CHAIN_DB) | |
| conn.execute( | |
| """INSERT OR REPLACE INTO primitive_findings | |
| (id, repo, gap_id, severity, description, triggered, confidence, created_at, harness_out) | |
| VALUES (?,?,?,?,?,?,?,?,?)""", | |
| ( | |
| finding_id, repo, gap_id, severity, description, | |
| 1 if triggered else 0, confidence, time.time(), | |
| json.dumps(harness_result or {}), | |
| ), | |
| ) | |
| conn.commit() | |
| conn.close() | |
| return finding_id | |
| def analyze_chains(repo: str) -> list[dict]: | |
| """ | |
| Ask Hermes to propose vulnerability chains from stored primitives. | |
| Returns THEORETICAL proposals — all tagged PENDING_HUMAN_REVIEW. | |
| Nothing is executed automatically. | |
| """ | |
| _init_db() | |
| conn = sqlite3.connect(CHAIN_DB) | |
| rows = conn.execute( | |
| """SELECT id, gap_id, severity, description, triggered, confidence | |
| FROM primitive_findings WHERE repo = ? | |
| ORDER BY created_at DESC""", | |
| (repo,), | |
| ).fetchall() | |
| conn.close() | |
| if len(rows) < 2: | |
| return [] | |
| primitives_text = "\n".join( | |
| f"- [{r[0]}] Gap: {r[1]} | Sev: {r[2]} | Triggered: {bool(r[4])} " | |
| f"| Confidence: {r[5]} | {r[3][:120]}" | |
| for r in rows | |
| ) | |
| headers = { | |
| "Authorization": f"Bearer {OPENROUTER_API_KEY}", | |
| "Content-Type": "application/json", | |
| "HTTP-Referer": "https://rhodawk.ai", | |
| } | |
| payload = { | |
| "model": HERMES_MODEL, | |
| "messages": [ | |
| { | |
| "role": "system", | |
| "content": ( | |
| "You are a senior security researcher conducting responsible vulnerability research. " | |
| "Analyse primitive findings and propose THEORETICAL vulnerability chains for human review. " | |
| "Be rigorous and conservative — only propose chains that are logically sound based on " | |
| "the available evidence. Mark any speculation clearly. Output valid JSON only." | |
| ), | |
| }, | |
| { | |
| "role": "user", | |
| "content": ( | |
| f"Analyse these primitive findings from {repo} and identify plausible chains.\n\n" | |
| f"PRIMITIVES:\n{primitives_text}\n\n" | |
| "Output ONLY this JSON:\n" | |
| '{"chains": [{' | |
| '"primitive_ids": ["id1","id2"],' | |
| '"description": "Step-by-step logical chain",' | |
| '"chained_severity": "P1|P2|P3",' | |
| '"confidence": "HIGH|MEDIUM|LOW",' | |
| '"required_conditions": ["condition1"],' | |
| '"theoretical_impact": "What an attacker could theoretically achieve",' | |
| '"human_verification_needed": "What a human researcher must manually verify before treating this as real"' | |
| "}]}" | |
| ), | |
| }, | |
| ], | |
| "max_tokens": 2048, | |
| "temperature": 0.1, | |
| } | |
| try: | |
| resp = requests.post( | |
| "https://openrouter.ai/api/v1/chat/completions", | |
| headers=headers, json=payload, timeout=120, | |
| ) | |
| resp.raise_for_status() | |
| raw = resp.json()["choices"][0]["message"]["content"] | |
| match = re.search(r"\{[\s\S]*\}", raw) | |
| if not match: | |
| return [] | |
| data = json.loads(match.group()) | |
| chains = data.get("chains", []) | |
| conn = sqlite3.connect(CHAIN_DB) | |
| for chain in chains: | |
| chain_id = hashlib.sha256( | |
| f"{repo}:{':'.join(chain.get('primitive_ids', []))}:{time.time()}".encode() | |
| ).hexdigest()[:16] | |
| chain["id"] = chain_id | |
| conn.execute( | |
| """INSERT OR IGNORE INTO chains | |
| (id, repo, primitive_ids, description, chained_severity, confidence, | |
| conditions, theoretical_impact, status, created_at) | |
| VALUES (?,?,?,?,?,?,?,?,'PENDING_HUMAN_REVIEW',?)""", | |
| ( | |
| chain_id, repo, | |
| json.dumps(chain.get("primitive_ids", [])), | |
| chain.get("description", ""), | |
| chain.get("chained_severity", "P3"), | |
| chain.get("confidence", "LOW"), | |
| json.dumps(chain.get("required_conditions", [])), | |
| chain.get("theoretical_impact", ""), | |
| time.time(), | |
| ), | |
| ) | |
| conn.commit() | |
| conn.close() | |
| return chains | |
| except Exception as e: | |
| return [{"error": str(e)}] | |
| def get_pending_chains(repo: Optional[str] = None) -> list[dict]: | |
| _init_db() | |
| conn = sqlite3.connect(CHAIN_DB) | |
| if repo: | |
| rows = conn.execute( | |
| """SELECT id, repo, description, chained_severity, confidence, status, created_at | |
| FROM chains WHERE repo=? AND status='PENDING_HUMAN_REVIEW' | |
| ORDER BY created_at DESC""", | |
| (repo,), | |
| ).fetchall() | |
| else: | |
| rows = conn.execute( | |
| """SELECT id, repo, description, chained_severity, confidence, status, created_at | |
| FROM chains WHERE status='PENDING_HUMAN_REVIEW' | |
| ORDER BY created_at DESC""", | |
| ).fetchall() | |
| conn.close() | |
| return [ | |
| { | |
| "id": r[0], "repo": r[1], "description": r[2], | |
| "severity": r[3], "confidence": r[4], "status": r[5], "created_at": r[6], | |
| } | |
| for r in rows | |
| ] | |
| def get_all_primitives(repo: Optional[str] = None) -> list[dict]: | |
| _init_db() | |
| conn = sqlite3.connect(CHAIN_DB) | |
| if repo: | |
| rows = conn.execute( | |
| "SELECT id, repo, gap_id, severity, description, triggered, confidence, created_at " | |
| "FROM primitive_findings WHERE repo=? ORDER BY created_at DESC", | |
| (repo,), | |
| ).fetchall() | |
| else: | |
| rows = conn.execute( | |
| "SELECT id, repo, gap_id, severity, description, triggered, confidence, created_at " | |
| "FROM primitive_findings ORDER BY created_at DESC", | |
| ).fetchall() | |
| conn.close() | |
| return [ | |
| { | |
| "id": r[0], "repo": r[1], "gap_id": r[2], "severity": r[3], | |
| "description": r[4], "triggered": bool(r[5]), | |
| "confidence": r[6], "created_at": r[7], | |
| } | |
| for r in rows | |
| ] | |
| def approve_chain(chain_id: str, reviewer: str) -> bool: | |
| _init_db() | |
| conn = sqlite3.connect(CHAIN_DB) | |
| conn.execute( | |
| "UPDATE chains SET status='HUMAN_APPROVED', human_approved=1, " | |
| "human_reviewer=?, reviewed_at=? WHERE id=?", | |
| (reviewer, time.time(), chain_id), | |
| ) | |
| conn.commit() | |
| conn.close() | |
| return True | |
| def reject_chain(chain_id: str, reviewer: str) -> bool: | |
| _init_db() | |
| conn = sqlite3.connect(CHAIN_DB) | |
| conn.execute( | |
| "UPDATE chains SET status='HUMAN_REJECTED', human_reviewer=?, reviewed_at=? WHERE id=?", | |
| (reviewer, time.time(), chain_id), | |
| ) | |
| conn.commit() | |
| conn.close() | |
| return True | |