Files

144 lines
5.8 KiB
Python

#!/usr/bin/env python3
"""Generate a sanitized quarterly security audit report template."""
from __future__ import annotations
import argparse
import sys
from datetime import date, datetime
from pathlib import Path
VALID_QUARTERS = {"Q1", "Q2", "Q3", "Q4"}
def _parse_date(value: str | None) -> date:
if value is None:
return date.today()
try:
return datetime.strptime(value, "%Y-%m-%d").date()
except ValueError as exc:
raise argparse.ArgumentTypeError("date must use YYYY-MM-DD format") from exc
def _quarter_for(month: int) -> str:
if month <= 3:
return "Q1"
if month <= 6:
return "Q2"
if month <= 9:
return "Q3"
return "Q4"
def build_report(*, audit_date: date, quarter: str | None = None) -> str:
"""Build a deterministic markdown report template."""
selected_quarter = (quarter or _quarter_for(audit_date.month)).upper()
if selected_quarter not in VALID_QUARTERS:
msg = f"quarter must be one of {', '.join(sorted(VALID_QUARTERS))}"
raise ValueError(msg)
title = f"{audit_date:%Y} {selected_quarter} Security Audit"
return f"""# {title}
Date: {audit_date.isoformat()}
Scope: ComfyUI-Doctor repository, ComfyUI custom-node runtime boundaries, local validation tooling, and public documentation.
## Audit Rules
- Do not include secrets, tokens, cookies, private URLs, private hostnames, user data, local state contents, generated reports with sensitive paths, or workstation-specific details.
- Record only sanitized command summaries, pass/fail status, affected public file paths, and public-safe remediation notes.
- Keep raw scanner output and target-specific evidence in maintainer-private storage.
- Run active or passive DAST only against targets you own or are explicitly authorized to test.
## Automated Checks
| Check | Command or Source | Status | Notes |
| --- | --- | --- | --- |
| Supply-chain scanner | `python scripts/check_supply_chain.py --skip-install-trees` | Pending | Required repo-local gate |
| Outbound safety | `python scripts/check_outbound_safety.py` | Pending | Required repo-local gate |
| Focused security gate | `python scripts/focused_gate.py --fast` | Pending | Plugin, metadata, dependency-policy, outbound-payload checks |
| Full local acceptance gate | `scripts/run_full_tests_windows.ps1` or `scripts/run_full_tests_linux.sh` | Pending | Required before accepting remediation changes |
| Semgrep SAST | `semgrep scan` in scheduled workflow | Pending | Optional when Semgrep install/network is available |
| Snyk dependency scan | `snyk test` in scheduled workflow | Pending | Optional when `SNYK_TOKEN` is configured |
| ZAP baseline scan | `zap-baseline.py` in scheduled workflow | Pending | Optional; requires authorized target URL |
## Manual Security Scenarios
| Scenario | Coverage Target | Status | Sanitized Result |
| --- | --- | --- | --- |
| SSRF metadata endpoint attempts | Provider base URL validation and redirect handling | Pending | |
| SSRF private/internal IP attempts | DNS resolution and private-target blocking | Pending | |
| XSS chat input attempts | Chat rendering and message sanitization | Pending | |
| XSS settings field attempts | Settings rendering and saved field handling | Pending | |
| Path traversal attempts | File/path inputs, report/history access, plugin trust scanning | Pending | |
| Admin bypass attempts | Write-sensitive endpoints and strict-token mode | Pending | |
| Credential exposure attempts | Server-side key store, status APIs, logs, and UI state | Pending | |
| Telemetry privacy review | Local-only opt-in buffer, export, clear, and toggle behavior | Pending | |
## Compliance Mapping
| Framework | Review Focus | Status | Notes |
| --- | --- | --- | --- |
| OWASP Top 10 | Injection, broken access control, SSRF, security logging, vulnerable components | Pending | |
| CWE Top 25 | Input validation, path traversal, XSS, credential handling, command execution | Pending | |
| Privacy/GDPR readiness | Data minimization, local telemetry, credential handling, deletion/export expectations | Pending | |
## Findings
| ID | Severity | Area | Public-Safe Summary | Remediation | Owner | Status |
| --- | --- | --- | --- | --- | --- | --- |
| | | | | | | |
## Remediation Validation
| Finding ID | Targeted Regression | Full Gate Result | Status |
| --- | --- | --- | --- |
| | | | |
## Sign-Off
- Auditor:
- Reviewer:
- Follow-up due date:
- Public release note needed: Yes / No
"""
def _write_output(path: Path, content: str, *, force: bool) -> None:
if path.exists() and not force:
raise FileExistsError(f"refusing to overwrite existing file: {path}")
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content, encoding="utf-8", newline="\n")
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(
description="Generate a sanitized quarterly security audit report template."
)
parser.add_argument("--date", type=_parse_date, default=None, help="Audit date in YYYY-MM-DD.")
parser.add_argument("--quarter", choices=sorted(VALID_QUARTERS), help="Audit quarter.")
parser.add_argument("--output", help="Write report to this path instead of stdout.")
parser.add_argument("--force", action="store_true", help="Overwrite --output if it exists.")
args = parser.parse_args(argv)
audit_date = args.date or date.today()
try:
report = build_report(audit_date=audit_date, quarter=args.quarter)
except ValueError as exc:
parser.error(str(exc))
if args.output:
try:
_write_output(Path(args.output), report, force=args.force)
except OSError as exc:
print(f"ERROR: {exc}", file=sys.stderr)
return 1
else:
sys.stdout.write(report)
return 0
if __name__ == "__main__":
raise SystemExit(main())