cybersec.html
Atualizado em
04/08/2025 17h36
cybersec.html
— 37 KB
Conteúdo do arquivo
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CyberSec - Terminal Challenge</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
background: #1a1a1a;
color: #ffffff;
min-height: 100vh;
line-height: 1.4;
}
.terminal-container {
max-width: 1400px;
margin: 0 auto;
background: #2a2a2a;
border: 2px solid #404040;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
}
.terminal-body {
padding: 20px;
background: #1a1a1a;
}
.header-section {
margin-bottom: 30px;
padding: 20px;
background: rgba(64, 64, 64, 0.3);
border: 1px solid #505050;
border-radius: 6px;
display: grid;
grid-template-columns: 1fr auto;
gap: 30px;
align-items: center;
}
.header-content h1 {
color: #ffffff;
font-size: clamp(1.5rem, 4vw, 2.5rem);
margin-bottom: 10px;
}
.header-content p {
color: #cccccc;
font-size: clamp(0.9rem, 2vw, 1.1rem);
}
.header-stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
min-width: 240px;
}
.game-layout {
display: grid;
grid-template-columns: 1fr;
gap: 20px;
}
@media (min-width: 1024px) {
.game-layout {
grid-template-columns: 2fr 1fr;
}
}
.scenario-section {
background: rgba(64, 64, 64, 0.2);
border: 1px solid #505050;
border-radius: 6px;
padding: 20px;
}
.info-section {
background: rgba(64, 64, 64, 0.2);
border: 1px solid #505050;
border-radius: 6px;
padding: 20px;
}
.section-title {
color: #ffffff;
font-size: clamp(1.1rem, 2.5vw, 1.4rem);
margin-bottom: 15px;
border-bottom: 1px solid #666666;
padding-bottom: 5px;
}
.code-container {
background: #000;
border: 1px solid #333;
border-radius: 4px;
margin: 15px 0;
overflow: hidden;
}
.code-header {
background: #1a1a1a;
color: #888;
padding: 8px 15px;
font-size: 12px;
border-bottom: 1px solid #333;
}
.code-content {
padding: 15px;
font-size: clamp(0.8rem, 1.5vw, 0.95rem);
line-height: 1.6;
overflow-x: auto;
color: #f8f8f2;
transition: all 0.3s ease;
white-space: pre-wrap;
}
.vulnerability-code {
border-left: 4px solid #666666;
}
.scan-button {
background: rgba(80, 80, 80, 0.5);
border: 1px solid #666666;
color: #ffffff;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
font-size: clamp(0.8rem, 1.5vw, 0.9rem);
font-weight: bold;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
}
.scan-button:hover {
background: rgba(100, 100, 100, 0.5);
border-color: #888888;
}
.scan-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.scan-button.scanned {
background: rgba(80, 80, 80, 0.3);
border-color: #666666;
color: #cccccc;
}
.scan-section {
display: flex;
justify-content: flex-end;
align-items: flex-start;
gap: 15px;
margin: 10px 0;
}
.scan-feedback {
background: rgba(52, 152, 219, 0.1);
border: 1px solid #3498db;
border-radius: 4px;
padding: 10px 15px;
color: #3498db;
font-size: clamp(0.8rem, 1.5vw, 0.9rem);
flex: 1;
display: none;
margin-right: auto;
}
.question-block {
background: rgba(100, 100, 100, 0.2);
border: 1px solid #aaaaaa;
border-radius: 4px;
padding: 15px;
margin: 20px 0;
}
.question-text {
color: #e0e0e0;
font-weight: bold;
font-size: clamp(0.95rem, 2vw, 1.1rem);
}
.options-grid {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
margin: 20px 0;
}
.option-button {
background: rgba(80, 80, 80, 0.3);
border: 1px solid #666666;
color: #ffffff;
padding: 12px 16px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
font-family: inherit;
font-size: clamp(0.85rem, 1.5vw, 0.95rem);
text-align: left;
}
.option-button:hover {
background: rgba(100, 100, 100, 0.3);
border-color: #888888;
}
.option-button.correct {
background: rgba(31, 85, 40, 0.5);
border-color: #003d08;
color: #ffffff;
}
.option-button.incorrect {
background: rgba(78, 34, 34, 0.5);
border-color: #420000;
color: #ffffff;
}
.option-button:disabled {
cursor: not-allowed;
opacity: 0.7;
}
.feedback-section {
margin: 20px 0;
padding: 15px;
border-radius: 4px;
border-left: 4px solid;
}
.feedback-section.success {
background: rgba(31, 85, 40, 0.3);
border-left-color: #002b12;
color: #ffffff;
}
.feedback-section.error {
background: rgba(78, 34, 34, 0.3);
border-left-color: #2c0000;
color: #ffffff;
}
.feedback-section.info {
background: rgba(52, 152, 219, 0.1);
border-left-color: #3498db;
color: #3498db;
}
.next-button {
width: 100%;
background: rgba(80, 80, 80, 0.5);
border: 1px solid #666666;
color: #ffffff;
padding: 15px;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
font-size: clamp(0.95rem, 2vw, 1.1rem);
font-weight: bold;
transition: all 0.3s ease;
margin-top: 20px;
}
.next-button:hover {
background: rgba(100, 100, 100, 0.5);
border-color: #888888;
}
.next-button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
margin-bottom: 20px;
}
.stat-box {
background: rgba(64, 64, 64, 0.5);
border: 1px solid #505050;
border-radius: 4px;
padding: 15px;
text-align: center;
}
.stat-value {
font-size: clamp(1.5rem, 3vw, 2rem);
font-weight: bold;
color: #ffffff;
margin-bottom: 5px;
}
.stat-label {
font-size: clamp(0.8rem, 1.5vw, 0.9rem);
color: #cccccc;
}
.progress-container {
max-width: 1400px;
margin: 0 auto;
padding: 15px;
border-radius: 6px;
}
.progress-label {
color: #95a5a6;
margin-bottom: 8px;
font-size: clamp(0.85rem, 1.5vw, 0.95rem);
}
.progress-label span{
color: #ffffff;
font-weight: bold;
}
.progress-bar {
width: 100%;
height: 12px;
background: rgba(80, 80, 80, 0.5);
border: 1px solid #666666;
border-radius: 6px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #888888, #aaaaaa);
transition: width 0.5s ease;
border-radius: 4px;
}
.hint-section {
background: rgba(241, 196, 15, 0.1);
border: 1px solid #f1c40f;
border-radius: 4px;
padding: 15px;
margin: 20px 0;
}
.hint-title {
color: #f1c40f;
font-weight: bold;
margin-bottom: 8px;
}
.hint-text {
color: #f4d03f;
font-size: clamp(0.85rem, 1.5vw, 0.95rem);
}
.completion-screen {
display: none;
text-align: center;
padding: 40px;
background: rgba(39, 174, 96, 0.1);
border: 2px solid #27ae60;
border-radius: 8px;
}
.completion-screen.active {
display: block;
}
.completion-title {
color: #27ae60;
font-size: clamp(1.8rem, 4vw, 2.5rem);
margin-bottom: 20px;
}
.completion-stats {
margin: 30px 0;
font-size: clamp(1rem, 2vw, 1.2rem);
}
.completion-stats span {
color: #3498db;
font-weight: bold;
}
.restart-button {
background: rgba(39, 174, 96, 0.2);
border: 2px solid #27ae60;
color: #27ae60;
padding: 15px 30px;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
font-size: clamp(0.95rem, 2vw, 1.1rem);
font-weight: bold;
transition: all 0.3s ease;
}
.restart-button:hover {
background: rgba(39, 174, 96, 0.3);
border-color: #2ecc71;
}
@media (max-width: 767px) {
.terminal-body {
padding: 15px;
}
.scenario-section, .info-section {
padding: 15px;
}
.stats-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="progress-container">
<div class="progress-label">Progresso do Treinamento | <span id="progress-percentage">0%</span></div>
<div class="progress-bar">
<div class="progress-fill" id="progress-fill" style="width: 0%"></div>
</div>
</div>
<div class="terminal-container">
<div class="terminal-body">
<div id="game-content">
<div class="game-layout">
<div class="scenario-section">
<h2 class="section-title" id="scenario-title">CyberSec - Terminal Challenge</h2>
<div id="scenario-content"></div>
</div>
<div class="info-section">
<div class="question-block">
<div class="question-text" id="question-text">Carregando pergunta...</div>
</div>
<div class="options-grid" id="options-container"></div>
<div id="feedback-container"></div>
<button class="next-button" id="next-button" disabled>
Próximo Cenário →
</button>
</div>
</div>
</div>
<div class="completion-screen" id="completion-screen">
<h2 class="completion-title">🏆 Treinamento Concluído!</h2>
<p style="color: #95a5a6; font-size: clamp(1rem, 2vw, 1.2rem); margin-bottom: 20px;">
Parabéns! Você completou todos os cenários de segurança.
</p>
<div class="completion-stats">
<p>Pontuação Final: <span id="final-score">0</span></p>
<p>Precisão Alcançada: <span id="final-accuracy">0%</span></p>
<p>Vulnerabilidades Encontradas: <span id="final-vulns">0</span></p>
</div>
<button class="restart-button" id="restart-button">
🔄 Reiniciar Treinamento
</button>
</div>
</div>
</div>
<script>
// CyberSec Terminal Challenge - JavaScript
(function() {
'use strict';
// Estado do jogo protegido
let gameData = {
current: 0,
points: 0,
userLevel: 1,
foundVulns: 0,
totalAnswers: 0,
correctCount: 0,
challenges: [
{
title: "🔐 Vulnerabilidade de Injeção SQL",
desc: "Analise este código de autenticação PHP:",
codeFile: "login.php",
codeContent: `<?php
$user = $_POST['username'];
$pass = $_POST['password'];
$sql = "SELECT * FROM users WHERE username = '$user' AND password = '$pass'";
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) > 0) {
$_SESSION['logged_in'] = true;
echo "Login realizado com sucesso!";
} else {
echo "Credenciais inválidas!";
}
?>`,
vulnInfo: "Dados do usuário inseridos diretamente na query SQL sem sanitização adequada.",
questionText: "Qual é a principal vulnerabilidade presente neste código?",
choices: [
"Falha na validação de sessão",
"Exposição de informações sensíveis",
"Injeção SQL - concatenação direta de entrada do usuário",
"Vulnerabilidade de Cross-Site Scripting"
],
rightAnswer: 2,
explanation: "O código concatena diretamente $_POST na query SQL, permitindo SQL Injection. Um atacante pode inserir: <code>' OR '1'='1' --</code> para burlar a autenticação. A solução é usar prepared statements ou sanitização adequada.",
hintText: "Observe como os valores $_POST são inseridos diretamente na string SQL sem qualquer validação ou escape."
},
{
title: "🚨 Cross-Site Scripting (XSS)",
desc: "Examine este código que renderiza comentários:",
codeFile: "comments.php",
codeContent: `<div class="comment-section">
<?php foreach($comments as $comment): ?>
<div class="comment-item">
<h4>Por: <?php echo $comment['author']; ?></h4>
<p><?php echo $comment['message']; ?></p>
<time><?php echo $comment['created_at']; ?></time>
</div>
<?php endforeach; ?>
</div>`,
vulnInfo: "Dados de comentários exibidos sem escape HTML adequado.",
questionText: "Como esta vulnerabilidade pode ser explorada?",
choices: [
"Ataques de força bruta contra o sistema",
"Inserção de código JavaScript malicioso nos campos de comentário",
"Manipulação de timestamps",
"Acesso não autorizado a outros comentários"
],
rightAnswer: 1,
explanation: "Sem htmlspecialchars() ou escape similar, um atacante pode inserir <code><script>alert('XSS')</script></code> ou outros códigos JavaScript maliciosos que serão executados no navegador de outros usuários. A solução é sempre escapar dados antes de exibi-los.",
hintText: "Note que os dados vindos do banco de dados são impressos diretamente no HTML sem nenhum tipo de sanitização."
},
{
title: "🔑 Falha de Controle de Acesso (IDOR)",
desc: "Analise este endpoint de API REST:",
codeFile: "user_api.js",
codeContent: `// GET /api/users/{id}
app.get('/api/users/:id', authenticateToken, (req, res) => {
const userId = req.params.id;
const query = 'SELECT id, username, email, phone FROM users WHERE id = ?';
db.query(query, [userId], (err, results) => {
if (err) {
return res.status(500).json({error: 'Erro interno do servidor'});
}
if (results.length > 0) {
res.json(results[0]);
} else {
res.status(404).json({error: 'Usuário não encontrado'});
}
});
});`,
vulnInfo: "Ausência de verificação de autorização - qualquer usuário autenticado pode acessar dados de outros.",
questionText: "Qual é o principal problema de segurança neste endpoint?",
choices: [
"Exposição de detalhes de erro do servidor",
"Vulnerabilidade de injeção SQL",
"Ausência de criptografia de dados",
"Falta de verificação de autorização (IDOR)"
],
rightAnswer: 3,
explanation: "Este é um caso clássico de IDOR (Insecure Direct Object References). O endpoint verifica apenas se o usuário está autenticado, mas não se ele tem permissão para acessar os dados do ID solicitado. Qualquer usuário logado pode ver dados de outros usuários mudando o ID na URL.",
hintText: "Observe que existe autenticação (authenticateToken) mas não há verificação se o usuário pode acessar o ID específico."
},
{
title: "🛡️ Configuração Insegura de Servidor",
desc: "Examine esta configuração do nginx:",
codeFile: "nginx.conf",
codeContent: `server {
listen 80;
server_name meusite.com.br;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /admin {
proxy_pass http://localhost:3000/admin;
# Panel administrativo sem restrições
}
# Logs em modo debug para produção
error_log /var/log/nginx/error.log debug;
access_log /var/log/nginx/access.log combined;
}`,
vulnInfo: "Múltiplas falhas: sem HTTPS, painel admin desprotegido, logs debug em produção.",
questionText: "Quantos problemas de segurança críticos você identifica?",
choices: [
"Pelo menos 3 problemas graves de segurança",
"Apenas 1 problema de configuração menor",
"Configuração adequada para produção",
"Somente problemas de performance"
],
rightAnswer: 0,
explanation: "Problemas identificados: 1) Ausência de HTTPS/SSL (porta 80), 2) Rota /admin sem qualquer proteção de acesso, 3) Logs em nível debug em produção podem expor informações sensíveis. Cada um desses problemas representa um risco significativo de segurança.",
hintText: "Analise o protocolo utilizado, as proteções da rota administrativa e o nível de detalhamento dos logs."
},
{
title: "💾 Hash de Senha Inadequado",
desc: "Revise este sistema de registro:",
codeFile: "register.js",
codeContent: `function createUser(username, password, email) {
// Validações básicas
if (!username || !password || !email) {
throw new Error('Todos os campos são obrigatórios');
}
if (password.length < 6) {
throw new Error('Senha muito curta');
}
// "Criptografia" da senha
const hashedPassword = Buffer.from(password).toString('base64');
const userData = {
username: username,
password: hashedPassword,
email: email,
createdAt: new Date()
};
return database.users.create(userData);
}`,
vulnInfo: "Base64 é apenas codificação, não oferece segurança real para senhas.",
questionText: "Por que este método de proteção de senha é inadequado?",
choices: [
"Validação de email insuficiente",
"Critério de senha muito restritivo",
"Base64 é codificação reversível, não hash criptográfico",
"Banco de dados não criptografado"
],
rightAnswer: 2,
explanation: "Base64 é apenas uma codificação reversível, não um hash criptográfico. Qualquer pessoa pode decodificar facilmente. Senhas devem usar algoritmos como bcrypt, scrypt ou Argon2, que incluem salt e são computacionalmente caros para quebrar.",
hintText: "A função Buffer.from().toString('base64') apenas converte a string para Base64, que pode ser facilmente revertida."
},
{
title: "🌐 CORS Mal Configurado",
desc: "Analise esta configuração de CORS:",
codeFile: "cors_config.js",
codeContent: `const express = require('express');
const cors = require('cors');
const app = express();
// Configuração CORS permissiva
app.use(cors({
origin: '*', // Aceita qualquer origem
credentials: true, // Permite cookies e headers de auth
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
}));
// Endpoint financeiro sensível
app.post('/api/transfer', authenticateUser, (req, res) => {
const { fromAccount, toAccount, amount } = req.body;
bankingService.transferMoney(fromAccount, toAccount, amount)
.then(result => res.json({success: true, transaction: result}))
.catch(err => res.status(400).json({error: err.message}));
});`,
vulnInfo: "CORS com origin '*' e credentials habilitado permite ataques CSRF de qualquer domínio.",
questionText: "Qual é o maior risco desta configuração CORS?",
choices: [
"Headers permitidos são insuficientes",
"Métodos HTTP muito liberais",
"Ausência de rate limiting",
"Permite ataques CSRF de qualquer site malicioso"
],
rightAnswer: 3,
explanation: "A combinação origin: '*' com credentials: true é extremamente perigosa. Permite que qualquer site execute requests autenticados contra sua API, facilitando ataques CSRF (Cross-Site Request Forgery). Um site malicioso pode fazer transferências bancárias em nome do usuário logado.",
hintText: "Observe a combinação perigosa: origin aceita qualquer site (*) mas credentials permite envio de cookies/auth."
},
{
title: "🔐 Deserialização Insegura",
desc: "Examine este código Python que processa dados serializados:",
codeFile: "data_processor.py",
codeContent: `import pickle
import base64
def process_user_data(data):
try:
# Decodifica dados do usuário
decoded_data = base64.b64decode(data)
# Deserializa o objeto
user_object = pickle.loads(decoded_data)
# Processa os dados
return {
'name': user_object.name,
'email': user_object.email,
'role': user_object.role
}
except Exception as e:
return {'error': 'Dados inválidos'}`,
vulnInfo: "pickle.loads() pode executar código arbitrário se o atacante controlar os dados serializados.",
questionText: "Por que este código é extremamente perigoso?",
choices: [
"pickle.loads() pode executar código malicioso",
"Base64 não é seguro",
"Falta validação de entrada",
"Exception muito genérica"
],
rightAnswer: 0,
explanation: "pickle.loads() pode executar qualquer código Python durante a deserialização. Use JSON ou formatos seguros como MessagePack. O pickle só é seguro quando você controla totalmente os dados serializados.",
hintText: "O módulo pickle do Python pode executar código durante a deserialização de objetos maliciosos."
},
{
title: "⚡ Race Condition em Transações",
desc: "Analise este código de transferência bancária:",
codeFile: "banking.js",
codeContent: `async function transferMoney(fromAccount, toAccount, amount) {
// Verifica saldo
const balance = await getBalance(fromAccount);
if (balance < amount) {
throw new Error('Saldo insuficiente');
}
// Simula processamento
await sleep(100);
// Executa transferência
await updateBalance(fromAccount, balance - amount);
await updateBalance(toAccount, await getBalance(toAccount) + amount);
return { success: true, newBalance: balance - amount };
}`,
vulnInfo: "Race condition: múltiplas chamadas simultâneas podem resultar em saldo negativo.",
questionText: "Qual problema pode ocorrer com chamadas simultâneas?",
choices: [
"Deadlock no banco de dados",
"Race condition permitindo saldo negativo",
"Overflow numérico",
"Perda de dados na transferência"
],
rightAnswer: 1,
explanation: "Sem locks/transações atômicas, duas transferências simultâneas podem passar na verificação de saldo mas ambas serem executadas. A solução é usar transações de banco de dados ou locks para garantir atomicidade.",
hintText: "Observe o delay entre verificar o saldo e executar a transferência. O que acontece se duas operações rodarem ao mesmo tempo?"
}
]
};
// Funções principais
function updateDisplay() {
const progress = (gameData.current / gameData.challenges.length) * 100;
document.getElementById('progress-fill').style.width = progress + '%';
document.getElementById('progress-percentage').textContent = Math.min(Math.round(progress), 100) + '%';
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function showChallenge() {
if (gameData.current >= gameData.challenges.length) {
showCompletionScreen();
return;
}
const challenge = gameData.challenges[gameData.current];
document.getElementById('question-text').textContent = challenge.questionText;
const contentHtml = `
<p style="margin-bottom: 15px; color: #cccccc;">${challenge.desc}</p>
<div class="code-container">
<div class="code-header">${challenge.codeFile}</div>
<div class="code-content vulnerability-code" id="vuln-code">${escapeHtml(challenge.codeContent)}</div>
</div>
<div class="scan-section">
<div class="scan-feedback" id="scan-feedback"></div>
<button class="scan-button" id="scan-button">
🔍 Escanear Código
</button>
</div>
`;
document.getElementById('scenario-content').innerHTML = contentHtml;
const optionsHtml = challenge.choices.map((choice, index) =>
`<button class="option-button" data-index="${index}">${choice}</button>`
).join('');
document.getElementById('options-container').innerHTML = optionsHtml;
document.getElementById('feedback-container').innerHTML = '';
document.getElementById('next-button').disabled = true;
// Event listeners para opções
document.querySelectorAll('.option-button').forEach(btn => {
btn.addEventListener('click', () => handleAnswer(parseInt(btn.dataset.index)));
});
// Event listener para botão de scan
const scanButton = document.getElementById('scan-button');
if (scanButton) {
scanButton.addEventListener('click', performScan);
}
}
function performScan() {
const scanButton = document.getElementById('scan-button');
const scanFeedback = document.getElementById('scan-feedback');
// Evita múltiplas execuções
if (scanButton.classList.contains('scanned')) {
return;
}
// Atualiza visual do botão
scanButton.classList.add('scanned');
scanButton.innerHTML = '✅ Vulnerabilidade Detectada';
scanButton.disabled = true;
// Mostra feedback ao lado do botão
const challenge = gameData.challenges[gameData.current];
scanFeedback.innerHTML = `🔍 <strong>Scanner:</strong> ${challenge.vulnInfo}`;
scanFeedback.style.display = 'block';
}
function showVulnerability() {
// Função mantida para compatibilidade, mas não mais usada
performScan();
}
function handleAnswer(selectedIndex) {
const challenge = gameData.challenges[gameData.current];
const buttons = document.querySelectorAll('.option-button');
gameData.totalAnswers++;
buttons.forEach((btn, index) => {
btn.disabled = true;
if (index === challenge.rightAnswer) {
btn.classList.add('correct');
} else if (index === selectedIndex && index !== challenge.rightAnswer) {
btn.classList.add('incorrect');
}
});
const isCorrect = selectedIndex === challenge.rightAnswer;
if (isCorrect) {
gameData.correctCount++;
gameData.points += 100;
gameData.foundVulns++;
if (gameData.correctCount === gameData.totalAnswers) {
gameData.points += 50; // Bonus precisão
}
showFeedback('success', `✅ <strong>Correto!</strong><br><br> ${challenge.explanation}`);
} else {
showFeedback('error', `❌ <strong>Resposta Incorreta!</strong><br><br>${challenge.explanation}`);
}
updateDisplay();
document.getElementById('next-button').disabled = false;
}
function showFeedback(type, message) {
const feedbackDiv = document.createElement('div');
feedbackDiv.className = `feedback-section ${type}`;
feedbackDiv.innerHTML = message;
const container = document.getElementById('feedback-container');
// Limpa feedbacks anteriores antes de adicionar novo
container.innerHTML = '';
container.appendChild(feedbackDiv);
}
function nextChallenge() {
gameData.current++;
showChallenge();
updateDisplay();
setTimeout(() => {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
}, 500);
}
function showCompletionScreen() {
const accuracy = gameData.totalAnswers > 0
? Math.round((gameData.correctCount / gameData.totalAnswers) * 100)
: 100;
document.getElementById('final-score').textContent = gameData.points;
document.getElementById('final-accuracy').textContent = accuracy + '%';
document.getElementById('final-vulns').textContent = gameData.foundVulns;
document.getElementById('game-content').style.display = 'none';
document.getElementById('completion-screen').classList.add('active');
}
function restartGame() {
gameData.current = 0;
gameData.points = 0;
gameData.userLevel = 1;
gameData.foundVulns = 0;
gameData.totalAnswers = 0;
gameData.correctCount = 0;
document.getElementById('game-content').style.display = 'block';
document.getElementById('completion-screen').classList.remove('active');
showChallenge();
updateDisplay();
}
// Event listeners
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('next-button').addEventListener('click', nextChallenge);
document.getElementById('restart-button').addEventListener('click', restartGame);
// Inicialização
showChallenge();
updateDisplay();
});
})();
</script>
</body>
</html>