Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/audit.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Audit checks — OBLITERATUS</title>
<meta name="description" content="The 12 OBLITERATUS attack-surface controls, their severities, the Mimikatz module and MITRE technique each maps to, and the exit-code contract.">
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="favicon.ico" sizes="48x48">
<link rel="stylesheet" href="site.css">
</head>
<body>
Expand Down
2 changes: 2 additions & 0 deletions docs/capabilities.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Capabilities &amp; gates — OBLITERATUS</title>
<meta name="description" content="Every OBLITERATUS API route, what it does, the flag that unlocks it and the MITRE ATT&CK technique it maps to.">
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="favicon.ico" sizes="48x48">
<link rel="stylesheet" href="site.css">
</head>
<body>
Expand Down
Binary file added docs/favicon.ico
Binary file not shown.
8 changes: 8 additions & 0 deletions docs/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OBLITERATUS — Windows credential exposure auditor</title>
<meta name="description" content="OBLITERATUS audits a Windows host for the credential-exposure conditions Mimikatz depends on, maps each finding to the MITRE technique, and ships the offensive modules compiled in but locked behind explicit flags.">
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="favicon.ico" sizes="48x48">
<link rel="stylesheet" href="site.css">
</head>
<body>
Expand Down
2 changes: 2 additions & 0 deletions docs/security.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Security model — OBLITERATUS</title>
<meta name="description" content="The OBLITERATUS threat model: what v1 exposed, what v2 does instead, where the trust boundary sits, and the two limits that are documented rather than hidden.">
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="alternate icon" href="favicon.ico" sizes="48x48">
<link rel="stylesheet" href="site.css">
</head>
<body>
Expand Down
108 changes: 82 additions & 26 deletions docs/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,23 @@
--bg: #0c0e12;
--bg-1: #12151b;
--bg-2: #171b23;
--bg-3: #1c212b;
--fg: #dfe4ec;
--fg-dim: #939cab;
--line: #262c37;
/* Hairlines, not borders: pure white at ~6% reads as a soft edge on the
dark palette; the old #262c37 was a saturated 1px line that boxed
everything in and made the cards look flat and dirty. */
--line: rgba(255, 255, 255, 0.06);
--line-strong: rgba(255, 255, 255, 0.1);
--accent: #e2544a;
--accent-2: #f0a04b;
--accent-dim: rgba(226, 84, 74, 0.14);
--accent-2-dim: rgba(240, 160, 75, 0.13);
--ok: #4fb477;
--ok-dim: rgba(79, 180, 119, 0.13);
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
--shadow-md: 0 2px 4px rgba(0, 0, 0, 0.3), 0 10px 28px rgba(0, 0, 0, 0.28);
--shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.35), 0 18px 44px rgba(0, 0, 0, 0.36);
--mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
--sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
Expand All @@ -25,14 +36,25 @@ body {
font: 16px/1.65 var(--sans);
}

/* A single soft glow at the top of the page. Costs nothing (no image, no
request) and stops the background from reading as a flat dead slab; the
red is the brand accent, kept faint so text contrast does not move. */
body::before {
content: "";
position: fixed;
inset: 0;
background: radial-gradient(900px 460px at 50% -8%, rgba(226, 84, 74, 0.07), transparent 70%);
pointer-events: none;
z-index: -1;
}

a { color: var(--accent-2); text-decoration: none; }
a:hover { text-decoration: underline; }

code, kbd, pre, .mono { font-family: var(--mono); }

