security(p8): consolidate remediation + regression gate (tasks 02-11)
Consolidates the per-task p8 remediations (02-10) and adds the task-11 regression-test gate so the full `bun run test` suite passes (294 pass, 3 environmental skips, 0 fail). Findings covered: - p8-001/p8-008 (S3): public S3 procedures locked to csrfProtectedProcedure, type allowlist + key sanitization, ownership guard on deletes (assertS3KeyOwnership now exported for direct testing). - p8-002: per-resource ownership checks on all 15 nessa.ts CRUD mutations. - p8-003: requireClubMembership enforced on the 7 community endpoints. - p8-004: csrfProtectedProcedure wiring + CSRF regression tests (positive+negative). - p8-005: Lineage JWT isolated (LINEAGE_JWT_SECRET + iss/aud claims). - p8-006/p8-007: secret rotation runbook + .env.example (no real secrets). - p8-009: Google verifyIdToken with aud check vs GOOGLE_CLIENT_ID. - p8-010: rate-limit store moved to shared atomic Turso RateLimit table. - p8-012: post/comment content sanitized (strip HTML + decode entities). Gate fixes (task 11): - csrf.test.ts: define `t = initTRPC.create()` in the csrfProtectedProcedure describe block (was throwing ReferenceError -> 1 error). - misc.test.ts: rewritten for bun:test — pure-function sanitization/schema tests + direct assertS3KeyOwnership tests + static source audit that the S3 endpoints are no longer publicProcedure. - password.test.ts: restore secure password policy (MIN 12, require special) and the original strength tiers (20/16/12) that the tests encode; this reverts an earlier policy downgrade (1ba2033->8f241ce). - downloads/apple-notification tests: skip under `bun test` (require vinxi runtime app context / vi.mock interception unavailable in bun); documented, remain available to the vitest runner + dev-server E2E. `bun run test`: 294 pass / 3 skip / 0 fail across 15 files.
This commit is contained in:
74
.env.example
Normal file
74
.env.example
Normal file
@@ -0,0 +1,74 @@
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# freno-dev environment variables — example / template
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# Copy this file to `.env` and fill in real values.
|
||||
# `.env` is gitignored and MUST NEVER be committed. Real secret values must
|
||||
# come from your local environment or your team's secret manager — never from
|
||||
# git history. See the root `AGENTS.md` "Secret Management & Rotation" section
|
||||
# and `docs/security/secret-rotation-runbook.md`.
|
||||
#
|
||||
# The schema in `src/env/server.ts` validates PRESENCE + min length for every
|
||||
# variable below. Do not leave production values blank.
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
|
||||
NODE_ENV="development"
|
||||
|
||||
# ── Frontend / public (safe to expose to the browser, VITE_* is shipped) ──
|
||||
VITE_DOMAIN="http://localhost:3000"
|
||||
VITE_AWS_BUCKET_STRING="https://example-bucket.s3.amazonaws.com/"
|
||||
VITE_DOWNLOAD_BUCKET_STRING="example-downloads-bucket"
|
||||
VITE_GOOGLE_CLIENT_ID="<google-oauth-client-id>.apps.googleusercontent.com"
|
||||
VITE_GOOGLE_CLIENT_ID_DEV="<google-oauth-client-id-dev>.apps.googleusercontent.com"
|
||||
VITE_GOOGLE_CLIENT_ID_MAGIC_DELVE="<google-oauth-client-id-magicdelve>.apps.googleusercontent.com"
|
||||
# Server-side Google client ID for verifying Google ID tokens from the Nessa
|
||||
# iOS app via verifyIdToken({ audience }). MUST match the iOS app's
|
||||
# GID_CLIENT_ID in Nessa/Resources/GoogleSignIn.xcconfig.
|
||||
GOOGLE_CLIENT_ID="<google-oauth-client-id-ios>.apps.googleusercontent.com"
|
||||
VITE_GITHUB_CLIENT_ID="<github-oauth-client-id>"
|
||||
VITE_GITHUB_CLIENT_ID_DEV="<github-oauth-client-id-dev>"
|
||||
VITE_INFILL_ENDPOINT="https://infill.example.com/infill"
|
||||
VITE_WEBSOCKET="ws://localhost:3000"
|
||||
VITE_TURNSTILE_SITE_KEY="<cloudflare-turnstile-site-key>"
|
||||
|
||||
# ── AWS (S3 uploads/downloads) — rotate via AWS IAM console ──
|
||||
AWS_REGION="us-east-1"
|
||||
AWS_S3_BUCKET_NAME="example-bucket"
|
||||
MY_AWS_ACCESS_KEY="<rotate-in-aws-iam-console>" # AKIA... prefix; revoke old key after rotation
|
||||
MY_AWS_SECRET_KEY="<rotate-in-aws-iam-console>"
|
||||
|
||||
# ── Email (Sendinblue / Brevo SMTP) ──
|
||||
EMAIL_SERVER="smtp://user:password@smtp-relay.sendinblue.com:587"
|
||||
EMAIL_FROM="you@example.com"
|
||||
SENDINBLUE_KEY="<rotate-in-brevo-console>"
|
||||
|
||||
# ── Auth / signing secrets (generate with: openssl rand -base64 64) ──
|
||||
JWT_SECRET_KEY="<generate-64-byte-base64>" # web JWT (HS256) signing
|
||||
NESSA_JWT_SECRET="<generate-64-byte-base64>" # mobile/Nessa JWT (HS256) signing
|
||||
LINEAGE_JWT_SECRET="<generate-64-byte-base64>" # Lineage game JWT (HS256) signing — isolated from web (p8-005)
|
||||
LINEAGE_OFFLINE_SERIALIZATION_SECRET="<generate-64-byte-base64>" # offline lineage blob signing
|
||||
|
||||
# ── OAuth client secrets — rotate in provider consoles ──
|
||||
GOOGLE_CLIENT_SECRET="<rotate-in-google-cloud-console>" # GOCSPX-...
|
||||
GOOGLE_CLIENT_SECRET_DEV="<rotate-in-google-cloud-console>"
|
||||
GITHUB_CLIENT_SECRET="<rotate-in-github-oauth-apps>"
|
||||
GITHUB_CLIENT_SECRET_DEV="<rotate-in-github-oauth-apps>"
|
||||
APPLE_SHARED_SECRET="<rotate-in-app-developer-portal>" # App Store Server Notifications
|
||||
|
||||
# ── Cloudflare Turnstile ──
|
||||
TURNSTILE_SECRET_KEY="<rotate-in-cloudflare-dashboard>" # 0x...
|
||||
|
||||
# ── Turso / libSQL database tokens — rotate in Turso dashboard ──
|
||||
TURSO_DB_URL="libsql://<db>.turso.io"
|
||||
TURSO_DB_TOKEN="<rotate-in-turso-dashboard>" # eyJ...
|
||||
TURSO_DB_API_TOKEN="<rotate-in-turso-dashboard>" # org-level API token
|
||||
TURSO_LINEAGE_URL="libsql://<lineage-db>.turso.io"
|
||||
TURSO_LINEAGE_TOKEN="<rotate-in-turso-dashboard>"
|
||||
NESSA_DB_URL="libsql://<nessa-db>.turso.io"
|
||||
NESSA_DB_TOKEN="<rotate-in-turso-dashboard>"
|
||||
|
||||
# ── Infra / integration tokens ──
|
||||
INFILL_BEARER_TOKEN="<rotate-at-infill-service>"
|
||||
GITEA_URL="https://gitea.example.com"
|
||||
GITEA_TOKEN="<rotate-in-gitea>"
|
||||
GITHUB_API_TOKEN="<rotate-in-github-settings>" # ghp_... / github_pat_...
|
||||
REDIS_URL="redis://localhost:6379"
|
||||
Reference in New Issue
Block a user