Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 154 additions & 57 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Forensic & Compliance Portal</title>

<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" integrity="sha384-4LISF5TTJX/fLmGSxO53rV4miRxdg84mZsxmO8Rx5jGtp/LbrixFETvWa5a6sESd" crossorigin="anonymous">

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.25/jspdf.plugin.autotable.min.js"></script>
<script src="https://unpkg.com/docx@7.1.0/build/index.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js" integrity="sha384-S3wQ/l0OsbJoFeJC81UIr3JOlx/OzNJpRt1bV+yhpWQxPAahfpQtpxBSfn+Isslc" crossorigin="anonymous"></script>
<!-- js-sha3 provides standards-compliant FIPS 202 SHA-3 (CryptoJS.SHA3 is legacy Keccak, not FIPS 202). -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-sha3/0.8.0/sha3.min.js" integrity="sha384-NKbaJKqJPrFeb86qfU2iNy4KO3oyIe6F5xm1PBYMpAkiVTxHjjafUKlebp0mAeNC" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js" integrity="sha384-JcnsjUPPylna1s1fvi1u12X5qjY5OL56iySh75FdtrwhO/SWXgMjoVqcKyIIWOLk" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.25/jspdf.plugin.autotable.min.js" integrity="sha384-GbQVk5KsTZEMJtvSbtmGpderuiERKpKVVFucxROOKnIXvtg03h3pORjyyGlPD6UO" crossorigin="anonymous"></script>
<script src="https://unpkg.com/docx@7.1.0/build/index.js" integrity="sha384-lGLqE+x3VglMuTcmrdpm32ZSDEh1a93PZ+jHuVZ8jG4DayHt4qNBJtmwL76C855e" crossorigin="anonymous"></script>

