🏡 index : github.com/captn3m0/which-electron.git

author Nemo <me@captnemo.in> 2021-07-18 17:26:38.0 +05:30:00
committer Nemo <me@captnemo.in> 2021-07-18 17:26:38.0 +05:30:00
commit
1c72d60761f4df15a0a38b78d2939e6b6c1d3870 [patch]
tree
cf1c00336801216c3d55dd71ebac1f68c7d681ef
parent
5ee877fdf31c9e61f97c96b3f1461668703f5980
download
1c72d60761f4df15a0a38b78d2939e6b6c1d3870.tar.gz

Re-organize directory structure



Diff

 README.md            |   4 ++--
 finder.js            |  74 --------------------------------------------------------------------------
 fingerprint.js       |  54 ------------------------------------------------------
 index.js             |  85 --------------------------------------------------------------------------------
 os.js                |  43 -------------------------------------------
 package.json         |   6 +++---
 utils.js             |   5 -----
 version.js           |  18 ------------------
 versions.json        | 446 --------------------------------------------------------------------------------
 zip.js               |  41 -----------------------------------------
 src/finder.js        |  74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/fingerprint.js   |  54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/index.js         |  85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/os.js            |  43 +++++++++++++++++++++++++++++++++++++++++++
 src/utils.js         |   5 +++++
 src/version.js       |  18 ++++++++++++++++++
 src/versions.json    | 446 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/zip.js           |  41 +++++++++++++++++++++++++++++++++++++++++
 tests/finder.js      |   2 +-
 tests/fingerprint.js |   2 +-
 tests/os.js          |   2 +-
 21 files changed, 774 insertions(+), 774 deletions(-)

diff --git a/README.md b/README.md
index c28f711..dea886d 100644
--- a/README.md
+++ a/README.md
@@ -1,6 +1,6 @@
# which-electron ![](https://img.shields.io/badge/Status-Beta-orange) ![npm](https://img.shields.io/npm/v/which-electron) ![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/which-electron) ![NPM](https://img.shields.io/npm/l/which-electron)

Try to find out which Electron version is bundled inside an application.
Find out which Electron version is bundled inside an application.

## Usage

