Please do not open a public issue for a security vulnerability.
Report it privately through GitHub's private vulnerability reporting: open the repository's Security tab and choose Report a vulnerability. This creates a draft advisory visible only to the maintainers.
Maintainers: private reporting must be switched on for this repository under Settings → Code security and analysis → Private vulnerability reporting, otherwise the link above will not be available to reporters.
Please include:
- what an attacker can achieve, and the impact;
- the affected file, endpoint or configuration;
- reproduction steps or a proof of concept;
- the version or commit you tested.
What to expect: acknowledgement within 7 days, an assessment with a planned fix window within 14 days, and credit in the advisory unless you prefer otherwise. Please give us a reasonable window to ship a fix before disclosing publicly.
- Findings that require an already-compromised server or database.
- Missing hardening that is the deployer's responsibility and documented in
the checklist below (for example, running with
DJANGO_DEBUG=true). - Vulnerabilities in Groq, Google reCAPTCHA, or other third-party services — report those to the vendor.
- Automated scanner output with no demonstrated impact.
This project has a single supported line: the latest commit on main.
Security fixes are not backported.
Work through this before exposing the app to the internet.
-
DJANGO_SECRET_KEYis set to a freshly generated random value, unique to this deployment. Generate with:python -c "from django.core.management.utils import get_random_secret_key as k; print(k())" -
GROQ_API_KEYis set, and is a key scoped to this application so it can be rotated independently. - No
.envfile is committed..gitignoreexcludes.envand.env.*while keeping.env.example. -
IP_HASH_SALTis set explicitly if you ever rotateDJANGO_SECRET_KEYand want stored IP hashes to remain comparable.
-
DJANGO_DEBUG=false. The app refuses to start in production without a secret key or allowed hosts, but debug mode disables those guards — make sure it is genuinely off. -
DJANGO_ALLOWED_HOSTSnames your real hostnames. Never*. -
CORS_ALLOWED_ORIGINSlists only the origins that must call the API. Never a wildcard, and never a private/LAN address in a public deployment. -
CSRF_TRUSTED_ORIGINSmatches your frontend origin. -
python manage.py check --deployreports no issues. - Served over HTTPS. Keep
SECURE_SSL_REDIRECT=trueunless a proxy in front already terminates TLS and redirects. - A strong, unique Django admin password; consider removing the
/admin/route entirely if you do not use it.
-
API_THROTTLE_RATEis tuned for your traffic. Every summarization request costs a Groq API call, so an unthrottled public endpoint is a direct billing risk. -
MAX_UPLOAD_SIZEis set appropriately; requests are processed synchronously, so large documents tie up a worker. - Consider a WAF or rate limiter at the edge in addition to the DRF throttle, which is per-IP and trivially bypassed with a proxy pool.
- Both reCAPTCHA halves (
RECAPTCHA_SECRET_KEYandVITE_RECAPTCHA_SITE_KEY) are configured, or the contact form will reject every submission.
- You have read the "Data collected" section of the README and accept what is stored.
- Decide whether you need request logging at all — set
ENABLE_REQUEST_LOGGING=falseto store nothing per request. - Put a retention policy in place. Nothing expires automatically.
- The database file or server is not world-readable and is backed up
somewhere private.
db.sqlite3and*.csvare gitignored — keep it that way. - Users are told that submitted text is sent to Groq for processing.
- Containers run as the non-root
app/nginxusers as shipped; do not override withuser: root. - Never run
manage.py runserverorvite devin production. The shipped images use gunicorn and nginx. - Keep dependencies patched:
pip-auditfor the backend andnpm audit --omit=devfor the frontend both run in CI. - The
/textsummery/health/endpoint is unauthenticated by design; it exposes only{"status": "ok"}.
- POS/NER output is rendered as raw HTML in the frontend via
dangerouslySetInnerHTML. The backend HTML-escapes every user-derived token before building that markup (pos_htmlinbackend/textApp/utils/entity.py, covered by a regression test). Any change to that rendering path must preserve the escaping or it becomes stored XSS. - Client IPs are hashed, never stored raw.
X-Forwarded-Foris trusted only for reading the client address — deploy behind a proxy that sets it, or the value is attacker-controlled. - Secrets fail loudly. Missing
DJANGO_SECRET_KEY,DJANGO_ALLOWED_HOSTSorGROQ_API_KEYraiseImproperlyConfiguredat startup when debug is off, rather than silently falling back to an insecure default. - Upstream failures surface as 5xx, never as an empty or fabricated summary.