@aegis-scan/skills 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ATTRIBUTION.md +111 -0
- package/CHANGELOG.md +48 -3
- package/package.json +1 -1
- package/skills/compliance/aegis-native/brutaler-anwalt/CHANGELOG.md +202 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/LICENSE +43 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/README.md +236 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/SKILL.md +339 -5
- package/skills/compliance/aegis-native/brutaler-anwalt/references/aegis-integration.md +3 -4
- package/skills/compliance/aegis-native/brutaler-anwalt/references/audit-patterns.md +842 -5
- package/skills/compliance/aegis-native/brutaler-anwalt/references/bgh-urteile.md +226 -10
- package/skills/compliance/aegis-native/brutaler-anwalt/references/branchenrecht.md +365 -1
- package/skills/compliance/aegis-native/brutaler-anwalt/references/checklisten.md +33 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/dsgvo.md +26 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/BDSG/paragraphs.md +62 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/BFSG/paragraphs.md +85 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/BGB/paragraphs.md +112 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/DDG/paragraphs.md +71 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/DSGVO/articles.md +182 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/EU-Verordnungen/AI-Act-2024-1689/articles.md +108 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/EU-Verordnungen/DSA-2022-2065/articles.md +131 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/HGB-AO/paragraphs.md +61 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/INDEX.md +93 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/TDDDG/paragraphs.md +67 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/UWG/paragraphs.md +117 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/gesetze/VSBG/paragraphs.md +57 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/it-recht.md +22 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/stack-patterns/INDEX.md +122 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/stack-patterns/ai/mistral-eu.md +123 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/stack-patterns/ai/openai-dpa.md +120 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/stack-patterns/auth/nextauth-tom.md +120 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/stack-patterns/auth/supabase-auth-tom.md +104 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/stack-patterns/nextjs/proxy-csp-pattern.md +93 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/stack-patterns/payment/stripe-pci-tom.md +121 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/stack-patterns/tracking/plausible-pattern.md +107 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/AffiliateDisclaimer.tsx.example +54 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/COMPLIANCE-AUDIT-TRAIL-template.md +95 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/DSE-Section-UGC.md.example +77 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/DSFA-template.md +76 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/LostFoundReportForm-consent.tsx.example +126 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/README.md +33 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/UmamiScript.tsx.example +64 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/VVT-template.md +60 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/data-retention-cron.ts.example +52 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/data-retention-workflow.yml.example +47 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/proxy-strict-dynamic.ts.example +80 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/security.txt.example +26 -0
- package/skills/compliance/aegis-native/brutaler-anwalt/scripts/health-check.sh +120 -0
- package/skills/defensive/aegis-native/rls-defense/SKILL.md +85 -0
- package/skills/foundation/aegis-native/aegis-module-builder/SKILL.md +5 -1
- package/skills/foundation/aegis-native/aegis-orchestrator/SKILL.md +87 -4
- package/skills/foundation/aegis-native/aegis-quality-gates/SKILL.md +69 -9
- package/skills/offensive/matty-fork/cicd-redteam/SKILL.md +531 -0
- package/skills/offensive/matty-fork/cloud-security/SKILL.md +106 -0
- package/skills/offensive/matty-fork/container-escape/SKILL.md +174 -0
- package/skills/offensive/matty-fork/mobile-pentester/SKILL.md +357 -0
- package/skills/offensive/matty-fork/subdomain-takeover/SKILL.md +154 -0
- package/skills/osint/elementalsouls-fork/offensive-osint/README.md +92 -0
- package/skills/osint/elementalsouls-fork/offensive-osint/SKILL.md +4177 -0
- package/skills/osint/elementalsouls-fork/osint-methodology/README.md +66 -0
- package/skills/osint/elementalsouls-fork/osint-methodology/SKILL.md +1695 -0
- package/sbom.cdx.json +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
license: MIT
|
|
3
|
+
purpose: Anonymized teaching snippets referenced by audit-patterns.md / dsgvo.md / checklisten.md.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Templates — anonymisierte Lehrbuch-Snippets
|
|
7
|
+
|
|
8
|
+
Diese Templates sind brand-agnostische Vorlagen, die in den References als
|
|
9
|
+
konkrete Lehrbuch-Beispiele zitiert werden. Sie ersetzen die in fruehen
|
|
10
|
+
Skill-Versionen direkt eingebetteten Brand-spezifischen Snippets.
|
|
11
|
+
|
|
12
|
+
**Konvention:**
|
|
13
|
+
- `<placeholder>` = vom Operator zu ersetzen (z.B. `<brand>`, `<your-domain>`)
|
|
14
|
+
- `<...>` in Code-Snippets sind absichtlich syntactically-invalid, damit
|
|
15
|
+
copy-paste-Hygiene erzwungen wird
|
|
16
|
+
- Alle `.example`-Files sind **keine** lauffaehigen Module — Build-Tools
|
|
17
|
+
sollen sie ignorieren
|
|
18
|
+
|
|
19
|
+
## Index
|
|
20
|
+
|
|
21
|
+
| Template | Referenced from | Use case |
|
|
22
|
+
|----------|----------------|----------|
|
|
23
|
+
| `DSFA-template.md` | `dsgvo.md` DSFA-Trigger | Datenschutz-Folgenabschaetzung Doc-Vorlage |
|
|
24
|
+
| `VVT-template.md` | `dsgvo.md` VVT | Verzeichnis Verarbeitungstaetigkeiten Vorlage |
|
|
25
|
+
| `COMPLIANCE-AUDIT-TRAIL-template.md` | (Skill-Output-Pattern) | Audit-Trail-Doku-Vorlage fuer eigene Audits |
|
|
26
|
+
| `AffiliateDisclaimer.tsx.example` | `checklisten.md` 3c | React-Component-Vorlage UWG § 5a Abs. 4 |
|
|
27
|
+
| `proxy-strict-dynamic.ts.example` | `audit-patterns.md` HIGH-RISK-CSP | Next.js proxy-CSP Strict-Dynamic-Pattern |
|
|
28
|
+
| `data-retention-cron.ts.example` | `audit-patterns.md` Phase 4 | Bearer-auth Retention-Cleanup Route |
|
|
29
|
+
| `data-retention-workflow.yml.example` | `audit-patterns.md` Phase 4 | GitHub Actions Cron-Trigger |
|
|
30
|
+
| `UmamiScript.tsx.example` | `audit-patterns.md` env-driven Tracking | env-driven Tracking-Component |
|
|
31
|
+
| `security.txt.example` | `audit-patterns.md` Phase 2 | RFC 9116 (kein Placeholder-Bug) |
|
|
32
|
+
| `DSE-Section-UGC.md.example` | `audit-patterns.md` Phase 5c | Vermisst-/Marketplace-DSE-Block |
|
|
33
|
+
| `LostFoundReportForm-consent.tsx.example` | `audit-patterns.md` Phase 5c | Consent-Toggle-Pattern UGC-Posts |
|
package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/UmamiScript.tsx.example
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// MIT-License — anonymized teaching snippet for brutaler-anwalt
|
|
2
|
+
// References: audit-patterns.md env-driven Tracking-Component
|
|
3
|
+
// Pattern: env-driven Umami / Plausible / Fathom Tracking-Snippet
|
|
4
|
+
|
|
5
|
+
// File: src/components/analytics/UmamiScript.tsx
|
|
6
|
+
// Use: include in app/layout.tsx (root layout). Loads only after consent OR
|
|
7
|
+
// if the tracker is configured "cookieless + IP-anon + DNT respect".
|
|
8
|
+
|
|
9
|
+
import Script from 'next/script';
|
|
10
|
+
|
|
11
|
+
interface UmamiScriptProps {
|
|
12
|
+
/** override the env-default; pass site-id explicitly when SSR-safe */
|
|
13
|
+
websiteId?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function UmamiScript({ websiteId }: UmamiScriptProps) {
|
|
17
|
+
// 1. host: env-driven; default = your own analytics subdomain (NOT vendor-default cloud).
|
|
18
|
+
// NEVER hardcode a vendor cloud URL — that's a Drittland-Trigger.
|
|
19
|
+
const host = (
|
|
20
|
+
process.env.NEXT_PUBLIC_ANALYTICS_HOST ??
|
|
21
|
+
'https://<your-analytics-subdomain>'
|
|
22
|
+
).replace(/\/+$/, '');
|
|
23
|
+
|
|
24
|
+
// 2. site-id from env (or prop override)
|
|
25
|
+
const id = websiteId ?? process.env.NEXT_PUBLIC_ANALYTICS_SITE_ID;
|
|
26
|
+
|
|
27
|
+
// 3. fail-soft: no tracking if env not configured (better than fallback to default-cloud)
|
|
28
|
+
if (!id) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Script
|
|
34
|
+
strategy="afterInteractive"
|
|
35
|
+
src={`${host}/script.js`}
|
|
36
|
+
data-website-id={id}
|
|
37
|
+
data-host-url={host}
|
|
38
|
+
// Privacy-Hardening: respect DNT and GPC client-side (server-side opt: track-DNT off)
|
|
39
|
+
data-do-not-track="true"
|
|
40
|
+
// Cookieless mode + IP-anonymisation are server-side settings.
|
|
41
|
+
// The DSE statement MUST match the actual server-config — verify with admin-panel.
|
|
42
|
+
async
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// USAGE in app/layout.tsx:
|
|
48
|
+
//
|
|
49
|
+
// import { UmamiScript } from '@/components/analytics/UmamiScript';
|
|
50
|
+
// export default function RootLayout({ children }) {
|
|
51
|
+
// return (
|
|
52
|
+
// <html><body>{children}<UmamiScript /></body></html>
|
|
53
|
+
// );
|
|
54
|
+
// }
|
|
55
|
+
//
|
|
56
|
+
// VERIFY:
|
|
57
|
+
// curl -s https://<your-domain> | grep -oE 'data-host-url="[^"]+"'
|
|
58
|
+
// # erwarte: dein operator-eigener Analytics-Host, nie ein Vendor-Cloud-Default
|
|
59
|
+
//
|
|
60
|
+
// DSE-PFLICHT (analog dazu):
|
|
61
|
+
// "Wir nutzen das selbstgehostete Analyse-Tool [Tool-Name] auf <your-analytics-subdomain>.
|
|
62
|
+
// Verarbeitung erfolgt cookieless mit serverseitiger IP-Anonymisierung. DNT/GPC werden
|
|
63
|
+
// respektiert. Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse an
|
|
64
|
+
// aggregierter Reichweitenmessung). Widerspruch jederzeit moeglich..."
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
license: MIT
|
|
3
|
+
purpose: Generische VVT-Vorlage (Art. 30 DSGVO). KMU-best-practice.
|
|
4
|
+
references: dsgvo.md (VVT-Block)
|
|
5
|
+
sources: Art. 30 Abs. 1 DSGVO + BayLDA-VVT-Hinweise
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Verzeichnis von Verarbeitungstaetigkeiten (VVT) — Vorlage
|
|
9
|
+
|
|
10
|
+
> Diese Vorlage entspricht Art. 30 Abs. 1 DSGVO. KMU mit < 250 MA und
|
|
11
|
+
> gelegentlicher Verarbeitung ohne Sonderkategorien sind nicht VVT-pflichtig
|
|
12
|
+
> (Art. 30 Abs. 5), aber BayLDA empfiehlt VVT auch fuer KMU zur Erfuellung
|
|
13
|
+
> Rechenschaftspflicht Art. 5 Abs. 2.
|
|
14
|
+
|
|
15
|
+
## Stammblatt
|
|
16
|
+
|
|
17
|
+
| Feld | Wert |
|
|
18
|
+
|------|------|
|
|
19
|
+
| Verantwortlicher | `<Operator-Firma>` |
|
|
20
|
+
| Anschrift | `<vollstaendige-Anschrift>` |
|
|
21
|
+
| Vertreter (Art. 27) | `<falls EU-Drittland-Sitz>` |
|
|
22
|
+
| DSB | `<falls bestellt>` |
|
|
23
|
+
| Stand | `<YYYY-MM-DD>` |
|
|
24
|
+
| Version | `<vN.N>` |
|
|
25
|
+
|
|
26
|
+
## Verarbeitungstaetigkeiten
|
|
27
|
+
|
|
28
|
+
Pro Verarbeitung ein Block.
|
|
29
|
+
|
|
30
|
+
### VT-001: `<Bezeichnung>`
|
|
31
|
+
|
|
32
|
+
| Pflicht-Feld (Art. 30 Abs. 1) | Wert |
|
|
33
|
+
|------------------------------|------|
|
|
34
|
+
| **a) Name + Kontaktdaten Verantwortlicher** | siehe Stammblatt |
|
|
35
|
+
| **b) Zwecke der Verarbeitung** | `<Zweck>` (Rechtsgrundlage Art. 6 Abs. 1 lit. `<a/b/c/d/e/f>`) |
|
|
36
|
+
| **c) Kategorien betroffener Personen** | `<Kunden / Mitarbeiter / Lieferanten / ...>` |
|
|
37
|
+
| **c) Kategorien personenbezogener Daten** | `<Stammdaten / Kontaktdaten / Nutzungsdaten / besondere Kategorien>` |
|
|
38
|
+
| **d) Kategorien von Empfaengern** | `<intern / Auftragsverarbeiter / Drittland>` |
|
|
39
|
+
| **e) Drittlandtransfer** | `<keine / USA / UK / ...>` (mit Mechanismus: SCC + TIA / Adequacy / DPF) |
|
|
40
|
+
| **f) Speicherdauer / Loeschfristen** | `<Frist>` (gesetzlicher Anker, z.B. § 257 HGB / § 147 AO) |
|
|
41
|
+
| **g) Allgemeine Beschreibung TOMs** | siehe `<TOMs-Doku-Verweis>` |
|
|
42
|
+
|
|
43
|
+
### VT-002: `<naechste Verarbeitung>`
|
|
44
|
+
|
|
45
|
+
(analog)
|
|
46
|
+
|
|
47
|
+
## Auftragsverarbeiter (Art. 28)
|
|
48
|
+
|
|
49
|
+
| Auftragsverarbeiter | Zweck | AVV-Status | Drittland | Standort |
|
|
50
|
+
|---------------------|-------|-----------|-----------|----------|
|
|
51
|
+
| `<Anbieter>` | `<Zweck>` | `<abgeschlossen YYYY-MM-DD>` | `<DE/EU/USA>` | `<Region>` |
|
|
52
|
+
|
|
53
|
+
## Review
|
|
54
|
+
|
|
55
|
+
VVT bei wesentlichen Aenderungen sofort updaten, ansonsten jaehrlich.
|
|
56
|
+
Naechstes Review: `<YYYY-MM-DD>`.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
*Disclaimer: technisch-indikative Vorlage, keine Rechtsberatung i.S.d. § 2 RDG.*
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// MIT-License — anonymized teaching snippet for brutaler-anwalt
|
|
2
|
+
// References: audit-patterns.md Phase 4 DSE-Drift-Audit Style 2 (DSE-Aussage "wir loeschen nach X")
|
|
3
|
+
// Pattern: Next.js API-Route mit Bearer-Auth, idempotent, audit-logged
|
|
4
|
+
|
|
5
|
+
// File: src/app/api/cron/data-retention/route.ts (Next.js App-Router)
|
|
6
|
+
|
|
7
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
8
|
+
|
|
9
|
+
const RETENTION_DAYS = Number(process.env.DATA_RETENTION_DAYS ?? '180');
|
|
10
|
+
|
|
11
|
+
export async function POST(req: NextRequest) {
|
|
12
|
+
// 1. Bearer-Auth — Cron-Secret aus env, nicht hardcoded
|
|
13
|
+
const authHeader = req.headers.get('authorization') ?? '';
|
|
14
|
+
const expected = `Bearer ${process.env.CRON_SECRET}`;
|
|
15
|
+
if (!process.env.CRON_SECRET || authHeader !== expected) {
|
|
16
|
+
return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 2. Compute cutoff
|
|
20
|
+
const cutoff = new Date(Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1000);
|
|
21
|
+
|
|
22
|
+
// 3. Delete-Logik — pro Tabelle / Collection / Bucket einzeln
|
|
23
|
+
const results = {
|
|
24
|
+
cutoff: cutoff.toISOString(),
|
|
25
|
+
deleted: {} as Record<string, number>,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Pseudocode — durch echten DB-Client ersetzen
|
|
30
|
+
// results.deleted.session_logs = await db.sessionLogs.deleteMany({ created_at: { lt: cutoff } });
|
|
31
|
+
// results.deleted.lost_found_posts = await db.lostFoundPosts.deleteMany({ expires_at: { lt: new Date() } });
|
|
32
|
+
// results.deleted.unverified_signups = await db.users.deleteMany({ verified: false, created_at: { lt: cutoff } });
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.error('[retention-cron] error', err);
|
|
35
|
+
return NextResponse.json({ error: 'cleanup-failed', details: String(err) }, { status: 500 });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 4. Audit-Log — Pflicht fuer Rechenschafts-Nachweis Art. 5 Abs. 2 DSGVO
|
|
39
|
+
console.log(JSON.stringify({
|
|
40
|
+
event: 'data_retention_cleanup',
|
|
41
|
+
timestamp: new Date().toISOString(),
|
|
42
|
+
cutoff: results.cutoff,
|
|
43
|
+
deleted: results.deleted,
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
return NextResponse.json(results);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// VERIFY:
|
|
50
|
+
// curl -X POST https://<your-domain>/api/cron/data-retention \
|
|
51
|
+
// -H "Authorization: Bearer $CRON_SECRET"
|
|
52
|
+
// # erwarte 200 + JSON mit deleted-counts; ohne Auth 401.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
## MIT-License — anonymized teaching snippet for brutaler-anwalt
|
|
2
|
+
## References: audit-patterns.md Phase 4 DSE-Drift-Audit Style 2
|
|
3
|
+
## Pattern: GitHub Actions Cron-Trigger fuer DSGVO-Retention-Cleanup
|
|
4
|
+
|
|
5
|
+
# File: .github/workflows/data-retention.yml
|
|
6
|
+
|
|
7
|
+
name: data-retention-cleanup
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
schedule:
|
|
11
|
+
# tgl. 03:00 UTC = 04:00 CET (low-traffic-window)
|
|
12
|
+
- cron: '0 3 * * *'
|
|
13
|
+
workflow_dispatch: {}
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
cleanup:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 5
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
steps:
|
|
22
|
+
- name: Trigger retention API
|
|
23
|
+
env:
|
|
24
|
+
CRON_SECRET: ${{ secrets.CRON_SECRET }}
|
|
25
|
+
API_URL: ${{ secrets.RETENTION_API_URL }} # https://<your-domain>/api/cron/data-retention
|
|
26
|
+
run: |
|
|
27
|
+
if [ -z "$CRON_SECRET" ] || [ -z "$API_URL" ]; then
|
|
28
|
+
echo "::error::CRON_SECRET or RETENTION_API_URL not set in GH secrets"
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
response=$(curl -sS -w "\n%{http_code}" -X POST "$API_URL" \
|
|
32
|
+
-H "Authorization: Bearer $CRON_SECRET" \
|
|
33
|
+
-H "Content-Type: application/json" \
|
|
34
|
+
--max-time 60)
|
|
35
|
+
body=$(echo "$response" | sed '$d')
|
|
36
|
+
status=$(echo "$response" | tail -n1)
|
|
37
|
+
echo "::group::API response"
|
|
38
|
+
echo "$body"
|
|
39
|
+
echo "::endgroup::"
|
|
40
|
+
if [ "$status" != "200" ]; then
|
|
41
|
+
echo "::error::retention API returned status $status"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# VERIFY in repo:
|
|
46
|
+
# gh workflow run data-retention-cleanup
|
|
47
|
+
# gh run list --workflow=data-retention-cleanup --limit 5
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// MIT-License — anonymized teaching snippet for brutaler-anwalt
|
|
2
|
+
// References: audit-patterns.md HIGH-RISK CSP unsafe-inline Migration
|
|
3
|
+
// Pattern: Next.js (App Router) middleware.ts / proxy.ts mit Strict-Dynamic-CSP
|
|
4
|
+
|
|
5
|
+
// File: src/middleware.ts (oder src/proxy.ts) — Next.js 14+
|
|
6
|
+
// Pattern: per-request nonce → CSP-Header → propagate via x-nonce request-header.
|
|
7
|
+
// Layout liest x-nonce via headers().get('x-nonce') und gibt es an inline-Scripts.
|
|
8
|
+
|
|
9
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
10
|
+
|
|
11
|
+
const cspDirectives = (nonce: string) => [
|
|
12
|
+
`default-src 'self'`,
|
|
13
|
+
// Strict-Dynamic + nonce: ersetzt unsafe-inline ohne legitime inline-scripts zu brechen
|
|
14
|
+
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic' https:`,
|
|
15
|
+
// Style: nonce + self; unsafe-inline weiterhin nur zugelassen wenn unvermeidbar
|
|
16
|
+
`style-src 'self' 'nonce-${nonce}'`,
|
|
17
|
+
`img-src 'self' data: https://<your-cdn-domain>`,
|
|
18
|
+
`font-src 'self' data:`,
|
|
19
|
+
// Connect: API-Endpoints + 3rd-party-Services aus DSE
|
|
20
|
+
`connect-src 'self' https://<api-domain> https://<analytics-host>`,
|
|
21
|
+
`frame-src 'self' https://<embed-domains>`,
|
|
22
|
+
`object-src 'none'`,
|
|
23
|
+
`base-uri 'self'`,
|
|
24
|
+
`form-action 'self'`,
|
|
25
|
+
`frame-ancestors 'none'`,
|
|
26
|
+
`upgrade-insecure-requests`,
|
|
27
|
+
].join('; ');
|
|
28
|
+
|
|
29
|
+
export function middleware(req: NextRequest) {
|
|
30
|
+
// 1. Generate per-request nonce (16 random bytes, base64)
|
|
31
|
+
const nonce = btoa(crypto.getRandomValues(new Uint8Array(16)).join(''));
|
|
32
|
+
|
|
33
|
+
// 2. Forward via request-header so layout/components can read it
|
|
34
|
+
const requestHeaders = new Headers(req.headers);
|
|
35
|
+
requestHeaders.set('x-nonce', nonce);
|
|
36
|
+
|
|
37
|
+
// 3. Build response with CSP-Header
|
|
38
|
+
const response = NextResponse.next({ request: { headers: requestHeaders } });
|
|
39
|
+
response.headers.set('Content-Security-Policy', cspDirectives(nonce));
|
|
40
|
+
|
|
41
|
+
// 4. Defense-in-depth headers
|
|
42
|
+
response.headers.set('X-Frame-Options', 'DENY');
|
|
43
|
+
response.headers.set('X-Content-Type-Options', 'nosniff');
|
|
44
|
+
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
45
|
+
response.headers.set(
|
|
46
|
+
'Strict-Transport-Security',
|
|
47
|
+
'max-age=63072000; includeSubDomains; preload',
|
|
48
|
+
);
|
|
49
|
+
response.headers.set(
|
|
50
|
+
'Permissions-Policy',
|
|
51
|
+
'camera=(), microphone=(), geolocation=(self), interest-cohort=()',
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return response;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const config = {
|
|
58
|
+
matcher: '/:path*',
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// USAGE in layout.tsx:
|
|
62
|
+
//
|
|
63
|
+
// import { headers } from 'next/headers';
|
|
64
|
+
// export default function RootLayout({ children }) {
|
|
65
|
+
// const nonce = headers().get('x-nonce') ?? '';
|
|
66
|
+
// return (
|
|
67
|
+
// <html>
|
|
68
|
+
// <body>
|
|
69
|
+
// <Script id="bootstrap" nonce={nonce} strategy="beforeInteractive">
|
|
70
|
+
// {`/* inline bootstrap */`}
|
|
71
|
+
// </Script>
|
|
72
|
+
// {children}
|
|
73
|
+
// </body>
|
|
74
|
+
// </html>
|
|
75
|
+
// );
|
|
76
|
+
// }
|
|
77
|
+
//
|
|
78
|
+
// VERIFY:
|
|
79
|
+
// curl -sIS https://<your-domain> | grep -i 'content-security-policy'
|
|
80
|
+
// # erwarte: 'nonce-...' + 'strict-dynamic' enthalten, KEIN 'unsafe-inline'
|
package/skills/compliance/aegis-native/brutaler-anwalt/references/templates/security.txt.example
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# MIT-License — anonymized teaching snippet for brutaler-anwalt
|
|
2
|
+
# References: audit-patterns.md Phase 2 Public-Static-File-Audit
|
|
3
|
+
# Spec: RFC 9116 (https://www.rfc-editor.org/rfc/rfc9116)
|
|
4
|
+
#
|
|
5
|
+
# File: public/.well-known/security.txt
|
|
6
|
+
# Critical: KEINE Placeholder-Tokens (`{{...}}`, `<...>`, `YOUR_*`, `AGENT:`,
|
|
7
|
+
# `TODO:`, `FIXME:`) im Production-Build. Die folgenden Werte sind
|
|
8
|
+
# OPERATOR-VERANTWORTUNG, vor Deploy konkret zu setzen.
|
|
9
|
+
|
|
10
|
+
Contact: mailto:security@<your-domain>
|
|
11
|
+
Contact: https://<your-domain>/security/contact
|
|
12
|
+
Expires: 2027-12-31T23:59:59Z
|
|
13
|
+
Encryption: https://<your-domain>/.well-known/pgp-key.txt
|
|
14
|
+
Acknowledgments: https://<your-domain>/security/hall-of-fame
|
|
15
|
+
Preferred-Languages: de, en
|
|
16
|
+
Canonical: https://<your-domain>/.well-known/security.txt
|
|
17
|
+
Policy: https://<your-domain>/security/responsible-disclosure
|
|
18
|
+
Hiring: https://<your-domain>/karriere
|
|
19
|
+
|
|
20
|
+
# Pre-Deploy-Verify:
|
|
21
|
+
# curl -s https://<your-domain>/.well-known/security.txt | \
|
|
22
|
+
# grep -E '\{\{|<[A-Z_]+>|YOUR_|AGENT:|ASSISTANT:|TODO:|FIXME:|placeholder' && \
|
|
23
|
+
# echo "🔴 KRITISCH — unrendered Template" || echo "✓ clean"
|
|
24
|
+
#
|
|
25
|
+
# Expires-Datum: alle 6-12 Monate updaten. Das Datum ist hier ein Lehrbuch-Wert
|
|
26
|
+
# und MUSS vom Operator vor Deploy gesetzt werden. RFC 9116 verlangt < 1 Jahr.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# brutaler-anwalt — Health-Check for skill consistency.
|
|
3
|
+
# Usage: bash scripts/health-check.sh
|
|
4
|
+
# Exit: 0 healthy · 1 issues found
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
9
|
+
issues=0
|
|
10
|
+
|
|
11
|
+
echo "▎ brutaler-anwalt Health-Check"
|
|
12
|
+
echo "▎ Skill-Dir: $SKILL_DIR"
|
|
13
|
+
echo
|
|
14
|
+
|
|
15
|
+
# 1. Brand-Leak-Check — operator-customizable deny-list of brand-codenames
|
|
16
|
+
# that must NOT appear in shipped skill content (SKILL.md / references / etc).
|
|
17
|
+
#
|
|
18
|
+
# CUSTOMIZATION: edit BRAND_PATTERN below to add your own brand-codenames
|
|
19
|
+
# (single-bar-separated, regex-syntax). Example: if your projects are named
|
|
20
|
+
# `acme-saas` and `bluefin`, set:
|
|
21
|
+
# BRAND_PATTERN="acme-saas|bluefin|your-internal-codename"
|
|
22
|
+
# Defaults are placeholder examples; replace before relying on the check.
|
|
23
|
+
#
|
|
24
|
+
# The check reads the deny-list from a gitignored sibling file when present,
|
|
25
|
+
# so operators can keep their real codenames out of the public skill repo.
|
|
26
|
+
echo "1/5 Brand-Leak-Check…"
|
|
27
|
+
LOCAL_DENY_FILE="$SKILL_DIR/scripts/brand-deny-list.local.txt"
|
|
28
|
+
if [[ -f "$LOCAL_DENY_FILE" ]]; then
|
|
29
|
+
# one pattern per line, joined with | for grep -E
|
|
30
|
+
BRAND_PATTERN=$(grep -vE '^[[:space:]]*(#|$)' "$LOCAL_DENY_FILE" | tr '\n' '|' | sed 's/|$//')
|
|
31
|
+
if [[ -z "$BRAND_PATTERN" ]]; then
|
|
32
|
+
BRAND_PATTERN="placeholder-codename-example"
|
|
33
|
+
fi
|
|
34
|
+
else
|
|
35
|
+
BRAND_PATTERN="placeholder-codename-example|placeholder-internal-project"
|
|
36
|
+
fi
|
|
37
|
+
brand_hits=$( (grep -rEnli "$BRAND_PATTERN" \
|
|
38
|
+
"$SKILL_DIR/SKILL.md" "$SKILL_DIR/README.md" "$SKILL_DIR/LICENSE" "$SKILL_DIR/CHANGELOG.md" "$SKILL_DIR/references/" 2>/dev/null || true) \
|
|
39
|
+
| wc -l | tr -d ' ')
|
|
40
|
+
if [[ "$brand_hits" == "0" ]]; then
|
|
41
|
+
echo " ✓ keine Brand-Leaks (Pattern: $BRAND_PATTERN)"
|
|
42
|
+
else
|
|
43
|
+
echo " ✗ $brand_hits Brand-Leak-Treffer:"
|
|
44
|
+
grep -rEni "$BRAND_PATTERN" \
|
|
45
|
+
"$SKILL_DIR/SKILL.md" "$SKILL_DIR/README.md" "$SKILL_DIR/LICENSE" "$SKILL_DIR/CHANGELOG.md" "$SKILL_DIR/references/" 2>/dev/null | head -10 || true
|
|
46
|
+
issues=$((issues + 1))
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# 2. Az.-Provenance — every entry should have a Source-URL
|
|
50
|
+
echo "2/5 Az.-Provenance-Check…"
|
|
51
|
+
az_count=$( (grep -cE "^### " "$SKILL_DIR/references/bgh-urteile.md" 2>/dev/null || echo 0) | head -1)
|
|
52
|
+
# Source-Verlinkung in beliebigem Markdown-Format: **Source**, "Source:", oder eingebetteter http(s)-Link.
|
|
53
|
+
src_count=$( (grep -cE "\*\*Source\*\*|Source:|https?://(juris|curia|dejure|openjur|rewis|edpb|gesetze-im-internet|eur-lex|bverwg|bag-urteil|nrwe|wettbewerbszentrale|noerr|twobirds|bird-bird|alro-recht)" "$SKILL_DIR/references/bgh-urteile.md" 2>/dev/null || echo 0) | head -1)
|
|
54
|
+
echo " Az.-Eintraege: $az_count · Source-Verlinkungen: $src_count"
|
|
55
|
+
if [[ "$src_count" -lt "$az_count" ]]; then
|
|
56
|
+
diff=$((az_count - src_count))
|
|
57
|
+
echo " ⚠ $diff Eintraege ohne Source-Verlinkung — Provenance-Disziplin §5 pruefen"
|
|
58
|
+
issues=$((issues + 1))
|
|
59
|
+
else
|
|
60
|
+
echo " ✓ alle Eintraege haben Source-Verlinkung"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# 3. Verzeichnis-Vollstaendigkeit
|
|
64
|
+
echo "3/5 Verzeichnis-Vollstaendigkeit…"
|
|
65
|
+
required=("SKILL.md" "README.md" "LICENSE" "CHANGELOG.md" \
|
|
66
|
+
"references/audit-patterns.md" "references/dsgvo.md" "references/it-recht.md" \
|
|
67
|
+
"references/vertragsrecht.md" "references/checklisten.md" "references/branchenrecht.md" \
|
|
68
|
+
"references/bgh-urteile.md" "references/abmahn-templates.md" "references/aegis-integration.md" \
|
|
69
|
+
"references/international.md" "references/strafrecht-steuer.md" \
|
|
70
|
+
"references/templates/README.md" \
|
|
71
|
+
"references/gesetze/INDEX.md" \
|
|
72
|
+
"references/stack-patterns/INDEX.md")
|
|
73
|
+
|
|
74
|
+
missing=0
|
|
75
|
+
for f in "${required[@]}"; do
|
|
76
|
+
if [[ ! -f "$SKILL_DIR/$f" ]]; then
|
|
77
|
+
echo " ✗ fehlt: $f"
|
|
78
|
+
missing=$((missing + 1))
|
|
79
|
+
fi
|
|
80
|
+
done
|
|
81
|
+
if [[ "$missing" == "0" ]]; then
|
|
82
|
+
echo " ✓ alle ${#required[@]} Pflicht-Files vorhanden"
|
|
83
|
+
else
|
|
84
|
+
echo " ✗ $missing Pflicht-Files fehlen"
|
|
85
|
+
issues=$((issues + 1))
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# 4. SKILL.md Reference-Loading-Map → File-Vorhandensein
|
|
89
|
+
echo "4/5 Reference-Loading-Map konsistent…"
|
|
90
|
+
map_files=$(grep -oE 'references/[a-z_-]+\.md' "$SKILL_DIR/SKILL.md" 2>/dev/null | sort -u)
|
|
91
|
+
for f in $map_files; do
|
|
92
|
+
if [[ ! -f "$SKILL_DIR/$f" ]]; then
|
|
93
|
+
echo " ✗ SKILL.md verlinkt $f, aber Datei fehlt"
|
|
94
|
+
issues=$((issues + 1))
|
|
95
|
+
fi
|
|
96
|
+
done
|
|
97
|
+
echo " ✓ alle in SKILL.md referenzierten Files vorhanden"
|
|
98
|
+
|
|
99
|
+
# 5. Templates ohne Brand-Leak — re-uses the BRAND_PATTERN configured above
|
|
100
|
+
# in section 1. Templates must not contain any operator-specific codename.
|
|
101
|
+
echo "5/5 Templates anonymisiert…"
|
|
102
|
+
template_brand_hits=$( (grep -rEnli "$BRAND_PATTERN" \
|
|
103
|
+
"$SKILL_DIR/references/templates/" 2>/dev/null || true) | wc -l | tr -d ' ')
|
|
104
|
+
if [[ "$template_brand_hits" == "0" ]]; then
|
|
105
|
+
echo " ✓ alle Templates anonymisiert"
|
|
106
|
+
else
|
|
107
|
+
echo " ✗ Templates enthalten Brand-Refs (sollten anonym sein):"
|
|
108
|
+
grep -rEni "$BRAND_PATTERN" \
|
|
109
|
+
"$SKILL_DIR/references/templates/" 2>/dev/null | head -10 || true
|
|
110
|
+
issues=$((issues + 1))
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
echo
|
|
114
|
+
if [[ "$issues" == "0" ]]; then
|
|
115
|
+
echo "✓ Health-Check passed — Skill ist konsistent."
|
|
116
|
+
exit 0
|
|
117
|
+
else
|
|
118
|
+
echo "✗ Health-Check failed — $issues Issues gefunden."
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
|
@@ -129,6 +129,91 @@ $$;
|
|
|
129
129
|
|
|
130
130
|
**SECURITY DEFINER without `SET search_path` is a search-path-poisoning vulnerability** — the function inherits the caller's search_path and an attacker who can prepend their own schema can hijack the function.
|
|
131
131
|
|
|
132
|
+
### 4a. SECURITY DEFINER RPC + `p_user_id` parameter — canonical authz guard
|
|
133
|
+
|
|
134
|
+
Pattern surfaced in production audits (CWE-863, IDOR via RPC): SECURITY DEFINER RPCs in the `public` schema accept a `p_user_id` parameter without verifying it equals `auth.uid()`. Every signed-in user can then call the RPC with any other user's id and act on their data:
|
|
135
|
+
|
|
136
|
+
- spend OTHER users' loyalty points (`purchase_item(other_uid, ...)`)
|
|
137
|
+
- award themselves arbitrary points (`award_points(my_uid, 999999)`)
|
|
138
|
+
- redeem rewards or finish duels on behalf of other users
|
|
139
|
+
|
|
140
|
+
These are **silent vulnerabilities** until Supabase ships a linter rule that surfaces them. Splinter rules `0028_anon_security_definer_function_executable` + `0029_authenticated_security_definer_function_executable` (added in early 2026) flag the privilege side, but they are argument-blind — a function can be linter-clean but still missing the internal `auth.uid()` check. Static migration audit catches the body-side gap at PR review.
|
|
141
|
+
|
|
142
|
+
**Canonical fix — install a single guard helper, then PERFORM it at the top of every RPC that takes a user-identity parameter:**
|
|
143
|
+
|
|
144
|
+
```sql
|
|
145
|
+
-- One helper, owned by you, called everywhere
|
|
146
|
+
CREATE OR REPLACE FUNCTION public._aegis_authorize_user(p_user_id uuid)
|
|
147
|
+
RETURNS void
|
|
148
|
+
LANGUAGE plpgsql
|
|
149
|
+
SECURITY INVOKER
|
|
150
|
+
STABLE
|
|
151
|
+
SET search_path = ''
|
|
152
|
+
AS $$
|
|
153
|
+
BEGIN
|
|
154
|
+
IF (SELECT auth.role()) = 'service_role' THEN
|
|
155
|
+
RETURN; -- server-side admin / cron bypass
|
|
156
|
+
END IF;
|
|
157
|
+
IF (SELECT auth.uid()) IS NULL OR (SELECT auth.uid()) <> p_user_id THEN
|
|
158
|
+
RAISE EXCEPTION 'AEGIS-AUTHZ: caller % may not act on user %',
|
|
159
|
+
coalesce((SELECT auth.uid())::text, 'anon'), p_user_id
|
|
160
|
+
USING ERRCODE = '42501';
|
|
161
|
+
END IF;
|
|
162
|
+
END;
|
|
163
|
+
$$;
|
|
164
|
+
|
|
165
|
+
-- Every RPC that touches another user's data calls this on entry
|
|
166
|
+
CREATE FUNCTION public.purchase_arena_item(p_user_id uuid, p_item_key text)
|
|
167
|
+
RETURNS jsonb LANGUAGE plpgsql SECURITY DEFINER
|
|
168
|
+
SET search_path = public, pg_temp AS $$
|
|
169
|
+
BEGIN
|
|
170
|
+
PERFORM public._aegis_authorize_user(p_user_id); -- ← MANDATORY first line
|
|
171
|
+
-- ... actual logic
|
|
172
|
+
END $$;
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Linter-clean grants — REVOKE from PUBLIC, not from anon:**
|
|
176
|
+
The default `EXECUTE` privilege on a SQL function is granted to `PUBLIC`, which transitively includes `anon`, `authenticated`, AND `service_role`. `REVOKE EXECUTE FROM anon` does **not** remove anon's access while the PUBLIC grant exists. To restrict to `authenticated` + `service_role`, you must:
|
|
177
|
+
|
|
178
|
+
```sql
|
|
179
|
+
REVOKE ALL ON FUNCTION public.<name>(<args>) FROM PUBLIC, anon;
|
|
180
|
+
GRANT EXECUTE ON FUNCTION public.<name>(<args>) TO authenticated, service_role;
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Bulk programmatic application** (idempotent — auto-discovers all guarded RPCs):
|
|
184
|
+
|
|
185
|
+
```sql
|
|
186
|
+
DO $$
|
|
187
|
+
DECLARE r record;
|
|
188
|
+
BEGIN
|
|
189
|
+
FOR r IN
|
|
190
|
+
SELECT p.proname, pg_get_function_identity_arguments(p.oid) AS args
|
|
191
|
+
FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
192
|
+
WHERE n.nspname = 'public' AND p.prosecdef = true
|
|
193
|
+
AND p.prorettype <> 'trigger'::regtype
|
|
194
|
+
AND p.prosrc ~ '_aegis_authorize_user' -- only the guarded ones
|
|
195
|
+
LOOP
|
|
196
|
+
EXECUTE format('REVOKE ALL ON FUNCTION public.%I(%s) FROM PUBLIC, anon',
|
|
197
|
+
r.proname, r.args);
|
|
198
|
+
EXECUTE format('GRANT EXECUTE ON FUNCTION public.%I(%s) TO authenticated, service_role',
|
|
199
|
+
r.proname, r.args);
|
|
200
|
+
END LOOP;
|
|
201
|
+
END $$;
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Decision tree for a SECURITY DEFINER function in `public`:**
|
|
205
|
+
|
|
206
|
+
| Function shape | Treatment |
|
|
207
|
+
|---|---|
|
|
208
|
+
| Returns `trigger` (called only by trigger system) | `REVOKE EXECUTE FROM PUBLIC, anon, authenticated`. Triggers run as table-owner regardless. |
|
|
209
|
+
| Cron / batch / admin-only (`cleanup_*`, `auto_unban_*`, `expire_*`) | `REVOKE FROM PUBLIC, anon, authenticated`. Only `service_role` may call. No internal guard needed. |
|
|
210
|
+
| User-callable RPC with `p_user_id` parameter | Inject `PERFORM _aegis_authorize_user(p_user_id);` at top. `REVOKE FROM PUBLIC, anon`; `GRANT TO authenticated, service_role`. |
|
|
211
|
+
| User-callable RPC with resource ID (`p_dog_id`, `p_post_id`) | Verify caller-ownership (or prefer `SECURITY INVOKER` and let RLS filter). |
|
|
212
|
+
| Read-only data accessor over RLS-protected tables | Switch to `SECURITY INVOKER`. RLS handles authz. |
|
|
213
|
+
| PostGIS / extension-owned C functions | Cannot revoke (extension owns them). Move PostGIS to `extensions` schema instead. |
|
|
214
|
+
|
|
215
|
+
**Why this surfaces in established projects without warning:** the Supabase database linter is rule-versioned. New rules ship without a code-change in your repo, and they retroactively flag patterns that were always exposures but were never explicitly checked. **Run `get_advisors` (or the SQL surrogates above) on every deploy**, not just at release.
|
|
216
|
+
|
|
132
217
|
### 5. Defensive testing — every policy needs a regression test
|
|
133
218
|
|
|
134
219
|
```sql
|
|
@@ -61,11 +61,15 @@ What does this feature do?
|
|
|
61
61
|
- User-story (1-2 sentences)
|
|
62
62
|
- Inputs (request shape, params, files)
|
|
63
63
|
- Outputs (response shape, side-effects)
|
|
64
|
-
- Acceptance-criteria (3-5 bullet points)
|
|
64
|
+
- Acceptance-criteria (3-5 bullet points, observable + independently verifiable)
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
Don't infer from chat-context. Demand the spec.
|
|
68
68
|
|
|
69
|
+
### Plans.md task discipline
|
|
70
|
+
|
|
71
|
+
Every module-build creates a row in `.aegis/Plans.md` per the format defined in `aegis-orchestrator` ("Plans.md — Live Working-Plan SSOT" section). The acceptance-criteria from the feature-spec become the AC checkboxes on the task row. As phases 2-6 run, the AC are checked off; task moves DONE only when all are checked. If a phase is blocked, the AC stays unchanged + the blocker is documented in `## Blockers`.
|
|
72
|
+
|
|
69
73
|
---
|
|
70
74
|
|
|
71
75
|
## Process
|