This document is the vulnerability disclosure policy for this repository — how to report a security problem in the starter itself.
It is not the API security guide. For how authentication, RBAC, password handling, token lifecycle,
rate limiting, validation, and security headers actually work in this template, read
docs/SECURITY.md.
This is a starter template rather than a versioned library. Security fixes land on main, and
projects built from the template are expected to pull them forward themselves. Only the current
main branch is supported.
Do not open a public issue for a security vulnerability.
Report it privately through GitHub Security Advisories:
https://github.com/aolus-software/clean-elysia/security/advisories/new
That creates a private thread visible only to the maintainers. If you cannot use GitHub Advisories, open a regular issue that says only "security report, please provide a private contact" with no technical detail, and a maintainer will follow up.
Please include, as far as you can determine it:
- the affected component (plugin, guard, module route, repository, config) and
file:lineif you have it, - the conditions required to reach it — authenticated or not, which role or permission, which data,
- a proof-of-concept request or sequence,
- the impact you believe it has.
You should get an acknowledgement within a few days. Please give the maintainers a reasonable window to ship a fix before disclosing publicly.
In scope — anything that ships in this repository:
- authentication and token handling:
AuthPlugin(src/libs/plugins/auth.plugin.ts), JWT signing and verification viaJWT_CONFIG, and thesrc/modules/auth/flows (login, register, email verification, password reset), - the cache-aside user lookup in
AuthPlugin— in particular stale or cross-tenant reads ofUserInformationCacheKeyafter a role, permission, or status change, - the RBAC layer:
PermissionGuardandRoleGuardin@guards, thesuperusershort-circuit, and any route whosebeforeHandlegating can be bypassed, - soft delete: reads that forget
isNull(<table>.deleted_at)and let deleted rows be read back (.claude/rules/repositories.md), - secret handling: bcrypt password hashing (
Hashin@utils),APP_KEY,JWT_SECRET, the crypto-jsEncrypt/Decrypthelpers, and anything leaking secrets into logs, responses, or OpenAPI examples, SecurityPlugin(src/libs/plugins/security.plugin.ts): the CORS config, the Helmet CSP, and the 100-requests-per-60-seconds rate limiter,BodyLimitPluginand other request-shaping plugins,- TypeBox validation gaps that let unvalidated input reach a service or a query,
- SQL injection or query-shape problems in
src/libs/repositories/— including unwhitelisted sort columns reachingORDER BY, - BullMQ job payloads that carry secrets, or workers that can be driven by attacker-controlled data
(
src/bull/), - the OpenAPI surface:
DocsPluginexposing schemas or examples it should not, - dependency vulnerabilities that are reachable through code in this repo.
Out of scope:
- vulnerabilities in a project built from this template that come from that project's own code,
- anything requiring a misconfiguration the docs tell you not to ship — for example leaving
ALLOWED_HOST=*, keeping the exampleAPP_KEY/JWT_SECRET, or running withNODE_ENV=developmentin production, - missing hardening that is documented as the deployer's responsibility (TLS termination, database and Redis network access, secret storage, ClickHouse exposure),
- reports generated by an automated scanner with no demonstrated exploit path.
These are documented rather than treated as vulnerabilities. Review them before deploying:
.envholds every secret and is never committed.JWT_SECRETis the only one with no envalid default — the process refuses to boot without it, which is deliberate: it is what signs and verifies every token. RotateAPP_KEYaway from its.env.exampleplaceholder (your-app-key) too; envalid does supply a default for that one, so the app starts happily with an insecure value. (APP_JWT_SECRETwas removed on 2026-08-21 — it never signed anything.)ALLOWED_HOSTfeedsCORSConfig.origin. Set it to your real front-end origins as a comma-separated list; an unset or*value means every origin.credentialsisfalseinsrc/libs/config/cors.config.ts— if you turn it on, a wildcard origin becomes unsafe.- The Scalar/OpenAPI reference at
/docs(and/docs/openapi.json) is disabled only whenNODE_ENV=production. Deploying withNODE_ENV=developmentorstagingpublishes your API schema. - The rate limiter is global at 100 requests per 60 seconds for every route
(
src/libs/plugins/security.plugin.ts). Tighten it, and add a stricter limit on credential endpoints, before exposing the API. BodyLimitPlugincaps request bodies at 100KB. Raise it deliberately, per-route, rather than globally.- Redis (
REDIS_PASSWORD) backs both the cache and BullMQ. Anything that can write to that Redis can forge cached user information and enqueue jobs. - The seed data in
src/libs/database/postgres/seed/createssuperuser@example.comandadmin@example.com, both with the passwordpassword, already email-verified. Change or remove them before any deployment that is reachable from the internet. - The Husky
pre-commithook runsbunx drizzle-kit migrateagainst theDATABASE_URLin your.env. Do not let that point at production. See.github/CONTRIBUTING.md.