# SECURITY AUDIT REPORT - ADOPT-A-STREET APPLICATION **Audit Date:** October 31, 2025 **Audited By:** Claude Code Security Auditor **Application:** Adopt-a-Street (React Frontend + Express/MongoDB Backend) **Audit Scope:** Backend API, Frontend Security, Infrastructure, API Security, Data Protection --- ## EXECUTIVE SUMMARY This comprehensive security audit reveals a **moderately secure application** with several critical vulnerabilities that require immediate attention. The application demonstrates good security practices in some areas (password hashing, JWT authentication, input validation) but has significant gaps in access control, dependency management, and API security. **Overall Security Rating:** 6/10 (Medium Risk) ### Key Findings: - 3 CRITICAL vulnerabilities requiring immediate remediation - 8 HIGH priority issues requiring urgent attention - 12 MEDIUM priority recommendations - 5 LOW priority improvements --- ## CRITICAL VULNERABILITIES (FIX IMMEDIATELY) ### 1. CRITICAL: Broken Access Control - User Profile IDOR Vulnerability **Location:** `/home/will/Code/adopt-a-street/backend/routes/users.js:10-21` **Issue:** ```javascript // GET /api/users/:id - Any authenticated user can view ANY user profile router.get("/:id", auth, async (req, res) => { const user = await User.findById(req.params.id).populate("adoptedStreets"); if (!user) { return res.status(404).json({ msg: "User not found" }); } res.json(user); }); ``` **Vulnerability:** Insecure Direct Object Reference (IDOR). Any authenticated user can access any other user's complete profile data by changing the ID parameter. The endpoint does NOT verify that `req.params.id === req.user.id`. **Impact:** Information disclosure, privacy violation, potential data harvesting **Remediation:** - Add authorization check to verify requesting user matches the profile being accessed, OR - Implement role-based access control if admin users need broader access, OR - Return only public profile information for other users' profiles **CVSS Score:** 7.5 (High) - OWASP A01:2021 – Broken Access Control --- ### 2. CRITICAL: Known Vulnerability in Axios Dependency **Location:** Both `/home/will/Code/adopt-a-street/backend/package.json` and `/home/will/Code/adopt-a-street/frontend/package.json` **Issue:** ```json "axios": "^1.8.3" ``` **Vulnerability:** CVE-2024-XXXX - Axios DoS through lack of data size check - Advisory: GHSA-4hjh-wcwx-xvwj - CVSS: 7.5 (High) - CWE-770: Allocation of Resources Without Limits - Affected versions: 1.0.0 - 1.11.0 **Impact:** Denial of Service attack through unlimited data size processing **Remediation:** ```bash # Backend cd backend && bun update axios # Frontend cd frontend && bun update axios ``` **Fix Available:** Yes - Update to axios >= 1.12.0 --- ### 3. CRITICAL: Missing Authorization on Reports Management **Location:** `/home/will/Code/adopt-a-street/backend/routes/reports.js:40-56` **Issue:** ```javascript // PUT /api/reports/:id - Mark report as resolved router.put("/:id", auth, async (req, res) => { const report = await Report.findById(req.params.id); if (!report) { return res.status(404).json({ msg: "Report not found" }); } report.status = "resolved"; await report.save(); res.json(report); }); ``` **Vulnerability:** ANY authenticated user can mark ANY report as resolved. No authorization check verifies the user is an admin or the report creator. **Impact:** - Unauthorized report manipulation - Circumvention of reporting workflow - Data integrity violation **Remediation:** - Implement role-based access control for report management - Only allow admins or street adopters to resolve reports - Add authorization middleware to verify permissions **CVSS Score:** 6.5 (Medium-High) - OWASP A01:2021 – Broken Access Control --- ## HIGH PRIORITY ISSUES (FIX SOON) ### 4. HIGH: Insufficient Input Validation on Multiple Routes **Locations:** - `/home/will/Code/adopt-a-street/backend/routes/tasks.js:25-40` - No validation on task creation - `/home/will/Code/adopt-a-street/backend/routes/events.js:19-36` - No validation on event creation - `/home/will/Code/adopt-a-street/backend/routes/rewards.js:20-37` - No validation on reward creation - `/home/will/Code/adopt-a-street/backend/routes/reports.js:21-37` - No validation on report creation **Issue:** Several POST routes lack input validation middleware despite validators existing in the codebase. **Example (Tasks):** ```javascript router.post("/", auth, async (req, res) => { const { street, description } = req.body; // NO VALIDATION HERE - accepts any data const newTask = new Task({ street, description }); const task = await newTask.save(); res.json(task); }); ``` **Impact:** - XSS attacks through unsanitized content - NoSQL injection attempts - Database integrity issues - Resource exhaustion (overly long descriptions) **Remediation:** Create validation middleware for each route: ```javascript // tasks.js const { createTaskValidation } = require("../middleware/validators/taskValidator"); router.post("/", auth, createTaskValidation, asyncHandler(async (req, res) => { // ... existing code })); ``` --- ### 5. HIGH: Missing MongoDB ObjectId Validation **Locations:** Multiple routes using `req.params.id` without validation **Issue:** Routes like `/api/posts/:id`, `/api/tasks/:id`, `/api/reports/:id` don't validate that the ID parameter is a valid MongoDB ObjectId before querying. **Affected Files:** - `/home/will/Code/adopt-a-street/backend/routes/posts.js:74-97` (like route) - `/home/will/Code/adopt-a-street/backend/routes/events.js:39-64` (RSVP route) - `/home/will/Code/adopt-a-street/backend/routes/rewards.js:40-68` (redeem route) **Impact:** - MongoDB query errors expose stack traces - Information leakage through error messages - Poor user experience **Remediation:** Add param validation to all ID-based routes: ```javascript const { param } = require('express-validator'); const idValidation = [ param('id').isMongoId().withMessage('Invalid ID format'), validate ]; router.put("/like/:id", auth, idValidation, asyncHandler(async (req, res) => { // ... existing code })); ``` --- ### 6. HIGH: XSS Risk in Frontend - No Content Sanitization **Location:** `/home/will/Code/adopt-a-street/frontend/src/components/SocialFeed.js:266` **Issue:** ```javascript
{post.content}
``` User-generated content is rendered directly without sanitization. While React escapes by default, this should be explicitly handled. **Impact:** - Stored XSS if content contains malicious HTML entities - Social engineering attacks - Session hijacking **Remediation:** Install and use DOMPurify for content sanitization: ```bash bun install dompurify ``` ```javascript import DOMPurify from 'dompurify'; ``` **Note:** React's default escaping provides basic protection, but explicit sanitization is recommended for user-generated content. --- ### 7. HIGH: JWT Token in localStorage (XSS Risk) **Location:** `/home/will/Code/adopt-a-street/frontend/src/context/AuthContext.js:55,93,125` **Issue:** ```javascript localStorage.setItem("token", res.data.token); ``` JWT tokens stored in localStorage are vulnerable to XSS attacks. Any XSS vulnerability in the application can steal authentication tokens. **Impact:** - Session hijacking via XSS - Long-term token theft (7-day expiration) - Account takeover **Remediation:** Use httpOnly cookies instead of localStorage: **Backend (auth route):** ```javascript res.cookie('token', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days }); ``` **Frontend:** Remove localStorage usage and rely on automatic cookie inclusion in requests. Implement CSRF protection. --- ### 8. HIGH: Missing Rate Limiting on Critical Routes **Location:** `/home/will/Code/adopt-a-street/backend/server.js:38-59` **Issue:** Rate limiting is configured but NOT applied to auth routes: ```javascript // Rate limiters are defined but NOT USED on auth routes app.use("/api/auth", authRoutes); // Should be: app.use("/api/auth", authLimiter, authRoutes); ``` **Impact:** - Brute force attacks on login - Account enumeration - Resource exhaustion **Remediation:** ```javascript // Apply rate limiting to auth routes app.use("/api/auth/login", authLimiter, authRoutes); app.use("/api/auth/register", authLimiter, authRoutes); app.use("/api/auth", authRoutes); // For other auth routes // Apply general rate limiting to all API routes app.use("/api/", apiLimiter); ``` --- ### 9. HIGH: Password Hashing - Weak Salt Rounds **Location:** `/home/will/Code/adopt-a-street/backend/routes/auth.js:42` **Issue:** ```javascript const salt = await bcrypt.genSalt(10); ``` Using 10 rounds is the minimum recommended. Modern best practice is 12-14 rounds. **Impact:** - Faster brute force attacks on stolen password hashes - Reduced time to crack passwords **Remediation:** ```javascript const salt = await bcrypt.genSalt(12); // Increase to 12 or 14 rounds ``` **Note:** Higher rounds increase CPU usage. Test performance impact. --- ### 10. HIGH: Missing CSRF Protection **Location:** Application-wide **Issue:** No CSRF protection mechanism implemented. All state-changing operations accept requests without CSRF token validation. **Impact:** - Cross-Site Request Forgery attacks - Unauthorized actions on behalf of authenticated users - Account manipulation **Remediation:** If using cookies (recommended over localStorage): ```bash bun install csurf ``` ```javascript const csrf = require('csurf'); const csrfProtection = csrf({ cookie: true }); app.use(csrfProtection); // Send CSRF token to frontend app.get('/api/csrf-token', (req, res) => { res.json({ csrfToken: req.csrfToken() }); }); ``` **Alternative:** If continuing with JWT in headers (x-auth-token), CSRF risk is lower but still implement SameSite cookie policies. --- ### 11. HIGH: No Content Security Policy (CSP) **Location:** Frontend application-wide **Issue:** No Content-Security-Policy headers configured to prevent XSS attacks. **Impact:** - XSS vulnerability exploitation - Malicious script injection - Data exfiltration **Remediation:** Configure CSP in backend or frontend: **Backend (Helmet):** ```javascript app.use(helmet.contentSecurityPolicy({ directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "'unsafe-inline'"], // Remove unsafe-inline in production styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", "data:", "https:"], connectSrc: ["'self'", "http://localhost:5000", process.env.API_URL], fontSrc: ["'self'"], objectSrc: ["'none'"], upgradeInsecureRequests: [] } })); ``` --- ## MEDIUM PRIORITY RECOMMENDATIONS ### 12. MEDIUM: Error Messages Expose Internal Information **Locations:** Multiple catch blocks throughout backend **Example:** `/home/will/Code/adopt-a-street/backend/routes/tasks.js:96` ```javascript console.error(err.message); res.status(500).send("Server error"); ``` **Issue:** Generic error messages are good, but errors are logged to console exposing stack traces in production. **Remediation:** - Use proper logging library (Winston, Morgan) - Implement error codes instead of generic messages - Never expose stack traces to clients - Log errors to file/monitoring service in production --- ### 13. MEDIUM: Missing Request Size Limits **Location:** `/home/will/Code/adopt-a-street/backend/server.js` **Issue:** No explicit limit on JSON request body size. **Impact:** - DoS through large payloads - Resource exhaustion **Remediation:** ```javascript app.use(express.json({ limit: '10mb' })); // Set appropriate limit ``` --- ### 14. MEDIUM: No Security Logging/Monitoring **Location:** Application-wide **Issue:** No centralized security event logging for: - Failed login attempts - Authorization failures - Suspicious activities - Rate limit violations **Impact:** - Cannot detect ongoing attacks - No audit trail for security incidents - Difficult forensics after breach **Remediation:** Implement security event logging: ```javascript const winston = require('winston'); const securityLogger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [ new winston.transports.File({ filename: 'security.log' }) ] }); // Log security events securityLogger.warn('Failed login attempt', { email: req.body.email, ip: req.ip, timestamp: new Date() }); ``` --- ### 15. MEDIUM: Insufficient Password Requirements **Location:** `/home/will/Code/adopt-a-street/backend/middleware/validators/authValidator.js:37-45` **Issue:** ```javascript body("password") .isLength({ min: 6 }) .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/) ``` Requirements are minimal: - Only 6 characters minimum (should be 8+) - No special character requirement - No maximum length (allows extremely long passwords) **Remediation:** ```javascript body("password") .isLength({ min: 8, max: 128 }) .withMessage("Password must be between 8 and 128 characters") .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/) .withMessage("Password must contain uppercase, lowercase, number, and special character"), ``` --- ### 16. MEDIUM: No Account Lockout Mechanism **Location:** `/home/will/Code/adopt-a-street/backend/routes/auth.js:70-106` **Issue:** No account lockout after multiple failed login attempts. **Impact:** - Brute force attacks possible (even with rate limiting) - Account compromise **Remediation:** Implement account lockout: ```javascript // In User model, add: { loginAttempts: { type: Number, default: 0 }, lockUntil: { type: Date } } // In login route, check and increment attempts if (user.lockUntil && user.lockUntil > Date.now()) { return res.status(423).json({ msg: "Account temporarily locked" }); } // After failed login: user.loginAttempts += 1; if (user.loginAttempts >= 5) { user.lockUntil = Date.now() + (15 * 60 * 1000); // 15 minutes } await user.save(); ``` --- ### 17. MEDIUM: Socket.IO Event Validation Missing **Location:** `/home/will/Code/adopt-a-street/backend/server.js:77-84` **Issue:** ```javascript socket.on("joinEvent", (eventId) => { socket.join(eventId); // No validation of eventId }); ``` No validation that: - eventId is a valid MongoDB ObjectId - User is authorized to join the event - Event exists **Impact:** - Users can join arbitrary rooms - Information disclosure - Resource exhaustion **Remediation:** ```javascript socket.on("joinEvent", async (eventId) => { // Validate eventId format if (!mongoose.Types.ObjectId.isValid(eventId)) { socket.emit('error', { msg: 'Invalid event ID' }); return; } // Verify event exists const event = await Event.findById(eventId); if (!event) { socket.emit('error', { msg: 'Event not found' }); return; } // Verify user is participant if (!event.participants.includes(socket.user.id)) { socket.emit('error', { msg: 'Not authorized to join this event' }); return; } socket.join(eventId); }); ``` --- ### 18. MEDIUM: Mass Assignment Vulnerability in User Model **Location:** `/home/will/Code/adopt-a-street/backend/models/User.js` **Issue:** User model allows direct assignment of fields like `isPremium`, `points`, and array fields without protection. **Impact:** - Users could manipulate points - Users could grant themselves premium status - Array manipulation attacks **Remediation:** In routes, explicitly select allowed fields: ```javascript // Bad const user = await User.findByIdAndUpdate(req.user.id, req.body); // Good const { name, email } = req.body; // Whitelist allowed fields const user = await User.findByIdAndUpdate(req.user.id, { name, email }); ``` --- ### 19. MEDIUM: No Password Change Endpoint with Old Password Verification **Location:** Missing functionality **Issue:** No endpoint to change password with old password verification. **Impact:** - Users cannot change compromised passwords - Account security management gap **Remediation:** Add password change endpoint: ```javascript router.put('/change-password', auth, async (req, res) => { const { currentPassword, newPassword } = req.body; const user = await User.findById(req.user.id); const isMatch = await bcrypt.compare(currentPassword, user.password); if (!isMatch) { return res.status(400).json({ msg: 'Current password is incorrect' }); } const salt = await bcrypt.genSalt(12); user.password = await bcrypt.hash(newPassword, salt); await user.save(); res.json({ msg: 'Password changed successfully' }); }); ``` --- ### 20. MEDIUM: File Upload Size Only Validated in Middleware **Location:** `/home/will/Code/adopt-a-street/backend/middleware/upload.js:32-34` **Issue:** File size limit (5MB) is correct, but should also be validated at application level and by file type. **Remediation:** - Add additional validation in route handlers - Implement virus scanning for uploaded files - Store files with randomized names to prevent overwrites --- ### 21. MEDIUM: No Email Verification on Registration **Location:** `/home/will/Code/adopt-a-street/backend/routes/auth.js:25-67` **Issue:** Users can register with any email address without verification. **Impact:** - Fake account creation - Email address spoofing - Spam potential **Remediation:** Implement email verification: ```javascript // After user creation const verificationToken = crypto.randomBytes(32).toString('hex'); user.verificationToken = verificationToken; user.verified = false; await user.save(); // Send verification email await sendVerificationEmail(user.email, verificationToken); ``` --- ### 22. MEDIUM: Insufficient CORS Configuration **Location:** `/home/will/Code/adopt-a-street/backend/server.js:26-31` **Issue:** ```javascript cors({ origin: process.env.FRONTEND_URL || "http://localhost:3000", credentials: true, }) ``` Allows credentials but no preflight caching configured. **Remediation:** ```javascript cors({ origin: process.env.FRONTEND_URL || "http://localhost:3000", credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'x-auth-token'], maxAge: 86400 // 24 hours preflight cache }) ``` --- ### 23. MEDIUM: Points Manipulation Risk in Gamification **Location:** `/home/will/Code/adopt-a-street/backend/routes/rewards.js:60` **Issue:** ```javascript user.points -= reward.cost; ``` Direct point manipulation without transaction logging or validation. **Impact:** - Point balance manipulation - Integer overflow/underflow - Audit trail gaps **Remediation:** Use the gamification service's deductRewardPoints function which includes transaction tracking. --- ## LOW PRIORITY IMPROVEMENTS ### 24. LOW: MongoDB Connection String in Environment Variable **Location:** `/home/will/Code/adopt-a-street/backend/server.js:64` **Status:** GOOD PRACTICE - Secrets are in .env file (which is gitignored) **Recommendation:** In production, use dedicated secret management: - AWS Secrets Manager - Azure Key Vault - HashiCorp Vault --- ### 25. LOW: JWT Expiration Time **Location:** `/home/will/Code/adopt-a-street/backend/routes/auth.js:57` **Issue:** 7-day JWT expiration is relatively long. **Recommendation:** - Reduce to 1 hour for access tokens - Implement refresh token pattern - Add token revocation list for logout --- ### 26. LOW: No API Versioning **Location:** All routes use `/api/` without version **Recommendation:** ```javascript app.use("/api/v1/auth", authRoutes); ``` Allows for breaking changes in future versions. --- ### 27. LOW: Missing Request ID Tracking **Recommendation:** Add request ID middleware for debugging: ```javascript const { v4: uuidv4 } = require('uuid'); app.use((req, res, next) => { req.id = uuidv4(); res.setHeader('X-Request-Id', req.id); next(); }); ``` --- ### 28. LOW: Database Queries Not Optimized with Projections **Example:** `/home/will/Code/adopt-a-street/backend/routes/posts.js:15-19` **Recommendation:** Use lean() and select() to reduce memory usage: ```javascript const posts = await Post.find() .sort({ createdAt: -1 }) .skip(skip) .limit(limit) .select('user content imageUrl likes createdAt') .populate("user", "name profilePicture") .lean(); // Returns plain objects, not Mongoose documents ``` --- ## COMPLIANCE CHECKLIST ### OWASP Top 10 (2021) Compliance | Risk | Status | Notes | |------|--------|-------| | A01:2021 - Broken Access Control | PARTIAL FAIL | User profile IDOR, reports authorization missing | | A02:2021 - Cryptographic Failures | PASS | Bcrypt for passwords, JWT for sessions | | A03:2021 - Injection | PARTIAL PASS | No SQL injection risk (Mongoose ORM), but input validation gaps | | A04:2021 - Insecure Design | PASS | Good architecture patterns, transaction usage | | A05:2021 - Security Misconfiguration | PARTIAL FAIL | Missing CSP, rate limiting not applied, error handling gaps | | A06:2021 - Vulnerable Components | FAIL | Axios vulnerability (High severity) | | A07:2021 - Auth & Session Management | PARTIAL PASS | JWT properly implemented, but tokens in localStorage | | A08:2021 - Software & Data Integrity | PASS | Good validation, but could improve | | A09:2021 - Security Logging & Monitoring | FAIL | No security event logging | | A10:2021 - Server-Side Request Forgery | N/A | No SSRF attack surface | ### Security Best Practices Compliance - Helmet (Security Headers): PARTIAL (installed but needs CSP configuration) - Rate Limiting: PARTIAL (configured but not applied correctly) - Input Validation: PARTIAL (validators exist but not consistently used) - Error Handling: GOOD (asyncHandler wrapper, centralized error handler) - Password Security: GOOD (bcrypt with salt) - Authentication: GOOD (JWT implementation correct) - Authorization: POOR (missing checks on critical routes) - CORS: GOOD (properly configured) - File Upload Security: GOOD (type and size validation) - Socket.IO Security: GOOD (authentication middleware implemented) --- ## DEPENDENCY AUDIT SUMMARY ### Backend Dependencies (165 prod, 435 dev) - 1 HIGH severity: axios DoS vulnerability - All other dependencies: No known vulnerabilities ### Frontend Dependencies - 1 HIGH severity: axios DoS vulnerability - Multiple LOW severity: brace-expansion (dev dependency, minimal risk) - 1 HIGH severity in @svgr/webpack (dev dependency, no runtime impact) ### Recommendation: ```bash cd backend && bun audit fix cd frontend && bun audit fix ``` --- ## SECURITY TESTING RECOMMENDATIONS ### Manual Testing Required: 1. Test IDOR vulnerability on `/api/users/:id` endpoint 2. Verify rate limiting is actually enforced 3. Test JWT token expiration handling 4. Test file upload with malicious files 5. Test Socket.IO authentication bypass attempts 6. Test MongoDB injection attempts on search queries (if implemented) ### Automated Testing Recommendations: 1. Set up OWASP ZAP or Burp Suite automated scanning 2. Implement security test suite with Jest/Supertest 3. Add pre-commit hook with `bun audit` 4. Set up Snyk or similar for continuous dependency monitoring --- ## REMEDIATION PRIORITY ROADMAP ### Sprint 1 (Immediate - Week 1): 1. Update axios dependency (CRITICAL) 2. Fix user profile IDOR vulnerability (CRITICAL) 3. Add authorization to reports management (CRITICAL) 4. Apply rate limiting to auth routes (HIGH) ### Sprint 2 (Urgent - Week 2): 5. Add input validation to all missing routes (HIGH) 6. Implement MongoDB ObjectId validation (HIGH) 7. Add CSRF protection (HIGH) 8. Increase bcrypt salt rounds (HIGH) ### Sprint 3 (Important - Week 3-4): 9. Migrate tokens from localStorage to httpOnly cookies (HIGH) 10. Implement Content Security Policy (HIGH) 11. Add security logging (MEDIUM) 12. Implement account lockout mechanism (MEDIUM) ### Sprint 4 (Improvements - Week 5-6): 13. Add Socket.IO event validation (MEDIUM) 14. Implement email verification (MEDIUM) 15. Add password change endpoint (MEDIUM) 16. Improve error handling and logging (MEDIUM) --- ## SECURITY BEST PRACTICES RECOMMENDATIONS ### 1. Implement Security Development Lifecycle - Security requirements in user stories - Threat modeling for new features - Security code review checklist - Regular security training for developers ### 2. Add Security Testing to CI/CD ```yaml # .github/workflows/security.yml - name: Security Audit run: bun audit --audit-level=moderate - name: SAST Scan run: bun run lint:security ``` ### 3. Environment-Specific Configurations - Use different JWT secrets per environment - Shorter token expiration in production - Strict CSP in production - HTTPS-only cookies in production ### 4. Monitoring & Alerting - Set up security event monitoring - Alert on multiple failed logins - Monitor rate limit violations - Track API response times for DoS detection --- ## CONCLUSION The Adopt-a-Street application demonstrates **good foundational security practices** including: - Password hashing with bcrypt - JWT authentication implementation - Input validation framework - Socket.IO authentication - Security headers with Helmet However, **critical gaps exist** that must be addressed: - Broken access control on user profiles and reports - Known vulnerability in axios dependency - Incomplete input validation coverage - Missing CSRF protection - Insufficient security logging **Immediate action required on 3 CRITICAL and 8 HIGH priority issues.** With the recommended remediations implemented, the application would achieve a security rating of **8.5/10 (Low-Medium Risk)**. --- ## CONTACT & FOLLOW-UP For questions about this audit report or remediation assistance: - Review findings with development team - Prioritize fixes based on risk and business impact - Re-audit after implementing Sprint 1 & 2 fixes - Schedule quarterly security audits going forward **Next Audit Recommended:** After critical and high priority fixes are implemented (approximately 4 weeks) --- **End of Security Audit Report**