Commit a9ca18f6 authored by Xunnamius (Luna)'s avatar Xunnamius (Luna)

a shit ton of updates

parent db110157
......@@ -10,7 +10,11 @@ import WriteFileWebpackPlugin from 'write-file-webpack-plugin'
require('dotenv').config();
const { HASHING_ALGORITHM, APPLICATION_LABEL } = process.env;
const {
HASHING_ALGORITHM,
APPLICATION_LABEL,
MAX_REQUEST_HISTORY,
} = process.env;
const paths = {};
......@@ -77,7 +81,8 @@ const configure = (NODE_ENV: ?string) => {
new webpack.DefinePlugin({
_NODE_ENV: JSON.stringify(NODE_ENV),
_HASHING_ALGORITHM: JSON.stringify(HASHING_ALGORITHM || 'SHA-256'),
_APPLICATION_LABEL: JSON.stringify(APPLICATION_LABEL || '_haschk')
_APPLICATION_LABEL: JSON.stringify(APPLICATION_LABEL || '_haschk'),
_MAX_REQUEST_HISTORY: JSON.stringify(MAX_REQUEST_HISTORY || 1000),
}),
new CopyWebpackPlugin([{
......@@ -119,7 +124,8 @@ const configure = (NODE_ENV: ?string) => {
new HtmlWebpackPlugin({
template: `${paths.src}/welcome.html`,
filename: 'welcome.html'
filename: 'welcome.html',
chunks: []
}),
new WriteFileWebpackPlugin()
......
......@@ -10,3 +10,6 @@ HASHING_ALGORITHM=SHA-256
# The Application Label (AL) is a well-defined string used in BD requests
APPLICATION_LABEL=_haschk
# The maximum number of requests we'll keep around to associate with downloads
MAX_REQUEST_HISTORY=1000
......@@ -3073,6 +3073,12 @@
}
}
},
"base32-encode": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/base32-encode/-/base32-encode-1.1.1.tgz",
"integrity": "sha512-eqa0BeGghj3guezlasdHJhr3+J5ZbbQvxeprkcDMbRQrjlqOT832IUDT4Al4ofAwekFYMqkkM9KMUHs9Cu0HKA==",
"dev": true
},
"base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
......@@ -5064,6 +5070,24 @@
"safer-buffer": "^2.1.0"
}
},
"ecc-qj": {
"version": "git+https://github.com/rynomad/ecc.git#a8c599363dab87a5c95274338d863233f00f7c4a",
"from": "git+https://github.com/rynomad/ecc.git",
"dev": true,
"optional": true,
"requires": {
"nan": "^1.6.2"
},
"dependencies": {
"nan": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-1.9.0.tgz",
"integrity": "sha1-GpzSdVYJdm9cKR5BlPzjn94oZRU=",
"dev": true,
"optional": true
}
}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
......@@ -12229,6 +12253,23 @@
"integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
"dev": true
},
"polyfill-promise": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/polyfill-promise/-/polyfill-promise-4.0.1.tgz",
"integrity": "sha1-+ESNwnw7ln1fZ/wGD1btZOIoa0k=",
"dev": true,
"requires": {
"bluebird": "^2.9.27"
},
"dependencies": {
"bluebird": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz",
"integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=",
"dev": true
}
}
},
"portfinder": {
"version": "1.0.25",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz",
......@@ -13713,6 +13754,12 @@
"integrity": "sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig==",
"dev": true
},
"sjcl": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.8.tgz",
"integrity": "sha512-LzIjEQ0S0DpIgnxMEayM1rq9aGwGRG4OnZhCdjx7glTaJtf4zRfpg87ImfjSJjoW9vKpagd82McDOwbRT5kQKQ==",
"dev": true
},
"slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
......@@ -14649,6 +14696,41 @@
"stylelint-config-recommended": "^3.0.0"
}
},
"subtle": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/subtle/-/subtle-0.1.8.tgz",
"integrity": "sha1-DBJZm5zxMSftoRYmUQzY8k+HiRY=",
"dev": true,
"requires": {
"ecc-jsbn": "0.0.1",
"ecc-qj": "git+https://github.com/rynomad/ecc.git",
"node-forge": "^0.6.20",
"polyfill-promise": "^4.0.1",
"sjcl": "^1.0.3"
},
"dependencies": {
"ecc-jsbn": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.0.1.tgz",
"integrity": "sha1-lwV3ujG0l2+xiJopjLdFHYlsRm0=",
"dev": true,
"requires": {
"jsbn": "git+https://github.com/rynomad/jsbn.git"
}
},
"jsbn": {
"version": "git+https://github.com/rynomad/jsbn.git#bb522b0124f75424f89d49446c40a87111942c7b",
"from": "git+https://github.com/rynomad/jsbn.git",
"dev": true
},
"node-forge": {
"version": "0.6.49",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.6.49.tgz",
"integrity": "sha1-8e6V1ddGI5OP4Z1piqWibVTS9g8=",
"dev": true
}
}
},
"sugarss": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz",
......
......@@ -40,7 +40,13 @@
},
"testPathIgnorePatterns": [
"/node_modules/"
]
],
"globals": {
"_NODE_ENV": "test",
"_HASHING_ALGORITHM": "SHA-256",
"_APPLICATION_LABEL": "_haschk",
"_MAX_REQUEST_HISTORY": "1000"
}
},
"dependencies": {
"@babel/core": "^7.8.4",
......@@ -52,6 +58,7 @@
"@babel/plugin-proposal-throw-expressions": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"@babel/preset-flow": "^7.8.3",
"base32-encode": "^1.1.1",
"axios": "^0.19.2",
"dotenv": "^8.2.0",
"eventemitter3": "^4.0.0",
......@@ -66,6 +73,7 @@
"babel-jest": "^25.1.0",
"babel-loader": "^8.0.6",
"babel-plugin-source-map-support": "^2.1.1",
"base32-encode": "^1.1.1",
"bluebird": "^3.7.2",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^5.1.1",
......
......@@ -7,15 +7,17 @@ import http from 'axios'
import {
Debug,
extractBDCandidatesFromURI,
extractAnswerDataFromResponse,
GOOGLE_DNS_HTTPS_BACKEND_EXISTS,
JUDGEMENT_UNKNOWN,
JUDGEMENT_UNDECIDED,
GOOGLE_DNS_HTTPS_BACKEND_EXISTS,
MAX_REQUEST_HISTORY,
} from 'universe'
import {
EventFrame,
EventFrameEmitter,
portMessageToEvent,
portMessageToEventFrame,
eventFrameToPortMessage,
} from 'universe/events'
......@@ -43,34 +45,35 @@ const webRequestFilter = {
* ? - When a message is received from another component of the extension
*/
export default (oracle: EventFrameEmitter, chrome: Chrome, context: Object) => {
chrome.runtime.onConnect.addListener((port) => {
Debug.log('runtime.onConnect: ', port);
chrome.runtime.onConnect.addListener(port => {
Debug.log(chrome, '[BACKGROUND EVENT] (port connected)', port);
// ? Whenever an event is triggered, we send it out for any other
// ? interested components in the system to pick up on
const globalListener = oracle.addGlobalListener((eventFrame: EventFrame, args: Array<any>) => {
Debug.log(chrome, '[BACKGROUND EVENT] (event and args sent through port)', port, eventFrame, args);
port.postMessage(eventFrameToPortMessage(eventFrame, args));
});
// ? When a port is destroyed for whatever reason (page refresh, popup
// ? closed, etc), remove the (dead) global listener associated with it
port.onDisconnect.addListener(() => {
Debug.log('(port disconnected)');
Debug.log(chrome, '[BACKGROUND EVENT] (port disconnected)', port);
oracle.removeGlobalListener(globalListener);
});
// ? This fires when we receive a message from another component of the
// ? extension. We translate the message into an event and emit it
port.onMessage.addListener(data => {
Debug.log('port.onMessage: ', data);
const { eventFrame, args } = portMessageToEvent(data);
Debug.log(chrome, '[BACKGROUND EVENT] (event and args received through port)', port, data);
const { eventFrame, args } = portMessageToEventFrame(data);
oracle.emitIgnoreGlobalListeners(eventFrame.name, eventFrame, ...args);
});
});
// ? This event fires when the extension is first installed
chrome.runtime.onInstalled.addListener(details => {
Debug.log('runtime.onInstalled: ', details);
Debug.log(chrome, '[BACKGROUND EVENT] runtime.onInstalled:', details);
if(['install', 'update'].includes(details.reason))
chrome.tabs.create({ url: chrome.runtime.getURL('welcome.html') });
......@@ -85,13 +88,24 @@ export default (oracle: EventFrameEmitter, chrome: Chrome, context: Object) => {
else
context.navHistory.set(details.requestId, [details.url]);
Debug.log(`webRequest.onBeforeRequest: ${details.requestId} =>`, context.navHistory.get(details.requestId));
Debug.log(
chrome,
`[BACKGROUND EVENT] webRequest.onBeforeRequest: ${details.requestId} =>`,
context.navHistory.get(details.requestId)
);
if(context.navHistory.size > MAX_REQUEST_HISTORY) {
// ? If the navigation history map is too large, do FIFO eviction
const evictedRequestId = context.navHistory.keys().next().value;
context.navHistory.delete(evictedRequestId);
Debug.log(chrome, `[BACKGROUND EVENT] (evicted ${evictedRequestId} from navigation history)`);
}
}, webRequestFilter);
// ? This event fires whenever a WebRequest error occurs
// * Only fires if we're debugging
Debug.if(() => chrome.webRequest.onErrorOccurred.addListener(details => {
console.log('webRequest.onErrorOccurred: ', details);
console.log('[BACKGROUND EVENT] webRequest.onErrorOccurred:', details);
}, webRequestFilter));
// ? This event fires when a new download begins in chrome
......@@ -100,33 +114,57 @@ export default (oracle: EventFrameEmitter, chrome: Chrome, context: Object) => {
// ! even years), so we cannot trust this event without a check
chrome.downloads.onCreated.addListener(downloadItem => {
if(!downloadItem.endTime) {
Debug.log('downloads.onCreated: ', downloadItem);
Debug.log(chrome, '[BACKGROUND EVENT] downloads.onCreated:', downloadItem);
oracle.emit('download.incoming', downloadItem);
}
else Debug.log('[IGNORED] downloads.onCreated: ', downloadItem);
else Debug.log(chrome, '[IGNORED BACKGROUND EVENT] downloads.onCreated:', downloadItem);
});
// ? This event fires when some download-related event changes
chrome.downloads.onChanged.addListener(targetItem => {
Debug.log('downloads.onChanged: ', targetItem);
Debug.log(chrome, '[BACKGROUND EVENT] downloads.onChanged:', targetItem);
// ? We ask for the most up-to-date DownloadItem instance
chrome.downloads.search({ id: targetItem.id }, async ([ downloadItem ]) => {
if(chrome.runtime.lastError) {
oracle.emit('error', chrome.runtime.lastError.message);
return;
}
downloadItem.judgement = JUDGEMENT_UNKNOWN;
downloadItem.backendDomain = null;
// ! If downloadItem.judgement === JUDGEMENT_UNKNOWN, none of the
// ! extended properties (below) if any are guaranteed to exist!
// ? If a download is paused, resumed, or cancelled, alert everyone
// * Note: the DownloadItem passed at these ^ points is incomplete!
if(targetItem?.paused?.current === true)
oracle.emit('download.paused', downloadItem);
else if(targetItem?.paused?.current === false)
oracle.emit('download.resumed', downloadItem);
else if(targetItem?.state?.current == 'interrupted')
oracle.emit('download.interrupted', downloadItem);
// ? Only trigger the moment a download completes
if(targetItem?.state?.current == 'complete') {
/**
* ? We ask for the most up-to-date DownloadItem instance and extend
* ? it with the following useful information:
* ? Extend the downloadItem with the following useful information
* ? upon completion (`backendDomain` and `judgement` technically
* ? always exist on any DownloadItems distributed through the
* ? oracle):
* ?
* ? - backendDomain {string|null} The BD of this download or NULL if HASCHK was not implemented
* ? - requestId {string} The Request ID associated with this download
* ? - requestStack {Array<Object>} A FILO stack of `details` objects
* ? - judgement {boolean} What HASCHK thinks about this DownloadItem (undecided or unknown)
* ?
* ? If backendDomain is NULL, then judgement will be JUDGEMENT_UNKNOWN, otherwise JUDGEMENT_UNDECIDED
* ? If backendDomain is NULL, then judgement will always be
* ? JUDGEMENT_UNKNOWN, otherwise it will be JUDGEMENT_UNDECIDED
*/
chrome.downloads.search({ id: targetItem.id }, async ([ downloadItem ]) => {
downloadItem.judgement = JUDGEMENT_UNKNOWN;
downloadItem.backendDomain = null;
else if(targetItem?.state?.current == 'complete') {
// First, identify the request stack and ID that lead to this
// download using DownloadItem::url and querying the top of the
// stack
......@@ -143,22 +181,24 @@ export default (oracle: EventFrameEmitter, chrome: Chrome, context: Object) => {
// queue and walk from the head (bottom of the stack) to the
// tail (top of the stack) until we get a response back from a
// 3LD or 2LD.
loop:
for (const uri of downloadItem.requestStack.slice(0).reverse()) {
for (const candidate of extractBDCandidatesFromURI(uri)) {
const query = await http.get(GOOGLE_DNS_HTTPS_BACKEND_EXISTS(candidate));
const data = !query.data.Answer ? '<no answer>' : query.data.Answer.slice(-1)[0].data;
if(data == '"OK"') {
downloadItem.backendDomain = candidate;
downloadItem.judgement = JUDGEMENT_UNDECIDED;
break loop;
if(downloadItem.requestStack) {
loop:
for (const uri of downloadItem.requestStack.slice(0).reverse()) {
for (const candidate of extractBDCandidatesFromURI(uri)) {
const response = await http.get(GOOGLE_DNS_HTTPS_BACKEND_EXISTS(candidate));
const data = extractAnswerDataFromResponse(response);
if(data == '"OK"') {
downloadItem.backendDomain = candidate;
downloadItem.judgement = JUDGEMENT_UNDECIDED;
break loop;
}
}
}
}
oracle.emit('download.completed', downloadItem);
});
}
}
});
});
};
......@@ -3,91 +3,110 @@
*/
import http from 'axios'
import ParsedUrl from 'url-parse'
import base32Encode from 'base32-encode'
import { EventFrameEmitter } from 'universe/events'
import type { Chrome } from 'universe'
import {
bufferToHex,
Debug,
HASHING_ALGORITHM,
GOOGLE_DNS_HTTPS_BACKEND_QUERY,
extractAnswerDataFromResponse,
JUDGEMENT_UNKNOWN,
JUDGEMENT_UNSAFE,
JUDGEMENT_SAFE,
} from 'universe'
import {Debug} from '../../universe'
export default (oracle: EventFrameEmitter, chrome: Chrome, context: Object) => {
Debug.if(() => oracle.addListener('download.completed', (e, downloadItem) => {
console.log('download.completed: ', e, downloadItem);
}));
// ? This is the NAH vs AH core "judgement" logic
oracle.addListener('download.completed', async (e, downloadItem) => {
// TODO
let authedHash: ?string;
let nonauthedHash: ?string;
let authedHashRaw: ?string;
import {
setDefaultBadgeState,
setIncomingBadgeState,
setErrorBadgeState,
setSafeBadgeState,
setUnsafeBadgeState,
} from 'universe/ui'
// ? Since it's finished downloading, grab the file's data
const $file = await http.get(`file://${downloadItem.filename}`, { responseType: 'arraybuffer' });
import type { Chrome } from 'universe'
import type { EventFrame } from 'universe/events'
// ? Hash file data with proper algorithm
// flow-disable-line
nonauthedHash = bufferToHex(await crypto.subtle.digest('SHA-256', $file.data));
declare var crypto;
// ? Determine resource identifier and prepare for DNS request
const resourcePath = (new ParsedUrl(downloadItem.url, {})).pathname;
const resourceIdentifier = bufferToHex(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(resourcePath)));
const outputLength = parseInt(HASHING_ALGORITHM);
export default (oracle: EventFrameEmitter, chrome: Chrome, context: Object) => {
// ? These events update the context.downloadItems cache
if(!resourceIdentifier || resourceIdentifier.length != outputLength)
throw new Error('failed to hash resource identifier');
const updateDownloadItemInContext = downloadItem => context.downloadItems[downloadItem.id] = downloadItem;
const [ riLeft, riRight ] = [
resourceIdentifier.slice(0, outputLength / 2),
resourceIdentifier.slice(outputLength / 2, outputLength)
];
oracle.addListener('download.incoming', (e, downloadItem) => updateDownloadItemInContext(downloadItem));
oracle.addListener('download.paused', (e, downloadItem) => updateDownloadItemInContext(downloadItem));
oracle.addListener('download.resumed', (e, downloadItem) => updateDownloadItemInContext(downloadItem));
oracle.addListener('download.interrupted', (e, downloadItem) => updateDownloadItemInContext(downloadItem));
oracle.addListener('download.completed', (e, downloadItem) => updateDownloadItemInContext(downloadItem));
// ? Make https-based DNS request
const targetDomain = downloadItem;
const $authedHash = await http.get(GOOGLE_DNS_HTTPS_BACKEND_QUERY(riLeft, riRight, targetDomain));
// ? This event fires whenever haschk decides a download is NOT safe
oracle.addListener(`judgement.${JUDGEMENT_UNSAFE}`, (e: EventFrame, downloadItem) => {
chrome.downloads.removeFile(downloadItem.id, () => {
if(chrome.runtime.lastError)
oracle.emit('error', chrome.runtime.lastError.message);
});
});
authedHashRaw = !$authedHash.data.Answer ? '<no answer>' : $authedHash.data.Answer.slice(-1)[0].data;
authedHash = authedHashRaw.replace(/[^0-9a-f]/gi, '');
// ? This event sets the default badge state on startup
oracle.addListener('startup', () => setDefaultBadgeState());
if(!authedHash || !authedHashRaw)
throw new TypeError('unexpected null type encountered');
// ? This event responds to any requests for the list of known download
// ? items (i.e. from another component of the extension)
oracle.addListener('request.updateDownloadItems', () => {
oracle.emit('response.updateDownloadItems', context.downloadItems);
});
// ? Compare DNS result (auth) with hashed local file data (nonauthed)
if(authedHash.length !== authedHashRaw.length - 2)
oracle.emit('judgement.unknown', downloadItem);
// ? These events update the badge state upon certain events happening
oracle.addListener('error', () => setErrorBadgeState());
oracle.addListener('download.incoming', () => setIncomingBadgeState());
oracle.addListener('download.paused', () => setDefaultBadgeState());
oracle.addListener('download.resumed', () => setIncomingBadgeState());
oracle.addListener('download.interrupted', () => setDefaultBadgeState());
oracle.addListener(`judgement.${JUDGEMENT_UNKNOWN}`, () => setDefaultBadgeState());
oracle.addListener(`judgement.${JUDGEMENT_SAFE}`, () => setSafeBadgeState());
oracle.addListener(`judgement.${JUDGEMENT_UNSAFE}`, () => setUnsafeBadgeState());
// ? This event is the heart of the extension where we implement the HASCHK
// ? protocol
oracle.addListener('download.completed', async (e: EventFrame, downloadItem) => {
if(downloadItem.judgement === JUDGEMENT_UNKNOWN) {
oracle.emit(`judgement.${JUDGEMENT_UNKNOWN}`, downloadItem);
return;
}
else
oracle.emit(`judgement.${authedHash !== nonauthedHash ? 'unsafe' : 'safe'}`, downloadItem);
});
// ? Since it's finished downloading, grab the file's data
const file = await http.get(`file://${downloadItem.filename}`, { responseType: 'arraybuffer' });
oracle.addListener('judgement.unknown', downloadItem => {
// TODO
context.judgedDownloadItems.push({
downloadItem: downloadItem,
judgement: JUDGEMENT_UNKNOWN
// ? Hash file data with proper algorithm
const base32FileHash = base32Encode(await crypto.subtle.digest(HASHING_ALGORITHM, file.data), 'Crockford', {
padding: false
});
});
oracle.addListener('judgement.safe', downloadItem => {
// TODO
context.judgedDownloadItems.push({
downloadItem: downloadItem,
judgement: JUDGEMENT_SAFE
// ? Construct BASE32 encoded URN and slice it up to yield C1 and C2
const base32Urn = base32Encode((new TextEncoder()).encode(`urn:hash::sha256:${base32FileHash}`), 'Crockford', {
padding: true
});
});
oracle.addListener('judgement.unsafe', downloadItem => {
// TODO
context.judgedDownloadItems.push({
downloadItem: downloadItem,
judgement: JUDGEMENT_UNSAFE
});
Debug.if(() =>
base32Urn.length % 2 !== 0 && console.warn(`URN length is not an even number (${base32Urn.length})!`));
const [ C1, C2 ] = [
base32Urn.slice(0, base32Urn.length / 2),
base32Urn.slice(base32Urn.length / 2, base32Urn.length),
];
// ? Make https-based DNS request
const queryUri = GOOGLE_DNS_HTTPS_BACKEND_QUERY(C1, C2, downloadItem.backendDomain);
const data = extractAnswerDataFromResponse(await http.get(queryUri));
Debug.log(chrome, `C1: ${C1}`);
Debug.log(chrome, `C2: ${C2}`);
Debug.log(chrome, `backend domain: ${downloadItem.backendDomain}`);
Debug.log(chrome, `query response data: ${data}`);
// ? Compare DNS result with expected
oracle.emit(`judgement.${data === '"OK"' ? JUDGEMENT_SAFE : JUDGEMENT_UNSAFE}`, downloadItem);
});
};
/** @flow
* @description Most of the core HASCHK logic and functionality is implemented here
*/
import { EventFrameEmitter } from 'universe/events'
import {
Debug,
JUDGEMENT_UNKNOWN,
JUDGEMENT_UNSAFE,
JUDGEMENT_SAFE,
} from 'universe'
import type { Chrome } from 'universe'
import type { EventFrame } from 'universe/events'
declare var crypto;
export default (oracle: EventFrameEmitter, chrome: Chrome) => {
// ? This is our generic error handler that fires whenever an error occurs
oracle.addListener('error', (errorFrame, exception, errorArgs) => {
Debug.log(chrome, `ErrorFrame:`, errorFrame);
Debug.log(chrome, `Exception object:`, exception);
Debug.log(chrome, `ErrorArgs:`, errorArgs);
console.error(`HASCHK ERROR: ${exception}`);
});
// * Debug-only event listeners
Debug.if(() => oracle.addListener('startup', (e: EventFrame) => {
console.log(`[BACKGROUND EVENT] ${e.name}:`, e);
}));
const downloadLogTemplate = (e: EventFrame, downloadItem) => {
console.log(`[BACKGROUND EVENT] ${e.name}: ${downloadItem.finalUrl}`);
};
Debug.if(() => oracle.addListener('download.incoming', (e: EventFrame, downloadItem) => {
downloadLogTemplate(e, downloadItem);
}));
Debug.if(() => oracle.addListener('download.paused', (e: EventFrame, downloadItem) => {
downloadLogTemplate(e, downloadItem);
}));
Debug.if(() => oracle.addListener('download.resumed', (e: EventFrame, downloadItem) => {
downloadLogTemplate(e, downloadItem);
}));
Debug.if(() => oracle.addListener('download.interrupted', (e: EventFrame, downloadItem) => {
downloadLogTemplate(e, downloadItem);
}));
Debug.if(() => oracle.addListener('download.completed', (e: EventFrame, downloadItem) => {
console.log(`[BACKGROUND EVENT] ${e.name}:`, e, downloadItem);
}));
const judgementLogTemplate = (e: EventFrame, downloadItem) => {
console.log(`[BACKGROUND EVENT] ${e.name}: file "${downloadItem.filename}"`);
};
Debug.if(() => oracle.addListener(`judgement.${JUDGEMENT_UNKNOWN}`, (e: EventFrame, downloadItem) => {
judgementLogTemplate(e, downloadItem);
}));
Debug.if(() => oracle.addListener(`judgement.${JUDGEMENT_SAFE}`, (e: EventFrame, downloadItem) => {
judgementLogTemplate(e, downloadItem);
}));
Debug.if(() => oracle.addListener(`judgement.${JUDGEMENT_UNSAFE}`, (e: EventFrame, downloadItem) => {
judgementLogTemplate(e, downloadItem);
}));
};
/** @flow
* @description All HASCHK UI logic goes here (i.e. popup, options)
*/
import { setBadge } from 'universe/ui'
import { EventFrameEmitter } from 'universe/events'
import { Debug } from 'universe'
import type { Chrome } from 'universe'
export default (oracle: EventFrameEmitter, chrome: Chrome, context: Object) => {
// ? This is our generic error handler that fires whenever an error occurs
oracle.addListener('error', (errorFrame, exception, errorArgs) => {
Debug.log(`ErrorFrame:`, errorFrame);
Debug.log(`Exception object:`, exception);
Debug.log(`ErrorArgs:`, errorArgs);
// TODO
setBadge(chrome)('ERR', '#000');
console.error(`HASCHK ERROR: ${exception}`);
});
// ? This event fires whenever a new download is observed
oracle.addListener('download.incoming', async (e, downloadItem) => {
// TODO: add the new download to popup UI (tell popup UI to update)
// ! Note how this function is async, which means you can await Promises
Debug.log(`file incoming from ${downloadItem.finalUrl}`);
});
// ? This event fires whenever haschk decides it cannot judge a download
oracle.addListener('judgement.unknown', downloadItem => {
// TODO
setBadge(chrome)(' ', '#D0D6B5');
Debug.log(`file "${downloadItem.filename}" judgement: UNKNOWN`);
});
// ? This event fires whenever haschk decides a download is safe
oracle.addListener('judgement.safe', downloadItem => {
// TODO
setBadge(chrome)(' ', '#6EEB83');
Debug.log(`file "${downloadItem.filename}" judgement: SAFE`);
});
// ? This event fires whenever haschk decides a download is NOT safe
oracle.addListener('judgement.unsafe', downloadItem => {
// TODO
setBadge(chrome)(' ', '#FF3C38');
Debug.log(`file "${downloadItem.filename}" judgement: UNSAFE`);
});
oracle.addListener('ui.clear', () => {
// TODO
setBadge(chrome)('');
context.judgedDownloadItems = [];
});
};
......@@ -8,7 +8,7 @@ import { EventFrameEmitter } from 'universe/events'
import registerChromeEvents from 'components/background/events.chrome'
import registerCoreEvents from 'components/background/events.core'
import registerUIEvents from 'components/background/events.ui'
import registerDebugEvents from 'components/background/events.debug'