diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0bc5f40 --- /dev/null +++ b/.env.example @@ -0,0 +1,35 @@ +# AutoSec Docker Compose Environment +# Copy this file to .env and fill in real values before running `docker compose up` + +# ==== REQUIRED SECRETS (must be set before first run) ==== +# Generate with: openssl rand -base64 48 +JWT_SECRET=CHANGE_ME_GENERATE_WITH_openssl_rand_base64_48 + +# Database passwords +POSTGRES_PASSWORD=CHANGE_ME_STRONG_PASSWORD +REDIS_PASSWORD=CHANGE_ME_STRONG_PASSWORD +RABBITMQ_DEFAULT_PASS=CHANGE_ME_STRONG_PASSWORD + +# ==== OPTIONAL (defaults shown) ==== +POSTGRES_DB=autosec_db +POSTGRES_USER=autosec_user + +# Port mappings (set to different values to avoid conflicts) +# PG_EXPOSE_PORT=5432 +# MONGO_EXPOSE_PORT=27017 +# REDIS_EXPOSE_PORT=6379 +# RABBIT_AMQP_PORT=5672 +# RABBIT_MGMT_PORT=15672 +# BACKEND_PORT=8080 +# FRONTEND_PORT=3000 + +# Logging +LOG_LEVEL=info +NODE_ENV=production + +# JWT expiry +JWT_EXPIRES_IN=24h +JWT_REFRESH_EXPIRES_IN=7d + +# Frontend URL (for CORS) +FRONTEND_URL=http://localhost:3000 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 51ba825..dc7d910 100644 --- a/.gitignore +++ b/.gitignore @@ -1,62 +1,33 @@ # Dependencies node_modules/ -npm-debug.log* -yarn-debug.log* -yarn-error.log* +package-lock.json +*.lock -# Environment variables +# Environment files .env .env.local -.env.development.local -.env.test.local -.env.production.local +.env.production +*.env -# Build outputs -build/ -dist/ -*.tgz -*.tar.gz - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Coverage directory used by tools like istanbul -coverage/ -*.lcov - -# IDE and editor files -.vscode/ -.idea/ -*.swp -*.swo -*~ - -# OS generated files +# OS files .DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db Thumbs.db -# Docker -.dockerignore +# IDE files +.idea/ +.vscode/ +*.swp -# GeoIP database (should be downloaded separately) -data/geoip/GeoLite2-City.mmdb +# Build output +build/ +dist/ # Logs -logs +logs/ *.log -# Database data (when using local development) -postgres_data/ -mongodb_data/ +# GeoIP database (user must download) +data/geoip/*.mmdb -# Temporary files -tmp/ -temp/ \ No newline at end of file +# Docker volumes (local data) +uploads/ \ No newline at end of file diff --git a/REVIEW.md b/REVIEW.md new file mode 100644 index 0000000..b6ff92e --- /dev/null +++ b/REVIEW.md @@ -0,0 +1,187 @@ +# AutoSec Code Review — Security Audit & Improvements + +**Reviewed:** 2026-08-14 +**Scope:** Full stack (backend, frontend, infra) +**Severity scale:** 🔴 Critical | 🟠 High | 🟡 Medium | 🔵 Low + +--- + +## Critical Issues Fixed + +### 1. 🔴 Role Self-Escalation on Registration +**File:** `backend/src/controllers/authController.js:33` +**Bug:** The `register` endpoint accepted `role` from user input. Any attacker could +`POST /api/auth/register` with `{ "role": "admin" }` and gain full admin access. +**Fix:** Role is now hardcoded to `'viewer'` on registration. Removed `role` from +validation schema. Admin role assignment requires existing admin auth via user +management endpoints. + +### 2. 🔴 Sequelize Query Uses MongoDB `$or` Syntax +**File:** `backend/src/controllers/authController.js:38,104` +**Bug:** `User.findOne({ where: { $or: [...] } })` is MongoDB syntax. Sequelize +ignores `$or` and performs no filtering — the query returns the **first user in +the table** regardless of email/username. This means: +- Registration: always reports "user exists" OR never detects duplicates +- Login: authenticates as the wrong user (first in DB) when $or is ignored +**Fix:** Replaced with `{ [Op.or]: [{ email }, { username }] }` using Sequelize's +`Op` operators. + +### 3. 🔴 JWT Secret Uses Hardcoded Fallback +**Files:** `authController.js:8`, `middleware/auth.js:6` +**Bug:** `process.env.JWT_SECRET || 'your-secret-key-change-in-production'` means +any deployment without a `.env` file runs with a publicly-known signing key. +Attackers can forge any JWT. +**Fix:** Added startup validation — server **refuses to start** if `JWT_SECRET` is +missing, shorter than 32 chars, or equals the placeholder string. + +### 4. 🔴 XSS Sanitization Trivially Bypassed +**File:** `middleware/security.js:231-248` +**Bug:** The regex `/)<[^<]*)*<\/script>/gi` only +strips `