You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

130 lines
4.6KB

  1. /*
  2. check valid JSON
  3. check valid SHA256
  4. upload to VirusTotal and assert less than 3 or so engines return suspicious for each ZIP
  5. ZIP must not contain a __MACOSX directory at root level.
  6. 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.
  7. 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.
  8. schema validation
  9. slug required
  10. 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.
  11. version required
  12. version must begin with "0.5." (this will be changed with each version bump.)
  13. */
  14. const fs = require("fs");
  15. const request = require("request");
  16. const { exec } = require('child_process');
  17. const vt = require("node-virustotal");//https://github.com/natewatson999/node-virustotal
  18. const con = vt.MakePublicConnection();
  19. const archArr = ['win', 'lin', 'mac'];
  20. const verbose = false;
  21. //VT_API_KEY is set in travisci (or set when run locally)
  22. con.setKey(process.env.VT_API_KEY);
  23. if(verbose){console.log('key', con.getKey());}
  24. //15 seconds between calls is required by virus total (4 per minute)
  25. //https://www.virustotal.com/en/documentation/public-api/v2/
  26. con.setDelay(15000);
  27. if(verbose){console.log('delay', con.getDelay());}
  28. //We always just check all JSON because it's easy.
  29. //However, we only want to check zip files of manifests that have changed.
  30. //TODO FIX - travis doesn't have master
  31. // https://github.com/travis-ci/travis-ci/issues/6069
  32. // todo check diff to see if "download" or "sha256" changed
  33. exec('git diff -w --stat --name-only origin/master -- plugins/', (error, stdout, stderr) => {
  34. if (error) {
  35. console.error(`exec error: ${error}`);
  36. return;
  37. }
  38. const changedManifestFiles = stdout.trim().split('\n');
  39. testAllManifests(changedManifestFiles);
  40. });
  41. function testAllManifests(changedManifestFiles){
  42. if(verbose){console.log("changedManifestFiles", changedManifestFiles);}
  43. fs.readdir('plugins', function(err, files) {
  44. if (err){ throw err; }
  45. for (let index in files) {
  46. const filePath = `plugins/${files[index]}`;
  47. if(!filePath.toLowerCase().endsWith('.json')){
  48. throw new Error("manifests should have .json extension");
  49. }
  50. fs.readFile(filePath, 'utf8', (err, data) => {
  51. if (err){ throw err; }
  52. if(verbose){console.log("testing: ", filePath);}
  53. const shouldTestZip = changedManifestFiles.includes(filePath);
  54. testOneManifest(filePath, data, shouldTestZip);
  55. });
  56. }
  57. });
  58. }
  59. function testOneManifest(filePath, fileContent, shouldTestZip = false) {
  60. let manifestObj;
  61. try {
  62. manifestObj = JSON.parse(fileContent);
  63. } catch(err){
  64. console.error(`Invalid JSON: ${filePath}`);
  65. throw err;
  66. }
  67. if (manifestObj.downloads) {
  68. let zipUrlsChecked = [];
  69. let lastSha256;
  70. archArr.map(arch => {
  71. const archObj = manifestObj.downloads[arch];
  72. if (archObj && archObj.download) {
  73. if(zipUrlsChecked.includes(archObj.download)){
  74. if(lastSha256 !== archObj.sha256){
  75. throw new Error('SHA256 should be the same if the download URL is the same.');
  76. }
  77. } else {
  78. zipUrlsChecked.push(archObj.download);
  79. lastSha256 = archObj.sha256;
  80. if(shouldTestZip){
  81. testOneArch(archObj);
  82. } else {
  83. if(verbose){console.log("not testing zip because the manifest hasn't changed: ", filePath);}
  84. }
  85. }
  86. }
  87. });
  88. }
  89. }
  90. function testOneArch(archObj) {
  91. const urlParts = archObj.download.split('/');
  92. const zipName = urlParts[urlParts.length - 1].split('\?')[0];
  93. if(verbose){console.log(`Downloading ${archObj.download}`);}
  94. request(archObj.download).pipe(fs.createWriteStream(zipName)).on('finish', ()=>{
  95. con.FileEvaluation(zipName, "application/zip", fs.readFileSync(zipName), function(data) {
  96. console.log(data);
  97. if(archObj.sha256 !== data.sha256){
  98. throw new Error(`Invalid sha256 value. manifest:${archObj.sha256} virustotal:${data.sha256}`);
  99. }
  100. if(data.positives > 2){
  101. throw new Error(`Too many positives from virustotal.`);
  102. }
  103. }, function(err) {
  104. if (err){ throw err; }
  105. });
  106. });
  107. }