Skip to content
Open
Show file tree
Hide file tree
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
242 changes: 205 additions & 37 deletions background.js
Original file line number Diff line number Diff line change
@@ -1,70 +1,238 @@
async function calculateHash(file, algorithm = 'SHA-256') {
const fileBuffer = await file.arrayBuffer();
const hashBuffer = await crypto.subtle.digest(algorithm, fileBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
importScripts('spark-md5.min.js');

function arrayBufferToHexString(buffer) {
const byteArray = new Uint8Array(buffer);
const hexCodes = [...byteArray].map((value) => {
const hexCode = value.toString(16);
return hexCode.padStart(2, '0');
});
return hexCodes.join('');
}

async function getFileContent(downloadUrl) {
const response = await fetch(downloadUrl);
return await response.blob();
async function getFileContent(url) {
const response = await fetch(url);
const data = await response.blob();
return data;
}

const downloadFileHashes = new Map();
async function calculateMD5(blob) {
console.log('calculateMD5 started');
return new Promise((resolve) => {
const fileReader = new FileReader();
fileReader.onload = (event) => {
const arrayBuffer = event.target.result;
const md5Hash = SparkMD5.ArrayBuffer.hash(arrayBuffer);
resolve(md5Hash);
};
fileReader.readAsArrayBuffer(blob);
});
}

async function onChangedListener(delta) {
if (delta.state && delta.state.current === 'complete') {
const fileHash = downloadFileHashes.get(delta.id);
async function calculateSHA1(blob) {
console.log('calculateSHA1 started');
const buffer = await blob.arrayBuffer();
const hash = await crypto.subtle.digest('SHA-1', buffer);
return arrayBufferToHexString(hash);
}

async function calculateSHA256(blob) {
console.log('calculateSHA256 started');
const buffer = await blob.arrayBuffer();
const hash = await crypto.subtle.digest('SHA-256', buffer);
return arrayBufferToHexString(hash);
}

async function calculateSHA512(blob) {
console.log('calculateSHA512 started');
const buffer = await blob.arrayBuffer();
const hash = await crypto.subtle.digest('SHA-512', buffer);
return arrayBufferToHexString(hash);
}

const dbName = 'fileHashesDB';
const objectStoreName = 'fileHashes';
let db;

function initIndexedDB() {
const request = indexedDB.open(dbName);

request.onupgradeneeded = (event) => {
db = event.target.result;
if (!db.objectStoreNames.contains(objectStoreName)) {
db.createObjectStore(objectStoreName, { keyPath: 'id' });
}
};

request.onsuccess = (event) => {
db = event.target.result;
};

request.onerror = (event) => {
console.error('Error opening indexedDB:', event.target.errorCode);
};
}

async function getFileHashFromStorage(id) {
return new Promise((resolve, reject) => {
const transaction = db.transaction([objectStoreName]);
const objectStore = transaction.objectStore(objectStoreName);
const request = objectStore.get(id);

request.onsuccess = () => {
resolve(request.result);
};

request.onerror = () => {
reject(request.error);
};
});
}

async function removeFileHashFromStorage(id) {
return new Promise((resolve, reject) => {
const transaction = db.transaction([objectStoreName], 'readwrite');
const objectStore = transaction.objectStore(objectStoreName);
const request = objectStore.delete(id);

request.onsuccess = () => {
resolve();
};

request.onerror = () => {
reject(request.error);
};
});
}

initIndexedDB();

if (fileHash) {
chrome.downloads.onChanged.addListener(async (delta) => {
if (delta.state && delta.state.current === 'complete') {
const storageData = await getFileHashFromStorage(delta.id);
if (storageData) {
const allHashes = storageData.allHashes;
chrome.downloads.search({ id: delta.id }, async ([download]) => {
const fileBlob = await getFileContent(download.url);
const calculatedHash = await calculateHash(fileBlob);
console.log('File blob:', fileBlob);
const calculatedHashes = {
md5: await calculateMD5(fileBlob),
sha1: await calculateSHA1(fileBlob),
sha256: await calculateSHA256(fileBlob),
sha512: await calculateSHA512(fileBlob)
};
console.log('Calculated hash:', calculatedHashes);

let isValidHash = false;
let hashType = '';

for (const [type, calculatedHash] of Object.entries(calculatedHashes)) {
if (allHashes[type] && Array.from(allHashes[type]).some((hash) => hash.toLowerCase() === calculatedHash.toLowerCase())) {
isValidHash = true;
hashType = type.toUpperCase();
console.log('Valid hash:', calculatedHash);
break;
}
console.log('Invalid hash:', calculatedHash);
}

let title, message;
let icon = 'images/valid.png'
if (calculatedHash.toLowerCase() === fileHash.toLowerCase()) {

if (isValidHash) {
title = 'Valid Hash';
message = 'The file hash matches the published hash.';
message = `The file hash matches one of the published ${hashType} hashes.`;
} else {
title = 'Invalid Hash';
message = 'The file hash does not match the published hash.';
icon = 'images/invalid.png'
message = 'The file hash does not match any of the published hashes.';
}
chrome.notifications.create({
type: 'basic',
iconUrl: icon,
iconUrl: 'images/icon128.png',
title: title,
message: message
});

// Remove the hash from the Map
downloadFileHashes.delete(delta.id);
// Remove the hash from the storage
await removeFileHashFromStorage(delta.id);
});
}
}
}

chrome.downloads.onDeterminingFilename.addListener((downloadItem, suggest) => {
const fileHash = downloadFileHashes.get(downloadItem.id);

if (fileHash) {
const url = new URL(downloadItem.url);
const filename = url.pathname.split('/').pop();
suggest({ filename: filename });
}
});

chrome.downloads.onChanged.addListener(onChangedListener);

chrome.runtime.onConnect.addListener((port) => {
console.log('Connection established with content script'); // Add this line
if (port.name === 'fileHash') {
port.onMessage.addListener((message) => {
if (message.type === 'fileHashFound') {
const fileHash = message.fileHash;
if (message.type === 'fileHashesFound') {
console.log('Received message from content script:', message); // Add this line
const allHashes = message.allHashes;
chrome.downloads.download({ url: message.url }, (downloadId) => {
downloadFileHashes.set(downloadId, fileHash);
console.log('Download triggered with ID:', downloadId); // Add this line
const transaction = db.transaction([objectStoreName], 'readwrite');
const objectStore = transaction.objectStore(objectStoreName);
const request = objectStore.add({ id: downloadId, allHashes: allHashes });

request.onsuccess = () => {
console.log('Hashes stored for download ID:', downloadId);
};

request.onerror = () => {
console.error('Error storing hashes for download ID:', downloadId);
};
});
}
});
}
});

function injectContentScript() {
chrome.tabs.executeScript({
code: `
function findAllHashes() {
const hashPatterns = {
md5: /\b[A-Fa-f0-9]{32}\b/g,
sha1: /\b[A-Fa-f0-9]{40}\b/g,
sha256: /\b[A-Fa-f0-9]{64}\b/g,
sha512: /\b[A-Fa-f0-9]{128}\b/g
};

const pageText = document.body.innerText;
const allHashes = {};

for (const [hashType, pattern] of Object.entries(hashPatterns)) {
const matches = pageText.match(pattern);
if (matches) {
allHashes[hashType] = new Set(matches);
}
}

return allHashes;
}

document.addEventListener('click', (event) => {
console.log("Click event captured"); // Add this line
const downloadLink = event.target.closest('a');

if (downloadLink) {
console.log("Clicked download link");
event.preventDefault();
const url = downloadLink.href;
const allHashes = findAllHashes();
console.log('All hashes found on the page:', allHashes);
const port = chrome.runtime.connect({ name: 'fileHash' });

if (port) {
console.log('Connected to background script'); // Add this line
} else {
console.log('Connection to background script failed'); // Add this line
}

port.postMessage({ type: 'fileHashesFound', allHashes, url });
port.disconnect();
}
});
`,
});
}

// Inject the content script when the extension is loaded
injectContentScript();
47 changes: 40 additions & 7 deletions contentScript.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,44 @@
document.addEventListener('click', (event) => {
if (event.target.tagName === 'A' && event.target.hasAttribute('data-file-hash')) {
function findAllHashes() {
const hashPatterns = {
md5: /\b[A-Fa-f0-9]{32}\b/g,
sha1: /\b[A-Fa-f0-9]{40}\b/g,
sha256: /\b[A-Fa-f0-9]{64}\b/g,
sha512: /\b[A-Fa-f0-9]{128}\b/g
};

const pageText = document.body.innerText;
const allHashes = {};

for (const [hashType, pattern] of Object.entries(hashPatterns)) {
const matches = pageText.match(pattern);
if (matches) {
allHashes[hashType] = new Set(matches);
}
}

return allHashes;
}

document.addEventListener('click', (event) => {
console.log("Click event captured"); // Add this line
const downloadLink = event.target.closest('a');

if (downloadLink) {
console.log("Clicked download link");
event.preventDefault();
const fileHash = event.target.getAttribute('data-file-hash');
const url = event.target.href;
console.log('File hash from the page:', fileHash);
const url = downloadLink.href;
const allHashes = findAllHashes();
console.log('All hashes found on the page:', allHashes);
const port = chrome.runtime.connect({ name: 'fileHash' });
port.postMessage({ type: 'fileHashFound', fileHash, url });

if (port) {
console.log('Connected to background script'); // Add this line
} else {
console.log('Connection to background script failed'); // Add this line
}

port.postMessage({ type: 'fileHashesFound', allHashes, url });
port.disconnect();
}
});
});

9 changes: 9 additions & 0 deletions contentScriptInjector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'injectContentScript') {
const script = document.createElement('script');
script.src = chrome.runtime.getURL('contentScript.js');
document.body.appendChild(script);
sendResponse({ success: true });
}
});

9 changes: 8 additions & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["contentScript.js"]
"js": ["contentScriptInjector.js"]
}
],
"web_accessible_resources": [
{
"resources": ["spark-md5.min.js"],
"matches": ["<all_urls>"],
"use_dynamic_url": true
}
],
"icons": {
Expand Down
10 changes: 5 additions & 5 deletions popup.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="popup.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Hash Checker Popup</title>
</head>
<body>
<h1>File Hash Checker</h1>
<p id="status"></p>
<button id="inject-content-script">Inject Content Script</button>
<script src="popup.js"></script>
</body>
</html>
9 changes: 8 additions & 1 deletion popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,11 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
statusElement.textContent = message.status;
}
});


document.getElementById('inject-content-script').addEventListener('click', () => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, { type: 'injectContentScript' }, (response) => {
console.log(response);
});
});
});
Loading