Skip to content

Security: aolus-software/clean-elysia

.github/SECURITY.md

Security Policy

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.

Supported versions

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.

Reporting a vulnerability

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:line if 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.

Scope

In scope — anything that ships in this repository:

  • authentication and token handling: AuthPlugin (src/libs/plugins/auth.plugin.ts), JWT signing and verification via JWT_CONFIG, and the src/modules/auth/ flows (login, register, email verification, password reset),
  • the cache-aside user lookup in AuthPlugin — in particular stale or cross-tenant reads of UserInformationCacheKey after a role, permission, or status change,
  • the RBAC layer: PermissionGuard and RoleGuard in @guards, the superuser short-circuit, and any route whose beforeHandle gating 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 (Hash in @utils), APP_KEY, JWT_SECRET, the crypto-js Encrypt/Decrypt helpers, 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,
  • BodyLimitPlugin and 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 reaching ORDER BY,
  • BullMQ job payloads that carry secrets, or workers that can be driven by attacker-controlled data (src/bull/),
  • the OpenAPI surface: DocsPlugin exposing 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 example APP_KEY / JWT_SECRET, or running with NODE_ENV=development in 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.

Known hardening notes

These are documented rather than treated as vulnerabilities. Review them before deploying:

  • .env holds every secret and is never committed. JWT_SECRET is 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. Rotate APP_KEY away from its .env.example placeholder (your-app-key) too; envalid does supply a default for that one, so the app starts happily with an insecure value. (APP_JWT_SECRET was removed on 2026-08-21 — it never signed anything.)
  • ALLOWED_HOST feeds CORSConfig.origin. Set it to your real front-end origins as a comma-separated list; an unset or * value means every origin. credentials is false in src/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 when NODE_ENV=production. Deploying with NODE_ENV=development or staging publishes 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.
  • BodyLimitPlugin caps 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/ creates superuser@example.com and admin@example.com, both with the password password, already email-verified. Change or remove them before any deployment that is reachable from the internet.
  • The Husky pre-commit hook runs bunx drizzle-kit migrate against the DATABASE_URL in your .env. Do not let that point at production. See .github/CONTRIBUTING.md.

There aren't any published security advisories