diff --git a/main.js b/main.js index b6c5bca..91ce239 100644 --- a/main.js +++ b/main.js @@ -6,6 +6,12 @@ const env = './.env'; let md5Previous = md5(fs.readFileSync(env)); +/** + * Watch the .env file for changes and restart the scanner with new options if there is any change + * @param {string} env - Path to the .env file + * @param {} event - A filesystem event + * @param {} filename - Name of the file for the filesystem event + */ fs.watch(env, (event, filename) => { if (filename) { const md5Current = md5(fs.readFileSync(env)); @@ -19,4 +25,8 @@ fs.watch(env, (event, filename) => { } }); +/** + * Start the scanner when the main script is called + * @param {string} "start" + */ scan("start"); \ No newline at end of file diff --git a/scripts/config.js b/scripts/config.js index c6f5f10..8990d59 100644 --- a/scripts/config.js +++ b/scripts/config.js @@ -2,11 +2,16 @@ const express = require('express'); const fs = require("fs"); const os = require("os"); const app = express(); -const bodyParser = require('body-parser') +const bodyParser = require('body-parser'); const port = 8080; const quarantinePath = "/usr/VSterilizer/infected/"; +/** + * Find the line for the given key and splice it to replace with the new value + * @param {string} key - Key for the given env variable + * @param {string} newValue - New value for the given key + */ async function changeParam(key, newValue) { const ENV_VARS = fs.readFileSync("./.env", "utf8").split(os.EOL); const target = ENV_VARS.indexOf(ENV_VARS.find((line) => { @@ -16,6 +21,15 @@ async function changeParam(key, newValue) { fs.writeFileSync("./.env", ENV_VARS.join(os.EOL)); } +/** + * Handle the option changes when a change request comes to the API endpoint + * It is safe to call parameter changer with every new request without checking + * if the given new option is the same with the old one since the watcher function + * which restarts the scanner for changes checks the MD5 hash of the file to ensure + * that there is an actual change in the file + * @param {request} req - The option change request + * @param {response} res - Passed to the function to enable sending responses in the handler function itself + */ async function optionHandler(req, res) { if (req.body.quarantineInfected) { await changeParam("quarantineInfected", quarantinePath); @@ -30,9 +44,14 @@ async function optionHandler(req, res) { app.use(bodyParser.json()); +/** + * @param {string} '/options' - API endpoint + * @param {request} req + * @param {response} res + */ app.post('/options', (req, res) => { optionHandler(req, res); }); -app.listen(port, () => console.log(`Hello world app listening on port ${port}!`)) +app.listen(port, () => console.log(`Listening on port ${port}!`)) app.on("exit", () => server.close()) diff --git a/scripts/scan.js b/scripts/scan.js index 77f9571..fd3e567 100644 --- a/scripts/scan.js +++ b/scripts/scan.js @@ -36,7 +36,10 @@ options = preference: 'clamdscan' // If clamdscan is found and active, it will be used by default } -// Send every bad file one by one +/** + * Send the results of a scan to the API endpoint in realtime + * @param {array} badFileList=null + */ async function sendResults(badFileList = null) { if (badFileList === null) { sendStatus("Scan completed, no infected files has been detected.") @@ -51,6 +54,11 @@ async function sendResults(badFileList = null) { } } +/** + * Sends status information to the API endpoint + * This includes data with information level severity such as a new USB being detected, a new scan running etc. + * @param {string} status + */ async function sendStatus(status) { let payload = { status: status }; let res = await axios.post('http://httpbin.org/post', payload); @@ -58,8 +66,12 @@ async function sendStatus(status) { console.log(data); } +/** + * Scan the given directory path for infected files + * Call the result sending function to send the results to the frontend API + * @param {string} path + */ async function scanDirectory(path) { - // Get instance by resolving ClamScan promise object const clamscan = await new NodeClam().init(options); try { // TODO: Replace hard-coded path with path var it is here for faster testing @@ -76,21 +88,29 @@ async function scanDirectory(path) { } } +/** + * Add the ability to wait in parts of the code without blocking rest of the program + * @param {integer} ms + */ function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } -// Parse log file to find file of interest (foi) -async function parseLog(parser) { +/** + * Parses the scanner log file to get detailed information about infected files + * Takes a keyword parameter which has the path of a given infected file + * @param {string} keyword + */ +async function parseLog(keyword) { let badFiles = []; let file = fs.readFileSync(options.scanLog, "utf8"); let arr = file.split(/\r?\n/); arr.forEach((line, idx) => { if (line.includes("FOUND")) { line = line.split(/[ ]+/); - let fname = line[0].split(parser); + let fname = line[0].split(keyword); fname = fname.pop().slice(0, -1); let badFileEntry = { filename: fname, @@ -102,31 +122,43 @@ async function parseLog(parser) { return badFiles; } -// To reload env vars have a script that the config file editor API one kill and re-start this one +/** + * Get the mounting point for a given serial number + * @param {string} serialNumber + */ async function getMountPoint(serialNumber) { sendStatus("Accessing the USB Device..."); await sleep(5000); // Wait for device to be mounted by kernel let out = child_process.spawnSync('/home/deari/projects/VSterilizer/getMountPoint.sh', [serialNumber]); - let length = out.stdout.toString('utf8').split("\n")[0].length; - // Use this to check if the found addy ends with number try other one if doesnt mount(out.stdout.toString('utf8').split("\n")[0]); } +/** + * Mount the given device to a auto-generated dir under the + * products own mounting directory + * @param {string} source + */ function mount(source) { let uuid = uuidv4(); child_process.execSync(`mkdir -p /media/VSterilizer/${uuid}`); child_process.execSync(`mount ${source} /media/VSterilizer/${uuid}`); - console.log(`/media/VSterilizer/${uuid}`); - // Remove Windows specific un-usable files which should never been generated anyways. Windows Search service bug causes this file fs.rmSync(`/media/VSterilizer/${uuid}/System\ Volume\ Information/`, { recursive: true, force: true }); scanDirectory(`/media/VSterilizer/${uuid}/`); } -// Send this to helper script +/** + * Enable watcher to detect newly plugged USB devices + * Calls the mounting point getter function when a new device is detected + * @param {string} 'add' + */ USBWatch.on('add', function (device) { getMountPoint(device.serialNumber); }); +/** + * Starting point of the scanner script + * Handles clean-up and starts the USB monitoring + */ function start() { fs.writeFileSync(options.scanLog, ''); // Clear the logs console.log("Started to monitor for USB inserts!"); @@ -135,6 +167,10 @@ function start() { //scanDirectory("/home/deari/Downloads/"); // Enable for testing } +/** + * Helper script to end the USB monitoring + * Utilized while stopping or restarting the service + */ function endWatch() { USBWatch.stopMonitoring(); }