diff --git a/index.html b/index.html index d276a17..71feaab 100644 --- a/index.html +++ b/index.html @@ -242,6 +242,11 @@

Evidence Validation Suite

CSV Report +
+ +
+
+
+ System Console +
+
+ Ready for client-side evidence hashing. +
+
+ @@ -486,17 +501,19 @@
Yahoo/Outlook:
const rowId = `row-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`; const isEml = file.name.toLowerCase().endsWith('.eml'); const rowCount = tableBody.rows.length + 1; + const safeFileName = escapeHtml(file.name); tableBody.insertAdjacentHTML('beforeend', ` ${rowCount} - ${file.name} + ${safeFileName} ${isEml ? 'EMAIL' : ''} Calculating... Calculating... + Calculating... `); fileQueue.push({ file, rowId }); @@ -517,8 +534,11 @@
Yahoo/Outlook:
const wordArray = CryptoJS.lib.WordArray.create(e.target.result); setTimeout(() => { if (row) { - row.querySelector('.md5').innerText = CryptoJS.MD5(wordArray).toString(); - row.querySelector('.sha256').innerText = CryptoJS.SHA256(wordArray).toString(); + const sha3 = CryptoJS.SHA3(wordArray, { outputLength: 256 }).toString(); + row.querySelector('.md5').innerText = CryptoJS.MD5(wordArray).toString(); + row.querySelector('.sha256').innerText = CryptoJS.SHA256(wordArray).toString(); + row.querySelector('.sha3').innerText = sha3; + logSystemMessage(`SHA-3 / Keccak-256 for ${file.name}: ${sha3}`); } processQueue(); }, 50); @@ -612,7 +632,7 @@
Yahoo/Outlook:
const text = await readFile(files[i]); const emailData = parseEml(text); - const sha256 = await calculateSHA256(files[i]); + const hashes = await calculateHashValues(files[i]); // HEADER WITH WRAPPED FILENAME doc.setFillColor(0, 51, 102); @@ -646,7 +666,8 @@
Yahoo/Outlook:
['Message-ID', emailData.messageId], ['Originating IP', emailData.serverIP], ['DKIM Status', emailData.dkim], - ['SHA-256 Hash', sha256] + ['SHA-256 Hash', hashes.sha256], + ['SHA-3 / Keccak-256 Hash', hashes.sha3] ], theme: 'grid', headStyles: { @@ -715,7 +736,9 @@
Yahoo/Outlook:
rows.push([ cells[1].innerText, cells[2].innerText, - cells[4].innerText + cells[3].innerText, + cells[4].innerText, + cells[5].innerText ]); } }); @@ -727,7 +750,7 @@
Yahoo/Outlook:
doc.autoTable({ startY: 70, - head: [['#', 'Filename', 'SHA-256']], + head: [['#', 'Filename', 'MD5', 'SHA-256', 'SHA-3 / Keccak-256']], body: rows, theme: 'grid', headStyles: { @@ -736,13 +759,15 @@
Yahoo/Outlook:
fontStyle: 'bold' }, bodyStyles: { - fontSize: 9, + fontSize: 7, font: 'courier' }, columnStyles: { - 0: { cellWidth: 40 }, - 1: { cellWidth: 300 }, - 2: { cellWidth: 450, font: 'courier' } + 0: { cellWidth: 35 }, + 1: { cellWidth: 170 }, + 2: { cellWidth: 130, font: 'courier' }, + 3: { cellWidth: 210, font: 'courier' }, + 4: { cellWidth: 210, font: 'courier' } } }); @@ -782,6 +807,13 @@
Yahoo/Outlook:
alignment: AlignmentType.CENTER })], shading: { fill: "003366" } + }), + new TableCell({ + children: [new Paragraph({ + children: [new TextRun({ text: "SHA-3 / Keccak-256", bold: true, color: "FFFFFF" })], + alignment: AlignmentType.CENTER + })], + shading: { fill: "003366" } }) ] })); @@ -795,6 +827,9 @@
Yahoo/Outlook:
new TableCell({ children: [new Paragraph(cells[2].innerText)] }), new TableCell({ children: [new Paragraph({ children: [new TextRun({ text: cells[4].innerText, font: "Courier New", size: 18 })] + })] }), + new TableCell({ children: [new Paragraph({ + children: [new TextRun({ text: cells[5].innerText, font: "Courier New", size: 18 })] })] }) ] })); @@ -837,33 +872,86 @@
Yahoo/Outlook:
// ===== DOWNLOAD CSV ===== function downloadSelectedCSV() { - let csv = "#,Subject,From,To,Date,Originating IP,DKIM,SHA-256\n"; - - let counter = 1; + let csv = "#,Filename,MD5,SHA-256,SHA-3 / Keccak-256,Subject,From,To,Date,Originating IP,DKIM\n"; const promises = []; document.querySelectorAll("#hashTableBody tr").forEach(tr => { - if (tr.querySelector('input').checked && tr.fileData && tr.fileData.name.toLowerCase().endsWith('.eml')) { + if (tr.querySelector('input').checked && tr.fileData) { const cells = tr.querySelectorAll('td'); - promises.push( - readFile(tr.fileData).then(text => { - const eml = parseEml(text); - return `${counter++},"${eml.subject}","${eml.from}","${eml.to}","${eml.date}","${eml.serverIP}","${eml.dkim}","${cells[4].innerText}"\n`; - }) - ); + const base = [ + cells[1].innerText, + tr.fileData.name, + cells[3].innerText, + cells[4].innerText, + cells[5].innerText + ]; + + if (tr.fileData.name.toLowerCase().endsWith('.eml')) { + promises.push( + readFile(tr.fileData).then(text => { + const eml = parseEml(text); + return toCsvRow([...base, eml.subject, eml.from, eml.to, eml.date, eml.serverIP, eml.dkim]); + }) + ); + } else { + promises.push(Promise.resolve(toCsvRow([...base, "", "", "", "", "", ""]))); + } } }); + if (promises.length === 0) { + alert("Please select files to include in report."); + return; + } + Promise.all(promises).then(rows => { csv += rows.join(''); const blob = new Blob([csv], { type: 'text/csv' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); - a.download = "Email_Report.csv"; + a.download = "Hash_Report.csv"; a.click(); }); } + // ===== DOWNLOAD EXCEL ===== + function downloadSelectedExcel() { + const rows = [["#", "Filename", "MD5", "SHA-256", "SHA-3 / Keccak-256"]]; + document.querySelectorAll("#hashTableBody tr").forEach(tr => { + if (tr.querySelector('input').checked && tr.fileData) { + const cells = tr.querySelectorAll('td'); + rows.push([ + cells[1].innerText, + tr.fileData.name, + cells[3].innerText, + cells[4].innerText, + cells[5].innerText + ]); + } + }); + + if (rows.length === 1) { + alert("Please select files to include in report."); + return; + } + + const htmlRows = rows.map((row, rowIndex) => { + const tag = rowIndex === 0 ? "th" : "td"; + return `${row.map(cell => `<${tag}>${escapeHtml(String(cell))}`).join("")}`; + }).join(""); + const worksheet = ` + + + ${htmlRows}
+ + `; + const blob = new Blob([worksheet], { type: "application/vnd.ms-excel" }); + const a = document.createElement("a"); + a.href = URL.createObjectURL(blob); + a.download = "Hash_Report.xls"; + a.click(); + } + // ===== UTILITY FUNCTIONS ===== function getSelectedEmlFiles() { const files = []; @@ -883,17 +971,41 @@
Yahoo/Outlook:
}); } - async function calculateSHA256(file) { + async function calculateHashValues(file) { return new Promise(resolve => { const reader = new FileReader(); reader.onload = e => { const wordArray = CryptoJS.lib.WordArray.create(e.target.result); - resolve(CryptoJS.SHA256(wordArray).toString()); + resolve({ + sha256: CryptoJS.SHA256(wordArray).toString(), + sha3: CryptoJS.SHA3(wordArray, { outputLength: 256 }).toString() + }); }; reader.readAsArrayBuffer(file); }); } + function toCsvRow(values) { + return values.map(value => `"${String(value).replace(/"/g, '""')}"`).join(",") + "\n"; + } + + function escapeHtml(value) { + return value + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + function logSystemMessage(message) { + const systemConsole = document.getElementById("systemConsole"); + const timestamp = new Date().toLocaleTimeString(); + systemConsole.insertAdjacentHTML("beforeend", `
[${timestamp}] ${escapeHtml(message)}
`); + systemConsole.scrollTop = systemConsole.scrollHeight; + console.info(message); + } + function toggleAll(checkbox) { document.querySelectorAll('input[name="fileSelect"]').forEach(c => c.checked = checkbox.checked); }