From 225b5a29cc2bb1446f745854a0ad16e7c974590e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 Aug 2025 03:48:36 +0000 Subject: [PATCH 1/4] Enhance security, environment config, and project structure across services Co-authored-by: tonykflex --- .env.example | 352 +++++++------------- ai-service/app/config.py | 7 +- backend/api/.eslintrc.js | 96 ++++++ backend/api/knexfile.js | 63 ++-- backend/api/src/config/index.js | 28 +- backend/api/src/middleware/security.js | 252 +++++++++++++++ docker-compose.yml | 35 +- frontend/App.tsx | 42 ++- frontend/jest.setup.js | 227 ++++++++++++- scripts/security-audit.sh | 428 +++++++++++++++++++++++++ 10 files changed, 1236 insertions(+), 294 deletions(-) create mode 100644 backend/api/.eslintrc.js create mode 100644 backend/api/src/middleware/security.js create mode 100755 scripts/security-audit.sh diff --git a/.env.example b/.env.example index 27ffa89..637ed2a 100644 --- a/.env.example +++ b/.env.example @@ -1,315 +1,191 @@ # ============================================================================= -# ModMaster Pro - Environment Variables Template +# ModMaster Pro Environment Configuration # ============================================================================= # Copy this file to .env and fill in your actual values -# ============================================================================= +# NEVER commit .env files to version control # ============================================================================= -# APPLICATION CONFIGURATION +# Application Settings # ============================================================================= APP_NAME=ModMaster Pro APP_VERSION=1.0.0 ENVIRONMENT=development -DEBUG=true -LOG_LEVEL=debug -TIMEZONE=UTC +DEBUG=false +API_PORT=3000 +API_HOST=0.0.0.0 +API_BASE_URL=http://localhost:3000 +API_VERSION=v1 + +# ============================================================================= +# Security - CRITICAL: Change these in production! +# ============================================================================= +JWT_SECRET=your_super_secure_jwt_secret_key_change_this_in_production +JWT_ALGORITHM=HS256 +JWT_EXPIRES_IN=24h +JWT_REFRESH_EXPIRES_IN=7d +JWT_ISSUER=modmaster-pro +JWT_AUDIENCE=modmaster-pro-users + +# Password Security +BCRYPT_ROUNDS=12 +PASSWORD_MIN_LENGTH=8 +PASSWORD_REQUIRE_UPPERCASE=true +PASSWORD_REQUIRE_LOWERCASE=true +PASSWORD_REQUIRE_NUMBERS=true +PASSWORD_REQUIRE_SPECIAL_CHARS=true # ============================================================================= -# DATABASE CONFIGURATION +# Database Configuration # ============================================================================= -# PostgreSQL -DATABASE_URL=postgresql://modmaster_user:modmaster_password@localhost:5432/modmaster_pro +DATABASE_URL=postgresql://modmaster_user:your_secure_password@localhost:5432/modmaster_pro POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_DB=modmaster_pro POSTGRES_USER=modmaster_user -POSTGRES_PASSWORD=modmaster_password +POSTGRES_PASSWORD=your_secure_database_password POSTGRES_SSL_MODE=disable -# Redis +# Database Pool Settings +DB_POOL_MIN=2 +DB_POOL_MAX=10 + +# Test Database (separate from main database) +TEST_POSTGRES_HOST=localhost +TEST_POSTGRES_PORT=5432 +TEST_POSTGRES_DB=modmaster_pro_test +TEST_POSTGRES_USER=modmaster_user +TEST_POSTGRES_PASSWORD=your_secure_test_password + +# ============================================================================= +# Redis Configuration +# ============================================================================= REDIS_URL=redis://localhost:6379 REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD= REDIS_DB=0 +REDIS_KEY_PREFIX=modmaster: -# Elasticsearch +# ============================================================================= +# Elasticsearch Configuration +# ============================================================================= ELASTICSEARCH_URL=http://localhost:9200 ELASTICSEARCH_HOST=localhost ELASTICSEARCH_PORT=9200 ELASTICSEARCH_USERNAME= ELASTICSEARCH_PASSWORD= +ELASTICSEARCH_INDEX_PREFIX=modmaster # ============================================================================= -# API CONFIGURATION +# CORS Configuration # ============================================================================= -API_HOST=0.0.0.0 -API_PORT=3000 -API_BASE_URL=http://localhost:3000 -API_VERSION=v1 -CORS_ORIGINS=http://localhost:3000,http://localhost:19000,http://localhost:19001 - -# ============================================================================= -# AUTHENTICATION & SECURITY -# ============================================================================= -JWT_SECRET=your_super_secret_jwt_key_change_in_production -JWT_ALGORITHM=HS256 -JWT_EXPIRES_IN=24h -JWT_REFRESH_EXPIRES_IN=7d -BCRYPT_ROUNDS=12 - -# OAuth Providers -GOOGLE_CLIENT_ID=your_google_client_id -GOOGLE_CLIENT_SECRET=your_google_client_secret -FACEBOOK_APP_ID=your_facebook_app_id -FACEBOOK_APP_SECRET=your_facebook_app_secret - -# ============================================================================= -# AI/ML SERVICE CONFIGURATION -# ============================================================================= -AI_SERVICE_URL=http://localhost:8000 -AI_SERVICE_PORT=8000 -MODEL_STORAGE_PATH=./ai-ml/models -TENSORFLOW_GPU_ENABLED=false -PYTORCH_GPU_ENABLED=false - -# Computer Vision -CV_MODEL_PATH=./ai-ml/models/computer-vision -CV_CONFIDENCE_THRESHOLD=0.85 -CV_MAX_IMAGE_SIZE=4096 - -# Recommendation Engine -RECOMMENDATION_MODEL_PATH=./ai-ml/models/recommendation-engine -RECOMMENDATION_CACHE_TTL=3600 +CORS_ORIGINS=http://localhost:3000,http://localhost:19000,http://localhost:19006 +CORS_ALLOW_CREDENTIALS=true +CORS_MAX_AGE=86400 # ============================================================================= -# WEB SCRAPING CONFIGURATION +# Rate Limiting # ============================================================================= -SCRAPING_SERVICE_URL=http://localhost:8001 -SCRAPING_SERVICE_PORT=8001 -N8N_URL=http://localhost:5678 -N8N_USERNAME=admin -N8N_PASSWORD=modmaster123 - -# Scraping Limits -SCRAPING_RATE_LIMIT=2 -SCRAPING_DELAY_MS=1000 -SCRAPING_MAX_RETRIES=3 -SCRAPING_TIMEOUT_MS=30000 - -# Proxy Configuration -USE_PROXIES=false -PROXY_LIST_PATH=./web-scraping/proxy_lists -PROXY_ROTATION_INTERVAL=300 +RATE_LIMIT_WINDOW_MS=900000 +RATE_LIMIT_MAX_REQUESTS=100 +RATE_LIMIT_SKIP_SUCCESSFUL_REQUESTS=false # ============================================================================= -# EXTERNAL API INTEGRATIONS +# File Upload Configuration # ============================================================================= -# NHTSA Vehicle API -NHTSA_API_KEY=your_nhtsa_api_key -NHTSA_API_URL=https://vpic.nhtsa.dot.gov/api - -# Edmunds API -EDMUNDS_API_KEY=your_edmunds_api_key -EDMUNDS_API_URL=https://api.edmunds.com/api - -# Google Vision API -GOOGLE_VISION_API_KEY=your_google_vision_api_key -GOOGLE_VISION_API_URL=https://vision.googleapis.com/v1 - -# AWS Services -AWS_ACCESS_KEY_ID=your_aws_access_key -AWS_SECRET_ACCESS_KEY=your_aws_secret_key -AWS_REGION=us-east-1 -AWS_S3_BUCKET=modmaster-pro-storage -AWS_REKOGNITION_ENABLED=false +MAX_FILE_SIZE=5242880 +ALLOWED_FILE_TYPES=jpg,jpeg,png,webp +UPLOAD_STORAGE_PATH=./uploads +MAX_REQUEST_SIZE=10MB # ============================================================================= -# MARKETPLACE INTEGRATIONS +# Logging Configuration # ============================================================================= -# Amazon -AMAZON_API_KEY=your_amazon_api_key -AMAZON_API_SECRET=your_amazon_api_secret -AMAZON_PARTNER_TAG=your_partner_tag -AMAZON_MARKETPLACE_ID=ATVPDKIKX0DER - -# eBay -EBAY_APP_ID=your_ebay_app_id -EBAY_CERT_ID=your_ebay_cert_id -EBAY_CLIENT_SECRET=your_ebay_client_secret -EBAY_SANDBOX=false - -# AutoZone -AUTOZONE_API_KEY=your_autozone_api_key -AUTOZONE_API_URL=https://api.autozone.com - -# Summit Racing -SUMMIT_RACING_API_KEY=your_summit_racing_api_key -SUMMIT_RACING_API_URL=https://api.summitracing.com +LOG_LEVEL=info +LOG_FORMAT=json +LOG_FILE_PATH=./logs +LOG_MAX_SIZE=100MB # ============================================================================= -# FILE STORAGE +# MinIO Object Storage # ============================================================================= -# MinIO (S3 Compatible) -MINIO_ENDPOINT=localhost:9000 -MINIO_ACCESS_KEY=modmaster_user -MINIO_SECRET_KEY=modmaster_password -MINIO_BUCKET=modmaster-pro -MINIO_USE_SSL=false - -# Cloud Storage STORAGE_PROVIDER=minio -UPLOAD_MAX_SIZE=10485760 -ALLOWED_IMAGE_TYPES=jpg,jpeg,png,webp -ALLOWED_VIDEO_TYPES=mp4,avi,mov +MINIO_ENDPOINT=localhost +MINIO_PORT=9000 +MINIO_USE_SSL=false +MINIO_ACCESS_KEY=modmaster_access_key +MINIO_SECRET_KEY=your_secure_minio_secret_key +MINIO_BUCKET_NAME=modmaster-pro # ============================================================================= -# MONITORING & ANALYTICS +# AWS S3 (Alternative to MinIO) # ============================================================================= -# Prometheus -PROMETHEUS_URL=http://localhost:9090 -PROMETHEUS_ENABLED=true - -# Grafana -GRAFANA_URL=http://localhost:3001 -GRAFANA_USERNAME=admin -GRAFANA_PASSWORD=modmaster123 - -# Sentry -SENTRY_DSN=your_sentry_dsn -SENTRY_ENVIRONMENT=development +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_REGION=us-east-1 +AWS_S3_BUCKET=modmaster-pro # ============================================================================= -# EMAIL & NOTIFICATIONS +# Email Configuration # ============================================================================= -# SMTP Configuration +EMAIL_PROVIDER=smtp SMTP_HOST=smtp.gmail.com SMTP_PORT=587 -SMTP_USERNAME=your_email@gmail.com +SMTP_USER=your_email@gmail.com SMTP_PASSWORD=your_app_password -SMTP_FROM_EMAIL=noreply@modmasterpro.com -SMTP_FROM_NAME=ModMaster Pro - -# Push Notifications -FIREBASE_SERVER_KEY=your_firebase_server_key -FIREBASE_PROJECT_ID=your_firebase_project_id - -# ============================================================================= -# PAYMENT PROCESSING -# ============================================================================= -# Stripe -STRIPE_PUBLISHABLE_KEY=pk_test_your_stripe_key -STRIPE_SECRET_KEY=sk_test_your_stripe_key -STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret - -# PayPal -PAYPAL_CLIENT_ID=your_paypal_client_id -PAYPAL_CLIENT_SECRET=your_paypal_client_secret -PAYPAL_MODE=sandbox +SMTP_SECURE=false +EMAIL_FROM=noreply@modmasterpro.com # ============================================================================= -# DEVELOPMENT & TESTING +# SMS Configuration (Twilio) # ============================================================================= -# Test Database -TEST_DATABASE_URL=postgresql://modmaster_user:modmaster_password@localhost:5432/modmaster_pro_test - -# Mock Services -MOCK_EXTERNAL_APIS=false -MOCK_AI_SERVICES=false -MOCK_SCRAPING=false +TWILIO_ACCOUNT_SID= +TWILIO_AUTH_TOKEN= +TWILIO_PHONE_NUMBER= # ============================================================================= -# DEPLOYMENT +# External Services # ============================================================================= -# Docker -DOCKER_REGISTRY=your_docker_registry -DOCKER_IMAGE_TAG=latest - -# Kubernetes -KUBERNETES_NAMESPACE=modmaster-pro -KUBERNETES_CONTEXT=your_k8s_context - -# Terraform -TF_VAR_environment=development -TF_VAR_project_name=modmaster-pro -TF_VAR_region=us-east-1 +BACKEND_API_URL=http://localhost:3000 +AI_SERVICE_URL=http://localhost:8001 +WEB_SCRAPING_URL=http://localhost:5678 # ============================================================================= -# FEATURE FLAGS +# n8n Workflow Automation # ============================================================================= -FEATURE_AI_SCANNING=true -FEATURE_SMART_RECOMMENDATIONS=true -FEATURE_REAL_TIME_PRICING=true -FEATURE_SOCIAL_FEATURES=true -FEATURE_SHOP_INTEGRATION=false -FEATURE_ENTERPRISE_ANALYTICS=false +N8N_URL=http://localhost:5678 +N8N_USERNAME=admin +N8N_PASSWORD=your_secure_n8n_password +N8N_WEBHOOK_TOKEN= # ============================================================================= -# PERFORMANCE & CACHING +# Grafana Monitoring # ============================================================================= -CACHE_TTL_DEFAULT=3600 -CACHE_TTL_USER_DATA=1800 -CACHE_TTL_PARTS_DATA=7200 -CACHE_TTL_PRICE_DATA=300 -CACHE_TTL_AI_PREDICTIONS=3600 - -# Rate Limiting -RATE_LIMIT_WINDOW_MS=900000 -RATE_LIMIT_MAX_REQUESTS=100 -RATE_LIMIT_SKIP_SUCCESSFUL_REQUESTS=false +GRAFANA_ADMIN_USER=admin +GRAFANA_ADMIN_PASSWORD=your_secure_grafana_password # ============================================================================= -# LOGGING +# AI Service Configuration # ============================================================================= -LOG_FORMAT=json -LOG_FILE_PATH=./logs -LOG_MAX_SIZE=100MB -LOG_MAX_FILES=10 -LOG_COMPRESS=true +INTERNAL_API_KEY=your_secure_internal_api_key_for_ai_service # ============================================================================= -# SECURITY +# Third-Party API Keys # ============================================================================= -# CORS -CORS_ALLOW_CREDENTIALS=true -CORS_MAX_AGE=86400 - -# Rate Limiting -RATE_LIMIT_ENABLED=true -RATE_LIMIT_SKIP_SUCCESSFUL_REQUESTS=false - -# Input Validation -MAX_REQUEST_SIZE=10MB -MAX_FILE_SIZE=5MB -ALLOWED_FILE_TYPES=jpg,jpeg,png,pdf,doc,docx +# Add your third-party API keys here +# GOOGLE_MAPS_API_KEY= +# STRIPE_SECRET_KEY= +# SENDGRID_API_KEY= +# FIREBASE_SERVER_KEY= # ============================================================================= -# NOTIFICATIONS +# Development/Testing # ============================================================================= -# Slack -SLACK_WEBHOOK_URL=your_slack_webhook_url -SLACK_CHANNEL=#modmaster-pro +# Set to true to enable detailed logging +# DEBUG=true -# Discord -DISCORD_WEBHOOK_URL=your_discord_webhook_url -DISCORD_CHANNEL_ID=your_discord_channel_id +# Set to true to skip authentication in development +# SKIP_AUTH=false -# ============================================================================= -# ANALYTICS & TRACKING -# ============================================================================= -# Google Analytics -GOOGLE_ANALYTICS_ID=your_ga_id -GOOGLE_ANALYTICS_ENABLED=false - -# Mixpanel -MIXPANEL_TOKEN=your_mixpanel_token -MIXPANEL_ENABLED=false - -# ============================================================================= -# BACKUP & RECOVERY -# ============================================================================= -BACKUP_ENABLED=true -BACKUP_SCHEDULE=0 2 * * * -BACKUP_RETENTION_DAYS=30 -BACKUP_STORAGE_PATH=./backups -BACKUP_COMPRESSION=true \ No newline at end of file +# Set to true to enable test mode +# TEST_MODE=false \ No newline at end of file diff --git a/ai-service/app/config.py b/ai-service/app/config.py index 553279a..f14b121 100644 --- a/ai-service/app/config.py +++ b/ai-service/app/config.py @@ -10,13 +10,10 @@ class Settings(BaseSettings): LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO") # Security - API_KEY: str = os.getenv("INTERNAL_API_KEY", "dev-api-key") + API_KEY: str = os.getenv("INTERNAL_API_KEY") or (lambda: (_ for _ in ()).throw(ValueError("INTERNAL_API_KEY environment variable is required for security")))() # Database - DATABASE_URL: str = os.getenv( - "DATABASE_URL", - "postgresql://modmaster_user:modmaster_password@postgres:5432/modmaster_pro" - ) + DATABASE_URL: str = os.getenv("DATABASE_URL") or (lambda: (_ for _ in ()).throw(ValueError("DATABASE_URL environment variable is required")))() # Redis REDIS_URL: str = os.getenv("REDIS_URL", "redis://redis:6379/1") diff --git a/backend/api/.eslintrc.js b/backend/api/.eslintrc.js new file mode 100644 index 0000000..9d6a475 --- /dev/null +++ b/backend/api/.eslintrc.js @@ -0,0 +1,96 @@ +module.exports = { + env: { + node: true, + es2021: true, + jest: true, + }, + extends: [ + 'eslint:recommended', + 'airbnb-base', + ], + plugins: [ + 'security', + 'node', + ], + parserOptions: { + ecmaVersion: 2021, + sourceType: 'module', + }, + rules: { + // Security rules + 'security/detect-object-injection': 'error', + 'security/detect-non-literal-regexp': 'error', + 'security/detect-unsafe-regex': 'error', + 'security/detect-buffer-noassert': 'error', + 'security/detect-child-process': 'error', + 'security/detect-disable-mustache-escape': 'error', + 'security/detect-eval-with-expression': 'error', + 'security/detect-no-csrf-before-method-override': 'error', + 'security/detect-non-literal-fs-filename': 'error', + 'security/detect-non-literal-require': 'error', + 'security/detect-possible-timing-attacks': 'error', + 'security/detect-pseudoRandomBytes': 'error', + + // Code quality + 'no-console': 'warn', + 'no-debugger': 'error', + 'no-alert': 'error', + 'no-eval': 'error', + 'no-implied-eval': 'error', + 'no-new-func': 'error', + 'no-script-url': 'error', + 'no-var': 'error', + 'prefer-const': 'error', + 'no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + 'no-undef': 'error', + 'no-global-assign': 'error', + + // Node.js specific + 'node/no-unsupported-features/es-syntax': 'off', + 'node/no-missing-import': 'error', + 'node/no-unpublished-import': 'error', + 'node/no-extraneous-import': 'error', + 'node/no-missing-require': 'error', + 'node/no-unpublished-require': 'error', + 'node/no-extraneous-require': 'error', + + // Import rules + 'import/no-unresolved': 'error', + 'import/named': 'error', + 'import/default': 'error', + 'import/namespace': 'error', + 'import/no-duplicates': 'error', + 'import/order': ['error', { + groups: [ + 'builtin', + 'external', + 'internal', + 'parent', + 'sibling', + 'index', + ], + 'newlines-between': 'always', + }], + + // Code style + 'indent': ['error', 2], + 'linebreak-style': ['error', 'unix'], + 'quotes': ['error', 'single'], + 'semi': ['error', 'always'], + 'comma-dangle': ['error', 'always-multiline'], + 'object-curly-spacing': ['error', 'always'], + 'array-bracket-spacing': ['error', 'never'], + 'max-len': ['error', { code: 100 }], + }, + overrides: [ + { + files: ['**/*.test.js', '**/*.spec.js'], + env: { + jest: true, + }, + rules: { + 'no-console': 'off', + }, + }, + ], +}; \ No newline at end of file diff --git a/backend/api/knexfile.js b/backend/api/knexfile.js index 0bd0aae..968e0c5 100644 --- a/backend/api/knexfile.js +++ b/backend/api/knexfile.js @@ -1,9 +1,22 @@ -const config = require('./src/config'); +require('dotenv').config(); module.exports = { development: { - client: 'pg', - connection: config.database, + client: 'postgresql', + connection: { + host: process.env.POSTGRES_HOST || 'localhost', + port: process.env.POSTGRES_PORT || 5432, + database: process.env.POSTGRES_DB || 'modmaster_pro', + user: process.env.POSTGRES_USER || 'modmaster_user', + password: process.env.POSTGRES_PASSWORD || (() => { + throw new Error('POSTGRES_PASSWORD environment variable is required for security'); + })(), + ssl: process.env.POSTGRES_SSL_MODE === 'require' ? { rejectUnauthorized: false } : false, + }, + pool: { + min: parseInt(process.env.DB_POOL_MIN, 10) || 2, + max: parseInt(process.env.DB_POOL_MAX, 10) || 10, + }, migrations: { directory: './src/database/migrations', tableName: 'knex_migrations', @@ -13,9 +26,22 @@ module.exports = { }, }, - staging: { - client: 'pg', - connection: config.database, + test: { + client: 'postgresql', + connection: { + host: process.env.TEST_POSTGRES_HOST || 'localhost', + port: process.env.TEST_POSTGRES_PORT || 5432, + database: process.env.TEST_POSTGRES_DB || 'modmaster_pro_test', + user: process.env.TEST_POSTGRES_USER || 'modmaster_user', + password: process.env.TEST_POSTGRES_PASSWORD || (() => { + throw new Error('TEST_POSTGRES_PASSWORD environment variable is required for security'); + })(), + ssl: false, + }, + pool: { + min: 1, + max: 5, + }, migrations: { directory: './src/database/migrations', tableName: 'knex_migrations', @@ -26,26 +52,13 @@ module.exports = { }, production: { - client: 'pg', - connection: config.database, - migrations: { - directory: './src/database/migrations', - tableName: 'knex_migrations', - }, + client: 'postgresql', + connection: process.env.DATABASE_URL || (() => { + throw new Error('DATABASE_URL environment variable is required for production'); + })(), pool: { - min: 5, - max: 20, - }, - }, - - test: { - client: 'pg', - connection: process.env.TEST_DATABASE_URL || { - host: 'localhost', - port: 5432, - database: 'modmaster_pro_test', - user: 'modmaster_user', - password: 'modmaster_password', + min: parseInt(process.env.DB_POOL_MIN, 10) || 2, + max: parseInt(process.env.DB_POOL_MAX, 10) || 10, }, migrations: { directory: './src/database/migrations', diff --git a/backend/api/src/config/index.js b/backend/api/src/config/index.js index 0b87466..ab6037b 100644 --- a/backend/api/src/config/index.js +++ b/backend/api/src/config/index.js @@ -15,12 +15,16 @@ const config = { // Database database: { - url: process.env.DATABASE_URL || 'postgresql://modmaster_user:modmaster_password@localhost:5432/modmaster_pro', + url: process.env.DATABASE_URL || (() => { + throw new Error('DATABASE_URL environment variable is required'); + })(), host: process.env.POSTGRES_HOST || 'localhost', port: parseInt(process.env.POSTGRES_PORT, 10) || 5432, name: process.env.POSTGRES_DB || 'modmaster_pro', username: process.env.POSTGRES_USER || 'modmaster_user', - password: process.env.POSTGRES_PASSWORD || 'modmaster_password', + password: process.env.POSTGRES_PASSWORD || (() => { + throw new Error('POSTGRES_PASSWORD environment variable is required for security'); + })(), sslMode: process.env.POSTGRES_SSL_MODE || 'disable', pool: { min: parseInt(process.env.DB_POOL_MIN, 10) || 2, @@ -50,7 +54,9 @@ const config = { // JWT Authentication jwt: { - secret: process.env.JWT_SECRET || 'your_super_secret_jwt_key_change_in_production', + secret: process.env.JWT_SECRET || (() => { + throw new Error('JWT_SECRET environment variable is required for security'); + })(), algorithm: process.env.JWT_ALGORITHM || 'HS256', expiresIn: process.env.JWT_EXPIRES_IN || '24h', refreshExpiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d', @@ -115,7 +121,10 @@ const config = { n8n: { url: process.env.N8N_URL || 'http://localhost:5678', username: process.env.N8N_USERNAME || 'admin', - password: process.env.N8N_PASSWORD || 'modmaster123', + password: process.env.N8N_PASSWORD || (() => { + throw new Error('N8N_PASSWORD environment variable is required for security'); + })(), + webhookToken: process.env.N8N_WEBHOOK_TOKEN || '', }, }, @@ -160,11 +169,14 @@ const config = { storage: { provider: process.env.STORAGE_PROVIDER || 'minio', minio: { - endpoint: process.env.MINIO_ENDPOINT || 'localhost:9000', - accessKey: process.env.MINIO_ACCESS_KEY || 'modmaster_user', - secretKey: process.env.MINIO_SECRET_KEY || 'modmaster_password', - bucket: process.env.MINIO_BUCKET || 'modmaster-pro', + endpoint: process.env.MINIO_ENDPOINT || 'localhost', + port: parseInt(process.env.MINIO_PORT, 10) || 9000, useSSL: process.env.MINIO_USE_SSL === 'true', + accessKey: process.env.MINIO_ACCESS_KEY || 'modmaster_access_key', + secretKey: process.env.MINIO_SECRET_KEY || (() => { + throw new Error('MINIO_SECRET_KEY environment variable is required for security'); + })(), + bucketName: process.env.MINIO_BUCKET_NAME || 'modmaster-pro', }, aws: { accessKeyId: process.env.AWS_ACCESS_KEY_ID || '', diff --git a/backend/api/src/middleware/security.js b/backend/api/src/middleware/security.js new file mode 100644 index 0000000..e2732f9 --- /dev/null +++ b/backend/api/src/middleware/security.js @@ -0,0 +1,252 @@ +const helmet = require('helmet'); +const rateLimit = require('express-rate-limit'); +const slowDown = require('express-slow-down'); +const hpp = require('hpp'); +const xss = require('xss-clean'); +const mongoSanitize = require('express-mongo-sanitize'); +const validator = require('validator'); + +/** + * Comprehensive security middleware configuration + */ +const securityMiddleware = { + /** + * Configure Helmet security headers + */ + helmet: helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + imgSrc: ["'self'", "data:", "https:", "blob:"], + scriptSrc: ["'self'"], + connectSrc: ["'self'", "https://api.modmasterpro.com"], + frameSrc: ["'none'"], + objectSrc: ["'none'"], + upgradeInsecureRequests: [], + }, + }, + crossOriginEmbedderPolicy: false, + crossOriginResourcePolicy: { policy: "cross-origin" }, + dnsPrefetchControl: { allow: false }, + frameguard: { action: "deny" }, + hidePoweredBy: true, + hsts: { + maxAge: 31536000, + includeSubDomains: true, + preload: true, + }, + ieNoOpen: true, + noSniff: true, + permittedCrossDomainPolicies: { permittedPolicies: "none" }, + referrerPolicy: { policy: "strict-origin-when-cross-origin" }, + xssFilter: true, + }), + + /** + * Rate limiting configuration + */ + rateLimiter: rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // limit each IP to 100 requests per windowMs + message: { + error: 'Too many requests from this IP, please try again later.', + code: 'RATE_LIMIT_EXCEEDED', + }, + standardHeaders: true, + legacyHeaders: false, + skipSuccessfulRequests: false, + keyGenerator: (req) => { + // Use X-Forwarded-For header if behind proxy, otherwise use IP + return req.headers['x-forwarded-for'] || req.ip || req.connection.remoteAddress; + }, + }), + + /** + * Slow down requests to prevent brute force attacks + */ + speedLimiter: slowDown({ + windowMs: 15 * 60 * 1000, // 15 minutes + delayAfter: 50, // allow 50 requests per 15 minutes, then... + delayMs: 500, // begin adding 500ms of delay per request above 50 + maxDelayMs: 20000, // max delay of 20 seconds + }), + + /** + * HTTP Parameter Pollution protection + */ + hpp: hpp({ + whitelist: ['filter', 'sort', 'page', 'limit'], // Allow these parameters to be duplicated + }), + + /** + * XSS protection + */ + xss: xss(), + + /** + * MongoDB injection protection + */ + mongoSanitize: mongoSanitize({ + replaceWith: '_', + onSanitize: ({ req, key }) => { + console.warn(`Sanitized MongoDB injection attempt: ${key}`); + }, + }), + + /** + * Custom input sanitization middleware + */ + sanitizeInput: (req, res, next) => { + // Sanitize query parameters + if (req.query) { + Object.keys(req.query).forEach(key => { + if (typeof req.query[key] === 'string') { + req.query[key] = validator.escape(req.query[key]); + } + }); + } + + // Sanitize body parameters + if (req.body) { + Object.keys(req.body).forEach(key => { + if (typeof req.body[key] === 'string') { + req.body[key] = validator.escape(req.body[key]); + } + }); + } + + // Sanitize URL parameters + if (req.params) { + Object.keys(req.params).forEach(key => { + if (typeof req.params[key] === 'string') { + req.params[key] = validator.escape(req.params[key]); + } + }); + } + + next(); + }, + + /** + * SQL injection protection middleware + */ + sqlInjectionProtection: (req, res, next) => { + const sqlPatterns = [ + /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION|SCRIPT)\b)/i, + /(\b(OR|AND)\b\s+\d+\s*=\s*\d+)/i, + /(\b(OR|AND)\b\s+['"]?\w+['"]?\s*=\s*['"]?\w+['"]?)/i, + /(\b(OR|AND)\b\s+\d+\s*=\s*\d+\s*--)/i, + /(\b(OR|AND)\b\s+\d+\s*=\s*\d+\s*#)/i, + /(\b(OR|AND)\b\s+\d+\s*=\s*\d+\s*\/\*)/i, + /(\b(OR|AND)\b\s+\d+\s*=\s*\d+\s*\*\/)/i, + /(\b(OR|AND)\b\s+\d+\s*=\s*\d+\s*;)/i, + /(\b(OR|AND)\b\s+\d+\s*=\s*\d+\s*\/)/i, + /(\b(OR|AND)\b\s+\d+\s*=\s*\d+\s*\\\\)/i, + ]; + + const checkForSQLInjection = (obj) => { + for (const key in obj) { + if (typeof obj[key] === 'string') { + for (const pattern of sqlPatterns) { + if (pattern.test(obj[key])) { + return true; + } + } + } else if (typeof obj[key] === 'object' && obj[key] !== null) { + if (checkForSQLInjection(obj[key])) { + return true; + } + } + } + return false; + }; + + if (checkForSQLInjection(req.query) || checkForSQLInjection(req.body) || checkForSQLInjection(req.params)) { + return res.status(400).json({ + error: 'Potential SQL injection detected', + code: 'SQL_INJECTION_DETECTED', + }); + } + + next(); + }, + + /** + * File upload security middleware + */ + fileUploadSecurity: (req, res, next) => { + const allowedMimeTypes = [ + 'image/jpeg', + 'image/jpg', + 'image/png', + 'image/webp', + 'image/gif', + 'application/pdf', + 'text/plain', + ]; + + const maxFileSize = 5 * 1024 * 1024; // 5MB + + if (req.files) { + for (const file of Object.values(req.files)) { + if (Array.isArray(file)) { + for (const f of file) { + if (!allowedMimeTypes.includes(f.mimetype)) { + return res.status(400).json({ + error: 'Invalid file type', + code: 'INVALID_FILE_TYPE', + }); + } + if (f.size > maxFileSize) { + return res.status(400).json({ + error: 'File too large', + code: 'FILE_TOO_LARGE', + }); + } + } + } else { + if (!allowedMimeTypes.includes(file.mimetype)) { + return res.status(400).json({ + error: 'Invalid file type', + code: 'INVALID_FILE_TYPE', + }); + } + if (file.size > maxFileSize) { + return res.status(400).json({ + error: 'File too large', + code: 'FILE_TOO_LARGE', + }); + } + } + } + } + + next(); + }, + + /** + * CORS configuration + */ + cors: { + origin: process.env.CORS_ORIGINS ? process.env.CORS_ORIGINS.split(',') : ['http://localhost:3000'], + credentials: true, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'], + maxAge: 86400, + }, + + /** + * Request size limiting + */ + requestSizeLimit: { + limit: '10mb', + message: { + error: 'Request entity too large', + code: 'REQUEST_TOO_LARGE', + }, + }, +}; + +module.exports = securityMiddleware; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 1e7a94d..12f6443 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,9 +6,9 @@ services: image: postgres:15-alpine container_name: modmaster-postgres environment: - POSTGRES_DB: modmaster_pro - POSTGRES_USER: modmaster_user - POSTGRES_PASSWORD: modmaster_password + POSTGRES_DB: ${POSTGRES_DB:-modmaster_pro} + POSTGRES_USER: ${POSTGRES_USER:-modmaster_user} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} ports: - "5432:5432" volumes: @@ -17,7 +17,7 @@ services: networks: - modmaster-network healthcheck: - test: ["CMD-SHELL", "pg_isready -U modmaster_user -d modmaster_pro"] + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-modmaster_user} -d ${POSTGRES_DB:-modmaster_pro}"] interval: 10s timeout: 5s retries: 5 @@ -64,8 +64,8 @@ services: container_name: modmaster-n8n environment: - N8N_BASIC_AUTH_ACTIVE=true - - N8N_BASIC_AUTH_USER=admin - - N8N_BASIC_AUTH_PASSWORD=modmaster123 + - N8N_BASIC_AUTH_USER=${N8N_USERNAME:-admin} + - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD} - N8N_HOST=localhost - N8N_PORT=5678 - N8N_PROTOCOL=http @@ -90,10 +90,10 @@ services: container_name: modmaster-backend-api environment: - NODE_ENV=development - - DATABASE_URL=postgresql://modmaster_user:modmaster_password@postgres:5432/modmaster_pro + - DATABASE_URL=postgresql://${POSTGRES_USER:-modmaster_user}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-modmaster_pro} - REDIS_URL=redis://redis:6379 - ELASTICSEARCH_URL=http://elasticsearch:9200 - - JWT_SECRET=dev_jwt_secret_key_change_in_production + - JWT_SECRET=${JWT_SECRET} - API_PORT=3000 ports: - "3000:3000" @@ -233,38 +233,41 @@ services: networks: - modmaster-network - # Grafana for Dashboards + # Grafana Dashboard grafana: - image: grafana/grafana:latest + image: grafana/grafana:10.2.0 container_name: modmaster-grafana environment: - - GF_SECURITY_ADMIN_PASSWORD=modmaster123 + - GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:-admin} + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD} - GF_USERS_ALLOW_SIGN_UP=false + - GF_INSTALL_PLUGINS=grafana-piechart-panel,grafana-worldmap-panel ports: - "3001:3000" volumes: - grafana_data:/var/lib/grafana - - ./infrastructure/monitoring/grafana:/etc/grafana/provisioning + - ./monitoring/grafana/provisioning:/etc/grafana/provisioning + - ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards networks: - modmaster-network depends_on: - prometheus - # MinIO for File Storage (S3 Compatible) + # MinIO Object Storage minio: image: minio/minio:latest container_name: modmaster-minio environment: - - MINIO_ROOT_USER=modmaster_user - - MINIO_ROOT_PASSWORD=modmaster_password + - MINIO_ROOT_USER=${MINIO_ACCESS_KEY:-modmaster_access_key} + - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY} ports: - "9000:9000" - "9001:9001" volumes: - minio_data:/data - command: server /data --console-address ":9001" networks: - modmaster-network + command: server /data --console-address ":9001" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s diff --git a/frontend/App.tsx b/frontend/App.tsx index 0519ecb..b7751ab 100644 --- a/frontend/App.tsx +++ b/frontend/App.tsx @@ -1 +1,41 @@ - \ No newline at end of file +import React from 'react'; +import { NavigationContainer } from '@react-navigation/native'; +import { Provider as PaperProvider } from 'react-native-paper'; +import { Provider as StoreProvider } from 'react-redux'; +import { QueryClient, QueryClientProvider } from 'react-query'; +import { StatusBar } from 'expo-status-bar'; +import { SafeAreaProvider } from 'react-native-safe-area-context'; + +import { store } from './src/store'; +import { theme } from './src/theme'; +import RootNavigator from './src/navigation/RootNavigator'; +import { AuthProvider } from './src/contexts/AuthContext'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: 3, + staleTime: 5 * 60 * 1000, // 5 minutes + cacheTime: 10 * 60 * 1000, // 10 minutes + }, + }, +}); + +export default function App() { + return ( + + + + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/frontend/jest.setup.js b/frontend/jest.setup.js index 0519ecb..801b8fd 100644 --- a/frontend/jest.setup.js +++ b/frontend/jest.setup.js @@ -1 +1,226 @@ - \ No newline at end of file +import 'react-native-gesture-handler/jestSetup'; +import '@testing-library/jest-native/extend-expect'; + +// Mock react-native-reanimated +jest.mock('react-native-reanimated', () => { + const Reanimated = require('react-native-reanimated/mock'); + Reanimated.default.call = () => {}; + return Reanimated; +}); + +// Mock expo modules +jest.mock('expo-camera'); +jest.mock('expo-image-picker'); +jest.mock('expo-file-system'); +jest.mock('expo-media-library'); +jest.mock('expo-location'); +jest.mock('expo-notifications'); +jest.mock('expo-device'); +jest.mock('expo-constants'); +jest.mock('expo-linking'); +jest.mock('expo-status-bar'); + +// Mock react-native-vector-icons +jest.mock('react-native-vector-icons/MaterialIcons', () => 'Icon'); +jest.mock('react-native-vector-icons/MaterialCommunityIcons', () => 'Icon'); + +// Mock AsyncStorage +jest.mock('@react-native-async-storage/async-storage', () => + require('@react-native-async-storage/async-storage/jest/async-storage-mock') +); + +// Mock NetInfo +jest.mock('@react-native-community/netinfo', () => ({ + fetch: jest.fn(() => Promise.resolve({ isConnected: true })), + addEventListener: jest.fn(), +})); + +// Mock react-native-sound +jest.mock('react-native-sound', () => ({ + setCategory: jest.fn(), +})); + +// Mock react-native-vibration +jest.mock('react-native-vibration', () => ({ + Vibration: { + vibrate: jest.fn(), + }, +})); + +// Mock react-native-haptic-feedback +jest.mock('react-native-haptic-feedback', () => ({ + trigger: jest.fn(), +})); + +// Mock react-native-device-info +jest.mock('react-native-device-info', () => ({ + getVersion: jest.fn(() => '1.0.0'), + getBuildNumber: jest.fn(() => '1'), + getDeviceId: jest.fn(() => 'test-device-id'), + getSystemName: jest.fn(() => 'iOS'), + getSystemVersion: jest.fn(() => '15.0'), +})); + +// Mock react-native-keychain +jest.mock('react-native-keychain', () => ({ + setInternetCredentials: jest.fn(), + getInternetCredentials: jest.fn(), + resetInternetCredentials: jest.fn(), +})); + +// Mock react-native-encrypted-storage +jest.mock('react-native-encrypted-storage', () => ({ + setItem: jest.fn(), + getItem: jest.fn(), + removeItem: jest.fn(), + clear: jest.fn(), +})); + +// Mock react-native-sensitive-info +jest.mock('react-native-sensitive-info', () => ({ + setItem: jest.fn(), + getItem: jest.fn(), + deleteItem: jest.fn(), + getAllItems: jest.fn(), +})); + +// Mock react-native-push-notification +jest.mock('react-native-push-notification', () => ({ + configure: jest.fn(), + onRegister: jest.fn(), + onNotification: jest.fn(), + requestPermissions: jest.fn(), + getScheduledLocalNotifications: jest.fn(), + scheduleLocalNotification: jest.fn(), + cancelLocalNotification: jest.fn(), + cancelAllLocalNotifications: jest.fn(), +})); + +// Mock react-native-background-actions +jest.mock('react-native-background-actions', () => ({ + start: jest.fn(), + stop: jest.fn(), + isRunning: jest.fn(), +})); + +// Mock react-native-background-downloader +jest.mock('react-native-background-downloader', () => ({ + download: jest.fn(), + pause: jest.fn(), + resume: jest.fn(), + stop: jest.fn(), + checkForExistingDownloads: jest.fn(), +})); + +// Mock react-native-background-upload +jest.mock('react-native-background-upload', () => ({ + startUpload: jest.fn(), + addListener: jest.fn(), + removeListener: jest.fn(), +})); + +// Mock react-native-background-geolocation +jest.mock('react-native-background-geolocation', () => ({ + configure: jest.fn(), + start: jest.fn(), + stop: jest.fn(), + getCurrentPosition: jest.fn(), + watchPosition: jest.fn(), + clearWatch: jest.fn(), +})); + +// Mock react-native-background-fetch +jest.mock('react-native-background-fetch', () => ({ + configure: jest.fn(), + start: jest.fn(), + stop: jest.fn(), + status: jest.fn(), +})); + +// Mock react-native-background-timer +jest.mock('react-native-background-timer', () => ({ + start: jest.fn(), + stop: jest.fn(), + setTimeout: jest.fn(), + clearTimeout: jest.fn(), + setInterval: jest.fn(), + clearInterval: jest.fn(), +})); + +// Mock react-native-background-job +jest.mock('react-native-background-job', () => ({ + register: jest.fn(), + schedule: jest.fn(), + cancel: jest.fn(), + cancelAll: jest.fn(), +})); + +// Mock react-native-background-actions +jest.mock('react-native-background-actions', () => ({ + start: jest.fn(), + stop: jest.fn(), + isRunning: jest.fn(), +})); + +// Mock react-native-background-downloader +jest.mock('react-native-background-downloader', () => ({ + download: jest.fn(), + pause: jest.fn(), + resume: jest.fn(), + stop: jest.fn(), + checkForExistingDownloads: jest.fn(), +})); + +// Mock react-native-background-upload +jest.mock('react-native-background-upload', () => ({ + startUpload: jest.fn(), + addListener: jest.fn(), + removeListener: jest.fn(), +})); + +// Mock react-native-background-geolocation +jest.mock('react-native-background-geolocation', () => ({ + configure: jest.fn(), + start: jest.fn(), + stop: jest.fn(), + getCurrentPosition: jest.fn(), + watchPosition: jest.fn(), + clearWatch: jest.fn(), +})); + +// Mock react-native-background-fetch +jest.mock('react-native-background-fetch', () => ({ + configure: jest.fn(), + start: jest.fn(), + stop: jest.fn(), + status: jest.fn(), +})); + +// Mock react-native-background-timer +jest.mock('react-native-background-timer', () => ({ + start: jest.fn(), + stop: jest.fn(), + setTimeout: jest.fn(), + clearTimeout: jest.fn(), + setInterval: jest.fn(), + clearInterval: jest.fn(), +})); + +// Mock react-native-background-job +jest.mock('react-native-background-job', () => ({ + register: jest.fn(), + schedule: jest.fn(), + cancel: jest.fn(), + cancelAll: jest.fn(), +})); + +// Global test setup +global.console = { + ...console, + // Uncomment to ignore a specific log level + // log: jest.fn(), + // debug: jest.fn(), + // info: jest.fn(), + // warn: jest.fn(), + // error: jest.fn(), +}; \ No newline at end of file diff --git a/scripts/security-audit.sh b/scripts/security-audit.sh new file mode 100755 index 0000000..4cc2b74 --- /dev/null +++ b/scripts/security-audit.sh @@ -0,0 +1,428 @@ +#!/bin/bash + +# ============================================================================= +# ModMaster Pro Security Audit Script +# ============================================================================= +# This script performs comprehensive security checks on the codebase + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Logging functions +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Check if required tools are installed +check_dependencies() { + log_info "Checking dependencies..." + + local missing_tools=() + + # Check for npm + if ! command -v npm &> /dev/null; then + missing_tools+=("npm") + fi + + # Check for pip + if ! command -v pip &> /dev/null; then + missing_tools+=("pip") + fi + + # Check for bandit (Python security linter) + if ! command -v bandit &> /dev/null; then + missing_tools+=("bandit") + fi + + # Check for safety (Python dependency checker) + if ! command -v safety &> /dev/null; then + missing_tools+=("safety") + fi + + # Check for auditjs (Node.js dependency checker) + if ! command -v auditjs &> /dev/null; then + missing_tools+=("auditjs") + fi + + if [ ${#missing_tools[@]} -ne 0 ]; then + log_warning "Missing tools: ${missing_tools[*]}" + log_info "Installing missing tools..." + + for tool in "${missing_tools[@]}"; do + case $tool in + "bandit") + pip install bandit + ;; + "safety") + pip install safety + ;; + "auditjs") + npm install -g auditjs + ;; + esac + done + else + log_success "All required tools are installed" + fi +} + +# Check for hardcoded secrets and passwords +check_hardcoded_secrets() { + log_info "Checking for hardcoded secrets and passwords..." + + local issues_found=false + + # Patterns to check for + local patterns=( + "password.*=.*['\"][^'\"]*['\"]" + "secret.*=.*['\"][^'\"]*['\"]" + "key.*=.*['\"][^'\"]*['\"]" + "token.*=.*['\"][^'\"]*['\"]" + "api_key.*=.*['\"][^'\"]*['\"]" + "jwt_secret.*=.*['\"][^'\"]*['\"]" + "modmaster_password" + "modmaster123" + "dev_jwt_secret" + "your_super_secret" + ) + + for pattern in "${patterns[@]}"; do + if grep -r --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=__pycache__ --exclude=*.log "$pattern" .; then + log_error "Found potential hardcoded secret: $pattern" + issues_found=true + fi + done + + if [ "$issues_found" = false ]; then + log_success "No hardcoded secrets found" + fi +} + +# Check for SQL injection vulnerabilities +check_sql_injection() { + log_info "Checking for SQL injection vulnerabilities..." + + local issues_found=false + + # Patterns that might indicate SQL injection + local patterns=( + "query.*\+.*req\." + "query.*\+.*params\." + "query.*\+.*body\." + "SELECT.*\+" + "INSERT.*\+" + "UPDATE.*\+" + "DELETE.*\+" + "WHERE.*\+" + ) + + for pattern in "${patterns[@]}"; do + if grep -r --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=__pycache__ --exclude=*.log "$pattern" .; then + log_error "Found potential SQL injection: $pattern" + issues_found=true + fi + done + + if [ "$issues_found" = false ]; then + log_success "No obvious SQL injection vulnerabilities found" + fi +} + +# Check for XSS vulnerabilities +check_xss_vulnerabilities() { + log_info "Checking for XSS vulnerabilities..." + + local issues_found=false + + # Patterns that might indicate XSS + local patterns=( + "innerHTML.*\+" + "document\.write.*\+" + "eval\(" + "innerHTML\s*=" + "outerHTML\s*=" + ) + + for pattern in "${patterns[@]}"; do + if grep -r --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=__pycache__ --exclude=*.log "$pattern" .; then + log_error "Found potential XSS vulnerability: $pattern" + issues_found=true + fi + done + + if [ "$issues_found" = false ]; then + log_success "No obvious XSS vulnerabilities found" + fi +} + +# Check for missing security headers +check_security_headers() { + log_info "Checking for security headers..." + + local issues_found=false + + # Check if Helmet is used + if ! grep -r "helmet" backend/ --exclude-dir=node_modules; then + log_warning "Helmet security middleware not found in backend" + issues_found=true + fi + + # Check if CORS is configured + if ! grep -r "cors" backend/ --exclude-dir=node_modules; then + log_warning "CORS configuration not found in backend" + issues_found=true + fi + + if [ "$issues_found" = false ]; then + log_success "Security headers appear to be configured" + fi +} + +# Check for outdated dependencies +check_dependencies_security() { + log_info "Checking for security vulnerabilities in dependencies..." + + # Check Node.js dependencies + if [ -f "backend/api/package.json" ]; then + log_info "Checking Node.js dependencies..." + cd backend/api + if npm audit --audit-level=moderate; then + log_success "No critical Node.js vulnerabilities found" + else + log_error "Node.js vulnerabilities found - run 'npm audit fix'" + fi + cd ../.. + fi + + # Check Python dependencies + if [ -f "requirements.txt" ]; then + log_info "Checking Python dependencies..." + if safety check -r requirements.txt; then + log_success "No critical Python vulnerabilities found" + else + log_error "Python vulnerabilities found - update requirements.txt" + fi + fi + + if [ -f "ai-service/requirements.txt" ]; then + log_info "Checking AI service Python dependencies..." + if safety check -r ai-service/requirements.txt; then + log_success "No critical AI service vulnerabilities found" + else + log_error "AI service vulnerabilities found - update requirements.txt" + fi + fi +} + +# Check for proper authentication +check_authentication() { + log_info "Checking authentication implementation..." + + local issues_found=false + + # Check if JWT is used + if ! grep -r "jsonwebtoken\|jwt" backend/ --exclude-dir=node_modules; then + log_warning "JWT authentication not found" + issues_found=true + fi + + # Check if bcrypt is used for password hashing + if ! grep -r "bcrypt" backend/ --exclude-dir=node_modules; then + log_warning "bcrypt password hashing not found" + issues_found=true + fi + + # Check if password validation exists + if ! grep -r "password.*validation\|password.*requirements" backend/ --exclude-dir=node_modules; then + log_warning "Password validation not found" + issues_found=true + fi + + if [ "$issues_found" = false ]; then + log_success "Authentication appears to be properly implemented" + fi +} + +# Check for proper input validation +check_input_validation() { + log_info "Checking input validation..." + + local issues_found=false + + # Check if validation middleware exists + if ! grep -r "validation\|joi\|express-validator" backend/ --exclude-dir=node_modules; then + log_warning "Input validation middleware not found" + issues_found=true + fi + + # Check if file upload validation exists + if ! grep -r "multer\|file.*validation" backend/ --exclude-dir=node_modules; then + log_warning "File upload validation not found" + issues_found=true + fi + + if [ "$issues_found" = false ]; then + log_success "Input validation appears to be implemented" + fi +} + +# Check for proper error handling +check_error_handling() { + log_info "Checking error handling..." + + local issues_found=false + + # Check if error handling middleware exists + if ! grep -r "error.*handler\|errorHandler" backend/ --exclude-dir=node_modules; then + log_warning "Error handling middleware not found" + issues_found=true + fi + + # Check if try-catch blocks are used + if ! grep -r "try.*catch\|async.*await" backend/ --exclude-dir=node_modules; then + log_warning "Async error handling not found" + issues_found=true + fi + + if [ "$issues_found" = false ]; then + log_success "Error handling appears to be implemented" + fi +} + +# Check for proper logging +check_logging() { + log_info "Checking logging implementation..." + + local issues_found=false + + # Check if logging is configured + if ! grep -r "winston\|morgan\|logger" backend/ --exclude-dir=node_modules; then + log_warning "Logging not configured" + issues_found=true + fi + + # Check if sensitive data is being logged + if grep -r "password.*log\|secret.*log\|token.*log" backend/ --exclude-dir=node_modules; then + log_error "Sensitive data might be logged" + issues_found=true + fi + + if [ "$issues_found" = false ]; then + log_success "Logging appears to be properly configured" + fi +} + +# Check for environment variable usage +check_environment_variables() { + log_info "Checking environment variable usage..." + + local issues_found=false + + # Check if .env files are in .gitignore + if ! grep -q "\.env" .gitignore; then + log_error ".env files not in .gitignore" + issues_found=true + fi + + # Check if sensitive config uses environment variables + if grep -r "password.*=.*['\"][^'\"]*['\"]" backend/api/src/config/ --exclude-dir=node_modules; then + log_error "Hardcoded passwords in config files" + issues_found=true + fi + + if [ "$issues_found" = false ]; then + log_success "Environment variables appear to be properly used" + fi +} + +# Run Python security checks +run_python_security_checks() { + log_info "Running Python security checks..." + + # Run bandit on Python files + if command -v bandit &> /dev/null; then + log_info "Running bandit security linter..." + if find . -name "*.py" -not -path "./node_modules/*" -not -path "./.git/*" | xargs bandit -r -f json -o bandit-report.json; then + log_success "Bandit security check completed" + else + log_error "Bandit found security issues" + fi + fi +} + +# Generate security report +generate_report() { + log_info "Generating security report..." + + local report_file="security-audit-report-$(date +%Y%m%d-%H%M%S).txt" + + { + echo "ModMaster Pro Security Audit Report" + echo "Generated: $(date)" + echo "==================================" + echo "" + echo "Summary:" + echo "- Hardcoded secrets: $(grep -c "hardcoded secret" /tmp/security-audit.log 2>/dev/null || echo "0")" + echo "- SQL injection vulnerabilities: $(grep -c "SQL injection" /tmp/security-audit.log 2>/dev/null || echo "0")" + echo "- XSS vulnerabilities: $(grep -c "XSS" /tmp/security-audit.log 2>/dev/null || echo "0")" + echo "" + echo "Recommendations:" + echo "1. Use environment variables for all secrets" + echo "2. Implement proper input validation" + echo "3. Use parameterized queries" + echo "4. Enable security headers" + echo "5. Regular dependency updates" + echo "6. Implement proper error handling" + echo "7. Use secure authentication" + echo "8. Enable comprehensive logging" + } > "$report_file" + + log_success "Security report generated: $report_file" +} + +# Main function +main() { + log_info "Starting ModMaster Pro Security Audit..." + + # Create temporary log file + exec 2> >(tee /tmp/security-audit.log) + + check_dependencies + check_hardcoded_secrets + check_sql_injection + check_xss_vulnerabilities + check_security_headers + check_dependencies_security + check_authentication + check_input_validation + check_error_handling + check_logging + check_environment_variables + run_python_security_checks + + log_info "Security audit completed" + generate_report + + log_info "Check /tmp/security-audit.log for detailed results" +} + +# Run main function +main "$@" \ No newline at end of file From 7fbb62f42584eeb4388ac9a5ba5ff8cd503115c2 Mon Sep 17 00:00:00 2001 From: Tony Condone Date: Wed, 27 Aug 2025 07:36:49 +0000 Subject: [PATCH 2/4] Fix Git configuration in workflow --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7d285be..430eb65 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -184,8 +184,8 @@ jobs: - name: Commit Progress Updates run: | - git config --local user.email "github-actions[bot]@users.noreply.github.com" - git config --local user.name "github-actions[bot]" + git config --local touyboateng339@gmail.com "github-actions[bot]@users.noreply.github.com" + git config --local tonycondone "github-actions[bot]" git add PROGRESS_REPORT.md From f6a86dacc6d9ccda73e00648d81dedbd8c5ffe3d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 27 Aug 2025 07:48:42 +0000 Subject: [PATCH 3/4] Fix git config command syntax for GitHub Actions bot Co-authored-by: tonykflex --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 430eb65..7d285be 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -184,8 +184,8 @@ jobs: - name: Commit Progress Updates run: | - git config --local touyboateng339@gmail.com "github-actions[bot]@users.noreply.github.com" - git config --local tonycondone "github-actions[bot]" + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" git add PROGRESS_REPORT.md From 3d7d91eae9d03e702b330d3f93504d9e71a96b5d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 27 Aug 2025 09:03:02 +0000 Subject: [PATCH 4/4] Update GitHub Actions workflow to support branch-specific progress updates Co-authored-by: tonykflex --- .github/workflows/main.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7d285be..0b1e73a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,6 +29,7 @@ jobs: with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.head_ref || github.ref }} - name: Setup Node.js uses: actions/setup-node@v4 @@ -187,11 +188,17 @@ jobs: git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" + # Get the current branch name + BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) + echo "Current branch: $BRANCH_NAME" + git add PROGRESS_REPORT.md if ! git diff --staged --quiet; then git commit -m "🤖 Automated progress update - $(date '+%Y-%m-%d %H:%M')" - git push origin main + + # Push to the current branch + git push origin HEAD:${{ github.head_ref || github.ref_name }} else echo "No changes to commit" fi