File size: 3,975 Bytes
245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f 200e3d7 245ed4f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from pydantic import BaseModel
import re, os, requests, base64
from groq import Groq
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# ββ helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def parse_github_url(url):
url = url.strip().rstrip('/')
m = re.match(r"https://github\.com/([^/]+)/([^/]+)/blob/[^/]+/(.+)", url)
if m:
return m.group(1), m.group(2), m.group(3)
return None, None, None
def get_file_content(owner, repo, path):
token = os.getenv("GITHUB_TOKEN", "")
headers = {"Authorization": f"token {token}"} if token else {}
r = requests.get(f"https://api.github.com/repos/{owner}/{repo}/contents/{path}", headers=headers, timeout=15)
if r.status_code != 200:
return None, f"GitHub API error: {r.status_code}"
return base64.b64decode(r.json()["content"]).decode("utf-8", errors="replace"), None
def scan_file(owner, repo, file_path):
code, err = get_file_content(owner, repo, file_path)
if err:
return {"error": err}
if len(code) > 6000:
code = code[:6000] + "\n... [truncated]"
groq_key = os.getenv("GROQ_API_KEY", "")
if not groq_key:
return {"error": "GROQ_API_KEY not configured"}
client = Groq(api_key=groq_key)
prompt = f"""You are a cybersecurity expert. Analyze this code for security vulnerabilities.
File: {file_path}
Repository: {owner}/{repo}
```
{code}
```
Provide a detailed security analysis in this exact markdown format:
# Security Analysis Report
## File Overview
- Repository: {owner}/{repo}
- File: {file_path}
- Language: [detected language]
- Lines analyzed: [count]
## Vulnerabilities Found
### [Vulnerability Name] β [CRITICAL/HIGH/MEDIUM/LOW]
- **Line**: [line number]
- **Code**: `[snippet]`
- **Issue**: [explanation]
- **CVE Reference**: [CVE ID if applicable]
## Remediation
[Specific fixes with corrected code]
## Risk Summary
- Critical: [n] | High: [n] | Medium: [n] | Low: [n]
- **Overall Risk**: [CRITICAL/HIGH/MEDIUM/LOW]
"""
try:
response = client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": prompt}],
temperature=0.1,
max_tokens=4096,
)
return {"result": response.choices[0].message.content}
except Exception as e:
return {"error": f"AI analysis failed: {str(e)}"}
# ββ API routes ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class ScanRequest(BaseModel):
url: str
@app.get("/api/health")
def health():
return {"status": "ok", "groq_key_set": bool(os.getenv("GROQ_API_KEY"))}
@app.post("/api/scan")
def scan(req: ScanRequest):
owner, repo, file_path = parse_github_url(req.url)
if not owner or not repo or not file_path:
return {"error": "Invalid GitHub file URL. Use format: github.com/owner/repo/blob/branch/file"}
return scan_file(owner, repo, file_path)
# ββ Serve React frontend ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if os.path.exists("static"):
app.mount("/assets", StaticFiles(directory="static/assets"), name="assets")
@app.get("/")
def serve_root():
return FileResponse("static/index.html")
@app.get("/{full_path:path}")
def serve_spa(full_path: str):
return FileResponse("static/index.html")
|