Status: two internal security passes (NMG-001 → NMG-013). All Critical/High items are fixed except NMG-006 (adversarial eval, ML-layer), whose measurement harness is prepared under
eval/. Do not run nemoguardian on the public internet without working through the hardening checklist.
| Version | Supported |
|---|---|
main |
✅ Active |
v1.1.0 |
✅ Latest release |
v1.0.0 |
Please do not open a public GitHub issue for security bugs.
Use GitHub private vulnerability reporting: Report a vulnerability on this repository. Reports go directly and privately to the maintainer.
When reporting, please include:
- Reproduction steps (curl, Python snippet, screenshot)
- Environment (commit SHA, Python version, Docker image tag, GPU host if relevant)
- Impact assessment (what data is exposed, what an attacker can do)
- Any suggested fix (optional but appreciated)
Canonical source of truth — if you find a regression, link to the NMG-NNN ID.
| # | Finding | Sev | Status |
|---|---|---|---|
| NMG-001 | Webhook trust-on-empty-secret | Crit | ✅ Fixed — fail-closed by default, opt-in demo only |
| NMG-002 | Raw API key in webhook response | Crit | ✅ Fixed — returns api_key_id only |
| NMG-003 | Demo endpoint on by default | High | ✅ Fixed — defaults 0, opt-in |
| NMG-004 | No webhook replay protection | High | ✅ Fixed — 300s timestamp tolerance + event-id idempotency (dedup table) |
| NMG-005 | CORS wildcard ["*"] |
High | ✅ Fixed — env allowlist NEMOGUARDIAN_CORS_ORIGINS, localhost-only default; * is an explicit opt-in |
| NMG-006 | Prompt injection into guards | High | ❌ Open (ML-layer; run harness ready in eval/) |
| NMG-007 | SQLite hygiene | Med | ✅ Fixed — WAL + busy_timeout; non-Docker DB defaults to ~/.nemoguardian (0700/0600), container DB at /var/lib/nemoguardian |
| NMG-008 | SSH key interpolated into shell | Med | ✅ Fixed — SSH-key validation at all provisioning endpoints |
| NMG-009 | Raw key forwarded to instances | Med | ✅ Fixed — provisioning mints a moderation-only instance-scoped key (tracked on the job, revoked on failed provisions); the caller's master key never leaves the control plane |
| NMG-010 | Verbose errors to clients | Low | ✅ Fixed — full trace logged server-side, generic detail returned |
| NMG-011 | No rate limiting | Med | ✅ Fixed — ASGI fixed-window limiter keyed on validated-API-key hash / trusted client IP, enforced before auth, with a request body-size cap (NEMOGUARDIAN_RATE_LIMIT, NEMOGUARDIAN_MAX_BODY_BYTES). Fail-open when unset (logs a warning); set a positive limit in production |
| NMG-012 | Container runs as root | Low | ✅ Fixed — runs as non-root app user (uid 1001) |
| NMG-013 | No secret-scan/SAST in CI | Info | ✅ Fixed — CI runs gitleaks (full history), CodeQL, and pip-audit, plus offline doc-link/secret/build guards |
| — | Triage model slug 220b-a12b → 550b-a55b | — | ✅ Fixed |
Items checked below are enforced in code on main; unchecked items are operator steps.
- Demo endpoint defaults to off (
NEMOGUARDIAN_ENABLE_DEMO_ENDPOINT=0) — NMG-003 - Replace
NEMOGUARDIAN_API_KEYwith a realnmg_...value (placeholder values are rejected) - Set
NEMOGUARDIAN_CORS_ORIGINSto your actual frontend origins — NMG-005 (localhost-only by default) - Set a positive
NEMOGUARDIAN_RATE_LIMIT— the limiter is fail-open (logs a warning) until you do — NMG-011 - Run behind an authenticated reverse proxy (Caddy, nginx, Cloudflare Access); set
NEMOGUARDIAN_TRUST_PROXYto your proxy hop count - Enable structured logging and ship to a SIEM
- Default model weights pinned by full commit SHA (transformers
revision); setNEMOGUARDIAN_*_REVISIONwhen overriding models - Cascade fails closed on total model failure (
NEMOGUARDIAN_FAIL_MODE=closed, the default) - Verify Stripe webhook signature secret is set (no empty
STRIPE_WEBHOOK_SECRET) — verification is fail-closed when unset - Container runs as a non-root user — NMG-012
- CI scans for secrets, SAST, and dependency CVEs — NMG-013
- Mirror
.envsecrets into a secret manager (Doppler, Vault, AWS SM)
- Adversarial robustness of the moderation models themselves (tracked as NMG-006; harness in
eval/) - Denial-of-service protection at the model layer (use the proxy)
- Multi-tenant key isolation (single-tenant self-hosted is the supported deployment)