@@ -33,7 +33,7 @@
4. A lookup table of [hashes from various electron releases](https://github.com/captn3m0/electron-fingerprints/) is used to guess the version. In case of multiple matches, it returns a range of versions.
5. Get the electron version from the electron binary (WIP)

Note that this can be run against untrusted binaries as it does not _try to run the application_. It has been tested against various file formats: zip/dmg/exe/AppImage/tar.gz etc. It extracts limited files using 7-zip.
Note that this can be run against untrusted binaries as it does not _try to run the application_. It has been tested against various file formats: zip/dmg/exe/AppImage/tar.gz etc. It extracts limited files using 7-zip to a temporary directory at runtime if needed.

## Known Issues

diff --git a/finder.js b/finder.js
deleted file mode 100644
index ff3d552..0000000 100644
--- a/finder.js
+++ /dev/null
@@ -1,74 +1,0 @@
// finds specific files from a list
const path = require("path");
const isDirectory = require('./utils').isDirectory;

module.exports = {
  // Finds the electron asar file, if we can
  asar: function(entries) {
    return entries
      .filter((e) => {
        return (
          isDirectory(e.attributes) == false &&
          path.basename(e.file) == "electron.asar"
        );
      })
      .map((e) => e.file);
  },
  binary: function(entries) {
    entries = entries.sort((a, b) => b.size - a.size);
    for (const entry of entries) {
      if (isDirectory(entry.attributes)) {
        continue;
      }
      let ext = path.extname(entry.file);
      let size = entry.size;
      // Return the first exe file
      if (ext == ".exe") {
        return entry.file;
      } else if (ext == "") {
        // or the largest file with no extension
        return entry.file;
      }
    }
  },

  version: function(entries) {
    return entries
      .filter((e) => {
        return isDirectory(e.attributes) == false && path.basename(e.file) == "version";
      })
      .map((e) => e.file);
  },

  findElectronPackageInsideNodeModules: function(entries) {
    return entries
      .filter((e) => {
        return isDirectory(e.attributes) == false && e.file.match(/node_modules\/electron\/package\.json$/);
      })
      .map((e) => e.file);
  },

  // Return a list of files that might be worth fingerprinting
  fingerprintable: function(entries) {
    return entries.filter((e) =>{
      if (isDirectory(e.attributes)) {
        return false;
      }
      if (!e.file) {
        return false;
      }
      let ext = path.extname(e.file);
      if (['.h', '.dll', '.bin', '.asar', '.dylib', '.so', '.exe'].indexOf(ext) !== -1) {
        return true
      }
      let b = path.basename(e.file);

      if (['electron framework', 'squirrel', 'electron', 'electron helper', 'chrome_100_percent', 'chrome_200_percent'].indexOf(b)!== -1) {
        return true;
      }

      return false;
    })
    .map((e)=>e.file)
  }
};
diff --git a/fingerprint.js b/fingerprint.js
deleted file mode 100644
index 8433570..0000000 100644
--- a/fingerprint.js
+++ /dev/null
@@ -1,54 +1,0 @@
const DB = require("electron-fingerprints");
const fs = require("fs");
const hasha = require("hasha");
const allVersions = require("./versions")["all"];

function checksumFile(algorithm, path) {
  return new Promise(function(resolve, reject) {
    let fs = require("fs");
    let crypto = require("crypto");

    let hash = crypto.createHash(algorithm).setEncoding("hex");
    fs.createReadStream(path)
      .once("error", reject)
      .pipe(hash)
      .once("finish", function() {
        resolve(hash.read());
      });
  });
}

module.exports = {
  guessFromHashes: function(os, arch, hashList) {
    let lookupTable = DB[`${os}-${arch}`];
    let allPossibleHashes = Object.keys(lookupTable);
    const intersectingHashes = allPossibleHashes.filter((value) =>
      hashList.includes(value)
    );
    // Set it to the starting list of versions.
    let possibleVersions = allVersions;
    for (i in hashList) {
      let hash = hashList[i];
      let versions = lookupTable[hash];
      if (versions) {
        possibleVersions = possibleVersions.filter((value) =>
          versions.includes(value)
        );
      }
    }

    if (possibleVersions == allVersions) {
      return [];
    } else {
      return possibleVersions;
    }
  },

  getHashes: function(dir) {
    let list = fs.readdirSync(dir);
    return list.map((f) => {
      let fn = `${dir}/${f}`;
      return hasha.fromFileSync(fn, { algorithm: "sha1" });
    });
  },
};
diff --git a/index.js b/index.js
deleted file mode 100644
index 6cd8ab9..0000000 100644
--- a/index.js
+++ /dev/null
@@ -1,85 +1,0 @@
const path = require("path");
const osguess = require("./os");
const finder = require("./finder");
const zip = require("./zip");
const fp = require("./fingerprint");
const cleanup = require("rimraf");
const V = require("./version");

// Input file comes from process.argv[2]
const FILENAME = process.argv[2];

console.log(FILENAME);

function logSupport(version) {
  if (V.isSupported(version)) {
    console.log(`${version} is currently supported`);
  } else {
    console.log(`${version} is currently not supported`);
  }
}

zip.listFileContents(FILENAME, (entries) => {
  let osguess1 = osguess.guessFromFilename(FILENAME);
  let osguess2 = osguess.guessFromContents(entries);

  if (osguess1 !== osguess2 && osguess1 && osguess2) {
    console.log(`Unsure about operating system. Going with ${osguess2}. Other option was ${osguess1}`);
  }
  if (osguess1 && !osguess2) {
    osguess2 = osguess1
  }
  let arch = osguess.guessArch(FILENAME, entries);
  let asar = finder.asar(entries);
  let binary = finder.binary(entries);
  let versionFiles = finder.version(entries);
  let enm = finder.findElectronPackageInsideNodeModules(entries);

  let filesToHash = finder.fingerprintable(entries);

  zip.extractSomeFiles(FILENAME, filesToHash, (dir) => {
    hashes = fp.getHashes(dir);
    guesses = fp.guessFromHashes(osguess2, arch, hashes);
    if (guesses.length == 1) {
      console.log("Fingerprint: " + guesses[0]);
      logSupport(guesses[0])
    } else if (guesses.length > 1) {
      console.log("Fingerprint: " + V.asText(guesses));
      logSupport(V.max(guesses))
    }

    cleanup.sync(dir);
  });

  // if (binary) {
  //   console.log(`${process.argv[2]}:${binary}`);
  // }
  if (versionFiles.length > 0) {
    versionFiles.map((f) => {
      zip.readFileContents(FILENAME, f, (c) => {
        console.log("Found Version file: " + c);
        logSupport(`${c}`)
      });
    });
  }
  if (asar.length > 0) {
    asar.forEach((a) => {
      console.log("Version Constraint (Unsupported): <v7.0.0");
    });
  }
  if (enm) {
    enm.forEach((a) => {
      zip.readFileContents(FILENAME, a, (c) => {
        try {
          let packageData = JSON.parse(c);
          console.log(
            "Found version in package.json file: " + packageData["version"]
          );
          logSupport(`v${packageData["version"]}`)
        } catch (e) {
          // TODO: Do something
        }
      });
    });
  }
});
diff --git a/os.js b/os.js
deleted file mode 100644
index 52919b9..0000000 100644
--- a/os.js
+++ /dev/null
@@ -1,43 +1,0 @@
// Guess the OS

const path = require('path')

module.exports = {
	guessFromFilename(inputFile) {
		let fn = path.basename(inputFile)
		if (fn.match(/linux/)) {
			return 'linux'
		} else if (fn.match(/mac/)) {
			return 'darwin'
    } else if (fn.match(/darwin/)) {
      return 'darwin'
		} else if (fn.match(/win/)) {
			return 'win32'
		} else {
			let ext = path.extname(inputFile).toLowerCase()
      if (ext == '.dmg') {return 'darwin'}
      if (ext == '.exe') {return 'win32'}
      if (['.deb', '.appimage', '.pacman'].indexOf(ext) !== -1) {
        return 'linux'
      }
		}
		return null;
	},
  guessArch(filename, entries) {
    return 'x64';
  },
	guessFromContents(entries) {
		for (i in entries) {
      let entry = entries[i]
			if (path.extname(entry.file) == ".so") {
				return 'linux'
			} else if (path.extname(entry.file) == '.dll') {
				return 'win32'
			} else if (path.extname(entry.file) == '.dylib') {
				return 'darwin'
			} else if (path.extname(entry.file) == '.plist') {
				return 'darwin'
			}
		}
	}
}
diff --git a/package.json b/package.json
index fde2fdc..a63c271 100644
--- a/package.json
+++ a/package.json
@@ -1,10 +1,10 @@
{
  "name": "which-electron",
  "version": "1.0.0",
  "version": "1.0.1",
  "description": "Guess which electron version is bundled in an application",
  "main": "index.js",
  "main": "src/index.js",
  "bin": {
    "which-electron": "index.js"
    "which-electron": "src/index.js"
  },

  "scripts": {
    "test": "kuta tests/*.js"
diff --git a/utils.js b/utils.js
deleted file mode 100644
index c74d212..0000000 100644
--- a/utils.js
+++ /dev/null
@@ -1,5 +1,0 @@
module.exports = {
  isDirectory: function(a) {
     return (a? a[0] == "D" : null)
  },
};
diff --git a/version.js b/version.js
deleted file mode 100644
index 2a92d4e..0000000 100644
--- a/version.js
+++ /dev/null
@@ -1,18 +1,0 @@
const semverSort = require('semver-sort');
const VERSIONS = require('./versions')

module.exports = {
  asText: function(listOfVersions) {
    sorted = semverSort.asc(listOfVersions);
    return `${sorted[0]}-${sorted[sorted.length-1]}`
  },

  max: function(listOfVersions) {
    sorted = semverSort.asc(listOfVersions);
    return sorted[sorted.length-1];
  },

  isSupported: function(v) {
    return (VERSIONS['supported'].indexOf(v) !== -1)
  }
}
diff --git a/versions.json b/versions.json
deleted file mode 100644
index 937b8d0..0000000 100644
--- a/versions.json
+++ /dev/null
@@ -1,446 +1,0 @@
{
  "supported": ["13.1.6", "12.0.14", "11.4.10"],
  "all": [
    "v0.24.0",
    "v0.25.0",
    "v0.25.1",
    "v0.25.2",
    "v0.25.3",
    "v0.26.0",
    "v0.26.1",
    "v0.27.0",
    "v0.27.1",
    "v0.27.2",
    "v0.27.3",
    "v0.28.0",
    "v0.28.1",
    "v0.28.2",
    "v0.28.3",
    "v0.29.0",
    "v0.29.1",
    "v0.29.2",
    "v0.30.0",
    "v0.30.1",
    "v0.30.2",
    "v0.30.3",
    "v0.30.4",
    "v0.30.5",
    "v0.30.6",
    "v0.30.7",
    "v0.30.8",
    "v0.31.0",
    "v0.31.1",
    "v0.31.2",
    "v0.32.0",
    "v0.32.1",
    "v0.32.2",
    "v0.32.3",
    "v0.33.0",
    "v0.33.1",
    "v0.33.2",
    "v0.33.3",
    "v0.33.4",
    "v0.33.5",
    "v0.33.6",
    "v0.33.7",
    "v0.33.8",
    "v0.33.9",
    "v0.34.0",
    "v0.34.1",
    "v0.34.2",
    "v0.34.3",
    "v0.34.4",
    "v0.34.5",
    "v0.35.0",
    "v0.35.1",
    "v0.35.2",
    "v0.35.3",
    "v0.35.4",
    "v0.35.5",
    "v0.35.6",
    "v0.36.0",
    "v0.36.1",
    "v0.36.10",
    "v0.36.11",
    "v0.36.12",
    "v0.36.2",
    "v0.36.3",
    "v0.36.4",
    "v0.36.5",
    "v0.36.6",
    "v0.36.7",
    "v0.36.8",
    "v0.36.9",
    "v0.37.0",
    "v0.37.1",
    "v0.37.2",
    "v0.37.3",
    "v0.37.4",
    "v0.37.5",
    "v0.37.6",
    "v0.37.7",
    "v0.37.8",
    "v1.0.0",
    "v1.0.1",
    "v1.0.2",
    "v1.1.0",
    "v1.1.1",
    "v1.1.2",
    "v1.1.3",
    "v1.2.0",
    "v1.2.1",
    "v1.2.2",
    "v1.2.3",
    "v1.2.4",
    "v1.2.5",
    "v1.2.6",
    "v1.2.7",
    "v1.2.8",
    "v1.3.0",
    "v1.3.1",
    "v1.3.10",
    "v1.3.11",
    "v1.3.12",
    "v1.3.13",
    "v1.3.14",
    "v1.3.15",
    "v1.3.2",
    "v1.3.3",
    "v1.3.4",
    "v1.3.5",
    "v1.3.6",
    "v1.3.7",
    "v1.3.8",
    "v1.3.9",
    "v1.4.0",
    "v1.4.1",
    "v1.4.10",
    "v1.4.11",
    "v1.4.12",
    "v1.4.13",
    "v1.4.14",
    "v1.4.15",
    "v1.4.16",
    "v1.4.2",
    "v1.4.3",
    "v1.4.4",
    "v1.4.5",
    "v1.4.6",
    "v1.4.7",
    "v1.4.8",
    "v1.4.9",
    "v1.5.0",
    "v1.5.1",
    "v1.6.0",
    "v1.6.1",
    "v1.6.10",
    "v1.6.11",
    "v1.6.12",
    "v1.6.13",
    "v1.6.14",
    "v1.6.15",
    "v1.6.16",
    "v1.6.17",
    "v1.6.18",
    "v1.6.2",
    "v1.6.3",
    "v1.6.4",
    "v1.6.5",
    "v1.6.6",
    "v1.6.7",
    "v1.6.8",
    "v1.6.9",
    "v1.7.0",
    "v1.7.1",
    "v1.7.10",
    "v1.7.11",
    "v1.7.12",
    "v1.7.13",
    "v1.7.14",
    "v1.7.15",
    "v1.7.16",
    "v1.7.2",
    "v1.7.3",
    "v1.7.4",
    "v1.7.5",
    "v1.7.6",
    "v1.7.7",
    "v1.7.8",
    "v1.7.9",
    "v1.8.0",
    "v1.8.1",
    "v1.8.2",
    "v1.8.3",
    "v1.8.4",
    "v1.8.5",
    "v1.8.6",
    "v1.8.7",
    "v1.8.8",
    "v10.0.0",
    "v10.0.1",
    "v10.1.0",
    "v10.1.1",
    "v10.1.2",
    "v10.1.3",
    "v10.1.4",
    "v10.1.5",
    "v10.1.6",
    "v10.1.7",
    "v10.2.0",
    "v10.3.0",
    "v10.3.1",
    "v10.3.2",
    "v10.4.0",
    "v10.4.1",
    "v10.4.2",
    "v10.4.3",
    "v10.4.4",
    "v10.4.5",
    "v10.4.6",
    "v10.4.7",
    "v11.0.0",
    "v11.0.1",
    "v11.0.2",
    "v11.0.3",
    "v11.0.4",
    "v11.0.5",
    "v11.1.0",
    "v11.1.1",
    "v11.2.0",
    "v11.2.1",
    "v11.2.2",
    "v11.2.3",
    "v11.3.0",
    "v11.4.0",
    "v11.4.1",
    "v11.4.10",
    "v11.4.2",
    "v11.4.3",
    "v11.4.4",
    "v11.4.5",
    "v11.4.6",
    "v11.4.7",
    "v11.4.8",
    "v11.4.9",
    "v12.0.0",
    "v12.0.1",
    "v12.0.10",
    "v12.0.11",
    "v12.0.12",
    "v12.0.13",
    "v12.0.14",
    "v12.0.15",
    "v12.0.2",
    "v12.0.3",
    "v12.0.4",
    "v12.0.5",
    "v12.0.6",
    "v12.0.7",
    "v12.0.8",
    "v12.0.9",
    "v13.0.0",
    "v13.0.1",
    "v13.1.0",
    "v13.1.1",
    "v13.1.2",
    "v13.1.3",
    "v13.1.4",
    "v13.1.5",
    "v13.1.6",
    "v13.1.7",
    "v2.0.0",
    "v2.0.1",
    "v2.0.10",
    "v2.0.11",
    "v2.0.12",
    "v2.0.13",
    "v2.0.14",
    "v2.0.15",
    "v2.0.16",
    "v2.0.17",
    "v2.0.18",
    "v2.0.2",
    "v2.0.3",
    "v2.0.4",
    "v2.0.5",
    "v2.0.6",
    "v2.0.7",
    "v2.0.8",
    "v2.0.9",
    "v2.1.0-unsupported-20180809",
    "v2.1.0-unsupported.20180809",
    "v3.0.0",
    "v3.0.1",
    "v3.0.10",
    "v3.0.11",
    "v3.0.12",
    "v3.0.13",
    "v3.0.14",
    "v3.0.15",
    "v3.0.16",
    "v3.0.2",
    "v3.0.3",
    "v3.0.4",
    "v3.0.5",
    "v3.0.6",
    "v3.0.7",
    "v3.0.8",
    "v3.0.9",
    "v3.1.0",
    "v3.1.1",
    "v3.1.10",
    "v3.1.11",
    "v3.1.12",
    "v3.1.13",
    "v3.1.2",
    "v3.1.3",
    "v3.1.4",
    "v3.1.5",
    "v3.1.6",
    "v3.1.7",
    "v3.1.8",
    "v3.1.9",
    "v4.0.0",
    "v4.0.1",
    "v4.0.2",
    "v4.0.3",
    "v4.0.4",
    "v4.0.5",
    "v4.0.6",
    "v4.0.7",
    "v4.0.8",
    "v4.1.0",
    "v4.1.1",
    "v4.1.2",
    "v4.1.3",
    "v4.1.4",
    "v4.1.5",
    "v4.2.0",
    "v4.2.1",
    "v4.2.10",
    "v4.2.11",
    "v4.2.12",
    "v4.2.2",
    "v4.2.3",
    "v4.2.4",
    "v4.2.5",
    "v4.2.6",
    "v4.2.7",
    "v4.2.8",
    "v4.2.9",
    "v5.0.0",
    "v5.0.1",
    "v5.0.10",
    "v5.0.11",
    "v5.0.12",
    "v5.0.13",
    "v5.0.2",
    "v5.0.3",
    "v5.0.4",
    "v5.0.5",
    "v5.0.6",
    "v5.0.7",
    "v5.0.8",
    "v5.0.9",
    "v6.0.0",
    "v6.0.1",
    "v6.0.10",
    "v6.0.11",
    "v6.0.12",
    "v6.0.2",
    "v6.0.3",
    "v6.0.4",
    "v6.0.5",
    "v6.0.6",
    "v6.0.7",
    "v6.0.8",
    "v6.0.9",
    "v6.1.0",
    "v6.1.1",
    "v6.1.10",
    "v6.1.11",
    "v6.1.12",
    "v6.1.2",
    "v6.1.3",
    "v6.1.4",
    "v6.1.5",
    "v6.1.6",
    "v6.1.7",
    "v6.1.8",
    "v6.1.9",
    "v7.0.0",
    "v7.0.1",
    "v7.1.0",
    "v7.1.1",
    "v7.1.10",
    "v7.1.11",
    "v7.1.12",
    "v7.1.13",
    "v7.1.14",
    "v7.1.2",
    "v7.1.3",
    "v7.1.4",
    "v7.1.5",
    "v7.1.6",
    "v7.1.7",
    "v7.1.8",
    "v7.1.9",
    "v7.2.0",
    "v7.2.1",
    "v7.2.2",
    "v7.2.3",
    "v7.2.4",
    "v7.3.0",
    "v7.3.1",
    "v7.3.2",
    "v7.3.3",
    "v8.0.0",
    "v8.0.1",
    "v8.0.2",
    "v8.0.3",
    "v8.1.0",
    "v8.1.1",
    "v8.2.0",
    "v8.2.1",
    "v8.2.2",
    "v8.2.3",
    "v8.2.4",
    "v8.2.5",
    "v8.3.0",
    "v8.3.1",
    "v8.3.2",
    "v8.3.3",
    "v8.3.4",
    "v8.4.0",
    "v8.4.1",
    "v8.5.0",
    "v8.5.1",
    "v8.5.2",
    "v8.5.3",
    "v8.5.4",
    "v8.5.5",
    "v9.0.0",
    "v9.0.1",
    "v9.0.2",
    "v9.0.3",
    "v9.0.4",
    "v9.0.5",
    "v9.0.6",
    "v9.1.0",
    "v9.1.1",
    "v9.1.2",
    "v9.2.0",
    "v9.2.1",
    "v9.3.0",
    "v9.3.1",
    "v9.3.2",
    "v9.3.3",
    "v9.3.4",
    "v9.3.5",
    "v9.4.0",
    "v9.4.1",
    "v9.4.2",
    "v9.4.3",
    "v9.4.4"
  ]

}

diff --git a/zip.js b/zip.js
deleted file mode 100644
index 51e5b0a..0000000 100644
--- a/zip.js
+++ /dev/null
@@ -1,41 +1,0 @@
const Seven = require("node-7z");
const sevenBin = require('7zip-bin').path7za;
const path = require("path");
const fs = require("fs");

module.exports = {
  readFileContents: function(archive, filepath, cb) {
    // TODO: Create a new temp directory
    let stream = Seven.extract(archive, "/tmp", {
      recursive: true,
      $cherryPick: filepath,
      $bin: sevenBin
    });
    let fn = path.basename(filepath);
    stream.on("end", ()=>{
      cb(fs.readFileSync(`/tmp/${fn}`, {encoding: 'utf8'}))
    });
  },
  extractSomeFiles: function(archive, list, cb) {
    let dir = fs.mkdtempSync('/tmp/which-electron')
    let stream = Seven.extract(archive, dir, {
      $cherryPick: list,
      $bin: sevenBin
    })
    stream.on('end', ()=>{
      cb(dir)
    })
  },
  listFileContents: function(archive, cb) {
    let zip = Seven.list(archive, {
      $bin: sevenBin
    });
    let entries = [];
    zip.on("data", (data) => {
      entries.push(data);
    });
    zip.on("end", () => {
      cb(entries);
    });
  },
};
diff --git a/src/finder.js b/src/finder.js
new file mode 100644
index 0000000..ff3d552 100644
--- /dev/null
+++ a/src/finder.js
@@ -1,0 +1,74 @@
// finds specific files from a list
const path = require("path");
const isDirectory = require('./utils').isDirectory;

module.exports = {
  // Finds the electron asar file, if we can
  asar: function(entries) {
    return entries
      .filter((e) => {
        return (
          isDirectory(e.attributes) == false &&
          path.basename(e.file) == "electron.asar"
        );
      })
      .map((e) => e.file);
  },
  binary: function(entries) {
    entries = entries.sort((a, b) => b.size - a.size);
    for (const entry of entries) {
      if (isDirectory(entry.attributes)) {
        continue;
      }
      let ext = path.extname(entry.file);
      let size = entry.size;
      // Return the first exe file
      if (ext == ".exe") {
        return entry.file;
      } else if (ext == "") {
        // or the largest file with no extension
        return entry.file;
      }
    }
  },

  version: function(entries) {
    return entries
      .filter((e) => {
        return isDirectory(e.attributes) == false && path.basename(e.file) == "version";
      })
      .map((e) => e.file);
  },

  findElectronPackageInsideNodeModules: function(entries) {
    return entries
      .filter((e) => {
        return isDirectory(e.attributes) == false && e.file.match(/node_modules\/electron\/package\.json$/);
      })
      .map((e) => e.file);
  },

  // Return a list of files that might be worth fingerprinting
  fingerprintable: function(entries) {
    return entries.filter((e) =>{
      if (isDirectory(e.attributes)) {
        return false;
      }
      if (!e.file) {
        return false;
      }
      let ext = path.extname(e.file);
      if (['.h', '.dll', '.bin', '.asar', '.dylib', '.so', '.exe'].indexOf(ext) !== -1) {
        return true
      }
      let b = path.basename(e.file);

      if (['electron framework', 'squirrel', 'electron', 'electron helper', 'chrome_100_percent', 'chrome_200_percent'].indexOf(b)!== -1) {
        return true;
      }

      return false;
    })
    .map((e)=>e.file)
  }
};
diff --git a/src/fingerprint.js b/src/fingerprint.js
new file mode 100644
index 0000000..8433570 100644
--- /dev/null
+++ a/src/fingerprint.js
@@ -1,0 +1,54 @@
const DB = require("electron-fingerprints");
const fs = require("fs");
const hasha = require("hasha");
const allVersions = require("./versions")["all"];

function checksumFile(algorithm, path) {
  return new Promise(function(resolve, reject) {
    let fs = require("fs");
    let crypto = require("crypto");

    let hash = crypto.createHash(algorithm).setEncoding("hex");
    fs.createReadStream(path)
      .once("error", reject)
      .pipe(hash)
      .once("finish", function() {
        resolve(hash.read());
      });
  });
}

module.exports = {
  guessFromHashes: function(os, arch, hashList) {
    let lookupTable = DB[`${os}-${arch}`];
    let allPossibleHashes = Object.keys(lookupTable);
    const intersectingHashes = allPossibleHashes.filter((value) =>
      hashList.includes(value)
    );
    // Set it to the starting list of versions.
    let possibleVersions = allVersions;
    for (i in hashList) {
      let hash = hashList[i];
      let versions = lookupTable[hash];
      if (versions) {
        possibleVersions = possibleVersions.filter((value) =>
          versions.includes(value)
        );
      }
    }

    if (possibleVersions == allVersions) {
      return [];
    } else {
      return possibleVersions;
    }
  },

  getHashes: function(dir) {
    let list = fs.readdirSync(dir);
    return list.map((f) => {
      let fn = `${dir}/${f}`;
      return hasha.fromFileSync(fn, { algorithm: "sha1" });
    });
  },
};
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..6cd8ab9 100644
--- /dev/null
+++ a/src/index.js
@@ -1,0 +1,85 @@
const path = require("path");
const osguess = require("./os");
const finder = require("./finder");
const zip = require("./zip");
const fp = require("./fingerprint");
const cleanup = require("rimraf");
const V = require("./version");

// Input file comes from process.argv[2]
const FILENAME = process.argv[2];

console.log(FILENAME);

function logSupport(version) {
  if (V.isSupported(version)) {
    console.log(`${version} is currently supported`);
  } else {
    console.log(`${version} is currently not supported`);
  }
}

zip.listFileContents(FILENAME, (entries) => {
  let osguess1 = osguess.guessFromFilename(FILENAME);
  let osguess2 = osguess.guessFromContents(entries);

  if (osguess1 !== osguess2 && osguess1 && osguess2) {
    console.log(`Unsure about operating system. Going with ${osguess2}. Other option was ${osguess1}`);
  }
  if (osguess1 && !osguess2) {
    osguess2 = osguess1
  }
  let arch = osguess.guessArch(FILENAME, entries);
  let asar = finder.asar(entries);
  let binary = finder.binary(entries);
  let versionFiles = finder.version(entries);
  let enm = finder.findElectronPackageInsideNodeModules(entries);

  let filesToHash = finder.fingerprintable(entries);

  zip.extractSomeFiles(FILENAME, filesToHash, (dir) => {
    hashes = fp.getHashes(dir);
    guesses = fp.guessFromHashes(osguess2, arch, hashes);
    if (guesses.length == 1) {
      console.log("Fingerprint: " + guesses[0]);
      logSupport(guesses[0])
    } else if (guesses.length > 1) {
      console.log("Fingerprint: " + V.asText(guesses));
      logSupport(V.max(guesses))
    }

    cleanup.sync(dir);
  });

  // if (binary) {
  //   console.log(`${process.argv[2]}:${binary}`);
  // }
  if (versionFiles.length > 0) {
    versionFiles.map((f) => {
      zip.readFileContents(FILENAME, f, (c) => {
        console.log("Found Version file: " + c);
        logSupport(`${c}`)
      });
    });
  }
  if (asar.length > 0) {
    asar.forEach((a) => {
      console.log("Version Constraint (Unsupported): <v7.0.0");
    });
  }
  if (enm) {
    enm.forEach((a) => {
      zip.readFileContents(FILENAME, a, (c) => {
        try {
          let packageData = JSON.parse(c);
          console.log(
            "Found version in package.json file: " + packageData["version"]
          );
          logSupport(`v${packageData["version"]}`)
        } catch (e) {
          // TODO: Do something
        }
      });
    });
  }
});
diff --git a/src/os.js b/src/os.js
new file mode 100644
index 0000000..52919b9 100644
--- /dev/null
+++ a/src/os.js
@@ -1,0 +1,43 @@
// Guess the OS

const path = require('path')

module.exports = {
	guessFromFilename(inputFile) {
		let fn = path.basename(inputFile)
		if (fn.match(/linux/)) {
			return 'linux'
		} else if (fn.match(/mac/)) {
			return 'darwin'
    } else if (fn.match(/darwin/)) {
      return 'darwin'
		} else if (fn.match(/win/)) {
			return 'win32'
		} else {
			let ext = path.extname(inputFile).toLowerCase()
      if (ext == '.dmg') {return 'darwin'}
      if (ext == '.exe') {return 'win32'}
      if (['.deb', '.appimage', '.pacman'].indexOf(ext) !== -1) {
        return 'linux'
      }
		}
		return null;
	},
  guessArch(filename, entries) {
    return 'x64';
  },
	guessFromContents(entries) {
		for (i in entries) {
      let entry = entries[i]
			if (path.extname(entry.file) == ".so") {
				return 'linux'
			} else if (path.extname(entry.file) == '.dll') {
				return 'win32'
			} else if (path.extname(entry.file) == '.dylib') {
				return 'darwin'
			} else if (path.extname(entry.file) == '.plist') {
				return 'darwin'
			}
		}
	}
}
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 0000000..c74d212 100644
--- /dev/null
+++ a/src/utils.js
@@ -1,0 +1,5 @@
module.exports = {
  isDirectory: function(a) {
     return (a? a[0] == "D" : null)
  },
};
diff --git a/src/version.js b/src/version.js
new file mode 100644
index 0000000..2a92d4e 100644
--- /dev/null
+++ a/src/version.js
@@ -1,0 +1,18 @@
const semverSort = require('semver-sort');
const VERSIONS = require('./versions')

module.exports = {
  asText: function(listOfVersions) {
    sorted = semverSort.asc(listOfVersions);
    return `${sorted[0]}-${sorted[sorted.length-1]}`
  },

  max: function(listOfVersions) {
    sorted = semverSort.asc(listOfVersions);
    return sorted[sorted.length-1];
  },

  isSupported: function(v) {
    return (VERSIONS['supported'].indexOf(v) !== -1)
  }
}
diff --git a/src/versions.json b/src/versions.json
new file mode 100644
index 0000000..937b8d0 100644
--- /dev/null
+++ a/src/versions.json
@@ -1,0 +1,446 @@
{
  "supported": ["13.1.6", "12.0.14", "11.4.10"],
  "all": [
    "v0.24.0",
    "v0.25.0",
    "v0.25.1",
    "v0.25.2",
    "v0.25.3",
    "v0.26.0",
    "v0.26.1",
    "v0.27.0",
    "v0.27.1",
    "v0.27.2",
    "v0.27.3",
    "v0.28.0",
    "v0.28.1",
    "v0.28.2",
    "v0.28.3",
    "v0.29.0",
    "v0.29.1",
    "v0.29.2",
    "v0.30.0",
    "v0.30.1",
    "v0.30.2",
    "v0.30.3",
    "v0.30.4",
    "v0.30.5",
    "v0.30.6",
    "v0.30.7",
    "v0.30.8",
    "v0.31.0",
    "v0.31.1",
    "v0.31.2",
    "v0.32.0",
    "v0.32.1",
    "v0.32.2",
    "v0.32.3",
    "v0.33.0",
    "v0.33.1",
    "v0.33.2",
    "v0.33.3",
    "v0.33.4",
    "v0.33.5",
    "v0.33.6",
    "v0.33.7",
    "v0.33.8",
    "v0.33.9",
    "v0.34.0",
    "v0.34.1",
    "v0.34.2",
    "v0.34.3",
    "v0.34.4",
    "v0.34.5",
    "v0.35.0",
    "v0.35.1",
    "v0.35.2",
    "v0.35.3",
    "v0.35.4",
    "v0.35.5",
    "v0.35.6",
    "v0.36.0",
    "v0.36.1",
    "v0.36.10",
    "v0.36.11",
    "v0.36.12",
    "v0.36.2",
    "v0.36.3",
    "v0.36.4",
    "v0.36.5",
    "v0.36.6",
    "v0.36.7",
    "v0.36.8",
    "v0.36.9",
    "v0.37.0",
    "v0.37.1",
    "v0.37.2",
    "v0.37.3",
    "v0.37.4",
    "v0.37.5",
    "v0.37.6",
    "v0.37.7",
    "v0.37.8",
    "v1.0.0",
    "v1.0.1",
    "v1.0.2",
    "v1.1.0",
    "v1.1.1",
    "v1.1.2",
    "v1.1.3",
    "v1.2.0",
    "v1.2.1",
    "v1.2.2",
    "v1.2.3",
    "v1.2.4",
    "v1.2.5",
    "v1.2.6",
    "v1.2.7",
    "v1.2.8",
    "v1.3.0",
    "v1.3.1",
    "v1.3.10",
    "v1.3.11",
    "v1.3.12",
    "v1.3.13",
    "v1.3.14",
    "v1.3.15",
    "v1.3.2",
    "v1.3.3",
    "v1.3.4",
    "v1.3.5",
    "v1.3.6",
    "v1.3.7",
    "v1.3.8",
    "v1.3.9",
    "v1.4.0",
    "v1.4.1",
    "v1.4.10",
    "v1.4.11",
    "v1.4.12",
    "v1.4.13",
    "v1.4.14",
    "v1.4.15",
    "v1.4.16",
    "v1.4.2",
    "v1.4.3",
    "v1.4.4",
    "v1.4.5",
    "v1.4.6",
    "v1.4.7",
    "v1.4.8",
    "v1.4.9",
    "v1.5.0",
    "v1.5.1",
    "v1.6.0",
    "v1.6.1",
    "v1.6.10",
    "v1.6.11",
    "v1.6.12",
    "v1.6.13",
    "v1.6.14",
    "v1.6.15",
    "v1.6.16",
    "v1.6.17",
    "v1.6.18",
    "v1.6.2",
    "v1.6.3",
    "v1.6.4",
    "v1.6.5",
    "v1.6.6",
    "v1.6.7",
    "v1.6.8",
    "v1.6.9",
    "v1.7.0",
    "v1.7.1",
    "v1.7.10",
    "v1.7.11",
    "v1.7.12",
    "v1.7.13",
    "v1.7.14",
    "v1.7.15",
    "v1.7.16",
    "v1.7.2",
    "v1.7.3",
    "v1.7.4",
    "v1.7.5",
    "v1.7.6",
    "v1.7.7",
    "v1.7.8",
    "v1.7.9",
    "v1.8.0",
    "v1.8.1",
    "v1.8.2",
    "v1.8.3",
    "v1.8.4",
    "v1.8.5",
    "v1.8.6",
    "v1.8.7",
    "v1.8.8",
    "v10.0.0",
    "v10.0.1",
    "v10.1.0",
    "v10.1.1",
    "v10.1.2",
    "v10.1.3",
    "v10.1.4",
    "v10.1.5",
    "v10.1.6",
    "v10.1.7",
    "v10.2.0",
    "v10.3.0",
    "v10.3.1",
    "v10.3.2",
    "v10.4.0",
    "v10.4.1",
    "v10.4.2",
    "v10.4.3",
    "v10.4.4",
    "v10.4.5",
    "v10.4.6",
    "v10.4.7",
    "v11.0.0",
    "v11.0.1",
    "v11.0.2",
    "v11.0.3",
    "v11.0.4",
    "v11.0.5",
    "v11.1.0",
    "v11.1.1",
    "v11.2.0",
    "v11.2.1",
    "v11.2.2",
    "v11.2.3",
    "v11.3.0",
    "v11.4.0",
    "v11.4.1",
    "v11.4.10",
    "v11.4.2",
    "v11.4.3",
    "v11.4.4",
    "v11.4.5",
    "v11.4.6",
    "v11.4.7",
    "v11.4.8",
    "v11.4.9",
    "v12.0.0",
    "v12.0.1",
    "v12.0.10",
    "v12.0.11",
    "v12.0.12",
    "v12.0.13",
    "v12.0.14",
    "v12.0.15",
    "v12.0.2",
    "v12.0.3",
    "v12.0.4",
    "v12.0.5",
    "v12.0.6",
    "v12.0.7",
    "v12.0.8",
    "v12.0.9",
    "v13.0.0",
    "v13.0.1",
    "v13.1.0",
    "v13.1.1",
    "v13.1.2",
    "v13.1.3",
    "v13.1.4",
    "v13.1.5",
    "v13.1.6",
    "v13.1.7",
    "v2.0.0",
    "v2.0.1",
    "v2.0.10",
    "v2.0.11",
    "v2.0.12",
    "v2.0.13",
    "v2.0.14",
    "v2.0.15",
    "v2.0.16",
    "v2.0.17",
    "v2.0.18",
    "v2.0.2",
    "v2.0.3",
    "v2.0.4",
    "v2.0.5",
    "v2.0.6",
    "v2.0.7",
    "v2.0.8",
    "v2.0.9",
    "v2.1.0-unsupported-20180809",
    "v2.1.0-unsupported.20180809",
    "v3.0.0",
    "v3.0.1",
    "v3.0.10",
    "v3.0.11",
    "v3.0.12",
    "v3.0.13",
    "v3.0.14",
    "v3.0.15",
    "v3.0.16",
    "v3.0.2",
    "v3.0.3",
    "v3.0.4",
    "v3.0.5",
    "v3.0.6",
    "v3.0.7",
    "v3.0.8",
    "v3.0.9",
    "v3.1.0",
    "v3.1.1",
    "v3.1.10",
    "v3.1.11",
    "v3.1.12",
    "v3.1.13",
    "v3.1.2",
    "v3.1.3",
    "v3.1.4",
    "v3.1.5",
    "v3.1.6",
    "v3.1.7",
    "v3.1.8",
    "v3.1.9",
    "v4.0.0",
    "v4.0.1",
    "v4.0.2",
    "v4.0.3",
    "v4.0.4",
    "v4.0.5",
    "v4.0.6",
    "v4.0.7",
    "v4.0.8",
    "v4.1.0",
    "v4.1.1",
    "v4.1.2",
    "v4.1.3",
    "v4.1.4",
    "v4.1.5",
    "v4.2.0",
    "v4.2.1",
    "v4.2.10",
    "v4.2.11",
    "v4.2.12",
    "v4.2.2",
    "v4.2.3",
    "v4.2.4",
    "v4.2.5",
    "v4.2.6",
    "v4.2.7",
    "v4.2.8",
    "v4.2.9",
    "v5.0.0",
    "v5.0.1",
    "v5.0.10",
    "v5.0.11",
    "v5.0.12",
    "v5.0.13",
    "v5.0.2",
    "v5.0.3",
    "v5.0.4",
    "v5.0.5",
    "v5.0.6",
    "v5.0.7",
    "v5.0.8",
    "v5.0.9",
    "v6.0.0",
    "v6.0.1",
    "v6.0.10",
    "v6.0.11",
    "v6.0.12",
    "v6.0.2",
    "v6.0.3",
    "v6.0.4",
    "v6.0.5",
    "v6.0.6",
    "v6.0.7",
    "v6.0.8",
    "v6.0.9",
    "v6.1.0",
    "v6.1.1",
    "v6.1.10",
    "v6.1.11",
    "v6.1.12",
    "v6.1.2",
    "v6.1.3",
    "v6.1.4",
    "v6.1.5",
    "v6.1.6",
    "v6.1.7",
    "v6.1.8",
    "v6.1.9",
    "v7.0.0",
    "v7.0.1",
    "v7.1.0",
    "v7.1.1",
    "v7.1.10",
    "v7.1.11",
    "v7.1.12",
    "v7.1.13",
    "v7.1.14",
    "v7.1.2",
    "v7.1.3",
    "v7.1.4",
    "v7.1.5",
    "v7.1.6",
    "v7.1.7",
    "v7.1.8",
    "v7.1.9",
    "v7.2.0",
    "v7.2.1",
    "v7.2.2",
    "v7.2.3",
    "v7.2.4",
    "v7.3.0",
    "v7.3.1",
    "v7.3.2",
    "v7.3.3",
    "v8.0.0",
    "v8.0.1",
    "v8.0.2",
    "v8.0.3",
    "v8.1.0",
    "v8.1.1",
    "v8.2.0",
    "v8.2.1",
    "v8.2.2",
    "v8.2.3",
    "v8.2.4",
    "v8.2.5",
    "v8.3.0",
    "v8.3.1",
    "v8.3.2",
    "v8.3.3",
    "v8.3.4",
    "v8.4.0",
    "v8.4.1",
    "v8.5.0",
    "v8.5.1",
    "v8.5.2",
    "v8.5.3",
    "v8.5.4",
    "v8.5.5",
    "v9.0.0",
    "v9.0.1",
    "v9.0.2",
    "v9.0.3",
    "v9.0.4",
    "v9.0.5",
    "v9.0.6",
    "v9.1.0",
    "v9.1.1",
    "v9.1.2",
    "v9.2.0",
    "v9.2.1",
    "v9.3.0",
    "v9.3.1",
    "v9.3.2",
    "v9.3.3",
    "v9.3.4",
    "v9.3.5",
    "v9.4.0",
    "v9.4.1",
    "v9.4.2",
    "v9.4.3",
    "v9.4.4"
  ]

}

diff --git a/src/zip.js b/src/zip.js
new file mode 100644
index 0000000..51e5b0a 100644
--- /dev/null
+++ a/src/zip.js
@@ -1,0 +1,41 @@
const Seven = require("node-7z");
const sevenBin = require('7zip-bin').path7za;
const path = require("path");
const fs = require("fs");

module.exports = {
  readFileContents: function(archive, filepath, cb) {
    // TODO: Create a new temp directory
    let stream = Seven.extract(archive, "/tmp", {
      recursive: true,
      $cherryPick: filepath,
      $bin: sevenBin
    });
    let fn = path.basename(filepath);
    stream.on("end", ()=>{
      cb(fs.readFileSync(`/tmp/${fn}`, {encoding: 'utf8'}))
    });
  },
  extractSomeFiles: function(archive, list, cb) {
    let dir = fs.mkdtempSync('/tmp/which-electron')
    let stream = Seven.extract(archive, dir, {
      $cherryPick: list,
      $bin: sevenBin
    })
    stream.on('end', ()=>{
      cb(dir)
    })
  },
  listFileContents: function(archive, cb) {
    let zip = Seven.list(archive, {
      $bin: sevenBin
    });
    let entries = [];
    zip.on("data", (data) => {
      entries.push(data);
    });
    zip.on("end", () => {
      cb(entries);
    });
  },
};
diff --git a/tests/finder.js b/tests/finder.js
index 1d821e2..7b9e03f 100644
--- a/tests/finder.js
+++ a/tests/finder.js
@@ -1,5 +1,5 @@
const test = require("kuta").test;
const finder = require("../finder");
const finder = require("../src/finder");
const assert = require("assert");
const _ = require("./utils");

diff --git a/tests/fingerprint.js b/tests/fingerprint.js
index 8023d72..9aa9d08 100644
--- a/tests/fingerprint.js
+++ a/tests/fingerprint.js
@@ -1,5 +1,5 @@
const test = require("kuta").test;
const fp = require("../fingerprint");
const fp = require("../src/fingerprint");
const assert = require("assert");

test("it should work with a single fingerprint", () => {
diff --git a/tests/os.js b/tests/os.js
index 78928f0..839bf11 100644
--- a/tests/os.js
+++ a/tests/os.js
@@ -1,5 +1,5 @@
const test = require('kuta').test;
const os = require('../os')
const os = require('../src/os')
const assert = require('assert')
const _ = require('./utils')