diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..f2008c5 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,18 @@ +# Code owners for auditor. +# +# These rules request a review from the listed owner whenever a matching path +# changes. Later rules win, so the more specific path-based rules below override +# the catch-all. Syntax: https://docs.github.com/articles/about-code-owners + +# Default owner for everything in the repo. +* @marcelrapold + +# Release surface — the prompts, their checksums, the build/CI scripts, and the +# GitHub configuration. Changes here ship to consumers, so they need review. +/scripts/ @marcelrapold +/CHECKSUMS.txt @marcelrapold +/audit-prompts/ @marcelrapold +/.github/ @marcelrapold + +# Web app (the landing page deployed to auditor.rapold.io). +/web/ @marcelrapold diff --git a/CHECKSUMS.txt b/CHECKSUMS.txt index a3ddb28..b36e1c4 100644 --- a/CHECKSUMS.txt +++ b/CHECKSUMS.txt @@ -10,7 +10,7 @@ afaad3168640d047af7082f93721ff6169f8c67ee0937f2a7a785fad21a0a798 *audit-prompts/ 26ae6a7f36ee3cd7e44539238ab833026a585b38884c3334ab785c19e35ce440 *audit-prompts/infrastructure-audit-master-prompt.md 9322253707945af22ebb32104bb4657dff584869a23798091985f0226e336e58 *audit-prompts/lean-audit-master-prompt.md eade468bffad5ee4143e0461502ccc8c607846fb4861c6b672b2a0af52031d4f *audit-prompts/performance-audit-master-prompt.md -b98bdf8671d2640b8582f0bec2a7e35f07ac1a67ec457496d239b0e9e8174ae4 *audit-prompts/repo-audit-master-prompt.md +ddee61e41e75d4b6d4a23d114f58eb5d1be7e13f29bbcf6ad37184076b14f872 *audit-prompts/repo-audit-master-prompt.md 435e1661ad34a4398d41715763c243cd4c76d9efb4d01b933f05768dca4ccc73 *audit-prompts/security-audit-master-prompt.md 6bcc77a3a95524cb58a189042ee579cd1172cc44ccc4fd1b7961387bfd899626 *ISSUE-OUTPUT-STANDARD.md d5c630f94295b56fb451a53836c69f5815e5096f4a694347a40c1f09543afb78 *DOCUMENTATION-STANDARD.en.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ffce53d..1273376 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,51 +1,62 @@ # Contributing -Danke für deinen Beitrag. Dieses Repo ist eine Sammlung von Audit-Master-Prompts plus den -Standards, die sie durchsetzen. Bitte halte beide Konventionen ein. +Thanks for your contribution. This repo is a collection of audit master prompts plus the standards +they enforce. Please follow both conventions. -## Dokumentation +## Documentation -Jede Markdown-Datei folgt dem [`DOCUMENTATION-STANDARD.md`](DOCUMENTATION-STANDARD.md) -(englisch: [`DOCUMENTATION-STANDARD.en.md`](DOCUMENTATION-STANDARD.en.md)). Kurz: +Every Markdown file follows the [`DOCUMENTATION-STANDARD.md`](DOCUMENTATION-STANDARD.md) +(English: [`DOCUMENTATION-STANDARD.en.md`](DOCUMENTATION-STANDARD.en.md)). In short: -- Überschriften in Satz-Schreibweise, **keine Emojis in Überschriften/Titeln**. -- Zweite Person, Präsens, Aktiv; kurze Sätze; parallele Listen. -- Hinweise als GitHub-Alerts (`> [!NOTE]`, `> [!TIP]`, `> [!WARNING]`) statt Emoji. -- Keine toten Links; keine Doku-Code-Drift. +- Headings in sentence case, **no emojis in headings or titles**. +- Second person, present tense, active voice; short sentences; parallel lists. +- Notes as GitHub alerts (`> [!NOTE]`, `> [!TIP]`, `> [!WARNING]`) instead of emoji. +- No dead links; no doc–code drift. -## Eine neue Audit-Vorlage beitragen +## Contributing a new audit template -Neue Vorlagen folgen dem Hausschema, damit sie mit den übrigen zusammenpassen: +New templates follow the house structure so they compose with the rest. Use the canonical heading +order — a CI gate enforces it (see below): -1. **Mission + Universality** (was geprüft wird; warum stack-agnostisch). -2. **Operating principles** (evidence-bound, cite-the-standard, severity-earned, adversarial, - praise-the-good, no-silent-truncation, fix-forward). -3. **Severity-Skala** (P0–P3) + Aufwand + Priority/ICE-Score. -4. **Phasen** 0 Recon → 1 paralleler Schwarm → 2 Dedupe → 3 adversarielle Verifikation → - 4 Benchmark → 5 Synthese. -5. **Shared finding schema** (JSON) und **Definition of Done**. -6. **Issue-Ausgabe** nach [`ISSUE-OUTPUT-STANDARD.md`](ISSUE-OUTPUT-STANDARD.md) — verbindlich: - zuerst ein Tracking-Issue (priorisierter Index + Management-Summary + Roadmap), dann pro - Befund ein Issue mit eigener Management-Summary. Standardsprache der Issues: Deutsch. +1. **Title + mission blockquote** (what is audited; why it is stack-agnostic). +2. **How to use this prompt** (the `TARGET`/`SCOPE`/… parameter block). +3. **Operating principles** (evidence-bound, cite-the-standard, severity-earned, adversarial, + praise-the-good, no-silent-truncation, fix-forward) plus the **P0–P3 severity scale** and the + effort + priority/ICE score. +4. **Phases 0–5**: 0 Recon → 1 parallel swarm → 2 dedupe → 3 adversarial verification → + 4 benchmark → 5 synthesis. +5. **Issue output** per [`ISSUE-OUTPUT-STANDARD.md`](ISSUE-OUTPUT-STANDARD.md) — mandatory: a + tracking issue first (prioritized index + management summary + roadmap), then one issue per + finding with its own management summary. Default issue language: German. +6. **Shared finding schema** (JSON) and **Definition of done**. -Halte Vorlagen provider-agnostisch, mappe Befunde auf anerkannte Standards und mache jede -Empfehlung sofort umsetzbar. +Keep templates provider-agnostic, map findings to recognized standards, and make every +recommendation immediately actionable. -## Pull-Requests +> [!NOTE] +> **Structure gate.** `node scripts/check-prompts.mjs` (the `prompts` workflow) enforces the house +> structure across all prompts so their findings compose: every canonical heading must be present, +> in the canonical order, with the P0–P3 severity scale and no legacy German severities, and the +> `Shared finding schema` block must carry the shared field keys (`id`, `title`, `severity`, +> `confidence`, `effort`, `evidence`, `fix`, `expected_impact`). `full-audit-master-prompt.md` is +> the orchestrator and is exempt. Run it before opening a PR. -- Ein PR pro thematischer Änderung; aussagekräftige Commit-Nachrichten. -- Aktualisiere [`CHANGELOG.md`](CHANGELOG.md) unter `[Unreleased]`. -- Versionierung nach [SemVer](https://semver.org/). +## Pull requests -## Release-Surface (Prompts und Checksummen) +- One PR per topical change; meaningful commit messages. +- Update [`CHANGELOG.md`](CHANGELOG.md) under `[Unreleased]`. +- Versioning per [SemVer](https://semver.org/). -- Beim Editieren eines `audit-prompts/*.md` oder eines Standards die `CHECKSUMS.txt` regenerieren +## Release surface (prompts and checksums) + +- When editing an `audit-prompts/*.md` file or a standard, regenerate `CHECKSUMS.txt` (`sha256sum audit-prompts/*.md ISSUE-OUTPUT-STANDARD.md DOCUMENTATION-STANDARD*.md > CHECKSUMS.txt`). - Die `prompts`-CI prüft das mit `sha256sum -c` und schlägt sonst fehl. -- Die in URLs gepinnte Release-Version wird **nur beim Release** gebumpt — über - `node scripts/bump-version.mjs vX.Y.Z`, nie von Hand. Siehe [`RELEASING.md`](RELEASING.md). + The `prompts` CI verifies it with `sha256sum -c` and fails otherwise. +- The release version pinned in URLs is bumped **only at release time** — via + `node scripts/bump-version.mjs vX.Y.Z`, never by hand. See [`RELEASING.md`](RELEASING.md). -## Verhalten und Sicherheit +## Conduct and security -- Es gilt der [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) (Contributor Covenant). -- Schwachstellen bitte **vertraulich** melden — siehe [`SECURITY.md`](SECURITY.md), nicht als öffentliches Issue. +- The [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) (Contributor Covenant) applies. +- Please report vulnerabilities **confidentially** — see [`SECURITY.md`](SECURITY.md), not as a + public issue. diff --git a/audit-prompts/repo-audit-master-prompt.md b/audit-prompts/repo-audit-master-prompt.md index 38a2ba6..919b11f 100644 --- a/audit-prompts/repo-audit-master-prompt.md +++ b/audit-prompts/repo-audit-master-prompt.md @@ -1,41 +1,26 @@ -# ULTIMATE REPOSITORY AUDIT — DEFAULT ANALYSIS PROMPT - -> **Usage:** Paste this entire prompt into your AI coding agent (Claude Code, Cursor, etc.) -> while the working directory is the root of the repository you want to audit. -> No arguments are required. The audit is strictly **read-only**. - ---- - -## MISSION - -You are a **Principal Engineer conducting a formal engineering audit** of this repository, operating at the standard of a Google-grade engineering review board (Google Engineering Practices, Google SRE, OWASP, SLSA). Your mandate: assess the repository for **full consistency and engineering excellence** across architecture, tech stack, documentation, code quality, testing, security, dependencies, CI/CD, observability, and release hygiene — then benchmark it against current industry best practices and produce a board-ready audit report. - -This audit is expected to be **exhaustive**. Invest as much runtime, tool calls, and parallelism as your environment allows. Depth and correctness take absolute priority over speed and brevity. - -## Operating principles - -1. **Evidence or it didn't happen.** Every finding MUST cite concrete evidence: `path/to/file.ext:line` (or a file path plus quoted snippet). Findings without evidence are discarded. Never report from assumption, file names alone, or general knowledge of "how projects like this usually look." -2. **Read-only.** You change nothing: no file edits, no formatting, no dependency installs that mutate lockfiles, no git writes. Commands you run must be inspection-only (`git log`, linters in check mode, dependency listing, etc.). -3. **No silent caps.** If you sample, truncate, time-box, or skip anything (large vendored dirs, generated code, a language you didn't deep-dive), you MUST declare it in the report's "Audit Coverage & Limitations" section. An unaudited area reported as "fine" is an audit failure. -4. **Consistency is the lens.** You are not only judging whether each part is good in isolation — you are judging whether the repository is *internally coherent*: one way of doing things, docs matching code, declared standards matching actual practice. -5. **Severity discipline.** Use the P0–P3 scale defined below. Do not inflate severities to seem thorough; do not bury real risks as "minor." -6. **Judge the repo it is, not the repo you'd write.** Stylistic preference is not a finding. A finding requires a concrete cost: defect risk, security exposure, onboarding friction, maintenance burden, or a documented-standard violation. - ---- - -## EXECUTION MODEL - -If your harness supports **parallel sub-agents / multi-agent workflows**, you MUST use them: run Phase 0 inline, fan out Phase 1's audit dimensions as parallel specialist agents, run the Phase 4 benchmark in parallel with late Phase 1, dedupe in Phase 2, then run Phase 3 adversarial verification as independent skeptic agents per finding. If the harness is single-threaded, execute the same phases sequentially without skipping any. - -Scale to repo size: -- **Small repo** (< ~20k LOC): every dimension still runs, agents may read most files. -- **Medium repo** (~20k–200k LOC): agents read all configuration/entry-point/public-API files fully and sample module internals systematically (not randomly — cover every top-level module). -- **Large repo / monorepo** (> ~200k LOC): audit per workspace/package; produce per-package scores plus a cross-package consistency analysis (shared tooling, version alignment, duplicated utilities across packages). Declare any package not deep-dived. +# Ultimate Repository Audit — Default Analysis Prompt + +> **Mission:** Subject this repository to a **formal engineering audit** at the standard of a +> Google-grade engineering review board (Google Engineering Practices, Google SRE, OWASP, SLSA). +> A swarm of specialist agents assesses the repo for **full consistency and engineering excellence** +> across architecture, tech stack, documentation, code quality, testing, security, dependencies, +> CI/CD, observability, and release hygiene, benchmarks it against current industry best practice, +> and produces a board-ready audit report plus a German (or English) GitHub issue backlog. The audit +> is strictly **read-only** and every finding is evidence-backed and adversarially verified. +> +> **Universality:** Stack-, language-, and platform-agnostic. Phase 0 infers the stack; no arguments +> are required. The audit is expected to be **exhaustive** — invest as much runtime, tool calls, and +> parallelism as your environment allows; depth and correctness take absolute priority over speed and +> brevity. --- ## How to use this prompt +Paste this entire prompt into your AI coding agent (Claude Code, Cursor, etc.) while the working +directory is the root of the repository you want to audit. No arguments are strictly required — +Phase 0 infers the stack — and the audit is **read-only**. + ``` TARGET: SCOPE: @@ -44,134 +29,238 @@ OUTPUT_LANG: ISSUE_TARGET: ``` -No arguments are strictly required — Phase 0 infers the stack. The audit is **read-only**. +> [!NOTE] +> **Execution model.** If your harness supports **parallel sub-agents / multi-agent workflows**, you +> MUST use them: run Phase 0 inline, fan out Phase 1's audit dimensions as parallel specialist +> agents, run the Phase 4 benchmark in parallel with late Phase 1, dedupe in Phase 2, then run +> Phase 3 adversarial verification as independent skeptic agents per finding. If the harness is +> single-threaded, execute the same phases sequentially without skipping any. +> +> **Scale to repo size.** *Small* (< ~20k LOC): every dimension still runs, agents may read most +> files. *Medium* (~20k–200k LOC): agents read all configuration/entry-point/public-API files fully +> and sample module internals systematically (not randomly — cover every top-level module). *Large / +> monorepo* (> ~200k LOC): audit per workspace/package; produce per-package scores plus a +> cross-package consistency analysis (shared tooling, version alignment, duplicated utilities). +> Declare any package not deep-dived. + +--- + +## Operating principles (binding for every agent) + +1. **Evidence or it didn't happen.** Every finding MUST cite concrete evidence: `path/to/file.ext:line` + (or a file path plus quoted snippet). Findings without evidence are discarded. Never report from + assumption, file names alone, or general knowledge of "how projects like this usually look." +2. **Read-only.** You change nothing: no file edits, no formatting, no dependency installs that + mutate lockfiles, no git writes. Commands you run must be inspection-only (`git log`, linters in + check mode, dependency listing, etc.). +3. **No silent caps.** If you sample, truncate, time-box, or skip anything (large vendored dirs, + generated code, a language you didn't deep-dive), you MUST declare it in the report's "Audit + Coverage & Limitations" section. An unaudited area reported as "fine" is an audit failure. +4. **Consistency is the lens.** You are not only judging whether each part is good in isolation — you + are judging whether the repository is *internally coherent*: one way of doing things, docs matching + code, declared standards matching actual practice. +5. **Severity discipline.** Use the P0–P3 scale defined below. Do not inflate severities to seem + thorough; do not bury real risks as "minor." +6. **Judge the repo it is, not the repo you'd write.** Stylistic preference is not a finding. A + finding requires a concrete cost: defect risk, security exposure, onboarding friction, maintenance + burden, or a documented-standard violation. +7. **Praise what is excellent.** Every dimension names what is genuinely well done — an honest audit + names strengths, not just gaps. + +### Severity scale + +| Level | Definition | +|---|---| +| **P0 — Critical** | Active security exposure (leaked secret, exploitable injection, vulnerable dependency with a known exploit on a reachable path), data-loss risk, legal/license violation. *Fix immediately.* | +| **P1 — High** | Materially raises defect or incident probability, or blocks team scaling: untested critical paths, broken/missing CI gates, EOL runtime, systematic error-swallowing, doc drift that will misdirect engineers on critical setup/deploy steps. | +| **P2 — Medium** | Consistency and maintainability debt with real ongoing cost: competing libraries, version drift, architecture erosion, incomplete docs, lint-reality gaps. | +| **P3 — Low** | Polish: naming inconsistencies, stale TODOs, minor doc gaps, nice-to-have tooling. | + +Each finding also carries an **effort** tag (S/M/L) and a **confidence** rating (high/medium/low). --- -## PHASE 0 — RECONNAISSANCE (run first, before any judgment) +## Phase 0 — Reconnaissance (run first, before any judgment) Build a factual inventory. No opinions in this phase. -1. **Layout:** top-level tree (2–3 levels), monorepo vs. single package, workspace definitions, generated/vendored directories (identify and mark them excluded from style judgments). +1. **Layout:** top-level tree (2–3 levels), monorepo vs. single package, workspace definitions, + generated/vendored directories (identify and mark them excluded from style judgments). 2. **Languages & size:** languages present, approximate LOC per language, file counts. -3. **Tech stack:** frameworks, runtimes and their versions (from manifests: `package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`, `*.csproj`, `pom.xml`/`build.gradle`, `Gemfile`, `composer.json`, Dockerfiles, infra-as-code, etc.). -4. **Tooling surface:** build system, package manager(s), linters/formatters configured, test frameworks, CI provider(s) and workflow files, containerization, IaC. -5. **Git signals (if a git repo):** age, commit cadence, number of authors, branch model evidence, last 30 commit subjects (quality sample), tags/releases. -6. **Declared intent:** read README, CONTRIBUTING, CLAUDE.md/AGENTS.md, ADRs, style guides — record what the repo *claims* its standards are. Phase 1 will test reality against these claims. +3. **Tech stack:** frameworks, runtimes and their versions (from manifests: `package.json`, + `pyproject.toml`, `go.mod`, `Cargo.toml`, `*.csproj`, `pom.xml`/`build.gradle`, `Gemfile`, + `composer.json`, Dockerfiles, infra-as-code, etc.). +4. **Tooling surface:** build system, package manager(s), linters/formatters configured, test + frameworks, CI provider(s) and workflow files, containerization, IaC. +5. **Git signals (if a git repo):** age, commit cadence, number of authors, branch model evidence, + last 30 commit subjects (quality sample), tags/releases. +6. **Declared intent:** read README, CONTRIBUTING, CLAUDE.md/AGENTS.md, ADRs, style guides — record + what the repo *claims* its standards are. Phase 1 will test reality against these claims. Output of Phase 0: a **Repository Fact Sheet** that all subsequent agents receive as context. --- -## PHASE 1 — PARALLEL DEEP-DIVE: TEN AUDIT DIMENSIONS - -Spawn one specialist agent per dimension (parallel where possible). Each agent receives the Fact Sheet, its charter below, and must return findings in the standard schema: - -``` -{ id, dimension, title, severity (P0|P1|P2|P3), evidence: [file:line + snippet], - impact (one sentence: who is hurt, how), recommendation (concrete, actionable), - effort (S|M|L), confidence (high|medium|low) } -``` +## Phase 1 — Parallel deep-dive: ten audit dimensions -…plus a **dimension grade (A–F)** with a two-sentence justification, and a list of **positive observations** (what is genuinely well done — an honest audit names strengths, not just gaps). +Spawn one specialist agent per dimension (parallel where possible). Each agent receives the Fact +Sheet, its charter below, and returns findings in the shared schema, **plus** a **dimension grade +(A–F)** with a two-sentence justification and a list of **positive observations** (what is genuinely +well done). -### 1. Architecture & Module Boundaries -- Identify the intended architecture (layers, hexagonal, feature-modules, MVC, microservices, …) from structure and docs; verify the code actually follows it. -- Layering violations (e.g., domain importing infrastructure, UI importing persistence), circular dependencies, god modules/classes, dead code, copy-paste duplication of non-trivial logic. +### 1. Architecture & module boundaries +- Identify the intended architecture (layers, hexagonal, feature-modules, MVC, microservices, …) from + structure and docs; verify the code actually follows it. +- Layering violations (e.g., domain importing infrastructure, UI importing persistence), circular + dependencies, god modules/classes, dead code, copy-paste duplication of non-trivial logic. - Public API surface: is what's exported intentional? Are internal modules leaking? -- Boundaries of side effects: where do I/O, network, and global state live? Is it contained or smeared everywhere? - -### 2. Tech-Stack Consistency -- **Version drift:** same dependency at different versions across workspaces; runtime version pinned inconsistently (e.g., `.nvmrc` vs. `engines` vs. CI vs. Dockerfile); language version in config vs. syntax actually used. -- **Competing libraries for the same job** (two HTTP clients, two date libraries, two state managers, two test runners, fetch + axios, lodash + ramda…): list each duplicate with usage counts and a consolidation recommendation. -- **Idiom consistency:** does the code use the framework the way one team would (one data-fetching pattern, one error-handling pattern, one DI approach), or does it read like five eras of five teams? -- Mixed paradigms without rationale (callbacks vs. promises vs. async/await; classes vs. hooks; ORM vs. raw SQL side by side). -- Configuration sprawl: overlapping/contradicting config files, dead config for tools no longer present. +- Boundaries of side effects: where do I/O, network, and global state live? Is it contained or smeared + everywhere? + +### 2. Tech-stack consistency +- **Version drift:** same dependency at different versions across workspaces; runtime version pinned + inconsistently (e.g., `.nvmrc` vs. `engines` vs. CI vs. Dockerfile); language version in config vs. + syntax actually used. +- **Competing libraries for the same job** (two HTTP clients, two date libraries, two state managers, + two test runners, fetch + axios, lodash + ramda…): list each duplicate with usage counts and a + consolidation recommendation. +- **Idiom consistency:** does the code use the framework the way one team would (one data-fetching + pattern, one error-handling pattern, one DI approach), or does it read like five eras of five teams? +- Mixed paradigms without rationale (callbacks vs. promises vs. async/await; classes vs. hooks; ORM + vs. raw SQL side by side). +- Configuration sprawl: overlapping/contradicting config files, dead config for tools no longer + present. ### 3. Documentation -- **Existence & quality:** README (purpose, setup, run, test, deploy?), CONTRIBUTING, architecture docs/ADRs, API documentation, inline doc-comments on public APIs, CHANGELOG, LICENSE. -- **Doc–code drift (critical):** actively verify claims — do documented commands exist in scripts/Makefile and run conceptually? Do documented env vars match what code reads? Do described directories/modules still exist? Are code samples in docs using current APIs? Every drift instance is a finding with both locations cited. -- **Onboarding path:** could a competent new engineer go from clone to running app + passing tests using only the docs? Identify the exact step where they'd get stuck. -- Comment quality in code: explaining *why* (good) vs. narrating the obvious or lying about behavior (findings). - -### 4. Code Quality & Conventions -- **Declared vs. enforced vs. actual:** compare style-guide/lint config against real code. Lint config that's ignored, disabled rules with no rationale, inline `eslint-disable`/`# noqa`/`@ts-ignore`/`any` density. -- Naming consistency (casing conventions per language, file naming, one term per concept across the codebase — not `user`/`account`/`member` for the same entity). -- **Error handling as a system:** one coherent strategy (error types, propagation, logging at the boundary) or ad-hoc (swallowed exceptions, `catch {}`, error strings, inconsistent retry logic)? Cite swallowed/ignored errors individually — they hide production incidents. -- Function/file size outliers, deeply nested logic, magic numbers/strings where constants exist elsewhere, TODO/FIXME/HACK census (count + the oldest/scariest ones). -- Type discipline (in typed languages): escape hatches, untyped boundaries, strictness settings vs. actual strictness. - -### 5. Testing & Quality Gates -- Test inventory: frameworks, counts per type (unit/integration/e2e), and whether the shape resembles a sane test pyramid for this kind of product. -- **Critical-path coverage:** identify the 5–10 most business-critical code paths from Phase 0; verify each has meaningful tests. Untested critical paths are P1 by default. -- Test quality: assertions that actually assert (vs. snapshot-everything or assert-not-null theatre), mocking depth (mocking your own internals is a smell), test data realism, determinism risks (real time, real network, sleeps, order dependence → flakiness indicators). -- Gates: are tests/lint/type-check/coverage enforced in CI, or merely available? Skipped/disabled tests (`.skip`, `xfail`, commented-out) with no linked reason. - -### 6. Security & Secrets -- **Secret scan:** hardcoded credentials, API keys, tokens, private keys, connection strings — in code, configs, scripts, CI files, and (if feasible) `git log -p` history samples. Any hit is P0. -- Input handling at trust boundaries: injection risks (SQL/command/template/path traversal), deserialization of untrusted data, file-upload handling, SSRF-prone fetches. -- AuthN/AuthZ patterns: where enforced (every entry point or scattered?), session/token handling, password storage, missing authorization checks on mutating endpoints. -- Known-vulnerable dependencies (run the ecosystem's audit tool in read-only mode if available: `npm audit`, `pip-audit`, `cargo audit`, `govulncheck`, …). -- Security hygiene: HTTPS/TLS settings, CORS configuration, security headers, secrets management approach (env vars vs. secret manager vs. plaintext), Dockerfile running as root, overly permissive IAM in IaC. +- **Existence & quality:** README (purpose, setup, run, test, deploy?), CONTRIBUTING, architecture + docs/ADRs, API documentation, inline doc-comments on public APIs, CHANGELOG, LICENSE. +- **Doc–code drift (critical):** actively verify claims — do documented commands exist in + scripts/Makefile and run conceptually? Do documented env vars match what code reads? Do described + directories/modules still exist? Are code samples in docs using current APIs? Every drift instance + is a finding with both locations cited. +- **Onboarding path:** could a competent new engineer go from clone to running app + passing tests + using only the docs? Identify the exact step where they'd get stuck. +- Comment quality in code: explaining *why* (good) vs. narrating the obvious or lying about behavior + (findings). + +### 4. Code quality & conventions +- **Declared vs. enforced vs. actual:** compare style-guide/lint config against real code. Lint config + that's ignored, disabled rules with no rationale, inline `eslint-disable`/`# noqa`/`@ts-ignore`/`any` + density. +- Naming consistency (casing conventions per language, file naming, one term per concept across the + codebase — not `user`/`account`/`member` for the same entity). +- **Error handling as a system:** one coherent strategy (error types, propagation, logging at the + boundary) or ad-hoc (swallowed exceptions, `catch {}`, error strings, inconsistent retry logic)? + Cite swallowed/ignored errors individually — they hide production incidents. +- Function/file size outliers, deeply nested logic, magic numbers/strings where constants exist + elsewhere, TODO/FIXME/HACK census (count + the oldest/scariest ones). +- Type discipline (in typed languages): escape hatches, untyped boundaries, strictness settings vs. + actual strictness. + +### 5. Testing & quality gates +- Test inventory: frameworks, counts per type (unit/integration/e2e), and whether the shape resembles + a sane test pyramid for this kind of product. +- **Critical-path coverage:** identify the 5–10 most business-critical code paths from Phase 0; verify + each has meaningful tests. Untested critical paths are P1 by default. +- Test quality: assertions that actually assert (vs. snapshot-everything or assert-not-null theatre), + mocking depth (mocking your own internals is a smell), test data realism, determinism risks (real + time, real network, sleeps, order dependence → flakiness indicators). +- Gates: are tests/lint/type-check/coverage enforced in CI, or merely available? Skipped/disabled + tests (`.skip`, `xfail`, commented-out) with no linked reason. + +### 6. Security & secrets +- **Secret scan:** hardcoded credentials, API keys, tokens, private keys, connection strings — in + code, configs, scripts, CI files, and (if feasible) `git log -p` history samples. Any hit is P0. +- Input handling at trust boundaries: injection risks (SQL/command/template/path traversal), + deserialization of untrusted data, file-upload handling, SSRF-prone fetches. +- AuthN/AuthZ patterns: where enforced (every entry point or scattered?), session/token handling, + password storage, missing authorization checks on mutating endpoints. +- Known-vulnerable dependencies (run the ecosystem's audit tool in read-only mode if available: + `npm audit`, `pip-audit`, `cargo audit`, `govulncheck`, …). +- Security hygiene: HTTPS/TLS settings, CORS configuration, security headers, secrets management + approach (env vars vs. secret manager vs. plaintext), Dockerfile running as root, overly permissive + IAM in IaC. - Map relevant findings to OWASP Top 10 categories. -### 7. Dependencies & Supply Chain -- Outdated dependencies: how far behind latest stable (major versions behind = higher severity), deprecated or unmaintained/archived packages (check repo activity for the most critical ones). -- Lockfile hygiene: lockfile present, committed, single (not npm+yarn+pnpm lockfiles side by side), in sync with the manifest. -- Dependency weight: trivially-replaceable micro-deps, duplicate transitive majors, anything famous for supply-chain incidents. -- License consistency: repo license vs. dependency licenses (flag copyleft in a proprietary repo), license file present and matching manifest metadata. +### 7. Dependencies & supply chain +- Outdated dependencies: how far behind latest stable (major versions behind = higher severity), + deprecated or unmaintained/archived packages (check repo activity for the most critical ones). +- Lockfile hygiene: lockfile present, committed, single (not npm+yarn+pnpm lockfiles side by side), + in sync with the manifest. +- Dependency weight: trivially-replaceable micro-deps, duplicate transitive majors, anything famous + for supply-chain incidents. +- License consistency: repo license vs. dependency licenses (flag copyleft in a proprietary repo), + license file present and matching manifest metadata. - Pinning strategy coherence: exact pins vs. ranges vs. `latest` — is there *one* policy? -### 8. CI/CD & Developer Experience -- Pipeline completeness: build, lint, type-check, test, security scan, artifact/deploy stages — what's missing? Do pipelines gate merges or just decorate them? -- Reproducibility: pinned tool versions in CI, hermetic builds, cache correctness, "works on my machine" risk (setup steps that exist only in a README and not in code). -- Local DX: one-command setup? one-command test run? Sane defaults, devcontainer/docker-compose for dependencies, time-to-first-green-test for a newcomer. -- CI hygiene: flaky-job workarounds (retries-as-policy), `continue-on-error` abuse, secrets handling in CI, long-dead workflows. +### 8. CI/CD & developer experience +- Pipeline completeness: build, lint, type-check, test, security scan, artifact/deploy stages — + what's missing? Do pipelines gate merges or just decorate them? +- Reproducibility: pinned tool versions in CI, hermetic builds, cache correctness, "works on my + machine" risk (setup steps that exist only in a README and not in code). +- Local DX: one-command setup? one-command test run? Sane defaults, devcontainer/docker-compose for + dependencies, time-to-first-green-test for a newcomer. +- CI hygiene: flaky-job workarounds (retries-as-policy), `continue-on-error` abuse, secrets handling + in CI, long-dead workflows. - Deployment story: documented and automated, or tribal knowledge? Rollback path? -### 9. Observability & Operations -- Logging: structured vs. printf-debugging remnants, consistent levels, no secrets/PII in logs, correlation/request IDs for services. -- Metrics & tracing: instrumentation for service-shaped code; health/readiness endpoints; alerting hooks or dashboards referenced. +### 9. Observability & operations +- Logging: structured vs. printf-debugging remnants, consistent levels, no secrets/PII in logs, + correlation/request IDs for services. +- Metrics & tracing: instrumentation for service-shaped code; health/readiness endpoints; alerting + hooks or dashboards referenced. - Error tracking: crash/exception reporting wired up (Sentry et al.) or errors vanish? -- **Configuration management:** every config/env var inventoried? `.env.example` complete and current vs. what code actually reads (this is a classic drift spot — verify)? Sane behavior on missing config (fail fast with a clear message vs. mysterious crash later)? +- **Configuration management:** every config/env var inventoried? `.env.example` complete and current + vs. what code actually reads (this is a classic drift spot — verify)? Sane behavior on missing + config (fail fast with a clear message vs. mysterious crash later)? - Graceful shutdown, timeout/retry/circuit-breaker posture for anything calling over a network. -### 10. Git & Release Hygiene -- Commit quality (sample ≥ 50): conventional/informative messages vs. "fix", "wip", "asdf"; commit size sanity. +### 10. Git & release hygiene +- Commit quality (sample ≥ 50): conventional/informative messages vs. "fix", "wip", "asdf"; commit + size sanity. - Branch/merge discipline evident from history; force-push or history-rewrite scars on main. -- Versioning: SemVer (or alternative) applied consistently? Tags match manifest versions? CHANGELOG maintained and matching tags? -- Repo hygiene: `.gitignore` correctness (build artifacts, IDE files, or — worse — `.env` files tracked?), large binaries in history, generated files committed without need. +- Versioning: SemVer (or alternative) applied consistently? Tags match manifest versions? CHANGELOG + maintained and matching tags? +- Repo hygiene: `.gitignore` correctness (build artifacts, IDE files, or — worse — `.env` files + tracked?), large binaries in history, generated files committed without need. - Ownership signals: CODEOWNERS, bus-factor concentration (one author wrote 95%?), PR templates. +Each agent returns its findings in the shared schema, a **dimension grade (A–F)**, and a **top +"protect this"** list of what is genuinely well done. + --- -## PHASE 2 — CROSS-POLLINATION & DEDUPE +## Phase 2 — Cross-pollination & dedupe Pool all Phase 1 findings; **deduplicate** across dimensions (the same root cause reported by two -dimension agents = one finding, strongest evidence kept) and surface **compound findings** (two -P2s that interact into a P1). The deduplicated set feeds Phase 3. +dimension agents = one finding, strongest evidence kept) and surface **compound findings** (two P2s +that interact into a P1). The deduplicated set feeds Phase 3. --- -## PHASE 3 — ADVERSARIAL VERIFICATION +## Phase 3 — Adversarial verification No finding reaches the report unverified. 1. Take the deduplicated findings from Phase 2 (one entry per root cause). -2. For every finding of severity P0–P2, dispatch an **independent skeptic** (separate agent if possible) whose explicit job is to **refute** it: re-open the cited files, check whether the evidence is real, current, in non-generated code, not a false positive (e.g., a "hardcoded secret" that is a documented test fixture; a "missing test" that exists under another name). Skeptics default to *refuted* when evidence is weak. -3. Findings survive only if the skeptic confirms the evidence. Refuted findings are dropped; partially confirmed ones are downgraded with a note. P3 findings get a lighter pass: spot-check at least a third of them; extrapolate honestly. -4. After verification, run one **completeness critic**: "Given the Fact Sheet, what would a top-tier auditor expect to see flagged that isn't here? Which dimension looks suspiciously clean?" Feed anything credible back through a quick verification round. - -### Severity definitions (apply strictly) - -- **P0 — Critical:** active security exposure (leaked secret, exploitable injection, vulnerable dependency with known exploit on a reachable path), data-loss risk, legal/license violation. *Fix immediately.* -- **P1 — High:** materially raises defect or incident probability, or blocks team scaling: untested critical paths, broken/missing CI gates, EOL runtime, systematic error-swallowing, doc drift that will misdirect engineers on critical setup/deploy steps. -- **P2 — Medium:** consistency and maintainability debt with real ongoing cost: competing libraries, version drift, architecture erosion, incomplete docs, lint-reality gaps. -- **P3 — Low:** polish: naming inconsistencies, stale TODOs, minor doc gaps, nice-to-have tooling. +2. For every finding of severity P0–P2, dispatch an **independent skeptic** (separate agent if + possible) whose explicit job is to **refute** it: re-open the cited files, check whether the + evidence is real, current, in non-generated code, not a false positive (e.g., a "hardcoded secret" + that is a documented test fixture; a "missing test" that exists under another name). Skeptics + default to *refuted* when evidence is weak. +3. Findings survive only if the skeptic confirms the evidence. Refuted findings are dropped; + partially confirmed ones are downgraded with a note. P3 findings get a lighter pass: spot-check at + least a third of them; extrapolate honestly. +4. After verification, run one **completeness critic**: "Given the Fact Sheet, what would a top-tier + auditor expect to see flagged that isn't here? Which dimension looks suspiciously clean?" Feed + anything credible back through a quick verification round. + +The P0–P3 severity definitions in the **Operating principles** above are applied strictly here: no +unevidenced P0s, no buried security issues, and no inflated severities. --- -## PHASE 4 — BENCHMARK: MARKET COMPARE & BEST PRACTICE +## Phase 4 — Benchmark: market compare & best practice Runs in parallel with late Phase 1 (it does not depend on the other phases). Requires web research; if unavailable, benchmark against your knowledge and **flag the cutoff date as a limitation**. @@ -181,21 +270,29 @@ if unavailable, benchmark against your knowledge and **flag the cutoff date as a widely adopted community standards, and what 2–3 named top-tier reference repos do. 2. Classify every notable pattern: **Industry Standard** / **Acceptable** / **Outdated** (name the migration path) / **Anti-Pattern** (cite why). -3. Check stack lifecycle: EOL/near-EOL runtimes/frameworks, deprecated major APIs, ecosystem direction. +3. Check stack lifecycle: EOL/near-EOL runtimes/frameworks, deprecated major APIs, ecosystem + direction. 4. Output a **Benchmark Table**: `Component | This repo | Industry standard | Classification | Migration note`. --- -## PHASE 5 — SYNTHESIS & REPORT +## Phase 5 — Synthesis & report -Produce the final report as a single Markdown document titled `REPO-AUDIT-REPORT.md` content (output it in chat; only write a file if the user asks). Structure — exactly these sections: +Produce the final report as a single Markdown document titled `REPO-AUDIT-REPORT.md` content (output +it in chat; only write a file if the user asks). Structure — exactly these sections: -1. **Executive Summary** (max ~1 page): overall verdict in plain language, overall grade, the 3–5 findings leadership must know, and the single biggest consistency risk. Written for a CTO, no jargon walls. -2. **Scorecard:** table of all 10 dimensions × grade (A–F) × one-line justification × finding counts by severity. Plus an **Overall Engineering Grade** (weighted: Security, Testing, Architecture count double). +1. **Executive Summary** (max ~1 page): overall verdict in plain language, overall grade, the 3–5 + findings leadership must know, and the single biggest consistency risk. Written for a CTO, no + jargon walls. +2. **Scorecard:** table of all 10 dimensions × grade (A–F) × one-line justification × finding counts + by severity. Plus an **Overall Engineering Grade** (weighted: Security, Testing, Architecture + count double). 3. **Repository Fact Sheet** (from Phase 0). -4. **Consistency Matrix:** the cross-cutting view — declared standard vs. actual practice per area (docs say / config enforces / code does), with drift highlighted. +4. **Consistency Matrix:** the cross-cutting view — declared standard vs. actual practice per area + (docs say / config enforces / code does), with drift highlighted. 5. **Benchmark Table** (from Phase 4) + named reference repositories used as comparators. -6. **Verified Findings Register:** every surviving finding in the standard schema, ordered by severity then effort; include the skeptic's confirmation note on P0/P1 items. +6. **Verified Findings Register:** every surviving finding in the shared schema, ordered by severity + then effort; include the skeptic's confirmation note on P0/P1 items. 7. **Strengths:** what this repo does well — be specific and evidence-based here too. 8. **Remediation Roadmap:** - **Quick Wins** (≤ 1 day each, do this week), @@ -203,11 +300,12 @@ Produce the final report as a single Markdown document titled `REPO-AUDIT-REPORT - **60 days** (consistency consolidation: library dedup, doc-drift repair, gate enforcement), - **90 days** (strategic: architecture corrections, migrations off outdated patterns). Each roadmap item references finding IDs. -9. **Audit Coverage & Limitations:** what was sampled vs. fully read, what was skipped and why, tool/web access constraints, confidence statement. +9. **Audit Coverage & Limitations:** what was sampled vs. fully read, what was skipped and why, + tool/web access constraints, confidence statement. --- -## ISSUE OUTPUT — mandatory (see [`ISSUE-OUTPUT-STANDARD.md`](../ISSUE-OUTPUT-STANDARD.md)) +## Issue output — mandatory (see [`ISSUE-OUTPUT-STANDARD.md`](../ISSUE-OUTPUT-STANDARD.md)) Beyond the report, turn verified findings into GitHub issues — **German by default**; preview/ dry-run first, created only on explicit authorization + repo access. Two-part contract: @@ -250,7 +348,7 @@ findings compose across audits in the orchestrator. --- -## DEFINITION OF DONE (self-check before delivering) +## Definition of done (self-check before delivering) - [ ] All 10 dimensions were audited and graded; none skipped silently. - [ ] Every finding in the register has `file:line` evidence and survived (or was downgraded by) adversarial verification. diff --git a/scripts/check-prompts.mjs b/scripts/check-prompts.mjs index 324134c..e1f772b 100644 --- a/scripts/check-prompts.mjs +++ b/scripts/check-prompts.mjs @@ -1,6 +1,16 @@ #!/usr/bin/env node // Enforces the canonical structure across all audit master prompts so their // findings compose (interoperability). The orchestrator (full-audit) is exempt. +// +// Three layers of contract are enforced per prompt: +// 1. Presence — every canonical heading exists (matched by prefix). +// 2. Order — the phase pipeline and the closing sections appear in the +// canonical sequence (Phase 0 → … → Phase 5 → Issue output → +// Shared finding schema → Definition of done), with the two +// framing sections (How to use, Operating principles) both +// ahead of Phase 0. +// 3. Schema — the Shared finding schema block carries the field keys that +// every audit in the library shares, so findings compose. import { readFileSync, readdirSync } from "node:fs"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; @@ -37,6 +47,39 @@ const LABELS = [ "Definition of done", ]; +// Canonical ORDER. The pipeline and closing sections must appear in this exact +// sequence. The two framing sections (How to use / Operating principles) must +// both sit ahead of Phase 0; their relative order is intentionally not pinned — +// most prompts lead with "How to use", a few lead with "Operating principles". +const FRAMING = ["How to use this prompt", "Operating principles"]; +const ORDERED = [ + "Phase 0", + "Phase 1", + "Phase 2", + "Phase 3", + "Phase 4", + "Phase 5", + "Issue output", + "Shared finding schema", + "Definition of done", +]; + +// Field keys shared by the finding schema of every audit in the library. These +// are exactly the keys present in all 13 specialist schemas today — the core +// contract that lets the orchestrator compose findings across audits. Audits +// add their own keys (cvss, harm_chain, class, …) on top; only this intersection +// is enforced so the gate stays strict without being flaky. +const SCHEMA_FIELDS = [ + "id", + "title", + "severity", + "confidence", + "effort", + "evidence", + "fix", + "expected_impact", +]; + // Uniform severity vocabulary (P0–P3) and no legacy German severities in headings/schema. const HAS_P0 = /\bP0\b/; const HAS_P3 = /\bP3\b/; @@ -53,6 +96,22 @@ if ( process.exit(1); } +// Returns the character offset of a label's heading, or -1 if absent. +function headingOffset(text, label) { + const i = REQUIRED.findIndex((_, k) => LABELS[k] === label); + const m = REQUIRED[i].exec(text); + return m ? m.index : -1; +} + +// Extracts the first fenced ```json block under the "Shared finding schema" heading. +function schemaBlock(text) { + const heading = /^#{1,2}\s+shared finding schema/im.exec(text); + if (!heading) return ""; + const rest = text.slice(heading.index); + const m = /```json\s*([\s\S]*?)```/.exec(rest); + return m ? m[1] : ""; +} + const files = readdirSync(DIR) .filter((f) => f.endsWith("-audit-master-prompt.md") && !EXEMPT.has(f)) .sort(); @@ -60,16 +119,51 @@ const files = readdirSync(DIR) let failed = 0; for (const f of files) { const text = readFileSync(join(DIR, f), "utf8"); - const missing = []; + const problems = []; + + // 1. Presence. REQUIRED.forEach((re, i) => { - if (!re.test(text)) missing.push(LABELS[i]); + if (!re.test(text)) problems.push(`missing section: ${LABELS[i]}`); }); - if (!HAS_P0.test(text) || !HAS_P3.test(text)) missing.push("P0–P3 severity scale"); - if (LEGACY_SEVERITY.test(text)) missing.push("legacy severity vocabulary present"); + if (!HAS_P0.test(text) || !HAS_P3.test(text)) problems.push("missing P0–P3 severity scale"); + if (LEGACY_SEVERITY.test(text)) problems.push("legacy severity vocabulary present"); + + // 2. Order — only meaningful once the sections exist. + const phase0 = headingOffset(text, "Phase 0"); + for (const label of FRAMING) { + const off = headingOffset(text, label); + if (off !== -1 && phase0 !== -1 && off > phase0) { + problems.push(`out of order: "${label}" must precede Phase 0`); + } + } + let prev = -1; + let prevLabel = ""; + for (const label of ORDERED) { + const off = headingOffset(text, label); + if (off === -1) continue; // presence check already reported it + if (off < prev) { + problems.push(`out of order: "${label}" appears before "${prevLabel}"`); + } + prev = off; + prevLabel = label; + } + + // 3. Shared finding schema field contract. + const schema = schemaBlock(text); + if (!schema) { + problems.push("Shared finding schema has no ```json block"); + } else { + const missingFields = SCHEMA_FIELDS.filter( + (k) => !new RegExp(`"${k}"\\s*:`).test(schema), + ); + if (missingFields.length) { + problems.push(`schema missing field(s): ${missingFields.join(", ")}`); + } + } - if (missing.length) { + if (problems.length) { failed++; - console.error(`✗ ${f}\n missing/invalid: ${missing.join(" · ")}`); + console.error(`✗ ${f}\n ${problems.join("\n ")}`); } else { console.log(`✓ ${f}`); } diff --git a/web/app/(de)/de/audits/[slug]/opengraph-image.tsx b/web/app/(de)/de/audits/[slug]/opengraph-image.tsx new file mode 100644 index 0000000..44721ed --- /dev/null +++ b/web/app/(de)/de/audits/[slug]/opengraph-image.tsx @@ -0,0 +1,28 @@ +import { auditDetail } from "@/lib/audit-details"; +import { AUDITS, auditTitle } from "@/lib/content"; +import { ogContentType, ogSize, renderOgImage } from "@/lib/og"; + +export const alt = "auditor — Multi-Agenten-Audit-Master-Prompts"; +export const size = ogSize; +export const contentType = ogContentType; + +// Pre-render one card per audit slug; mirror the page's `dynamicParams = false`. +export function generateStaticParams() { + return AUDITS.map((a) => ({ slug: a.name })); +} + +export const dynamicParams = false; + +export default async function OpengraphImageDe({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const { slug } = await params; + const audit = AUDITS.find((a) => a.name === slug); + const detail = auditDetail(slug, "de"); + return renderOgImage({ + title: audit ? auditTitle(audit) : "auditor", + subtitle: detail?.tagline ?? "", + }); +} diff --git a/web/app/(de)/de/audits/[slug]/page.tsx b/web/app/(de)/de/audits/[slug]/page.tsx index ec74c1d..52ee082 100644 --- a/web/app/(de)/de/audits/[slug]/page.tsx +++ b/web/app/(de)/de/audits/[slug]/page.tsx @@ -3,7 +3,6 @@ import { notFound } from "next/navigation"; import { AuditDetailPage } from "@/components/audit-page"; import { auditDetail } from "@/lib/audit-details"; import { AUDITS, auditTitle } from "@/lib/content"; -import { SITE_URL } from "@/lib/site"; export const dynamicParams = false; @@ -34,7 +33,6 @@ export async function generateMetadata({ url: path, title, description: detail.tagline, - images: [`${SITE_URL}/de/opengraph-image`], }, }; } diff --git a/web/app/(en)/audits/[slug]/opengraph-image.tsx b/web/app/(en)/audits/[slug]/opengraph-image.tsx new file mode 100644 index 0000000..31bba07 --- /dev/null +++ b/web/app/(en)/audits/[slug]/opengraph-image.tsx @@ -0,0 +1,28 @@ +import { auditDetail } from "@/lib/audit-details"; +import { AUDITS, auditTitle } from "@/lib/content"; +import { ogContentType, ogSize, renderOgImage } from "@/lib/og"; + +export const alt = "auditor — multi-agent audit master prompts"; +export const size = ogSize; +export const contentType = ogContentType; + +// Pre-render one card per audit slug; mirror the page's `dynamicParams = false`. +export function generateStaticParams() { + return AUDITS.map((a) => ({ slug: a.name })); +} + +export const dynamicParams = false; + +export default async function OpengraphImage({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const { slug } = await params; + const audit = AUDITS.find((a) => a.name === slug); + const detail = auditDetail(slug, "en"); + return renderOgImage({ + title: audit ? auditTitle(audit) : "auditor", + subtitle: detail?.tagline ?? "", + }); +} diff --git a/web/app/(en)/audits/[slug]/page.tsx b/web/app/(en)/audits/[slug]/page.tsx index cfc137b..d2c5b95 100644 --- a/web/app/(en)/audits/[slug]/page.tsx +++ b/web/app/(en)/audits/[slug]/page.tsx @@ -3,7 +3,6 @@ import { notFound } from "next/navigation"; import { AuditDetailPage } from "@/components/audit-page"; import { auditDetail } from "@/lib/audit-details"; import { AUDITS, auditTitle } from "@/lib/content"; -import { SITE_URL } from "@/lib/site"; export const dynamicParams = false; @@ -34,7 +33,6 @@ export async function generateMetadata({ url: path, title, description: detail.tagline, - images: [`${SITE_URL}/opengraph-image`], }, }; } diff --git a/web/components/audit-page.tsx b/web/components/audit-page.tsx index f61e050..c75bbe2 100644 --- a/web/components/audit-page.tsx +++ b/web/components/audit-page.tsx @@ -5,9 +5,10 @@ import { Reveal } from "@/components/reveal"; import { SiteFooter, SiteHeader } from "@/components/site-chrome"; import { buttonVariants } from "@/components/ui/button"; import { cn } from "@/lib/utils"; -import { AUDITS, PROMPTS, auditCommand } from "@/lib/content"; +import { AUDITS, PROMPTS, auditCommand, auditTitle } from "@/lib/content"; import { auditDetail } from "@/lib/audit-details"; import { glossify } from "@/lib/glossary"; +import { SITE_URL } from "@/lib/site"; import { type Lang, t } from "@/lib/i18n"; /** A per-audit detail page: approach + concrete use cases, for deep-linking. */ @@ -21,6 +22,36 @@ export function AuditDetailPage({ name, lang }: { name: string; lang: Lang }) { const home = lang === "de" ? "/de" : "/"; const langHref = lang === "de" ? `/audits/${name}` : `/de/audits/${name}`; + // Canonical URLs mirror the visual breadcrumb and the page's `alternates`. + const title = auditTitle(audit); + const base = lang === "de" ? "/de" : ""; + const pageUrl = `${SITE_URL}${base}/audits/${name}`; + const homeUrl = `${SITE_URL}${base || "/"}`; + const auditsUrl = `${SITE_URL}${base}/#audits`; + + const articleLd = { + "@context": "https://schema.org", + "@type": "TechArticle", + headline: title, + name: title, + description: detail.tagline, + url: pageUrl, + inLanguage: lang === "de" ? "de" : "en", + isPartOf: { "@type": "WebSite", name: "auditor", url: SITE_URL }, + author: { "@type": "Person", name: "Marcel Rapold" }, + image: `${pageUrl}/opengraph-image`, + }; + + const breadcrumbLd = { + "@context": "https://schema.org", + "@type": "BreadcrumbList", + itemListElement: [ + { "@type": "ListItem", position: 1, name: "auditor", item: homeUrl }, + { "@type": "ListItem", position: 2, name: tt.nav.audits, item: auditsUrl }, + { "@type": "ListItem", position: 3, name: audit.name, item: pageUrl }, + ], + }; + const copy = ( +