code {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: 4px;
padding: 1px 5px;
font-size: 0.88em;
Expand All @@ -42,11 +64,12 @@ pre {
background: var(--bg-1);
border: 1px solid var(--line);
border-left: 3px solid var(--accent);
border-radius: 6px;
border-radius: 10px;
padding: 14px 16px;
overflow-x: auto;
font-size: 13.5px;
line-height: 1.6;
box-shadow: var(--shadow-sm);
}
pre code { background: none; border: 0; padding: 0; font-size: inherit; }

Expand All @@ -56,10 +79,11 @@ pre code { background: none; border: 0; padding: 0; font-size: inherit; }

header.top {
border-bottom: 1px solid var(--line);
background: rgba(12, 14, 18, 0.92);
background: rgba(12, 14, 18, 0.82);
position: sticky;
top: 0;
backdrop-filter: blur(6px);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
z-index: 10;
}

Expand Down Expand Up @@ -93,6 +117,7 @@ footer {
color: var(--fg-dim);
font-size: 13.5px;
padding: 22px 0 40px;
margin-top: 20px;
}
footer .wrap { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; }

Expand All @@ -106,9 +131,11 @@ h1 {
}
h2 {
font-size: 22px;
margin: 40px 0 12px;
padding-top: 14px;
border-top: 1px solid var(--line);
margin: 44px 0 12px;
padding-top: 20px;
/* Kept as a section rule — long pages need one — but at 5% white it reads
as a soft break, not a line ruled across the page. */
border-top: 1px solid rgba(255, 255, 255, 0.05);
}
h3 { font-size: 17px; margin: 26px 0 8px; }
p { margin: 12px 0; }
Expand All @@ -119,24 +146,32 @@ p { margin: 12px 0; }

/* -------------------------------------------------------------------- pills */

/* Pills carry a tint, not a stroke: a 1px box around a 12px chip reads as a
cage. The background does the work, and the tint doubles as the meaning. */
.pills { display: flex; gap: 8px; flex-wrap: wrap; margin: 14px 0 0; }
.pill {
font-family: var(--mono);
font-size: 12px;
color: var(--fg-dim);
border: 1px solid var(--line);
background: var(--bg-1);
background: rgba(255, 255, 255, 0.04);
border-radius: 999px;
padding: 3px 10px;
white-space: nowrap;
}
.pill.hot { color: var(--accent); border-color: #40221f; }
.pill.warm { color: var(--accent-2); border-color: #3d2c19; }
.pill.ok { color: var(--ok); border-color: #1e3a2a; }
.pill.hot { color: var(--accent); background: var(--accent-dim); }
.pill.warm { color: var(--accent-2); background: var(--accent-2-dim); }
.pill.ok { color: var(--ok); background: var(--ok-dim); }

/* ------------------------------------------------------------------- tables */

.tw { overflow-x: auto; margin: 16px 0; border: 1px solid var(--line); border-radius: 6px; }
.tw {
overflow-x: auto;
margin: 16px 0;
border: 1px solid var(--line);
border-radius: 10px;
background: var(--bg-1);
box-shadow: var(--shadow-sm);
}

table { border-collapse: collapse; width: 100%; font-size: 14.5px; }

Expand All @@ -148,7 +183,7 @@ caption {
padding: 10px 12px 0;
}

th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--line); vertical-align: top; }
th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid rgba(255, 255, 255, 0.05); vertical-align: top; }

thead th {
background: var(--bg-2);
Expand All @@ -161,7 +196,7 @@ thead th {
}

tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--bg-1); }
tbody tr:hover { background: rgba(255, 255, 255, 0.025); }

td.rt, th.rt { font-family: var(--mono); font-size: 13px; white-space: nowrap; }
td.sev { font-family: var(--mono); font-size: 12.5px; white-space: nowrap; }
Expand All @@ -176,22 +211,43 @@ td.sev { font-family: var(--mono); font-size: 12.5px; white-space: nowrap; }

/* --------------------------------------------------------------- components */

.shot { width: 100%; border: 1px solid var(--line); border-radius: 8px; margin: 20px 0 6px; }
.shot { width: 100%; border: 1px solid var(--line); border-radius: 10px; margin: 20px 0 6px; box-shadow: var(--shadow-sm); }

/* Callouts keep the left accent bar — it carries the meaning (amber = note,
red = warning) — but drop the full grey stroke that used to box them. */
.callout {
border: 1px solid var(--line);
border-left: 3px solid var(--accent-2);
background: var(--bg-1);
border-radius: 6px;
padding: 14px 16px;
background: linear-gradient(90deg, var(--accent-2-dim), rgba(240, 160, 75, 0.03));
border-radius: 10px;
padding: 14px 18px;
margin: 18px 0;
font-size: 14.5px;
}
.callout.warn { border-left-color: var(--accent); }
.callout.warn {
border-left-color: var(--accent);
background: linear-gradient(90deg, var(--accent-dim), rgba(226, 84, 74, 0.03));
}
.callout strong { color: var(--fg); }

.cardgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 14px; margin: 18px 0; }
.card { border: 1px solid var(--line); background: var(--bg-1); border-radius: 6px; padding: 14px 16px; }
/* Cards: the old rule drew a 1px #262c37 box on a #12151b background — the
same value family, so the edge read as dirt rather than as a line, and the
flat fill gave the card no place to sit. Now the surface itself does the
separating: a vertical gradient, a barely-there hairline, and a real
shadow. Hover lifts the card and the accent arrives as a tint, not a cage. */
.cardgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 16px; margin: 18px 0; }
.card {
border: 1px solid var(--line);
background: linear-gradient(180deg, var(--bg-3), var(--bg-1));
border-radius: 12px;
padding: 16px 18px;
box-shadow: var(--shadow-sm);
transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}
.card:hover {
transform: translateY(-2px);
border-color: var(--line-strong);
box-shadow: var(--shadow-md);
}
.card h3 { margin: 0 0 6px; font-size: 15px; }
.card p { margin: 0; font-size: 14px; color: var(--fg-dim); }

