If you discover a security issue, email the maintainer directly (see GitHub profile) or use GitHub's private security advisory. Do not open a public issue.
A response will follow within 48 hours. Please include:
- affected version / commit
- reproduction steps
- expected vs observed behaviour
- any proof-of-concept code
| Version | Supported |
|---|---|
| 0.1.x | ✅ |
- Header:
X-API-Keyrequired for all/v1/*routes - Keys stored as SHA-256 hex digests — raw keys never touch disk
- Constant-time comparison (
hmac.compare_digest) - Dev-only auto-key printed at startup when
QUANTFORGE_API_KEYSis unset
slowapiwith per-API-key (or per-IP) limits- Default: 120 requests/minute, 2000 requests/hour
- Exceeded →
429 Too Many RequestswithRetry-Afterheader
- 256 KiB max body (DoS defense)
X-Request-IDcorrelation (auto-generated or echoed)- Strict input validation via Pydantic (bounds + regex on every field)
- Content-Length pre-check rejects oversized payloads before body read
Every response carries:
Strict-Transport-Security: max-age=31536000; includeSubDomainsX-Content-Type-Options: nosniffX-Frame-Options: DENYContent-Security-Policywith strict allowlistReferrer-Policy: no-referrerPermissions-Policy: geolocation=(), microphone=(), camera=()Cross-Origin-Opener-Policy: same-originCross-Origin-Resource-Policy: same-site
Strict allowlist only — no wildcard with credentials. Configure additional
origins via QUANTFORGE_CORS_ORIGINS (comma-separated).
- Runs as non-root UID 10001
readOnlyRootFilesystem: true- All Linux capabilities dropped
seccompProfile: RuntimeDefaultallowPrivilegeEscalation: false- Network isolation via
NetworkPolicy(default deny)
banditfor Python security lint (MEDIUM+ fails the build)safetyfor CVE scanning of dependenciesCodeQLfor static analysis (Python + JavaScript)gitleaksfor committed-secret detection (fails PRs with leaks)Trivyfor container image vulnerability scan (CRITICAL/HIGH surfaced to GitHub Security tab)- Dependabot weekly PRs for pip, docker, and GitHub Actions
- Deploys only fire after test + lint + security-scan are all green.
- CI posts to Render/Vercel deploy hooks via URLs stored as GitHub secrets
(
RENDER_DEPLOY_HOOK_URL,VERCEL_DEPLOY_HOOK_URL). The URLs themselves are not logged. - A post-deploy
smokejob polls/healthzand asserts that an unauthenticated request to/v1/meta/versionreturns 401/403. If a deploy accidentally shipsQUANTFORGE_ALLOW_UNAUTH=true, the job fails and flags the regression before traffic hits it.
Run through this before pointing real clients at a fresh deploy. The
companion guide in DEPLOYMENT.md walks through the
mechanics.
-
QUANTFORGE_API_KEYSset to comma-separated SHA-256 digests (not raw keys). At least one key per distinct client so you can revoke granularly. -
QUANTFORGE_JWT_SECRETis ≥ 32 random bytes if any route uses JWT auth. Generated viasecrets.token_urlsafe(48)oropenssl rand. -
QUANTFORGE_ALLOW_UNAUTH=false— confirm with the smoke job. - No secrets in repo.
gitleaksis green onmain.
-
QUANTFORGE_CORS_ORIGINScontains only your frontend origin(s). No wildcards, nohttp://, no trailing paths. - Vercel enforces HSTS (see
vercel.jsonheaders block). - Render serves only over HTTPS (default; don't disable).
- Render disk is mounted at
/app/dataandQUANTFORGE_AUDIT_DBpoints inside it. Without this, audit history resets on every deploy. - Audit log rotation threshold (
MAX_ROWS=200_000) is acceptable for your compliance retention; bump the disk size if you need longer.
- Rate limits appropriate for the caller base (default 120/min, 2000/hr per key). Tighten for public deploys, loosen for trusted internal.
-
REDIS_URLset if you run >1 instance of the API — otherwise the in-memory cache and rate-limit counters diverge between instances. - Trivy SARIF uploads are visible under Security → Code scanning.
- At least one secondary on-call has access to rotate secrets.