|
@@ -1,129 +0,0 @@ |
|
|
/* |
|
|
|
|
|
|
|
|
|
|
|
check valid JSON |
|
|
|
|
|
check valid SHA256 |
|
|
|
|
|
upload to VirusTotal and assert less than 3 or so engines return suspicious for each ZIP |
|
|
|
|
|
|
|
|
|
|
|
ZIP must not contain a __MACOSX directory at root level. |
|
|
|
|
|
ZIP must not be a tarbomb. Yes, I know the first is a subset of this one, but it's better to have more feedback. |
|
|
|
|
|
We might even want to assert that the single folder at root level is named as the slug of the plugin. I'm for this. |
|
|
|
|
|
|
|
|
|
|
|
schema validation |
|
|
|
|
|
|
|
|
|
|
|
slug required |
|
|
|
|
|
if we could have a "soft" warning for slugs matching /[a-zA-Z0-9_\-]/+, that'd be great, although I'd rather their slugs break this than change. |
|
|
|
|
|
version required |
|
|
|
|
|
version must begin with "0.5." (this will be changed with each version bump.) |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const fs = require("fs"); |
|
|
|
|
|
const request = require("request"); |
|
|
|
|
|
const { exec } = require('child_process'); |
|
|
|
|
|
const vt = require("node-virustotal");//https://github.com/natewatson999/node-virustotal |
|
|
|
|
|
const con = vt.MakePublicConnection(); |
|
|
|
|
|
const archArr = ['win', 'lin', 'mac']; |
|
|
|
|
|
const verbose = false; |
|
|
|
|
|
|
|
|
|
|
|
//VT_API_KEY is set in travisci (or set when run locally) |
|
|
|
|
|
con.setKey(process.env.VT_API_KEY); |
|
|
|
|
|
if(verbose){console.log('key', con.getKey());} |
|
|
|
|
|
|
|
|
|
|
|
//15 seconds between calls is required by virus total (4 per minute) |
|
|
|
|
|
//https://www.virustotal.com/en/documentation/public-api/v2/ |
|
|
|
|
|
con.setDelay(15000); |
|
|
|
|
|
if(verbose){console.log('delay', con.getDelay());} |
|
|
|
|
|
|
|
|
|
|
|
//We always just check all JSON because it's easy. |
|
|
|
|
|
//However, we only want to check zip files of manifests that have changed. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//TODO FIX - travis doesn't have master |
|
|
|
|
|
// https://github.com/travis-ci/travis-ci/issues/6069 |
|
|
|
|
|
// todo check diff to see if "download" or "sha256" changed |
|
|
|
|
|
exec('git diff -w --stat --name-only origin/master -- plugins/', (error, stdout, stderr) => { |
|
|
|
|
|
if (error) { |
|
|
|
|
|
console.error(`exec error: ${error}`); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
const changedManifestFiles = stdout.trim().split('\n'); |
|
|
|
|
|
testAllManifests(changedManifestFiles); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
function testAllManifests(changedManifestFiles){ |
|
|
|
|
|
if(verbose){console.log("changedManifestFiles", changedManifestFiles);} |
|
|
|
|
|
fs.readdir('plugins', function(err, files) { |
|
|
|
|
|
if (err){ throw err; } |
|
|
|
|
|
|
|
|
|
|
|
for (let index in files) { |
|
|
|
|
|
const filePath = `plugins/${files[index]}`; |
|
|
|
|
|
if(!filePath.toLowerCase().endsWith('.json')){ |
|
|
|
|
|
throw new Error("manifests should have .json extension"); |
|
|
|
|
|
} |
|
|
|
|
|
fs.readFile(filePath, 'utf8', (err, data) => { |
|
|
|
|
|
if (err){ throw err; } |
|
|
|
|
|
if(verbose){console.log("testing: ", filePath);} |
|
|
|
|
|
|
|
|
|
|
|
const shouldTestZip = changedManifestFiles.includes(filePath); |
|
|
|
|
|
testOneManifest(filePath, data, shouldTestZip); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function testOneManifest(filePath, fileContent, shouldTestZip = false) { |
|
|
|
|
|
let manifestObj; |
|
|
|
|
|
try { |
|
|
|
|
|
manifestObj = JSON.parse(fileContent); |
|
|
|
|
|
} catch(err){ |
|
|
|
|
|
console.error(`Invalid JSON: ${filePath}`); |
|
|
|
|
|
throw err; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (manifestObj.downloads) { |
|
|
|
|
|
let zipUrlsChecked = []; |
|
|
|
|
|
let lastSha256; |
|
|
|
|
|
|
|
|
|
|
|
archArr.map(arch => { |
|
|
|
|
|
const archObj = manifestObj.downloads[arch]; |
|
|
|
|
|
if (archObj && archObj.download) { |
|
|
|
|
|
if(zipUrlsChecked.includes(archObj.download)){ |
|
|
|
|
|
if(lastSha256 !== archObj.sha256){ |
|
|
|
|
|
throw new Error('SHA256 should be the same if the download URL is the same.'); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
zipUrlsChecked.push(archObj.download); |
|
|
|
|
|
lastSha256 = archObj.sha256; |
|
|
|
|
|
|
|
|
|
|
|
if(shouldTestZip){ |
|
|
|
|
|
testOneArch(archObj); |
|
|
|
|
|
} else { |
|
|
|
|
|
if(verbose){console.log("not testing zip because the manifest hasn't changed: ", filePath);} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function testOneArch(archObj) { |
|
|
|
|
|
const urlParts = archObj.download.split('/'); |
|
|
|
|
|
const zipName = urlParts[urlParts.length - 1].split('\?')[0]; |
|
|
|
|
|
if(verbose){console.log(`Downloading ${archObj.download}`);} |
|
|
|
|
|
request(archObj.download).pipe(fs.createWriteStream(zipName)).on('finish', ()=>{ |
|
|
|
|
|
con.FileEvaluation(zipName, "application/zip", fs.readFileSync(zipName), function(data) { |
|
|
|
|
|
console.log(data); |
|
|
|
|
|
if(archObj.sha256 !== data.sha256){ |
|
|
|
|
|
throw new Error(`Invalid sha256 value. manifest:${archObj.sha256} virustotal:${data.sha256}`); |
|
|
|
|
|
} |
|
|
|
|
|
if(data.positives > 2){ |
|
|
|
|
|
throw new Error(`Too many positives from virustotal.`); |
|
|
|
|
|
} |
|
|
|
|
|
}, function(err) { |
|
|
|
|
|
if (err){ throw err; } |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|