MetaGuardSOT/static/script.js

244 lines
7.2 KiB
JavaScript

let selectedFiles = [];
let scanResults = [];
let selectedIndex = -1;
const fileInput = document.getElementById('fileInput');
const uploadInfo = document.getElementById('uploadInfo');
const resultsBody = document.getElementById('resultsBody');
const detailsContent = document.getElementById('detailsContent');
const emptyState = document.getElementById('emptyState');
const btnScan = document.getElementById('btnScan');
const btnSanitize = document.getElementById('btnSanitize');
const statusText = document.getElementById('statusText');
const dropZone = document.getElementById('dropZone');
fileInput.addEventListener('change', handleFileSelect);
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('drag-over');
if (e.dataTransfer.files.length) {
fileInput.files = e.dataTransfer.files;
handleFileSelect();
}
});
function handleFileSelect() {
selectedFiles = Array.from(fileInput.files);
if (selectedFiles.length === 0) {
uploadInfo.textContent = 'No files selected';
} else if (selectedFiles.length === 1) {
uploadInfo.textContent = selectedFiles[0].name;
} else {
uploadInfo.textContent = `${selectedFiles.length} files selected`;
}
}
async function uploadFiles() {
if (!selectedFiles.length) return false;
setStatus('Uploading files...');
const formData = new FormData();
for (const file of selectedFiles) {
formData.append('files', file);
}
try {
const resp = await fetch('/api/upload', { method: 'POST', body: formData });
const data = await resp.json();
if (data.error) {
setStatus('Upload failed: ' + data.error);
return false;
}
setStatus(`Uploaded ${data.count} file(s)`);
return true;
} catch (e) {
setStatus('Upload failed: ' + e.message);
return false;
}
}
async function startScan() {
const context = document.getElementById('context').value;
const recursive = document.getElementById('recursive').checked;
const uploaded = await uploadFiles();
if (!uploaded && !selectedFiles.length) {
setStatus('No files to scan. Select files first.');
return;
}
btnScan.disabled = true;
btnScan.textContent = 'Scanning...';
setStatus('Scanning...');
try {
const resp = await fetch('/api/scan', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ target: '', recursive, context })
});
const data = await resp.json();
if (data.error) {
setStatus('Scan failed: ' + data.error);
return;
}
scanResults = data.results;
selectedIndex = -1;
renderTable();
setStatus(`Scan complete: ${data.count} file(s) analyzed`);
} catch (e) {
setStatus('Scan failed: ' + e.message);
} finally {
btnScan.disabled = false;
btnScan.textContent = 'Scan';
}
}
function renderTable() {
resultsBody.innerHTML = '';
emptyState.style.display = scanResults.length ? 'none' : 'block';
scanResults.forEach((r, i) => {
const tr = document.createElement('tr');
tr.dataset.index = i;
const author = r.metadata.author || r.metadata.last_modified_by || '';
tr.innerHTML = `
<td class="level-${r.level}">${r.level}</td>
<td class="level-${r.level}">${r.total_risk}</td>
<td>${r.file_type}</td>
<td class="path-cell" title="${escapeHtml(r.path)}">${escapeHtml(r.path)}</td>
<td>${escapeHtml(author)}</td>
`;
tr.addEventListener('click', () => selectRow(i));
resultsBody.appendChild(tr);
});
}
function selectRow(index) {
selectedIndex = index;
const r = scanResults[index];
document.querySelectorAll('#resultsBody tr').forEach(tr => {
tr.classList.toggle('selected', parseInt(tr.dataset.index) === index);
});
btnSanitize.disabled = r.file_type !== 'image';
let html = '';
html += `<span class="level-${r.level}"><b>${r.level}</b> (Score: ${r.total_risk})</span>\n\n`;
html += `<b>Path:</b> ${escapeHtml(r.path)}\n`;
html += `<b>Type:</b> ${r.file_type}\n`;
html += `<b>Size:</b> ${formatSize(r.size_bytes)}\n`;
html += `<b>SHA-256:</b> ${r.sha256}\n`;
html += `<b>Context:</b> ${r.context}\n\n`;
html += `<b>Metadata:</b>\n`;
for (const [k, v] of Object.entries(r.metadata)) {
if (k === 'all_exif_tags') continue;
if (typeof v === 'object' && v !== null) {
html += ` ${k}: ${JSON.stringify(v)}\n`;
} else {
html += ` ${k}: ${v}\n`;
}
}
if (r.findings && r.findings.length) {
html += `\n<b>Findings:</b>\n`;
r.findings.forEach(f => {
html += ` <span class="finding-${f.severity}">${f.severity}</span> | ${f.name} | ${f.evidence}\n`;
});
}
detailsContent.innerHTML = html;
}
async function exportReport(fmt) {
if (!scanResults.length) {
setStatus('No results to export. Run scan first.');
return;
}
setStatus(`Exporting ${fmt.toUpperCase()}...`);
try {
const resp = await fetch(`/api/export/${fmt}`);
if (!resp.ok) {
const data = await resp.json();
setStatus('Export failed: ' + (data.error || resp.statusText));
return;
}
const blob = await resp.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `report.${fmt}`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
setStatus(`Exported ${fmt.toUpperCase()} successfully`);
} catch (e) {
setStatus('Export failed: ' + e.message);
}
}
async function sanitizeFile() {
if (selectedIndex < 0) {
setStatus('Select a file first.');
return;
}
const r = scanResults[selectedIndex];
if (r.file_type !== 'image') {
setStatus('Sanitization supported only for image files.');
return;
}
setStatus('Sanitizing...');
try {
const resp = await fetch(`/api/sanitize/${selectedIndex}`, { method: 'POST' });
const data = await resp.json();
if (data.error) {
setStatus('Sanitization failed: ' + data.error);
} else {
setStatus(`Sanitized copy created: ${data.sanitized_path}`);
}
} catch (e) {
setStatus('Sanitization failed: ' + e.message);
}
}
function setStatus(msg) {
statusText.textContent = msg;
}
function formatSize(bytes) {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / 1048576).toFixed(1) + ' MB';
}
function escapeHtml(s) {
if (!s) return '';
return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}