sec-scan
Anbieterneutraler Security-Scan eines eigenen Repos: Bedrohungsmodell, Aufteilung nach Vertrauensgrenzen, gezielte Prüfung je Paket (Injection, Authn/Authz, Secrets, Krypto, Deserialisierung, Pfade, S
SKILL.mdskills/dev/sec-scan/SKILL.md
GitHub<img src="banner.png" width="100%" alt="sec-scan banner">
sec-scan
Kern der ellmos-Security-Familie. Der gemeinsame Vertrag (Phasen, Dateien, Lebenszyklus, Severity, Regeln, Adapter) steht in references/contract.md, Schemas in schemas/, Validator in scripts/secfind.py + scripts/seclib.py (nur Standardbibliothek). Dieser Skill zitiert den Vertrag, wiederholt ihn nicht. Nachbarn: sec-threat-model, sec-diff-scan, sec-validate, sec-triage, sec-report.
Modi
- standard: ein Durchlauf, ein Modell, eine Sitzung.
- deep: mehrere unabhängige Durchläufe (andere Paketreihenfolge, Zuschnitt oder Modell), Vereinigung per
id. Nur nach Kostenwarnung und Bestätigung des Nutzers (Vertrag 6.5).
Ablauf
- Ziel und Scope: Repo, Ref (Commit-SHA oder
working-tree), ausgeschlossene Pfade.scanIdvergeben. Das Scan-Verzeichnis heißtsec-scan-<scanId>/(Vertrag 1), liegt unter einem Basisordner außerhalb des Repos und wird erst nach dem Preflight angelegt. - Preflight:
- Eigener oder autorisierter Code? Sonst Abbruch (Vertrag 6.1).
- Lock-Prüfung für das Repo und den Basisordner des Scan-Verzeichnisses:
python <lock_scan.py> --check-dir '<Pfad>' --json, Exitcode sofort sichern. 0 = frei, 1 = gesperrt, alles andere (Absturz, unlesbare Ausgabe) = unbekannt = stopp (Vertrag 6.2). Interpreter ausdrücklich aufrufen, nie das Skript "öffnen". Gibt eslock_scan.pyauf dem Host nicht (kein ellmos-Lock-System), stattdessenLOCK*.txtim Repo und in den Elternordnern suchen: keine = frei (im Bericht vermerken), eine oder mehr = stopp. - Egress ansagen: geht Code an einen Anbieter (gehostete Review-Schicht, Fremdmodell)? Private Repos nur in der lokalen Sitzung (Vertrag 6.6). Kosten ansagen, bei
deepBestätigung abwarten (6.5).
- Bedrohungsmodell:
threatmodel.mdvorhanden und aktuell? Sonstsec-threat-modelausführen. - Pakete bilden: das Repo nach Vertrauensgrenzen und Einstiegspunkten aus dem Modell teilen, je Paket Dateien und Untersuchungsfragen. Sehr große Pakete teilen, statt oberflächlich zu lesen.
- Je Paket prüfen (Source-to-Sink: Quelle -> Transformation -> Senke, Gegenmaßnahmen auf dem Pfad mitlesen):
Injection (SQL, Shell, Template, LDAP/NoSQL), Authn/Authz und Mandantentrennung, Secrets im Code, Kryptografie (Algorithmen, Zufall, Schlüsselumgang), unsichere Deserialisierung, Pfad-/Dateizugriff (Traversal, Upload, Symlinks), SSRF und offene Weiterleitung, unsichere Konfiguration (Debug, CORS, Defaults, Rechte), Nutzung bekannt verwundbarer Abhängigkeiten (Muster im Code; Advisory-Abgleich ist
security-tech-stack-check). Befunde, die der Pfad nicht trägt, nicht aufnehmen; Verdacht ohne Beleg gehört incoverage.limits, nicht infindings. - Kandidat festhalten:
ruledotted lowercase (py.sql-injection),cwefalls passend,severityundconfidencenach Vertrag 3,status: candidate,location,evidenceals zitierter Code/Trace. Geheimniswerte maskieren (Vertrag 4).idimmer perpython scripts/secfind.py id RULE PATH -(Snippet über stdin), nie von Hand. - Coverage ehrlich führen:
scannedmit Tieferead/skimmed/pattern-only,notScannedmit Grund (generiert, vendored, zu groß, Binär, nicht erreichbar),limitsfür Methodengrenzen.complete: truenur bei leeremnotScanned(Vertrag 5).unknownist nicht "sicher". - Validieren:
python scripts/secfind.py validate findings.json coverage.json, Exitcode (0/1) und Ausgabe zeigen. Bei Fehlern korrigieren und erneut laufen lassen. - Abschluss: Anzahl Kandidaten je Severity, Coverage-Stand, nächste Schritte (
sec-validate, dannsec-triage,sec-report). Ein partieller Scan wird als partiell gemeldet (Vertrag 6.9).
deep
Mindestens zwei Durchläufe mit unterschiedlichem Zuschnitt, je eigene Kandidatenliste. Vereinigung per id: gleiche id zusammenführen (Evidenz ergänzen, bei abweichender Severity die höhere übernehmen und die Abweichung in description nennen), neue id aufnehmen. Coverage = Vereinigung der gelesenen Pfade; provider.model nennt alle beteiligten Modelle.
Adapter
Ein vorhandener Anbieter-Scanner kann den Scan-Teil übernehmen (Vertrag 7): Claude /claude-security, Codex codex-security. Gemini und Kimi haben keinen und folgen dem obigen Ablauf.
- Scanner nach Preflight (Schritt 2) laufen lassen; seine Ausgabedateien lesen und feldweise auf
findings.json/coverage.jsonabbilden. Die Dateien und Felder des Scanners werden hier nicht vorweggenommen; das Mapping entsteht beim Lesen der tatsächlichen Ausgabe und wird incoverage.limitsvermerkt. - Alle Funde starten als
candidate, auch wenn der Scanner sie als bestätigt führt. Ein Adapter überspringt die Validierung nie. provider.name/provider.modelnach dem Scan,provider.engine= Name des Scanners.- Coverage aus dem Scan-Bericht übernehmen. Gibt er keine Abdeckung her,
complete: falseund unternotScannedeinen Eintrag "Abdeckung des Scanners unbekannt" anlegen (ohnenotScanned-Eintrag istcomplete: falselaut Validator ungültig;limitsallein genügt nicht). ids persecfind.py idneu bilden, damit Funde mit denen anderer Läufe zusammenfallen. Danach Schritt 8.
Grenzen
Kein Patchen, kein Commit, kein Netzscan (Vertrag 6.1, 6.3). Keine Laufzeit-Exploits; Nachstellen ist sec-validate.
Changelog
0.1.0 (2026-10-31)
- Erstfassung: Modi standard/deep, Preflight, Pakete, Adapter.