Files
SKELETONKEY/docs/app.js
T
leviathan 5071ad4ba9 site: marketing-grade redesign with --explain showcase + animated hero
Full rewrite of docs/index.html + style.css + new app.js + OG card.

Hero
  - Animated gradient mesh background (3 drifting blurred blobs;
    respects prefers-reduced-motion).
  - Space Grotesk display wordmark with subtle white→gray gradient.
  - Eyebrow chip with pulsing dot showing current release.
  - Type-on-load install command with blinking cursor in a faux-terminal
    chrome (traffic-light dots, title bar, copy button).
  - Stats row that counts up from 0 on first paint: 31 modules, 10 KEV,
    119 detection rules, 88 tests.
  - Primary CTA + secondary 'See --explain in action' + GitHub link.

Trust strip
  - 'Grounded in authoritative sources' row: CISA KEV, NVD CVE API,
    MITRE ATT&CK, kernel.org stable tree, Debian Security Tracker,
    NIST CWE. Establishes the federal-data-source provenance.

--explain showcase (flagship section)
  - Big terminal mockup that types out a real --explain nf_tables run
    line-by-line on scroll-into-view (45-95ms per line, easing).
  - Four annotation cards explaining each part: triage metadata,
    host fingerprint, detect() trace, OPSEC footprint.

Bento grid (8 feature cards in a varied 3-col layout)
  - Auto-pick safest exploit (large card with code sample)
  - 119 detection rules (with animated per-format coverage bars)
  - CISA KEV prioritized (red-accented)
  - OPSEC notes per exploit
  - One host fingerprint, every module (large card with struct excerpt)
  - JSON for pipelines
  - No SaaS, no telemetry
  - Verifier ready (Vagrant + Parallels)

Module corpus
  - Same green/yellow split as before, but every KEV-listed module pill
    now carries a ★ prefix + red-tinted border so 'actively exploited
    in the wild' is visible at a glance.

Audience
  - 4 colored cards (red/blue/gray/purple) — pentesters, SOC, sysadmins,
    researchers — each with a deep link to the right doc.

Verified-vs-claimed honesty callout
  - Featured gradient-bordered card restating the no-fabricated-offsets
    bar. ✓ icon, project's defining trust claim.

Quickstart
  - Tabbed: install / scan / explain / auto / detect-rules. Each tab is
    a short, copy-ready snippet with inline comments.

Roadmap timeline
  - Three columns: shipped / in flight / next. Shipped lists every
    feature from the last several sessions (--explain, OPSEC, CWE/
    ATT&CK/KEV pipeline, 119 rules, host refactor, 88 tests, drift
    detector, VM scaffold). Next lists arm64 musl, mass-fleet
    aggregator, SIEM query templates, CI hardening.

Footer
  - Four-column gradient footer (Brand / Project / Docs / Ethics) +
    bottom bar with credits to original PoC authors + license + repo
    link.

