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

This commit is contained in:
n1073645 2019-12-13 16:11:48 +00:00
commit 6e411c9dd9
10 changed files with 2085 additions and 1298 deletions

3163
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -591,6 +591,44 @@ class Utils {
return utf8 ? Utils.byteArrayToUtf8(arr) : Utils.byteArrayToChars(arr); 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. * Parses CSV data and returns it as a two dimensional array or strings.

View File

@ -197,6 +197,7 @@ class Manager {
this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter); 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.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-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); 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)); 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); this.addDynamicListener(".extract-file,.extract-file i", "click", this.output.extractFileClick, this.output);

View File

@ -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="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" /> <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"> <script type="application/javascript">
"use strict"; "use strict";
@ -197,7 +197,7 @@
</button> </button>
<button type="button" class="mx-2 btn btn-lg btn-success btn-raised btn-block" id="bake"> <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> <span>Bake!</span>
</button> </button>
@ -271,7 +271,7 @@
<div class="file-overlay" id="file-overlay"></div> <div class="file-overlay" id="file-overlay"></div>
<div style="position: relative; height: 100%;"> <div style="position: relative; height: 100%;">
<div class="io-card card"> <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"> <div class="card-body">
<button type="button" class="close" id="input-file-close">&times;</button> <button type="button" class="close" id="input-file-close">&times;</button>
Name: <span id="input-file-name"></span><br> Name: <span id="input-file-name"></span><br>
@ -346,24 +346,26 @@
<div id="output-highlighter" class="no-select"></div> <div id="output-highlighter" class="no-select"></div>
<div id="output-html"></div> <div id="output-html"></div>
<textarea id="output-text" readonly="readonly" spellcheck="false"></textarea> <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 id="output-file">
<div class="file-overlay"></div> <div class="file-overlay"></div>
<div style="position: relative; height: 100%;"> <div style="position: relative; height: 100%;">
<div class="io-card card"> <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"> <div class="card-body">
Size: <span id="output-file-size"></span><br> 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-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"> <div class="input-group">
<span class="input-group-btn"> <span class="input-group-prepend">
<button id="output-file-slice" type="button" class="btn btn-secondary bmd-btn-icon" title="View slice"> <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> <i class="material-icons">search</i>
</button> </button>
</span> </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> <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> </div>
</div> </div>
@ -489,6 +491,15 @@
</select> </select>
</div> </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"> <div class="form-group option-item">
<label for="errorTimeout" class="bmd-label-floating">Operation error timeout in ms (0 for never)</label> <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"> <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 Keep the current tab in sync between the input and output
</label> </label>
</div> </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>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" id="reset-options">Reset options to default</button> <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> <h5 class="modal-title">CyberChef - The Cyber Swiss Army Knife</h5>
</div> </div>
<div class="modal-body"> <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"> <p class="subtext">
Version <%= htmlWebpackPlugin.options.version %><br> Version <%= htmlWebpackPlugin.options.version %><br>
Compile time: <%= htmlWebpackPlugin.options.compileTime %> Compile time: <%= htmlWebpackPlugin.options.compileTime %>
@ -745,7 +749,7 @@
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div> </div>
<a href="https://github.com/gchq/CyberChef"> <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> </a>
</div> </div>
</div> </div>

View File

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

View File

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

View File

@ -476,7 +476,7 @@ class InputWaiter {
*/ */
resetFileThumb() { resetFileThumb() {
const fileThumb = document.getElementById("input-file-thumbnail"); 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() // and manually fire inputChange()
inputText.value = val; inputText.value = val;
inputText.setSelectionRange(selStart + pastedData.length, selStart + pastedData.length); 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; 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>"; const optionsStr = "This behaviour can be changed in the <a href='#' onclick='document.getElementById(\"options\").click()'>Options pane</a>";
if (!this.app.options.userSetCR) { const preserveStr = `A carriage return (\\r, 0x0d) was detected in your input. To preserve it, editing has been disabled.<br>${optionsStr}`;
// User has not set a CR preference yet const dontPreserveStr = `A carriage return (\\r, 0x0d) was detected in your input. It has not been preserved.<br>${optionsStr}`;
let preserve = await new Promise(function(resolve, reject) {
this.app.confirm( switch (this.app.options.preserveCR) {
"Carriage Return Detected", case "always":
"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.", this.app.alert(preserveStr, 6000);
"Preserve Carriage Returns", return true;
"Enable Editing", resolve, this); case "never":
}.bind(this)); this.app.alert(dontPreserveStr, 6000);
if (preserve === undefined) { return false;
// 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);
}
} }
return this.app.options.preserveCR; // 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;
}
this.app.alert(dontPreserveStr, 6000);
return false;
} }
/** /**

View File

@ -1122,8 +1122,8 @@ class OutputWaiter {
showFileOverlay = document.getElementById("show-file-overlay"), showFileOverlay = document.getElementById("show-file-overlay"),
sliceFromEl = document.getElementById("output-file-slice-from"), sliceFromEl = document.getElementById("output-file-slice-from"),
sliceToEl = document.getElementById("output-file-slice-to"), sliceToEl = document.getElementById("output-file-slice-to"),
sliceFrom = parseInt(sliceFromEl.value, 10), sliceFrom = parseInt(sliceFromEl.value, 10) * 1024,
sliceTo = parseInt(sliceToEl.value, 10), sliceTo = parseInt(sliceToEl.value, 10) * 1024,
output = this.outputs[this.manager.tabs.getActiveOutputTab()].data; output = this.outputs[this.manager.tabs.getActiveOutputTab()].data;
let str; let str;
@ -1137,6 +1137,39 @@ class OutputWaiter {
showFileOverlay.style.display = "block"; showFileOverlay.style.display = "block";
outputText.value = Utils.printable(str, true); 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"; outputText.style.display = "block";
outputHtml.style.display = "none"; outputHtml.style.display = "none";