Compare commits

...

15 Commits

Author SHA1 Message Date
n1474335
ae20d82e1d Fix Nightwatch test 2019-08-30 15:30:30 +01:00
n1474335
463b2ce040 9.3.0 2019-08-30 11:58:37 +01:00
n1474335
412b47abba Updated CHANGELOG 2019-08-30 11:58:32 +01:00
n1474335
ed216eee73 Merge branch 'j433866-show-on-map' 2019-08-30 11:57:00 +01:00
n1474335
9dd5234962 Tidied up 'Show on map' operation 2019-08-30 11:56:48 +01:00
n1474335
018532016b Merge branch 'show-on-map' of https://github.com/j433866/CyberChef into j433866-show-on-map 2019-08-30 11:44:12 +01:00
n1474335
7dfecc38f6 Added more UI tests to ensure all modules load, categories can be viewed, and operations can be dragged 2019-08-30 11:43:30 +01:00
n1474335
572f035877 Background magic is now debounced to prevent it firing too often. 2019-08-29 14:08:56 +01:00
j433866
2628f17fae Change maps source to use Wikimedia maps.
Add link to Wikimedia maps ToS.
If there's no data, show the map anyway.
2019-08-29 11:43:45 +01:00
j433866
69fb6e77fc Merge remote-tracking branch 'upstream/master' into show-on-map 2019-08-29 10:42:40 +01:00
j433866
6992858e67 9.2.3 2019-08-29 10:23:41 +01:00
j433866
59917cca45 Add overflow CSS rule to fix scrolling. Fixes #626 2019-08-29 10:18:52 +01:00
n1474335
2f0b959aa4 Fixed copy:ghPages docs error 2019-08-28 18:48:31 +01:00
j433866
b491b9d77d Move conversion of co-ordinates to run() instead of present() 2019-01-18 11:31:53 +00:00
j433866
237f792fb4 Add new Show on map operation 2019-01-18 11:19:06 +00:00
15 changed files with 228 additions and 49 deletions

View File

