"""
VulnScout — Gradio app (Hugging Face Space front-end).
Paste a Solidity contract/function → VulnScout encodes it on a **Modal GPU** with the
SCAR sparse retriever and surfaces the most similar *known-vulnerable* code, each with the
real auditor finding behind it. Every audit is live Modal inference (the judged artifact).
Requires (as Space secrets): MODAL_TOKEN_ID, MODAL_TOKEN_SECRET → so this Space can call
the deployed `vulnscout` Modal app. Without them, the UI loads in a clearly-labelled demo
fallback instead of crashing.
"""
import os
import gradio as gr
MODAL_APP = "vulnscout"
MODAL_CLS = "VulnScoutEngine"
# --- Lazy Modal engine handle (cached) ---------------------------------------
_engine = None
_engine_err = None
def _get_engine():
global _engine, _engine_err
if _engine is not None or _engine_err is not None:
return _engine
try:
import modal
Engine = modal.Cls.from_name(MODAL_APP, MODAL_CLS)
_engine = Engine()
except Exception as e: # missing tokens, app not deployed, etc. — degrade, don't crash
_engine_err = str(e)
return _engine
SEVERITY_COLORS = {
"critical": "#b3001b", "high": "#d7263d", "medium": "#e08a00",
"low": "#3a7ca5", "informational": "#6c757d", "info": "#6c757d",
}
def _severity_of(finding: str) -> str:
head = (finding or "").strip().lower()
for sev in ("critical", "high", "medium", "low", "informational", "info"):
if head.startswith(sev) or f"severity: {sev}" in head:
return sev
return "info"
def _render(out: dict) -> str:
if not out or "error" in out:
return f"
⚠️ {out.get('error','no response') if out else 'no response'}
"
m = (f""
f"⚡ live on Modal {out['gpu']} · encode {out['encode_ms']}ms · "
f"retrieve {out['retrieve_ms']}ms · "
f"this audit ≈ ${out['est_cost_usd']:.6f} · "
f"index = {out['n_index']:,} known vulns · "
f"{out['n_active']} sparse features active
")
cards = []
for i, r in enumerate(out.get("results", []), 1):
sev = _severity_of(r["finding"])
color = SEVERITY_COLORS.get(sev, "#6c757d")
code = r["code"] if len(r["code"]) <= 1100 else r["code"][:1100] + "\n… (truncated)"
cards.append(
f""
f"
"
f""
f"{sev}"
f"match #{i} · "
f"similarity {r['score']:.3f} · source: {r['source']}
"
f"
{_esc(r['finding'])}
"
f"
{_esc(code)}
"
f"
"
)
if not cards:
cards = ["No similar known vulnerabilities found.
"]
feats = out.get("active_features", [])
feat_html = ""
if feats:
chips = " ".join(
f"#{f['feature']} · {f['weight']:.2f}"
for f in feats)
feat_html = (f"🔍 Why these matches — top sparse SAE features that fired "
f"(interpretable signal)
{chips}
")
return m + "".join(cards) + feat_html
def _esc(s: str) -> str:
return (s.replace("&", "&").replace("<", "<").replace(">", ">"))
WAKING = ("⏳ Waking the Modal GPU… the first audit after idle "
"spins up an A10G from scale-to-zero (~30s). After that, audits are ~0.5s. "
"Encoding your contract live on Modal…
")
def audit(contract: str, top_k: int):
engine = _get_engine()
if engine is None:
yield (""
"⚠️ Modal backend not connected. Set MODAL_TOKEN_ID and "
"MODAL_TOKEN_SECRET as Space secrets and deploy "
"modal_app.py. See the demo video in the README for a live run."
f"
({_engine_err})
")
return
if not (contract or "").strip():
yield "Paste a Solidity contract or function to audit.
"
return
yield WAKING
try:
out = engine.audit.remote(contract, int(top_k))
except Exception as e:
yield f"⚠️ Modal call failed: {_esc(str(e))}
"
return
yield _render(out)
EXAMPLES = [
["""// Arbitrary delegatecall: attacker-controlled target runs in this contract's context
function execute(address target, bytes calldata data) external {
(bool ok, ) = target.delegatecall(data); // <-- no access control / allowlist
require(ok, "call failed");
}""", 5],
["""// Unchecked return value of a low-level transfer
function pay(address payable to, uint256 amt) external {
to.send(amt); // <-- return value ignored
}""", 5],
["""// Reentrancy: external call before state update
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "insufficient");
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok, "transfer failed");
balances[msg.sender] -= amount; // <-- updated AFTER the external call
}""", 5],
]
HERO = """
Build Small · powered by Modal
🛡️ VulnScout
Sparse, interpretable vulnerability precedent search for Solidity.
Paste a contract — get the closest known-exploited code and the
auditor's finding behind it, encoded live on a Modal GPU.
"""
STATS = """
1.5B
params · laptop-class
"""
MODAL_CARD = """
⚡ Modal, three ways
🏋️ Trained on Modal H100s · ~$280
📦 Indexed the corpus on Modal · Batch
⚡ This audit runs on Modal · live
"""
EMPTY = """
🔍
Paste a Solidity contract or pick an example, then
Scout for vulnerabilities.
"""
CSS = """
.gradio-container {max-width: 1100px !important; margin: 0 auto !important;}
footer {display: none !important;}
.vs-stat {background:#0e1420; border:1px solid #1e2737; border-radius:12px;
padding:10px 18px; min-width:130px; text-align:center;}
.vs-num {font-size:21px; font-weight:800; color:#e2e8f0;
font-family:'JetBrains Mono',ui-monospace,monospace;}
.vs-lbl {font-size:11px; color:#7c8aa0; margin-top:3px; letter-spacing:.02em;}
.scout-btn, .scout-btn button {width:100% !important; font-size:16px !important;
font-weight:700 !important; letter-spacing:.01em; padding:14px 16px !important;
box-shadow:0 8px 26px rgba(34,211,238,.22) !important;}
.code-card .cm-editor, .code-card textarea {font-family:'JetBrains Mono',ui-monospace,monospace !important;
font-size:13px !important;}
.gradio-container thead th {background:#0e1420 !important; color:#cbd5e1 !important; border-color:#1e2737 !important;}
.gradio-container tbody td {background:#0c1018 !important; color:#c0ccdb !important; border-color:#1b2230 !important;}
.gradio-container tbody tr:hover td {background:#11161f !important;}
"""
theme = gr.themes.Base(
primary_hue=gr.themes.colors.cyan,
secondary_hue=gr.themes.colors.indigo,
neutral_hue=gr.themes.colors.slate,
font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"],
font_mono=[gr.themes.GoogleFont("JetBrains Mono"), "ui-monospace", "monospace"],
).set(
body_background_fill="#080b11",
body_text_color="#cbd5e1",
block_background_fill="#0c1018",
block_border_color="#1b2230",
block_label_text_color="#8a97ad",
input_background_fill="#0a0e15",
button_primary_background_fill="linear-gradient(90deg,#22d3ee,#6366f1)",
button_primary_background_fill_hover="linear-gradient(90deg,#3edcf5,#7c7ff5)",
button_primary_text_color="#06121a",
slider_color="#22d3ee",
)
with gr.Blocks(title="VulnScout — Solidity vulnerability scout", css=CSS, theme=theme) as demo:
gr.HTML(HERO)
gr.HTML(STATS)
with gr.Row():
with gr.Column(scale=3):
inp = gr.Code(label="Solidity contract / function", language=None,
lines=16, elem_classes="code-card")
with gr.Column(scale=2):
top_k = gr.Slider(1, 10, value=5, step=1, label="Matches to return")
btn = gr.Button("🔎 Scout for vulnerabilities", variant="primary",
elem_classes="scout-btn")
gr.HTML(MODAL_CARD)
out = gr.HTML(EMPTY)
gr.HTML("⚡ Try an example
")
gr.Examples(examples=EXAMPLES, inputs=[inp, top_k])
gr.HTML(""
"VulnScout finds precedent — it does not replace a professional audit. "
"· Built on SCAR · Compute by Modal
")
btn.click(audit, inputs=[inp, top_k], outputs=out)
if __name__ == "__main__":
demo.launch()