From 1c72d60761f4df15a0a38b78d2939e6b6c1d3870 Mon Sep 17 00:00:00 2001 From: Nemo <me@captnemo.in> Date: Sun, 18 Jul 2021 17:26:38 +0530 Subject: [PATCH] Re-organize directory structure --- 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     -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') -- rgit 0.1.5