Fix high and medium security vulnerabilities
CSRF: upgrade session cookie sameSite from 'lax' to 'strict'. Rate limiting: login endpoint now blocks an IP after 10 failed attempts in a 15-minute window; resets on success. In-memory, no new dependency. SESSION_SECRET: server exits at startup when NODE_ENV=production and SESSION_SECRET is unset. docker-compose.yml updated to pass it via env; .env.example added with generation instructions. Security headers: add X-Content-Type-Options, X-Frame-Options, and Referrer-Policy to all responses. Sensitive data: routing_number and account_number are now omitted from GET /api/account/:id responses for non-admin users. Image size: logo upload capped at 512 KB in the account PUT handler. Amount validation: checks (POST/PUT) and deposit items (POST/PUT) now reject non-finite and non-positive amounts. QBO import: uploaded file is rejected if its MIME type is not text or a known CSV variant.
This commit is contained in:
@@ -262,12 +262,20 @@ function confirmDeposits(db, records, account_id) {
|
||||
// POST /api/qbo-import/parse
|
||||
router.post('/parse', upload.single('file'), (req, res) => {
|
||||
if (!req.file) return res.status(400).json({ error: 'No file uploaded.' });
|
||||
|
||||
const type = req.body.type;
|
||||
if (type !== 'checks' && type !== 'deposits') {
|
||||
fs.unlink(req.file.path, () => {});
|
||||
return res.status(400).json({ error: 'Invalid type. Must be "checks" or "deposits".' });
|
||||
}
|
||||
|
||||
// Reject non-text MIME types — only CSV/plain text is expected
|
||||
const mime = (req.file.mimetype || '').toLowerCase();
|
||||
if (!mime.startsWith('text/') && mime !== 'application/csv' && mime !== 'application/vnd.ms-excel') {
|
||||
fs.unlink(req.file.path, () => {});
|
||||
return res.status(400).json({ error: 'File must be a CSV text file.' });
|
||||
}
|
||||
|
||||
let text;
|
||||
try {
|
||||
text = fs.readFileSync(req.file.path, 'utf8');
|
||||
|
||||
Reference in New Issue
Block a user