Skip to content

Instantly share code, notes, and snippets.

@trs
Last active July 12, 2018 17:37
Show Gist options
  • Save trs/46fee9bacbd0e58a029d283996cb7eb9 to your computer and use it in GitHub Desktop.
Save trs/46fee9bacbd0e58a029d283996cb7eb9 to your computer and use it in GitHub Desktop.
Quick script to scan a directory of projects for infected eslint-scope installs (rev 3.7.2). Runs in node 8+.
const path = require('path');
const fs = require('fs');
const {exec} = require('child_process');
const PROJECTS_DIR = process.cwd() || __dirname;
const BAD_PACKAGES = [
{
name: 'eslint-scope',
revisions: ['3.7.2']
},
{
name: 'eslint-config-eslint',
revisions: ['5.0.2']
}
];
const DEPENDENCY_KEYS = [
'dependencies',
'devDependencies',
'peerDependencies',
'bundledDependencies',
'optionalDependencies'
];
const projectDirectories = fs.readdirSync(PROJECTS_DIR)
.filter(d => fs.statSync(path.join(PROJECTS_DIR, d)).isDirectory())
.filter(d => fs.existsSync(path.join(PROJECTS_DIR, d, 'package.json')));
function recurseCheckDependencies(dependencies, subpath = []) {
const matchedSubdepdencies = [];
Object.keys(dependencies).forEach(packageName => {
const packageInfo = dependencies[packageName];
for (const {name, revisions} of BAD_PACKAGES) {
if (packageName.trim() === name) {
if (revisions.includes(packageInfo.version.trim())) {
console.log('bad revision found!');
matchedSubdepdencies.push(subpath.concat([packageName, packageInfo.version.trim()]));
}
}
}
if (packageInfo.dependencies) {
const matchedSub = recurseCheckDependencies(
packageInfo.dependencies,
subpath.concat([packageName])
);
if (matchedSub && matchedSub.length) {
matchedSubdepdencies.push(...matchedSub);
}
}
});
if (matchedSubdepdencies.length > 0) {
return matchedSubdepdencies;
}
return null;
}
async function checkDirectory(d) {
console.log(`checking project ${d}`);
const fullPath = path.join(PROJECTS_DIR, d);
const rawResult = await new Promise(resolve => exec('npm list --json', {
cwd: fullPath,
maxBuffer: 10000 * 1024
}, (error, stdout, stderr) => {
if (stdout && stdout.length) {
resolve(stdout);
}
resolve(stdout || stderr);
}));
const result = JSON.parse(rawResult);
return DEPENDENCY_KEYS.reduce((total, key) => {
const matched = recurseCheckDependencies(result[key] || {}) || [];
if (matched && matched.length) {
console.log(` !!! [${key}] found bad revisions (${key})`, matched.map(m => m.join(' -> ')));
} else {
console.log(` OK (${key})`);
}
return [...total, ...matched];
}, []);
}
async function checkAllDirectories() {
const allBadPaths = [];
for (let di = 0; di < projectDirectories.length; di++) {
const d = projectDirectories[di];
try {
const badPaths = await checkDirectory(d);
if (badPaths && badPaths.length) {
allBadPaths.push(...badPaths.map(p => [d].concat(p)));
}
} catch (err) {
console.log(`failed to scan ${d}`, err);
}
}
if (!allBadPaths.length) {
console.log('completed successfully');
} else {
console.log('!!! Bad packages found');
allBadPaths.forEach(p => {
console.log(p.join(' -> '));
});
}
}
(async function () {
try {
await checkAllDirectories();
} catch (err) {
console.error('failed', err);
} finally {
process.exit(0);
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment