<!DOCTYPE html>
<html lang="en"><head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>SkillAudit β Detection Rules Explorer (40 Rules, 367 Patterns)</title>
<meta name="description" content="Browse all 40 SkillAudit detection rules. Credential theft, prompt injection, data exfiltration, A2A attacks, OWASP vulnerabilities, and more.">
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#0f0f23;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,monospace;line-height:1.6}
a{color:#00ff88;text-decoration:none}a:hover{text-decoration:underline}
.container{max-width:960px;margin:0 auto;padding:1.5rem}
.header{text-align:center;padding:1.5rem 0 1rem}
.header h1{font-size:1.5rem;color:#888}.header h1 span{color:#00ff88}
.header p{color:#555;font-size:0.9rem;margin-top:0.3rem}
/* Stats bar */
.stats{display:flex;justify-content:center;gap:2rem;margin:1rem 0 1.5rem;flex-wrap:wrap}
.stat{text-align:center}
.stat .val{font-size:1.8rem;font-weight:900;color:#00ff88}
.stat .lbl{font-size:0.7rem;color:#888;text-transform:uppercase;letter-spacing:0.05em}
/* Filter */
.filter-bar{display:flex;gap:0.5rem;margin-bottom:1.5rem;flex-wrap:wrap;align-items:center}
.filter-bar input{flex:1;min-width:200px;background:#0a0a1e;border:1px solid #3a3a6a;border-radius:8px;padding:0.6rem 1rem;color:#e0e0e0;font-family:monospace;font-size:0.9rem;outline:none}
.filter-bar input:focus{border-color:#00ff88}
.filter-btn{background:#1a1a3e;border:1px solid #3a3a6a;color:#888;border-radius:6px;padding:0.4rem 0.8rem;font-family:monospace;font-size:0.8rem;cursor:pointer;transition:all 0.15s}
.filter-btn:hover{border-color:#00ff88;color:#00ff88}
.filter-btn.active{background:#00ff88;color:#000;border-color:#00ff88;font-weight:700}
/* Category sections */
.category{margin-bottom:2rem}
.cat-header{display:flex;align-items:center;gap:0.8rem;padding:0.6rem 0;border-bottom:1px solid #2a2a5a;margin-bottom:0.8rem;cursor:pointer}
.cat-header:hover .cat-name{color:#00ff88}
.cat-icon{font-size:1.3rem}
.cat-name{font-size:1.05rem;font-weight:700;color:#ccc;flex:1}
.cat-count{font-size:0.75rem;color:#555;background:#1a1a3e;padding:0.2rem 0.6rem;border-radius:10px}
/* Rule cards */
.rule{background:#111133;border:1px solid #2a2a5a;border-radius:10px;padding:1rem 1.2rem;margin-bottom:0.6rem;transition:border-color 0.15s}
.rule:hover{border-color:#3a3a7a}
.rule-top{display:flex;align-items:center;gap:0.6rem;flex-wrap:wrap}
.rule-id{font-weight:700;color:#00ff88;font-size:0.85rem;font-family:monospace}
.rule-name{font-weight:600;color:#ddd;font-size:0.95rem}
.sev{display:inline-block;padding:0.15rem 0.5rem;border-radius:5px;font-size:0.65rem;font-weight:800;text-transform:uppercase;color:#000;letter-spacing:0.03em}
.sev-critical{background:#ff0044}.sev-high{background:#ff4444}.sev-medium{background:#ffaa00}.sev-low{background:#88ff00}.sev-info{background:#888}
.pat-count{font-size:0.75rem;color:#555;margin-left:auto}
.rule-desc{color:#aaa;font-size:0.85rem;margin-top:0.5rem;line-height:1.5}
.empty{text-align:center;padding:2rem;color:#555;font-style:italic}
.footer{text-align:center;padding:2rem 0;color:#555;font-size:0.8rem;border-top:1px solid #1a1a3e;margin-top:2rem}
</style>
</head><body>
<div class="container">
<div class="header">
<h1>π‘οΈ Skill<span>Audit</span> β Rules Explorer</h1>
<p>Every detection rule, explained. Know exactly what we scan for.</p>
</div>
<div class="stats" id="stats"></div>
<div class="filter-bar">
<input type="text" id="search" placeholder="Search rules by name, ID, or description..." oninput="filterRules()">
<button class="filter-btn active" data-sev="all" onclick="toggleSev(this)">All</button>
<button class="filter-btn" data-sev="critical" onclick="toggleSev(this)">Critical</button>
<button class="filter-btn" data-sev="high" onclick="toggleSev(this)">High</button>
<button class="filter-btn" data-sev="medium" onclick="toggleSev(this)">Medium</button>
</div>
<div id="rules-list"></div>
<div class="footer">
<a href="/">β Back to SkillAudit</a> Β· <a href="/docs">API Docs</a> Β· <a href="/integrations">Integrations</a><br>
Built by <a href="https://moltbook.com/u/Megamind_0x">Megamind_0x</a> π§
</div>
</div>
<script>
const CAT_META = {
credential_theft: {icon:'π',label:'Credential Theft'},
credential_reference: {icon:'π',label:'Credential Reference'},
data_exfiltration: {icon:'π€',label:'Data Exfiltration'},
prompt_injection: {icon:'π',label:'Prompt Injection'},
code_execution: {icon:'β‘',label:'Code Execution'},
network: {icon:'π',label:'Network'},
filesystem: {icon:'π',label:'Filesystem'},
obfuscation: {icon:'π',label:'Obfuscation'},
privilege_escalation: {icon:'π',label:'Privilege Escalation'},
crypto_theft: {icon:'π°',label:'Crypto Theft'},
agent_manipulation: {icon:'π§ ',label:'Agent Manipulation'},
evasion: {icon:'π»',label:'Evasion'},
persistence: {icon:'π',label:'Persistence'},
supply_chain: {icon:'π¦',label:'Supply Chain'},
reconnaissance: {icon:'π',label:'Reconnaissance'},
denial_of_service: {icon:'π₯',label:'Denial of Service'},
};
let allRules = [];
let activeSev = 'all';
function esc(s){const d=document.createElement('div');d.textContent=s;return d.innerHTML}
function toggleSev(btn){
activeSev = btn.dataset.sev;
document.querySelectorAll('.filter-btn').forEach(b=>b.classList.toggle('active',b.dataset.sev===activeSev));
filterRules();
}
function filterRules(){
const q = document.getElementById('search').value.toLowerCase();
const list = document.getElementById('rules-list');
// Group by category
const groups = {};
for(const r of allRules){
// Filter by severity
if(activeSev !== 'all' && r.severity !== activeSev) continue;
// Filter by search
if(q && !r.id.toLowerCase().includes(q) && !r.name.toLowerCase().includes(q) && !r.description.toLowerCase().includes(q) && !r.category.toLowerCase().includes(q)) continue;
if(!groups[r.category]) groups[r.category] = [];
groups[r.category].push(r);
}
if(Object.keys(groups).length === 0){
list.innerHTML = '<div class="empty">No rules match your filter.</div>';
return;
}
// Sort categories by rule count (most rules first)
const sorted = Object.entries(groups).sort((a,b) => b[1].length - a[1].length);
let html = '';
for(const [cat, rules] of sorted){
const meta = CAT_META[cat] || {icon:'π',label:cat.replace(/_/g,' ')};
html += `<div class="category">
<div class="cat-header">
<span class="cat-icon">${meta.icon}</span>
<span class="cat-name">${esc(meta.label)}</span>
<span class="cat-count">${rules.length} rule${rules.length!==1?'s':''}</span>
</div>`;
for(const r of rules){
html += `<div class="rule">
<div class="rule-top">
<span class="sev sev-${r.severity}">${r.severity}</span>
<span class="rule-id">${esc(r.id)}</span>
<span class="rule-name">${esc(r.name)}</span>
<span class="pat-count">${r.patternCount} pattern${r.patternCount!==1?'s':''}</span>
</div>
<div class="rule-desc">${esc(r.description)}</div>
</div>`;
}
html += '</div>';
}
list.innerHTML = html;
}
async function load(){
try{
const res = await fetch('/rules.json');
const d = await res.json();
allRules = d.rules || [];
// Stats
const total = allRules.length;
const patterns = allRules.reduce((s,r) => s + r.patternCount, 0);
const cats = new Set(allRules.map(r => r.category)).size;
const crit = allRules.filter(r => r.severity === 'critical').length;
document.getElementById('stats').innerHTML = `
<div class="stat"><div class="val">${total}</div><div class="lbl">Rules</div></div>
<div class="stat"><div class="val">${patterns}</div><div class="lbl">Patterns</div></div>
<div class="stat"><div class="val">${cats}</div><div class="lbl">Categories</div></div>
<div class="stat"><div class="val">${crit}</div><div class="lbl">Critical</div></div>
`;
filterRules();
}catch(e){
document.getElementById('rules-list').innerHTML = '<div class="empty">Failed to load rules: '+esc(e.message)+'</div>';
}
}
load();
</script>
</body></html>