AI-Powered Solidity Smart Contract Vulnerability Scanner
VulnGuard AI helps developers and auditors find security vulnerabilities in Ethereum smart contracts before they go live. Paste your Solidity code, click Scan, and receive a detailed vulnerability report powered by OpenRouter-hosted LLMs — complete with severity ratings, plain-English explanations, before/after fix code, and a downloadable PDF audit report.
- AI-Powered Analysis — Uses OpenRouter to detect 15+ vulnerability classes including reentrancy, integer overflow, access control issues, and more
- Monaco Code Editor — Full-featured Solidity editor with syntax highlighting and drag-and-drop
.solfile support - Severity Ratings — Every finding is rated CRITICAL / HIGH / MEDIUM / LOW with color-coded cards
- Before / After Code — Each vulnerability shows the vulnerable snippet alongside the fixed version
- AI Deep Fix — Generate an enhanced, detailed fix for any individual vulnerability
- Severity Distribution Chart — Visual pie chart summarizing your contract's risk profile
- PDF Audit Report — Download a professional PDF report with cover page, executive summary, and per-vulnerability detail
- Example Contracts — Built-in VulnerableBank, InsecureToken, and SafeBank examples to explore
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite, Tailwind CSS v4, Monaco Editor |
| Backend | Node.js, Express 5, TypeScript |
| Database | Supabase Postgres + Drizzle ORM |
| AI | OpenRouter (anthropic/claude-3.7-sonnet by default) |
| API Contract | OpenAPI 3.0, Orval codegen, Zod validation |
| PDFKit | |
| Charts | Recharts |
| Package Manager | pnpm (monorepo) |
vulnguard-ai/
├── artifacts/
│ ├── api-server/ # Express backend (port 8080)
│ │ └── src/
│ │ └── routes/
│ │ └── vulnguard/
│ │ ├── scan.ts # POST /api/scan
│ │ ├── generate-fix.ts # POST /api/generate-fix
│ │ ├── report.ts # GET /api/report/:scanId
│ │ ├── store.ts # In-memory scan store
│ │ └── prompts.ts # LLM system prompts
│ └── vulnguard/ # React frontend (Vite)
│ └── src/
│ ├── pages/Home.tsx
│ ├── hooks/use-scanner.ts
│ └── components/
│ ├── VulnerabilityCard.tsx
│ └── SeverityChart.tsx
├── lib/
│ ├── api-spec/openapi.yaml # OpenAPI 3.0 specification
│ ├── api-client-react/ # Generated React Query hooks
│ ├── api-zod/ # Generated Zod validation schemas
│ └── integrations-anthropic-ai/ # OpenRouter client wrapper
├── .env.example # Environment variable template
└── README.md
- Node.js 20+
- pnpm 9+
- An OpenRouter API key — get one at openrouter.ai/keys
git clone https://github.com/IamHammadDevX/VulnGaurd-AI.git
cd VulnGaurd-AIcp .env.example .envOpen .env and fill in your values:
OPENROUTER_API_KEY=sk-or-your-key-here
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
OPENROUTER_MODEL=anthropic/claude-3.7-sonnet
DATABASE_URL=postgresql://postgres.<project-ref>:<password>@aws-0-<region>.pooler.supabase.com:6543/postgres?sslmode=require
PORT=8080
NODE_ENV=development
LOG_LEVEL=infoNever commit your
.envfile. It is already listed in.gitignore.
pnpm installpnpm --filter @workspace/db run push:secureThis command performs schema push, applies RLS hardening, and verifies RLS status/policies.
pnpm --filter @workspace/api-spec run codegenOpen two terminals:
Terminal 1 — API Server
pnpm --filter @workspace/api-server run devTerminal 2 — Frontend
pnpm --filter @workspace/vulnguard run devThen open http://localhost:5173 in your browser.
Scan a Solidity smart contract for vulnerabilities.
Request
{
"code": "pragma solidity ^0.8.0; contract MyContract { ... }",
"contractName": "MyContract"
}Response
{
"success": true,
"scanId": "uuid-v4",
"contract_name": "MyContract",
"total_vulnerabilities": 3,
"risk_score": 72,
"vulnerabilities": [
{
"id": 1,
"type": "Reentrancy",
"severity": "CRITICAL",
"title": "Reentrancy in withdraw()",
"description": "...",
"technical_risk": "...",
"vulnerable_code": "...",
"fixed_code": "...",
"recommendation": "..."
}
],
"summary": "...",
"analysis_time_ms": 12500,
"timestamp": "2026-01-01T00:00:00.000Z"
}Generate an enhanced AI fix for a specific vulnerability.
Request
{
"vulnerability": { ... },
"contractCode": "pragma solidity ..."
}Response
{
"success": true,
"original_code": "...",
"fixed_code": "...",
"explanation": "...",
"resources": ["https://..."]
}Download the PDF audit report for a completed scan.
Response — application/pdf binary stream
| Category | Examples |
|---|---|
| Reentrancy | Single-function, cross-function, cross-contract |
| Arithmetic | Integer overflow/underflow, division by zero |
| Access Control | Missing modifiers, tx.origin usage |
| Logic Errors | Race conditions, front-running, TOD |
| Bad Randomness | block.timestamp, blockhash misuse |
| Denial of Service | Gas limit issues, push payment patterns |
| Flash Loan | Price manipulation, oracle attacks |
| Unchecked Calls | Return value ignored, low-level calls |
| Self-Destruct | Unprotected selfdestruct |
| Timestamp Dependence | Miner-manipulable timestamps |
This project is built to run natively on Replit. Add your OpenRouter credentials as environment variables.
- Fork or import the repo on Replit
- Add
OPENROUTER_API_KEYand optionalOPENROUTER_MODELin Secrets - Click Run — all three services start automatically
If running on Render's free tier, the backend spins down after ~15 minutes of inactivity, causing next request to take 50+ seconds (cold start).
Solution: Keep backend alive with periodic health check pings.
📖 Setup Guide: See KEEP_ALIVE_SETUP.md
Quick Summary:
- Use UptimeRobot (free) to ping
/api/healthzevery 5 minutes - Prevents cold starts entirely
- Takes 2 minutes to set up
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | Supabase Postgres connection string used by Drizzle and API runtime |
SUPABASE_DB_URL |
No | Fallback DB URL if DATABASE_URL is not set |
SUPABASE_URL |
Yes (for auth) | Supabase project URL used by API auth verification |
SUPABASE_ANON_KEY |
Yes (for auth) | Supabase anon/publishable key used by frontend auth |
VITE_SUPABASE_URL |
Yes (frontend auth) | Vite-exposed Supabase URL |
VITE_SUPABASE_ANON_KEY |
Yes (frontend auth) | Vite-exposed Supabase anon/publishable key |
OPENROUTER_API_KEY |
Yes | Your OpenRouter API key |
OPENROUTER_BASE_URL |
No | OpenRouter base URL (default: https://openrouter.ai/api/v1) |
OPENROUTER_MODEL |
No | OpenRouter model ID (default: anthropic/claude-3.7-sonnet) |
OPENROUTER_HTTP_REFERER |
No | Optional referer header sent to OpenRouter |
OPENROUTER_APP_NAME |
No | Optional app title header sent to OpenRouter |
PORT |
No | API server port (default: 8080) |
NODE_ENV |
No | development or production |
LOG_LEVEL |
No | Pino log level (default: info) |
For a full setup walkthrough, see docs/SUPABASE_SETUP.md.
- API keys are read exclusively from environment variables — never hardcoded
.envis listed in.gitignoreand must never be committed- AI responses are validated against Zod schemas before use — malformed model output returns HTTP 502 instead of propagating bad data
- Contract code is limited to 50KB per request to prevent abuse
- All scan data is stored in-memory only (no database) — data is cleared on server restart
MIT — see LICENSE for details.
- OpenRouter — LLM gateway and routing
- OpenZeppelin — Security patterns and references
- SWC Registry — Smart contract weakness classification
- Secureum — Solidity security knowledge base