Expand All @@ -206,14 +262,14 @@ td.sev { font-family: var(--mono); font-size: 12.5px; white-space: nowrap; }
width: 22px;
height: 22px;
border-radius: 50%;
border: 1px solid var(--line);
background: var(--bg-2);
background: var(--bg-3);
color: var(--fg-dim);
font-family: var(--mono);
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: inset 0 0 0 1px var(--line);
}

dl.entry { margin: 0; }
Expand Down
53 changes: 30 additions & 23 deletions docs/site.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
// Reads the newest release from the GitHub API so the download table never
// goes stale between releases.
// goes stale between releases. One request, one origin.
//
// The SHA-256 column comes from the `digest` field that every release asset
// already carries in that same response. It deliberately does NOT fetch
// checksums.txt to parse it: that file sits behind
// github.com/.../releases/download/..., which answers with a 302 to a CDN that
// sends no Access-Control-Allow-Origin — so a browser refuses to read it and
// the console reports "blocked by CORS policy". (Following the redirect from
// the API asset endpoint does not help either: the *final* response is the one
// the browser checks, and it has no ACAO header.) The api.github.com origin
// does send `Access-Control-Allow-Origin: *`, so everything on this page is
// readable in exactly one request, from one origin.
//
// Every string that comes from the API is written with textContent, never
// innerHTML, and every href is resolved and checked against this repo's own
Expand Down Expand Up @@ -55,21 +66,24 @@
return (bytes / (1024 * 1024)).toFixed(1) + " MiB";
}

function parseSums(text) {
var out = {};
String(text || "").split("\n").forEach(function (line) {
var m = line.trim().split(/\s+/);
if (m.length >= 2) out[m[1]] = m[0];
});
return out;
// GitHub reports digests as "sha256:<64 hex>". Anything else — absent, a
// different algorithm, a truncated value — reads as no hash rather than as
// a plausible-looking one, so a wrong column never looks right.
var NO_HASH = "\u2014";
function hashOf(digest) {
if (typeof digest !== "string" || digest.indexOf("sha256:") !== 0) return NO_HASH;
var hex = digest.slice(7);
return /^[0-9a-f]{64}$/.test(hex) ? hex : NO_HASH;
}

function render(release, sums) {
function render(release) {
var assets = (release.assets || []).filter(function (a) {
return safeURL(a.browser_download_url);
});
if (!assets.length) throw new Error("no release asset survived the URL check");

var withHash = assets.filter(function (a) { return hashOf(a.digest) !== NO_HASH; }).length;

var table = el("table");
table.appendChild(el("caption", "Latest release: " + release.tag_name +
", published " + String(release.published_at || "").slice(0, 10)));
Expand All @@ -91,34 +105,27 @@
tr.appendChild(td);

tr.appendChild(el("td", human(a.size)));
tr.appendChild(el("td", sums[a.name] || "\u2014", "hash"));
tr.appendChild(el("td", hashOf(a.digest), "hash"));
tbody.appendChild(tr);
});
table.appendChild(tbody);

while (mount.firstChild) mount.removeChild(mount.firstChild);
mount.appendChild(table);
if (fallback) fallback.hidden = true;
status.textContent = "Read live from the GitHub API, so this table follows " +
"the newest release without a rebuild.";

status.textContent = withHash === assets.length
? "Read live from the GitHub API — filenames, sizes and SHA-256 digests all come from this one response."
: "Read live from the GitHub API. " + withHash + " of " + assets.length +
" assets carry an SHA-256 digest in the release metadata; the rest show an em dash.";
}

fetch(API, { headers: { Accept: "application/vnd.github+json" } })
.then(function (r) {
if (!r.ok) throw new Error("the API answered " + r.status);
return r.json();
})
.then(function (release) {
var sums = (release.assets || []).filter(function (a) {
return a.name === "checksums.txt";
})[0];
var sumsURL = sums ? safeURL(sums.browser_download_url) : null;
if (!sumsURL) return render(release, {});
return fetch(sumsURL)
.then(function (r) { return r.ok ? r.text() : ""; })
.then(function (t) { render(release, parseSums(t)); })
.catch(function () { render(release, {}); });
})
.then(render)
.catch(function (err) {
status.textContent = "Could not build the live table automatically (" +
err.message + "). The pinned list below is the v2.0.2 snapshot; the " +
Expand Down
Loading
Loading