// UltyScan Web Interface - Frontend Logic document.addEventListener("DOMContentLoaded", function () { initTabs(); initModeHandler(); loadWorkspaces(); checkScanStatus(); }); // Tab Navigation function initTabs() { const tabs = document.querySelectorAll(".tab"); tabs.forEach((tab) => { tab.addEventListener("click", () => { tabs.forEach((t) => t.classList.remove("active")); document .querySelectorAll(".tab-content") .forEach((c) => c.classList.remove("active")); tab.classList.add("active"); document.getElementById(tab.dataset.tab).classList.add("active"); }); }); } // Dynamic form fields based on mode function initModeHandler() { const modeSelect = document.getElementById("mode"); const portGroup = document.getElementById("port-group"); const targetFileGroup = document.getElementById("target-file-group"); const singleTargetGroup = document.getElementById("single-target-group"); if (!modeSelect) return; modeSelect.addEventListener("change", function () { const mode = this.value; // Modes that require a file const fileModes = [ "airstrike", "nuke", "massportscan", "massweb", "masswebscan", "massvulnscan", "flyover", ]; // Modes that require a port const portModes = ["port", "webporthttp", "webporthttps"]; if (fileModes.includes(mode)) { targetFileGroup.style.display = "block"; singleTargetGroup.querySelector("input").required = false; } else { targetFileGroup.style.display = "none"; singleTargetGroup.querySelector("input").required = true; } if (portModes.includes(mode)) { portGroup.style.display = "block"; portGroup.querySelector("input").required = true; } else { portGroup.style.display = "none"; portGroup.querySelector("input").required = false; } }); } // Submit Scan Form async function submitScan(event) { event.preventDefault(); const form = event.target; const formData = new FormData(form); const submitBtn = form.querySelector('button[type="submit"]'); const originalText = submitBtn.innerHTML; submitBtn.innerHTML = ' Starting...'; submitBtn.disabled = true; try { const response = await fetch("execute.php", { method: "POST", body: formData, }); const result = await response.json(); if (result.success) { showNotification("Scan started successfully!", "success"); updateConsole(result.command); startStatusPolling(); } else { showNotification("Error: " + result.error, "error"); } } catch (error) { showNotification("Failed to start scan: " + error.message, "error"); } finally { submitBtn.innerHTML = originalText; submitBtn.disabled = false; } } // Load Workspaces async function loadWorkspaces() { const container = document.getElementById("workspace-list"); if (!container) return; container.innerHTML = '

Loading workspaces...

'; try { const response = await fetch("workspaces.php?action=list"); const result = await response.json(); if (result.workspaces && result.workspaces.length > 0) { container.innerHTML = result.workspaces .map( (ws) => `
${escapeHtml(ws)}
` ) .join(""); } else { container.innerHTML = '

No workspaces found. Run a scan to create one.

'; } } catch (error) { container.innerHTML = '

Failed to load workspaces.

'; } } // Workspace Actions async function viewWorkspace(name) { try { const response = await fetch( `workspaces.php?action=view&name=${encodeURIComponent(name)}` ); const result = await response.json(); if (result.reportPath) { window.open(result.reportPath, "_blank"); } else { showNotification("No report found for this workspace.", "warning"); } } catch (error) { showNotification("Failed to open workspace.", "error"); } } async function exportWorkspace(name) { showNotification("Exporting workspace: " + name, "info"); try { const response = await fetch("workspaces.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "export", name: name }), }); const result = await response.json(); if (result.success) { showNotification("Workspace exported: " + result.path, "success"); } else { showNotification("Export failed: " + result.error, "error"); } } catch (error) { showNotification("Export failed.", "error"); } } async function deleteWorkspace(name) { if (!confirm(`Are you sure you want to delete workspace "${name}"?`)) return; try { const response = await fetch("workspaces.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "delete", name: name }), }); const result = await response.json(); if (result.success) { showNotification("Workspace deleted.", "success"); loadWorkspaces(); } else { showNotification("Delete failed: " + result.error, "error"); } } catch (error) { showNotification("Delete failed.", "error"); } } // Check Scan Status let statusInterval = null; async function checkScanStatus() { const statusBadge = document.getElementById("scan-status"); const stopBtn = document.getElementById("stop-scan-btn"); const consoleOutput = document.getElementById("console-output"); if (!statusBadge) return; try { const response = await fetch("status.php"); const result = await response.json(); if (result.running) { statusBadge.className = "status-badge status-running"; statusBadge.innerHTML = ' Scan Running'; if (stopBtn) stopBtn.style.display = "inline-flex"; startStatusPolling(); } else { statusBadge.className = "status-badge status-idle"; statusBadge.textContent = "Idle"; if (stopBtn) stopBtn.style.display = "none"; stopStatusPolling(); } // Update console with log content - only if there's actual content if (consoleOutput) { if (result.logContent || result.workspaceOutput) { let output = "UltyScan Console\n"; output += "================\n"; if (result.latestLogFile) { output += `Log: ${result.latestLogFile}\n`; } output += `Time: ${result.timestamp}\n`; output += "----------------\n\n"; if (result.workspaceOutput) { output += result.workspaceOutput; } else if (result.logContent) { output += result.logContent; } consoleOutput.textContent = output; consoleOutput.scrollTop = consoleOutput.scrollHeight; } // Don't clear console if there's no log content - keep existing content } } catch (error) { console.error("Status check failed:", error); } } function startStatusPolling() { if (statusInterval) return; statusInterval = setInterval(checkScanStatus, 5000); } function stopStatusPolling() { if (statusInterval) { clearInterval(statusInterval); statusInterval = null; } } // Console Output function updateConsole(text) { const console = document.getElementById("console-output"); if (console) { console.textContent += "\n$ " + text; console.scrollTop = console.scrollHeight; } } // Notifications function showNotification(message, type = "info") { // Create notification element const notification = document.createElement("div"); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; padding: 1rem 1.5rem; border-radius: 8px; color: white; font-weight: 500; z-index: 9999; animation: slideIn 0.3s ease; max-width: 400px; `; const colors = { success: "var(--accent-success)", error: "var(--accent-danger)", warning: "var(--accent-warning)", info: "var(--accent-primary)", }; notification.style.background = colors[type] || colors.info; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.animation = "slideOut 0.3s ease forwards"; setTimeout(() => notification.remove(), 300); }, 4000); } // Utility function escapeHtml(text) { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } // Add animation keyframes const style = document.createElement("style"); style.textContent = ` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } `; document.head.appendChild(style);