draggable topology + attack visualizer + tools in containers
- topology nodes draggable (pointer events, SVG transform), layout hint shown - attack visualizer: /api/attacks logs attacker->target events; red pulsing dashed arcs on recent attacks (60s hot), ⚔ counts ok/fail - submit_flag now takes attacker_idx vs target_idx (A/D semantics); UI has target dropdown (enemy teams), leaderboard records target - containers get vim+curl+wget+netcat+git+pip3 (Dockerfiles blogpost/cdn/ phew/sheesh/warmup); warmup base ubuntu:20.04 EOL -> 24.04 - team portal: target dropdown refreshed after login (was empty pre-auth)
This commit is contained in:
+30
-1
@@ -138,7 +138,11 @@
|
||||
<option value="sheesh">sheesh (crypto)</option>
|
||||
<option value="warmup">warmup</option>
|
||||
</select>
|
||||
<label>Flag</label>
|
||||
<label>Target (tim musuh yang diserang)</label>
|
||||
<select id="target">
|
||||
<option value="">Memuat target…</option>
|
||||
</select>
|
||||
<label>Flag (yang kamu curi dari target)</label>
|
||||
<input type="text" id="flag" placeholder="GEMASTIK18{...}" autocomplete="off">
|
||||
<button onclick="submitFlag()" style="width:100%;margin-top:16px">Submit Flag</button>
|
||||
<div id="result"></div>
|
||||
@@ -229,6 +233,8 @@ async function doLogin() {
|
||||
document.getElementById('portalScreen').style.display = 'block';
|
||||
document.getElementById('logoutBtn').style.display = '';
|
||||
loadInfo();
|
||||
loadTargetDropdown(); // target dropdown needs auth — refresh after login
|
||||
loadTargets();
|
||||
} else {
|
||||
err.textContent = 'Password salah. Coba lagi.';
|
||||
err.style.display = 'block';
|
||||
@@ -305,6 +311,7 @@ async function submitFlag() {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
team: TEAM_ID,
|
||||
target: parseInt(document.getElementById('target').value || '0', 10) || TEAM_ID,
|
||||
challenge: document.getElementById('challenge').value,
|
||||
flag: document.getElementById('flag').value.trim(),
|
||||
}),
|
||||
@@ -313,6 +320,28 @@ async function submitFlag() {
|
||||
else { res.className = 'err'; res.textContent = '❌ ' + (d.error || 'Gagal'); }
|
||||
}
|
||||
|
||||
// target dropdown: all enemy teams (exclude own)
|
||||
async function loadTargetDropdown() {
|
||||
try {
|
||||
const d = await api(`/api/team/${TEAM_ID}/targets`);
|
||||
const sel = document.getElementById('target');
|
||||
const targets = (d.targets || []).filter(t => t.team_idx !== TEAM_ID);
|
||||
const seen = new Map();
|
||||
for (const t of targets) seen.set(t.team_idx, t.team_label || ('Team ' + t.team_idx));
|
||||
sel.innerHTML = '';
|
||||
if (!seen.size) {
|
||||
sel.innerHTML = '<option value="">Tidak ada tim musuh</option>';
|
||||
return;
|
||||
}
|
||||
for (const [idx, label] of seen) {
|
||||
const o = document.createElement('option');
|
||||
o.value = idx; o.textContent = `${label} (tim ${idx})`;
|
||||
sel.appendChild(o);
|
||||
}
|
||||
} catch (e) { /* non-fatal */ }
|
||||
}
|
||||
loadTargetDropdown();
|
||||
|
||||
checkSession();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user