Now that the web crypto API is widespread, librejs doesn't need to depend on jssha for SHA-512 hashing. Since web crypto is async, I've propagated async-ness through the call stack for the hash function.
I'm looking to package librejs for Debian, and since jssha isn't already packaged for Debian removing the dependency makes the packaging work easier. --- README | 2 +- bg/ListManager.js | 4 ++-- common/Storage.js | 30 +++++++++++++++++++----------- docs/librejs.texi | 2 +- main_background.js | 12 ++++++------ package.json | 9 +++++++++ 6 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 package.json diff --git a/README b/README index 7c488cf..6efccf4 100644 --- a/README +++ b/README @@ -4,7 +4,7 @@ BUILD: First, you need to get some stuff with npm: - $ npm install acorn jssha browserify + $ npm install $ export PATH=$PATH:./node_modules/.bin To build the extension run: diff --git a/bg/ListManager.js b/bg/ListManager.js index 2800c3f..4c45c2a 100644 --- a/bg/ListManager.js +++ b/bg/ListManager.js @@ -49,9 +49,9 @@ class ListManager { with a trailing (hash). Returns "blacklisted", "whitelisted" or defValue */ - getStatus(key, defValue = 'unknown') { + async getStatus(key, defValue = 'unknown') { const { blacklist, whitelist } = this.lists; - const inline = ListStore.inlineItem(key); + const inline = await ListStore.inlineItem(key); if (inline) { return blacklist.contains(inline) ? 'blacklisted' diff --git a/common/Storage.js b/common/Storage.js index 0d27c01..398dbb4 100644 --- a/common/Storage.js +++ b/common/Storage.js @@ -67,12 +67,19 @@ class ListStore { }); } - static inlineItem(url) { + static async inlineItem(url) { // here we simplify and hash inline script references - return url.startsWith('inline:') ? url - : url.startsWith('view-source:') - && url.replace(/^view-source:[\w-+]+:\/+([^/]+).*#line\d+/, 'inline://$1#') - .replace(/\n[^]*/, s => s.replace(/\s+/g, ' ').substring(0, 16) + '…' + hash(s.trim())); + if (url.startsWith('inline:')) { + return url; + } else if (url.startsWith('view-source:')) { + const replaced = url.replace(/^view-source:[\w-+]+:\/+([^/]+).*#line\d+/, 'inline://$1#'); + const lines = replaced.split('\n'); + for (let i = 1; i < lines.length; i++) { + lines[i] = lines[i].replace(/\s+/g, ' ').substring(0, 16) + '…' + await hash(s.trim()); + } + } else { + return false; + } } static hashItem(hash) { return hash.startsWith('(') ? hash : `(${hash})`; @@ -124,14 +131,15 @@ class ListStore { } } -function hash(source) { - const shaObj = new jssha('SHA-256', 'TEXT') - shaObj.update(source); - return shaObj.getHash('HEX'); +async function hash(source) { + const encoder = new TextEncoder(); + const data = encoder.encode(source); + const hashBuffer = await crypto.subtle.digest('SHA-256', data); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); + return hashHex; } if (typeof module === 'object') { module.exports = { ListStore, Storage, hash }; - // TODO: eliminate the var - var jssha = require('jssha'); } diff --git a/docs/librejs.texi b/docs/librejs.texi index c3e7ca7..15f665c 100644 --- a/docs/librejs.texi +++ b/docs/librejs.texi @@ -343,7 +343,7 @@ LibreJS @value{VERSION} depends on a number of Node.js-based libraries that can be installed using the @code{npm} utility: @verbatim - $ npm install acorn jssha browserify + $ npm install $ export PATH=$PATH:./node_modules/.bin @end verbatim diff --git a/main_background.js b/main_background.js index 87b80ae..40407df 100644 --- a/main_background.js +++ b/main_background.js @@ -66,7 +66,7 @@ async function createReport(initializer) { let [url] = (template.url || (await browser.tabs.get(initializer.tabId)).url).split('#'); template.url = url; template.site = ListStore.siteItem(url); - template.siteStatus = listManager.getStatus(template.site); + template.siteStatus = await listManager.getStatus(template.site); const list = { 'whitelisted': whitelist, 'blacklisted': blacklist }[template.siteStatus]; if (list) { template.listedSite = ListManager.siteMatch(template.site, list); @@ -133,7 +133,7 @@ async function updateReport(tabId, oldReport, updateUI = false) { if (!Array.isArray(entries)) continue; const defValue = property === 'accepted' || property === 'blocked' ? property : 'unknown'; for (const script of entries) { - const status = listManager.getStatus(script[0], defValue); + const status = await listManager.getStatus(script[0], defValue); if (Array.isArray(newReport[status])) newReport[status].push(script); } } @@ -190,7 +190,7 @@ async function addReportEntry(tabId, action) { let entryType; // Update the report if the scriptName is new for the entryType. try { - entryType = listManager.getStatus(scriptName, actionType); + entryType = await listManager.getStatus(scriptName, actionType); const entries = report[entryType]; if (!entries.find(e => e[0] === scriptName)) { dbgPrint(activityReports); @@ -257,7 +257,7 @@ async function connected(p) { if (m.site) { key = ListStore.siteItem(m.site); } else { - key = ListStore.inlineItem(key) || key; + key = (await ListStore.inlineItem(key)) || key; } await listManager[action](key); update = true; @@ -389,7 +389,7 @@ async function checkScriptAndUpdateReport(scriptSrc, url, tabId, whitelisted, is return `/* LibreJS: script whitelisted by user preference. */\n${scriptSrc}`; } - const [accepted, editedSource, reason] = listManager.builtInHashes.has(hash(scriptSrc)) ? [true, scriptSrc, 'Common script known to be free software.'] : checkLib.checkScriptSource(scriptSrc, scriptName, isExternal); + const [accepted, editedSource, reason] = listManager.builtInHashes.has(await hash(scriptSrc)) ? [true, scriptSrc, 'Common script known to be free software.'] : checkLib.checkScriptSource(scriptSrc, scriptName, isExternal); if (tabId < 0) { return editedSource; @@ -450,7 +450,7 @@ function blockGoogleAnalytics(request) { async function blockBlacklistedScripts(request) { const { tabId, documentUrl } = request; const url = ListStore.urlItem(request.url); - const status = listManager.getStatus(url); + const status = await listManager.getStatus(url); if (status !== 'blacklisted') return {}; const blacklistedSite = ListManager.siteMatch(url, blacklist); await addReportEntry(tabId, { diff --git a/package.json b/package.json new file mode 100644 index 0000000..e385007 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name": "librejs", + "dependencies": { + "acorn": "^8.12.1" + }, + "devDependencies": { + "browserify": "^17.0.0" + } +} -- 2.43.0