Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Role and Purpose
You are an expert Senior Full Stack Engineer specializing in secure NestJS applications and Drizzle ORM. Your primary goal is to write robust, production-ready, and highly secure TypeScript code. Security and strict typing are more important than brevity.

# General Security Guidelines
- NEVER hardcode secrets, API keys, or passwords. Always use `@nestjs/config` and `ConfigService`.
- NEVER disable TypeScript strict mode features. Avoid using `any`; use `unknown` if the type is truly dynamic, and narrow it down with type guards.
- Log errors properly using a dedicated Logger module, but NEVER log sensitive user data (PII, passwords, tokens).

# NestJS Security Rules
- **Data Validation:** Every incoming Request Body, Query, and Param MUST be validated using DTOs with `class-validator` and `class-transformer`.
- **ValidationPipe:** Assume the global `ValidationPipe` is configured with `{ whitelist: true, forbidNonWhitelisted: true, transform: true }`. Do not write manual validation logic in controllers.
- **Authentication & Authorization:** All endpoints must be secured by default. Use `@UseGuards()` explicitly. If a route is public, explicitly mark it with a custom `@Public()` decorator.
- **Headers & Rate Limiting:** Assume `helmet` and `@nestjs/throttler` are active. Ensure custom routes do not bypass rate limiting without explicit justification.

# Drizzle ORM & Database Rules
- **No Raw SQL:** NEVER use raw SQL strings (e.g., `sql\`SELECT * FROM users\``) unless absolutely necessary for complex aggregations that Drizzle's query builder cannot handle.
- **Schema Validation:** Use `drizzle-zod` to infer Zod schemas directly from the Drizzle database schema. Use these schemas to validate data before insertion.
- **Data Exposure:** Never return full database models directly from the controller. Always map database entities to Response DTOs using `class-transformer` (e.g., `@Exclude()` on password hashes).
- **Mutations:** All database mutations (INSERT, UPDATE, DELETE) must be wrapped in transactions if they affect more than one table.

# Code Generation Output Format
- When generating controllers, always include the Swagger decorators (e.g., `@ApiTags`, `@ApiResponse`) to document the expected security headers and error responses.
- Write isolated, pure functions where possible to make unit testing security rules easier.
- If you detect a potential security flaw in the user's prompt, stop and warn the user before writing the code.
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,11 @@ EMAIL_VERIFICATION_TOKEN_TTL_SECONDS=86400
PASSWORD_CHANGE_GRACE_PERIOD_HOURS=24
FORGOT_PASSWORD_MIN_RESPONSE_MS=250

# Fail-closed RBAC when PermissionGuard lacks @RequirePermissions (auto true in production)
PERMISSION_GUARD_STRICT=false

DISABLE_REDIS=true # Set to true when Redis is not available locally

# Webhook outbox relay (requires Redis)
WEBHOOK_OUTBOX_BATCH_SIZE=50
WEBHOOK_OUTBOX_RELAY_INTERVAL_MS=5000
35 changes: 25 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ jobs:
- name: TypeScript type-check
run: npm run type-check

# ─────────────────────────────────────────────
# Job 2: Unit tests + coverage ≥ 85%
# ─────────────────────────────────────────────
test:
name: Tests (coverage ≥ 85%)
runs-on: ubuntu-latest
Expand Down Expand Up @@ -96,9 +93,30 @@ jobs:
directory: ./coverage
fail_ci_if_error: false

# ─────────────────────────────────────────────
# Job 3: npm audit (falha em high/critical)
# ─────────────────────────────────────────────
e2e:
name: E2E Tests (Testcontainers)
runs-on: ubuntu-latest
needs: lint-typecheck

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Run E2E tests
run: npm run test:e2e
env:
NODE_ENV: test
DISABLE_REDIS: 'true'

audit:
name: Security Audit
runs-on: ubuntu-latest
Expand All @@ -120,13 +138,10 @@ jobs:
- name: npm audit (high/critical → fail)
run: npm audit --audit-level=high

# ─────────────────────────────────────────────
# Job 4: Build TypeScript + Docker + Trivy scan
# ─────────────────────────────────────────────
build:
name: Build & Docker Scan
runs-on: ubuntu-latest
needs: [test, audit]
needs: [test, e2e, audit]

steps:
- name: Checkout
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ lerna-debug.log*
.DS_Store

# Tests
test/.testcontainers-env.json
/coverage
/.claude/
/.nyc_output
Expand Down
128 changes: 0 additions & 128 deletions CODE_OF_CONDUCT.md

This file was deleted.

