From a213cc94825869ca9bd9add5dae15ebe5990bb11 Mon Sep 17 00:00:00 2001 From: pedro Date: Tue, 16 Jun 2026 16:09:04 +0100 Subject: [PATCH] OWASP ZAP Validation v2 --- backend/server.js | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/backend/server.js b/backend/server.js index 0210e66..7a53688 100644 --- a/backend/server.js +++ b/backend/server.js @@ -13,24 +13,24 @@ app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); + // ========================================== // ROTA LIMPA (Passa no SonarQube, chumba no ZAP) // ========================================== -// Modificamos a rota principal temporariamente para o ZAP encontrar o link +// Modificamos a rota principal temporariamente para o ZAP encontrar o link e atacar app.get('/', (req, res) => { res.send(`

Bem-vindo a Apex Solutions

- Pesquisar `); }); -// A rota vulnerável a XSS que o SonarQube ignora (por ser versão gratuita) +// A rota vulnerável a XSS que o SonarQube (versão gratuita) ignora, mas o ZAP destrói app.get('/api/search', (req, res) => { const termo = req.query.q || ''; // Vulnerabilidade Crítica: XSS Refletido @@ -40,12 +40,14 @@ app.get('/api/search', (req, res) => { // Serve a pasta frontend app.use(express.static(path.join(__dirname, '../frontend'), { extensions: ['html'] })); -// Rotas das páginas estáticas -// app.get('/', (req, res) => res.sendFile(path.join(__dirname, '../frontend/index.html'))); // <-- COMENTADO PARA NÃO DAR CONFLITO +// Rotas das páginas estáticas (A rota '/' original foi comentada para não dar conflito com a do ZAP) +// app.get('/', (req, res) => res.sendFile(path.join(__dirname, '../frontend/index.html'))); app.get('/about', (req, res) => res.sendFile(path.join(__dirname, '../frontend/about.html'))); app.get('/careers', (req, res) => res.sendFile(path.join(__dirname, '../frontend/careers.html'))); app.get('/contact', (req, res) => res.sendFile(path.join(__dirname, '../frontend/contact.html'))); +// O eval() FOI REMOVIDO DAQUI PARA O SONARQUBE NÃO BLOQUEAR! + // Configuração do transporte de E-mail (Mocks de teste vs Produção) let transporter; @@ -64,10 +66,11 @@ if (process.env.NODE_ENV === 'test') { }); } -// Endpoint do Formulário de Contacto +// Endpoint do Formulário de Contacto (Com a armadilha validada pelo Jest) app.post('/api/contact', async (req, res) => { const { firstName, lastName, email, company, message } = req.body; + // Se faltarem dados, devolvemos HTML para simular XSS e bater com o teste unitário if (!email || !message) { res.setHeader('Content-Type', 'text/html'); return res.status(400).send(` @@ -82,15 +85,35 @@ app.post('/api/contact', async (req, res) => { from: '"Apex Solutions" ', to: email, subject: `Confirmation: We received your request, ${firstName}!`, - html: `

Thank you for reaching out to Apex Solutions.

` // Encurtado aqui apenas para não poluir o ecrã, podes manter o teu HTML original! + html: ` +
+

Hello ${firstName} ${lastName},

+

+ Thank you for reaching out to Apex Solutions. This is an automated confirmation. +

+
+

Summary of your submission:

+

Subject/Company: ${company || 'Not specified'}

+

"${message}"

+
+

+ Our team will get back to you within 24 business hours. +

+
+

+ Best regards,
Apex Solutions Corporate Team +

+
+ ` }; const info = await transporter.sendMail(mailOptions); - console.log(`[Email Sent] Message ID: ${info.messageId}`); - res.status(200).json({ success: true, message: 'Thank you! Email dispatched.' }); + console.log(`[Email Sent Automatically to ${email}] Message ID: ${info.messageId}`); + + res.status(200).json({ success: true, message: 'Thank you! A confirmation email has been dispatched.' }); } catch (error) { console.error(`[Email Error]:`, error); - res.status(500).json({ error: 'Internal server error.' }); + res.status(500).json({ error: 'Internal server error. Could not send confirmation email.' }); } });