hhurley
Version:
Tool to find lost security patches for Linux distributions.
167 lines (135 loc) • 4.88 kB
JavaScript
/*
Copyright IBM Research Emergent Solutions
Jesús Pérez <jesusprubio@gmail.com>
This code may only be used under the MIT license found at
https://opensource.org/licenses/MIT.
*/
;
const path = require('path');
const fs = require('fs');
const util = require('util');
const EventEmitter = require('events').EventEmitter;
const pkgInfo = require('./package');
const cfg = require('./cfg');
const utils = require('./lib/utils');
// const parser = require('./lib/parsers');
const providers = utils.requireDir(module, './lib/providers');
const dbg = utils.dbg(__filename);
const baseFilePath = path.resolve(__dirname, cfg.baseFile.path);
// function parse(opts) {
// return {
// concurrency: parser.natural(opts.concurrency),
// timeout: parser.natural(opts.timeout)
// };
// }
class Cli {
constructor(opts = {}) {
this.version = pkgInfo.version;
this.forceDown = opts.forceDown || false;
this.fileExists = false;
this.interval = null;
dbg(`Checking if the base file exists ... (${baseFilePath})`);
try {
fs.statSync(baseFilePath);
dbg('File already exists');
this.fileExists = true;
} catch (err) {
if (err.code !== 'ENOENT') {
// Not expected error.
throw err;
}
dbg('File doesn\'t exist');
}
dbg(`Version: ${this.version}`);
}
stop() {
clearInterval(this.interval);
}
// TODO: Allow to pass the options here.
search() {
dbg('Starting ...');
// Conditional promise.
const downloadC = () =>
new Promise((resD, rejD) => {
if (this.forceDown || !this.fileExists) {
dbg('Downloading base file ...');
// TODO: Add progress support:
// https://www.npmjs.com/package/progress-stream
this.emit('file:downloading');
utils.downBase(cfg.baseFile.url, baseFilePath)
.then(() => {
this.emit('file:downloaded');
resD();
})
.catch(err => rejD(err));
} else {
dbg('File already exists');
resD();
}
});
downloadC()
.then(() => {
dbg('Download finished, reading the file now ...');
// TODO: Ue an iterator. In case a huge file the memory print could be too much.
utils.readFile(baseFilePath, { encoding: 'utf8' })
.then((baseFile) => {
let lastCve;
dbg('Base file correctly readed');
const split = baseFile.split('\n');
dbg('Base file splitted:', split);
utils.each(split, (line) => {
dbg(`New line to inspect: ${line}`);
if (line[0] === 'C' && line[1] === 'V' &&
line[2] === 'E' && line[3] === '-') {
lastCve = line.substring(0, 12).trim();
dbg(`New CVE ID: ${lastCve}`);
}
if (line[0] === '\t' && line[1] === 'N' && line[2] === 'O' &&
line[3] === 'T' && line[4] === 'E') {
let link = line.substring(7).trim();
dbg(`New note line found: ${link}`);
// TODO: Trim this (if possible):
// https://github.com/chriso/validator.js/#validators
if (utils.validator.isURL(link, {
protocols: ['http', 'https'],
require_protocol: true,
require_valid_protocol: true,
require_host: true,
})) {
dbg(`New URL found: ${link}`);
// Sometimes the url includes a "/" at the end and we don't
// want it because sometimes we need to add stuff to the end.
// ie: Github url -> url.patch
if (link.slice(-1) === '/') { link = link.slice(0, -1); }
const patchInfo = { cve: lastCve };
utils.each(Object.keys(providers), (name) => {
// Until one is found.
if (!patchInfo.patchUrl) {
// We need to found all the included ones.
if (providers[name].match(link)) {
// TODO: Add a check to confirm the methods exist.
const hash = providers[name].getHash(link);
const patchUrl = providers[name].massage(link);
if (patchUrl) {
patchInfo.provider = name;
patchInfo.id = `${name}:${patchInfo.cve}:${hash}`;
patchInfo.link = link;
patchInfo.patchUrl = patchUrl;
}
}
}
});
if (patchInfo.patchUrl) { this.emit('new', patchInfo); }
}
}
});
dbg('Done');
this.emit('end');
})
.catch(err => this.emit('error', err));
})
.catch(err => this.emit('error', err));
}
}
util.inherits(Cli, EventEmitter);
module.exports = Cli;