From e11aefd18421df804da2298502aa1448c7950107 Mon Sep 17 00:00:00 2001 From: null Date: Sun, 17 May 2026 15:46:59 -0500 Subject: [PATCH] =?UTF-8?q?fix:=20audit=20issues=20#10=20#14=20#16=20#19?= =?UTF-8?q?=20=E2=80=94=20CORS=20errors,=20JSON=20middleware,=20Zoho=20fie?= =?UTF-8?q?lds,=20noValidate=20(batch=200.6.8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- server/index.js | 16 +++++++++++----- src/lib/api.js | 41 +++++++++++++++++++++++++++++++---------- src/pages/Contact.jsx | 2 +- src/pages/Support.jsx | 2 +- 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 767da86..7b56828 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "queuenorth-website", "private": true, - "version": "0.5.7", + "version": "0.6.0", "type": "module", "scripts": { "dev": "concurrently \"vite\" \"node server/index.js\"", diff --git a/server/index.js b/server/index.js index a4efc63..14d95aa 100644 --- a/server/index.js +++ b/server/index.js @@ -38,7 +38,14 @@ const log = { // --- Rate Limiting --- const rateLimitWindowMs = 60 * 1000 // 1 minute -const rateLimitMax = parseInt(process.env.RATE_LIMIT_PER_MINUTE || '5', 10) +const rateLimitMax = (() => { + const val = parseInt(process.env.RATE_LIMIT_PER_MINUTE || '5', 10) + if (isNaN(val) || val < 1) { + log.warn('[RateLimit] Invalid RATE_LIMIT_PER_MINUTE, defaulting to 5') + return 5 + } + return val +})() const apiLimiter = rateLimit({ windowMs: rateLimitWindowMs, @@ -103,8 +110,7 @@ const corsConfig = cors({ app.use(corsConfig) log.info(`[CORS] Enabled with origin: ${corsOrigin}`) -// Middleware -app.use(express.json({ limit: '1mb' })) +// Middleware — JSON body parsing only on POST routes (issue #14) app.use(express.urlencoded({ extended: true, limit: '1mb' })) // Rate limiting for API routes only @@ -366,7 +372,7 @@ app.get('/api/health', (req, res) => { }) // Submit lead -app.post('/api/leads', (req, res) => { +app.post('/api/leads', express.json({ limit: '1mb' }), (req, res) => { try { const parsed = leadSchema.safeParse(req.body) @@ -440,7 +446,7 @@ app.post('/api/leads', (req, res) => { }) // Submit support request -app.post('/api/support', (req, res) => { +app.post('/api/support', express.json({ limit: '1mb' }), (req, res) => { try { const parsed = supportSchema.safeParse(req.body) diff --git a/src/lib/api.js b/src/lib/api.js index 123d665..8cdafec 100644 --- a/src/lib/api.js +++ b/src/lib/api.js @@ -4,7 +4,15 @@ const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001/api' export const api = { get: async (endpoint) => { - const response = await fetch(`${API_BASE_URL}${endpoint}`) + let response + try { + response = await fetch(`${API_BASE_URL}${endpoint}`) + } catch (err) { + if (err instanceof TypeError && err.message === 'Failed to fetch') { + throw new Error('Unable to reach the server. This may be a network or CORS issue.') + } + throw new Error(`Network error: ${err.message}`) + } if (!response.ok) { throw new Error(`API error: ${response.statusText}`) } @@ -12,19 +20,32 @@ export const api = { }, post: async (endpoint, data) => { - const response = await fetch(`${API_BASE_URL}${endpoint}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), - }) + let response + try { + response = await fetch(`${API_BASE_URL}${endpoint}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + } catch (err) { + if (err instanceof TypeError && err.message === 'Failed to fetch') { + throw new Error('Unable to reach the server. This may be a network or CORS issue.') + } + throw new Error(`Network error: ${err.message}`) + } if (!response.ok) { - const errorData = await response.json() + let errorData + try { + errorData = await response.json() + } catch { + throw new Error(`Server error (${response.status}): ${response.statusText}`) + } throw new Error(errorData.error || `API error: ${response.statusText}`) } return response.json() }, } -export { queryClient } +export { queryClient } \ No newline at end of file diff --git a/src/pages/Contact.jsx b/src/pages/Contact.jsx index 639bb42..e4c15bf 100644 --- a/src/pages/Contact.jsx +++ b/src/pages/Contact.jsx @@ -172,7 +172,7 @@ const Contact = () => { {/* Right - Form */}
-
+