content.config.ts (5217B)
1 import { defineCollection, z } from 'astro:content'; 2 import { glob } from 'astro/loaders'; 3 4 // Every cheatsheet is a markdown file under src/content/sheets/<category>/. 5 // Nav + taxonomy are driven by the `category` frontmatter, not the folder. 6 const sheets = defineCollection({ 7 loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/sheets' }), 8 schema: z.object({ 9 title: z.string(), 10 description: z.string().default(''), 11 category: z.string(), 12 // Additional shelves share the canonical sheet and URL. 13 alsoIn: z.array(z.string()).default([]), 14 // Optional second-level grouping shown as sub-sections on a busy 15 // category page (Active Directory has 130+ sheets). Derived from the 16 // source vault path by scripts/stamp-subcategory.py. 17 subcategory: z.string().optional(), 18 // Optional editorial order inside a subcategory. The CPTS workflow uses 19 // this to preserve attack-stage order instead of sorting stage names A→Z. 20 order: z.number().int().nonnegative().optional(), 21 tags: z.array(z.string()).default([]), 22 tools: z.array(z.string()).default([]), 23 difficulty: z.enum(['beginner', 'intermediate', 'advanced']).default('intermediate'), 24 // Accept a quoted string or a bare YAML date; always expose YYYY-MM-DD. 25 updated: z 26 .union([z.string(), z.date()]) 27 .optional() 28 .transform((v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v)), 29 source: z.string().optional(), 30 // Third-party provenance. `source` above records only the vault path a sheet 31 // was imported from, which says nothing about who wrote it; these fields 32 // record the real upstream so the page can credit it. Set them together. 33 // `none` means the upstream publishes no licence at all — that grants no 34 // right to copy, so such a sheet must be a link-only stub, not a mirror. 35 upstreamName: z.string().optional(), 36 upstreamUrl: z.string().url().optional(), 37 upstreamAuthor: z.string().optional(), 38 upstreamLicense: z.enum(['MIT', 'BSD-2-Clause', 'BSD-3-Clause', 'Apache-2.0', 'none', 'proprietary']).optional(), 39 upstreamRelation: z.enum(['verbatim', 'derived', 'inspired', 'link-only']).optional(), 40 // The notice MIT/BSD/Apache require to travel with a copy. 41 upstreamCopyright: z.string().optional(), 42 // Secondary provenance, for a sheet that draws on more than one upstream. 43 // The flat `upstream*` fields above hold a single source, which is enough 44 // for a sheet that *is* a copy or a rewrite of one thing; it cannot express 45 // a sheet that reproduces an MIT cheatsheet and also folds in a section 46 // derived from somewhere else. Each entry credits one source and says in 47 // `note` what it actually contributed, so the credit is specific rather 48 // than a wall of links. Rendered by SheetReferences at the foot of the page. 49 references: z 50 .array( 51 z.object({ 52 name: z.string(), 53 url: z.string().url(), 54 author: z.string().optional(), 55 license: z 56 .enum(['MIT', 'BSD-2-Clause', 'BSD-3-Clause', 'Apache-2.0', 'CC-BY-NC-4.0', 'none', 'proprietary']) 57 .optional(), 58 relation: z.enum(['verbatim', 'derived', 'inspired', 'link-only']).optional(), 59 note: z.string().optional(), 60 }), 61 ) 62 .default([]), 63 // When set, the sheet embeds/links a PDF that lives in public/pdfs/<pdf>. 64 pdf: z.string().optional(), 65 draft: z.boolean().default(false), 66 }), 67 }); 68 69 // Mirror of PayloadsAllTheThings (MIT, swisskyrepo). Generated by 70 // scripts/sync-payloads.py — do not hand-edit files under src/content/payloads. 71 const payloads = defineCollection({ 72 loader: glob({ pattern: '**/*.md', base: './src/content/payloads' }), 73 schema: z.object({ 74 title: z.string(), 75 topic: z.string(), 76 topicSlug: z.string(), 77 sourcePath: z.string(), 78 sourceUrl: z.string(), 79 sha: z.string(), 80 isReadme: z.boolean().default(false), 81 }), 82 }); 83 84 // Mirror of InternalAllTheThings (swisskyrepo). Generated by 85 // scripts/sync-internal.py — do not hand-edit files under src/content/internal. 86 const internal = defineCollection({ 87 loader: glob({ pattern: '**/*.md', base: './src/content/internal' }), 88 schema: z.object({ 89 title: z.string(), 90 section: z.string(), 91 sectionSlug: z.string(), 92 sourcePath: z.string(), 93 sourceUrl: z.string(), 94 sha: z.string(), 95 isIndex: z.boolean().default(false), 96 }), 97 }); 98 99 // Curated HackTricks mirror (CC BY-NC 4.0, Carlos Polop and contributors). 100 // Generated by scripts/sync-hacktricks.py. The collection is kept separate 101 // from hand-authored sheets so its non-commercial licence and provenance stay 102 // visible on every page. 103 const hacktricks = defineCollection({ 104 loader: glob({ pattern: '**/*.md', base: './src/content/hacktricks' }), 105 schema: z.object({ 106 title: z.string(), 107 section: z.string(), 108 sectionSlug: z.string(), 109 sourcePath: z.string(), 110 sourceUrl: z.string().url(), 111 sha: z.string(), 112 isIndex: z.boolean().default(false), 113 modified: z.boolean().default(true), 114 license: z.literal('CC-BY-NC-4.0'), 115 }), 116 }); 117 118 export const collections = { sheets, payloads, internal, hacktricks };