/* * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript. * * Copyright (C) 2018 Giorgio Maone * Copyright (C) 2022 Yuchen Pei * * This file is part of GNU LibreJS. * * GNU LibreJS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GNU LibreJS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU LibreJS. If not, see . */ 'use strict'; describe('LibreJS\' components', () => { let LibreJS = browser.extension.getBackgroundPage().LibreJS; let license = { id: 'GPL-3.0', url: 'http://www.gnu.org/licenses/gpl-3.0.html', magnet: 'magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt', }; let unknownLicense = { id: 'Acme-proprietary-1.5', url: 'http://www.acme.com/license-1.5.html', magnet: 'magnet:?xt=urn:btih:2f739d935676111cfff4b4693e3816e664797050&dn=acme-1.5.txt' }; let trivial = '1+1'; let nontrivial = 'function nt() { document.documentElement.innerHTML=""; nt(); }'; // code calling reserved object is nontrivial const nontrivialCall = 'eval();'; // code calling anything else is trivial const trivialCall = 'foo();'; let licensed = `// @license ${license.magnet} ${license.id}\n${nontrivial}\n// @license-end`; let unknownLicensed = `// @license ${unknownLicense.magnet} ${unknownLicense.id}\n${nontrivial}\n// @license-end`; let commentedOutUnknownLicensed = unknownLicensed.split('\n').map(y => '// ' + y).join('\n'); let malformedLicensed = `// @license\n${nontrivial}`; let commentedOutMalformedLicensed = malformedLicensed.split('\n').map(y => '// ' + y).join('\n'); let tab, documentUrl; const enableContactFinderTests = false; beforeAll(async () => { let url = browser.extension.getURL('/test/resources/index.html'); tab = (await browser.tabs.query({ url }))[0] || (await browser.tabs.create({ url })); documentUrl = url; }); describe('The whitelist/blacklist manager', () => { let { ListManager, ListStore, Storage } = LibreJS; let lm = new ListManager(new ListStore('_test.whitelist', Storage.CSV), new ListStore('_test.blacklist', Storage.CSV), new Set()); let forgot = ['http://formerly.whitelist.ed/', 'http://formerly.blacklist.ed/']; beforeAll(async () => { await lm.whitelist('https://fsf.org/*', 'https://*.gnu.org/*', forgot[0]); await lm.blacklist('https://*.evil.gnu.org/*', 'https://verybad.com/*', forgot[1]); }); it('Should handle basic CRUD operations', async () => { expect(lm.getStatus(forgot[0])).toBe('whitelisted'); expect(lm.getStatus(forgot[1])).toBe('blacklisted'); await lm.forget(...forgot); for (let url of forgot) { expect(lm.getStatus(url)).toBe('unknown'); } }); it('Should support full path wildcards', () => { expect(lm.getStatus('https://unknown.org')).toBe('unknown'); expect(lm.getStatus('https://fsf.org/some/path')).toBe('whitelisted'); expect(lm.getStatus('https://fsf.org/')).toBe('whitelisted'); expect(lm.getStatus('https://fsf.org')).toBe('whitelisted'); expect(lm.getStatus('https://subdomain.fsf.org')).toBe('unknown'); expect(lm.getStatus('https://verybad.com/some/other/path?with=querystring')).toBe('blacklisted'); }); it('Should support subdomain wildcards', () => { expect(lm.getStatus('https://gnu.org')).toBe('whitelisted'); expect(lm.getStatus('https://www.gnu.org')).toBe('whitelisted'); expect(lm.getStatus('https://evil.gnu.org')).toBe('blacklisted'); expect(lm.getStatus('https://more.evil.gnu.org')).toBe('blacklisted'); expect(lm.getStatus('https://more.evil.gnu.org/some/evil/path?too')).toBe('blacklisted'); }); }) describe('The external script source processor', () => { let url = 'https://www.gnu.org/mock-script.js'; let processScript = async (source, whitelisted = false) => await LibreJS.handleScript({ text: source, request: { url, tabId: tab.id, documentUrl, frameId: 0 }, }, whitelisted); it('should accept whitelisted scripts', async () => { expect(await processScript(nontrivial, true) || nontrivial).toContain(nontrivial); }); it('should block trivial scripts too', async () => { let processed = await processScript(trivial); expect(processed || trivial).not.toContain(trivial); }); it('should block non-trivial scripts', async () => { let processed = await processScript(nontrivial); expect(processed || nontrivial).not.toContain(nontrivial); }); it('should accept scripts with known free license tags', async () => { let processed = await processScript(licensed); expect(processed || licensed).toContain(nontrivial); }); it('should block scripts with unknown license tags', async () => { let processed = await processScript(unknownLicensed); expect(processed).not.toContain(nontrivial); }); it('should leave alone scripts with commented out unknown license tags', async () => { let processed = await processScript(commentedOutUnknownLicensed); expect(processed).toContain(nontrivial); }); it('should block scripts with malformed license tags', async () => { let processed = await processScript(malformedLicensed); expect(processed).not.toContain(nontrivial); }); it('should leave alone scripts with commented out malformed license tags', async () => { let processed = await processScript(commentedOutMalformedLicensed); expect(processed).toContain(nontrivial); }); }); describe('The HTML processor', () => { let processHtml = async (html, whitelisted = false) => LibreJS.editHtml(html, tab.url, tab.id, 0, whitelisted); let addScript = (html, script, before = '') => html.replace(before, `${before}`); let addToBody = (html, fragment) => html.replace('', `${fragment}`); let jsUrl = js => `javascript:${encodeURIComponent(js)}`; function extractScripts(html, def = '') { let matches = html && html.match(/