Files
Secure-Prompt/secure_prompx.html

1013 lines
42 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SecurePrompt — Web Güvenlik Denetimi Prompt Oluşturucu</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Syne:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<style>
:root {
--bg: #0a0c10;
--bg2: #0f1318;
--bg3: #151a22;
--bg4: #1c2330;
--border: #1e2d3d;
--border2: #243447;
--text: #e2e8f0;
--text2: #8899aa;
--text3: #4a5568;
--accent: #00d4ff;
--accent2: #0099cc;
--green: #00e5a0;
--green2: #00a070;
--red: #ff4757;
--orange: #ff9f43;
--yellow: #ffd32a;
--fe: #7c6af5;
--be: #00e5a0;
--db: #ff6b6b;
--inf: #45aaf2;
--font-head: 'Syne', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
background: var(--bg);
color: var(--text);
font-family: var(--font-mono);
min-height: 100vh;
overflow-x: hidden;
}
/* NOISE TEXTURE */
body::before {
content: '';
position: fixed;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.04'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 0;
opacity: 0.4;
}
/* GRID BG */
body::after {
content: '';
position: fixed;
inset: 0;
background-image:
linear-gradient(var(--border) 1px, transparent 1px),
linear-gradient(90deg, var(--border) 1px, transparent 1px);
background-size: 40px 40px;
opacity: 0.3;
pointer-events: none;
z-index: 0;
}
.layout {
position: relative;
z-index: 1;
display: grid;
grid-template-columns: 320px 1fr;
grid-template-rows: auto 1fr;
min-height: 100vh;
}
/* ── HEADER ── */
header {
grid-column: 1 / -1;
padding: 1rem 1.5rem;
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
gap: 1rem;
background: rgba(10,12,16,0.95);
backdrop-filter: blur(8px);
position: sticky;
top: 0;
z-index: 100;
}
.logo {
font-family: var(--font-head);
font-size: 20px;
font-weight: 800;
letter-spacing: -0.5px;
display: flex;
align-items: center;
gap: 8px;
}
.logo-icon {
width: 32px; height: 32px;
background: linear-gradient(135deg, var(--accent), var(--fe));
border-radius: 8px;
display: flex; align-items: center; justify-content: center;
font-size: 14px;
}
.logo span { color: var(--accent); }
.header-meta {
margin-left: auto;
display: flex;
align-items: center;
gap: 12px;
}
.badge {
font-size: 10px;
font-weight: 600;
padding: 3px 8px;
border-radius: 4px;
border: 1px solid;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.badge-green { color: var(--green); border-color: var(--green2); background: rgba(0,229,160,0.08); }
.badge-blue { color: var(--accent); border-color: var(--accent2); background: rgba(0,212,255,0.08); }
.stat-chips { display: flex; gap: 8px; }
.chip {
font-size: 11px;
padding: 4px 10px;
border-radius: 6px;
background: var(--bg3);
border: 1px solid var(--border);
color: var(--text2);
display: flex; align-items: center; gap: 5px;
}
.chip strong { color: var(--text); font-weight: 600; }
/* ── SIDEBAR ── */
.sidebar {
border-right: 1px solid var(--border);
background: var(--bg2);
padding: 1.25rem;
overflow-y: auto;
height: calc(100vh - 57px);
position: sticky;
top: 57px;
}
.section { margin-bottom: 1.5rem; }
.section-title {
font-size: 10px;
font-weight: 600;
color: var(--text3);
text-transform: uppercase;
letter-spacing: 0.12em;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 6px;
}
.section-title::after {
content: '';
flex: 1;
height: 1px;
background: var(--border);
}
.pills { display: flex; flex-wrap: wrap; gap: 6px; }
.pill {
font-family: var(--font-mono);
font-size: 11px;
padding: 4px 10px;
border-radius: 5px;
border: 1px solid var(--border2);
background: transparent;
color: var(--text2);
cursor: pointer;
transition: all 0.15s;
}
.pill:hover { border-color: var(--accent2); color: var(--text); }
.pill.on { background: rgba(0,212,255,0.12); border-color: var(--accent); color: var(--accent); }
/* LAYER TABS */
.layer-tabs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6px;
margin-bottom: 10px;
}
.ltab {
font-family: var(--font-mono);
font-size: 11px;
padding: 6px 8px;
border-radius: 6px;
border: 1px solid var(--border2);
background: transparent;
color: var(--text2);
cursor: pointer;
transition: all 0.15s;
display: flex;
align-items: center;
gap: 5px;
}
.ltab:hover { background: var(--bg3); color: var(--text); }
.ltab.on { background: var(--bg4); border-color: var(--border2); color: var(--text); }
.ltab.on[data-l="fe"] { border-color: var(--fe); color: var(--fe); background: rgba(124,106,245,0.1); }
.ltab.on[data-l="be"] { border-color: var(--be); color: var(--be); background: rgba(0,229,160,0.08); }
.ltab.on[data-l="db"] { border-color: var(--db); color: var(--db); background: rgba(255,107,107,0.08); }
.ltab.on[data-l="inf"] { border-color: var(--inf); color: var(--inf); background: rgba(69,170,242,0.08); }
.dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.dot-fe { background: var(--fe); }
.dot-be { background: var(--be); }
.dot-db { background: var(--db); }
.dot-inf { background: var(--inf); }
/* CAT CARDS */
.cat-grid { display: flex; flex-direction: column; gap: 5px; }
.ccard {
border: 1px solid var(--border);
border-radius: 7px;
padding: 8px 10px;
cursor: pointer;
display: flex;
align-items: flex-start;
gap: 8px;
transition: all 0.15s;
background: var(--bg);
}
.ccard:hover { border-color: var(--border2); background: var(--bg3); }
.ccard.on { border-color: var(--fe); background: rgba(124,106,245,0.08); }
.ccard.on[data-layer="be"] { border-color: var(--be); background: rgba(0,229,160,0.06); }
.ccard.on[data-layer="db"] { border-color: var(--db); background: rgba(255,107,107,0.06); }
.ccard.on[data-layer="inf"] { border-color: var(--inf); background: rgba(69,170,242,0.06); }
.ccheck {
width: 14px; height: 14px;
border-radius: 3px;
border: 1.5px solid var(--border2);
flex-shrink: 0;
margin-top: 2px;
display: flex; align-items: center; justify-content: center;
transition: all 0.15s;
}
.ccard.on .ccheck { background: var(--fe); border-color: var(--fe); }
.ccard.on[data-layer="be"] .ccheck { background: var(--be); border-color: var(--be); }
.ccard.on[data-layer="db"] .ccheck { background: var(--db); border-color: var(--db); }
.ccard.on[data-layer="inf"] .ccheck { background: var(--inf); border-color: var(--inf); }
.ccheck svg { display: none; width: 9px; height: 9px; }
.ccard.on .ccheck svg { display: block; }
.clbl { font-size: 12px; font-weight: 500; color: var(--text); line-height: 1.2; }
.cdesc { font-size: 10px; color: var(--text2); margin-top: 3px; line-height: 1.4; }
.sev {
font-size: 9px;
padding: 1px 5px;
border-radius: 3px;
margin-left: 5px;
font-weight: 600;
vertical-align: middle;
letter-spacing: 0.04em;
}
.sev-c { background: rgba(255,71,87,0.15); color: var(--red); border: 1px solid rgba(255,71,87,0.3); }
.sev-h { background: rgba(255,159,67,0.15); color: var(--orange); border: 1px solid rgba(255,159,67,0.3); }
.sev-m { background: rgba(69,170,242,0.15); color: var(--inf); border: 1px solid rgba(69,170,242,0.3); }
.select-all-btn {
font-family: var(--font-mono);
font-size: 10px;
padding: 3px 8px;
border-radius: 4px;
border: 1px solid var(--border2);
background: transparent;
color: var(--text3);
cursor: pointer;
transition: all 0.15s;
margin-left: auto;
}
.select-all-btn:hover { color: var(--text2); border-color: var(--border2); }
.section-title-row {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.section-title-row .section-title { margin-bottom: 0; flex: 1; }
/* ── MAIN ── */
main {
display: grid;
grid-template-rows: 1fr auto;
overflow: hidden;
}
.prompt-area {
padding: 1.5rem;
overflow-y: auto;
height: calc(100vh - 57px - 80px);
}
.prompt-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 1rem;
}
.prompt-header h2 {
font-family: var(--font-head);
font-size: 14px;
font-weight: 600;
color: var(--text2);
text-transform: uppercase;
letter-spacing: 0.1em;
}
.line-count {
font-size: 10px;
color: var(--text3);
margin-left: auto;
}
/* PROMPT EDITOR */
.prompt-container {
position: relative;
border: 1px solid var(--border);
border-radius: 10px;
overflow: hidden;
background: var(--bg2);
}
.prompt-container::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 2px;
background: linear-gradient(90deg, var(--accent), var(--fe), var(--be), var(--db));
}
.line-numbers {
position: absolute;
left: 0; top: 0; bottom: 0;
width: 44px;
background: var(--bg);
border-right: 1px solid var(--border);
padding: 1rem 0;
overflow: hidden;
pointer-events: none;
}
.line-num {
font-size: 10px;
color: var(--text3);
text-align: right;
padding: 0 8px;
line-height: 1.75;
font-family: var(--font-mono);
height: 20.3px;
display: flex;
align-items: center;
justify-content: flex-end;
}
#prompt-output {
font-family: var(--font-mono);
font-size: 12px;
line-height: 1.75;
color: var(--text);
padding: 1rem 1rem 1rem 56px;
white-space: pre-wrap;
word-break: break-word;
min-height: 400px;
outline: none;
background: transparent;
width: 100%;
border: none;
resize: none;
caret-color: var(--accent);
}
/* SYNTAX HIGHLIGHT */
.hl-h1 { color: var(--accent); font-weight: 600; }
.hl-h2 { color: var(--green); font-weight: 600; }
.hl-h3 { color: var(--fe); font-weight: 500; }
.hl-bold { color: var(--orange); font-weight: 600; }
.hl-code { color: var(--yellow); background: rgba(255,211,42,0.08); padding: 0 3px; border-radius: 3px; }
.hl-bullet { color: var(--text2); }
.hl-dash { color: var(--accent2); }
/* BOTTOM BAR */
.bottom-bar {
height: 80px;
border-top: 1px solid var(--border);
background: rgba(10,12,16,0.98);
backdrop-filter: blur(8px);
display: flex;
align-items: center;
padding: 0 1.5rem;
gap: 10px;
}
.btn {
font-family: var(--font-mono);
font-size: 12px;
font-weight: 500;
padding: 9px 18px;
border-radius: 7px;
cursor: pointer;
transition: all 0.15s;
display: flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.btn-primary {
background: linear-gradient(135deg, var(--accent2), var(--accent));
border: 1px solid var(--accent);
color: #000;
font-weight: 600;
}
.btn-primary:hover { opacity: 0.9; transform: translateY(-1px); box-shadow: 0 4px 20px rgba(0,212,255,0.3); }
.btn-secondary {
background: var(--bg3);
border: 1px solid var(--border2);
color: var(--text);
}
.btn-secondary:hover { border-color: var(--accent2); color: var(--accent); background: rgba(0,212,255,0.06); }
.btn-green {
background: rgba(0,229,160,0.1);
border: 1px solid var(--green2);
color: var(--green);
}
.btn-green:hover { background: rgba(0,229,160,0.18); transform: translateY(-1px); }
.btn-orange {
background: rgba(255,159,67,0.1);
border: 1px solid rgba(255,159,67,0.4);
color: var(--orange);
}
.btn-orange:hover { background: rgba(255,159,67,0.18); transform: translateY(-1px); }
.toast {
position: fixed;
bottom: 100px;
right: 24px;
background: var(--bg4);
border: 1px solid var(--green2);
color: var(--green);
font-size: 12px;
padding: 10px 18px;
border-radius: 8px;
z-index: 999;
opacity: 0;
transform: translateY(10px);
transition: all 0.25s;
pointer-events: none;
}
.toast.show { opacity: 1; transform: translateY(0); }
/* EMPTY STATE */
.empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
min-height: 300px;
color: var(--text3);
gap: 8px;
}
.empty-icon { font-size: 40px; opacity: 0.3; }
.empty-text { font-size: 13px; }
/* SCROLLBAR */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--text3); }
/* PROGRESS BAR */
.progress-bar {
height: 3px;
background: var(--bg3);
border-radius: 2px;
margin-top: 6px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent), var(--fe));
border-radius: 2px;
transition: width 0.3s ease;
}
/* MODAL */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.7);
backdrop-filter: blur(4px);
z-index: 200;
display: none;
align-items: center;
justify-content: center;
}
.modal-overlay.show { display: flex; }
.modal {
background: var(--bg2);
border: 1px solid var(--border2);
border-radius: 12px;
padding: 1.5rem;
max-width: 480px;
width: 90%;
}
.modal h3 {
font-family: var(--font-head);
font-size: 16px;
margin-bottom: 1rem;
color: var(--accent);
}
.modal-btns { display: flex; gap: 8px; margin-top: 1.25rem; justify-content: flex-end; }
.modal-format-list { display: flex; flex-direction: column; gap: 8px; }
.modal-opt {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border: 1px solid var(--border);
border-radius: 7px;
cursor: pointer;
transition: all 0.15s;
}
.modal-opt:hover { border-color: var(--border2); background: var(--bg3); }
.modal-opt.sel { border-color: var(--accent); background: rgba(0,212,255,0.08); }
.modal-opt-icon { font-size: 20px; }
.modal-opt-title { font-size: 13px; font-weight: 500; color: var(--text); }
.modal-opt-desc { font-size: 11px; color: var(--text2); }
/* ANIM */
@keyframes fadeIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.ccard { animation: fadeIn 0.15s ease; }
/* RESPONSIVE */
@media (max-width: 768px) {
.layout { grid-template-columns: 1fr; }
.sidebar { height: auto; position: static; border-right: none; border-bottom: 1px solid var(--border); }
main { height: auto; }
.prompt-area { height: auto; }
.stat-chips { display: none; }
}
</style>
</head>
<body>
<div class="layout">
<!-- HEADER -->
<header>
<div class="logo">
<div class="logo-icon">🛡</div>
Secure<span>Prompt</span>
</div>
<span class="badge badge-green">v2.0</span>
<span class="badge badge-blue">TR</span>
<div class="header-meta">
<div class="stat-chips">
<div class="chip">Kategori: <strong id="h-cat">0</strong></div>
<div class="chip">Kontrol: <strong id="h-chk">~0</strong></div>
<div class="chip">Katman: <strong id="h-lay">0</strong></div>
<div class="chip">Token: <strong id="h-tok">~0</strong></div>
</div>
</div>
</header>
<!-- SIDEBAR -->
<aside class="sidebar">
<!-- APP TYPE -->
<div class="section">
<div class="section-title">Uygulama Türü</div>
<div class="pills" id="app-pills">
<button class="pill on" data-v="fullstack">Full-stack</button>
<button class="pill" data-v="spa">SPA / React</button>
<button class="pill" data-v="api">REST / GraphQL</button>
<button class="pill" data-v="docker">Docker</button>
<button class="pill" data-v="iot">IoT / Edge</button>
</div>
</div>
<!-- LAYER + CATS -->
<div class="section">
<div class="section-title-row">
<div class="section-title" style="margin-bottom:0;flex:1">Güvenlik Katmanı</div>
<button class="select-all-btn" id="all-btn">tümünü seç</button>
</div>
<div class="layer-tabs" id="layer-tabs">
<button class="ltab on" data-l="fe"><span class="dot dot-fe"></span>Frontend</button>
<button class="ltab" data-l="be"><span class="dot dot-be"></span>Backend</button>
<button class="ltab" data-l="db"><span class="dot dot-db"></span>Veritabanı</button>
<button class="ltab" data-l="inf"><span class="dot dot-inf"></span>Altyapı</button>
</div>
<div class="cat-grid" id="cat-grid"></div>
<div class="progress-bar"><div class="progress-fill" id="progress-fill" style="width:0%"></div></div>
<div style="font-size:10px;color:var(--text3);margin-top:4px;text-align:right" id="progress-txt">0 / 0 kategori</div>
</div>
<!-- OUTPUT -->
<div class="section">
<div class="section-title">Çıktı Formatı</div>
<div class="pills" id="out-pills">
<button class="pill on" data-v="report">Markdown</button>
<button class="pill" data-v="json">JSON/SARIF</button>
<button class="pill" data-v="pr">Pull Request</button>
<button class="pill" data-v="fix">Düzeltme</button>
</div>
</div>
<!-- PRIORITY -->
<div class="section">
<div class="section-title">Öncelik Filtresi</div>
<div class="pills" id="prio-pills">
<button class="pill on" data-v="all">Tümü</button>
<button class="pill" data-v="critical">Kritik</button>
<button class="pill" data-v="high">Kritik+Yüksek</button>
</div>
</div>
</aside>
<!-- MAIN CONTENT -->
<main>
<div class="prompt-area">
<div class="prompt-header">
<h2>// Generated Prompt</h2>
<span class="line-count" id="line-count">0 satır · 0 karakter</span>
</div>
<div class="prompt-container" id="prompt-container">
<div class="line-numbers" id="line-numbers"></div>
<div id="prompt-output" contenteditable="false" spellcheck="false">
<div class="empty">
<div class="empty-icon"></div>
<div class="empty-text">Sol panelden kategori seçin...</div>
</div>
</div>
</div>
</div>
<!-- BOTTOM BAR -->
<div class="bottom-bar">
<button class="btn btn-primary" id="btn-copy">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
Kopyala
</button>
<button class="btn btn-green" id="btn-md">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14,2 14,8 20,8"/></svg>
.md İndir
</button>
<button class="btn btn-orange" id="btn-pdf">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14,2 14,8 20,8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
PDF İndir
</button>
<button class="btn btn-secondary" id="btn-reset" style="margin-left:auto">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="1,4 1,10 7,10"/><path d="M3.51 15a9 9 0 1 0 .49-3.08"/></svg>
Sıfırla
</button>
</div>
</main>
</div>
<!-- TOAST -->
<div class="toast" id="toast"></div>
<script>
var CATS = {
fe:[
{id:'owasp',lbl:'OWASP Top 10',sev:'c',desc:'Injection, XSS, IDOR, SSRF, XXE',chk:14,tok:180},
{id:'auth',lbl:'Kimlik Doğrulama',sev:'c',desc:'JWT, session, OAuth, MFA',chk:9,tok:120},
{id:'headers',lbl:'HTTP Başlıkları',sev:'h',desc:'CSP, HSTS, CORS, X-Frame-Options',chk:5,tok:70},
{id:'secrets',lbl:'Gizli Veri Sızıntısı',sev:'c',desc:'API key, .env, kaynak kod tespiti',chk:7,tok:110}
],
be:[
{id:'injection',lbl:'Injection Saldırıları',sev:'c',desc:'SQL, NoSQL, OS, LDAP, SSTI, XXE',chk:12,tok:200},
{id:'waf',lbl:'Saldırı Tespiti & Önleme',sev:'c',desc:'WAF, rate limit, anomali, brute force',chk:10,tok:160},
{id:'api',lbl:'API Güvenliği',sev:'h',desc:'Rate limiting, input validation, schema',chk:8,tok:120},
{id:'bizlogic',lbl:'İş Mantığı Hataları',sev:'h',desc:'Race condition, fiyat manipülasyonu',chk:7,tok:110},
{id:'fileup',lbl:'Dosya Yükleme',sev:'h',desc:'MIME bypass, path traversal, webshell',chk:6,tok:95},
{id:'crypto',lbl:'Kriptografi',sev:'h',desc:'Zayıf algoritma, key yönetimi, TLS',chk:5,tok:75}
],
db:[
{id:'sqli',lbl:'SQL Injection (Derin)',sev:'c',desc:'Blind, time-based, ORM bypass',chk:10,tok:180},
{id:'nosqli',lbl:'NoSQL Injection',sev:'c',desc:'MongoDB, Redis, Elasticsearch',chk:7,tok:130},
{id:'dbaccess',lbl:'Erişim Kontrolü',sev:'c',desc:'Least privilege, RLS, tenant izolasyon',chk:8,tok:120},
{id:'dbenc',lbl:'Şifreleme & Maskeleme',sev:'h',desc:'At-rest, in-transit, PII maskeleme',chk:6,tok:95},
{id:'dbconf',lbl:'Yapılandırma Güvenliği',sev:'h',desc:'Default cred, public erişim, backup',chk:5,tok:80}
],
inf:[
{id:'docker',lbl:'Docker & Container',sev:'h',desc:'Root kullanıcı, cap-drop, image tarama',chk:8,tok:110},
{id:'deps',lbl:'Bağımlılık Güncellemeleri',sev:'h',desc:'CVE tarama, package audit, SCA',chk:6,tok:90},
{id:'network',lbl:'Ağ & Firewall',sev:'h',desc:'Açık port, segmentasyon, TLS',chk:5,tok:75},
{id:'logging',lbl:'Loglama & İzleme',sev:'m',desc:'Audit trail, SIEM, anomali',chk:4,tok:65}
]
};
var TEXTS = {
owasp:'### OWASP Top 10\n- A01 Kırık Erişim Kontrolü: IDOR, path traversal, yetkisiz endpoint\n- A02 Kriptografik Başarısızlıklar: açık metin iletim, zayıf şifreleme\n- A03 Injection: SQL/NoSQL/OS/LDAP/template — tüm giriş noktaları\n- A04 Güvensiz Tasarım: iş mantığııkları, tehdit modeli eksiklikleri\n- A05 Güvenlik Yapılandırma Hataları: default credential, debug mode\n- A06 Savunmasız Bileşenler: CVE veritabanıyla karşılaştır\n- A07 Kimlik Doğrulama Hataları: brute force, credential stuffing\n- A08 Yazılım Bütünlüğü Hataları: CI/CD güvenliği, unsigned update\n- A09 Güvenlik Loglama Eksiklikleri: kritik olayların loglanması\n- A10 SSRF: kullanıcı kontrolündeki URL doğrulama ve filtreleme',
auth:'### Kimlik Doğrulama & Yetkilendirme\n- JWT: alg=none saldırısı, weak secret, expiry, refresh token rotasyonu\n- Session: fixation, CSRF, secure/httpOnly bayraklar, idle timeout\n- OAuth/OIDC: redirect_uri doğrulama, state parametresi, PKCE\n- MFA: bypass senaryoları, backup code güvenliği, rate limiting\n- RBAC/ABAC: yetki yükseltme vektörleri\n- Password: bcrypt/argon2 kullanımı, account lockout politikası',
headers:'### HTTP Güvenlik Başlıkları\n- CSP: unsafe-inline/unsafe-eval tespiti; nonce/hash tabanlı politika öner\n- HSTS: max-age ≥ 31536000, includeSubDomains, preload\n- X-Frame-Options / frame-ancestors: clickjacking koruması\n- CORS: wildcard (*) origin, credentialed request uyumsuzluğu\n- Permissions-Policy: gereksiz browser özellik izinleri\n- Referrer-Policy: hassas URL sızıntısı önleme',
secrets:'### Gizli Veri & Sızıntı Tespiti\n- Kaynak kodda hardcoded API key, token, şifre tara\n- .env dosyaları .gitignore kapsamında mı?\n- Git geçmişinde accidental secret commit tara\n- Hata mesajları ve stack trace production\'da gizleniyor mu?\n- Browser DevTools üzerinden erişilebilir hassas veri tespiti',
injection:'### Injection Saldırıları — Kapsamlı Denetim\n\n**SQL Injection**\nTüm kullanıcı giriş noktalarını tara (form, URL param, header, cookie):\n- Classic: \' OR \'1\'=\'1 — string terminasyonu testi\n- Blind Boolean: AND 1=1 / AND 1=2 ile koşullu yanıt farkı\n- Time-based: SLEEP(5), WAITFOR DELAY ile gecikme testi\n- Error-based: EXTRACTVALUE(), UPDATEXML() ile hata mesajı sızdırma\n- Union-based: kolon sayısı tespiti ve veri exfiltration\n- ORM Bypass: raw query, format string birleştirme tespiti\n- Second-order: veritabanına yazılan, sonra çalıştırılan payload\n\n**NoSQL Injection**\n- MongoDB: $where, $gt, $ne, $regex operatör enjeksiyonu\n- Redis: KEYS *, FLUSHALL komut enjeksiyonu\n- Elasticsearch: script injection, aggregation manipulation\n\n**OS Command Injection**\n- exec(), system(), popen(), subprocess kullanımları\n- Payload: ; ls -la, | cat /etc/passwd, `id`, $(id)\n- Kontrol: shell=False, argüman listesi kullanımı\n\n**Template Injection (SSTI)**\n- Jinja2: {{7*7}}, motor tespiti ve RCE zinciri\n- Twig, Pebble, Velocity, Freemarker motor tespiti\n\n**LDAP & XPath Injection**\n- DN ve filter stringe özel karakter enjeksiyonu\n- XPath: \' or \'1\'=\'1 ile authentication bypass',
waf:'### Saldırı Tespiti & Önleme Mekanizmaları\n\n**WAF Denetimi**\n- WAF varlığını doğrula (ModSecurity, AWS WAF, Cloudflare)\n- OWASP CRS kural seti aktif mi?\n- Bypass senaryoları: encoding, case variation, fragmentation\n\n**Rate Limiting & Brute Force Önleme**\n- Login endpoint: IP ve hesap bazlı throttle var mı?\n- API endpoint bazında limit (örn. 100 req/min per IP)\n- Token bucket / sliding window algoritması\n- 429 yanıtında Retry-After header\n- CAPTCHA entegrasyonu (başarısız deneme eşiği)\n\n**Anomali Tespiti**\n- Olağandışı istek hacmi alerting\n- Coğrafi anomali: olağandışı bölge erişimi\n- User agent analizi: bot/scanner fingerprint\n- Honeypot endpoint: sahte endpointe erişim alarmı\n\n**DDoS Önleme**\n- Connection limit per IP (nginx: limit_conn)\n- Request size limit (max body, max header)\n- Slowloris koruması (keepalive timeout)\n\n**Input Sanitization Pipeline**\n- Tüm giriş noktaları için merkezi validation katmanı\n- Allowlist vs blocklist (allowlist tercih edilmeli)\n- Output encoding: context-aware (HTML, JS, URL, SQL)',
api:'### API Güvenliği\n- Rate limiting: endpoint bazında, IP ve kullanıcı bazlı throttling\n- Input validation: tip, uzunluk, format doğrulaması; allowlist yaklaşımı\n- GraphQL: introspection productionda kapalı mı? query depth/complexity limit?\n- Mass assignment: DTO whitelist, ORM güvenli property binding\n- Verb tampering: GET ile state değiştirme mümkün mü?\n- Pagination: keyset pagination güvenliği, offset injection riski',
bizlogic:'### İş Mantığı & Uygulama Katmanı\n- Race condition / TOCTOU: eşzamanlı istek ile bakiye/stok manipülasyonu\n- Negatif değer: fiyat alanına -100 ile kredi kazanma\n- Akış atlama: çok adımlı işlemlerde adım atlama (step 1 → step 3)\n- İş kural bypass: indirim kodu çoklu kullanım, referral loop\n- Parametre manipülasyonu: başkasına ait kaynak IDsi ile CRUD\n- Async işlem yarışı: webhooklar arası durum tutarsızlığı',
fileup:'### Dosya Yükleme Güvenliği\n- MIME type doğrulaması: Content-Typea güvenme, magic byte kontrolü\n- Uzantı whitelist: .jpg .png .pdf dışını reddet\n- Dosya boyutu limiti: frontend ve backendde zorunlu\n- Depolama yolu: web root dışına kaydet; path traversal önle\n- Antivirus tarama: yükleme sonrası otomatik tarama\n- Webshell tespiti: .php .jsp .aspx yüklenebiliyor mu?\n- Metadata sızdırma: EXIF verisi (GPS, cihaz bilgisi) temizleme',
crypto:'### Kriptografi & Şifreleme (Backend)\n- MD5/SHA1 kullanımını tespit et; SHA-256+ veya bcrypt/argon2 öner\n- AES-ECB modunu tespit et; GCM veya CBC+HMAC öner\n- RSA key uzunluğu: minimum 2048-bit\n- Math.random() güvenlik bağlamında kullanılıyor mu?\n- Key rotation politikası, HSM/KMS entegrasyonu\n- Sertifika geçerliliği ve pinning durumu',
sqli:'### SQL Injection — Derinlemesine Veritabanı Testi\n\n**Tespit Metodolojisi**\n1. Hata tabanlı: \' -- ve \' OR \'1\'=\'1\' --\n2. Boolean blind: AND 1=1 (true) vs AND 1=2 (false)\n3. Time-based blind: SLEEP(5) / pg_sleep(5) / WAITFOR DELAY\n4. Out-of-band: DNS/HTTP exfiltration\n\n**ORM Güvenlik Denetimi**\n- Django ORM: raw(), extra() kullanımları\n- SQLAlchemy: text() ile ham query, format string birleştirme\n- Sequelize: literal() kullanımı where clauseda\n- Hibernate: createNativeQuery() ile birleştirme\n- TypeORM: getRepository().query() raw kullanımı\n\n**Stored Procedure Güvenliği**\n- SP içindeki dinamik SQL: EXEC(@sql) pattern tespiti\n- SP parametrelerinin tiplendiğini doğrula\n\n**Araç**\n```bash\nsqlmap -u "https://target.com/api?id=1" --batch --level=3\ngrep -rn "raw(|text(|createNativeQuery" --include="*.py" .\n```',
nosqli:'### NoSQL Injection Denetimi\n\n**MongoDB**\n- Operatör injection: {"$gt":""}, {"$ne":null}, {"$where":"1==1"}\n- Auth bypass: {"username":{"$gt":""},"password":{"$gt":""}}\n- Mongoose: schema type enforcement; Mixed type kullanımından kaçın\n\n**Redis**\n- KEYS * ile keyspace enumeration\n- EVAL ile Lua script çalıştırma\n- CONFIG SET ile RCE riski\n- requirepass aktif mi?\n\n**Elasticsearch**\n- _search endpoint authentication zorunlu mu?\n- Script injection riski\n- Unlimited aggregation ile DoS',
dbaccess:'### Veritabanı Erişim Kontrolü\n- Least privilege: SELECT/INSERT/UPDATE yetkisi yeterli mi?\n- Rol ayrımı: readonly, readwrite, admin rolleri\n- PostgreSQL public schema erişimi kısıtlandı mı?\n- Row Level Security (RLS): çok kiracılı mimari\n- Connection string: şifre kaynak koduna gömülmüş mü?\n- Superuser bağlantı: postgres/root user ile bağlantı tespiti',
dbenc:'### Veri Şifreleme & Maskeleme\n- At-rest encryption: TDE aktif mi?\n- In-transit: SSL/TLS zorunlu mu? (sslmode=require)\n- Kolon bazlı şifreleme: TCKN, kart numarası — AES-256-GCM\n- PII maskeleme: log dosyalarında e-posta, telefon maskeleniyor mu?\n- Audit log: DML işlemleri loglanıyor mu?\n- Backup şifreleme ve off-site güvenliği\n- Key management: anahtarlar veritabanından ayrı mı?',
dbconf:'### Veritabanı Yapılandırma Güvenliği\n- Default credential: postgres/postgres, root/root testi\n- Public internet erişimi: 5432, 3306, 27017 açık mı?\n- Firewall: yalnızca uygulama sunucusu IPsine izin ver\n- trust authentication veya 0.0.0.0 binding tespiti\n- Gereksiz extension: xp_cmdshell devre dışı mı?\n- CVE patch durumu ve version güncelliği',
docker:'### Docker & Container Güvenliği\n- Root kullanıcı: USER nobody / USER 1001 kullanımı\n- Read-only filesystem: --read-only flag\n- Capability drop: --cap-drop ALL\n- no-new-privileges güvenlik flag\n- Image tarama: trivy ile CVE tarama\n- Secret yönetimi: ENV yerine Docker secrets / Vault\n- Network isolation: servis bazlı network\n- Distroless / minimal base image',
deps:'### Bağımlılık & Supply Chain Güvenliği\n- npm audit / pip-audit / trivy ile CVE önceliklendirme\n- Güncel olmayan major/minor versiyonlar\n- package-lock.json / poetry.lock commit edildi mi?\n- Typosquatting riski taşıyan paket isimleri\n- Lisans uyumsuzluğu: GPL contamination\n- GitHub Actions: third-party actionların pinned hash kullanımı',
network:'### Ağ Güvenliği & Firewall\n- Gereksiz açık port tespiti: nmap/netstat\n- Ingress/egress kısıtlaması\n- Network segmentasyon: veritabanı ayrı subnet\n- TLS 1.0/1.1 devre dışı mı? Güçlü cipher suite\n- VPN/bastion: doğrudan SSH erişimi kapalı mı?',
logging:'### Loglama, İzleme & Olay Müdahalesi\n- Kritik olaylar loglanıyor mu? (login failure, privilege change)\n- Log tampering koruması: append-only, log signing\n- Anomali tespiti: brute force, unusual access alerting\n- Log retention ve KVKK/GDPR uyumu\n- Incident response playbook varlığı'
};
var APP_LABELS = {
fullstack:'Full-stack web uygulaması', spa:'SPA (React/Vue/Angular)',
api:'REST/GraphQL API servisi', docker:'Docker/Container mimarisi', iot:'IoT/Edge uygulaması'
};
var OUT_LABELS = {
report:'Markdown formatında yapılandırılmış güvenlik raporu',
json:'JSON/SARIF formatında makine okunabilir çıktı',
pr:'Her bulgu için pull request açıklama şablonu',
fix:'Önceliklendirilmiş düzeltme adımları listesi'
};
var PRIO_LABELS = {
all:'Tüm seviyelerdeki bulgular',
critical:'Yalnızca Kritik (CVSS ≥ 9.0)',
high:'Kritik ve Yüksek (CVSS ≥ 7.0)'
};
var state = {
app:'fullstack', layer:'fe',
cats:{'owasp':1,'auth':1,'injection':1,'waf':1,'sqli':1,'nosqli':1,'dbaccess':1},
out:'report', prio:'all'
};
var ALL_IDS = Object.values(CATS).flat().map(function(c){return c.id;});
var TOTAL = ALL_IDS.length;
function allCatsList() { return Object.values(CATS).flat(); }
// PILLS
function bindPills(id, stateKey, cb) {
var cont = document.getElementById(id);
cont.querySelectorAll('.pill').forEach(function(btn) {
btn.classList.toggle('on', btn.getAttribute('data-v') === state[stateKey]);
btn.onclick = function() {
cont.querySelectorAll('.pill').forEach(function(b){b.classList.remove('on');});
btn.classList.add('on');
state[stateKey] = btn.getAttribute('data-v');
if (cb) cb();
renderPrompt();
};
});
}
// LAYER TABS
function renderLayerTabs() {
document.querySelectorAll('#layer-tabs .ltab').forEach(function(tab) {
var l = tab.getAttribute('data-l');
tab.classList.toggle('on', l === state.layer);
tab.onclick = function() {
state.layer = l;
renderLayerTabs();
renderCats();
};
});
}
// CAT GRID
function renderCats() {
var grid = document.getElementById('cat-grid');
grid.innerHTML = '';
var layer = state.layer;
CATS[layer].forEach(function(cat) {
var on = !!state.cats[cat.id];
var card = document.createElement('div');
card.className = 'ccard' + (on ? ' on' : '');
if (on) card.setAttribute('data-layer', layer);
var sevClass = cat.sev==='c'?'sev-c':cat.sev==='h'?'sev-h':'sev-m';
var sevTxt = cat.sev==='c'?'KRİTİK':cat.sev==='h'?'YÜKSEK':'ORTA';
card.innerHTML =
'<div class="ccheck"><svg viewBox="0 0 10 8" fill="none" stroke="white" stroke-width="2"><polyline points="1,4 3.5,7 9,1"/></svg></div>' +
'<div><div class="clbl">' + cat.lbl + '<span class="sev ' + sevClass + '">' + sevTxt + '</span></div>' +
'<div class="cdesc">' + cat.desc + '</div></div>';
card.onclick = function() {
if (state.cats[cat.id]) {
if (Object.keys(state.cats).length > 1) delete state.cats[cat.id];
} else {
state.cats[cat.id] = 1;
}
renderCats();
updateStats();
renderPrompt();
};
grid.appendChild(card);
});
updateProgress();
}
function updateProgress() {
var selected = Object.keys(state.cats).length;
var pct = Math.round(selected / TOTAL * 100);
document.getElementById('progress-fill').style.width = pct + '%';
document.getElementById('progress-txt').textContent = selected + ' / ' + TOTAL + ' kategori seçili';
}
// STATS
function updateStats() {
var all = allCatsList();
var chk = 0, tok = 400, layers = {};
Object.keys(state.cats).forEach(function(id) {
var cat = all.find(function(c){return c.id===id;});
if (!cat) return;
chk += cat.chk; tok += cat.tok;
Object.keys(CATS).forEach(function(k){
if (CATS[k].find(function(c){return c.id===id;})) layers[k]=1;
});
});
var n = Object.keys(state.cats).length;
document.getElementById('h-cat').textContent = n;
document.getElementById('h-chk').textContent = '~' + chk;
document.getElementById('h-lay').textContent = Object.keys(layers).length;
document.getElementById('h-tok').textContent = '~' + tok;
}
// BUILD PROMPT
function buildPrompt() {
var parts = Object.keys(state.cats).map(function(id){return TEXTS[id]||'';}).filter(Boolean);
if (!parts.length) return '';
return '# Web Uygulaması Güvenlik Denetimi — Ajan Talimatları\n\n' +
'## Görev\nSen uzman bir güvenlik mühendisisin. **' + APP_LABELS[state.app] + '** üzerinde kapsamlı güvenlik denetimi gerçekleştireceksin. Her bulgu için somut kanıt (dosya yolu, satır numarası, kod parçası) sunacaksın.\n\n' +
'- Uygulama türü: ' + APP_LABELS[state.app] + '\n' +
'- Öncelik filtresi: ' + PRIO_LABELS[state.prio] + '\n' +
'- Çıktı formatı: ' + OUT_LABELS[state.out] + '\n\n' +
'## Adım 1 — Keşif\nProje yapısını incele: dizin ağacı, bağımlılık dosyaları, Dockerfile, docker-compose.yml, CI/CD config, nginx/apache config, ORM model ve migration dosyaları.\n\n' +
'## Adım 2 — Otomatik Tarama\n```bash\nnpm audit --json 2>/dev/null || pip-audit --format json\ngrep -rn -E "(raw\\(|execute\\(|query\\(.*\\+|SLEEP\\(|WAITFOR)" --include="*.{js,ts,py}" .\ngrep -rn -E "SELECT.*\\+|WHERE.*\\+|INSERT.*\\+" --include="*.{py,js,ts,java,php}" .\ngrep -rn -E "\\$where|\\$gt.*req\\.|\\$ne.*req\\." --include="*.{js,ts}" .\ntrivy image <image_name> --severity HIGH,CRITICAL\n```\n\n' +
'## Adım 3 — Denetim Kategorileri\n\n' + parts.join('\n\n') + '\n\n' +
'## Bulgu Formatı\nHer bulgu için:\n- **Önem**: Kritik / Yüksek / Orta / Düşük\n- **CVSS Skoru**\n- **Katman**: Frontend / Backend / Veritabanı / Altyapı\n- **Konum**: `dosya/yolu:satır`\n- **Açıklama**: teknik detay\n- **Kanıt**: ilgili kod parçası\n- **Sömürü Senaryosu**: saldırgan nasıl kullanır?\n- **Düzeltme**: somut kod/config değişikliği\n- **Referans**: CVE / CWE / OWASP\n\n' +
'## Kurallar\n1. Gerçek kanıt olmadan bulgu raporlama\n2. Her kategoride en az 3 somut kontrol gerçekleştir\n3. Kritik bulgular için acil düzeltme adımlarını önceliklendir\n4. Injection bulgularında güvenli alternatif kod örneği ekle\n\n' +
'## Denetim Sonu\n- Bulgu özet tablosu (Kritik/Yüksek/Orta/Düşük × Katman matrisi)\n- Genel güvenlik puanı (0-100)\n- Kısa vadeli (≤1 hafta): acil patch\n- Orta vadeli (≤1 ay): mimari iyileştirmeler\n- Uzun vadeli: güvenlik olgunluk roadmap\n\n**Denetimi başlat. Önce keşif adımını tamamla ve proje yapısını özetle.**';
}
// RENDER PROMPT WITH SYNTAX HIGHLIGHT + LINE NUMBERS
function renderPrompt() {
var txt = buildPrompt();
var el = document.getElementById('prompt-output');
var lnEl = document.getElementById('line-numbers');
if (!txt) {
el.innerHTML = '<div class="empty"><div class="empty-icon">⚡</div><div class="empty-text">Sol panelden kategori seçin...</div></div>';
lnEl.innerHTML = '';
document.getElementById('line-count').textContent = '0 satır · 0 karakter';
return;
}
var lines = txt.split('\n');
var html = lines.map(function(line) {
var esc = line.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
if (/^# /.test(line)) return '<span class="hl-h1">' + esc + '</span>';
if (/^## /.test(line)) return '<span class="hl-h2">' + esc + '</span>';
if (/^### /.test(line)) return '<span class="hl-h3">' + esc + '</span>';
if (/^```/.test(line)) return '<span class="hl-code">' + esc + '</span>';
esc = esc.replace(/\*\*([^*]+)\*\*/g, '<span class="hl-bold">**$1**</span>');
esc = esc.replace(/`([^`]+)`/g, '<span class="hl-code">`$1`</span>');
if (/^- /.test(line)) esc = '<span class="hl-dash">-</span>' + esc.slice(1);
return esc;
}).join('\n');
el.innerHTML = html;
// Line numbers
var numHtml = lines.map(function(_, i) {
return '<div class="line-num">' + (i+1) + '</div>';
}).join('');
lnEl.innerHTML = numHtml;
document.getElementById('line-count').textContent = lines.length + ' satır · ' + txt.length + ' karakter';
updateStats();
updateProgress();
}
// COPY
document.getElementById('btn-copy').onclick = function() {
var txt = buildPrompt();
if (!txt) { showToast('⚠ Önce kategori seçin'); return; }
navigator.clipboard.writeText(txt).then(function() {
showToast('✓ Prompt kopyalandı!');
}).catch(function() {
var ta = document.createElement('textarea');
ta.value = txt; document.body.appendChild(ta);
ta.select(); document.execCommand('copy');
document.body.removeChild(ta);
showToast('✓ Prompt kopyalandı!');
});
};
// MARKDOWN DOWNLOAD
document.getElementById('btn-md').onclick = function() {
var txt = buildPrompt();
if (!txt) { showToast('⚠ Önce kategori seçin'); return; }
var blob = new Blob([txt], {type: 'text/markdown;charset=utf-8'});
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = 'security-audit-prompt-' + new Date().toISOString().slice(0,10) + '.md';
a.click();
URL.revokeObjectURL(url);
showToast('✓ Markdown indirildi!');
};
// PDF DOWNLOAD
document.getElementById('btn-pdf').onclick = function() {
var txt = buildPrompt();
if (!txt) { showToast('⚠ Önce kategori seçin'); return; }
try {
var jspdf = window.jspdf || (window.jsPDF ? {jsPDF: window.jsPDF} : null);
if (!jspdf && typeof jspdf === 'undefined') {
// fallback: print
var w = window.open('', '_blank');
w.document.write('<html><head><title>Security Audit Prompt</title><style>body{font-family:monospace;font-size:11px;line-height:1.7;padding:2rem;white-space:pre-wrap;}</style></head><body>' + txt.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') + '</body></html>');
w.document.close(); w.print();
showToast('✓ Yazdırma diyaloğu açıldı');
return;
}
var doc = new jspdf.jsPDF({orientation:'p', unit:'mm', format:'a4'});
doc.setFont('courier', 'normal');
doc.setFontSize(8);
var lines = doc.splitTextToSize(txt, 180);
var y = 15, pageH = 285;
for (var i = 0; i < lines.length; i++) {
if (y > pageH) { doc.addPage(); y = 15; }
doc.text(lines[i], 15, y);
y += 4;
}
doc.save('security-audit-prompt-' + new Date().toISOString().slice(0,10) + '.pdf');
showToast('✓ PDF indirildi!');
} catch(e) {
showToast('⚠ PDF hatası, Markdown deneyin');
}
};
// RESET
document.getElementById('btn-reset').onclick = function() {
state.cats = {injection:1, sqli:1};
state.app = 'fullstack'; state.out = 'report'; state.prio = 'all';
bindPills('app-pills','app');
bindPills('out-pills','out');
bindPills('prio-pills','prio');
renderCats(); updateStats(); renderPrompt();
showToast('✓ Sıfırlandı');
};
// SELECT ALL
document.getElementById('all-btn').onclick = function() {
var allOn = ALL_IDS.every(function(id){return !!state.cats[id];});
state.cats = {};
if (!allOn) { ALL_IDS.forEach(function(id){state.cats[id]=1;}); }
else { state.cats = {injection:1,sqli:1}; }
renderCats(); updateStats(); renderPrompt();
};
// TOAST
function showToast(msg) {
var t = document.getElementById('toast');
t.textContent = msg;
t.classList.add('show');
setTimeout(function(){ t.classList.remove('show'); }, 2500);
}
// INIT
bindPills('app-pills','app');
bindPills('out-pills','out');
bindPills('prio-pills','prio');
renderLayerTabs();
renderCats();
updateStats();
renderPrompt();
</script>
</body>
</html>