Browse Source

Merge remote-tracking branch 'upstream/master' into dish-fix

n1073645 5 years ago
parent
commit
6e411c9dd9

File diff suppressed because it is too large
+ 316 - 316
package-lock.json


+ 18 - 18
package.json

@@ -1,6 +1,6 @@
 {
   "name": "cyberchef",
-  "version": "9.11.11",
+  "version": "9.11.14",
   "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
   "author": "n1474335 <n1474335@gmail.com>",
   "homepage": "https://gchq.github.io/CyberChef",
@@ -36,20 +36,20 @@
     "node >= 10"
   ],
   "devDependencies": {
-    "@babel/core": "^7.7.2",
-    "@babel/plugin-transform-runtime": "^7.6.2",
-    "@babel/preset-env": "^7.7.1",
-    "autoprefixer": "^9.7.2",
+    "@babel/core": "^7.7.5",
+    "@babel/plugin-transform-runtime": "^7.7.6",
+    "@babel/preset-env": "^7.7.6",
+    "autoprefixer": "^9.7.3",
     "babel-eslint": "^10.0.3",
     "babel-loader": "^8.0.6",
     "babel-plugin-dynamic-import-node": "^2.3.0",
     "chromedriver": "^78.0.1",
     "colors": "^1.4.0",
     "copy-webpack-plugin": "^5.0.5",
-    "css-loader": "^3.2.0",
-    "eslint": "^6.6.0",
+    "css-loader": "^3.2.1",
+    "eslint": "^6.7.2",
     "exports-loader": "^0.7.0",
-    "file-loader": "^4.2.0",
+    "file-loader": "^5.0.2",
     "grunt": "^1.0.4",
     "grunt-accessibility": "~6.0.0",
     "grunt-chmod": "~1.1.1",
@@ -65,17 +65,17 @@
     "html-webpack-plugin": "^3.2.0",
     "imports-loader": "^0.8.0",
     "mini-css-extract-plugin": "^0.8.0",
-    "nightwatch": "^1.2.4",
+    "nightwatch": "^1.3.2",
     "node-sass": "^4.13.0",
-    "postcss-css-variables": "^0.13.0",
+    "postcss-css-variables": "^0.14.0",
     "postcss-import": "^12.0.1",
     "postcss-loader": "^3.0.0",
     "prompt": "^1.0.0",
     "sass-loader": "^8.0.0",
     "sitemap": "^5.1.0",
-    "style-loader": "^1.0.0",
-    "svg-url-loader": "^3.0.2",
-    "url-loader": "^2.2.0",
+    "style-loader": "^1.0.1",
+    "svg-url-loader": "^3.0.3",
+    "url-loader": "^3.0.0",
     "webpack": "^4.41.2",
     "webpack-bundle-analyzer": "^3.6.0",
     "webpack-dev-server": "^3.9.0",
@@ -84,20 +84,20 @@
   },
   "dependencies": {
     "@babel/polyfill": "^7.7.0",
-    "@babel/runtime": "^7.7.2",
+    "@babel/runtime": "^7.7.6",
     "arrive": "^2.4.1",
     "avsc": "^5.4.16",
     "babel-plugin-transform-builtin-extend": "1.1.2",
     "bcryptjs": "^2.4.3",
     "bignumber.js": "^9.0.0",
     "blakejs": "^1.1.0",
-    "bootstrap": "4.3.1",
-    "bootstrap-colorpicker": "^3.1.2",
+    "bootstrap": "4.4.1",
+    "bootstrap-colorpicker": "^3.2.0",
     "bootstrap-material-design": "^4.1.2",
     "bson": "^4.0.2",
     "chi-squared": "^1.1.0",
     "codepage": "^1.14.0",
-    "core-js": "^3.4.1",
+    "core-js": "^3.4.8",
     "crypto-api": "^0.8.5",
     "crypto-js": "^3.1.9-1",
     "ctph.js": "0.0.5",
@@ -113,7 +113,7 @@
     "file-saver": "^2.0.2",
     "geodesy": "^1.1.3",
     "highlight.js": "^9.16.2",
-    "jimp": "^0.8.5",
+    "jimp": "^0.9.3",
     "jquery": "3.4.1",
     "js-crc": "^0.2.0",
     "js-sha3": "^0.8.0",

+ 4 - 4
src/core/Chef.mjs

@@ -73,10 +73,10 @@ class Chef {
         // The threshold is specified in KiB.
         const threshold = (options.ioDisplayThreshold || 1024) * 1024;
         const returnType =
-            this.dish.size > threshold ?
-                Dish.ARRAY_BUFFER :
-                this.dish.type === Dish.HTML ?
-                    Dish.HTML :
+            this.dish.type === Dish.HTML ?
+                Dish.HTML :
+                this.dish.size > threshold ?
+                    Dish.ARRAY_BUFFER :
                     Dish.STRING;
 
         return {

+ 38 - 0
src/core/Utils.mjs

@@ -591,6 +591,44 @@ class Utils {
         return utf8 ? Utils.byteArrayToUtf8(arr) : Utils.byteArrayToChars(arr);
     }
 
+    /**
+     * Calculates the Shannon entropy for a given set of data.
+     *
+     * @param {Uint8Array|ArrayBuffer} input
+     * @returns {number}
+     */
+    static calculateShannonEntropy(data) {
+        if (data instanceof ArrayBuffer) {
+            data = new Uint8Array(data);
+        }
+        const prob = [],
+            occurrences = new Array(256).fill(0);
+
+        // Count occurrences of each byte in the input
+        let i;
+        for (i = 0; i < data.length; i++) {
+            occurrences[data[i]]++;
+        }
+
+        // Store probability list
+        for (i = 0; i < occurrences.length; i++) {
+            if (occurrences[i] > 0) {
+                prob.push(occurrences[i] / data.length);
+            }
+        }
+
+        // Calculate Shannon entropy
+        let entropy = 0,
+            p;
+
+        for (i = 0; i < prob.length; i++) {
+            p = prob[i];
+            entropy += p * Math.log(p) / Math.log(2);
+        }
+
+        return -entropy;
+    }
+
 
     /**
      * Parses CSV data and returns it as a two dimensional array or strings.

+ 1 - 0
src/web/Manager.mjs

@@ -197,6 +197,7 @@ class Manager {
         this.addMultiEventListener("#output-text", "mousedown dblclick select",  this.highlighter.outputMousedown, this.highlighter);
         this.addMultiEventListener("#output-html", "mousedown dblclick select",  this.highlighter.outputHtmlMousedown, this.highlighter);
         this.addDynamicListener("#output-file-download", "click", this.output.downloadFile, this.output);
+        this.addDynamicListener("#output-file-show-all", "click", this.output.showAllFile, this.output);
         this.addDynamicListener("#output-file-slice i", "click", this.output.displayFileSlice, this.output);
         document.getElementById("show-file-overlay").addEventListener("click", this.output.showFileOverlayClick.bind(this.output));
         this.addDynamicListener(".extract-file,.extract-file i", "click", this.output.extractFileClick, this.output);

+ 22 - 18
src/web/html/index.html

@@ -29,7 +29,7 @@
         <meta name="description" content="The Cyber Swiss Army Knife - a web app for encryption, encoding, compression and data analysis" />
         <meta name="keywords" content="base64, hex, decode, encode, encrypt, decrypt, compress, decompress, regex, regular expressions, hash, crypt, hexadecimal, user agent, url, certificate, x.509, parser, JSON, gzip,  md5, sha1, aes, des, blowfish, xor" />
 
-        <link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico') %>" />
+        <link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico').default %>" />
 
         <script type="application/javascript">
             "use strict";
@@ -197,7 +197,7 @@
                             </button>
 
                             <button type="button" class="mx-2 btn btn-lg btn-success btn-raised btn-block" id="bake">
-                                <img aria-hidden="true" src="<%- require('../static/images/cook_male-32x32.png') %>" alt="Chef Icon"/>
+                                <img aria-hidden="true" src="<%- require('../static/images/cook_male-32x32.png').default %>" alt="Chef Icon"/>
                                 <span>Bake!</span>
                             </button>
 
@@ -271,7 +271,7 @@
                                 <div class="file-overlay" id="file-overlay"></div>
                                 <div style="position: relative; height: 100%;">
                                     <div class="io-card card">
-                                        <img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon" id="input-file-thumbnail"/>
+                                        <img aria-hidden="true" src="<%- require('../static/images/file-128x128.png').default %>" alt="File icon" id="input-file-thumbnail"/>
                                         <div class="card-body">
                                             <button type="button" class="close" id="input-file-close">&times;</button>
                                             Name: <span id="input-file-name"></span><br>
@@ -346,24 +346,26 @@
                                 <div id="output-highlighter" class="no-select"></div>
                                 <div id="output-html"></div>
                                 <textarea id="output-text" readonly="readonly" spellcheck="false"></textarea>
-                                <img id="show-file-overlay" aria-hidden="true" src="<%- require('../static/images/file-32x32.png') %>" alt="Show file overlay" title="Show file overlay"/>
+                                <img id="show-file-overlay" aria-hidden="true" src="<%- require('../static/images/file-32x32.png').default %>" alt="Show file overlay" title="Show file overlay"/>
                                 <div id="output-file">
                                     <div class="file-overlay"></div>
                                     <div style="position: relative; height: 100%;">
                                         <div class="io-card card">
-                                            <img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon"/>
+                                            <img aria-hidden="true" src="<%- require('../static/images/file-128x128.png').default %>" alt="File icon"/>
                                             <div class="card-body">
                                                 Size: <span id="output-file-size"></span><br>
                                                 <button id="output-file-download" type="button" class="btn btn-primary btn-outline">Download</button>
+                                                <button id="output-file-show-all" type="button" class="btn btn-warning btn-outline" data-toggle="tooltip" title="Warning: This could crash your browser. Use at your own risk.">Show all</button>
                                                 <div class="input-group">
-                                                    <span class="input-group-btn">
-                                                        <button id="output-file-slice" type="button" class="btn btn-secondary bmd-btn-icon" title="View slice">
+                                                    <span class="input-group-prepend">
+                                                        <button id="output-file-slice" type="button" class="btn btn-secondary bmd-btn-icon" data-toggle="tooltip" title="View slice">
                                                             <i class="material-icons">search</i>
                                                         </button>
                                                     </span>
-                                                    <input type="number" class="form-control" id="output-file-slice-from" placeholder="From" value="0" step="1024" min="0">
+                                                    <input type="number" class="form-control" id="output-file-slice-from" placeholder="From" value="0" step="128" min="0">
                                                     <div class="input-group-addon">to</div>
-                                                    <input type="number" class="form-control" id="output-file-slice-to" placeholder="To" value="2048" step="1024" min="0">
+                                                    <input type="number" class="form-control" id="output-file-slice-to" placeholder="To" value="256" step="128" min="0">
+                                                    <div class="input-group-addon">KiB</div>
                                                 </div>
                                             </div>
                                         </div>
@@ -489,6 +491,15 @@
                             </select>
                         </div>
 
+                        <div class="form-group option-item">
+                            <label for="preserveCR" class="bmd-label-floating"> Preserve carriage returns (0x0d)</label>
+                            <select class="form-control" option="preserveCR" id="preserveCR" data-toggle="tooltip" data-placement="bottom" data-offset="-10%" data-html="true" title="HTML textareas don't support carriage returns, so if we want to preserve them in our input, we have to disable editing.<br><br>The default option is to only do this for high-entropy inputs, but you can force the choice using this dropdown.">
+                                <option value="entropy">For high-entropy inputs</option>
+                                <option value="always">Always</option>
+                                <option value="never">Never</option>
+                            </select>
+                        </div>
+
                         <div class="form-group option-item">
                             <label for="errorTimeout" class="bmd-label-floating">Operation error timeout in ms (0 for never)</label>
                             <input type="number" class="form-control" option="errorTimeout" id="errorTimeout">
@@ -573,13 +584,6 @@
                                 Keep the current tab in sync between the input and output
                             </label>
                         </div>
-
-                        <div class="checkbox option-item">
-                            <label for="preserveCR" data-toggle="tooltip" data-placement="right" data-html="true" title="As HTML textareas don't support carriage returns, editing input must be turned off to preserve them.<br><br>When this option is enabled, editing is disabled for pasted text that contains carriage returns. Otherwise, editing will remain enabled but carriage returns will not be preserved.">
-                                <input type="checkbox" option="preserveCR" id="preserveCR">
-                                Preserve carriage returns when pasting an input
-                            </label>
-                        </div>
                     </div>
                     <div class="modal-footer">
                         <button type="button" class="btn btn-secondary" id="reset-options">Reset options to default</button>
@@ -622,7 +626,7 @@
                         <h5 class="modal-title">CyberChef - The Cyber Swiss Army Knife</h5>
                     </div>
                     <div class="modal-body">
-                        <img aria-hidden="true" class="about-img-left" src="<%- require('../static/images/cyberchef-128x128.png') %>" alt="CyberChef Logo"/>
+                        <img aria-hidden="true" class="about-img-left" src="<%- require('../static/images/cyberchef-128x128.png').default %>" alt="CyberChef Logo"/>
                         <p class="subtext">
                             Version <%=  htmlWebpackPlugin.options.version %><br>
                             Compile time: <%= htmlWebpackPlugin.options.compileTime %>
@@ -745,7 +749,7 @@
                         <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                     </div>
                     <a href="https://github.com/gchq/CyberChef">
-                        <img aria-hidden="true" style="position: absolute; top: 0; right: 0; border: 0;" src="<%- require('../static/images/fork_me.png') %>" alt="Fork me on GitHub">
+                        <img aria-hidden="true" style="position: absolute; top: 0; right: 0; border: 0;" src="<%- require('../static/images/fork_me.png').default %>" alt="Fork me on GitHub">
                     </a>
                 </div>
             </div>

+ 2 - 3
src/web/index.js

@@ -49,13 +49,12 @@ function main() {
         attemptHighlight:    true,
         theme:               "classic",
         useMetaKey:          false,
-        ioDisplayThreshold:  512,
+        ioDisplayThreshold:  2048,
         logLevel:            "info",
         autoMagic:           true,
         imagePreview:        true,
         syncTabs:            true,
-        preserveCR:          true,
-        userSetCR:           false
+        preserveCR:          "entropy"
     };
 
     document.removeEventListener("DOMContentLoaded", main, false);

+ 5 - 0
src/web/stylesheets/components/_pane.css

@@ -98,6 +98,11 @@
 .io-card.card input[type=number] {
     padding-right: 6px;
     padding-left: 6px;
+    height: unset;
+}
+
+.io-card.card .input-group {
+    padding-top: 5px;
 }
 
 #files .card-header .float-right a:hover {

+ 25 - 25
src/web/waiters/InputWaiter.mjs

@@ -476,7 +476,7 @@ class InputWaiter {
      */
     resetFileThumb() {
         const fileThumb = document.getElementById("input-file-thumbnail");
-        fileThumb.src = require("../static/images/file-128x128.png");
+        fileThumb.src = require("../static/images/file-128x128.png").default;
     }
 
     /**
@@ -767,7 +767,9 @@ class InputWaiter {
             // and manually fire inputChange()
             inputText.value = val;
             inputText.setSelectionRange(selStart + pastedData.length, selStart + pastedData.length);
-            this.debounceInputChange(e);
+            // Don't debounce here otherwise the keyup event for the Ctrl key will cancel an autobake
+            // (at least for large inputs)
+            this.inputChange(e, true);
         }
     }
 
@@ -858,31 +860,29 @@ class InputWaiter {
         if (input.indexOf("\r") < 0) return false;
 
         const optionsStr = "This behaviour can be changed in the <a href='#' onclick='document.getElementById(\"options\").click()'>Options pane</a>";
-        if (!this.app.options.userSetCR) {
-            // User has not set a CR preference yet
-            let preserve = await new Promise(function(resolve, reject) {
-                this.app.confirm(
-                    "Carriage Return Detected",
-                    "A <a href='https://wikipedia.org/wiki/Carriage_return'>carriage return</a> (<code>\\r</code>, <code>0x0d</code>) was detected in your input. As HTML textareas <a href='https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element'>can't display carriage returns</a>, editing must be turned off to preserve them. <br>Alternatively, you can enable editing but your carriage returns will not be preserved.<br><br>This preference will be saved but can be toggled in the options pane.",
-                    "Preserve Carriage Returns",
-                    "Enable Editing", resolve, this);
-            }.bind(this));
-            if (preserve === undefined) {
-                // The confirm pane was closed without picking a specific choice
-                this.app.alert(`Not preserving carriage returns.\n${optionsStr}`, 5000);
-                preserve = false;
-            }
-            this.manager.options.updateOption("preserveCR", preserve);
-            this.manager.options.updateOption("userSetCR", true);
-        } else {
-            if (this.app.options.preserveCR) {
-                this.app.alert(`A carriage return (\\r, 0x0d) was detected in your input, so editing has been disabled to preserve it.<br>${optionsStr}`, 10000);
-            } else {
-                this.app.alert(`A carriage return (\\r, 0x0d) was detected in your input. Editing is remaining enabled, but carriage returns will not be preserved.<br>${optionsStr}`, 10000);
-            }
+        const preserveStr = `A carriage return (\\r, 0x0d) was detected in your input. To preserve it, editing has been disabled.<br>${optionsStr}`;
+        const dontPreserveStr = `A carriage return (\\r, 0x0d) was detected in your input. It has not been preserved.<br>${optionsStr}`;
+
+        switch (this.app.options.preserveCR) {
+            case "always":
+                this.app.alert(preserveStr, 6000);
+                return true;
+            case "never":
+                this.app.alert(dontPreserveStr, 6000);
+                return false;
+        }
+
+        // Only preserve for high-entropy inputs
+        const data = Utils.strToArrayBuffer(input);
+        const entropy = Utils.calculateShannonEntropy(data);
+
+        if (entropy > 6) {
+            this.app.alert(preserveStr, 6000);
+            return true;
         }
 
-        return this.app.options.preserveCR;
+        this.app.alert(dontPreserveStr, 6000);
+        return false;
     }
 
     /**

+ 35 - 2
src/web/waiters/OutputWaiter.mjs

@@ -1122,8 +1122,8 @@ class OutputWaiter {
             showFileOverlay = document.getElementById("show-file-overlay"),
             sliceFromEl = document.getElementById("output-file-slice-from"),
             sliceToEl = document.getElementById("output-file-slice-to"),
-            sliceFrom = parseInt(sliceFromEl.value, 10),
-            sliceTo = parseInt(sliceToEl.value, 10),
+            sliceFrom = parseInt(sliceFromEl.value, 10) * 1024,
+            sliceTo = parseInt(sliceToEl.value, 10) * 1024,
             output = this.outputs[this.manager.tabs.getActiveOutputTab()].data;
 
         let str;
@@ -1137,6 +1137,39 @@ class OutputWaiter {
         showFileOverlay.style.display = "block";
         outputText.value = Utils.printable(str, true);
 
+        outputText.style.display = "block";
+        outputHtml.style.display = "none";
+        outputFile.style.display = "none";
+        outputHighlighter.display = "block";
+        inputHighlighter.display = "block";
+
+        this.toggleLoader(false);
+    }
+
+    /**
+     * Handler for showing an entire file at user's discretion (even if it's way too big)
+     */
+    async showAllFile() {
+        document.querySelector("#output-loader .loading-msg").textContent = "Loading entire file at user instruction. This may cause a crash...";
+        this.toggleLoader(true);
+        const outputText = document.getElementById("output-text"),
+            outputHtml = document.getElementById("output-html"),
+            outputFile = document.getElementById("output-file"),
+            outputHighlighter = document.getElementById("output-highlighter"),
+            inputHighlighter = document.getElementById("input-highlighter"),
+            showFileOverlay = document.getElementById("show-file-overlay"),
+            output = this.outputs[this.manager.tabs.getActiveOutputTab()].data;
+
+        let str;
+        if (output.type === "ArrayBuffer") {
+            str = Utils.arrayBufferToStr(output.result);
+        } else {
+            str = Utils.arrayBufferToStr(await this.getDishBuffer(output.dish));
+        }
+
+        outputText.classList.remove("blur");
+        showFileOverlay.style.display = "none";
+        outputText.value = Utils.printable(str, true);
 
         outputText.style.display = "block";
         outputHtml.style.display = "none";

Some files were not shown because too many files changed in this diff