Summary
POST /contact in server/routes/contactRoutes.js has two related weaknesses: it applies no rate limit, and it directly interpolates all user-supplied fields into an HTML-formatted email without any escaping or sanitization.
Affected code
const mailOptions = {
from: email, // caller-controlled
to: process.env.EMAIL_RECEIVER || process.env.EMAIL_USER,
html: `
<p><strong>Name:</strong> ${name}</p>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Category:</strong> ${category}</p>
<p><strong>Message:</strong></p>
<p>${message}</p> <!-- unsanitized HTML -->
`
};
Issues
1. No rate limiting
There is no per-IP or per-email rate limit. Any anonymous caller can POST to this endpoint in an unbounded loop and exhaust the Gmail sending quota, triggering Google's abuse rate limits and blocking legitimate contact submissions.
2. Unsanitized HTML interpolation in email body
The name, email, category, and message fields are used verbatim in an HTML template. A caller who submits:
{ "message": "<img src=x onerror='...'><a href='https://phishing.example.com'>Click here</a>" }
will have that HTML rendered inside the recipient's email client. This enables phishing links, misleading content, or tracking pixels to be delivered via the contact form to the maintainer's inbox.
Suggested Fix
- Add an express-rate-limit middleware to cap POST /contact to a small number of requests per IP (e.g., 5 per hour):
import rateLimit from 'express-rate-limit';
const contactLimiter = rateLimit({ windowMs: 60 * 60 * 1000, max: 5 });
router.post('/', contactLimiter, async (req, res) => { ... });
- Escape all user-supplied values before inserting them into the HTML template. Use a simple helper that replaces
<, >, &, ", and ' with their HTML entities, or switch to a plain-text email body.
Summary
POST /contactinserver/routes/contactRoutes.jshas two related weaknesses: it applies no rate limit, and it directly interpolates all user-supplied fields into an HTML-formatted email without any escaping or sanitization.Affected code
Issues
1. No rate limiting
There is no per-IP or per-email rate limit. Any anonymous caller can POST to this endpoint in an unbounded loop and exhaust the Gmail sending quota, triggering Google's abuse rate limits and blocking legitimate contact submissions.
2. Unsanitized HTML interpolation in email body
The
name,email,category, andmessagefields are used verbatim in an HTML template. A caller who submits:{ "message": "<img src=x onerror='...'><a href='https://phishing.example.com'>Click here</a>" }will have that HTML rendered inside the recipient's email client. This enables phishing links, misleading content, or tracking pixels to be delivered via the contact form to the maintainer's inbox.
Suggested Fix
<,>,&,", and'with their HTML entities, or switch to a plain-text email body.