20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,14 @@ Instead of assembling auth, RBAC, logging, health checks, and database patterns
- **Correlation ID Injection Prevention** — UUID v4 validation on `X-Correlation-Id`; invalid values discarded and replaced server-side.
- **Production Observability** — structured logs with correlation IDs, liveness/readiness probes, graceful shutdown.
- **Drizzle ORM + Migration Workflow** — predictable schema evolution with explicit SQL migrations.
- **Multi-Tenancy via PostgreSQL RLS** — `TenantDatabaseService.withTenant()` wraps every query in a transaction with `set_config('app.current_tenant', orgId, true)`; belt-and-suspenders with explicit `WHERE organization_id` clause.
- **Resilient Webhooks** — BullMQ async delivery with HMAC-SHA256 signing; graceful degradation when Redis is unavailable (`DISABLE_REDIS=true`).
- **Multi-Tenancy via PostgreSQL RLS** — `TenantDatabaseService` sets `app.current_tenant` per transaction; RLS policies on `users`, `sessions`, `audit_logs`, and webhooks; `runWithRlsBypass()` for trusted system paths (auth, risk engine).
- **Resilient Webhooks** — transactional outbox (`webhook_deliveries` + `enqueued_at`), `WebhookOutboxRelay`, BullMQ delivery with exponential backoff and HMAC-SHA256 signing; graceful degradation when Redis is unavailable (`DISABLE_REDIS=true`).
- **Async Transactional Email** — `MailFacade` enqueues via BullMQ `email-delivery` when Redis is available; synchronous fallback via Nodemailer when `DISABLE_REDIS=true`.
- **Account Recovery (Opaque Tokens)** — `POST /auth/forgot-password` always returns 202 (anti-enumeration); SHA-256 token hash in Redis; burn-after-read on reset; never JWT in email links.
- **Email Verification (Double Opt-in)** — `POST /auth/send-verification` + `POST /auth/verify-email`; `@RequireEmailVerification()` guard for sensitive routes.
- **Password Change Grace Period** — `GracePeriodGuard` blocks sensitive actions for 24h after password change (`passwordChangedAt`).
- **Transactional Email (Ports & Adapters)** — Nodemailer + Ethereal in dev; SMTP in production via `MailFacade` / `IEmailProvider`.
- **CI/CD & Security Pipeline** — GitHub Actions: lint → type-check → coverage ≥ 85% → npm audit → Docker build → Trivy scan; weekly CodeQL + Snyk scan; Dependabot for npm/Actions/Docker.
- **Transactional Email (Ports & Adapters)** — Nodemailer + Ethereal in dev; SMTP in production via `MailFacade` / `IEmailProvider`; optional BullMQ queue when Redis is enabled.
- **CI/CD & Security Pipeline** — GitHub Actions: lint → type-check → unit tests (coverage ≥ 85%) → E2E (Testcontainers) → npm audit → Docker build → Trivy scan; weekly CodeQL + Snyk scan; Dependabot for npm/Actions/Docker.

## Practical Example: RBAC + Multi-Tenant Endpoint

Expand Down Expand Up @@ -88,14 +89,14 @@ See [docs/examples/rbac-multi-tenant.md](./docs/examples/rbac-multi-tenant.md) f
## Architecture Snapshot

- **Framework:** NestJS 11
- **Database:** PostgreSQL + Drizzle ORM (10 tables, 5 migrations)
- **Database:** PostgreSQL + Drizzle ORM (10 tables, 9 migrations)
- **Auth:** JWT RS256 (access 15m, refresh 7d) · Argon2id · opaque recovery tokens · email verification
- **Email:** Nodemailer (Ethereal dev / SMTP prod) via Ports & Adapters
- **Email:** Nodemailer (Ethereal dev / SMTP prod) · optional BullMQ `email-delivery` queue
- **Authorization:** RBAC with Redis/in-memory permission cache
- **Cache/Infra:** Redis (optional locally) · BullMQ webhooks · opaque token store
- **Cache/Infra:** Redis (optional locally) · BullMQ webhooks + email · opaque token store
- **Rate Limiting:** express-rate-limit + @nestjs/throttler
- **Observability:** Pino + correlation ID + health endpoints
- **Tests:** 97 unit tests (21 suites) · E2E tenant isolation in `test/`
- **Observability:** Pino + correlation ID (HTTP + webhook jobs) + health endpoints
- **Tests:** 104 unit tests (22 suites) · E2E with Testcontainers (`tenant-isolation`, `webhook-flow`)

## Quick Start

Expand Down Expand Up @@ -129,6 +130,7 @@ npm run db:studio # open Drizzle Studio
- Auth keys: `PRIVATE_KEY`, `PUBLIC_KEY`
- Cache: `RBAC_CACHE_TTL`, `DISABLE_REDIS` (default `true` for local dev)
- Email: `APP_URL`, `SMTP_*`, token TTLs (`PASSWORD_RESET_TOKEN_TTL_SECONDS`, etc.)
- Webhooks: `WEBHOOK_OUTBOX_BATCH_SIZE`, `WEBHOOK_OUTBOX_RELAY_INTERVAL_MS` (requires Redis)
- Security: `ALLOWED_ORIGINS`, `PERMISSION_GUARD_STRICT`, `PASSWORD_CHANGE_GRACE_PERIOD_HOURS`, `NODE_ENV`

See full details in:
Expand Down
Loading
Loading