# Auditoria de Segurança e Qualidade - DashDescomplicar **Data:** 14-02-2026 **Auditor:** Claude (DevHelper) **Versão do Projecto:** 1.0.0 **Stack:** React 19 + TypeScript + Vite + Express + MySQL --- ## Sumário Executivo | Severidade | Contagem | |------------|----------| | **CRÍTICO** | 3 | | **ALTO** | 6 | | **MÉDIO** | 6 | | **BAIXO** | 5 | | **TOTAL** | 20 | ### Veredicto **Estado actual: NÃO APTO PARA PRODUÇÃO** O projecto apresenta vulnerabilidades críticas de segurança que devem ser resolvidas antes de qualquer deploy em produção. A exposição de credenciais no histórico Git e o hardcoded de passwords são falhas graves que comprometem a segurança de toda a infraestrutura. --- ## 1. VULNERABILIDADES CRÍTICAS ### 1.1 Credenciais Expostas no Repositório **Severidade:** CRÍTICO **Ficheiro:** `.env` **Linhas:** 1-24 **Descrição:** O ficheiro `.env` contém credenciais sensíveis que foram expostas no repositório Git. Embora o `.gitignore` inclua `.env`, o ficheiro pode ter sido commitado anteriormente, permanecendo no histórico. **Credenciais expostas:** - Password da base de dados MySQL - Token da API Hetzner Cloud - 6 passwords SSH de servidores de produção - Credenciais de acesso root a servidores críticos **Impacto:** - Acesso não autorizado à base de dados - Controlo total dos servidores Hetzner - Acesso SSH a 6 servidores de produção - Comprometimento completo da infraestrutura **Remediação:** ```bash # 1. Remover .env do histórico git rm --cached .env git commit -m "security: remove exposed credentials" # 2. Limpar histórico (CUIDADO: reescreve histórico) git filter-branch --force --index-filter \ "git rm --cached --ignore-unmatch .env" \ --prune-empty --tag-name-filter cat -- --all # 3. Force push (se aplicável) git push origin --force --all ``` **Mitigação Imediata:** - Rodar TODAS as credenciais expostas - Revogar token Hetzner actual - Alterar passwords SSH de todos os servidores --- ### 1.2 Credenciais Hardcoded no Código **Severidade:** CRÍTICO **Ficheiro:** `api/db.ts` **Linha:** 11 **Código problemático:** ```typescript const config = { host: process.env.DB_HOST || 'localhost', user: process.env.DB_USER || 'ealmeida_desk24', password: process.env.DB_PASS || '9qPRdCGGqM4o', // CRÍTICO database: process.env.DB_NAME || 'ealmeida_desk24', // ... } ``` **Descrição:** A password da base de dados está hardcoded como fallback. Se as variáveis de ambiente não estiverem definidas, a password real será utilizada. **Remediação:** ```typescript const config = { host: process.env.DB_HOST || 'localhost', user: process.env.DB_USER, password: process.env.DB_PASS, // SEM fallback database: process.env.DB_NAME, // ... } // Validação obrigatória if (!process.env.DB_PASS) { throw new Error('DB_PASS environment variable is required') } ``` --- ### 1.3 API Key Hardcoded **Severidade:** CRÍTICO **Ficheiro:** `api/routes/wp-monitor.ts` **Linha:** 15 **Código problemático:** ```typescript const API_KEY = process.env.WP_MONITOR_API_KEY || 'descomplicar-monitor-2026' ``` **Descrição:** API key fraca e previsível hardcoded como fallback. Qualquer pessoa com acesso ao código pode autenticar-se na API. **Remediação:** ```typescript const API_KEY = process.env.WP_MONITOR_API_KEY if (!API_KEY) { throw new Error('WP_MONITOR_API_KEY environment variable is required') } ``` --- ## 2. VULNERABILIDADES ALTAS ### 2.1 Ausência de Rate Limiting **Severidade:** ALTO **Ficheiro:** `api/server.ts` **Descrição:** As APIs não têm protecção contra abuso ou ataques de força bruta. Um atacante pode fazer milhares de requests sem restrição. **Remediação:** ```bash npm install express-rate-limit ``` ```typescript import rateLimit from 'express-rate-limit' const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutos max: 100, // limite por IP message: { error: 'Too many requests' } }) app.use('/api', limiter) ``` --- ### 2.2 CORS Permissivo **Severidade:** ALTO **Ficheiro:** `api/server.ts` **Linha:** 21 **Código problemático:** ```typescript app.use(cors({ origin: process.env.FRONTEND_URL || 'http://localhost:5173', credentials: true })) ``` **Descrição:** Em ambiente de desenvolvimento, o CORS permite credenciais de qualquer origem. Isto pode ser explorado para ataques CSRF. **Remediação:** ```typescript const allowedOrigins = [ 'https://dashboard.descomplicar.pt', 'https://desk.descomplicar.pt' ] if (process.env.NODE_ENV !== 'production') { allowedOrigins.push('http://localhost:3050') } app.use(cors({ origin: (origin, callback) => { if (!origin || allowedOrigins.includes(origin)) { callback(null, true) } else { callback(new Error('Not allowed by CORS')) } }, credentials: true })) ``` --- ### 2.3 SQL Injection Potencial **Severidade:** ALTO **Ficheiro:** `api/services/dashboard.ts` **Descrição:** Embora a maioria das queries use prepared statements, algumas queries dinâmicas podem ser vulneráveis se os parâmetros não forem devidamente sanitizados. **Exemplo de query segura (actual):** ```typescript const [rows] = await db.query( `SELECT ... WHERE t.duedate BETWEEN ? AND ?`, [inicio_semana, fim_semana] ) ``` **Recomendação:** - Auditar todas as queries - Garantir que TODOS os parâmetros externos usam prepared statements - Nunca concatenar strings para construir queries --- ### 2.4 Ausência de Validação de Input **Severidade:** ALTO **Ficheiro:** Todos os ficheiros em `api/routes/` **Descrição:** Nenhum endpoint valida o schema dos dados recebidos. Dados maliciosos ou malformados podem causar erros inesperados ou vulnerabilidades. **Remediação:** ```bash npm install zod ``` ```typescript import { z } from 'zod' const metricsHistorySchema = z.object({ params: z.object({ server_id: z.string().regex(/^\d+$/) }), query: z.object({ hours: z.string().regex(/^\d+$/).optional() }) }) router.get('/history/:server_id', async (req, res) => { const { params, query } = metricsHistorySchema.parse({ params: req.params, query: req.query }) // ... }) ``` --- ### 2.5 Stack Trace Exposto **Severidade:** ALTO **Ficheiro:** `api/server.ts` **Linha:** 58 **Código problemático:** ```typescript app.use((err: any, _req: express.Request, res: express.Response, _next: express.NextFunction) => { console.error('Server error:', err) res.status(500).json({ error: 'Internal server error' }) }) ``` **Descrição:** Em produção, `console.error` pode vazar informações sensíveis para logs que podem ser acedidos por atacantes. **Remediação:** ```typescript app.use((err: any, req: express.Request, res: express.Response, _next: express.NextFunction) => { const errorId = crypto.randomUUID() // Log estruturado sem stack trace em produção if (process.env.NODE_ENV === 'production') { console.error(JSON.stringify({ errorId, message: err.message, path: req.path, timestamp: new Date().toISOString() })) } else { console.error('Server error:', err) } res.status(500).json({ error: 'Internal server error', errorId }) }) ``` --- ### 2.6 Passwords SSH em Plaintext **Severidade:** ALTO **Ficheiro:** `.env` **Descrição:** 6 servidores têm passwords SSH armazenadas em plaintext. Isto viola as melhores práticas de segurança. **Servidores afectados:** - `SERVER_HOST` (176.9.3.158) - `EASY_HOST` (178.63.18.51) - `MCPHUB_HOST` (mcp-hub.descomplicar.pt) - `MEET_HOST` (meet.descomplicar.pt) - `WHATSAPP_HOST` (whatsapp.descomplicar.pt) - `WHATSMS_HOST` (whatsms.descomplicar.pt) **Remediação:** 1. Migrar para autenticação por chave SSH 2. Remover passwords do `.env` 3. Usar SSH keys com passphrase ```bash # Gerar chave SSH ssh-keygen -t ed25519 -C "dashboard@descomplicar" # Copiar para servidor ssh-copy-id -i ~/.ssh/dashboard.pub root@server ``` --- ## 3. VULNERABILIDADES MÉDIAS ### 3.1 Configuração OIDC Incompleta **Severidade:** MÉDIO **Ficheiro:** `src/auth/config.ts` **Código:** ```typescript export const oidcConfig = { authority: 'https://auth.descomplicar.pt/application/o/dashboard-descomplicar/', client_id: 'OKRSM2FZeSxJDhoV9e17dGRU1L1NEE1JBdnPVWTO', // Sem client_secret redirect_uri: window.location.origin + '/callback', // ... } ``` **Descrição:** A configuração OIDC não inclui `client_secret`, o que pode indicar uma configuração de cliente público. Verificar se é intencional. **Recomendação:** - Se cliente confidencial: adicionar `client_secret` via env var - Se cliente público: garantir que PKCE está habilitado --- ### 3.2 Mock Data em Produção **Severidade:** MÉDIO **Ficheiro:** `src/App.tsx` **Linha:** ~650 **Código:** ```typescript } catch (error) { console.error('Failed to fetch dashboard data:', error) setData(getMockData()) // Fallback para mock } ``` **Descrição:** Em produção, falhas na API resultam em dados fictícios em vez de erro claro. Isto pode mascarar problemas e confundir utilizadores. **Remediação:** ```typescript } catch (error) { console.error('Failed to fetch dashboard data:', error) // Em produção, mostrar erro em vez de mock if (process.env.NODE_ENV === 'production') { setError('Não foi possível carregar os dados. Tente novamente.') } else { setData(getMockData()) } } ``` --- ### 3.3 Connection Pool Sem Timeout **Severidade:** MÉDIO **Ficheiro:** `api/db.ts` **Código:** ```typescript const config = { // ... waitForConnections: true, connectionLimit: 10, queueLimit: 0, // Sem connectTimeout ou acquireTimeout } ``` **Remediação:** ```typescript const config = { // ... waitForConnections: true, connectionLimit: 10, queueLimit: 0, connectTimeout: 10000, // 10 segundos acquireTimeout: 15000, // 15 segundos timeout: 30000, // 30 segundos para queries } ``` --- ### 3.4 Tipo `any` em Catch Blocks **Severidade:** MÉDIO **Ficheiro:** Vários **Exemplos:** ```typescript // api/server.ts app.use((err: any, _req, res, _next) => { ... }) // api/routes/hetzner.ts } catch (error) { console.error('Error collecting metrics:', error) } ``` **Remediação:** ```typescript } catch (error: unknown) { const message = error instanceof Error ? error.message : 'Unknown error' console.error('Error:', message) } ``` --- ### 3.5 APIs Sem Autenticação Backend **Severidade:** MÉDIO **Ficheiro:** `api/routes/*` **Descrição:** As APIs do backend não validam o token OIDC. Embora o frontend exija autenticação, as APIs estão acessíveis directamente. **Remediação:** ```typescript import { auth } from 'express-openid-connect' app.use(auth({ issuerBaseURL: 'https://auth.descomplicar.pt', baseURL: 'https://dashboard.descomplicar.pt', clientID: process.env.OIDC_CLIENT_ID, secret: process.env.OIDC_SECRET, })) // Middleware para proteger rotas app.use('/api', (req, res, next) => { if (!req.oidc.isAuthenticated()) { return res.status(401).json({ error: 'Unauthorized' }) } next() }) ``` --- ### 3.6 Algoritmos SSH Legacy **Severidade:** MÉDIO **Ficheiro:** `api/services/server-metrics.ts` **Linha:** 108 **Código:** ```typescript algorithms: { kex: [ // ... 'diffie-hellman-group14-sha1', // Legacy 'diffie-hellman-group1-sha1' // INSEGURO ] } ``` **Remediação:** ```typescript algorithms: { kex: [ 'curve25519-sha256', 'curve25519-sha256@libssh.org', 'ecdh-sha2-nistp256', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp521', 'diffie-hellman-group-exchange-sha256', ] } ``` --- ## 4. VULNERABILIDADES BAIXAS ### 4.1 Componente App.tsx Grande **Severidade:** BAIXO **Ficheiro:** `src/App.tsx` **Linhas:** ~700 **Descrição:** O componente principal tem mais de 700 linhas, dificultando manutenção e testes. **Recomendação:** Extrair para componentes menores: - `components/HeroStat.tsx` - `components/GlassCard.tsx` - `components/StatusPill.tsx` - `components/ListItem.tsx` - `components/SummaryWidget.tsx` - `components/ProgressRing.tsx` --- ### 4.2 Estilos Inline **Severidade:** BAIXO **Ficheiro:** `src/App.tsx` **Exemplos:** ```typescript style={{ width: size, height: size }} ``` **Recomendação:** Mover para CSS classes em `index.css` ou usar Tailwind props. --- ### 4.3 Ausência de Testes **Severidade:** BAIXO **Ficheiro:** N/A **Descrição:** O projecto não tem testes unitários, de integração ou E2E. **Recomendação:** ```bash npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom ``` **Estrutura sugerida:** ``` src/ __tests__/ App.test.tsx auth/ AuthWrapper.test.tsx pages/ __tests__/ Financial.test.tsx Monitor.test.tsx api/ __tests__/ routes/ dashboard.test.ts ``` --- ### 4.4 README Genérico **Severidade:** BAIXO **Ficheiro:** `README.md` **Descrição:** README é o template padrão do Vite, sem informação específica do projecto. **Recomendação:** Adicionar: - Descrição do projecto - Instruções de setup - Variáveis de ambiente necessárias - Endpoints da API - Arquitectura --- ### 4.5 Logs Verbosos em Produção **Severidade:** BAIXO **Ficheiro:** `api/server.ts` **Código:** ```typescript console.log('='.repeat(50)) console.log(`API Server running on http://localhost:${PORT}`) ``` **Recomendação:** ```typescript if (process.env.NODE_ENV !== 'production') { console.log('API Server running on http://localhost:' + PORT) } ``` --- ## 5. ANÁLISE DE QUALIDADE ### 5.1 TypeScript | Aspecto | Status | |---------|--------| | Strict mode | ✅ Habilitado | | No implicit any | ✅ Configurado | | No unused locals | ✅ Configurado | | No unused parameters | ✅ Configurado | | Strict null checks | ✅ Habilitado | ### 5.2 React | Aspecto | Status | |---------|--------| | React 19 | ✅ Versão actual | | Hooks rules | ✅ ESLint plugin | | React Refresh | ✅ Configurado | | StrictMode | ✅ Habilitado | ### 5.3 Arquitectura | Aspecto | Status | |---------|--------| | Separação frontend/backend | ✅ | | Services layer | ✅ | | Rotas modulares | ✅ | | Lazy loading | ❌ Ausente | | Code splitting | ❌ Ausente | | Testes | ❌ Ausente | --- ## 6. PLANO DE REMEDIAÇÃO ### Fase 1 - Crítico (Imediato) | # | Acção | Prazo | |---|-------|-------| | 1 | Remover `.env` do histórico Git | HOJE | | 2 | Rodar todas as credenciais expostas | HOJE | | 3 | Remover hardcoded passwords | HOJE | | 4 | Configurar secrets management | 1 dia | ### Fase 2 - Alto (Esta semana) | # | Acção | Prazo | |---|-------|-------| | 5 | Implementar rate limiting | 2 dias | | 6 | Corrigir CORS para produção | 2 dias | | 7 | Adicionar validação de input (Zod) | 3 dias | | 8 | Implementar autenticação no backend | 3 dias | | 9 | Migrar SSH para chave-based auth | 5 dias | | 10 | Corrigir error handling | 2 dias | ### Fase 3 - Médio (Próximas 2 semanas) | # | Acção | Prazo | |---|-------|-------| | 11 | Configurar timeouts DB | 1 dia | | 12 | Corrigir tipo `any` em catches | 2 dias | | 13 | Revisar algoritmos SSH | 1 dia | | 14 | Melhorar tratamento de erros frontend | 2 dias | ### Fase 4 - Baixo (Próximo mês) | # | Acção | Prazo | |---|-------|-------| | 15 | Refactor App.tsx | 3 dias | | 16 | Adicionar testes unitários | 5 dias | | 17 | Adicionar testes E2E | 5 dias | | 18 | Melhorar documentação | 2 dias | --- ## 7. FICHEIROS ANALISADOS ### Frontend - `src/App.tsx` - `src/main.tsx` - `src/index.css` - `src/auth/AuthWrapper.tsx` - `src/auth/config.ts` - `src/pages/Financial.tsx` - `src/pages/Monitor.tsx` ### Backend - `api/server.ts` - `api/db.ts` - `api/routes/dashboard.ts` - `api/routes/monitor.ts` - `api/routes/financial.ts` - `api/routes/hetzner.ts` - `api/routes/diagnostic.ts` - `api/routes/wp-monitor.ts` - `api/services/dashboard.ts` - `api/services/monitoring.ts` - `api/services/hetzner.ts` - `api/services/server-metrics.ts` - `api/services/financial.ts` ### Configuração - `.env` - `.gitignore` - `package.json` - `tsconfig.json` - `tsconfig.app.json` - `eslint.config.js` - `vite.config.ts` - `Dockerfile` --- ## 8. ASSINATURA **Auditor:** Claude (DevHelper Agent) **Data:** 14-02-2026 **Versão do Relatório:** 1.0 --- *Este relatório foi gerado automaticamente. Recomenda-se revisão por especialista em segurança antes de implementação das remediações.*