Files
SlipItIn/.github/skills/security-specialist/scripts/scan_db.py
Tim Krampitz 01046b01e4 Neue Skills, Referenzen & OpenWiki-Doku integriert
Umfangreiche Erweiterung der Skill-Bibliothek: Neue Skills für Humanisierung (Englisch/PT-BR), Design-Validierung, AI-SEO und Coolify-Deployment inkl. Regelwerke, Presets, Pattern-Referenzen, Testfälle und Automatisierungsskripte. Zusätzliche Skills für Revenue-Centric Design, Pier Cloud, OKF, Lebenslauf- und LinkedIn-Optimierung sowie zahlreiche Referenzdateien, Checklisten und YAML/JSON/Markdown-Templates. Einführung einer vollständigen OpenWiki-Dokumentation mit Architektur-, Domain- und Workflow-Beschreibungen, zentralem Index und automatisierten Updates. Modularer Aufbau, restriktive Lizenzen und umfassende Qualitäts- und Evaluationsmechanismen für alle neuen Inhalte.
2026-07-26 14:00:58 +02:00

176 lines
6.1 KiB
Python

#!/usr/bin/env python3
"""SQLite-based security scan database manager."""
import argparse
import json
import sqlite3
import uuid
from datetime import datetime, timezone
from pathlib import Path
SCHEMA = """
CREATE TABLE IF NOT EXISTS scans (
id TEXT PRIMARY KEY,
repo_path TEXT NOT NULL,
started_at TEXT NOT NULL,
sealed_at TEXT,
status TEXT NOT NULL CHECK(status IN ('active', 'sealed'))
);
CREATE TABLE IF NOT EXISTS findings (
id TEXT PRIMARY KEY,
scan_id TEXT NOT NULL REFERENCES scans(id),
title TEXT NOT NULL,
severity TEXT NOT NULL CHECK(severity IN ('critical', 'high', 'medium', 'low', 'info')),
category TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'open'
CHECK(status IN ('open', 'fixed', 'false-positive', 'accepted-risk', 'tracked')),
file_path TEXT,
line_number INTEGER,
description TEXT,
evidence TEXT,
created_at TEXT NOT NULL,
tracking_url TEXT,
notes TEXT
);
"""
def _connect(repo: str) -> sqlite3.Connection:
db_path = Path(repo) / ".security" / "scan.db"
if not db_path.exists():
raise SystemExit(f"Database not found: {db_path}")
conn = sqlite3.connect(str(db_path))
conn.row_factory = sqlite3.Row
return conn
def _now() -> str:
return datetime.now(timezone.utc).isoformat()
def cmd_init(args: argparse.Namespace) -> None:
"""Initialize .security/scan.db and create a new scan record."""
sec_dir = Path(args.repo) / ".security"
sec_dir.mkdir(parents=True, exist_ok=True)
db_path = sec_dir / "scan.db"
conn = sqlite3.connect(str(db_path))
conn.executescript(SCHEMA)
scan_id = str(uuid.uuid4())
conn.execute(
"INSERT INTO scans (id, repo_path, started_at, status) VALUES (?, ?, ?, ?)",
(scan_id, str(Path(args.repo).resolve()), _now(), "active"),
)
conn.commit()
conn.close()
print(json.dumps({"scan_id": scan_id, "db": str(db_path)}))
def cmd_add_finding(args: argparse.Namespace) -> None:
"""Insert a finding into the database."""
conn = _connect(args.repo)
finding_id = str(uuid.uuid4())
conn.execute(
"""INSERT INTO findings
(id, scan_id, title, severity, category, status, file_path, line_number,
description, evidence, created_at)
VALUES (?, ?, ?, ?, ?, 'open', ?, ?, ?, ?, ?)""",
(finding_id, args.scan_id, args.title, args.severity, args.category,
args.file, args.line, args.description, args.evidence, _now()),
)
conn.commit()
conn.close()
print(json.dumps({"finding_id": finding_id}))
def cmd_list_findings(args: argparse.Namespace) -> None:
"""List findings as JSON, with optional filters."""
conn = _connect(args.repo)
query = "SELECT * FROM findings WHERE scan_id = ?"
params: list = [args.scan_id]
if args.severity:
query += " AND severity = ?"
params.append(args.severity)
if args.status:
query += " AND status = ?"
params.append(args.status)
rows = conn.execute(query, params).fetchall()
conn.close()
print(json.dumps([dict(r) for r in rows], indent=2))
def cmd_update_status(args: argparse.Namespace) -> None:
"""Update a finding's status and optional tracking metadata."""
conn = _connect(args.repo)
parts = ["status = ?"]
params: list = [args.status]
if args.tracking_url:
parts.append("tracking_url = ?")
params.append(args.tracking_url)
if args.note:
parts.append("notes = ?")
params.append(args.note)
params.append(args.finding_id)
conn.execute(f"UPDATE findings SET {', '.join(parts)} WHERE id = ?", params)
conn.commit()
conn.close()
print(json.dumps({"updated": args.finding_id}))
def cmd_stats(args: argparse.Namespace) -> None:
"""Print severity/category/status counts for a scan."""
conn = _connect(args.repo)
result: dict = {"by_severity": {}, "by_category": {}, "by_status": {}}
for col, key in [("severity", "by_severity"), ("category", "by_category"), ("status", "by_status")]:
rows = conn.execute(
f"SELECT {col}, COUNT(*) as cnt FROM findings WHERE scan_id = ? GROUP BY {col}",
(args.scan_id,),
).fetchall()
result[key] = {r[col]: r["cnt"] for r in rows}
conn.close()
print(json.dumps(result, indent=2))
def main() -> None:
parser = argparse.ArgumentParser(description="Security scan database manager")
sub = parser.add_subparsers(dest="command", required=True)
p_init = sub.add_parser("init")
p_init.add_argument("--repo", required=True)
p_add = sub.add_parser("add-finding")
p_add.add_argument("--repo", required=True)
p_add.add_argument("--scan-id", required=True)
p_add.add_argument("--title", required=True)
p_add.add_argument("--severity", required=True, choices=["critical", "high", "medium", "low", "info"])
p_add.add_argument("--category", required=True)
p_add.add_argument("--file", required=True)
p_add.add_argument("--line", type=int, required=True)
p_add.add_argument("--description", required=True)
p_add.add_argument("--evidence", required=True)
p_list = sub.add_parser("list-findings")
p_list.add_argument("--repo", required=True)
p_list.add_argument("--scan-id", required=True)
p_list.add_argument("--severity", choices=["critical", "high", "medium", "low", "info"])
p_list.add_argument("--status", choices=["open", "fixed", "false-positive", "accepted-risk", "tracked"])
p_upd = sub.add_parser("update-status")
p_upd.add_argument("--repo", required=True)
p_upd.add_argument("--finding-id", required=True)
p_upd.add_argument("--status", required=True, choices=["open", "fixed", "false-positive", "accepted-risk", "tracked"])
p_upd.add_argument("--tracking-url")
p_upd.add_argument("--note")
p_stats = sub.add_parser("stats")
p_stats.add_argument("--repo", required=True)
p_stats.add_argument("--scan-id", required=True)
args = parser.parse_args()
{"init": cmd_init, "add-finding": cmd_add_finding, "list-findings": cmd_list_findings,
"update-status": cmd_update_status, "stats": cmd_stats}[args.command](args)
if __name__ == "__main__":
main()