NovaVision is a research artifact: a localhost-by-default Flask app plus an offline Python
evaluation pipeline. The app binds 127.0.0.1 unless you opt in to a public bind, stores no
user data, and ships no secrets or API keys. It downloads model weights (SD-Turbo, CLIP, the
emotion classifier) from Hugging Face via from_pretrained; an HF_TOKEN is needed only for
the hosted hf-api backend.
Only the latest main is supported. Fixes land on main; there are no backports.
Please report privately rather than opening a public issue: use the repository's Security tab → Report a vulnerability to open a private security advisory.
Expect an acknowledgement within a few days.
- Localhost by default. Both entry points (
server.py,app.py) bind127.0.0.1. A public bind (0.0.0.0) requires an explicitNOVA_PUBLIC=1opt-in (auto-enabled only inside a Hugging Face Spaces sandbox). The shared rule lives innovavision/serving.pyso the two entry points cannot disagree. - Generate route is protected.
/api/generateenforces a per-IP rate limit (NOVA_RATE_LIMIT, default 30/min), a concurrency cap (NOVA_MAX_CONCURRENCY, default 2, which sheds load rather than queueing), and an optional bearer token (NOVA_API_TOKEN): set the token whenever you bind publicly. The rate-limit key isrequest.remote_addr;X-Forwarded-Foris trusted only whenNOVA_TRUST_PROXY=1(set it only behind a real reverse proxy, since the header is otherwise client-spoofable). - No repo-root exposure. Flask serves only the dedicated
static/directory, never the project root, so source, configs, and data are not downloadable. - CORS is disabled by default (same-origin). Set
CORS_ORIGINSto allow specific domains. - Request bodies are capped at 32 KB (the worst-case wire size of a legal 2000-character
text, JSON-escaped) and input text at 2000 characters;
stylemust be a known preset andseeda signed 64-bit integer, so no unvalidated input is echoed back. - No unsafe deserialization. Model weights load only via Hugging Face
from_pretrained(safetensors);pickle.load/torch.load/weights_only=False/shell=Trueare blocked by a CI test (tests/test_security.py). - API errors return generic messages; details are logged server-side only.
- Production serving.
python server.pyruns Flask's built-in development server; for any public bind, serve through a real WSGI server instead:make serve-prod(gunicorn, installed with theappextra), withBIND=0.0.0.0:8000only behind your own hardening.
- No secrets are committed. Configuration is read from environment variables; see
.env.example. - The full git history is scanned for committed secrets on every push (
gitleaksin CI), and the dependency chain is audited bypip-audit. - CI runs with least-privilege
permissions: contents: read; no workflow has write scope.
tests/fixtures/affectbench_sample.csv is fictional, hand-authored example content (see
data/README.md). No personal or production data is included anywhere in this repository.