index.astro (12448B)
1 --- 2 import Base from '../layouts/Base.astro'; 3 import HeroLoop from '../components/HeroLoop.astro'; 4 import Marquee from '../components/Marquee.astro'; 5 import Operator from '../components/Operator.astro'; 6 import SectionHeader from '../components/SectionHeader.astro'; 7 import RecordRow from '../components/RecordRow.astro'; 8 import Callout from '../components/Callout.astro'; 9 import { CATEGORIES, categoryOf } from '../lib/taxonomy'; 10 import { allSheets, categoryCounts, sheetHref } from '../lib/sheets'; 11 import { payloadsByTopic } from '../lib/payloads'; 12 import { internalBySection, internalRootPages } from '../lib/internal'; 13 import { hackTricksBySection } from '../lib/hacktricks'; 14 import { url } from '../lib/url'; 15 16 const sheets = await allSheets(); 17 const counts = await categoryCounts(); 18 const total = sheets.length; 19 const domains = CATEGORIES.filter((c) => (counts[c.slug] ?? 0) > 0).length; 20 21 const payloadTopics = await payloadsByTopic(); 22 const payloadPages = payloadTopics.reduce((n, g) => n + g.count, 0); 23 24 const internalSections = await internalBySection(); 25 const internalRoot = await internalRootPages(); 26 const internalPages = internalSections.reduce((n, g) => n + g.count, 0) + internalRoot.length; 27 28 const hackTricksSections = await hackTricksBySection(); 29 const hackTricksPages = hackTricksSections.reduce((n, g) => n + g.count, 0); 30 31 // Latest sheet date, for the operator readout. 32 const latest = sheets 33 .map((s) => s.data.updated) 34 .filter((d): d is string => !!d) 35 .sort() 36 .at(-1); 37 const updatedLabel = latest ? `${latest.slice(0, 7).replace('-', '.')} · latest` : '2026 · current'; 38 39 // ── Hero: six flagship shelves, one accent each (a designed showcase; the 40 // full taxonomy is in the shelves grid below). Real routes + counts. ── 41 const HERO = [ 42 { slug: 'active-directory', accent: 'pine' }, 43 { slug: 'enumeration', accent: 'love' }, 44 { slug: 'exploitation', accent: 'gold' }, 45 { slug: 'privilege-escalation', accent: 'foam' }, 46 { slug: 'web', accent: 'iris' }, 47 { slug: 'dfir', accent: 'rose' }, 48 ]; 49 const heroShelves = HERO.map((h, i) => { 50 const c = categoryOf(h.slug); 51 return { n: String(i + 1).padStart(2, '0'), label: c.tag, path: url(h.slug), accent: h.accent, desc: c.blurb }; 52 }); 53 54 // ── Manifest band lanes ── 55 const laneTitles = [ 56 { n: '01', title: 'DΛΣMӨП', accent: 'iris' }, 57 { n: '02', title: 'Cheatsheets', accent: 'foam' }, 58 { n: '03', title: 'Offline search', accent: 'gold' }, 59 { n: '04', title: 'Copy-ready', accent: 'pine' }, 60 { n: '05', title: `${CATEGORIES.length} shelves`, accent: 'love' }, 61 { n: '06', title: 'Zero fluff', accent: 'rose' }, 62 ]; 63 const laneMeta = [ 64 { tag: 'Sys', text: 'Open notebook', accent: 'iris' }, 65 { tag: 'Search', text: 'Pagefind · offline', accent: 'love' }, 66 { tag: 'Sheets', text: `${total} · curated`, accent: 'foam' }, 67 { tag: 'Theme', text: 'Rosé Pine Dawn', accent: 'gold' }, 68 { tag: 'Field', text: 'AD · web · privesc', accent: 'pine' }, 69 { tag: 'Mirror', text: 'Three upstream trees', accent: 'love' }, 70 { tag: 'Doctrine', text: 'Terse, not thin', accent: 'rose' }, 71 ]; 72 73 // ── Index rows: recent sheets, bucketed for the client-side filter ── 74 const byDate = [...sheets].sort((a, b) => (b.data.updated ?? '').localeCompare(a.data.updated ?? '')); 75 // Recent, but spread across shelves: cap 3 per category so one busy shelf 76 // (the CPTS workflow, freshest by date) does not fill the whole index and the 77 // Sheets / Workflow filters both have rows. 78 const perCat: Record<string, number> = {}; 79 const recentRaw: typeof byDate = []; 80 for (const entry of byDate) { 81 const c = entry.data.category; 82 perCat[c] = (perCat[c] ?? 0) + 1; 83 if (perCat[c] > 3) continue; 84 recentRaw.push(entry); 85 if (recentRaw.length >= 24) break; 86 } 87 const recent = recentRaw 88 .map((entry, i) => { 89 const cat = categoryOf(entry.data.category); 90 const bucket = entry.data.category === 'pentest-workflow' ? 'workflow' : 'sheets'; 91 const metaVal = entry.data.updated ? entry.data.updated.replace(/-/g, '.') : entry.data.difficulty.toUpperCase(); 92 const tags = entry.data.tags.slice(0, 2).join(' · '); 93 return { 94 href: url(sheetHref(entry)), 95 n: String(i + 1).padStart(2, '0'), 96 title: entry.data.title, 97 kind: cat.tag, 98 accent: cat.accent, 99 meta: metaVal, 100 excerpt: entry.data.description || `${cat.title} — terse, tested, kept current.`, 101 tagline: [cat.title, tags].filter(Boolean).join(' · '), 102 bucket, 103 }; 104 }); 105 106 // ── Shelves grid: the full taxonomy ── 107 const shelfCards = CATEGORIES.map((cat, i) => ({ 108 n: String(i + 1).padStart(2, '0'), 109 slug: cat.slug, 110 label: cat.title, 111 accent: cat.accent, 112 count: counts[cat.slug] ?? 0, 113 })); 114 115 // ── Mirrors manifest: the two swisskyrepo trees + their largest sub-trees ── 116 const topPayloads = [...payloadTopics].sort((a, b) => b.count - a.count).slice(0, 2); 117 const topInternal = [...internalSections].sort((a, b) => b.count - a.count).slice(0, 2); 118 const mirrorRows = [ 119 { n: '01', path: 'payloads/', count: payloadPages }, 120 { n: '02', path: 'internal/', count: internalPages }, 121 { n: '03', path: 'hacktricks/', count: hackTricksPages }, 122 { n: '04', path: `payloads/${topPayloads[0]?.slug ?? ''}/`, count: topPayloads[0]?.count ?? 0 }, 123 { n: '05', path: `internal/${topInternal[0]?.slug ?? ''}/`, count: topInternal[0]?.count ?? 0 }, 124 { n: '06', path: `hacktricks/section/${hackTricksSections[0]?.slug ?? ''}/`, count: hackTricksSections[0]?.count ?? 0 }, 125 ]; 126 --- 127 <Base> 128 <!-- 1 · Hero loop --> 129 <HeroLoop shelves={heroShelves} name="DΛΣMӨП-RΣF" tagline="DÆMON//SEC · Cheatsheet vault" /> 130 131 <!-- 2 · Manifest band --> 132 <Marquee titles={laneTitles} meta={laneMeta} manifest={String(CATEGORIES.length).padStart(2, '0')} /> 133 134 <!-- 3 · Operator band --> 135 <Operator sheets={total} domains={domains} mirrors={3} updated={updatedLabel} /> 136 137 <!-- 4 · Index --> 138 <section id="index" class="dsection" data-index> 139 <SectionHeader 140 eyebrow="Reference transmissions" 141 title="Terse, tested, kept current." 142 intro={`${total} sheets across ${domains} shelves, each stripped to the commands that matter.`} 143 tone="iris" 144 /> 145 <div class="filters"> 146 <button type="button" class="pill" data-filter="all" aria-pressed="true">All</button> 147 <button type="button" class="pill" data-filter="sheets" aria-pressed="false">Sheets</button> 148 <button type="button" class="pill" data-filter="workflow" aria-pressed="false">Workflow</button> 149 <a class="pill" href={url('payloads')}>Payloads ↗</a> 150 <a class="pill" href={url('internal')}>Internal ↗</a> 151 <a class="pill" href={url('hacktricks')}>HackTricks ↗</a> 152 <span class="filters__count" data-shown>10 shown</span> 153 </div> 154 <div class="index-list"> 155 {recent.map((r) => ( 156 <RecordRow 157 href={r.href} 158 n={r.n} 159 title={r.title} 160 kind={r.kind} 161 accent={r.accent} 162 meta={r.meta} 163 excerpt={r.excerpt} 164 tagline={r.tagline} 165 bucket={r.bucket} 166 /> 167 ))} 168 </div> 169 <button type="button" class="show-more" data-more hidden> 170 <span data-more-label>Show more</span><span>↓</span> 171 </button> 172 <div class="index-foot"> 173 <span data-foot>Showing 10 of {total} sheets</span> 174 <a href={url('tags')}>All {CATEGORIES.length} shelves ↗</a> 175 </div> 176 </section> 177 178 <!-- 5 · Shelves grid (the full taxonomy — "pick your vector") --> 179 <section id="shelves" class="dsection"> 180 <SectionHeader 181 eyebrow="Shelves / taxonomy" 182 title="Pick your vector." 183 intro={`${CATEGORIES.length} shelves, grouped by how you actually reach for them.`} 184 tone="pine" 185 /> 186 <div class="shelves-grid"> 187 {shelfCards.map((s) => ( 188 <a class="shelf" href={url(s.slug)} style={`--acc: var(--${s.accent});`}> 189 <span class="shelf__top"><span>{s.n}</span><span>{s.count} {s.count === 1 ? 'sheet' : 'sheets'}</span></span> 190 <span class="shelf__title">{s.label}</span> 191 </a> 192 ))} 193 </div> 194 </section> 195 196 <!-- 6 · Mirrors band (the upstreamed repos — swisskyrepo) --> 197 <section id="mirrors" class="mirrors bleed" aria-labelledby="mirrors-title"> 198 <span class="mirrors__scrim" aria-hidden="true"></span> 199 <div class="archive mirrors__inner"> 200 <div> 201 <span class="mirrors__eyebrow">Third party / mirrored</span> 202 <h2 id="mirrors-title" class="mirrors__title">Some trees are borrowed.</h2> 203 <p class="mirrors__body"> 204 Three directories are generated from upstream security references: swisskyrepo's 205 PayloadsAllTheThings and InternalAllTheThings, plus an attributed selection from 206 HackTricks — mirrored because the official site has gotten harder to read on its own 207 terms, buried under ads and AI-generated filler. Same content, read clean, searchable 208 offline, and every page still links back to its exact source. Attribution, modification 209 notes, and licences live on the credits page. 210 </p> 211 <dl class="mirrors__dl"> 212 <div><dt>Trees</dt><dd>03</dd></div> 213 <div><dt>Licence</dt><dd><span class="mirrors__chip">Upstream</span></dd></div> 214 <div><dt>Sync</dt><dd>Scheduled</dd></div> 215 <div><dt>Edits</dt><dd>None</dd></div> 216 </dl> 217 <a class="mirrors__cta" href={url('credits')}>Read the credits →</a> 218 </div> 219 <div class="mirror-panel"> 220 <div class="mirror-panel__head"><span>Mirror manifest // upstream</span><span class="lic">swisskyrepo</span></div> 221 {mirrorRows.map((m) => ( 222 <div class="mirror-row"><span class="n">{m.n}</span><span class="path">{m.path}</span><span class="count">{m.count} files</span></div> 223 ))} 224 </div> 225 </div> 226 </section> 227 228 <!-- 7 · Scope callout --> 229 <div class="archive" style="padding-top:80px;"> 230 <Callout tone="warning" label="Scope"> 231 Authorised testing, CTFs and education only. HackTricks-derived pages are also restricted to 232 non-commercial use under CC BY-NC 4.0. Know your scope, and get permission first, 233 before you touch a system. 234 </Callout> 235 </div> 236 <div style="height:96px"></div> 237 </Base> 238 239 <script> 240 // Index filter pills — client-side by bucket, with a show-more cap. The 241 // Payloads / Internal pills are real links (they navigate); All / Sheets / 242 // Workflow filter the visible rows. Progressive: rows are all rendered, so 243 // the index is complete with JS off. 244 function initIndexFilters() { 245 const root = document.querySelector('[data-index]') as HTMLElement | null; 246 if (!root || root.dataset.bound) return; 247 root.dataset.bound = '1'; 248 const CAP = 10; 249 const rows = [...root.querySelectorAll<HTMLElement>('.rec')]; 250 const pills = [...root.querySelectorAll<HTMLButtonElement>('button[data-filter]')]; 251 const shown = root.querySelector<HTMLElement>('[data-shown]'); 252 const foot = root.querySelector<HTMLElement>('[data-foot]'); 253 const more = root.querySelector<HTMLElement>('[data-more]'); 254 const moreLabel = root.querySelector<HTMLElement>('[data-more-label]'); 255 let filter = 'all', expanded = false; 256 257 const apply = () => { 258 const matching = rows.filter((r) => filter === 'all' || r.dataset.bucket === filter); 259 rows.forEach((r) => { r.hidden = true; }); 260 const visible = expanded ? matching : matching.slice(0, CAP); 261 visible.forEach((r) => { r.hidden = false; }); 262 if (shown) shown.textContent = `${visible.length} shown`; 263 if (foot) foot.textContent = `Showing ${visible.length} of ${matching.length} ${filter === 'all' ? 'entries' : filter}`; 264 if (more && moreLabel) { 265 const rem = matching.length - visible.length; 266 more.hidden = rem <= 0; 267 moreLabel.textContent = `Show ${Math.min(CAP, rem)} more — ${rem} remaining`; 268 } 269 pills.forEach((p) => p.setAttribute('aria-pressed', String(p.dataset.filter === filter))); 270 }; 271 272 pills.forEach((p) => p.addEventListener('click', () => { filter = p.dataset.filter || 'all'; expanded = false; apply(); })); 273 more?.addEventListener('click', () => { expanded = true; apply(); }); 274 apply(); 275 } 276 document.addEventListener('astro:page-load', initIndexFilters); 277 if (document.readyState !== 'loading') initIndexFilters(); 278 else document.addEventListener('DOMContentLoaded', initIndexFilters); 279 </script>