diff --git a/SECURITY_GUIDE.md b/SECURITY_GUIDE.md new file mode 100644 index 0000000..b2f049f --- /dev/null +++ b/SECURITY_GUIDE.md @@ -0,0 +1,236 @@ +# Security Implementation Guide + +This document outlines the security measures implemented in Venturalink and provides guidance for maintaining security best practices. + +## 🔒 Security Features Implemented + +### 1. Helmet Security Headers + +Helmet provides protection against common web vulnerabilities by setting HTTP headers: + +- **Content-Security-Policy (CSP)**: Prevents XSS attacks by controlling resource loading +- **Strict-Transport-Security (HSTS)**: Forces HTTPS connections +- **X-Frame-Options**: Prevents clickjacking attacks +- **X-Content-Type-Options**: Prevents MIME-type sniffing +- **X-XSS-Protection**: Additional XSS protection layer + +### 2. Rate Limiting + +Protection against abuse and DoS attacks: + +- **General API**: 100 requests per 15 minutes per IP +- **Chatbot API**: 20 requests per 15 minutes per IP +- Automatic retry-after headers +- Configurable limits per endpoint + +### 3. Input Validation & Sanitization + +Using express-validator for robust input handling: + +- Message length validation (1-1000 characters) +- HTML/special character sanitization +- Type checking and format validation +- Comprehensive error messages + +### 4. Error Handling + +- Global error handler +- Environment-aware error messages (detailed in dev, generic in production) +- Proper HTTP status codes +- Request logging for debugging + +## 🚀 Installation + +Install security dependencies: + +```bash +npm install helmet express-rate-limit express-validator +``` + +## 📝 Configuration + +### Environment Variables + +Create a `.env` file with the following variables: + +```env +# Server Configuration +PORT=8080 +NODE_ENV=production + +# API Keys (NEVER commit these!) +API_KEY=your_gemini_api_key_here + +# Firebase Configuration (Use environment variables!) +VITE_FIREBASE_API_KEY=your_firebase_api_key +VITE_FIREBASE_AUTH_DOMAIN=your_auth_domain +VITE_FIREBASE_PROJECT_ID=your_project_id +VITE_FIREBASE_STORAGE_BUCKET=your_storage_bucket +VITE_FIREBASE_MESSAGING_SENDER_ID=your_sender_id +VITE_FIREBASE_APP_ID=your_app_id +VITE_FIREBASE_MEASUREMENT_ID=your_measurement_id +``` + +### Rate Limit Configuration + +Adjust rate limits in `server.js` based on your needs: + +```javascript +const apiLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // Time window + max: 100, // Max requests per window + message: "Custom error message" +}); +``` + +### CSP Configuration + +Modify Content-Security-Policy directives in `server.js`: + +```javascript +contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + scriptSrc: ["'self'", "trusted-cdn.com"], + // Add more directives as needed + } +} +``` + +## 🔐 Security Best Practices + +### 1. API Key Management + +**❌ NEVER DO THIS:** +```javascript +const apiKey = "AIzaSyA37bruIT_neT5w-8CUuPGofy0Lnv2UJOg"; // Hardcoded! +``` + +**✅ DO THIS:** +```javascript +const apiKey = process.env.API_KEY; // From environment +``` + +### 2. Firebase Configuration + +**❌ NEVER commit firebase-config.js with real keys!** + +**✅ Use environment variables:** +```javascript +const firebaseConfig = { + apiKey: import.meta.env.VITE_FIREBASE_API_KEY, + authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, + // ... other config +}; +``` + +### 3. Error Handling + +**❌ Don't expose internal errors:** +```javascript +res.status(500).json({ error: error.stack }); // Exposes internals! +``` + +**✅ Use environment-aware messages:** +```javascript +res.status(500).json({ + error: process.env.NODE_ENV === 'production' + ? "Something went wrong" + : error.message +}); +``` + +### 4. Input Validation + +**❌ Trust user input:** +```javascript +const message = req.body.message; // No validation! +``` + +**✅ Validate and sanitize:** +```javascript +body('message') + .trim() + .notEmpty() + .isLength({ min: 1, max: 1000 }) + .escape() +``` + +## 🛡️ Security Checklist + +Before deploying to production: + +- [ ] All API keys moved to environment variables +- [ ] `.env` file added to `.gitignore` +- [ ] Firebase security rules configured +- [ ] Rate limiting enabled and tested +- [ ] HTTPS enforced (HSTS enabled) +- [ ] CSP configured for your domains +- [ ] Error messages don't expose internals +- [ ] Input validation on all endpoints +- [ ] Dependencies updated (`npm audit`) +- [ ] Security headers verified (use securityheaders.com) + +## 🔍 Testing Security + +### Test Rate Limiting + +```bash +# Send multiple requests quickly +for i in {1..25}; do + curl -X POST http://localhost:8080/api/chat \ + -H "Content-Type: application/json" \ + -d '{"message":"test"}' & +done +``` + +Expected: After 20 requests, you should get a 429 (Too Many Requests) error. + +### Test Input Validation + +```bash +# Test empty message +curl -X POST http://localhost:8080/api/chat \ + -H "Content-Type: application/json" \ + -d '{"message":""}' + +# Test too long message +curl -X POST http://localhost:8080/api/chat \ + -H "Content-Type: application/json" \ + -d '{"message":"'$(python3 -c 'print("a"*1001)')'"}' +``` + +Expected: Both should return 400 (Bad Request) with validation errors. + +### Test Security Headers + +```bash +curl -I https://your-domain.com +``` + +Look for these headers: +- `Strict-Transport-Security` +- `X-Frame-Options` +- `X-Content-Type-Options` +- `Content-Security-Policy` + +## 📚 Additional Resources + +- [OWASP Top 10](https://owasp.org/www-project-top-ten/) +- [Helmet.js Documentation](https://helmetjs.github.io/) +- [Express Security Best Practices](https://expressjs.com/en/advanced/best-practice-security.html) +- [Firebase Security Rules](https://firebase.google.com/docs/rules) +- [Node.js Security Checklist](https://blog.risingstack.com/node-js-security-checklist/) + +## 🚨 Reporting Security Issues + +If you discover a security vulnerability, please email security@venturalink.com instead of opening a public issue. + +## 📄 License + +This security implementation follows the same license as the main project. + +--- + +**Last Updated**: January 2026 +**Maintained By**: Venturalink Security Team diff --git a/package.json b/package.json index 5535dc7..f155f18 100644 --- a/package.json +++ b/package.json @@ -6,24 +6,36 @@ "scripts": { "start": "node server.js", "generate-env": "node generate-env.js", - "build": "npm run generate-env" + "build": "npm run generate-env", + "dev": "node server.js", + "audit": "npm audit", + "audit-fix": "npm audit fix" }, "repository": { "type": "git", - "url": "git+https://github.com/krishagandhi0711/Venturalink.git" + "url": "git+https://github.com/eccentriccoder01/Venturalink.git" }, - "keywords": [], + "keywords": [ + "venturalink", + "startup", + "investment", + "entrepreneur", + "investor" + ], "author": "", "license": "ISC", "type": "commonjs", "bugs": { - "url": "https://github.com/krishagandhi0711/Venturalink/issues" + "url": "https://github.com/eccentriccoder01/Venturalink/issues" }, - "homepage": "https://github.com/krishagandhi0711/Venturalink#readme", + "homepage": "https://github.com/eccentriccoder01/Venturalink#readme", "dependencies": { "express": "^5.1.0", "dotenv": "^16.4.5", "cors": "^2.8.5", - "@google/generative-ai": "^0.21.0" + "@google/generative-ai": "^0.21.0", + "helmet": "^8.0.0", + "express-rate-limit": "^7.4.1", + "express-validator": "^7.2.0" } } diff --git a/server.js b/server.js index 8fc3f64..645dfb9 100644 --- a/server.js +++ b/server.js @@ -3,13 +3,79 @@ require('dotenv').config(); const express = require("express"); const path = require("path"); const cors = require("cors"); +const helmet = require("helmet"); +const rateLimit = require("express-rate-limit"); +const { body, validationResult } = require("express-validator"); const { GoogleGenerativeAI } = require("@google/generative-ai"); const app = express(); const PORT = process.env.PORT || 8080; -app.use(express.json()); + +// ===== SECURITY MIDDLEWARE ===== + +// Helmet - Security headers +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://www.gstatic.com", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:", "blob:"], + connectSrc: [ + "'self'", + "https://firestore.googleapis.com", + "https://identitytoolkit.googleapis.com", + "https://securetoken.googleapis.com", + "https://www.googleapis.com" + ], + frameSrc: ["'none'"], + objectSrc: ["'none'"], + upgradeInsecureRequests: [] + } + }, + hsts: { + maxAge: 31536000, + includeSubDomains: true, + preload: true + }, + frameguard: { + action: 'deny' + }, + noSniff: true, + xssFilter: true +})); + +// Rate limiting for API endpoints +const apiLimiter = 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.", + retryAfter: "15 minutes" + }, + standardHeaders: true, + legacyHeaders: false, +}); + +// Stricter rate limiting for chatbot +const chatLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 20, // Limit each IP to 20 chat requests per windowMs + message: { + error: "Too many chat requests, please try again later.", + retryAfter: "15 minutes" + }, + standardHeaders: true, + legacyHeaders: false, +}); + +// ===== BASIC MIDDLEWARE ===== + +app.use(express.json({ limit: '10mb' })); app.use(express.static(__dirname)); +// CORS configuration app.use( cors({ origin: [ @@ -19,9 +85,19 @@ app.use( ], methods: ["GET", "POST"], allowedHeaders: ["Content-Type", "Authorization"], + credentials: true }) ); +// Request logging middleware +app.use((req, res, next) => { + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] ${req.method} ${req.path} - IP: ${req.ip}`); + next(); +}); + +// ===== GOOGLE GEMINI AI INITIALIZATION ===== + let genAI, model; try { const apiKey = process.env.API_KEY; @@ -35,6 +111,10 @@ try { } catch (error) { console.error("❌ Failed to initialize Google Gemini AI:", error.message); } + +// ===== ROUTES ===== + +// Static page routes app.get("/", (req, res) => res.sendFile(path.join(__dirname, "index.html"))); app.get("/login", (req, res) => res.sendFile(path.join(__dirname, "login.html"))); app.get("/register", (req, res) => res.sendFile(path.join(__dirname, "register.html"))); @@ -43,61 +123,121 @@ app.get("/contact", (req, res) => res.sendFile(path.join(__dirname, "contact.htm app.get("/features", (req, res) => res.sendFile(path.join(__dirname, "features.html"))); app.get("/pricing", (req, res) => res.sendFile(path.join(__dirname, "pricing.html"))); -// ✅ Chatbot API Routes -app.get("/api/chat/health", (req, res) => { +// ===== API ROUTES ===== + +// Health check endpoint +app.get("/api/chat/health", apiLimiter, (req, res) => { res.json({ status: "healthy", chatbot: model ? "enabled" : "disabled", - message: "Venturalink Chatbot API is running!" + message: "Venturalink Chatbot API is running!", + timestamp: new Date().toISOString() }); }); -app.post("/api/chat", async (req, res) => { - try { - if (!model) { - return res.status(503).json({ - error: "Chatbot service unavailable. API key not configured." - }); - } - - const { message } = req.body; - if (!message || typeof message !== 'string' || message.trim().length === 0) { - return res.status(400).json({ error: "Message is required and must be a non-empty string" }); - } - - const contextualPrompt = `You are Venturalink's AI assistant. Venturalink is a platform that connects entrepreneurs, investors, and advisors for startup ecosystem collaboration. +// Chatbot endpoint with validation and rate limiting +app.post( + "/api/chat", + chatLimiter, + [ + body('message') + .trim() + .notEmpty().withMessage('Message is required') + .isLength({ min: 1, max: 1000 }).withMessage('Message must be between 1 and 1000 characters') + .escape() // Sanitize HTML/special characters + ], + async (req, res) => { + try { + // Check validation errors + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + error: "Validation failed", + details: errors.array().map(err => ({ + field: err.path, + message: err.msg + })) + }); + } + + // Check if chatbot is available + if (!model) { + return res.status(503).json({ + error: "Chatbot service unavailable", + message: "API key not configured. Please contact support." + }); + } + + const { message } = req.body; + + // Create contextual prompt + const contextualPrompt = `You are Venturalink's AI assistant. Venturalink is a platform that connects entrepreneurs, investors, and advisors for startup ecosystem collaboration. User question: ${message} Please provide a helpful response related to entrepreneurship, startup funding, business development, or general business advice. If the question is not business-related, still try to be helpful while gently steering toward business topics when appropriate.`; - const result = await model.generateContent(contextualPrompt); - - const reply = result.response?.candidates?.[0]?.content?.parts?.[0]?.text || - "I couldn't generate a response at the moment. Please try again."; - - res.json({ - reply, - timestamp: new Date().toISOString(), - model: "gemini-2.0-flash" - }); - } catch (error) { - console.error("❌ Chatbot Error:", error); - res.status(500).json({ - error: "Internal Server Error", - message: "Failed to process your request. Please try again later." - }); + // Generate AI response + const result = await model.generateContent(contextualPrompt); + + const reply = result.response?.candidates?.[0]?.content?.parts?.[0]?.text || + "I couldn't generate a response at the moment. Please try again."; + + res.json({ + reply, + timestamp: new Date().toISOString(), + model: "gemini-2.0-flash" + }); + + } catch (error) { + console.error("❌ Chatbot Error:", error); + + // Don't expose internal error details in production + const errorMessage = process.env.NODE_ENV === 'production' + ? "Failed to process your request. Please try again later." + : error.message; + + res.status(500).json({ + error: "Internal Server Error", + message: errorMessage + }); + } } +); + +// ===== ERROR HANDLING ===== + +// 404 handler +app.use((req, res) => { + res.status(404).json({ + error: "Not Found", + message: "The requested resource was not found", + path: req.path + }); }); -app.use((req, res) => res.status(404).send("Page not found")); +// Global error handler +app.use((err, req, res, next) => { + console.error("❌ Unhandled Error:", err); + + res.status(err.status || 500).json({ + error: "Internal Server Error", + message: process.env.NODE_ENV === 'production' + ? "Something went wrong" + : err.message + }); +}); + +// ===== SERVER STARTUP ===== if (process.env.NODE_ENV !== 'production') { app.listen(PORT, () => { console.log(`🚀 Venturalink server running at http://localhost:${PORT}`); console.log(`🤖 Chatbot API available at http://localhost:${PORT}/api/chat`); + console.log(`🔒 Security headers enabled`); + console.log(`⏱️ Rate limiting active`); }); } else { - + // Export for serverless deployment (Vercel) module.exports = app; }