const TOOL_ORDER = [ "decomposer", "exiftool", "binwalk", "foremost", "outguess", "pngcheck", "steghide", "zsteg", "strings", ]; function escapeHtml(text) { if (typeof text !== "string") return text; return text .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function capitalize(str) { if (typeof str !== "string" || str.length === 0) return str; return str.charAt(0).toUpperCase() + str.slice(1); } function slideUp(element, duration = 400) { element.style.height = element.offsetHeight + "px"; element.offsetHeight; // force reflow element.style.transitionProperty = "height, margin, padding"; element.style.transitionDuration = duration + "ms"; element.style.boxSizing = "border-box"; element.style.overflow = "hidden"; element.style.height = 0; element.style.paddingTop = 0; element.style.paddingBottom = 0; element.style.marginTop = 0; element.style.marginBottom = 0; window.setTimeout(() => { element.style.display = "none"; // Optional: reset styles element.style.removeProperty("height"); element.style.removeProperty("padding-top"); element.style.removeProperty("padding-bottom"); element.style.removeProperty("margin-top"); element.style.removeProperty("margin-bottom"); element.style.removeProperty("overflow"); element.style.removeProperty("transition-duration"); element.style.removeProperty("transition-property"); }, duration); } function formatBytes(bytes) { if (typeof bytes !== "number" || isNaN(bytes)) return bytes; const units = ["B", "KB", "MB", "GB", "TB", "PB"]; let i = 0; while (bytes >= 1024 && i < units.length - 1) { bytes /= 1024; i++; } return `${bytes % 1 === 0 ? bytes : bytes.toFixed(2)} ${units[i]}`; } /** * Alert functions */ function showAlert(message, alertType, reset) { const resultDiv = document.getElementById("result-analyzers"); if (reset) { resultDiv.innerHTML = ""; } resultDiv.innerHTML += ``; } function showSuccess(message, reset) { showAlert(message, "success", reset); } function showDanger(message, reset) { showAlert(message, "danger", reset); } function showWarning(message, reset) { showAlert(message, "warning", reset); } function showInfo(message, reset) { showAlert(message, "info", reset); } /** * Copy to clipboard */ document.addEventListener('click', function (event) { // Check if the clicked element is a copy icon if (event.target.classList.contains('copy-icon')) { const icon = event.target; const textarea = icon.closest('.textarea-container').querySelector('textarea'); if (textarea) { textarea.select(); navigator.clipboard.writeText(textarea.value).then(() => { icon.classList.remove('fa-copy'); icon.classList.add('fa-check'); setTimeout(() => { icon.classList.remove('fa-check'); icon.classList.add('fa-copy'); }, 1500); }); } } }); /** * Image preview functions */ // Reference the modal elements const modal = document.getElementById("image-modal-container"); const modalImage = document.getElementById("modal-image"); let currentImages = []; let currentIndex = -1; function updateCurrentImages() { currentImages = Array.from(document.querySelectorAll(".results_img img")); } // Function to handle image click (open modal) function openImageModal(src) { updateCurrentImages(); currentIndex = currentImages.findIndex((img) => img.src === src); if (currentIndex !== -1) { modalImage.src = currentImages[currentIndex].src; modal.classList.add("modal-visible"); modal.classList.remove("modal-hidden"); } } function closeImageModal() { modal.classList.remove("modal-visible"); modal.classList.add("modal-hidden"); currentIndex = -1; } // Handle dynamic images added with innerHTML document.addEventListener("click", function (e) { const img = e.target.closest(".results_img img"); if (img) { openImageModal(img.src); } }); // Click on modal image to download and close modalImage.addEventListener("click", function () { const link = document.createElement("a"); link.href = modalImage.src; link.download = modalImage.src.split("/").pop(); document.body.appendChild(link); link.click(); document.body.removeChild(link); closeImageModal(); }); // Optional: Close modal if user clicks outside the image modal.addEventListener("click", function (e) { if (e.target === modal) { closeImageModal(); } }); // Keyboard support document.addEventListener("keydown", function (e) { if (!modal.classList.contains("modal-visible")) return; if (e.key === "Escape") { closeImageModal(); } else if (e.key === "ArrowLeft") { // Show previous image if (currentIndex > 0) { currentIndex--; modalImage.src = currentImages[currentIndex].src; } } else if (e.key === "ArrowRight") { // Show next image if (currentIndex < currentImages.length - 1) { currentIndex++; modalImage.src = currentImages[currentIndex].src; } } }); async function fetchImageInfo(submission_hash) { const infoResp = await fetch(`/infos/${submission_hash}`); const infoData = await infoResp.json(); const resultInfosDiv = document.getElementById("result-infos"); resultInfosDiv.innerHTML = ""; // Clear previous info const mainImgLeft = document.createElement("div"); const mainImgRight = document.createElement("div"); const tableInfos = document.createElement("table"); mainImgLeft.id = "main-img-left"; mainImgRight.id = "main-img-right"; resultInfosDiv.appendChild(mainImgLeft); resultInfosDiv.appendChild(mainImgRight); mainImgRight.appendChild(tableInfos); mainImgLeft.innerHTML += `
`; tableInfos.innerHTML += ` First upload:${infoData.first_submission_date}`; tableInfos.innerHTML += ` Last upload:${infoData.last_submission_date}`; if (Array.isArray(infoData.names)) { const nameList = infoData.names .map((name) => `${escapeHtml(name)}`) .join(", "); tableInfos.innerHTML += ` Name(s):${nameList}`; } tableInfos.innerHTML += ` Size:${formatBytes( infoData.size )}`; tableInfos.innerHTML += ` Upload count:${infoData.upload_count}`; if (Array.isArray(infoData.passwords)) { const passwordList = infoData.passwords .map((pwd) => `${escapeHtml(pwd)}`) .join(", "); tableInfos.innerHTML += ` Common password(s):${passwordList}`; } } /** * Parsing results from the server */ function parseResult(result) { const resultDiv = document.getElementById("result-analyzers"); resultDiv.innerHTML = ""; for (const tool of TOOL_ORDER) { if (!Object.keys(result).includes(tool)) { // Skip if the tool is not in the result continue; } const analyzer = document.createElement("div"); analyzer.className = `analyzer a-${escapeHtml(tool)}`; resultDiv.appendChild(analyzer); analyzer.innerHTML += `

${capitalize(tool)}

`; if (result[tool]["status"] === "ok") { if ("images" in result[tool]) { // Parse image output var channels = ["Superimposed", "Red", "Green", "Blue", "Alpha"]; if (Object.keys(result[tool]["images"]).length == 1) { channels = ["Grayscale"]; } else if (Object.keys(result[tool]["images"]).length == 4) { channels = ["Superimposed", "Red", "Green", "Blue"]; } for (const channel of channels) { const images = result[tool]["images"][channel]; analyzer.innerHTML += `

${capitalize(escapeHtml(channel))}

`; for (const image of images) { analyzer.innerHTML += `
`; } } } // Parse text output if (typeof result[tool]["output"] === "string") { analyzer.innerHTML += `
${escapeHtml(
          result[tool]["output"]
        )}
`; } else if (Array.isArray(result[tool]["output"])) { if (result[tool]["output"].length > 0) { var texarea_content = `
`; texarea_content += ``; texarea_content += ``; texarea_content += `
`; analyzer.innerHTML += texarea_content; } } else if (typeof result[tool]["output"] === "object") { var table_content = `
`; table_content += ``; for (const key in result[tool]["output"]) { table_content += ``; table_content += ``; } table_content += `
${escapeHtml(key)}${escapeHtml( result[tool]["output"][key] )}
`; table_content += ``; analyzer.innerHTML += table_content; } else { // analyzer.innerHTML += `
${result[tool]["output"]}
`; } if ("download" in result[tool]) { // Parse download link analyzer.innerHTML += `
Download file`; } } else if (result[tool]["status"] === "error") { showDanger(result[tool]["error"]); } } } // Poll status of a submission async function pollStatus(submission_hash) { if (!window.location.pathname.endsWith(`/${submission_hash}`)) { window.history.pushState({}, "", `/${submission_hash}`); } const statusResp = await fetch(`/status/${submission_hash}`); const statusData = await statusResp.json(); if (statusData.status === "completed") { const resultResp = await fetch(`/result/${submission_hash}`); const resultData = await resultResp.json(); fetchImageInfo(submission_hash); if ("error" in resultData){ showWarning(`❌ ${resultData.error}`, true); }else if ("results" in resultData) { parseResult(resultData.results); } } else if (statusData.status === "error") { //resultDiv.innerHTML = ""; showDanger("❌ Error during the analysis.", true); } else { setTimeout(() => pollStatus(submission_hash), 1000); try { const resultResp = await fetch(`/result/${submission_hash}`); const resultData = await resultResp.json(); if ("error" in resultData){ // showWarning(`❌ ${resultData.error}`, true); }else if ("results" in resultData) { fetchImageInfo(submission_hash); parseResult(resultData.results); } } catch (error) { console.dir(error); } } } /** * Drag and drop functionality */ const dropZone = document.getElementById("drop-zone"); const fileInput = document.getElementById("image"); const browseBtn = document.getElementById("browse-btn"); const dragMsg = document.getElementById("drag-msg"); function showFilename(filename) { dragMsg.innerHTML = "🖼️ " + escapeHtml(filename); } if (browseBtn) { // Clicking the button triggers file input browseBtn.addEventListener("click", () => fileInput.click()); dropZone.addEventListener("click", () => fileInput.click()); dropZone.addEventListener("dragover", (e) => { e.preventDefault(); dropZone.style.boxShadow = "0 0 20px #9fef00"; }); dropZone.addEventListener("dragleave", () => { dropZone.style.boxShadow = ""; }); // Drag events ["dragenter", "dragover"].forEach((eventName) => { dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.add("dragover"); }); }); ["dragleave", "drop"].forEach((eventName) => { dropZone.addEventListener(eventName, (e) => { e.preventDefault(); dropZone.classList.remove("dragover"); }); }); // Drop file dropZone.addEventListener("drop", (e) => { const files = e.dataTransfer.files; if (files.length > 0) { fileInput.files = files; // Attach files to the hidden input showFilename(fileInput.files[0].name); } }); fileInput.addEventListener("change", () => { if (fileInput.files.length > 0) { showFilename(fileInput.files[0].name); } }); // File upload Form submission document .getElementById("upload-form") .addEventListener("submit", function (e) { e.preventDefault(); const fileInput = document.getElementById("image"); const passwordInput = document.getElementById("password"); const deepCheckbox = document.getElementById("deep"); const progressContainer = document.getElementById("progress-container"); const progressBar = document.getElementById("progress-bar"); if (!fileInput.files.length) { showDanger("Please select an image.", true); } const formData = new FormData(); formData.append("image", fileInput.files[0]); formData.append("password", passwordInput.value); formData.append("deep", deepCheckbox.checked); const xhr = new XMLHttpRequest(); xhr.open("POST", "/upload", true); xhr.upload.addEventListener("loadstart", () => { progressContainer.classList.remove("d-none"); progressBar.style.width = "0%"; progressBar.textContent = "0%"; }); xhr.upload.addEventListener("progress", (e) => { if (e.lengthComputable) { const percent = Math.round((e.loaded / e.total) * 100); progressBar.style.width = percent + "%"; progressBar.textContent = percent + "%"; } }); xhr.onload = () => { if (xhr.status === 200) { try { const response = JSON.parse(xhr.responseText); if (response.submission_hash) { pollStatus(response.submission_hash); } else { showDanger( "❌ Invalid server response: missing submission_hash.", true ); } } catch (e) { showDanger("❌ Invalid server response.", true); } } else { showDanger(`❌ HTTP error ${xhr.status}`, true); } progressBar.style.width = "100%"; progressBar.textContent = "Upload complete"; const uploadForm = document.getElementById("upload-form"); slideUp(uploadForm); }; xhr.onerror = () => { showDanger("❌ An error occurred during the transfer", true); }; xhr.send(formData); }); }