diff --git a/src/core/FlowControl.js b/src/core/FlowControl.js index e886be6c..5b7728ff 100755 --- a/src/core/FlowControl.js +++ b/src/core/FlowControl.js @@ -283,6 +283,18 @@ const FlowControl = { Properties `; + /** + * Returns a CSS colour value based on an integer input. + * + * @param {number} val + * @returns {string} + */ + function chooseColour(val) { + if (val < 3) return "green"; + if (val < 5) return "goldenrod"; + return "red"; + } + options.forEach(option => { // Construct recipe URL // Replace this Magic op with the generated recipe @@ -295,18 +307,20 @@ const FlowControl = { fileType = "", matchingOps = "", useful = "", - validUTF8 = option.isUTF8 ? "Valid UTF8\n" : ""; + entropy = `Entropy: ${option.entropy.toFixed(2)}`, + validUTF8 = option.isUTF8 ? "Valid UTF8\n" : ""; if (option.languageScores[0].probability > 0) { let likelyLangs = option.languageScores.filter(l => l.probability > 0); if (likelyLangs.length < 1) likelyLangs = [option.languageScores[0]]; - language = "Possible languages:\n " + likelyLangs.map(lang => { - return Magic.codeToLanguage(lang.lang); - }).join("\n ") + "\n"; + language = "" + + "Possible languages:\n " + likelyLangs.map(lang => { + return Magic.codeToLanguage(lang.lang); + }).join("\n ") + "\n"; } if (option.fileType) { - fileType = `File type: ${option.fileType.mime} (${option.fileType.ext})\n`; + fileType = `File type: ${option.fileType.mime} (${option.fileType.ext})\n`; } if (option.matchingOps.length) { @@ -314,17 +328,17 @@ const FlowControl = { } if (option.useful) { - useful = "Useful op detected\n"; + useful = "Useful op detected\n"; } output += ` ${Utils.generatePrettyRecipe(option.recipe, true)} ${Utils.escapeHtml(Utils.printable(Utils.truncate(option.data, 99)))} - ${language}${fileType}${matchingOps}${useful}${validUTF8} + ${language}${fileType}${matchingOps}${useful}${validUTF8}${entropy} `; }); - output += ""; + output += ""; if (!options.length) { output = "Nothing of interest could be detected about the input data.\nHave you tried modifying the operation arguments?"; diff --git a/src/core/lib/Magic.js b/src/core/lib/Magic.js index 47dadb9e..868ad976 100644 --- a/src/core/lib/Magic.js +++ b/src/core/lib/Magic.js @@ -173,6 +173,24 @@ class Magic { return true; } + /** + * Calculates the Shannon entropy of the input data. + * + * @returns {number} + */ + calcEntropy() { + let prob = this._freqDist(), + entropy = 0, + p; + + for (let i = 0; i < prob.length; i++) { + p = prob[i] / 100; + if (p === 0) continue; + entropy += p * Math.log(p) / Math.log(2); + } + return -entropy; + } + /** * Generate various simple brute-forced encodings of the data (trucated to 100 bytes). * @@ -262,6 +280,7 @@ class Magic { languageScores: this.detectLanguage(extLang), fileType: this.detectFileType(), isUTF8: this.isUTF8(), + entropy: this.calcEntropy(), matchingOps: matchingOps, useful: useful }); @@ -324,6 +343,10 @@ class Magic { aScore += a.recipe.length; bScore += b.recipe.length; + // Lower entropy is "better", so we add the entropy to the score + aScore += a.entropy; + bScore += b.entropy; + return aScore - bScore; }); } @@ -351,12 +374,14 @@ class Magic { } /** - * Calculates the number of times each byte appears in the input + * Calculates the number of times each byte appears in the input as a percentage * * @private * @returns {number[]} */ _freqDist() { + if (this.freqDist) return this.freqDist; + const len = this.inputBuffer.length; let i = len, counts = new Array(256).fill(0); @@ -367,9 +392,10 @@ class Magic { counts[this.inputBuffer[i]]++; } - return counts.map(c => { + this.freqDist = counts.map(c => { return c / len * 100; }); + return this.freqDist; } /**