Tech
  - Typography: Inter (UI) + JetBrains Mono (code) + Space Grotesk
    (display wordmark), all via Google Fonts with display=swap.
  - Palette: deep purple-tinted dark (#07070d) + emerald accent
    (#10b981) + cyan secondary (#06b6d4) + KEV-red (#ef4444) +
    violet (#a855f7) for threat-intel framing.
  - CSS: ~28KB unminified, custom-properties driven; gracefully
    degrades to single-column on every grid section at narrow widths.
  - JS: ~8KB vanilla, no frameworks. Respects prefers-reduced-motion
    everywhere. IntersectionObserver-driven scroll reveal and
    stat-count-up.
  - OG image: hand-authored SVG → rsvg-convert → 1200x630 PNG
    (121KB). Renders cleanly when shared on Twitter/LinkedIn/Slack.
  - 4 new files: app.js, og.svg, og.png; rewrites: index.html, style.css.

Refreshed content:
  - v0.5.0 → v0.6.0 throughout.
  - '28 verified modules' → 31.
  - Adds KEV cross-ref, --explain, OPSEC, ATT&CK/CWE callouts that
    didn't exist in the previous version.

HTML structure validated balanced (Python html.parser smoke test).
2026-05-23 11:42:56 -04:00

214 lines
8.4 KiB
JavaScript

/* SKELETONKEY landing page — interactive bits.
* No frameworks. ~150 lines vanilla JS. Respects prefers-reduced-motion. */
(function () {
'use strict';
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
/* ============================================================
* 1. typed install command in the hero
* ============================================================ */
const installCmd =
'curl -sSL https://github.com/KaraZajac/SKELETONKEY/releases/latest/download/install.sh | sh \\\n && skeletonkey --auto --i-know';
const typedEl = document.getElementById('install-typed');
const cursorEl = document.getElementById('install-cursor');
function typeInstall(cb) {
if (reduceMotion) {
typedEl.textContent = installCmd;
if (cursorEl) cursorEl.style.display = 'none';
if (cb) cb();
return;
}
let i = 0;
function step() {
typedEl.textContent = installCmd.slice(0, i);
i++;
if (i <= installCmd.length) {
setTimeout(step, 18 + Math.random() * 22);
} else {
if (cursorEl) {
// keep cursor blinking for 2s, then hide
setTimeout(() => { cursorEl.style.display = 'none'; }, 2000);
}
if (cb) cb();
}
}
step();
}
/* ============================================================
* 2. copy install command
* ============================================================ */
window.copyInstall = function (btn) {
const text = installCmd;
navigator.clipboard.writeText(text).then(() => {
const original = btn.textContent;
btn.textContent = 'copied!';
btn.classList.add('copied');
setTimeout(() => {
btn.textContent = original;
btn.classList.remove('copied');
}, 1500);
}).catch(() => {
btn.textContent = '(copy failed)';
setTimeout(() => { btn.textContent = 'copy'; }, 1500);
});
};
/* ============================================================
* 3. stat count-up animation on view
* ============================================================ */
function countUp(el) {
const target = parseInt(el.dataset.target, 10);
if (!target || reduceMotion) { el.textContent = target; return; }
const dur = 1100;
const start = performance.now();
function tick(now) {
const t = Math.min((now - start) / dur, 1);
// ease-out
const v = Math.round(target * (1 - Math.pow(1 - t, 3)));
el.textContent = v;
if (t < 1) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
}
/* ============================================================
* 4. --explain terminal: line-by-line reveal
* ============================================================ */
const explainHTML = [
'\n',
'<span class="t-rule">════════════════════════════════════════════════════</span>\n',
' <span class="t-mod">nf_tables</span> <span class="t-cve">CVE-2024-1086</span>\n',
'<span class="t-rule">════════════════════════════════════════════════════</span>\n',
' <span class="t-summary">nf_tables nft_verdict_init UAF (cross-cache) → arbitrary kernel R/W</span>\n',
'\n',
'<span class="t-header">WEAKNESS</span>\n',
' <span class="t-cwe">CWE-416</span>\n',
' <span class="t-label">MITRE ATT&amp;CK:</span> <span class="t-tech">T1068</span>\n',
'\n',
'<span class="t-header">THREAT INTEL</span>\n',
' <span class="t-kev-yes">★ In CISA Known Exploited Vulnerabilities catalog (added 2024-05-30)</span>\n',
' <span class="t-label">Affected:</span> 5.14 ≤ K, fixed mainline 6.8; backports: 6.7.2 / 6.6.13 / 6.1.74 / 5.15.149 / 5.10.210\n',
'\n',
'<span class="t-header">HOST FINGERPRINT</span>\n',
' <span class="t-label">kernel:</span> 5.15.0-43-generic (x86_64)\n',
' <span class="t-label">distro:</span> Ubuntu 22.04.5 LTS\n',
' <span class="t-label">unpriv userns:</span> ALLOWED\n',
'\n',
'<span class="t-header">DETECT() TRACE (live; reads ctx->host, fires gates)</span>\n',
'<span class="t-i">[i] nf_tables: kernel 5.15.0-43-generic in vulnerable range</span>\n',
'<span class="t-i">[i] nf_tables: userns gate passed</span>\n',
'<span class="t-i">[i] nf_tables: nft_verdict_init reachable; bug is fireable here</span>\n',
'\n',
'<span class="t-header">VERDICT:</span> <span class="t-vuln">VULNERABLE</span>\n',
' -&gt; bug is reachable. The OPSEC section below shows what a successful\n',
' exploit() would leave on this host.\n',
'\n',
'<span class="t-header">OPSEC FOOTPRINT (what exploit() leaves on this host)</span>\n',
' unshare(CLONE_NEWUSER|CLONE_NEWNET) + nfnetlink batch (NEWTABLE +\n',
' NEWCHAIN/LOCAL_OUT + NEWSET verdict-key + NEWSETELEM malformed NFT_GOTO)\n',
' committed twice. msg_msg cg-96 groom; dmesg: KASAN double-free on vuln\n',
' kernels. Cleanup is finisher-gated; no persistent files on success.\n',
'\n',
'<span class="t-header">DETECTION COVERAGE (rules embedded in this binary)</span>\n',
' <span class="t-check">✓</span> auditd <span class="t-check">✓</span> sigma <span class="t-check">✓</span> yara <span class="t-check">✓</span> falco\n',
];
function playExplain(el) {
if (reduceMotion) { el.innerHTML = explainHTML.join(''); return; }
let i = 0;
el.innerHTML = '';
function step() {
if (i >= explainHTML.length) return;
el.innerHTML += explainHTML[i];
i++;
// pause longer on blank lines to feel like real terminal output
const next = explainHTML[i - 1];
const delay = next === '\n' ? 60 : (45 + Math.random() * 50);
setTimeout(step, delay);
}
step();
}
/* ============================================================
* 5. quickstart tabs
* ============================================================ */
function initTabs() {
const tabs = document.querySelectorAll('.tab');
const panels = document.querySelectorAll('.tab-panel');
tabs.forEach((t) => {
t.addEventListener('click', () => {
const tab = t.dataset.tab;
tabs.forEach((x) => x.classList.toggle('active', x === t));
panels.forEach((p) => p.classList.toggle('active', p.dataset.tab === tab));
});
});
}
/* ============================================================
* 6. scroll-triggered reveal + first-time triggers
* ============================================================ */
function initReveal() {
if (!('IntersectionObserver' in window) || reduceMotion) {
document.querySelectorAll('.reveal').forEach((el) => el.classList.add('in'));
// also fire one-shot animations immediately
countAllStats();
const explainEl = document.getElementById('explain-output');
if (explainEl) playExplain(explainEl);
return;
}
const obs = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
e.target.classList.add('in');
// fire one-shot effects when the right section becomes visible
if (e.target.id === 'explain') {
const out = e.target.querySelector('#explain-output');
if (out && !out.dataset.played) {
out.dataset.played = '1';
playExplain(out);
}
}
obs.unobserve(e.target);
}
});
}, { threshold: 0.15 });
document.querySelectorAll('.reveal').forEach((el) => obs.observe(el));
}
function countAllStats() {
document.querySelectorAll('.stat-chip .num').forEach(countUp);
}
/* fire the stats count-up as soon as the hero shows */
function initStatsCountUp() {
if (!('IntersectionObserver' in window) || reduceMotion) {
countAllStats();
return;
}
const row = document.getElementById('stats-row');
if (!row) return;
const o = new IntersectionObserver((es) => {
if (es[0].isIntersecting) {
countAllStats();
o.disconnect();
}
});
o.observe(row);
}
/* ============================================================
* boot
* ============================================================ */
document.addEventListener('DOMContentLoaded', () => {
typeInstall();
initTabs();
initReveal();
initStatsCountUp();
});
})();