<style>
:root {
Expand Down Expand Up @@ -80,6 +82,8 @@
.table-header { background-color: #e9ecef; color: #333; font-weight: 700; border-bottom: 2px solid #ccc; }
.hash-font { font-family: 'Courier New', monospace; font-size: 0.8rem; color: #004085; }
.search-input { border-radius: 4px; padding: 8px 15px; border: 1px solid #ddd; background: white; }
.system-console { background: #0f172a; color: #d1fae5; font-family: 'Courier New', monospace; font-size: 0.8rem; max-height: 120px; overflow-y: auto; }
.system-console-entry { margin-bottom: 4px; }

/* BLOG CARDS */
.news-section { background: #fff; padding: 60px 0; }
Expand Down Expand Up @@ -290,13 +294,25 @@ <h5 class="fw-bold">Drag & Drop Evidence Files Here</h5>
<th width="30%">Filename</th>
<th width="20%">MD5 Hash</th>
<th width="40%">SHA-256 (Section 63 BSA Compliant)</th>
<th>SHA3-256 (FIPS 202)</th>
</tr>
</thead>
<tbody id="hashTableBody" style="background: white;"></tbody>
</table>
</div>
</div>

<!-- SYSTEM CONSOLE -->
<div class="mt-3 border rounded overflow-hidden bg-white">
<div class="table-header px-3 py-2 d-flex justify-content-between align-items-center">
<span>System Console</span>
<button type="button" class="btn btn-sm btn-outline-secondary py-0" onclick="clearSystemConsole()">Clear</button>
</div>
<div id="systemConsole" class="system-console p-3">
<div class="system-console-entry">Ready.</div>
</div>
</div>

</div>
</div>

Expand Down Expand Up @@ -459,14 +475,15 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
</div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>

<script>
const fileInput = document.getElementById('fileInput');
const tableBody = document.getElementById('hashTableBody');
const dropZone = document.getElementById('dropZone');
let fileQueue = [];
let isProcessing = false;
let isProcessing = false;
const MAX_SYSTEM_LOG_ENTRIES = 100;

// FILE INPUT HANDLERS
dropZone.onclick = () => fileInput.click();
Expand Down Expand Up @@ -497,6 +514,7 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
</td>
<td class="hash-font md5">Calculating...</td>
<td class="hash-font sha256">Calculating...</td>
<td class="hash-font sha3">Calculating...</td>
</tr>
`);
fileQueue.push({ file, rowId });
Expand All @@ -513,18 +531,44 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
if (row) row.fileData = file;

const reader = new FileReader();
reader.onload = function(e) {
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();
}
processQueue();
}, 50);
};
reader.readAsArrayBuffer(file);
}
reader.onload = function(e) {
const wordArray = CryptoJS.lib.WordArray.create(e.target.result);
setTimeout(() => {
if (row) {
const md5 = CryptoJS.MD5(wordArray).toString();
const sha256 = CryptoJS.SHA256(wordArray).toString();
const sha3 = sha3_256(e.target.result);
row.dataset.md5 = md5;
row.dataset.sha256 = sha256;
row.dataset.sha3 = sha3;
row.querySelector('.md5').innerText = md5;
row.querySelector('.sha256').innerText = sha256;
row.querySelector('.sha3').innerText = sha3;
addSystemLog(`SHA3-256 calculated for ${file.name}`);
}
processQueue();
}, 50);
};
reader.onerror = () => {
if (row) {
row.querySelector('.md5').innerText = 'Read error';
row.querySelector('.sha256').innerText = 'Read error';
row.querySelector('.sha3').innerText = 'Read error';
}
addSystemLog(`File read failed for ${file.name}`);
processQueue();
};
reader.onabort = () => {
if (row) {
row.querySelector('.md5').innerText = 'Read aborted';
row.querySelector('.sha256').innerText = 'Read aborted';
row.querySelector('.sha3').innerText = 'Read aborted';
}
addSystemLog(`File read aborted for ${file.name}`);
processQueue();
};
reader.readAsArrayBuffer(file);
}

// PARSE EML - EXTRACT METADATA ONLY
function parseEml(text) {
Expand Down Expand Up @@ -697,7 +741,7 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
}

// ===== GENERATE HASH PDF =====
function generateHashPDF() {
async function generateHashPDF() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF('l', 'pt', 'a4');

Expand All @@ -709,16 +753,19 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
doc.text("FORENSIC HASH REPORT", 421, 32, { align: 'center' });

const rows = [];
document.querySelectorAll("#hashTableBody tr").forEach(tr => {
for (const tr of document.querySelectorAll("#hashTableBody tr")) {
if (tr.querySelector('input').checked) {
const cells = tr.querySelectorAll('td');
rows.push([
cells[1].innerText,
cells[2].innerText,
cells[4].innerText
]);
const sha256 = tr.dataset.sha256 || cells[4].innerText;
const sha3 = tr.dataset.sha3 || cells[5].innerText;
rows.push([
cells[1].innerText,
cells[2].innerText,
sha256,
sha3
]);
}
});
}
Comment on lines +756 to +768

if (rows.length === 0) {
alert("Please select files to include in report.");
Expand All @@ -727,7 +774,7 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>

doc.autoTable({
startY: 70,
head: [['#', 'Filename', 'SHA-256']],
head: [['#', 'Filename', 'SHA-256', 'SHA3-256 (FIPS 202)']],
body: rows,
theme: 'grid',
headStyles: {
Expand All @@ -740,17 +787,18 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
font: 'courier'
},
columnStyles: {
0: { cellWidth: 40 },
1: { cellWidth: 300 },
2: { cellWidth: 450, font: 'courier' }
0: { cellWidth: 35 },
1: { cellWidth: 260 },
2: { cellWidth: 260, font: 'courier' },
3: { cellWidth: 260, font: 'courier' }
}
});

doc.save("Hash_Report.pdf", { compress: true });
}

// ===== GENERATE HASH WORD =====
function generateHashWord() {
async function generateHashWord() {
if (typeof docx === 'undefined') {
alert("Word library loading...");
return;
Expand Down Expand Up @@ -782,24 +830,36 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
alignment: AlignmentType.CENTER
})],
shading: { fill: "003366" }
}),
new TableCell({
children: [new Paragraph({
children: [new TextRun({ text: "SHA3-256 (FIPS 202)", bold: true, color: "FFFFFF" })],
alignment: AlignmentType.CENTER
})],
shading: { fill: "003366" }
})
]
}));

document.querySelectorAll("#hashTableBody tr").forEach(tr => {
for (const tr of document.querySelectorAll("#hashTableBody tr")) {
if (tr.querySelector('input').checked) {
const cells = tr.querySelectorAll('td');
rows.push(new TableRow({
children: [
new TableCell({ children: [new Paragraph(cells[1].innerText)] }),
new TableCell({ children: [new Paragraph(cells[2].innerText)] }),
const sha256 = tr.dataset.sha256 || cells[4].innerText;
const sha3 = tr.dataset.sha3 || cells[5].innerText;
rows.push(new TableRow({
children: [
new TableCell({ children: [new Paragraph(cells[1].innerText)] }),
new TableCell({ children: [new Paragraph(cells[2].innerText)] }),
new TableCell({ children: [new Paragraph({
children: [new TextRun({ text: sha256, font: "Courier New", size: 18 })]
})] }),
new TableCell({ children: [new Paragraph({
children: [new TextRun({ text: cells[4].innerText, font: "Courier New", size: 18 })]
children: [new TextRun({ text: sha3, font: "Courier New", size: 18 })]
})] })
]
}));
}
});
}

if (rows.length === 1) {
alert("Please select files to include in report.");
Expand Down Expand Up @@ -876,23 +936,60 @@ <h6 class="fw-bold mt-3">Yahoo/Outlook:</h6>
}

function readFile(file) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = e => resolve(e.target.result);
reader.readAsText(file);
});
}

async function calculateSHA256(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());
};
reader.readAsArrayBuffer(file);
});
}
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = e => resolve(e.target.result);
reader.onerror = () => reject(new Error(`Failed to read ${file.name}`));
reader.onabort = () => reject(new Error(`Read aborted for ${file.name}`));
reader.readAsText(file);
});
}

async function calculateSHA256(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = e => {
const wordArray = CryptoJS.lib.WordArray.create(e.target.result);
resolve(CryptoJS.SHA256(wordArray).toString());
};
reader.onerror = () => reject(new Error(`Failed to calculate SHA-256 for ${file.name}`));
reader.onabort = () => reject(new Error(`SHA-256 calculation aborted for ${file.name}`));
reader.readAsArrayBuffer(file);
});
}

async function calculateSHA3(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = e => {
const sha3 = sha3_256(e.target.result);
addSystemLog(`SHA3-256 calculated for ${file.name}`);
resolve(sha3);
};
reader.onerror = () => reject(new Error(`Failed to calculate SHA3-256 for ${file.name}`));
reader.onabort = () => reject(new Error(`SHA3-256 calculation aborted for ${file.name}`));
reader.readAsArrayBuffer(file);
});
}

function addSystemLog(message) {
const consolePanel = document.getElementById('systemConsole');
if (!consolePanel) return;
const entry = document.createElement('div');
entry.className = 'system-console-entry';
entry.innerText = `[${new Date().toLocaleTimeString()}] ${message}`;
consolePanel.appendChild(entry);
while (consolePanel.children.length > MAX_SYSTEM_LOG_ENTRIES) {
consolePanel.removeChild(consolePanel.firstElementChild);
}
consolePanel.scrollTop = consolePanel.scrollHeight;
}

function clearSystemConsole() {
const consolePanel = document.getElementById('systemConsole');
if (!consolePanel) return;
consolePanel.innerHTML = '<div class="system-console-entry">Ready.</div>';
}

function toggleAll(checkbox) {
document.querySelectorAll('input[name="fileSelect"]').forEach(c => c.checked = checkbox.checked);
Expand Down