const { useState, useEffect, useRef } = React;
// Auth via API key
// Inject spinner keyframe
if (!document.getElementById("wwyw-spin")) {
const s = document.createElement("style");
s.id = "wwyw-spin";
s.textContent = "@keyframes spin { to { transform: rotate(360deg) } } @keyframes pulse { 0%,100%{opacity:.4} 50%{opacity:1} }";
document.head.appendChild(s);
}
const API = "https://analyzer.wewatchyourwebsite.com/js";
function apiHeaders(extra={}) {
const s = getSession();
const h = { ...extra };
if (s && s.apiKey) h["X-API-Key"] = s.apiKey;
return h;
}
const PLANS = [
{ id:"basic", name:"Basic", price:49, scans:1, followups:0, fixes:5, color:"#38bdf8", desc:"Single scan + report" },
{ id:"standard", name:"Standard", price:99, scans:1, followups:3, fixes:20, color:"#a78bfa", desc:"Full scan + 3 follow-ups", popular:true },
{ id:"pro", name:"Professional", price:149, scans:1, followups:4, fixes:999, color:"#f97316", desc:"Full scan + 4 follow-ups + unlimited fixes" },
];
const SEVERITY_COLOR = { critical:"#ef4444", high:"#f97316", medium:"#eab308", low:"#22c55e", informational: "#94a3b8" };
const SEVERITY_BG = { critical:"#fef2f2", high:"#fff7ed", medium:"#fefce8", low:"#f0fdf4", informational:"#f8fafc" };
const SOURCE_LABEL = { secrets:"Secrets", semgrep:"AST Analysis", dep_audit:"Dependency Audit", llm_review:"LLM Review", ast_scan:"Bundle AST" };
// ── Session helpers ───────────────────────────────────────────────────────────
function getSession() {
return JSON.parse(localStorage.getItem("wwyw_session") || "null");
}
function setSession(data) {
localStorage.setItem("wwyw_session", JSON.stringify(data));
}
// ── Auth / Plan Selection ─────────────────────────────────────────────────────
function Login({ onAuth }) {
const [key, setKey] = useState(""); const [err, setErr] = useState(""); const [checking, setChecking] = useState(false);
const session = getSession();
if (session) {
onAuth(session);
return null;
}
const doLogin = async () => {
if (!key.trim()) { setErr("Please enter your API key"); return; }
setChecking(true); setErr("");
try {
const res = await fetch(API + "/jobs/auth-check", { headers: { "X-API-Key": key.trim() } });
if (res.status === 403 || res.status === 401) { setErr("Invalid API key"); setChecking(false); return; }
const plan = { plan:"api", price:0, scansRemaining:999, fixesRemaining:999, apiKey:key.trim(), purchasedAt:new Date().toISOString() };
setSession(plan);
onAuth(plan);
} catch(e) { setErr("Connection error"); }
setChecking(false);
};
return (
WWYW
JavaScript Security Analyzer
API Key
{ setKey(e.target.value); setErr(""); }}
onKeyDown={e=>e.key==="Enter"&&doLogin()}
style={{ width:"100%", padding:"10px 12px", borderRadius:8, outline:"none", boxSizing:"border-box",
border:err?"1px solid #ef4444":"1px solid #334155", background:"#0f172a", color:"#f1f5f9", fontSize:14 }} />
{err &&
{err}
}
{checking?"Verifying...":"Connect"}
Contact us to receive an API key
);
}
function PlanSelector({ onSelect, onBack }) {
return (
WWYW Security Analysis
One-time payment. No subscription. Scan your AI-generated app before it goes live.
{PLANS.map(plan => (
{plan.popular && (
MOST POPULAR
)}
{plan.name}
${plan.price}
once
{plan.desc}
{[
`1 full security scan`,
plan.followups > 0 ? `${plan.followups} follow-up scans` : null,
`${plan.fixes === 999 ? "Unlimited" : plan.fixes} AI code fixes`,
"Full findings report",
"Remediation guidance",
plan.id === "pro" ? "Priority support" : null,
].filter(Boolean).map((item, i) => (
✓
{item}
))}
{
const session = { plan: plan.id, price: plan.price, scansRemaining: plan.scans + plan.followups,
fixesRemaining: plan.fixes, purchasedAt: new Date().toISOString() };
setSession(session);
onSelect(session);
}}
style={{ width:"100%", padding:"10px 0", background:plan.color, color:"#0f172a",
fontWeight:700, border:"none", borderRadius:8, cursor:"pointer", fontSize:14 }}>
Get Started
))}
{onBack && (
← Back
)}
Secure payment via Stripe · traef@wewatchyourwebsite.com
);
}
// ── Status Badge ──────────────────────────────────────────────────────────────
function StatusBadge({ status, overall }) {
const val = (overall||status||"").toLowerCase();
const cfg = {
block:{ bg:"#fef2f2", color:"#ef4444", label:"BLOCK" }, blocked:{ bg:"#fef2f2", color:"#ef4444", label:"BLOCK" },
warn:{ bg:"#fff7ed", color:"#f97316", label:"WARN" }, pass:{ bg:"#f0fdf4", color:"#22c55e", label:"PASS" },
running:{ bg:"#eff6ff", color:"#3b82f6", label:"RUNNING" }, queued:{ bg:"#f8fafc", color:"#94a3b8", label:"QUEUED" },
failed:{ bg:"#fef2f2", color:"#ef4444", label:"FAILED" }, complete:{ bg:"#f0fdf4", color:"#22c55e", label:"DONE" },
}[val] || { bg:"#f8fafc", color:"#94a3b8", label:val?.toUpperCase()||"?" };
return {cfg.label} ;
}
// ── Submit Panel ──────────────────────────────────────────────────────────────
function SubmitPanel({ onSubmit, loading, session }) {
const [tab, setTab] = useState("url");
const [url, setUrl] = useState("");
const [bulk, setBulk] = useState("");
const [file, setFile] = useState(null);
const fileRef = useRef();
const canScan = session.scansRemaining > 0;
const submit = () => {
if (!canScan) return;
if (tab==="url" && url.trim()) onSubmit({ type:"url", value:url.trim() });
if (tab==="bulk") { const urls=bulk.split("\n").map(s=>s.trim()).filter(Boolean); if(urls.length) onSubmit({type:"bulk",value:urls}); }
if (tab==="zip" && file) onSubmit({ type:"zip", value:file });
};
const tabs = [{ id:"url", label:"URL" }, { id:"bulk", label:"Bulk" }, { id:"zip", label:"ZIP" }];
return (
{/* Scan credits */}
Scans remaining:
{session.scansRemaining}
Fixes remaining:
{session.fixesRemaining===999?"∞":session.fixesRemaining}
{!canScan && (
No scans remaining.{" "}
{ localStorage.removeItem("wwyw_session"); window.location.reload(); }}>
Upgrade plan
)}
{tabs.map(t=>(
setTab(t.id)}
style={{ padding:"4px 12px", borderRadius:6, border:"none", cursor:"pointer", fontSize:11,
background:tab===t.id?"#38bdf8":"#0f172a", color:tab===t.id?"#0f172a":"#94a3b8", fontWeight:tab===t.id?700:400 }}>
{t.label}
))}
{tab==="url" && (
setUrl(e.target.value)} onKeyDown={e=>e.key==="Enter"&&submit()}
placeholder="github.com/user/repo or https://site.com"
style={{ flex:1, padding:"8px 11px", borderRadius:8, border:"1px solid #334155",
background:"#0f172a", color:"#f1f5f9", fontSize:12, outline:"none" }} />
{loading?"Scanning...":"Scan"}
)}
{tab==="bulk" && (
)}
{tab==="zip" && (
setFile(e.target.files[0])} />
fileRef.current.click()}
style={{ padding:"8px 14px", background:"#0f172a", color:"#94a3b8",
border:"1px solid #334155", borderRadius:8, cursor:"pointer", fontSize:12 }}>
{file?file.name:"Choose ZIP"}
{loading?"Uploading...":"Scan"}
)}
);
}
// ── Job Card ──────────────────────────────────────────────────────────────────
function JobCard({ job, onSelect, selected }) {
const src = job.source?.split("/").slice(-1)[0]||job.source||"Unknown";
const s = job.summary||{};
return (
onSelect(job)}
style={{ background:selected?"#1e3a5f":"#1e293b", borderRadius:9, padding:"11px 14px",
cursor:"pointer", marginBottom:8, border:selected?"1px solid #38bdf8":"1px solid transparent" }}>
{job.source}
{ e.stopPropagation(); navigator.clipboard.writeText(job.job_id); }}
title="Click to copy Job ID">
{job.job_id}
{s.total>0 && (
{s.critical>0&&● {s.critical} Crit }
{s.high>0&&● {s.high} High }
{s.medium>0&&● {s.medium} Med }
{s.low>0&&● {s.low} Low }
)}
{(job.status==="running"||job.status==="queued")&&(
)}
);
}
// ── Fix Confirm ───────────────────────────────────────────────────────────────
function FixConfirm({ finding, remaining, onConfirm, onCancel }) {
return (
Generate Code Fix
AI will generate a specific code fix for:
{finding.severity?.toUpperCase()} -- {(finding.message||finding.type||"").substring(0,55)}
Fixes remaining on your plan
{remaining===999?"Unlimited":remaining}
API cost
~$0.004
{remaining===0 ? (
No fixes remaining on your plan.{" "}
{ localStorage.removeItem("wwyw_session"); window.location.reload(); }}>
Upgrade
) : (
Cancel
Generate Fix
)}
);
}
// ── Finding Row ───────────────────────────────────────────────────────────────
function FindingRow({ f, onFix }) {
const [open, setOpen] = useState(false);
const sev = f.severity?.toLowerCase();
return (
setOpen(!open)} style={{ padding:"9px 13px", cursor:"pointer",
display:"flex", justifyContent:"space-between", alignItems:"center" }}>
{sev}
{SOURCE_LABEL[f.source]||f.source}
{f.message||f.title||f.description||f.type}
{open?"▲":"▼"}
{open && (
{f.file&&
File: {f.file.split("/").slice(-3).join("/")} {f.line?`(line ${f.line})`:""}
}
{(f.detail||f.description)&&
{f.detail||f.description}
}
{f.fix&&
Fix: {f.fix}
}
{(f.preview||f.snippet)&&(
{f.preview||f.snippet}
)}
onFix(f)}
style={{ marginTop:9, padding:"5px 13px", background:"#38bdf8", color:"#0f172a",
fontWeight:700, border:"none", borderRadius:6, cursor:"pointer", fontSize:11 }}>
✦ Generate Fix
)}
);
}
// ── Reviewed & Dismissed (False Positives) ───────────────────────────────────
function ReviewedDismissed({ items }) {
const [open, setOpen] = useState(false);
if (!items || items.length === 0) return null;
return (
setOpen(!open)}
style={{ display:"flex", alignItems:"center", gap:8, cursor:"pointer",
padding:"10px 14px", background:"#0f172a", borderRadius:8,
border:"1px solid #1e293b" }}>
✓
Reviewed & Dismissed — {items.length} finding{items.length!==1?"s":""} analyzed and determined to be false positive{items.length!==1?"s":""}
▶
{open && (
{items.map((fp, i) => (
FALSE POSITIVE
{fp.original_severity?.toUpperCase()}
{fp.file}:{fp.line}
{fp.description}
Why FP:
{fp.fp_reason}
{fp.fp_category?.replace(/_/g, " ")}
))}
)}
);
}
// ── Attack Chain Step ─────────────────────────────────────────────────────────
function ChainStep({ step, isLast }) {
const [open, setOpen] = useState(false);
return (
{step.step}
{!isLast &&
}
setOpen(!open)}
style={{ background:"#0f172a", borderRadius:8, padding:"10px 14px",
cursor:"pointer", border:"1px solid #1e293b" }}>
{step.action}
{open ? "▲" : "▼"}
{open && (
{step.technical_detail && (
Technical Detail
{step.technical_detail}
)}
{step.payload_example && (
Payload / PoC
{step.payload_example}
)}
{step.connects_to && !isLast && (
→
{step.connects_to}
)}
)}
);
}
// ── Attack Chain Card ─────────────────────────────────────────────────────────
function ChainCard({ chain }) {
const [open, setOpen] = useState(false);
const sevColor = SEVERITY_COLOR[chain.severity] || "#94a3b8";
const likelihoodColor = { high:"#ef4444", medium:"#eab308", low:"#22c55e" }[chain.likelihood] || "#94a3b8";
return (
setOpen(!open)}
style={{ padding:"14px 16px", cursor:"pointer",
display:"flex", justifyContent:"space-between", alignItems:"flex-start" }}>
{chain.severity}
{chain.likelihood} likelihood
{chain.steps ? chain.steps.length : 0} steps
{chain.title}
{chain.attacker_goal}
{open ? "▲" : "▼"}
{open && (
Executive Summary
{chain.executive_summary}
Attack Steps
{(chain.steps || []).map((step, i) => (
))}
{chain.combined_impact && (
Business Impact
{chain.combined_impact}
)}
{chain.remediation_priority && (
Fix This First
{chain.remediation_priority}
)}
)}
);
}
// ── Attack Chains Panel ───────────────────────────────────────────────────────
function AttackChainsPanel({ data }) {
if (!data) return (
No attack chain data available.
);
if (data.stage8_skipped) return (
{data.attack_surface_summary || "No chains constructed."}
);
const chains = data.chains || [];
const unchained = data.unchained_criticals || [];
return (
{data.attack_surface_summary && (
Attack Surface Summary
{data.attack_surface_summary}
)}
{chains.length === 0 && (
No multi-step chains identified.
)}
{chains.map((chain, i) =>
)}
{unchained.length > 0 && (
Standalone Critical Findings
{unchained.map((u, i) => (
{u.description}
{u.standalone_impact}
))}
)}
);
}
// ── Report Panel ──────────────────────────────────────────────────────────────
function ReportPanel({ job, onFix }) {
const [filter, setFilter] = useState("all");
const [view, setView] = useState("findings");
const r = job.report;
if (!r) return (
{job.status==="running"?"Analysis in progress...":
job.status==="queued"?"Waiting to start...":
job.status==="failed"?`Failed: ${job.error}`:"No report available"}
);
const s=r.summary||{}; const findings=r.findings||[];
const filtered = filter==="all"?findings:findings.filter(f=>f.severity?.toLowerCase()===filter);
const sevCounts = ["critical","high","medium","low"].filter(sv=>(s[sv]||0)>0);
return (
{[{label:"Files",val:r.files_scanned,color:"#38bdf8"},{label:"Framework",val:r.framework?.replace("Framework.",""),color:"#a78bfa"},
{label:"Critical",val:s.critical||0,color:"#ef4444"},{label:"High",val:s.high||0,color:"#f97316"},
{label:"Medium",val:s.medium||0,color:"#eab308"},{label:"Low",val:s.low||0,color:"#22c55e"},
{label:"Chains",val:(r.attack_chains?.chains||[]).length||0,color:"#f472b6"},
].map(item=>(
{item.val??"-"}
{item.label}
))}
{findings.length>0&&(
{["findings","compliance","attack chains"].map(v=>(
setView(v)}
style={{ padding:"5px 14px", borderRadius:6, border:view===v?"1px solid #38bdf8":"1px solid #334155",
background:view===v?"#1e3a5f":"#0f172a", color:view===v?"#38bdf8":"#64748b",
cursor:"pointer", fontSize:12, fontWeight:view===v?700:400, textTransform:"capitalize" }}>
{v}
))}
)}
{view==="compliance"&&findings.length>0&&(()=>{
const owaspGroups = {};
findings.forEach(fi=>{
const owasp = fi.owasp || "Unmapped";
if (!owaspGroups[owasp]) owaspGroups[owasp] = [];
owaspGroups[owasp].push(fi);
});
const sorted = Object.entries(owaspGroups).sort((a,b)=>{
if (a[0]==="Unmapped") return 1;
if (b[0]==="Unmapped") return -1;
return b[1].length - a[1].length;
});
const owaspColors = {
"A01:2021 Broken Access Control":"#ef4444",
"A02:2021 Cryptographic Failures":"#f97316",
"A03:2021 Injection":"#eab308",
"A04:2021 Insecure Design":"#a78bfa",
"A07:2021 Identification and Auth Failures":"#38bdf8",
"A08:2021 Software and Data Integrity Failures":"#f472b6",
"A10:2021 SSRF":"#22c55e",
};
return (
OWASP Top 10 Coverage
{sorted.filter(([k])=>k!=="Unmapped").map(([owasp, items])=>(
{owasp}
{items.length}
{items.filter(x=>x.severity==="critical").length} critical, {items.filter(x=>x.severity==="high").length} high
))}
{sorted.some(([k])=>k==="Unmapped")&&(
{sorted.find(([k])=>k==="Unmapped")[1].length} findings not yet mapped to OWASP (dependency advisories)
)}
{sorted.map(([owasp, items])=>(
{owasp}
{items.length} finding{items.length!==1?"s":""}
{items.map((fi,i)=>(
{fi.severity?.toUpperCase()}
{(fi.message||fi.title||fi.type||"").substring(0,70)}
{fi.cwe&&{fi.cwe} }
{fi.source}
))}
))}
);
})()}
{view==="attack chains"&&(
)}
{view==="findings"&&<>
{findings.length>0&&(
{["all",...sevCounts].map(sv=>(
setFilter(sv)}
style={{ padding:"3px 11px", borderRadius:20, border:"none", cursor:"pointer", fontSize:11,
background:filter===sv?(SEVERITY_COLOR[sv]||"#38bdf8"):"#1e293b",
color:filter===sv?"#fff":"#94a3b8", fontWeight:filter===sv?700:400 }}>
{sv==="all"?`All (${findings.length})`:`${sv} (${s[sv]||0})`}
))}
)}
{filtered.length===0&&
No findings for this filter.
}
{filtered.map((f,i)=>
)}
>}
);
}
// ── Fix Slide-over ────────────────────────────────────────────────────────────
function FixPanel({ finding, onClose }) {
const [fix, setFix] = useState(""); const [loading, setLoading] = useState(false);
useEffect(() => {
if (!finding) return;
setFix(""); setLoading(true);
fetch("https://analyzer.wewatchyourwebsite.com/js/remediate", {
method:"POST", headers: apiHeaders({"Content-Type":"application/json"}),
body: JSON.stringify({
model:"claude-sonnet-4-20250514", max_tokens:1000,
messages:[{role:"user", content:
`You are a senior security engineer. Generate a specific code fix for this vulnerability.
Severity: ${finding.severity}
Source: ${finding.source||"unknown"}
Type: ${finding.type||finding.rule_id||finding.pattern||"unknown"}
${finding.source==="dep_audit" ? `Package: ${finding.package||"unknown"}
Vulnerable versions: ${finding.version||"unknown"}
Patched in: ${finding.patched_in||"unknown"}
CVSS: ${finding.cvss||"unknown"}
Vuln ID: ${finding.vuln_id||"unknown"}
Detector: ${finding.detector||"unknown"}` : `File: ${finding.file||"unknown"} Line: ${finding.line||"unknown"}
Code: ${finding.preview||finding.snippet||"not available"}`}
Issue: ${finding.message||finding.title||finding.description}
Detail: ${finding.detail||finding.description||""}
Fix guidance: ${finding.fix||"none"}
${finding.source==="dep_audit" ?
"IMPORTANT: Sanity-check the patched_in version against the vulnerable range. If patched_in is LOWER than the vulnerable range (e.g. vulnerable <=7.5.0 but patched_in says 3.20.2), flag this as suspicious advisory data and recommend the user verify the correct patched version on GitHub/npm before upgrading. Provide: 1) Why this specific version is vulnerable (cite the CVE/GHSA if known, 2-3 sentences) 2) Exact upgrade command (npm/yarn) — but ONLY if the patched version makes sense relative to the vulnerable range 3) Breaking changes to watch for when upgrading 4) If a major version jump, mention migration steps." :
"Provide: 1) Why this is vulnerable (2-3 sentences) 2) Corrected code with comments 3) Additional steps needed."}`}]
})
})
.then(r=>r.json()).then(d=>setFix(d.content?.[0]?.text||"No response"))
.catch(e=>setFix(`Error: ${e.message}`)).finally(()=>setLoading(false));
}, [finding]);
if (!finding) return null;
return (
Generated Fix
{finding.severity?.toUpperCase()} -- {finding.file?.split("/").slice(-1)[0]}
✕
{loading?
Generating fix...
:
{fix} }
{!loading&&fix&&(
navigator.clipboard.writeText(fix)}
style={{ flex:1, padding:"8px 0", background:"#38bdf8", color:"#0f172a",
fontWeight:700, border:"none", borderRadius:6, cursor:"pointer", fontSize:12 }}>Copy
Close
)}
);
}
// ── Main ──────────────────────────────────────────────────────────────────────
function App() {
const [session, setSession] = useState(null);
const [jobs, setJobs] = useState(() => {
try {
const saved = localStorage.getItem('wwyw_jobs');
return saved ? JSON.parse(saved) : [];
} catch { return []; }
});
// Persist jobs to localStorage whenever they change
useEffect(() => {
try {
// Only save completed jobs to avoid storing stale running states
const toSave = jobs.filter(j => ['complete','blocked','failed'].includes(j.status));
localStorage.setItem('wwyw_jobs', JSON.stringify(toSave.slice(0, 20)));
} catch(e) {}
}, [jobs]);
const [selected, setSelected] = useState(null);
const [confirmFinding, setConfirmFinding] = useState(null);
const [fixFinding, setFixFinding] = useState(null);
const [loading, setLoading] = useState(false);
const [sess, setSess] = useState(session);
const handleAuth = (plan) => { setSession(plan); setSess(plan); };
const handleSubmit = async ({ type, value }) => {
if (!sess || sess.scansRemaining <= 0) return;
setLoading(true);
const sources = Array.isArray(value) ? value : [value];
for (let i = 0; i < sources.length; i++) {
const src = sources[i];
try {
let res, data;
if (type === "zip") {
const form = new FormData();
form.append("file", src);
res = await fetch(`${API}/analyze/upload`, { method:"POST", headers: apiHeaders(), body:form });
} else {
res = await fetch(`${API}/analyze`, {
method:"POST",
headers: apiHeaders({ "Content-Type":"application/json" }),
body: JSON.stringify({ source: src, history_scan: false }),
});
}
data = await res.json();
const jobId = data.job_id;
if (!jobId) throw new Error(data.detail || "No job ID returned");
setJobs(prev => [{ job_id:jobId, source:src, status:"queued", overall:null, summary:null, report:null }, ...prev]);
// Poll until complete
const poll = setInterval(async () => {
try {
const sr = await fetch(`${API}/jobs/${jobId}`, { headers: apiHeaders() });
const status = await sr.json();
const done = ["complete","blocked","failed"].includes(status.status);
if (done && (status.overall === "block" || status.overall === "warn" || status.overall === "pass")) {
// Fetch full report
const rr = await fetch(`${API}/jobs/${jobId}/report`, { headers: apiHeaders() });
const report = await rr.json();
setJobs(prev => prev.map(j => j.job_id === jobId ? {
...status, report, source: src
} : j));
// Decrement scan only on success
setSess(prev => {
const u = { ...prev, scansRemaining: prev.scansRemaining - 1 };
localStorage.setItem("wwyw_session", JSON.stringify(u));
setSession(u);
return u;
});
} else {
setJobs(prev => prev.map(j => j.job_id === jobId ? { ...j, ...status, source: src } : j));
}
if (done) clearInterval(poll);
} catch(e) {
clearInterval(poll);
}
}, 5000);
} catch(e) {
setJobs(prev => [{ job_id:`err-${Date.now()}`, source:src, status:"failed",
error:e.message, overall:null, summary:null, report:null }, ...prev]);
}
}
setLoading(false);
};
const handleFixConfirm = () => {
if (!sess || sess.fixesRemaining === 0) return;
const updated = { ...sess, fixesRemaining: sess.fixesRemaining===999?999:sess.fixesRemaining-1 };
setSession(updated); setSess(updated);
localStorage.setItem("wwyw_session", JSON.stringify(updated));
setFixFinding(confirmFinding);
setConfirmFinding(null);
};
const selectedJob = jobs.find(j=>j.job_id===selected?.job_id)||selected;
const planInfo = PLANS.find(p=>p.id===sess?.plan);
if (!sess) return ;
return (
WWYW
JavaScript Security Analyzer
{planInfo &&
{planInfo.name} }
Scans: {sess.scansRemaining}
Fixes: {sess.fixesRemaining===999?"∞":sess.fixesRemaining}
{ localStorage.removeItem("wwyw_session"); window.location.reload(); }}
style={{ color:"#334155", fontSize:11, cursor:"pointer" }}>Change plan
{jobs.length===0&&(
Submit a GitHub URL, live site, or ZIP to begin your scan.
)}
{jobs.length>0&&(
{ if(confirm("Clear all scan history?")){ setJobs([]); setSelected(null); localStorage.removeItem("wwyw_jobs"); }}}
style={{ color:"#475569", fontSize:10, cursor:"pointer", padding:"2px 6px",
borderRadius:4, border:"1px solid #334155" }}>Clear history
)}
{jobs.map(j=>
)}
{!selectedJob?(
🔍
Select a scan to view results
Submit a repository or URL to begin
):(
{selectedJob.source?.split("/").slice(-1)[0]||selectedJob.source}
{selectedJob.source}
)}
{confirmFinding&&
setConfirmFinding(null)} />}
{fixFinding&&setFixFinding(null)} />}
);
}
ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(App));