Files
DashDescomplicar/AUDIT-REPORT.md
T

16 KiB

type, title, description, timestamp, layer
type title description timestamp layer
Document Audit Report Relatório de auditoria de segurança e qualidade do DashDescomplicar (React 19 + TypeScript + Vite + Express + MySQL), com 20 vulnerabilidades classificadas e veredicto de não apto para produção. 2026-02-14T03:33:42.730555+00:00 wiki

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:

# 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:

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:

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:

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:

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:

npm install express-rate-limit
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:

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:

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):

const [rows] = await db.query<RowDataPacket[]>(
  `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:

npm install zod
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:

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:

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
# 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:

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:

} 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:

} 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:

const config = {
  // ...
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0,
  // Sem connectTimeout ou acquireTimeout
}

Remediação:

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:

// api/server.ts
app.use((err: any, _req, res, _next) => { ... })

// api/routes/hetzner.ts
} catch (error) {
  console.error('Error collecting metrics:', error)
}

Remediação:

} 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:

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:

algorithms: {
  kex: [
    // ...
    'diffie-hellman-group14-sha1',  // Legacy
    'diffie-hellman-group1-sha1'    // INSEGURO
  ]
}

Remediação:

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:

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:

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:

console.log('='.repeat(50))
console.log(`API Server running on http://localhost:${PORT}`)

Recomendação:

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.