File: /home/thehunarfound/public_html/DMS/node_modules/snyk/lib/protect/patch.js
module.exports = patch;
var now = new Date();
var debug = require('debug')('snyk');
var chalk = require('chalk');
var recursive = require('recursive-readdir');
var tempfile = require('tempfile');
var fs = require('then-fs');
var path = require('path');
var _ = require('lodash');
var applyPatch = require('./apply-patch');
var stripVersions = require('./strip-versions');
var getVulnSource = require('./get-vuln-source');
var dedupe = require('./dedupe-patches');
var writePatchFlag = require('./write-patch-flag');
var spinner = require('../spinner');
var errors = require('../error');
var analytics = require('../analytics');
var getPatchFile = require('./fetch-patch');
var ensurePatchUtilExists = require('./ensure-patch');
// note: cwd is optional and mostly used for testing
function patch(vulns, live, cwd) {
var lbl = 'Applying patches...';
var errorList = [];
return ensurePatchUtilExists().then(spinner(lbl)).then(function () {
// the target directory where our module name will live
vulns.forEach(function (vuln) {
vuln.source = getVulnSource(vuln, cwd, live);
});
var deduped = dedupe(vulns);
debug('patching %s vulns after dedupe', deduped.packages.length);
// find the patches, pull them down off the web, save them in a temp file
// then apply each individual patch - but do it one at a time (via reduce)
var promises = deduped.packages.reduce(function (acc, vuln) {
return acc.then(function (res) {
var patches = vuln.patches; // this is also deduped in `dedupe`
if (patches === null) {
debug('no patch available for ' + vuln.id);
analytics.add('no-patch', vuln.from.slice(1).join(' > '));
return res;
}
analytics.add('patch', vuln.from.slice(1).join(' > '));
// the colon doesn't like Windows, ref: https://git.io/vw2iO
var fileSafeId = vuln.id.replace(/:/g, '-');
var flag = path.resolve(vuln.source, '.snyk-' + fileSafeId + '.flag');
var oldFlag = path.resolve(vuln.source, '.snyk-' + vuln.id + '.flag');
// get the patches on the local fs
var promises = patches.urls.map(function (url) {
var filename = tempfile('.' + fileSafeId + '.snyk-patch');
return getPatchFile(url, filename, 1).then(function (patch) {
// check whether there's a trace of us having patched before
return fs.exists(flag).then(function (exists) {
// if the file doesn't exist, look for the old style filename
// in case and for backwards compatability
return exists || fs.exists(oldFlag);
}).then(function (exists) {
if (!exists) {
return patch;
}
debug('Previous flag found = ' + exists +
' | Restoring file back to original to apply the patch again');
// else revert the patch
return new Promise(function (resolve, reject) {
recursive(vuln.source, function (error, files) {
if (error) {
return reject(error);
}
resolve(Promise.all(files.filter(function (file) {
return file.slice(-5) === '.orig';
}).map(function (file) {
return fs.rename(file, path.dirname(file) + '/' +
path.basename(file).slice(0, -5));
})));
});
}).then(function () {
return patch;
});
});
}).then(function (patch) {
if (patch === false) {
debug('already patched %s', vuln.id);
return vuln;
}
debug('applying patch file for %s: \n%s\n%s', vuln.id, url, patch);
return applyPatch(patch, vuln, live)
.then(function () {
return true;
}, function (e) {
errorList.push(e);
return false;
})
.then(writePatchFlag(now, vuln))
.then(function (ok) {
return ok ? vuln : false;
});
});
});
return Promise.all(promises).then(function (result) {
res.push(result);
return res; // this is what makes the waterfall reduce chain work
});
});
}, Promise.resolve(deduped.removed));
var promise = promises.then(function (res) {
var patched = _.flatten(res).filter(Boolean);
if (!live) {
debug('[skipping - dry run]');
return patched;
}
return Promise.all(patched);
}).then(function (patched) {
var config = {};
// this reduce function will look to see if the patch actually resolves
// more than one vulnerability, and if it does, it'll replicate the
// patch rule against the *other* vuln.ids. This will happen when the user
// runs the wizard and selects to apply a patch that fixes more than one
// vuln.
var mapped = patched.map(patchRule).reduce(function (acc, curr, i) {
var vuln = patched[i];
if (vuln.grouped && vuln.grouped.includes) {
vuln.grouped.includes.forEach(function (id) {
var rule = _.cloneDeep(curr);
rule.vulnId = id;
acc.push(rule);
});
}
acc.push(curr);
return acc;
}, []);
config.patch = mapped.reduce(function (acc, curr) {
if (!acc[curr.vulnId]) {
acc[curr.vulnId] = [];
}
var id = curr.vulnId;
delete curr.vulnId;
acc[id].push(curr);
return acc;
}, {});
debug('patched', config);
return config;
});
return promise;
}).then(spinner.clear(lbl)).then(function (res) {
if (errorList.length) {
errorList.forEach(function (error) {
console.log(chalk.red(errors.message(error)));
});
throw new Error('Please email support@snyk.io if this problem persists.');
}
return res;
});
}
function patchRule(vuln) {
var rule = {
vulnId: vuln.id,
};
rule[stripVersions(vuln.from.slice(1)).join(' > ')] = {
patched: now.toJSON(),
};
return rule;
}
;;