@@ -2,6 +2,9 @@
All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master).
### [9.3.0] - 2019-08-30
- 'Show on map' operation added [@j433866] | [#477]
### [9.2.0] - 2019-08-23
- 'Parse UDP' operation added [@h345983745] | [#614]
@@ -170,6 +173,7 @@ All major and minor version changes will be documented in this file. Details of
[9.3.0]: https://github.com/gchq/CyberChef/releases/tag/v9.3.0
[9.2.0]: https://github.com/gchq/CyberChef/releases/tag/v9.2.0
[9.1.0]: https://github.com/gchq/CyberChef/releases/tag/v9.1.0
[9.0.0]: https://github.com/gchq/CyberChef/releases/tag/v9.0.0
@@ -279,6 +283,7 @@ All major and minor version changes will be documented in this file. Details of
[#467]: https://github.com/gchq/CyberChef/pull/467
[#468]: https://github.com/gchq/CyberChef/pull/468
[#476]: https://github.com/gchq/CyberChef/pull/476
[#477]: https://github.com/gchq/CyberChef/pull/477
[#489]: https://github.com/gchq/CyberChef/pull/489
[#496]: https://github.com/gchq/CyberChef/pull/496
[#506]: https://github.com/gchq/CyberChef/pull/506

View File

@@ -270,11 +270,7 @@ module.exports = function (grunt) {
{
src: "build/prod/index.html",
dest: "build/prod/index.html"
},
{
expand: true,
dest: "build/prod/"
},
}
]
},
standalone: {

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "9.2.2",
"version": "9.3.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "9.2.2",
"version": "9.3.0",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",

View File

@@ -1303,6 +1303,30 @@ export function sendStatusMessage(msg) {
console.debug(msg);
}
const debounceTimeouts = {};
/**
* Debouncer to stop functions from being executed multiple times in a
* short space of time
* https://davidwalsh.name/javascript-debounce-function
*
* @param {function} func - The function to be executed after the debounce time
* @param {number} wait - The time (ms) to wait before executing the function
* @param {string} id - Unique ID to reference the timeout for the function
* @param {object} scope - The object to bind to the debounced function
* @param {array} args - Array of arguments to be passed to func
* @returns {function}
*/
export function debounce(func, wait, id, scope, args) {
return function() {
const later = function() {
func.apply(scope, args);
};
clearTimeout(debounceTimeouts[id]);
debounceTimeouts[id] = setTimeout(later, wait);
};
}
/*
* Polyfills

View File

@@ -228,6 +228,7 @@
"Convert speed",
"Convert data units",
"Convert co-ordinate format",
"Show on map",
"Parse UNIX file permissions",
"Swap endianness",
"Parse colour code",

View File

@@ -0,0 +1,113 @@
/**
* @author j433866 [j433866@gmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Show on map operation
*/
class ShowOnMap extends Operation {
/**
* ShowOnMap constructor
*/
constructor() {
super();
this.name = "Show on map";
this.module = "Hashing";
this.description = "Displays co-ordinates on a slippy map.<br><br>Co-ordinates will be converted to decimal degrees before being shown on the map.<br><br>Supported formats:<ul><li>Degrees Minutes Seconds (DMS)</li><li>Degrees Decimal Minutes (DDM)</li><li>Decimal Degrees (DD)</li><li>Geohash</li><li>Military Grid Reference System (MGRS)</li><li>Ordnance Survey National Grid (OSNG)</li><li>Universal Transverse Mercator (UTM)</li></ul><br>This operation will not work offline.";
this.infoURL = "https://foundation.wikimedia.org/wiki/Maps_Terms_of_Use";
this.inputType = "string";
this.outputType = "string";
this.presentType = "html";
this.args = [
{
name: "Zoom Level",
type: "number",
value: 13
},
{
name: "Input Format",
type: "option",
value: ["Auto"].concat(FORMATS)
},
{
name: "Input Delimiter",
type: "option",
value: [
"Auto",
"Direction Preceding",
"Direction Following",
"\\n",
"Comma",
"Semi-colon",
"Colon"
]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (input.replace(/\s+/g, "") !== "") {
const inFormat = args[1],
inDelim = args[2];
let latLong;
try {
latLong = convertCoordinates(input, inFormat, inDelim, "Decimal Degrees", "Comma", "None", 5);
} catch (error) {
throw new OperationError(error);
}
latLong = latLong.replace(/[,]$/, "");
latLong = latLong.replace(/°/g, "");
return latLong;
}
return input;
}
/**
* @param {string} data
* @param {Object[]} args
* @returns {string}
*/
async present(data, args) {
if (data.replace(/\s+/g, "") === "") {
data = "0, 0";
}
const zoomLevel = args[0];
const tileUrl = "https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png",
tileAttribution = "<a href=\"https://wikimediafoundation.org/wiki/Maps_Terms_of_Use\">Wikimedia maps</a> | &copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors",
leafletUrl = "https://unpkg.com/leaflet@1.5.0/dist/leaflet.js",
leafletCssUrl = "https://unpkg.com/leaflet@1.5.0/dist/leaflet.css";
return `<link rel="stylesheet" href="${leafletCssUrl}" crossorigin=""/>
<style>#output-html { white-space: normal; padding: 0; }</style>
<div id="presentedMap" style="width: 100%; height: 100%;"></div>
<script type="text/javascript">
var mapscript = document.createElement('script');
document.body.appendChild(mapscript);
mapscript.onload = function() {
var presentMap = L.map('presentedMap').setView([${data}], ${zoomLevel});
L.tileLayer('${tileUrl}', {
attribution: '${tileAttribution}'
}).addTo(presentMap);
L.marker([${data}]).addTo(presentMap)
.bindPopup('${data}')
.openPopup();
};
mapscript.src = "${leafletUrl}";
</script>`;
}
}
export default ShowOnMap;

View File

@@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Utils from "../core/Utils";
import Utils, { debounce } from "../core/Utils";
import {fromBase64} from "../core/lib/Base64";
import Manager from "./Manager";
import HTMLCategory from "./HTMLCategory";
@@ -41,7 +41,6 @@ class App {
this.autoBakePause = false;
this.progress = 0;
this.ingId = 0;
this.timeouts = {};
}
@@ -295,7 +294,7 @@ class App {
minSize: minimise ? [0, 0, 0] : [240, 310, 450],
gutterSize: 4,
expandToMin: true,
onDrag: this.debounce(function() {
onDrag: debounce(function() {
this.manager.recipe.adjustWidth();
this.manager.input.calcMaxTabs();
this.manager.output.calcMaxTabs();
@@ -723,6 +722,7 @@ class App {
this.updateTitle(false, null, true);
}
/**
* Update the page title to contain the new recipe
*
@@ -766,29 +766,6 @@ class App {
this.loadURIParams();
}
/**
* Debouncer to stop functions from being executed multiple times in a
* short space of time
* https://davidwalsh.name/javascript-debounce-function
*
* @param {function} func - The function to be executed after the debounce time
* @param {number} wait - The time (ms) to wait before executing the function
* @param {string} id - Unique ID to reference the timeout for the function
* @param {object} scope - The object to bind to the debounced function
* @param {array} args - Array of arguments to be passed to func
* @returns {function}
*/
debounce(func, wait, id, scope, args) {
return function() {
const later = function() {
func.apply(scope, args);
};
clearTimeout(this.timeouts[id]);
this.timeouts[id] = setTimeout(later, wait);
}.bind(this);
}
}
export default App;

View File

@@ -45,6 +45,10 @@ div#output {
position: relative;
}
#operations.split {
overflow: auto;
}
.split.split-horizontal, .gutter.gutter-horizontal {
height: 100%;
float: left;

View File

@@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import ChefWorker from "worker-loader?inline&fallback=false!../../core/ChefWorker";
import ChefWorker from "worker-loader?inline&fallback=false!../../core/ChefWorker.js";
/**
* Waiter to handle conversations with a ChefWorker in the background.

View File

@@ -5,9 +5,9 @@
* @license Apache-2.0
*/
import LoaderWorker from "worker-loader?inline&fallback=false!../workers/LoaderWorker";
import InputWorker from "worker-loader?inline&fallback=false!../workers/InputWorker";
import Utils from "../../core/Utils.mjs";
import LoaderWorker from "worker-loader?inline&fallback=false!../workers/LoaderWorker.js";
import InputWorker from "worker-loader?inline&fallback=false!../workers/InputWorker.mjs";
import Utils, { debounce } from "../../core/Utils.mjs";
import { toBase64 } from "../../core/lib/Base64.mjs";
import { isImage } from "../../core/lib/FileType.mjs";
@@ -270,7 +270,7 @@ class InputWaiter {
this.showLoadingInfo(r.data, true);
break;
case "setInput":
this.app.debounce(this.set, 50, "setInput", this, [r.data.inputObj, r.data.silent])();
debounce(this.set, 50, "setInput", this, [r.data.inputObj, r.data.silent])();
break;
case "inputAdded":
this.inputAdded(r.data.changeTab, r.data.inputNum);
@@ -316,7 +316,7 @@ class InputWaiter {
*/
bakeAll() {
this.app.progress = 0;
this.app.debounce(this.manager.controls.toggleBakeButtonFunction, 20, "toggleBakeButton", this, ["loading"]);
debounce(this.manager.controls.toggleBakeButtonFunction, 20, "toggleBakeButton", this, ["loading"]);
this.inputWorker.postMessage({
action: "bakeAll"
});
@@ -681,7 +681,7 @@ class InputWaiter {
* @param {event} e
*/
debounceInputChange(e) {
this.app.debounce(this.inputChange, 50, "inputChange", this, [e])();
debounce(this.inputChange, 50, "inputChange", this, [e])();
}
/**

View File

@@ -5,10 +5,10 @@
* @license Apache-2.0
*/
import Utils from "../../core/Utils.mjs";
import Utils, { debounce } from "../../core/Utils.mjs";
import Dish from "../../core/Dish.mjs";
import FileSaver from "file-saver";
import ZipWorker from "worker-loader?inline&fallback=false!../workers/ZipWorker";
import ZipWorker from "worker-loader?inline&fallback=false!../workers/ZipWorker.mjs";
/**
* Waiter to handle events related to the output
@@ -369,7 +369,7 @@ class OutputWaiter {
}
this.setOutputInfo(length, lines, output.data.duration);
this.backgroundMagic();
debounce(this.backgroundMagic, 50, "backgroundMagic", this, [])();
}
}.bind(this));
}
@@ -717,7 +717,7 @@ class OutputWaiter {
}
}
this.app.debounce(this.set, 50, "setOutput", this, [inputNum])();
debounce(this.set, 50, "setOutput", this, [inputNum])();
document.getElementById("output-html").scroll(0, 0);
document.getElementById("output-text").scroll(0, 0);

View File

@@ -4,6 +4,8 @@
* @license Apache-2.0
*/
import { debounce } from "../../core/Utils.mjs";
/**
* Waiter to handle events related to the window object.
*/
@@ -25,7 +27,7 @@ class WindowWaiter {
* continuous resetting).
*/
windowResize() {
this.app.debounce(this.app.resetLayout, 200, "windowResize", this.app, [])();
debounce(this.app.resetLayout, 200, "windowResize", this.app, [])();
}

View File

@@ -5,8 +5,9 @@
* @license Apache-2.0
*/
import ChefWorker from "worker-loader?inline&fallback=false!../../core/ChefWorker";
import DishWorker from "worker-loader?inline&fallback=false!../workers/DishWorker";
import ChefWorker from "worker-loader?inline&fallback=false!../../core/ChefWorker.js";
import DishWorker from "worker-loader?inline&fallback=false!../workers/DishWorker.mjs";
import { debounce } from "../../core/Utils.mjs";
/**
* Waiter to handle conversations with the ChefWorker
@@ -281,7 +282,7 @@ class WorkerWaiter {
*/
setBakingStatus(bakingStatus) {
this.app.baking = bakingStatus;
this.app.debounce(this.manager.controls.toggleBakeButtonFunction, 20, "toggleBakeButton", this, [bakingStatus ? "cancel" : "bake"])();
debounce(this.manager.controls.toggleBakeButtonFunction, 20, "toggleBakeButton", this, [bakingStatus ? "cancel" : "bake"])();
if (bakingStatus) this.manager.output.hideMagicButton();
}

View File

@@ -75,7 +75,7 @@ module.exports = {
// Confirm that it has been added to the recipe
browser
.useCss()
.waitForElementVisible(op)
.waitForElementVisible(op, 100)
.expect.element(op).text.to.contain("To Hex");
// Enter input
@@ -107,6 +107,10 @@ module.exports = {
loadOp("BSON deserialise", browser)
.waitForElementNotVisible("#output-loader", 5000);
// Charts
loadOp("Entropy", browser)
.waitForElementNotVisible("#output-loader", 5000);
// Ciphers
loadOp("AES Encrypt", browser)
.waitForElementNotVisible("#output-loader", 5000);
@@ -135,6 +139,10 @@ module.exports = {
loadOp("Encode text", browser)
.waitForElementNotVisible("#output-loader", 5000);
// Hashing
loadOp("Streebog", browser)
.waitForElementNotVisible("#output-loader", 5000);
// Image
loadOp("Extract EXIF", browser)
.waitForElementNotVisible("#output-loader", 5000);
@@ -162,6 +170,54 @@ module.exports = {
// UserAgent
loadOp("Parse User Agent", browser)
.waitForElementNotVisible("#output-loader", 5000);
// YARA
loadOp("YARA Rules", browser)
.waitForElementNotVisible("#output-loader", 5000);
browser.click("#clr-recipe");
},
"Move around the UI": browser => {
const otherCat = "//a[contains(@class, 'category-title') and contains(@data-target, '#catOther')]",
genUUID = "//li[contains(@class, 'operation') and text()='Generate UUID']";
browser.useXpath();
// Scroll to a lower category
browser
.getLocationInView(otherCat)
.expect.element(otherCat).to.be.visible;
// Open category
browser
.click(otherCat)
.expect.element(genUUID).to.be.visible;
// Add op to recipe
/* mouseButtonUp drops wherever the actual cursor is, not necessarily in the right place,
so we can't test Sortable.js properly using Nightwatch. html-dnd doesn't work either.
Instead of relying on drag and drop, we double click on the op to load it. */
browser
.getLocationInView(genUUID)
.moveToElement(genUUID, 10, 10)
.doubleClick()
.useCss()
.waitForElementVisible(".operation .op-title", 1000)
.waitForElementNotVisible("#stale-indicator", 1000)
.expect.element("#output-text").to.have.value.which.matches(/[\da-f-]{36}/);
browser.click("#clr-recipe");
},
"Search": browser => {
// Search for an op
browser
.useCss()
.clearValue("#search")
.setValue("#search", "md5")
.useXpath()
.waitForElementVisible("//ul[@id='search-results']//u[text()='MD5']", 1000);
},
after: browser => {