Spaces:
Runtime error
Runtime error
TafadzwaTaps commited on
Commit ·
8ef8aee
1
Parent(s): 2630621
latest deployment
Browse files- app.py +23 -23
- frontend/api.js +19 -15
- frontend/app.js +23 -18
app.py
CHANGED
|
@@ -246,7 +246,6 @@ def scan_vulnerabilities(text: str):
|
|
| 246 |
# AI ENGINE
|
| 247 |
# =========================================================
|
| 248 |
async def ai_enrich(text: str, findings):
|
| 249 |
-
# 🔒 No API key fallback
|
| 250 |
if not HF_API_KEY:
|
| 251 |
return {
|
| 252 |
"explanation": "AI disabled",
|
|
@@ -262,19 +261,24 @@ async def ai_enrich(text: str, findings):
|
|
| 262 |
"inputs": f"""
|
| 263 |
You are a cybersecurity expert.
|
| 264 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
Findings:
|
| 266 |
{findings}
|
| 267 |
|
| 268 |
-
Return STRICT JSON ONLY:
|
| 269 |
-
{{ "explanation": "", "fixes": [] }}
|
| 270 |
-
|
| 271 |
Code:
|
| 272 |
-
{text[:
|
| 273 |
"""
|
| 274 |
}
|
| 275 |
)
|
| 276 |
|
| 277 |
-
#
|
| 278 |
try:
|
| 279 |
data = res.json()
|
| 280 |
except Exception as e:
|
|
@@ -282,15 +286,15 @@ Code:
|
|
| 282 |
print("RAW RESPONSE:", res.text)
|
| 283 |
|
| 284 |
return {
|
| 285 |
-
"explanation":
|
| 286 |
"fixes": []
|
| 287 |
}
|
| 288 |
|
| 289 |
-
#
|
| 290 |
if isinstance(data, list):
|
| 291 |
data = data[0]
|
| 292 |
|
| 293 |
-
#
|
| 294 |
if isinstance(data, dict) and "generated_text" in data:
|
| 295 |
text_output = data["generated_text"]
|
| 296 |
|
|
@@ -300,15 +304,13 @@ Code:
|
|
| 300 |
return parsed
|
| 301 |
except:
|
| 302 |
return {
|
| 303 |
-
"explanation": text_output,
|
| 304 |
"fixes": []
|
| 305 |
}
|
| 306 |
|
| 307 |
-
# 🔥 STEP 4: VALID DICT
|
| 308 |
if isinstance(data, dict):
|
| 309 |
return data
|
| 310 |
|
| 311 |
-
# 🔥 STEP 5: FALLBACK
|
| 312 |
return {
|
| 313 |
"explanation": str(data),
|
| 314 |
"fixes": []
|
|
@@ -316,7 +318,6 @@ Code:
|
|
| 316 |
|
| 317 |
except Exception as e:
|
| 318 |
print("🔥 AI REQUEST ERROR:", str(e))
|
| 319 |
-
|
| 320 |
return {
|
| 321 |
"explanation": "AI request failed",
|
| 322 |
"fixes": []
|
|
@@ -341,16 +342,7 @@ async def analyze(req: AnalyzeRequest, auth=Depends(get_user)):
|
|
| 341 |
|
| 342 |
findings = scan_vulnerabilities(req.text)
|
| 343 |
|
| 344 |
-
|
| 345 |
-
ai_raw = await ai_enrich(req.text, findings)
|
| 346 |
-
|
| 347 |
-
if isinstance(ai_raw, dict):
|
| 348 |
-
ai = ai_raw
|
| 349 |
-
else:
|
| 350 |
-
ai = {
|
| 351 |
-
"explanation": str(ai_raw),
|
| 352 |
-
"fixes": []
|
| 353 |
-
}
|
| 354 |
|
| 355 |
analysis_id = str(uuid.uuid4())
|
| 356 |
|
|
@@ -400,6 +392,14 @@ def usage(auth=Depends(get_user)):
|
|
| 400 |
def home():
|
| 401 |
return {"status": "SafeAIScan running on Hugging Face Spaces"}
|
| 402 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
@app.get("/api/history")
|
| 404 |
def history(auth=Depends(get_user)):
|
| 405 |
user = auth["user"]
|
|
|
|
| 246 |
# AI ENGINE
|
| 247 |
# =========================================================
|
| 248 |
async def ai_enrich(text: str, findings):
|
|
|
|
| 249 |
if not HF_API_KEY:
|
| 250 |
return {
|
| 251 |
"explanation": "AI disabled",
|
|
|
|
| 261 |
"inputs": f"""
|
| 262 |
You are a cybersecurity expert.
|
| 263 |
|
| 264 |
+
Analyze this code and return ONLY valid JSON.
|
| 265 |
+
|
| 266 |
+
Format:
|
| 267 |
+
{{ "explanation": "string", "fixes": ["string"] }}
|
| 268 |
+
|
| 269 |
+
Do not include markdown.
|
| 270 |
+
Do not include text outside JSON.
|
| 271 |
+
|
| 272 |
Findings:
|
| 273 |
{findings}
|
| 274 |
|
|
|
|
|
|
|
|
|
|
| 275 |
Code:
|
| 276 |
+
{text[:1500]}
|
| 277 |
"""
|
| 278 |
}
|
| 279 |
)
|
| 280 |
|
| 281 |
+
# SAFE PARSE
|
| 282 |
try:
|
| 283 |
data = res.json()
|
| 284 |
except Exception as e:
|
|
|
|
| 286 |
print("RAW RESPONSE:", res.text)
|
| 287 |
|
| 288 |
return {
|
| 289 |
+
"explanation": res.text[:500],
|
| 290 |
"fixes": []
|
| 291 |
}
|
| 292 |
|
| 293 |
+
# HANDLE HF FORMAT
|
| 294 |
if isinstance(data, list):
|
| 295 |
data = data[0]
|
| 296 |
|
| 297 |
+
# HANDLE GENERATED TEXT
|
| 298 |
if isinstance(data, dict) and "generated_text" in data:
|
| 299 |
text_output = data["generated_text"]
|
| 300 |
|
|
|
|
| 304 |
return parsed
|
| 305 |
except:
|
| 306 |
return {
|
| 307 |
+
"explanation": text_output[:500],
|
| 308 |
"fixes": []
|
| 309 |
}
|
| 310 |
|
|
|
|
| 311 |
if isinstance(data, dict):
|
| 312 |
return data
|
| 313 |
|
|
|
|
| 314 |
return {
|
| 315 |
"explanation": str(data),
|
| 316 |
"fixes": []
|
|
|
|
| 318 |
|
| 319 |
except Exception as e:
|
| 320 |
print("🔥 AI REQUEST ERROR:", str(e))
|
|
|
|
| 321 |
return {
|
| 322 |
"explanation": "AI request failed",
|
| 323 |
"fixes": []
|
|
|
|
| 342 |
|
| 343 |
findings = scan_vulnerabilities(req.text)
|
| 344 |
|
| 345 |
+
ai = await ai_enrich(req.text, findings)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 346 |
|
| 347 |
analysis_id = str(uuid.uuid4())
|
| 348 |
|
|
|
|
| 392 |
def home():
|
| 393 |
return {"status": "SafeAIScan running on Hugging Face Spaces"}
|
| 394 |
|
| 395 |
+
@app.get("/api/me")
|
| 396 |
+
def get_me(auth=Depends(get_user)):
|
| 397 |
+
user = auth["user"]
|
| 398 |
+
|
| 399 |
+
return {
|
| 400 |
+
"plan": user.get("plan", "free")
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
@app.get("/api/history")
|
| 404 |
def history(auth=Depends(get_user)):
|
| 405 |
user = auth["user"]
|
frontend/api.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
| 1 |
-
// =========================
|
| 2 |
-
// CONFIG
|
| 3 |
-
// =========================
|
| 4 |
const BASE_URL = "https://rathious-safeaiscan.hf.space";
|
| 5 |
|
| 6 |
-
// =========================
|
| 7 |
-
// TOKEN HELPERS
|
| 8 |
-
// =========================
|
| 9 |
function getToken() {
|
| 10 |
return localStorage.getItem("access_token");
|
| 11 |
}
|
|
@@ -19,9 +13,6 @@ function clearAuth() {
|
|
| 19 |
window.location.replace("login.html");
|
| 20 |
}
|
| 21 |
|
| 22 |
-
// =========================
|
| 23 |
-
// CORE REQUEST WRAPPER
|
| 24 |
-
// =========================
|
| 25 |
async function apiRequest(endpoint, options = {}) {
|
| 26 |
const res = await fetch(BASE_URL + endpoint, {
|
| 27 |
...options,
|
|
@@ -36,7 +27,6 @@ async function apiRequest(endpoint, options = {}) {
|
|
| 36 |
});
|
| 37 |
|
| 38 |
if (res.status === 401) {
|
| 39 |
-
console.warn("Session expired");
|
| 40 |
clearAuth();
|
| 41 |
return null;
|
| 42 |
}
|
|
@@ -44,9 +34,18 @@ async function apiRequest(endpoint, options = {}) {
|
|
| 44 |
return res;
|
| 45 |
}
|
| 46 |
|
| 47 |
-
//
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
async function analyzeCode(text) {
|
| 51 |
const res = await apiRequest("/api/analyze", {
|
| 52 |
method: "POST",
|
|
@@ -65,10 +64,15 @@ async function analyzeCode(text) {
|
|
| 65 |
|
| 66 |
async function getUsage() {
|
| 67 |
const res = await apiRequest("/api/usage");
|
| 68 |
-
return
|
| 69 |
}
|
| 70 |
|
| 71 |
async function getHistory() {
|
| 72 |
const res = await apiRequest("/api/history");
|
| 73 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
const BASE_URL = "https://rathious-safeaiscan.hf.space";
|
| 2 |
|
|
|
|
|
|
|
|
|
|
| 3 |
function getToken() {
|
| 4 |
return localStorage.getItem("access_token");
|
| 5 |
}
|
|
|
|
| 13 |
window.location.replace("login.html");
|
| 14 |
}
|
| 15 |
|
|
|
|
|
|
|
|
|
|
| 16 |
async function apiRequest(endpoint, options = {}) {
|
| 17 |
const res = await fetch(BASE_URL + endpoint, {
|
| 18 |
...options,
|
|
|
|
| 27 |
});
|
| 28 |
|
| 29 |
if (res.status === 401) {
|
|
|
|
| 30 |
clearAuth();
|
| 31 |
return null;
|
| 32 |
}
|
|
|
|
| 34 |
return res;
|
| 35 |
}
|
| 36 |
|
| 37 |
+
// SAFE JSON PARSE
|
| 38 |
+
async function safeJson(res) {
|
| 39 |
+
const text = await res.text();
|
| 40 |
+
try {
|
| 41 |
+
return JSON.parse(text);
|
| 42 |
+
} catch {
|
| 43 |
+
console.error("RAW ERROR:", text);
|
| 44 |
+
throw new Error("Server error: " + text);
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
// API CALLS
|
| 49 |
async function analyzeCode(text) {
|
| 50 |
const res = await apiRequest("/api/analyze", {
|
| 51 |
method: "POST",
|
|
|
|
| 64 |
|
| 65 |
async function getUsage() {
|
| 66 |
const res = await apiRequest("/api/usage");
|
| 67 |
+
return safeJson(res);
|
| 68 |
}
|
| 69 |
|
| 70 |
async function getHistory() {
|
| 71 |
const res = await apiRequest("/api/history");
|
| 72 |
+
return safeJson(res);
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
async function getMe() {
|
| 76 |
+
const res = await apiRequest("/api/me");
|
| 77 |
+
return safeJson(res);
|
| 78 |
}
|
frontend/app.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
| 1 |
-
// =========================
|
| 2 |
-
// UI ACTIONS
|
| 3 |
-
// =========================
|
| 4 |
-
|
| 5 |
async function scan() {
|
| 6 |
const code = document.getElementById("code").value;
|
| 7 |
|
|
@@ -11,6 +7,11 @@ async function scan() {
|
|
| 11 |
document.getElementById("result").innerText =
|
| 12 |
JSON.stringify(data, null, 2);
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
} catch (err) {
|
| 15 |
console.error(err);
|
| 16 |
alert("Scan failed");
|
|
@@ -38,17 +39,26 @@ async function loadHistory() {
|
|
| 38 |
try {
|
| 39 |
const data = await getHistory();
|
| 40 |
|
| 41 |
-
const
|
| 42 |
-
|
| 43 |
|
| 44 |
data.forEach(item => {
|
| 45 |
const li = document.createElement("li");
|
| 46 |
li.innerText = item.risk + " (" + item.score + ")";
|
| 47 |
-
|
| 48 |
});
|
| 49 |
|
| 50 |
} catch (err) {
|
| 51 |
-
console.error(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
}
|
| 53 |
}
|
| 54 |
|
|
@@ -56,12 +66,12 @@ function copyKey() {
|
|
| 56 |
const key = localStorage.getItem("api_key");
|
| 57 |
|
| 58 |
if (!key) {
|
| 59 |
-
alert("No API key
|
| 60 |
return;
|
| 61 |
}
|
| 62 |
|
| 63 |
navigator.clipboard.writeText(key);
|
| 64 |
-
alert("
|
| 65 |
}
|
| 66 |
|
| 67 |
function logout() {
|
|
@@ -69,24 +79,19 @@ function logout() {
|
|
| 69 |
window.location.replace("login.html");
|
| 70 |
}
|
| 71 |
|
| 72 |
-
// =========================
|
| 73 |
// INIT
|
| 74 |
-
// =========================
|
| 75 |
async function init() {
|
| 76 |
-
// Show API key
|
| 77 |
-
const apiKey = localStorage.getItem("api_key");
|
| 78 |
document.getElementById("apiKey").innerText =
|
| 79 |
-
|
| 80 |
|
| 81 |
await loadUsage();
|
| 82 |
await loadHistory();
|
|
|
|
| 83 |
}
|
| 84 |
|
| 85 |
init();
|
| 86 |
|
| 87 |
-
//
|
| 88 |
-
// MAKE FUNCTIONS GLOBAL (IMPORTANT)
|
| 89 |
-
// =========================
|
| 90 |
window.scan = scan;
|
| 91 |
window.copyKey = copyKey;
|
| 92 |
window.logout = logout;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
async function scan() {
|
| 2 |
const code = document.getElementById("code").value;
|
| 3 |
|
|
|
|
| 7 |
document.getElementById("result").innerText =
|
| 8 |
JSON.stringify(data, null, 2);
|
| 9 |
|
| 10 |
+
// ✅ LIVE USAGE UPDATE
|
| 11 |
+
if (data.usage_today !== undefined) {
|
| 12 |
+
document.getElementById("usage").innerText = data.usage_today;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
} catch (err) {
|
| 16 |
console.error(err);
|
| 17 |
alert("Scan failed");
|
|
|
|
| 39 |
try {
|
| 40 |
const data = await getHistory();
|
| 41 |
|
| 42 |
+
const list = document.getElementById("history");
|
| 43 |
+
list.innerHTML = "";
|
| 44 |
|
| 45 |
data.forEach(item => {
|
| 46 |
const li = document.createElement("li");
|
| 47 |
li.innerText = item.risk + " (" + item.score + ")";
|
| 48 |
+
list.appendChild(li);
|
| 49 |
});
|
| 50 |
|
| 51 |
} catch (err) {
|
| 52 |
+
console.error(err);
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
async function loadPlan() {
|
| 57 |
+
try {
|
| 58 |
+
const data = await getMe();
|
| 59 |
+
document.getElementById("plan").innerText = data.plan;
|
| 60 |
+
} catch {
|
| 61 |
+
document.getElementById("plan").innerText = "Free";
|
| 62 |
}
|
| 63 |
}
|
| 64 |
|
|
|
|
| 66 |
const key = localStorage.getItem("api_key");
|
| 67 |
|
| 68 |
if (!key) {
|
| 69 |
+
alert("No API key");
|
| 70 |
return;
|
| 71 |
}
|
| 72 |
|
| 73 |
navigator.clipboard.writeText(key);
|
| 74 |
+
alert("Copied!");
|
| 75 |
}
|
| 76 |
|
| 77 |
function logout() {
|
|
|
|
| 79 |
window.location.replace("login.html");
|
| 80 |
}
|
| 81 |
|
|
|
|
| 82 |
// INIT
|
|
|
|
| 83 |
async function init() {
|
|
|
|
|
|
|
| 84 |
document.getElementById("apiKey").innerText =
|
| 85 |
+
localStorage.getItem("api_key") || "Not available";
|
| 86 |
|
| 87 |
await loadUsage();
|
| 88 |
await loadHistory();
|
| 89 |
+
await loadPlan();
|
| 90 |
}
|
| 91 |
|
| 92 |
init();
|
| 93 |
|
| 94 |
+
// 🔥 REQUIRED FOR BUTTONS
|
|
|
|
|
|
|
| 95 |
window.scan = scan;
|
| 96 |
window.copyKey = copyKey;
|
| 97 |
window.logout = logout;
|