From f2d115ee4da1485dc821312db2562b344662f83c Mon Sep 17 00:00:00 2001 From: Klaxon Date: Sat, 29 Dec 2018 00:44:59 +1000 Subject: [PATCH 001/113] add lorem ipsum generator --- src/core/config/Categories.json | 3 +- src/core/lib/LoremIpsum.mjs | 221 ++++++++++++++++++++ src/core/operations/LoremIpsumGenerator.mjs | 70 +++++++ 3 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 src/core/lib/LoremIpsum.mjs create mode 100644 src/core/operations/LoremIpsumGenerator.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 686c9842..3678a88b 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -375,7 +375,8 @@ "Parse QR Code", "Haversine distance", "Numberwang", - "XKCD Random Number" + "XKCD Random Number", + "Lorem Ipsum Generator" ] }, { diff --git a/src/core/lib/LoremIpsum.mjs b/src/core/lib/LoremIpsum.mjs new file mode 100644 index 00000000..9712a429 --- /dev/null +++ b/src/core/lib/LoremIpsum.mjs @@ -0,0 +1,221 @@ +/** + * Lorem Ipsum generator. + * + * @author Klaxon [klaxon@veyr.com] + * @copyright Crown Copyright 2016 + * @license Apache-2.0 + */ + + /** + * generate lorem ipsum paragraphs. + * + * @param {number} length + * @returns {string} + */ +export function GenerateParagraphs(length=3) { + const paragraphs = []; + while (paragraphs.length < length) { + const paragraphLength = getRandomLength(PARAGRAPH_LENGTH_MEAN, PARAGRAPH_LENGTH_STD_DEV); + const sentences = []; + while (sentences.length < paragraphLength) { + const sentenceLength = getRandomLength(SENTENCE_LENGTH_MEAN, SENTENCE_LENGTH_STD_DEV); + const sentence = getWords(sentenceLength); + sentences.push(formatSentence(sentence)); + } + paragraphs.push(formatParagraph(sentences)); + } + paragraphs[paragraphs.length-1] = paragraphs[paragraphs.length-1].slice(0, -2); + paragraphs[0] = replaceStart(paragraphs[0]); + return paragraphs.join(""); +} + +/** +* generate lorem ipsum sentences. +* +* @param {number} length +* @returns {string} +*/ +export function GenerateSentences(length=3) { + const sentences = []; + while (sentences.length < length) { + const sentenceLength = getRandomLength(SENTENCE_LENGTH_MEAN, SENTENCE_LENGTH_STD_DEV); + const sentence = getWords(sentenceLength); + sentences.push(formatSentence(sentence)); + } + const paragraphs = sentencesToParagraphs(sentences); + return paragraphs.join(""); +} + +/** +* generate lorem ipsum words. +* +* @param {number} length +* @returns {string} +*/ +export function GenerateWords(length=3) { + const words = getWords(length); + const sentences = wordsToSentences(words); + const paragraphs = sentencesToParagraphs(sentences); + return paragraphs.join(""); +} + + /** + * generate lorem ipsum bytes. + * + * @param {number} length + * @returns {string} + */ +export function GenerateBytes(length=3) { + const str = GenerateWords(length/3); + return str.slice(0, length); +} + +/** + * get array of randomly selected words from the lorem ipsum wordList. + * + * @param {number} length + * @returns {string[]} + * @private + */ +function getWords(length=3) { + const words = []; + let word; + let previousWord; + while (words.length < length){ + do { + word = wordList[Math.floor(Math.random() * wordList.length)]; + } + while (previousWord === word); + words.push(word); + previousWord = word; + } + return words; +} + +/** + * convert an array or words into an array of sentences" + * + * @param {string[]} words + * @returns {string[]} + * @private + */ +function wordsToSentences(words) { + const sentences = []; + while (words.length > 0) { + const sentenceLength = getRandomLength(SENTENCE_LENGTH_MEAN, SENTENCE_LENGTH_STD_DEV); + if (sentenceLength <= words.length) { + sentences.push(formatSentence(words.splice(0, sentenceLength))); + } else { + sentences.push(formatSentence(words.splice(0, words.length))); + } + } + return sentences; +} + +/** + * convert an array or sentences into an array of paragraphs" + * + * @param {string[]} sentences + * @returns {string[]} + * @private + */ +function sentencesToParagraphs(sentences) { + const paragraphs = []; + while (sentences.length > 0) { + const paragraphLength = getRandomLength(PARAGRAPH_LENGTH_MEAN, PARAGRAPH_LENGTH_STD_DEV); + paragraphs.push(formatParagraph(sentences.splice(0, paragraphLength))); + } + paragraphs[paragraphs.length-1] = paragraphs[paragraphs.length-1].slice(0, -1); + paragraphs[0] = replaceStart(paragraphs[0]); + return paragraphs; +} + +/** + * format an array of words into a sentence. + * + * @param {string[]} words + * @returns {string} + * @private + */ +function formatSentence(words) { + //0.35 chance of a comma being added randomly to the sentence. + if (Math.random() < PROBABILITY_OF_A_COMMA) { + const pos = Math.round(Math.random()*(words.length-1)); + words[pos] +=","; + } + let sentence = words.join(" "); + sentence = sentence.charAt(0).toUpperCase() + sentence.slice(1); + sentence += "."; + return sentence; +} + +/** + * format an array of sentences into a paragraph + * + * @param {string[]} sentences + * @returns {string} + * @private + */ +function formatParagraph(sentences) { + let paragraph = sentences.join(" "); + paragraph += "\n\n"; + return paragraph; +} + +/** + * get a random number based on a mean and standard deviation. + * + * @param {number} Mean + * @param {number} stdDev + * @returns {number} + * @private + */ +function getRandomLength(mean, stdDev) { + let length; + do { + length = Math.round((Math.random()*2-1)+(Math.random()*2-1)+(Math.random()*2-1)*stdDev+mean); + } + while (length <= 0); + return length; +} + +/** + * replace first 5 words with "Lorem ipsum dolor sit amet" + * + * @param {string[]} str + * @returns {string[]} + * @private + */ +function replaceStart(str) { + let words = str.split(" "); + if (words.length > 5) { + words.splice(0, 5, "Lorem", "ipsum", "dolor", "sit", "amet"); + return words.join(" "); + } else { + const lorem = ["Lorem", "ipsum", "dolor", "sit", "amet"]; + words = lorem.slice(0, words.length); + str = words.join(" "); + str += "."; + return str; + } +} + +const SENTENCE_LENGTH_MEAN = 15; +const SENTENCE_LENGTH_STD_DEV = 9; +const PARAGRAPH_LENGTH_MEAN = 5; +const PARAGRAPH_LENGTH_STD_DEV = 2; +const PROBABILITY_OF_A_COMMA = 0.35; + +const wordList = [ + "ad", "adipisicing", "aliqua", "aliquip", "amet", "anim", + "aute", "cillum", "commodo", "consectetur", "consequat", "culpa", + "cupidatat", "deserunt", "do", "dolor", "dolore", "duis", + "ea", "eiusmod", "elit", "enim", "esse", "est", + "et", "eu", "ex", "excepteur", "exercitation", "fugiat", + "id", "in", "incididunt", "ipsum", "irure", "labore", + "laboris", "laborum", "Lorem", "magna", "minim", "mollit", + "nisi", "non", "nostrud", "nulla", "occaecat", "officia", + "pariatur", "proident", "qui", "quis", "reprehenderit", "sint", + "sit", "sunt", "tempor", "ullamco", "ut", "velit", + "veniam", "voluptate", +]; diff --git a/src/core/operations/LoremIpsumGenerator.mjs b/src/core/operations/LoremIpsumGenerator.mjs new file mode 100644 index 00000000..228daaa1 --- /dev/null +++ b/src/core/operations/LoremIpsumGenerator.mjs @@ -0,0 +1,70 @@ +/** + * @author klaxon [klaxon@veyr.com] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import { GenerateParagraphs, GenerateSentences, GenerateWords, GenerateBytes } from "../lib/LoremIpsum"; + +/** + * Lorem Ipsum Generator operation + */ +class LoremIpsumGenerator extends Operation { + + /** + * LoremIpsumGenerator constructor + */ + constructor() { + super(); + + this.name = "Lorem Ipsum Generator"; + this.module = "Default"; + this.description = "Generate varying length lorem ipsum placeholder text."; + this.infoURL = "https://wikipedia.org/wiki/Lorem_ipsum"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Length", + "type": "number", + "value": "3" + }, + { + "name": "Length in", + "type": "option", + "value": ["Paragraphs", "Sentences", "Words", "Bytes"] + } + + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [length, lengthType] = args; + if (length < 1){ + throw new OperationError("Length must be greater than 0"); + } + switch (lengthType) { + case "Paragraphs": + return GenerateParagraphs(length); + case "Sentences": + return GenerateSentences(length); + case "Words": + return GenerateWords(length); + case "Bytes": + return GenerateBytes(length); + default: + throw new OperationError("invalid lengthType"); + + } + } + +} + +export default LoremIpsumGenerator; From 32aea6b86cb982d4f901197c162ce10fe90ee644 Mon Sep 17 00:00:00 2001 From: Spencer Walden Date: Sun, 30 Dec 2018 03:20:24 -0800 Subject: [PATCH 002/113] Adds 'To Case Insensitive Regex' operation --- .../operations/ToCaseInsensitiveRegex.mjs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/core/operations/ToCaseInsensitiveRegex.mjs diff --git a/src/core/operations/ToCaseInsensitiveRegex.mjs b/src/core/operations/ToCaseInsensitiveRegex.mjs new file mode 100644 index 00000000..32fb96c7 --- /dev/null +++ b/src/core/operations/ToCaseInsensitiveRegex.mjs @@ -0,0 +1,39 @@ +/** + * @author masq [github.cyberchef@masq.cc] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; + +/** + * To Case Insensitive Regex operation + */ +class ToCaseInsensitiveRegex extends Operation { + + /** + * ToCaseInsensitiveRegex constructor + */ + constructor() { + super(); + + this.name = "To Case Insensitive Regex"; + this.module = "Default"; + this.description = "Converts a case-sensitive regex string into a case-insensitive regex string in case /i flag is unavailable to you."; + this.infoURL = "https://wikipedia.org/wiki/Regular_expression"; + this.inputType = "string"; + this.outputType = "string"; + this.args = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + return input.replace(/[a-z]/ig, m => `[${m.toLowerCase()}${m.toUpperCase()}]`); + } +} + +export default ToCaseInsensitiveRegex; From 3c16b839b6fdbfe1d95cd0df6337f94d6f7f31c6 Mon Sep 17 00:00:00 2001 From: Spencer Walden Date: Sun, 30 Dec 2018 03:20:44 -0800 Subject: [PATCH 003/113] Adds 'From Case Insensitive Regex' operation --- .../operations/FromCaseInsensitiveRegex.mjs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/core/operations/FromCaseInsensitiveRegex.mjs diff --git a/src/core/operations/FromCaseInsensitiveRegex.mjs b/src/core/operations/FromCaseInsensitiveRegex.mjs new file mode 100644 index 00000000..2448c5e5 --- /dev/null +++ b/src/core/operations/FromCaseInsensitiveRegex.mjs @@ -0,0 +1,39 @@ +/** + * @author masq [github.cyberchef@masq.cc] + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; + +/** + * From Case Insensitive Regex operation + */ +class FromCaseInsensitiveRegex extends Operation { + + /** + * FromCaseInsensitiveRegex constructor + */ + constructor() { + super(); + + this.name = "From Case Insensitive Regex"; + this.module = "Default"; + this.description = "Converts a case-insensitive regex string to a case sensitive regex string (no guarantee on it being the proper original casing) in case /i wasn't available at the time but now is, or you need it to be case-sensitive again."; + this.infoURL = "https://wikipedia.org/wiki/Regular_expression"; + this.inputType = "string"; + this.outputType = "string"; + this.args = []; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + return input.replace(/\[[a-z]{2}\]/ig, m => m[1].toUpperCase() === m[2].toUpperCase() ? m[1] : m); + } +} + +export default FromCaseInsensitiveRegex; From b750006cf0bef9b8609d30ffb445281150b0837a Mon Sep 17 00:00:00 2001 From: Spencer Walden Date: Sun, 30 Dec 2018 03:21:19 -0800 Subject: [PATCH 004/113] Adds tests for 'To/From Case Insensitive Regex' operations --- .../operations/ToFromInsensitiveRegex.mjs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 test/tests/operations/ToFromInsensitiveRegex.mjs diff --git a/test/tests/operations/ToFromInsensitiveRegex.mjs b/test/tests/operations/ToFromInsensitiveRegex.mjs new file mode 100644 index 00000000..13c24e44 --- /dev/null +++ b/test/tests/operations/ToFromInsensitiveRegex.mjs @@ -0,0 +1,56 @@ +/** + * To/From Case Insensitive Regex tests. + * + * @author masq [github.cyberchef@masq.cc] + * + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + */ +import TestRegister from "../../TestRegister"; + +TestRegister.addTests([ + { + name: "To Case Insensitive Regex: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "To Case Insensitive Regex", + args: [], + }, + ], + }, + { + name: "From Case Insensitive Regex: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "From Case Insensitive Regex", + args: [], + }, + ], + }, + { + name: "To Case Insensitive Regex: simple test", + input: "S0meth!ng", + expectedOutput: "[sS]0[mM][eE][tT][hH]![nN][gG]", + recipeConfig: [ + { + op: "To Case Insensitive Regex", + args: [], + }, + ], + }, + { + name: "From Case Insensitive Regex: simple test", + input: "[sS]0[mM][eE][tT][hH]![nN][Gg] [wr][On][g]?", + expectedOutput: "s0meth!nG [wr][On][g]?", + recipeConfig: [ + { + op: "From Case Insensitive Regex", + args: [], + }, + ], + }, +]); From 1d04b649e00f81220e8ad7c00744817d6aa87acc Mon Sep 17 00:00:00 2001 From: Spencer Walden Date: Sun, 30 Dec 2018 03:21:52 -0800 Subject: [PATCH 005/113] Adds 'To/From Case Insensitive Regex' operations under 'Utils' --- src/core/config/Categories.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index a93149fe..c2dad458 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -189,6 +189,8 @@ "Remove null bytes", "To Upper case", "To Lower case", + "To Case Insensitive Regex", + "From Case Insensitive Regex", "Add line numbers", "Remove line numbers", "To Table", From 126ad585c03f238769fc085fc151f56fd6fe1d2c Mon Sep 17 00:00:00 2001 From: Spencer Walden Date: Sun, 30 Dec 2018 03:22:23 -0800 Subject: [PATCH 006/113] Registers tests for 'To/From Case Insensitive Regex' operations --- test/index.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/index.mjs b/test/index.mjs index ff13fe3b..3900490a 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -82,6 +82,7 @@ import "./tests/operations/TranslateDateTimeFormat"; import "./tests/operations/Magic"; import "./tests/operations/ParseTLV"; import "./tests/operations/Media"; +import "./tests/operations/ToFromInsensitiveRegex"; let allTestsPassing = true; const testStatusCounts = { From f8450183c15be1c3868e52d1cfd880b85fcdfeb2 Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Thu, 3 Jan 2019 17:51:20 +0000 Subject: [PATCH 007/113] Enigma: fix 4th rotor ringstellung --- src/core/operations/Enigma.mjs | 33 +++++++------- tests/operations/tests/Enigma.mjs | 73 +++++++++++++++++++------------ 2 files changed, 61 insertions(+), 45 deletions(-) diff --git a/src/core/operations/Enigma.mjs b/src/core/operations/Enigma.mjs index d43d780d..38456ebf 100644 --- a/src/core/operations/Enigma.mjs +++ b/src/core/operations/Enigma.mjs @@ -82,6 +82,11 @@ class EnigmaOp extends Operation { value: Enigma.ROTORS_OPTIONAL, defaultIndex: 10 }, + { + name: "4th rotor ring setting", + type: "option", + value: Enigma.LETTERS + }, { name: "4th rotor initial value", type: "option", @@ -130,25 +135,17 @@ class EnigmaOp extends Operation { * @returns {string} */ run(input, args) { - const [ - rotor1str, rotor1ring, rotor1pos, - rotor2str, rotor2ring, rotor2pos, - rotor3str, rotor3ring, rotor3pos, - rotor4str, rotor4pos, - reflectorstr, plugboardstr, - removeOther - ] = args; + const reflectorstr = args[12]; + const plugboardstr = args[13]; + const removeOther = args[14]; const rotors = []; - const [rotor1wiring, rotor1steps] = this.parseRotorStr(rotor1str, 1); - rotors.push(new Enigma.Rotor(rotor1wiring, rotor1steps, rotor1ring, rotor1pos)); - const [rotor2wiring, rotor2steps] = this.parseRotorStr(rotor2str, 2); - rotors.push(new Enigma.Rotor(rotor2wiring, rotor2steps, rotor2ring, rotor2pos)); - const [rotor3wiring, rotor3steps] = this.parseRotorStr(rotor3str, 3); - rotors.push(new Enigma.Rotor(rotor3wiring, rotor3steps, rotor3ring, rotor3pos)); - if (rotor4str !== "") { - // Fourth rotor doesn't have a ring setting - A is equivalent to no setting - const [rotor4wiring, rotor4steps] = this.parseRotorStr(rotor4str, 4); - rotors.push(new Enigma.Rotor(rotor4wiring, rotor4steps, "A", rotor4pos)); + for (let i=0; i<4; i++) { + if (i === 3 && args[i*3] === "") { + // No fourth rotor + break; + } + const [rotorwiring, rotorsteps] = this.parseRotorStr(args[i*3], 1); + rotors.push(new Enigma.Rotor(rotorwiring, rotorsteps, args[i*3 + 1], args[i*3 + 2])); } const reflector = new Enigma.Reflector(reflectorstr); const plugboard = new Enigma.Plugboard(plugboardstr); diff --git a/tests/operations/tests/Enigma.mjs b/tests/operations/tests/Enigma.mjs index a1acaf33..f8776b42 100644 --- a/tests/operations/tests/Enigma.mjs +++ b/tests/operations/tests/Enigma.mjs @@ -21,7 +21,7 @@ TestRegister.addTests([ "BDFHJLCPRTXVZNYEIWGAKMUSQO Date: Thu, 3 Jan 2019 18:51:39 +0000 Subject: [PATCH 008/113] Enigma: make sure op class is called Enigma --- src/core/operations/Enigma.mjs | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/core/operations/Enigma.mjs b/src/core/operations/Enigma.mjs index 38456ebf..c45f59ae 100644 --- a/src/core/operations/Enigma.mjs +++ b/src/core/operations/Enigma.mjs @@ -8,12 +8,12 @@ import Operation from "../Operation"; import OperationError from "../errors/OperationError"; -import * as Enigma from "../lib/Enigma"; +import {ROTORS, LETTERS, ROTORS_OPTIONAL, REFLECTORS, Rotor, Reflector, Plugboard, EnigmaMachine} from "../lib/Enigma"; /** * Enigma operation */ -class EnigmaOp extends Operation { +class Enigma extends Operation { /** * Enigma constructor */ @@ -30,72 +30,72 @@ class EnigmaOp extends Operation { { name: "1st (right-hand) rotor", type: "editableOption", - value: Enigma.ROTORS, + value: ROTORS, // Default config is the rotors I-III *left to right* defaultIndex: 2 }, { name: "1st rotor ring setting", type: "option", - value: Enigma.LETTERS + value: LETTERS }, { name: "1st rotor initial value", type: "option", - value: Enigma.LETTERS + value: LETTERS }, { name: "2nd rotor", type: "editableOption", - value: Enigma.ROTORS, + value: ROTORS, defaultIndex: 1 }, { name: "2nd rotor ring setting", type: "option", - value: Enigma.LETTERS + value: LETTERS }, { name: "2nd rotor initial value", type: "option", - value: Enigma.LETTERS + value: LETTERS }, { name: "3rd rotor", type: "editableOption", - value: Enigma.ROTORS, + value: ROTORS, defaultIndex: 0 }, { name: "3rd rotor ring setting", type: "option", - value: Enigma.LETTERS + value: LETTERS }, { name: "3rd rotor initial value", type: "option", - value: Enigma.LETTERS + value: LETTERS }, { name: "4th rotor", type: "editableOption", - value: Enigma.ROTORS_OPTIONAL, + value: ROTORS_OPTIONAL, defaultIndex: 10 }, { name: "4th rotor ring setting", type: "option", - value: Enigma.LETTERS + value: LETTERS }, { name: "4th rotor initial value", type: "option", - value: Enigma.LETTERS + value: LETTERS }, { name: "Reflector", type: "editableOption", - value: Enigma.REFLECTORS + value: REFLECTORS }, { name: "Plugboard", @@ -145,14 +145,14 @@ class EnigmaOp extends Operation { break; } const [rotorwiring, rotorsteps] = this.parseRotorStr(args[i*3], 1); - rotors.push(new Enigma.Rotor(rotorwiring, rotorsteps, args[i*3 + 1], args[i*3 + 2])); + rotors.push(new Rotor(rotorwiring, rotorsteps, args[i*3 + 1], args[i*3 + 2])); } - const reflector = new Enigma.Reflector(reflectorstr); - const plugboard = new Enigma.Plugboard(plugboardstr); + const reflector = new Reflector(reflectorstr); + const plugboard = new Plugboard(plugboardstr); if (removeOther) { input = input.replace(/[^A-Za-z]/g, ""); } - const enigma = new Enigma.EnigmaMachine(rotors, reflector, plugboard); + const enigma = new EnigmaMachine(rotors, reflector, plugboard); let result = enigma.crypt(input); if (removeOther) { // Five character cipher groups is traditional @@ -194,4 +194,4 @@ class EnigmaOp extends Operation { } -export default EnigmaOp; +export default Enigma; From b6eb4e84e4ea9da996c0a3c08d9f9c4da78ad4f5 Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Fri, 4 Jan 2019 13:33:31 +0000 Subject: [PATCH 009/113] Add Bombe operation Still needs some work, but functional --- src/core/config/Categories.json | 3 +- src/core/lib/Bombe.mjs | 472 +++++++++++++++++++++++++++++++ src/core/lib/Enigma.mjs | 25 +- src/core/operations/Bombe.mjs | 118 ++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/Bombe.mjs | 67 +++++ 6 files changed, 682 insertions(+), 4 deletions(-) create mode 100644 src/core/lib/Bombe.mjs create mode 100644 src/core/operations/Bombe.mjs create mode 100644 tests/operations/tests/Bombe.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 3e0d108e..5a40846c 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -103,7 +103,8 @@ "Citrix CTX1 Encode", "Citrix CTX1 Decode", "Pseudo-Random Number Generator", - "Enigma" + "Enigma", + "Bombe" ] }, { diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs new file mode 100644 index 00000000..70a2d2cb --- /dev/null +++ b/src/core/lib/Bombe.mjs @@ -0,0 +1,472 @@ +/** + * Emulation of the Bombe machine. + * + * @author s2224834 + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import OperationError from "../errors/OperationError"; +import Utils from "../Utils"; +import {Rotor, a2i, i2a} from "./Enigma"; + +/** + * Convenience/optimisation subclass of Rotor + * + * This allows creating multiple Rotors which share backing maps, to avoid repeatedly parsing the + * rotor spec strings and duplicating the maps in memory. + */ +class CopyRotor extends Rotor { + /** + * Return a copy of this Rotor. + */ + copy() { + const clone = { + map: this.map, + revMap: this.revMap, + pos: this.pos, + step: this.step, + transform: this.transform, + revTransform: this.revTransform, + }; + return clone; + } +} + +/** + * Node in the menu graph + * + * A node represents a cipher/plaintext letter. + */ +class Node { + /** + * Node constructor. + * @param {number} letter - The plain/ciphertext letter this node represents (as a number). + */ + constructor(letter) { + this.letter = letter; + this.edges = new Set(); + this.visited = false; + } +} + +/** + * Edge in the menu graph + * + * An edge represents an Enigma machine transformation between two letters. + */ +class Edge { + /** + * Edge constructor - an Enigma machine mapping between letters + * @param {number} pos - The rotor position, relative to the beginning of the crib, at this edge + * @param {number} node1 - Letter at one end (as a number) + * @param {number} node2 - Letter at the other end + */ + constructor(pos, node1, node2) { + this.pos = pos; + this.node1 = node1; + this.node2 = node2; + node1.edges.add(this); + node2.edges.add(this); + this.visited = false; + } + + /** + * Given the node at one end of this edge, return the other end. + * @param node {number} - The node we have + * @returns {number} + */ + getOther(node) { + if (this.node1 === node) { + return this.node2; + } + return this.node1; + } +} + +/** + * Scrambler. + * + * This is effectively just an Enigma machine, but it only operates on one character at a time and + * the stepping mechanism is different. + */ +class Scrambler { + /** Scrambler constructor. + * @param {Object[]} rotors - List of rotors in this scrambler + * @param {Object} reflector - This scrambler's reflector + * @param {number} pos - Position offset from start of crib + * @param {number} end1 - Letter in menu this scrambler is attached to + * @param {number} end2 - Other letter in menu this scrambler is attached to + */ + constructor(rotors, reflector, pos, end1, end2) { + this.reflector = reflector; + this.rotors = rotors; + this.rotorsRev = [].concat(rotors).reverse(); + this.initialPos = pos; + this.rotors[0].pos += pos; + this.end1 = end1; + this.end2 = end2; + } + + /** + * Step the rotors forward. + * + * All nodes in the Bombe step in sync. + * @param {number} n - How many rotors to step + */ + step(n) { + // The Bombe steps the slowest rotor on an actual Enigma first. + for (let i=this.rotors.length - 1; i>=this.rotors.length-n; i--) { + this.rotors[i].step(); + } + } + + /** + * Run a letter through the scrambler. + * @param {number} i - The letter to transform (as a number) + * @returns {number} + */ + transform(i) { + let letter = i; + for (const rotor of this.rotors) { + letter = rotor.transform(letter); + } + letter = this.reflector.transform(letter); + for (const rotor of this.rotorsRev) { + letter = rotor.revTransform(letter); + } + return letter; + } + + /** + * Given one letter in the menu this scrambler maps to, return the other. + * @param end {number} - The node we have + * @returns {number} + */ + getOtherEnd(end) { + if (this.end1 === end) { + return this.end2; + } + return this.end1; + } + + /** + * Read the position this scrambler is set to. + * Note that because of Enigma's stepping, you need to set an actual Enigma to the previous + * position in order to get it to make a certain set of electrical connections when a button + * is pressed - this function *does* take this into account. + * However, as with the rest of the Bombe, it does not take stepping into account - the middle + * and slow rotors are treated as static. + * @return {string} + */ + getPos() { + let result = ""; + for (let i=0; i 25) { + // A crib longer than this will definitely cause the middle rotor to step somewhere + // A shorter crib is preferable to reduce this chance, of course + throw new OperationError("Crib is too long"); + } + for (let i=0; i nConnections) { + mostConnected = oMostConnected; + nConnections = oNConnections; + } + } + return [loops, nNodes, mostConnected, nConnections, edges]; + } + + /** + * Build a menu from the ciphertext and crib. + * A menu is just a graph where letters in either the ciphertext or crib (Enigma is symmetric, + * so there's no difference mathematically) are nodes and states of the Enigma machine itself + * are the edges. + * Additionally, we want a single connected graph, and of the subgraphs available, we want the + * one with the most loops (since these generate feedback cycles which efficiently close off + * disallowed states). + * Finally, we want to identify the most connected node in that graph (as it's the best choice + * of measurement point). + * @returns [Object, Object[]] - the most connected node, and the list of edges in the subgraph + */ + makeMenu() { + // First, we make a graph of all of the mappings given by the crib + // Make all nodes first + const nodes = new Map(); + for (const c of this.ciphertext + this.crib) { + if (!nodes.has(c)) { + const node = new Node(c); + nodes.set(c, node); + } + } + // Then all edges + for (let i=0; i { + let result = b[0] - a[0]; + if (result === 0) { + result = b[1] - a[1]; + } + return result; + }); + this.nLoops = graphs[0][0]; + return [graphs[0][2], graphs[0][4]]; + } + + /** + * Implement Welchman's diagonal board: If A steckers to B, that implies B steckers to A, and + * so forth. This function just gets the paired wire. + * @param {number[2]} i - Bombe state wire + * @returns {number[2]} + */ + getDiagonal(i) { + return [i[1], i[0]]; + } + + /** + * Bombe electrical simulation. Energise a wire. For all connected wires (both via the diagonal + * board and via the scramblers), energise them too, recursively. + * @param {number[2]} i - Bombe state wire + */ + energise(i) { + const idx = 26*i[0] + i[1]; + if (this.wires[idx]) { + return; + } + this.energiseCount ++; + this.wires[idx] = true; + this.energise(this.getDiagonal(i)); + + for (const scrambler of this.scramblers[i[0]]) { + const out = scrambler.transform(i[1]); + const other = scrambler.getOtherEnd(i[0]); + this.energise([other, out]); + } + } + + /** + * Having set up the Bombe, do the actual attack run. This tries every possible rotor setting + * and attempts to logically invalidate them. If it can't, it's added to the list of candidate + * solutions. + * @returns {string[][2]} - list of pairs of candidate rotor setting, and calculated stecker pair + */ + run() { + let stops = 0; + const result = []; + // For each possible rotor setting + const nChecks = Math.pow(26, this.baseRotors.length); + for (let i=1; i<=nChecks; i++) { + this.wires.fill(false); + // Energise the test input, follow the current through each scrambler + // (and the diagonal board) + this.energiseCount = 0; + this.energise(this.testInput); + // Count the energised outputs + let count = 0; + for (let j=26*this.testRegister; j<26*(1+this.testRegister); j++) { + if (this.wires[j]) { + count++; + } + } + // If it's not all of them, we have a stop + if (count < 26) { + stops += 1; + let stecker; + // The Bombe tells us one stecker pair as well. The input wire and test register we + // started with are hypothesised to be a stecker pair. + if (count === 25) { + // Our steckering hypothesis is wrong. Correct value is the un-energised wire. + for (let j=0; j<26; j++) { + if (!this.wires[26*this.testRegister + j]) { + stecker = `${i2a(this.testRegister)} <-> ${i2a(j)}`; + break; + } + } + } else if (count === 1) { + // This means our hypothesis for the steckering is correct. + stecker = `${i2a(this.testRegister)} <-> ${i2a(this.testInput[1])}`; + } else { + // Unusual, probably indicative of a poor menu. I'm a little unclear on how + // this was really handled, but we'll return it for the moment. + stecker = `? (wire count: ${count})`; + } + result.push([this.indicator.getPos(), stecker]); + } + // Step all the scramblers + // This loop counts how many rotors have reached their starting position (meaning the + // next one needs to step as well) + let n = 1; + for (let j=1; j 2) { + const msg = `Bombe run with ${this.nLoops} loops in menu (2+ desirable): ${stops} stops, ${Math.floor(100 * i / nChecks)}% done`; + this.update(msg); + } + } + return result; + } +} diff --git a/src/core/lib/Enigma.mjs b/src/core/lib/Enigma.mjs index 845ce413..9fc0f7d0 100644 --- a/src/core/lib/Enigma.mjs +++ b/src/core/lib/Enigma.mjs @@ -103,15 +103,17 @@ export class Rotor { if (!/^[A-Z]$/.test(initialPosition)) { throw new OperationError("Rotor initial position must be exactly one uppercase letter"); } - this.map = {}; - this.revMap = {}; + this.map = new Array(26).fill(); + this.revMap = new Array(26).fill(); + const uniq = {}; for (let i=0; i S\)/, + recipeConfig: [ + { + "op": "Bombe", + "args": [ + "BDFHJLCPRTXVZNYEIWGAKMUSQO G\)/, + recipeConfig: [ + { + "op": "Bombe", + "args": [ + "BDFHJLCPRTXVZNYEIWGAKMUSQO S\)/, + recipeConfig: [ + { + "op": "Bombe", + "args": [ + "BDFHJLCPRTXVZNYEIWGAKMUSQO Date: Tue, 8 Jan 2019 11:12:02 +0000 Subject: [PATCH 010/113] Add new Subsection operation --- src/core/operations/Subsection.mjs | 141 +++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/core/operations/Subsection.mjs diff --git a/src/core/operations/Subsection.mjs b/src/core/operations/Subsection.mjs new file mode 100644 index 00000000..b9981100 --- /dev/null +++ b/src/core/operations/Subsection.mjs @@ -0,0 +1,141 @@ +/** + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import XRegExp from "xregexp"; +import Operation from "../Operation"; +import Recipe from "../Recipe"; +import Dish from "../Dish"; + +/** + * Subsection operation + */ +class Subsection extends Operation { + + /** + * Subsection constructor + */ + constructor() { + super(); + + this.name = "Subsection"; + this.flowControl = true; + this.module = "Regex"; + this.description = "Select a part of the input data using regex, and run all subsequent operations on each match separately."; + this.infoURL = ""; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Section (regex)", + "type": "string", + "value": "" + }, + { + "name": "Case sensitive matching", + "type": "boolean", + "value": true + }, + { + "name": "Ignore errors", + "type": "boolean", + "value": false + } + ]; + } + + /** + * @param {Object} state - The current state of the recipe. + * @param {number} state.progress - The current position in the recipe. + * @param {Dish} state.Dish - The Dish being operated on + * @param {Operation[]} state.opList - The list of operations in the recipe + * @returns {Object} - The updated state of the recipe + */ + async run(state) { + const opList = state.opList, + inputType = opList[state.progress].inputType, + outputType = opList[state.progress].outputType, + input = await state.dish.get(inputType), + ings = opList[state.progress].ingValues, + [section, caseSensitive, ignoreErrors] = ings, + subOpList = []; + + if (input && section !== "") { + // Create subOpList for each tranche to operate on + // (all remaining operations unless we encounter a Merge) + for (let i = state.progress + 1; i < opList.length; i++) { + if (opList[i].name === "Merge" && !opList[i].disabled) { + break; + } else { + subOpList.push(opList[i]); + } + } + + let flags = "g", + inOffset = 0, + output = "", + m, + progress = 0; + if (!caseSensitive) + flags += "i"; + const regex = new XRegExp(section, flags), + recipe = new Recipe(); + + recipe.addOperations(subOpList); + state.forkOffset += state.progress + 1; + + // Take a deep(ish) copy of the ingredient values + const ingValues = subOpList.map(op => JSON.parse(JSON.stringify(op.ingValues))); + let matched = false; + + // Run recipe over each match + while ((m = regex.exec(input))) { + matched = true; + // Add up to match + let matchStr = m[0]; + if (m.length === 1) { + output += input.slice(inOffset, m.index); + inOffset = regex.lastIndex; + } else if (m.length >= 2) { + matchStr = m[1]; + // Need to add some of the matched string that isn't in the capture group + output += input.slice(inOffset, m.index + m[0].indexOf(m[1])); + // Set i to be after the end of the first capture group + inOffset = regex.lastIndex - (m[0].length - (m[0].indexOf(m[1]) + m[1].length)); + } + // Baseline ing values for each tranche so that registers are reset + subOpList.forEach((op, i) => { + op.ingValues = JSON.parse(JSON.stringify(ingValues[i])); + }); + + const dish = new Dish(); + dish.set(matchStr, inputType); + + try { + progress = await recipe.execute(dish, 0, state); + } catch (err) { + if (!ignoreErrors) { + throw err; + } + progress = err.progress + 1; + } + output += await dish.get(outputType); + } + // If no matches were found, advance progress to after a Merge op + // Otherwise, the operations below Subsection will be run on all the input data + if (!matched) { + state.progress += subOpList.length + 1; + } + + output += input.slice(inOffset); + state.progress += progress; + state.dish.set(output, outputType); + } + return state; + } + +} + +export default Subsection; From 1a827ef44f034fc90c652e594fc97b4866935488 Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 8 Jan 2019 11:17:06 +0000 Subject: [PATCH 011/113] Add Subsection to Flow Control category --- src/core/config/Categories.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index e9fe3399..832c91e8 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -364,6 +364,7 @@ "ops": [ "Magic", "Fork", + "Subsection", "Merge", "Register", "Label", From 8ac5b484938fe86da5fc332f0e6c4f686ae73db1 Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 8 Jan 2019 11:51:33 +0000 Subject: [PATCH 012/113] Update operation description --- src/core/operations/Subsection.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/Subsection.mjs b/src/core/operations/Subsection.mjs index b9981100..94258b6d 100644 --- a/src/core/operations/Subsection.mjs +++ b/src/core/operations/Subsection.mjs @@ -23,7 +23,7 @@ class Subsection extends Operation { this.name = "Subsection"; this.flowControl = true; this.module = "Regex"; - this.description = "Select a part of the input data using regex, and run all subsequent operations on each match separately."; + this.description = "Select a part of the input data using a regular expression (regex), and run all subsequent operations on each match separately.

You can use up to one capture group, where the recipe will only be run on the data in the capture group. If there's more than one capture group, only the first one will be operated on."; this.infoURL = ""; this.inputType = "string"; this.outputType = "string"; From 5ac469b17467f8f279d592b34e00dd629227f95e Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 8 Jan 2019 16:19:58 +0000 Subject: [PATCH 013/113] Added yara rule support --- package-lock.json | 151 +++++++++++++++--------------- package.json | 1 + src/core/operations/YaraRules.mjs | 68 ++++++++++++++ 3 files changed, 147 insertions(+), 73 deletions(-) create mode 100644 src/core/operations/YaraRules.mjs diff --git a/package-lock.json b/package-lock.json index 4f1be0a3..f1980a00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1171,7 +1171,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1284,7 +1284,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -1369,7 +1369,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1457,7 +1457,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "dev": true, "requires": { @@ -1863,7 +1863,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -1900,7 +1900,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -1950,7 +1950,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2015,7 +2015,7 @@ }, "cacache": { "version": "10.0.4", - "resolved": "http://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", "dev": true, "requires": { @@ -2092,7 +2092,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -2123,7 +2123,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -2590,7 +2590,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -2603,7 +2603,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -2721,7 +2721,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -3055,7 +3055,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -3119,7 +3119,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true }, @@ -3307,7 +3307,7 @@ }, "entities": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", "dev": true }, @@ -3731,7 +3731,7 @@ }, "eventemitter2": { "version": "0.4.14", - "resolved": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", "dev": true }, @@ -3743,7 +3743,7 @@ }, "events": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, @@ -4149,7 +4149,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -4377,7 +4377,7 @@ }, "fs-extra": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", "dev": true, "requires": { @@ -5023,7 +5023,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -5103,7 +5103,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -5173,7 +5173,7 @@ }, "grunt-cli": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", "dev": true, "requires": { @@ -5221,7 +5221,7 @@ "dependencies": { "shelljs": { "version": "0.5.3", - "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", "integrity": "sha1-xUmCuZbHbvDB5rWfvcWCX1txMRM=", "dev": true } @@ -5241,7 +5241,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -5269,7 +5269,7 @@ }, "grunt-contrib-jshint": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=", "dev": true, "requires": { @@ -5368,7 +5368,7 @@ "dependencies": { "colors": { "version": "1.1.2", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true } @@ -5432,7 +5432,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -5450,7 +5450,7 @@ }, "handle-thing": { "version": "1.2.5", - "resolved": "http://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", "dev": true }, @@ -5725,7 +5725,7 @@ }, "htmlparser2": { "version": "3.8.3", - "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", "dev": true, "requires": { @@ -5744,7 +5744,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -5773,7 +5773,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -6225,7 +6225,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -6750,7 +6750,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -6856,7 +6856,7 @@ }, "kew": { "version": "0.7.0", - "resolved": "http://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", "dev": true }, @@ -6928,6 +6928,11 @@ "resolved": "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz", "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, + "libyara-wasm": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.2.tgz", + "integrity": "sha512-Q4Biyfwiwrz7u25j7HNbGjJkcuekMJJeGCwbUgc7s7GMriZBSctCJPzlP5MBKgMzGMe0fNc9Hu5EP6Y1lBFnWg==" + }, "livereload-js": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", @@ -6948,7 +6953,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -6961,7 +6966,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -7196,7 +7201,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -7255,7 +7260,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -7432,7 +7437,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -7554,7 +7559,7 @@ }, "ncp": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=", "dev": true }, @@ -7617,7 +7622,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true } @@ -7756,7 +7761,7 @@ "dependencies": { "colors": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" }, "underscore": { @@ -8015,13 +8020,13 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -8030,7 +8035,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -8173,7 +8178,7 @@ }, "parse-asn1": { "version": "5.1.1", - "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { @@ -8231,7 +8236,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -8272,7 +8277,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -8437,7 +8442,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -8839,7 +8844,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" }, "promise-inflight": { @@ -8864,13 +8869,13 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true }, "winston": { "version": "2.1.1", - "resolved": "http://registry.npmjs.org/winston/-/winston-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", "dev": true, "requires": { @@ -8885,7 +8890,7 @@ "dependencies": { "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, @@ -9064,7 +9069,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -9253,7 +9258,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -9304,7 +9309,7 @@ }, "htmlparser2": { "version": "3.3.0", - "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "dev": true, "requires": { @@ -9316,7 +9321,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -9426,7 +9431,7 @@ }, "require-uncached": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { @@ -9593,7 +9598,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -9914,7 +9919,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -9958,7 +9963,7 @@ }, "shelljs": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", "dev": true }, @@ -10610,7 +10615,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -10627,7 +10632,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -10706,7 +10711,7 @@ }, "tar": { "version": "2.2.1", - "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { @@ -10734,7 +10739,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -11381,7 +11386,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -11407,7 +11412,7 @@ }, "valid-data-url": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", "integrity": "sha512-FXg2qXMzfAhZc0y2HzELNfUeiOjPr+52hU1DNBWiJJ2luXD+dD1R9NA48Ug5aj0ibbxroeGDc/RJv6ThiGgkDw==", "dev": true }, @@ -11423,7 +11428,7 @@ }, "validator": { "version": "9.4.1", - "resolved": "http://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz", "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==", "dev": true }, @@ -11847,7 +11852,7 @@ }, "webpack-node-externals": { "version": "1.7.2", - "resolved": "http://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", "integrity": "sha512-ajerHZ+BJKeCLviLUUmnyd5B4RavLF76uv3cs6KNuO8W+HuQaEs0y0L7o40NQxdPy5w0pcv8Ew7yPUAQG0UdCg==", "dev": true }, @@ -11944,14 +11949,14 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true, "optional": true }, "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true, "optional": true @@ -11984,7 +11989,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { diff --git a/package.json b/package.json index 4fac8034..8c0740ca 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "jsonwebtoken": "^8.3.0", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", + "libyara-wasm": "0.0.2", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YaraRules.mjs new file mode 100644 index 00000000..9f2d9b79 --- /dev/null +++ b/src/core/operations/YaraRules.mjs @@ -0,0 +1,68 @@ +/** + * @author Matt C [matt@artemisbot.uk] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import Yara from "libyara-wasm"; + +/** + * Yara Rules operation + */ +class YaraRules extends Operation { + + /** + * YaraRules constructor + */ + constructor() { + super(); + + this.name = "Yara Rules"; + this.module = "Yara"; + this.description = "Yara support"; + this.infoURL = "https://en.wikipedia.org/wiki/YARA"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [{ + name: "Rules", + type: "string", + value: "" + }]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + return new Promise((resolve, reject) => { + Yara().then(yara => { + const resp = yara.run(input, args[0]); + if (resp.compileErrors.size() > 0) { + for (let i = 0; i < resp.compileErrors.size(); i++) { + const compileError = resp.compileErrors.get(i); + reject(new OperationError(`Error on line ${compileError.lineNumber}: ${compileError.message}`)); + } + } + const matchedRules = resp.matchedRules; + let matchString = ""; + for (let i = 0; i < matchedRules.keys().size(); i++) { + const ruleMatches = matchedRules.get(matchedRules.keys().get(i)); + matchString += `Rule "${matchedRules.keys().get(i)}" matches:\n`; + + for (let j = 0; j < ruleMatches.size(); j++) { + const match = ruleMatches.get(j); + matchString += `Position ${match.location}, length ${match.matchLength}, data: ${match.data}\n`; + } + } + resolve(matchString); + }); + }); + } + +} + +export default YaraRules; From 766de7e6fa2475ab4d3a1d2d5ce5f1f5f6862ab0 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 8 Jan 2019 17:51:43 +0000 Subject: [PATCH 014/113] Fixed bug in 'Regular expression' operation when highlighting lookaheads --- package-lock.json | 128 +++++++++++----------- src/core/operations/RegularExpression.mjs | 31 ++---- 2 files changed, 76 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index 57175720..d19abbcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1497,7 +1497,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1571,7 +1571,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -1615,7 +1615,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -1700,7 +1700,7 @@ }, "util": { "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1848,7 +1848,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "dev": true, "requires": { @@ -2286,7 +2286,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2323,7 +2323,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2388,7 +2388,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2551,7 +2551,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -2600,7 +2600,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -2779,7 +2779,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -3133,7 +3133,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -3146,7 +3146,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -3322,7 +3322,7 @@ }, "regexpu-core": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "dev": true, "requires": { @@ -3339,7 +3339,7 @@ }, "regjsparser": { "version": "0.1.5", - "resolved": "http://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { @@ -4271,7 +4271,7 @@ }, "source-map": { "version": "0.1.43", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", "requires": { "amdefine": ">=0.0.4" @@ -4461,7 +4461,7 @@ "dependencies": { "source-map": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", "integrity": "sha1-D+llA6yGpa213mP05BKuSHLNvoY=", "dev": true } @@ -4794,7 +4794,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -4829,7 +4829,7 @@ }, "findup-sync": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", "dev": true, "requires": { @@ -4896,7 +4896,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -5019,7 +5019,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -5098,12 +5098,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5123,7 +5125,8 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", @@ -5271,6 +5274,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5660,7 +5664,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -5823,7 +5827,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -5935,7 +5939,7 @@ }, "resolve": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true } @@ -5981,7 +5985,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -6125,7 +6129,7 @@ "dependencies": { "colors": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true } @@ -6189,7 +6193,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -6409,7 +6413,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -6970,7 +6974,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -7025,7 +7029,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -7429,7 +7433,7 @@ }, "underscore": { "version": "1.8.3", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", "dev": true } @@ -8222,7 +8226,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -8471,7 +8475,7 @@ "dependencies": { "commander": { "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true, "optional": true @@ -8591,7 +8595,7 @@ }, "multimatch": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", "dev": true, "requires": { @@ -8603,7 +8607,7 @@ }, "mute-stream": { "version": "0.0.7", - "resolved": "http://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, @@ -8922,12 +8926,12 @@ "dependencies": { "colors": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz", "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" }, "underscore": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", "integrity": "sha1-QLq4S60Z0jAJbo1u9ii/8FXYPbA=" } } @@ -9426,7 +9430,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -9514,7 +9518,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -10152,13 +10156,13 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true }, "winston": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", + "resolved": "http://registry.npmjs.org/winston/-/winston-2.1.1.tgz", "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", "dev": true, "requires": { @@ -10173,7 +10177,7 @@ "dependencies": { "colors": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, @@ -10490,7 +10494,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -10783,7 +10787,7 @@ "dependencies": { "underscore": { "version": "1.6.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true } @@ -11102,7 +11106,7 @@ "dependencies": { "source-map": { "version": "0.4.4", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { @@ -11710,7 +11714,7 @@ }, "split2": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/split2/-/split2-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.1.1.tgz", "integrity": "sha1-Fi2bGIZfAqsvKtlYVSLbm1TEgfk=", "dev": true, "requires": { @@ -11719,7 +11723,7 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, @@ -11851,7 +11855,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -11868,7 +11872,7 @@ }, "stream-browserify": { "version": "2.0.1", - "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { @@ -11954,7 +11958,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -12071,7 +12075,7 @@ }, "supports-color": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "symbol-tree": { @@ -12534,7 +12538,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -12609,7 +12613,7 @@ }, "underscore": { "version": "1.7.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" }, "underscore-contrib": { @@ -12623,7 +12627,7 @@ "dependencies": { "underscore": { "version": "1.6.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true } @@ -12938,7 +12942,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -13008,7 +13012,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { @@ -13666,14 +13670,14 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true, "optional": true }, "colors": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true, "optional": true @@ -13725,7 +13729,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -13880,7 +13884,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { diff --git a/src/core/operations/RegularExpression.mjs b/src/core/operations/RegularExpression.mjs index 03918982..cce65c63 100644 --- a/src/core/operations/RegularExpression.mjs +++ b/src/core/operations/RegularExpression.mjs @@ -228,40 +228,29 @@ function regexList (input, regex, displayTotal, matches, captureGroups) { function regexHighlight (input, regex, displayTotal) { let output = "", title = "", - m, hl = 1, - i = 0, total = 0; - while ((m = regex.exec(input))) { - // Moves pointer when an empty string is matched (prevents infinite loop) - if (m.index === regex.lastIndex) { - regex.lastIndex++; - } + output = input.replace(regex, (match, ...args) => { + args.pop(); // Throw away full string + const offset = args.pop(), + groups = args; - // Add up to match - output += Utils.escapeHtml(input.slice(i, m.index)); - - title = `Offset: ${m.index}\n`; - if (m.length > 1) { + title = `Offset: ${offset}\n`; + if (groups.length) { title += "Groups:\n"; - for (let n = 1; n < m.length; ++n) { - title += `\t${n}: ${m[n]}\n`; + for (let i = 0; i < groups.length; i++) { + title += `\t${i+1}: ${Utils.escapeHtml(groups[i])}\n`; } } - // Add match with highlighting - output += "" + Utils.escapeHtml(m[0]) + ""; - // Switch highlight hl = hl === 1 ? 2 : 1; - i = regex.lastIndex; total++; - } - // Add all after final match - output += Utils.escapeHtml(input.slice(i, input.length)); + return `${Utils.escapeHtml(match)}`; + }); if (displayTotal) output = "Total found: " + total + "\n\n" + output; From 3a6b2875d5883824f1dfd2eca3982b2f1bbd0c61 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 8 Jan 2019 17:51:47 +0000 Subject: [PATCH 015/113] 8.19.6 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d19abbcc..b8c5b5fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.5", + "version": "8.19.6", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 2274ca30..8dbdc291 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.5", + "version": "8.19.6", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From ad6e30f3d4ebf3ab1cad298a614c4f71b72f6767 Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Tue, 8 Jan 2019 18:25:42 +0000 Subject: [PATCH 016/113] Bombe: review, tests, validation --- src/core/lib/Bombe.mjs | 34 ++++----- src/core/lib/Enigma.mjs | 6 +- src/core/operations/Bombe.mjs | 31 ++++++-- src/core/operations/Enigma.mjs | 6 +- tests/operations/tests/Bombe.mjs | 127 +++++++++++++++++++++++++++++++ 5 files changed, 173 insertions(+), 31 deletions(-) diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index 70a2d2cb..53766560 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -77,10 +77,7 @@ class Edge { * @returns {number} */ getOther(node) { - if (this.node1 === node) { - return this.node2; - } - return this.node1; + return this.node1 === node ? this.node2 : this.node1; } } @@ -144,10 +141,7 @@ class Scrambler { * @returns {number} */ getOtherEnd(end) { - if (this.end1 === end) { - return this.end2; - } - return this.end1; + return this.end1 === end ? this.end2 : this.end1; } /** @@ -194,8 +188,11 @@ export class BombeMachine { * @param {function} update - Function to call to send status updates (optional) */ constructor(rotors, reflector, ciphertext, crib, update=undefined) { - if (ciphertext.length !== crib.length) { - throw new OperationError("Ciphertext and crib length differ"); + if (ciphertext.length < crib.length) { + throw new OperationError("Crib overruns supplied ciphertext"); + } + if (ciphertext.length > crib.length) { + throw new OperationError("Ciphertext is longer than crib"); } if (crib.length < 2) { // This is the absolute bare minimum to be sane, and even then it's likely too short to @@ -226,7 +223,7 @@ export class BombeMachine { // This is the bundle of wires corresponding to the 26 letters within each of the 26 // possible nodes in the menu - this.wires = new Array(26*26).fill(false); + this.wires = new Array(26*26); // These are the pseudo-Engima devices corresponding to each edge in the menu, and the // nodes in the menu they each connect to @@ -271,9 +268,9 @@ export class BombeMachine { * If we have a way of sending status messages, do so. * @param {string} msg - Message to send. */ - update(msg) { + update(...msg) { if (this.updateFn !== undefined) { - this.updateFn(msg); + this.updateFn(...msg); } } @@ -411,7 +408,10 @@ export class BombeMachine { // For each possible rotor setting const nChecks = Math.pow(26, this.baseRotors.length); for (let i=1; i<=nChecks; i++) { - this.wires.fill(false); + // Benchmarking suggests this is faster than using .fill() + for (let i=0; i 2) { - const msg = `Bombe run with ${this.nLoops} loops in menu (2+ desirable): ${stops} stops, ${Math.floor(100 * i / nChecks)}% done`; - this.update(msg); + // (note this won't be triggered on 3-rotor runs - they run fast enough it doesn't seem necessary) + if (n > 3) { + this.update(this.nLoops, stops, i/nChecks); } } return result; diff --git a/src/core/lib/Enigma.mjs b/src/core/lib/Enigma.mjs index 9fc0f7d0..cfc93933 100644 --- a/src/core/lib/Enigma.mjs +++ b/src/core/lib/Enigma.mjs @@ -103,8 +103,8 @@ export class Rotor { if (!/^[A-Z]$/.test(initialPosition)) { throw new OperationError("Rotor initial position must be exactly one uppercase letter"); } - this.map = new Array(26).fill(); - this.revMap = new Array(26).fill(); + this.map = new Array(26); + this.revMap = new Array(26); const uniq = {}; for (let i=0; i S\)/, + recipeConfig: [ + { + "op": "Bombe", + "args": [ + "BDFHJLCPRTXVZNYEIWGAKMUSQO Date: Tue, 8 Jan 2019 18:29:07 +0000 Subject: [PATCH 017/113] Fixed 'Maximise output' button functionality --- src/web/App.mjs | 13 ++++++++++--- src/web/OutputWaiter.mjs | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/web/App.mjs b/src/web/App.mjs index 1dab16e6..846803a1 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -238,12 +238,18 @@ class App { /** * Sets up the adjustable splitter to allow the user to resize areas of the page. + * + * @param {boolean} [minimise=false] - Set this flag if attempting to minimuse frames to 0 width */ - initialiseSplitter() { + initialiseSplitter(minimise=false) { + if (this.columnSplitter) this.columnSplitter.destroy(); + if (this.ioSplitter) this.ioSplitter.destroy(); + this.columnSplitter = Split(["#operations", "#recipe", "#IO"], { sizes: [20, 30, 50], - minSize: [240, 370, 450], + minSize: minimise ? [0, 0, 0] : [240, 370, 450], gutterSize: 4, + expandToMin: false, onDrag: function() { this.manager.recipe.adjustWidth(); }.bind(this) @@ -251,7 +257,8 @@ class App { this.ioSplitter = Split(["#input", "#output"], { direction: "vertical", - gutterSize: 4 + gutterSize: 4, + minSize: minimise ? [0, 0] : [100, 100] }); this.resetLayout(); diff --git a/src/web/OutputWaiter.mjs b/src/web/OutputWaiter.mjs index 7203a16f..28deaffa 100755 --- a/src/web/OutputWaiter.mjs +++ b/src/web/OutputWaiter.mjs @@ -319,6 +319,7 @@ class OutputWaiter { const el = e.target.id === "maximise-output" ? e.target : e.target.parentNode; if (el.getAttribute("data-original-title").indexOf("Maximise") === 0) { + this.app.initialiseSplitter(true); this.app.columnSplitter.collapse(0); this.app.columnSplitter.collapse(1); this.app.ioSplitter.collapse(0); @@ -328,6 +329,7 @@ class OutputWaiter { } else { $(el).attr("data-original-title", "Maximise output pane"); el.querySelector("i").innerHTML = "fullscreen"; + this.app.initialiseSplitter(false); this.app.resetLayout(); } } From fe1332f18e2ea23a92508ed2fc6d4b1fa42c7b32 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 8 Jan 2019 18:29:14 +0000 Subject: [PATCH 018/113] 8.19.7 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b8c5b5fe..81136cc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.6", + "version": "8.19.7", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 8dbdc291..e1b801d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.6", + "version": "8.19.7", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From e74c86842b740c9d5b690c540ec6d28559d339f9 Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Tue, 8 Jan 2019 19:37:34 +0000 Subject: [PATCH 019/113] Bombe: add trial decryption preview --- src/core/lib/Bombe.mjs | 60 +++++++++++++++++++++++++++----- src/core/operations/Bombe.mjs | 8 ++--- tests/operations/tests/Bombe.mjs | 27 +++++++++++--- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index 53766560..1e6c3d2d 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -191,9 +191,6 @@ export class BombeMachine { if (ciphertext.length < crib.length) { throw new OperationError("Crib overruns supplied ciphertext"); } - if (ciphertext.length > crib.length) { - throw new OperationError("Ciphertext is longer than crib"); - } if (crib.length < 2) { // This is the absolute bare minimum to be sane, and even then it's likely too short to // be useful @@ -204,7 +201,7 @@ export class BombeMachine { // A shorter crib is preferable to reduce this chance, of course throw new OperationError("Crib is too long"); } - for (let i=0; i ${i2a(j)}`; + stecker = [this.testRegister, j]; break; } } } else if (count === 1) { // This means our hypothesis for the steckering is correct. - stecker = `${i2a(this.testRegister)} <-> ${i2a(this.testInput[1])}`; + stecker = [this.testRegister, this.testInput[1]]; } else { // Unusual, probably indicative of a poor menu. I'm a little unclear on how // this was really handled, but we'll return it for the moment. - stecker = `? (wire count: ${count})`; + stecker = undefined; } - result.push([this.indicator.getPos(), stecker]); + const testDecrypt = this.tryDecrypt(stecker); + let steckerStr; + if (stecker !== undefined) { + steckerStr = `${i2a(stecker[0])}${i2a(stecker[1])}`; + } else { + steckerStr = `?? (wire count: ${count})`; + } + result.push([this.indicator.getPos(), steckerStr, testDecrypt]); } // Step all the scramblers // This loop counts how many rotors have reached their starting position (meaning the diff --git a/src/core/operations/Bombe.mjs b/src/core/operations/Bombe.mjs index e875e50d..9ddd4b7b 100644 --- a/src/core/operations/Bombe.mjs +++ b/src/core/operations/Bombe.mjs @@ -112,7 +112,7 @@ class Bombe extends Operation { // For symmetry with the Enigma op, for the input we'll just remove all invalid characters input = input.replace(/[^A-Za-z]/g, "").toUpperCase(); crib = crib.replace(/[^A-Za-z]/g, "").toUpperCase(); - const ciphertext = input.slice(offset, offset+crib.length); + const ciphertext = input.slice(offset); const reflector = new Reflector(reflectorstr); let update; if (ENVIRONMENT_IS_WORKER()) { @@ -122,9 +122,9 @@ class Bombe extends Operation { } const bombe = new BombeMachine(rotors, reflector, ciphertext, crib, update); const result = bombe.run(); - let msg = `Bombe run on menu with ${bombe.nLoops} loops (2+ desirable). Note: Rotor positions are listed left to right and start at the beginning of the crib, and ignore stepping and the ring setting. One stecker pair is determined. Results:\n`; - for (const [setting, wires] of result) { - msg += `Stop: ${setting} (${wires})\n`; + let msg = `Bombe run on menu with ${bombe.nLoops} loops (2+ desirable). Note: Rotor positions are listed left to right and start at the beginning of the crib, and ignore stepping and the ring setting. One stecker pair is determined. A decryption preview starting at the beginning of the crib and ignoring stepping is also provided. Results:\n`; + for (const [setting, stecker, decrypt] of result) { + msg += `Stop: ${setting} (plugboard: ${stecker}): ${decrypt}\n`; } return msg; } diff --git a/tests/operations/tests/Bombe.mjs b/tests/operations/tests/Bombe.mjs index 8742cfc0..65f4b701 100644 --- a/tests/operations/tests/Bombe.mjs +++ b/tests/operations/tests/Bombe.mjs @@ -8,9 +8,10 @@ import TestRegister from "../TestRegister"; TestRegister.addTests([ { + // Plugboard for this test is BO LC KE GA name: "Bombe: 3 rotor (self-stecker)", input: "BBYFLTHHYIJQAYBBYS", - expectedMatch: /LGA \(S <-> S\)/, + expectedMatch: /LGA \(plugboard: SS\): VFISUSGTKSTMPSUNAK/, recipeConfig: [ { "op": "Bombe", @@ -28,7 +29,7 @@ TestRegister.addTests([ { name: "Bombe: 3 rotor (other stecker)", input: "JBYALIHDYNUAAVKBYM", - expectedMatch: /LGA \(A <-> G\)/, + expectedMatch: /LGA \(plugboard: AG\): QFIMUMAFKMQSKMYNGW/, recipeConfig: [ { "op": "Bombe", @@ -46,7 +47,7 @@ TestRegister.addTests([ { name: "Bombe: crib offset", input: "AAABBYFLTHHYIJQAYBBYS", // first three chars here are faked - expectedMatch: /LGA \(S <-> S\)/, + expectedMatch: /LGA \(plugboard: SS\): VFISUSGTKSTMPSUNAK/, recipeConfig: [ { "op": "Bombe", @@ -61,12 +62,30 @@ TestRegister.addTests([ } ] }, + { + name: "Bombe: multiple stops", + input: "BBYFLTHHYIJQAYBBYS", + expectedMatch: /LGA \(plugboard: TT\): VFISUSGTKSTMPSUNAK/, + recipeConfig: [ + { + "op": "Bombe", + "args": [ + "BDFHJLCPRTXVZNYEIWGAKMUSQO S\)/, + expectedMatch: /LHSC \(plugboard: SS\)/, recipeConfig: [ { "op": "Bombe", From df8abb099c77babbe6a2a30f4cafc5c3ec6d7207 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 8 Jan 2019 22:23:14 +0000 Subject: [PATCH 020/113] Added code argtype --- package-lock.json | 74 +++++++++++++++---------------- src/core/operations/YaraRules.mjs | 2 +- src/web/HTMLIngredient.mjs | 10 +++++ 3 files changed, 48 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index eae5e06f..7241c6de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1571,7 +1571,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -2779,7 +2779,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -3322,7 +3322,7 @@ }, "regexpu-core": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "dev": true, "requires": { @@ -3339,7 +3339,7 @@ }, "regjsparser": { "version": "0.1.5", - "resolved": "http://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { @@ -4271,7 +4271,7 @@ }, "source-map": { "version": "0.1.43", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", "requires": { "amdefine": ">=0.0.4" @@ -4461,7 +4461,7 @@ "dependencies": { "source-map": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", "integrity": "sha1-D+llA6yGpa213mP05BKuSHLNvoY=", "dev": true } @@ -4829,7 +4829,7 @@ }, "findup-sync": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=", "dev": true, "requires": { @@ -4896,7 +4896,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -5019,7 +5019,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -5660,7 +5660,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -5823,7 +5823,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -5935,7 +5935,7 @@ }, "resolve": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true } @@ -6409,7 +6409,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -6970,7 +6970,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -7025,7 +7025,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -7429,7 +7429,7 @@ }, "underscore": { "version": "1.8.3", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", "dev": true } @@ -8227,7 +8227,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -8596,7 +8596,7 @@ }, "multimatch": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", "dev": true, "requires": { @@ -8608,7 +8608,7 @@ }, "mute-stream": { "version": "0.0.7", - "resolved": "http://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, @@ -8932,7 +8932,7 @@ }, "underscore": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz", "integrity": "sha1-QLq4S60Z0jAJbo1u9ii/8FXYPbA=" } } @@ -9431,7 +9431,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -9519,7 +9519,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -10495,7 +10495,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -10788,7 +10788,7 @@ "dependencies": { "underscore": { "version": "1.6.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true } @@ -11107,7 +11107,7 @@ "dependencies": { "source-map": { "version": "0.4.4", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { @@ -11715,7 +11715,7 @@ }, "split2": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/split2/-/split2-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.1.1.tgz", "integrity": "sha1-Fi2bGIZfAqsvKtlYVSLbm1TEgfk=", "dev": true, "requires": { @@ -11724,7 +11724,7 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, @@ -11856,7 +11856,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -11873,7 +11873,7 @@ }, "stream-browserify": { "version": "2.0.1", - "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { @@ -11959,7 +11959,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -12076,7 +12076,7 @@ }, "supports-color": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "symbol-tree": { @@ -12539,7 +12539,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -12614,7 +12614,7 @@ }, "underscore": { "version": "1.7.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" }, "underscore-contrib": { @@ -12628,7 +12628,7 @@ "dependencies": { "underscore": { "version": "1.6.0", - "resolved": "http://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true } @@ -13013,7 +13013,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { @@ -13730,7 +13730,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { @@ -13885,7 +13885,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YaraRules.mjs index 9f2d9b79..3037bdb6 100644 --- a/src/core/operations/YaraRules.mjs +++ b/src/core/operations/YaraRules.mjs @@ -27,7 +27,7 @@ class YaraRules extends Operation { this.outputType = "string"; this.args = [{ name: "Rules", - type: "string", + type: "code", value: "" }]; } diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index bb01d7de..0cbb2dad 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -42,6 +42,16 @@ class HTMLIngredient { i, m; switch (this.type) { + case "code": + html+= `
+ + + ${this.hint ? "" + this.hint + "" : ""}`; + break; case "string": case "binaryString": case "byteArray": From 4c1521a98ee41cd447f691673acbdf08baafa2c3 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 8 Jan 2019 23:26:14 +0000 Subject: [PATCH 021/113] No data matches & warnings support --- src/core/operations/YaraRules.mjs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YaraRules.mjs index 3037bdb6..74de6f29 100644 --- a/src/core/operations/YaraRules.mjs +++ b/src/core/operations/YaraRules.mjs @@ -40,23 +40,32 @@ class YaraRules extends Operation { run(input, args) { return new Promise((resolve, reject) => { Yara().then(yara => { + let matchString = ""; const resp = yara.run(input, args[0]); if (resp.compileErrors.size() > 0) { for (let i = 0; i < resp.compileErrors.size(); i++) { const compileError = resp.compileErrors.get(i); - reject(new OperationError(`Error on line ${compileError.lineNumber}: ${compileError.message}`)); + if (!compileError.warning) { + reject(new OperationError(`Error on line ${compileError.lineNumber}: ${compileError.message}`)); + } else { + matchString += `Warning on line ${compileError.lineNumber}: ${compileError.message}`; + } } } const matchedRules = resp.matchedRules; - let matchString = ""; for (let i = 0; i < matchedRules.keys().size(); i++) { const ruleMatches = matchedRules.get(matchedRules.keys().get(i)); - matchString += `Rule "${matchedRules.keys().get(i)}" matches:\n`; + if (ruleMatches.size() === 0) { + matchString += `Input matches rule "${matchedRules.keys().get(i)}".\n`; + } else { + matchString += `Rule "${matchedRules.keys().get(i)}" matches:\n`; - for (let j = 0; j < ruleMatches.size(); j++) { - const match = ruleMatches.get(j); - matchString += `Position ${match.location}, length ${match.matchLength}, data: ${match.data}\n`; + for (let j = 0; j < ruleMatches.size(); j++) { + const match = ruleMatches.get(j); + matchString += `Position ${match.location}, length ${match.matchLength}, data: ${match.data}\n`; + } } + } resolve(matchString); }); From 26a2fb66625cb9bd703ef49446f7aeca6436028e Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 9 Jan 2019 09:56:55 +0000 Subject: [PATCH 022/113] Increased size of rule inp & expanded memory for wasm --- package-lock.json | 6 +++--- package.json | 2 +- src/web/HTMLIngredient.mjs | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7241c6de..8827fd47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7756,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.2.tgz", - "integrity": "sha512-Q4Biyfwiwrz7u25j7HNbGjJkcuekMJJeGCwbUgc7s7GMriZBSctCJPzlP5MBKgMzGMe0fNc9Hu5EP6Y1lBFnWg==" + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.4.tgz", + "integrity": "sha512-Puw8AfHRgAiS2SBvJBlh3DEYU3icU16MciwQK5Fsxel021UK7DcY1A5DAKYanPNeXVztlz/9USZbEneAkcWzvA==" }, "livereload-js": { "version": "2.4.0", diff --git a/package.json b/package.json index 1f207e4b..ffdc5dfd 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.2", + "libyara-wasm": "0.0.4", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index 0cbb2dad..4de7e43f 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -49,6 +49,7 @@ class HTMLIngredient { id="${this.id}" arg-name="${this.name}" value="${this.value}" + rows=5 ${this.disabled ? "disabled" : ""}> ${this.hint ? "" + this.hint + "" : ""}`; break; From 4db2335107ddf519784b4033336087ebf16ec2b0 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 9 Jan 2019 11:45:11 +0000 Subject: [PATCH 023/113] Speedrunning strats (increased speed on big files) --- package-lock.json | 6 +++--- package.json | 2 +- src/core/operations/YaraRules.mjs | 9 +++++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8827fd47..573f8e67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7756,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.4.tgz", - "integrity": "sha512-Puw8AfHRgAiS2SBvJBlh3DEYU3icU16MciwQK5Fsxel021UK7DcY1A5DAKYanPNeXVztlz/9USZbEneAkcWzvA==" + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.6.tgz", + "integrity": "sha512-Crnaz5G/ejjZrEYTlyUZIaquR66djW8w8UR8GtgFrpWzhiySPJTcdxwOhGmCku2VhhETPznz20KxBNifBSF+oA==" }, "livereload-js": { "version": "2.4.0", diff --git a/package.json b/package.json index ffdc5dfd..2f9c7d04 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.4", + "libyara-wasm": "0.0.6", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YaraRules.mjs index 74de6f29..b6d78a8c 100644 --- a/src/core/operations/YaraRules.mjs +++ b/src/core/operations/YaraRules.mjs @@ -23,7 +23,7 @@ class YaraRules extends Operation { this.module = "Yara"; this.description = "Yara support"; this.infoURL = "https://en.wikipedia.org/wiki/YARA"; - this.inputType = "string"; + this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [{ name: "Rules", @@ -41,7 +41,12 @@ class YaraRules extends Operation { return new Promise((resolve, reject) => { Yara().then(yara => { let matchString = ""; - const resp = yara.run(input, args[0]); + const inpArr = new Uint8Array(input); // I know this is garbage but it's like 1.5 times faster + const inpVec = new yara.vectorChar(); + for (let i = 0; i < inpArr.length; i++) { + inpVec.push_back(inpArr[i]); + } + const resp = yara.run(inpVec, args[0]); if (resp.compileErrors.size() > 0) { for (let i = 0; i < resp.compileErrors.size(); i++) { const compileError = resp.compileErrors.get(i); From ebb632e8882f8f2bad67429c4bf49ab13a211587 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 9 Jan 2019 14:29:14 +0000 Subject: [PATCH 024/113] Added metadata, string identifiers and operation args --- package-lock.json | 6 ++-- package.json | 2 +- src/core/operations/YaraRules.mjs | 58 +++++++++++++++++++++++-------- src/web/HTMLIngredient.mjs | 3 +- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 573f8e67..2b4d058b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7756,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.6.tgz", - "integrity": "sha512-Crnaz5G/ejjZrEYTlyUZIaquR66djW8w8UR8GtgFrpWzhiySPJTcdxwOhGmCku2VhhETPznz20KxBNifBSF+oA==" + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.8.tgz", + "integrity": "sha512-ZB+Ya3bEBoanvde47X8RzqpMBHgrPxrTZIJ/UEoatVnOEy2he1IORuotdSkP2o73URRzHGN1jNWDIhTdfbZ3rQ==" }, "livereload-js": { "version": "2.4.0", diff --git a/package.json b/package.json index 2f9c7d04..8e21a0a6 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.6", + "libyara-wasm": "0.0.8", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YaraRules.mjs index b6d78a8c..bfdebb13 100644 --- a/src/core/operations/YaraRules.mjs +++ b/src/core/operations/YaraRules.mjs @@ -25,11 +25,28 @@ class YaraRules extends Operation { this.infoURL = "https://en.wikipedia.org/wiki/YARA"; this.inputType = "ArrayBuffer"; this.outputType = "string"; - this.args = [{ - name: "Rules", - type: "code", - value: "" - }]; + this.args = [ + { + name: "Rules", + type: "code", + value: "" + }, + { + name: "Show strings", + type: "boolean", + value: false + }, + { + name: "Show string lengths", + type: "boolean", + value: false + }, + { + name: "Show metadata", + type: "boolean", + value: false + } + ]; } /** @@ -38,6 +55,7 @@ class YaraRules extends Operation { * @returns {string} */ run(input, args) { + const [rules, showStrings, showLengths, showMeta] = args; return new Promise((resolve, reject) => { Yara().then(yara => { let matchString = ""; @@ -46,7 +64,7 @@ class YaraRules extends Operation { for (let i = 0; i < inpArr.length; i++) { inpVec.push_back(inpArr[i]); } - const resp = yara.run(inpVec, args[0]); + const resp = yara.run(inpVec, rules); if (resp.compileErrors.size() > 0) { for (let i = 0; i < resp.compileErrors.size(); i++) { const compileError = resp.compileErrors.get(i); @@ -58,16 +76,26 @@ class YaraRules extends Operation { } } const matchedRules = resp.matchedRules; - for (let i = 0; i < matchedRules.keys().size(); i++) { - const ruleMatches = matchedRules.get(matchedRules.keys().get(i)); - if (ruleMatches.size() === 0) { - matchString += `Input matches rule "${matchedRules.keys().get(i)}".\n`; + for (let i = 0; i < matchedRules.size(); i++) { + const rule = matchedRules.get(i); + const matches = rule.resolvedMatches; + let meta = ""; + if (showMeta && rule.metadata.size() > 0) { + meta += " ["; + for (let j = 0; j < rule.metadata.size(); j++) { + meta += `${rule.metadata.get(j).identifier}: ${rule.metadata.get(j).data}, `; + } + meta = meta.slice(0, -2) + "]"; + } + if (matches.size() === 0 || !(showStrings || showLengths)) { + matchString += `Input matches rule "${rule.ruleName}"${meta}.\n`; } else { - matchString += `Rule "${matchedRules.keys().get(i)}" matches:\n`; - - for (let j = 0; j < ruleMatches.size(); j++) { - const match = ruleMatches.get(j); - matchString += `Position ${match.location}, length ${match.matchLength}, data: ${match.data}\n`; + matchString += `Rule "${rule.ruleName}"${meta} matches:\n`; + for (let j = 0; j < matches.size(); j++) { + const match = matches.get(j); + if (showStrings || showLengths) { + matchString += `Pos ${match.location}, ${showLengths ? `length ${match.matchLength}, ` : ""}identifier ${match.stringIdentifier}${showStrings ? `, data: "${match.data}"` : ""}\n`; + } } } diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index 4de7e43f..234c5343 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -51,7 +51,8 @@ class HTMLIngredient { value="${this.value}" rows=5 ${this.disabled ? "disabled" : ""}> - ${this.hint ? "" + this.hint + "" : ""}`; + ${this.hint ? "" + this.hint + "" : ""} +
`; break; case "string": case "binaryString": From dd9ba4d250b381e45966b74e9109e95b2306939b Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 9 Jan 2019 15:28:50 +0000 Subject: [PATCH 025/113] Fixed problems flagged by n's review --- src/core/Ingredient.mjs | 2 ++ src/core/Operation.mjs | 1 + .../{YaraRules.mjs => YARARules.mjs} | 24 +++++++++++-------- src/web/HTMLIngredient.mjs | 14 ++--------- 4 files changed, 19 insertions(+), 22 deletions(-) rename src/core/operations/{YaraRules.mjs => YARARules.mjs} (79%) diff --git a/src/core/Ingredient.mjs b/src/core/Ingredient.mjs index 00dd5f6d..96cdd400 100755 --- a/src/core/Ingredient.mjs +++ b/src/core/Ingredient.mjs @@ -23,6 +23,7 @@ class Ingredient { this._value = null; this.disabled = false; this.hint = ""; + this.rows = 0; this.toggleValues = []; this.target = null; this.defaultIndex = 0; @@ -45,6 +46,7 @@ class Ingredient { this.defaultValue = ingredientConfig.value; this.disabled = !!ingredientConfig.disabled; this.hint = ingredientConfig.hint || false; + this.rows = ingredientConfig.rows || false; this.toggleValues = ingredientConfig.toggleValues; this.target = typeof ingredientConfig.target !== "undefined" ? ingredientConfig.target : null; this.defaultIndex = typeof ingredientConfig.defaultIndex !== "undefined" ? ingredientConfig.defaultIndex : 0; diff --git a/src/core/Operation.mjs b/src/core/Operation.mjs index 3f6b3e86..d57f885d 100755 --- a/src/core/Operation.mjs +++ b/src/core/Operation.mjs @@ -179,6 +179,7 @@ class Operation { if (ing.toggleValues) conf.toggleValues = ing.toggleValues; if (ing.hint) conf.hint = ing.hint; + if (ing.rows) conf.rows = ing.rows; if (ing.disabled) conf.disabled = ing.disabled; if (ing.target) conf.target = ing.target; if (ing.defaultIndex) conf.defaultIndex = ing.defaultIndex; diff --git a/src/core/operations/YaraRules.mjs b/src/core/operations/YARARules.mjs similarity index 79% rename from src/core/operations/YaraRules.mjs rename to src/core/operations/YARARules.mjs index bfdebb13..094d9f43 100644 --- a/src/core/operations/YaraRules.mjs +++ b/src/core/operations/YARARules.mjs @@ -9,41 +9,45 @@ import OperationError from "../errors/OperationError"; import Yara from "libyara-wasm"; /** - * Yara Rules operation + * YARA Rules operation */ -class YaraRules extends Operation { +class YARARules extends Operation { /** - * YaraRules constructor + * YARARules constructor */ constructor() { super(); - this.name = "Yara Rules"; + this.name = "YARA Rules"; this.module = "Yara"; - this.description = "Yara support"; - this.infoURL = "https://en.wikipedia.org/wiki/YARA"; + this.description = "YARA is a tool developed at VirusTotal, primarily aimed at helping malware researchers to identify and classify malware samples. It matches based on rules specified by the user containing textual or binary patterns and a boolean expression. For help on writing rules, see the YARA documentation."; + this.infoURL = "https://wikipedia.org/wiki/YARA"; this.inputType = "ArrayBuffer"; this.outputType = "string"; this.args = [ { name: "Rules", - type: "code", - value: "" + type: "text", + value: "", + rows: 5 }, { name: "Show strings", type: "boolean", + hint: "Show each match's data", value: false }, { name: "Show string lengths", type: "boolean", + hint: "Show the length of each match's data", value: false }, { name: "Show metadata", type: "boolean", + hint: "Show the metadata of each rule", value: false } ]; @@ -59,7 +63,7 @@ class YaraRules extends Operation { return new Promise((resolve, reject) => { Yara().then(yara => { let matchString = ""; - const inpArr = new Uint8Array(input); // I know this is garbage but it's like 1.5 times faster + const inpArr = new Uint8Array(input); const inpVec = new yara.vectorChar(); for (let i = 0; i < inpArr.length; i++) { inpVec.push_back(inpArr[i]); @@ -107,4 +111,4 @@ class YaraRules extends Operation { } -export default YaraRules; +export default YARARules; diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index 234c5343..59b7bec7 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -25,6 +25,7 @@ class HTMLIngredient { this.value = config.value; this.disabled = config.disabled || false; this.hint = config.hint || false; + this.rows = config.rows || false; this.target = config.target; this.defaultIndex = config.defaultIndex || 0; this.toggleValues = config.toggleValues; @@ -42,18 +43,6 @@ class HTMLIngredient { i, m; switch (this.type) { - case "code": - html+= `
- - - ${this.hint ? "" + this.hint + "" : ""} -
`; - break; case "string": case "binaryString": case "byteArray": @@ -241,6 +230,7 @@ class HTMLIngredient { class="form-control arg" id="${this.id}" arg-name="${this.name}" + rows="${this.rows ? this.rows : 3}" ${this.disabled ? "disabled" : ""}>${this.value} ${this.hint ? "" + this.hint + "" : ""} `; From c49a770c59adb28cfd082dc2c935f7c900902625 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 9 Jan 2019 16:36:34 +0000 Subject: [PATCH 026/113] Tidied up Lorem Ipsum op --- CHANGELOG.md | 5 ++ src/core/config/Categories.json | 4 +- src/core/lib/LoremIpsum.mjs | 81 ++++++++++--------- ...umGenerator.mjs => GenerateLoremIpsum.mjs} | 12 +-- 4 files changed, 58 insertions(+), 44 deletions(-) rename src/core/operations/{LoremIpsumGenerator.mjs => GenerateLoremIpsum.mjs} (85%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a71bb41..d9b8eff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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). +### [8.20.0] - 2019-01-09 +- 'Generate Lorem Ipsum' operation added [@klaxon1] | [#455] + ### [8.19.0] - 2018-12-30 - UI test suite added to confirm that the app loads correctly in a reasonable time and that various operations from each module can be run [@n1474335] | [#458] @@ -88,6 +91,7 @@ All major and minor version changes will be documented in this file. Details of +[8.20.0]: https://github.com/gchq/CyberChef/releases/tag/v8.20.0 [8.19.0]: https://github.com/gchq/CyberChef/releases/tag/v8.19.0 [8.18.0]: https://github.com/gchq/CyberChef/releases/tag/v8.18.0 [8.17.0]: https://github.com/gchq/CyberChef/releases/tag/v8.17.0 @@ -159,4 +163,5 @@ All major and minor version changes will be documented in this file. Details of [#446]: https://github.com/gchq/CyberChef/pull/446 [#448]: https://github.com/gchq/CyberChef/pull/448 [#449]: https://github.com/gchq/CyberChef/pull/449 +[#455]: https://github.com/gchq/CyberChef/pull/455 [#458]: https://github.com/gchq/CyberChef/pull/458 diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 3678a88b..3e792b99 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -374,9 +374,9 @@ "Generate QR Code", "Parse QR Code", "Haversine distance", + "Generate Lorem Ipsum", "Numberwang", - "XKCD Random Number", - "Lorem Ipsum Generator" + "XKCD Random Number" ] }, { diff --git a/src/core/lib/LoremIpsum.mjs b/src/core/lib/LoremIpsum.mjs index 9712a429..d7fff69b 100644 --- a/src/core/lib/LoremIpsum.mjs +++ b/src/core/lib/LoremIpsum.mjs @@ -2,16 +2,16 @@ * Lorem Ipsum generator. * * @author Klaxon [klaxon@veyr.com] - * @copyright Crown Copyright 2016 + * @copyright Crown Copyright 2018 * @license Apache-2.0 */ - /** - * generate lorem ipsum paragraphs. - * - * @param {number} length - * @returns {string} - */ +/** + * Generate lorem ipsum paragraphs. + * + * @param {number} length + * @returns {string} + */ export function GenerateParagraphs(length=3) { const paragraphs = []; while (paragraphs.length < length) { @@ -29,12 +29,13 @@ export function GenerateParagraphs(length=3) { return paragraphs.join(""); } + /** -* generate lorem ipsum sentences. -* -* @param {number} length -* @returns {string} -*/ + * Generate lorem ipsum sentences. + * + * @param {number} length + * @returns {string} + */ export function GenerateSentences(length=3) { const sentences = []; while (sentences.length < length) { @@ -46,12 +47,13 @@ export function GenerateSentences(length=3) { return paragraphs.join(""); } + /** -* generate lorem ipsum words. -* -* @param {number} length -* @returns {string} -*/ + * Generate lorem ipsum words. + * + * @param {number} length + * @returns {string} + */ export function GenerateWords(length=3) { const words = getWords(length); const sentences = wordsToSentences(words); @@ -59,19 +61,21 @@ export function GenerateWords(length=3) { return paragraphs.join(""); } - /** - * generate lorem ipsum bytes. - * - * @param {number} length - * @returns {string} - */ + +/** + * Generate lorem ipsum bytes. + * + * @param {number} length + * @returns {string} + */ export function GenerateBytes(length=3) { const str = GenerateWords(length/3); return str.slice(0, length); } + /** - * get array of randomly selected words from the lorem ipsum wordList. + * Get array of randomly selected words from the lorem ipsum wordList. * * @param {number} length * @returns {string[]} @@ -84,16 +88,16 @@ function getWords(length=3) { while (words.length < length){ do { word = wordList[Math.floor(Math.random() * wordList.length)]; - } - while (previousWord === word); + } while (previousWord === word); words.push(word); previousWord = word; } return words; } + /** - * convert an array or words into an array of sentences" + * Convert an array of words into an array of sentences * * @param {string[]} words * @returns {string[]} @@ -112,8 +116,9 @@ function wordsToSentences(words) { return sentences; } + /** - * convert an array or sentences into an array of paragraphs" + * Convert an array of sentences into an array of paragraphs * * @param {string[]} sentences * @returns {string[]} @@ -130,15 +135,16 @@ function sentencesToParagraphs(sentences) { return paragraphs; } + /** - * format an array of words into a sentence. + * Format an array of words into a sentence. * * @param {string[]} words * @returns {string} * @private */ function formatSentence(words) { - //0.35 chance of a comma being added randomly to the sentence. + // 0.35 chance of a comma being added randomly to the sentence. if (Math.random() < PROBABILITY_OF_A_COMMA) { const pos = Math.round(Math.random()*(words.length-1)); words[pos] +=","; @@ -149,8 +155,9 @@ function formatSentence(words) { return sentence; } + /** - * format an array of sentences into a paragraph + * Format an array of sentences into a paragraph. * * @param {string[]} sentences * @returns {string} @@ -162,10 +169,11 @@ function formatParagraph(sentences) { return paragraph; } + /** - * get a random number based on a mean and standard deviation. + * Get a random number based on a mean and standard deviation. * - * @param {number} Mean + * @param {number} mean * @param {number} stdDev * @returns {number} * @private @@ -174,13 +182,13 @@ function getRandomLength(mean, stdDev) { let length; do { length = Math.round((Math.random()*2-1)+(Math.random()*2-1)+(Math.random()*2-1)*stdDev+mean); - } - while (length <= 0); + } while (length <= 0); return length; } + /** - * replace first 5 words with "Lorem ipsum dolor sit amet" + * Replace first 5 words with "Lorem ipsum dolor sit amet" * * @param {string[]} str * @returns {string[]} @@ -200,6 +208,7 @@ function replaceStart(str) { } } + const SENTENCE_LENGTH_MEAN = 15; const SENTENCE_LENGTH_STD_DEV = 9; const PARAGRAPH_LENGTH_MEAN = 5; diff --git a/src/core/operations/LoremIpsumGenerator.mjs b/src/core/operations/GenerateLoremIpsum.mjs similarity index 85% rename from src/core/operations/LoremIpsumGenerator.mjs rename to src/core/operations/GenerateLoremIpsum.mjs index 228daaa1..fb5ecd17 100644 --- a/src/core/operations/LoremIpsumGenerator.mjs +++ b/src/core/operations/GenerateLoremIpsum.mjs @@ -9,17 +9,17 @@ import OperationError from "../errors/OperationError"; import { GenerateParagraphs, GenerateSentences, GenerateWords, GenerateBytes } from "../lib/LoremIpsum"; /** - * Lorem Ipsum Generator operation + * Generate Lorem Ipsum operation */ -class LoremIpsumGenerator extends Operation { +class GenerateLoremIpsum extends Operation { /** - * LoremIpsumGenerator constructor + * GenerateLoremIpsum constructor */ constructor() { super(); - this.name = "Lorem Ipsum Generator"; + this.name = "Generate Lorem Ipsum"; this.module = "Default"; this.description = "Generate varying length lorem ipsum placeholder text."; this.infoURL = "https://wikipedia.org/wiki/Lorem_ipsum"; @@ -60,11 +60,11 @@ class LoremIpsumGenerator extends Operation { case "Bytes": return GenerateBytes(length); default: - throw new OperationError("invalid lengthType"); + throw new OperationError("Invalid length type"); } } } -export default LoremIpsumGenerator; +export default GenerateLoremIpsum; From 324c409ff11c563fa8d4dce0ccef6460d4934d1d Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 9 Jan 2019 16:38:53 +0000 Subject: [PATCH 027/113] 8.20.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 81136cc8..fb28121d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.7", + "version": "8.20.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e1b801d2..bdc53f20 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.19.7", + "version": "8.20.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 8c757d1e03918ef2f3fb9098e0642719260b6690 Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Wed, 9 Jan 2019 20:44:14 +0000 Subject: [PATCH 028/113] Bombe: optimise This cuts about 85% off the execution time. --- src/core/lib/Bombe.mjs | 209 +++++++++++++++++++++++-------- tests/operations/tests/Bombe.mjs | 5 +- 2 files changed, 157 insertions(+), 57 deletions(-) diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index 1e6c3d2d..3103f56a 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -81,6 +81,104 @@ class Edge { } } +/** + * As all the Bombe's rotors move in step, at any given point the vast majority of the scramblers + * in the machine share the majority of their state, which is hosted in this class. + */ +class SharedScrambler { + /** + * SharedScrambler constructor. + * @param {Object[]} rotors - List of rotors in the shared state _only_. + * @param {Object} reflector - The reflector in use. + */ + constructor(rotors, reflector) { + this.reflector = reflector; + this.rotors = rotors; + this.rotorsRev = [].concat(rotors).reverse(); + this.lowerCache = new Array(26); + this.higherCache = new Array(26); + for (let i=0; i<26; i++) { + this.higherCache[i] = new Array(26); + } + this.cacheGen(); + } + + /** + * Step the rotors forward. + * @param {number} n - How many rotors to step. This includes the rotors which are not part of + * the shared state, so should be 2 or more. + */ + step(n) { + for (let i=0; i=this.rotors.length-n; i--) { - this.rotors[i].step(); - } + step() { + // The Bombe steps the slowest rotor on an actual Enigma fastest, for reasons. + // ...but for optimisation reasons I'm going to cheat and not do that, as this vastly + // simplifies caching the state of the majority of the scramblers. The results are the + // same, just in a slightly different order. + this.rotor.step(); } + /** * Run a letter through the scrambler. * @param {number} i - The letter to transform (as a number) @@ -125,13 +224,14 @@ class Scrambler { */ transform(i) { let letter = i; - for (const rotor of this.rotors) { - letter = rotor.transform(letter); - } - letter = this.reflector.transform(letter); - for (const rotor of this.rotorsRev) { - letter = rotor.revTransform(letter); + const cached = this.baseScrambler.fullTransform(this.rotor.pos, i); + if (cached !== undefined) { + return cached; } + letter = this.rotor.transform(letter); + letter = this.baseScrambler.transform(letter); + letter = this.rotor.revTransform(letter); + this.baseScrambler.addCache(this.rotor.pos, i, letter); return letter; } @@ -155,15 +255,11 @@ class Scrambler { */ getPos() { let result = ""; - for (let i=0; i 1) { + this.sharedScrambler.step(n); + } for (const scrambler of this.allScramblers) { - scrambler.step(n); + scrambler.step(); } // Send status messages at what seems to be a reasonably sensible frequency // (note this won't be triggered on 3-rotor runs - they run fast enough it doesn't seem necessary) diff --git a/tests/operations/tests/Bombe.mjs b/tests/operations/tests/Bombe.mjs index 65f4b701..6a96884c 100644 --- a/tests/operations/tests/Bombe.mjs +++ b/tests/operations/tests/Bombe.mjs @@ -27,6 +27,7 @@ TestRegister.addTests([ ] }, { + // This test produces a menu that doesn't use the first letter, which is also a good test name: "Bombe: 3 rotor (other stecker)", input: "JBYALIHDYNUAAVKBYM", expectedMatch: /LGA \(plugboard: AG\): QFIMUMAFKMQSKMYNGW/, @@ -80,8 +81,7 @@ TestRegister.addTests([ } ] }, - /* - * Long test is long + // This test is a bit slow - it takes about 12s on my test hardware { name: "Bombe: 4 rotor", input: "LUOXGJSHGEDSRDOQQX", @@ -100,7 +100,6 @@ TestRegister.addTests([ } ] }, - */ { name: "Bombe: no crib", input: "JBYALIHDYNUAAVKBYM", From 995fcab071e187c092ee085bc6e5c41c817e9590 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Thu, 10 Jan 2019 15:01:01 +0000 Subject: [PATCH 029/113] Tidied up Case Insensitive Regex ops --- CHANGELOG.md | 6 ++++++ src/core/operations/FromCaseInsensitiveRegex.mjs | 2 +- src/core/operations/ToCaseInsensitiveRegex.mjs | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9b8eff0..4761b540 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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). +### [8.21.0] - 2019-01-10 +- 'To Case Insensitive Regex' and 'From Case Insensitive Regex' operations added [@masq] | [#461] + ### [8.20.0] - 2019-01-09 - 'Generate Lorem Ipsum' operation added [@klaxon1] | [#455] @@ -91,6 +94,7 @@ All major and minor version changes will be documented in this file. Details of +[8.21.0]: https://github.com/gchq/CyberChef/releases/tag/v8.21.0 [8.20.0]: https://github.com/gchq/CyberChef/releases/tag/v8.20.0 [8.19.0]: https://github.com/gchq/CyberChef/releases/tag/v8.19.0 [8.18.0]: https://github.com/gchq/CyberChef/releases/tag/v8.18.0 @@ -134,6 +138,7 @@ All major and minor version changes will be documented in this file. Details of [@tcode2k16]: https://github.com/tcode2k16 [@Cynser]: https://github.com/Cynser [@anthony-arnold]: https://github.com/anthony-arnold +[@masq]: https://github.com/masq [#95]: https://github.com/gchq/CyberChef/pull/299 [#173]: https://github.com/gchq/CyberChef/pull/173 @@ -165,3 +170,4 @@ All major and minor version changes will be documented in this file. Details of [#449]: https://github.com/gchq/CyberChef/pull/449 [#455]: https://github.com/gchq/CyberChef/pull/455 [#458]: https://github.com/gchq/CyberChef/pull/458 +[#461]: https://github.com/gchq/CyberChef/pull/461 diff --git a/src/core/operations/FromCaseInsensitiveRegex.mjs b/src/core/operations/FromCaseInsensitiveRegex.mjs index 2448c5e5..36cd9b14 100644 --- a/src/core/operations/FromCaseInsensitiveRegex.mjs +++ b/src/core/operations/FromCaseInsensitiveRegex.mjs @@ -19,7 +19,7 @@ class FromCaseInsensitiveRegex extends Operation { this.name = "From Case Insensitive Regex"; this.module = "Default"; - this.description = "Converts a case-insensitive regex string to a case sensitive regex string (no guarantee on it being the proper original casing) in case /i wasn't available at the time but now is, or you need it to be case-sensitive again."; + this.description = "Converts a case-insensitive regex string to a case sensitive regex string (no guarantee on it being the proper original casing) in case the i flag wasn't available at the time but now is, or you need it to be case-sensitive again.

e.g. [mM][oO][zZ][iI][lL][lL][aA]/[0-9].[0-9] .* becomes Mozilla/[0-9].[0-9] .*"; this.infoURL = "https://wikipedia.org/wiki/Regular_expression"; this.inputType = "string"; this.outputType = "string"; diff --git a/src/core/operations/ToCaseInsensitiveRegex.mjs b/src/core/operations/ToCaseInsensitiveRegex.mjs index 32fb96c7..0d606a05 100644 --- a/src/core/operations/ToCaseInsensitiveRegex.mjs +++ b/src/core/operations/ToCaseInsensitiveRegex.mjs @@ -19,7 +19,7 @@ class ToCaseInsensitiveRegex extends Operation { this.name = "To Case Insensitive Regex"; this.module = "Default"; - this.description = "Converts a case-sensitive regex string into a case-insensitive regex string in case /i flag is unavailable to you."; + this.description = "Converts a case-sensitive regex string into a case-insensitive regex string in case the i flag is unavailable to you.

e.g. Mozilla/[0-9].[0-9] .* becomes [mM][oO][zZ][iI][lL][lL][aA]/[0-9].[0-9] .*"; this.infoURL = "https://wikipedia.org/wiki/Regular_expression"; this.inputType = "string"; this.outputType = "string"; From 863a5256255367889452fd33da3842e987714c09 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Thu, 10 Jan 2019 15:02:26 +0000 Subject: [PATCH 030/113] 8.21.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb28121d..9078d0c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.20.0", + "version": "8.21.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index bdc53f20..c0152ab0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.20.0", + "version": "8.21.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 9e63e40dab12874585a82445e3abf63dac6308b1 Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 10 Jan 2019 15:24:29 +0000 Subject: [PATCH 031/113] Add new MGRS module and update webpack-dev-server --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 48751e21..92fda38b 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "url-loader": "^1.1.2", "web-resource-inliner": "^4.2.1", "webpack": "^4.25.1", - "webpack-dev-server": "^3.1.10", + "webpack-dev-server": "^3.1.14", "webpack-node-externals": "^1.7.2", "worker-loader": "^2.0.0" }, @@ -104,6 +104,7 @@ "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", + "mgrs": "^1.0.0", "moment": "^2.22.2", "moment-timezone": "^0.5.23", "ngeohash": "^0.6.0", From c2068b343b3ae7c767d341f6c798f0fda35f3c59 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Thu, 10 Jan 2019 15:42:48 +0000 Subject: [PATCH 032/113] Tidied up and added global matching to Subsection operation --- CHANGELOG.md | 5 +++++ src/core/operations/Subsection.mjs | 30 +++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4761b540..71949c82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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). +### [8.22.0] - 2019-01-10 +- 'Subsection' operation added [@j433866] | [#467] + ### [8.21.0] - 2019-01-10 - 'To Case Insensitive Regex' and 'From Case Insensitive Regex' operations added [@masq] | [#461] @@ -94,6 +97,7 @@ All major and minor version changes will be documented in this file. Details of +[8.22.0]: https://github.com/gchq/CyberChef/releases/tag/v8.22.0 [8.21.0]: https://github.com/gchq/CyberChef/releases/tag/v8.21.0 [8.20.0]: https://github.com/gchq/CyberChef/releases/tag/v8.20.0 [8.19.0]: https://github.com/gchq/CyberChef/releases/tag/v8.19.0 @@ -171,3 +175,4 @@ All major and minor version changes will be documented in this file. Details of [#455]: https://github.com/gchq/CyberChef/pull/455 [#458]: https://github.com/gchq/CyberChef/pull/458 [#461]: https://github.com/gchq/CyberChef/pull/461 +[#467]: https://github.com/gchq/CyberChef/pull/467 diff --git a/src/core/operations/Subsection.mjs b/src/core/operations/Subsection.mjs index 94258b6d..8133d31c 100644 --- a/src/core/operations/Subsection.mjs +++ b/src/core/operations/Subsection.mjs @@ -38,6 +38,11 @@ class Subsection extends Operation { "type": "boolean", "value": true }, + { + "name": "Global matching", + "type": "boolean", + "value": true + }, { "name": "Ignore errors", "type": "boolean", @@ -49,7 +54,7 @@ class Subsection extends Operation { /** * @param {Object} state - The current state of the recipe. * @param {number} state.progress - The current position in the recipe. - * @param {Dish} state.Dish - The Dish being operated on + * @param {Dish} state.dish - The Dish being operated on * @param {Operation[]} state.opList - The list of operations in the recipe * @returns {Object} - The updated state of the recipe */ @@ -59,12 +64,12 @@ class Subsection extends Operation { outputType = opList[state.progress].outputType, input = await state.dish.get(inputType), ings = opList[state.progress].ingValues, - [section, caseSensitive, ignoreErrors] = ings, + [section, caseSensitive, global, ignoreErrors] = ings, subOpList = []; if (input && section !== "") { // Create subOpList for each tranche to operate on - // (all remaining operations unless we encounter a Merge) + // all remaining operations unless we encounter a Merge for (let i = state.progress + 1; i < opList.length; i++) { if (opList[i].name === "Merge" && !opList[i].disabled) { break; @@ -73,13 +78,15 @@ class Subsection extends Operation { } } - let flags = "g", + let flags = "", inOffset = 0, output = "", m, progress = 0; - if (!caseSensitive) - flags += "i"; + + if (!caseSensitive) flags += "i"; + if (global) flags += "g"; + const regex = new XRegExp(section, flags), recipe = new Recipe(); @@ -95,16 +102,19 @@ class Subsection extends Operation { matched = true; // Add up to match let matchStr = m[0]; - if (m.length === 1) { + + if (m.length === 1) { // No capture groups output += input.slice(inOffset, m.index); - inOffset = regex.lastIndex; + inOffset = m.index + m[0].length; } else if (m.length >= 2) { matchStr = m[1]; + // Need to add some of the matched string that isn't in the capture group output += input.slice(inOffset, m.index + m[0].indexOf(m[1])); // Set i to be after the end of the first capture group - inOffset = regex.lastIndex - (m[0].length - (m[0].indexOf(m[1]) + m[1].length)); + inOffset = m.index + m[0].indexOf(m[1]) + m[1].length; } + // Baseline ing values for each tranche so that registers are reset subOpList.forEach((op, i) => { op.ingValues = JSON.parse(JSON.stringify(ingValues[i])); @@ -122,7 +132,9 @@ class Subsection extends Operation { progress = err.progress + 1; } output += await dish.get(outputType); + if (!regex.global) break; } + // If no matches were found, advance progress to after a Merge op // Otherwise, the operations below Subsection will be run on all the input data if (!matched) { From 9787ab04cdc671b9cf1ea8f64306f136eaa9371a Mon Sep 17 00:00:00 2001 From: n1474335 Date: Thu, 10 Jan 2019 15:44:02 +0000 Subject: [PATCH 033/113] 8.22.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9078d0c3..00e57f64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.21.0", + "version": "8.22.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c0152ab0..96053213 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.21.0", + "version": "8.22.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 3eb44708e503e42e3429ca9488b004efeef5b5e5 Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Thu, 10 Jan 2019 18:04:02 +0000 Subject: [PATCH 034/113] Add MultiBombe Runs the Bombe multiple times with different rotor specs. Edits the core BombeMachine a little to add the ability to switch rotors without rewiring everything --- src/core/config/Categories.json | 3 +- src/core/lib/Bombe.mjs | 63 ++++- src/core/lib/Enigma.mjs | 1 + src/core/operations/MultipleBombe.mjs | 307 +++++++++++++++++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/Bombe.mjs | 2 +- tests/operations/tests/MultipleBombe.mjs | 47 ++++ 7 files changed, 411 insertions(+), 13 deletions(-) create mode 100644 src/core/operations/MultipleBombe.mjs create mode 100644 tests/operations/tests/MultipleBombe.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 5a40846c..986606c9 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -104,7 +104,8 @@ "Citrix CTX1 Decode", "Pseudo-Random Number Generator", "Enigma", - "Bombe" + "Bombe", + "Multiple Bombe" ] }, { diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index 3103f56a..8b781b68 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -92,14 +92,24 @@ class SharedScrambler { * @param {Object} reflector - The reflector in use. */ constructor(rotors, reflector) { - this.reflector = reflector; - this.rotors = rotors; - this.rotorsRev = [].concat(rotors).reverse(); this.lowerCache = new Array(26); this.higherCache = new Array(26); for (let i=0; i<26; i++) { this.higherCache[i] = new Array(26); } + this.changeRotors(rotors, reflector); + } + + /** + * Replace the rotors and reflector in this SharedScrambler. + * This takes care of flushing caches as well. + * @param {Object[]} rotors - List of rotors in the shared state _only_. + * @param {Object} reflector - The reflector in use. + */ + changeRotors(rotors, reflector) { + this.reflector = reflector; + this.rotors = rotors; + this.rotorsRev = [].concat(rotors).reverse(); this.cacheGen(); } @@ -195,13 +205,22 @@ class Scrambler { */ constructor(base, rotor, pos, end1, end2) { this.baseScrambler = base; - this.rotor = rotor; this.initialPos = pos; - this.rotor.pos += pos; + this.changeRotor(rotor); this.end1 = end1; this.end2 = end2; } + /** + * Replace the rotor in this scrambler. + * The position is reset automatically. + * @param {Object} rotor - New rotor + */ + changeRotor(rotor) { + this.rotor = rotor; + this.rotor.pos += this.initialPos; + } + /** * Step the rotors forward. * @@ -304,12 +323,7 @@ export class BombeMachine { } this.ciphertext = ciphertext; this.crib = crib; - // This is ordered from the Enigma fast rotor to the slow, so bottom to top for the Bombe - this.baseRotors = []; - for (const rstr of rotors) { - const rotor = new CopyRotor(rstr, "", "A", "A"); - this.baseRotors.push(rotor); - } + this.initRotors(rotors); this.updateFn = update; const [mostConnected, edges] = this.makeMenu(); @@ -355,6 +369,33 @@ export class BombeMachine { } } + /** + * Build Rotor objects from list of rotor wiring strings. + * @param {string[]} rotors - List of rotor wiring strings + */ + initRotors(rotors) { + // This is ordered from the Enigma fast rotor to the slow, so bottom to top for the Bombe + this.baseRotors = []; + for (const rstr of rotors) { + const rotor = new CopyRotor(rstr, "", "A", "A"); + this.baseRotors.push(rotor); + } + } + + /** + * Replace the rotors and reflector in all components of this Bombe. + * @param {string[]} rotors - List of rotor wiring strings + * @param {Object} reflector - Reflector object + */ + changeRotors(rotors, reflector) { + // At the end of the run, the rotors are all back in the same position they started + this.initRotors(rotors); + this.sharedScrambler.changeRotors(this.baseRotors.slice(1), reflector); + for (const scrambler of this.allScramblers) { + scrambler.changeRotor(this.baseRotors[0].copy()); + } + } + /** * If we have a way of sending status messages, do so. * @param {string} msg - Message to send. diff --git a/src/core/lib/Enigma.mjs b/src/core/lib/Enigma.mjs index cfc93933..0a083bce 100644 --- a/src/core/lib/Enigma.mjs +++ b/src/core/lib/Enigma.mjs @@ -171,6 +171,7 @@ class PairMapBase { constructor(pairs, name="PairMapBase") { // I've chosen to make whitespace significant here to make a) code and // b) inputs easier to read + this.pairs = pairs; this.map = {}; if (pairs === "") { return; diff --git a/src/core/operations/MultipleBombe.mjs b/src/core/operations/MultipleBombe.mjs new file mode 100644 index 00000000..6bcd1051 --- /dev/null +++ b/src/core/operations/MultipleBombe.mjs @@ -0,0 +1,307 @@ +/** + * Emulation of the Bombe machine. + * This version carries out multiple Bombe runs to handle unknown rotor configurations. + * + * @author s2224834 + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import {BombeMachine} from "../lib/Bombe"; +import {ROTORS, REFLECTORS, Reflector} from "../lib/Enigma"; + +/** + * Convenience method for flattening the preset ROTORS object into a newline-separated string. + * @param {Object[]} - Preset rotors object + * @param {number} s - Start index + * @param {number} n - End index + * @returns {string} + */ +function rotorsFormat(rotors, s, n) { + const res = []; + for (const i of rotors.slice(s, n)) { + res.push(i.value); + } + return res.join("\n"); +} + +/** + * Combinatorics choose function + * @param {number} n + * @param {number} k + * @returns number + */ +function choose(n, k) { + let res = 1; + for (let i=1; i<=k; i++) { + res *= (n + 1 - i) / i; + } + return res; +} + +/** + * Bombe operation + */ +class MultipleBombe extends Operation { + /** + * Bombe constructor + */ + constructor() { + super(); + + this.name = "Multiple Bombe"; + this.module = "Default"; + this.description = "Emulation of the Bombe machine used to attack Enigma. This version carries out multiple Bombe runs to handle unknown rotor configurations.

You should test your menu on the single Bombe operation before running it here. See the description of the Bombe operation for instructions on choosing a crib."; + this.infoURL = "https://wikipedia.org/wiki/Bombe"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Standard Enigmas", + "type": "populateOption", + "value": [ + { + name: "German Service Enigma (First - 3 rotor)", + value: rotorsFormat(ROTORS, 0, 5) + }, + { + name: "German Service Enigma (Second - 3 rotor)", + value: rotorsFormat(ROTORS, 0, 8) + }, + { + name: "German Service Enigma (Third - 4 rotor)", + value: rotorsFormat(ROTORS, 0, 8) + }, + { + name: "German Service Enigma (Fourth - 4 rotor)", + value: rotorsFormat(ROTORS, 0, 8) + }, + { + name: "User defined", + value: "" + }, + ], + "target": 1 + }, + { + name: "Main rotors", + type: "text", + value: "" + }, + { + "name": "Standard Enigmas", + "type": "populateOption", + "value": [ + { + name: "German Service Enigma (First - 3 rotor)", + value: "" + }, + { + name: "German Service Enigma (Second - 3 rotor)", + value: "" + }, + { + name: "German Service Enigma (Third - 4 rotor)", + value: rotorsFormat(ROTORS, 8, 9) + }, + { + name: "German Service Enigma (Fourth - 4 rotor)", + value: rotorsFormat(ROTORS, 8, 10) + }, + { + name: "User defined", + value: "" + }, + ], + "target": 3 + }, + { + name: "4th rotor", + type: "text", + value: "" + }, + { + "name": "Standard Enigmas", + "type": "populateOption", + "value": [ + { + name: "German Service Enigma (First - 3 rotor)", + value: rotorsFormat(REFLECTORS, 0, 1) + }, + { + name: "German Service Enigma (Second - 3 rotor)", + value: rotorsFormat(REFLECTORS, 0, 2) + }, + { + name: "German Service Enigma (Third - 4 rotor)", + value: rotorsFormat(REFLECTORS, 2, 3) + }, + { + name: "German Service Enigma (Fourth - 4 rotor)", + value: rotorsFormat(REFLECTORS, 2, 4) + }, + { + name: "User defined", + value: "" + }, + ], + "target": 5 + }, + { + name: "Reflectors", + type: "text", + value: "" + }, + { + name: "Crib", + type: "string", + value: "" + }, + { + name: "Crib offset", + type: "number", + value: 0 + } + ]; + } + + /** + * Format and send a status update message. + * @param {number} nLoops - Number of loops in the menu + * @param {number} nStops - How many stops so far + * @param {number} progress - Progress (as a float in the range 0..1) + */ + updateStatus(nLoops, nStops, progress) { + const msg = `Bombe run with ${nLoops} loops in menu (2+ desirable): ${nStops} stops, ${Math.floor(100 * progress)}% done`; + self.sendStatusMessage(msg); + } + + /** + * Early rotor description string validation. + * Drops stepping information. + * @param {string} rstr - The rotor description string + * @returns {string} - Rotor description with stepping stripped, if any + */ + validateRotor(rstr) { + // The Bombe doesn't take stepping into account so we'll just ignore it here + if (rstr.includes("<")) { + rstr = rstr.split("<", 2)[0]; + } + // Duplicate the validation of the rotor strings here, otherwise you might get an error + // thrown halfway into a big Bombe run + if (!/^[A-Z]{26}$/.test(rstr)) { + throw new OperationError("Rotor wiring must be 26 unique uppercase letters"); + } + if (new Set(rstr).size !== 26) { + throw new OperationError("Rotor wiring must be 26 unique uppercase letters"); + } + return rstr; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const mainRotorsStr = args[1]; + const fourthRotorsStr = args[3]; + const reflectorsStr = args[5]; + let crib = args[6]; + const offset = args[7]; + // TODO got this far + const rotors = []; + const fourthRotors = []; + const reflectors = []; + for (let rstr of mainRotorsStr.split("\n")) { + rstr = this.validateRotor(rstr); + rotors.push(rstr); + } + if (rotors.length < 3) { + throw new OperationError("A minimum of three rotors must be supplied"); + } + if (fourthRotorsStr !== "") { + for (let rstr of fourthRotorsStr.split("\n")) { + rstr = this.validateRotor(rstr); + fourthRotors.push(rstr); + } + } + if (fourthRotors.length === 0) { + fourthRotors.push(""); + } + for (const rstr of reflectorsStr.split("\n")) { + const reflector = new Reflector(rstr); + reflectors.push(reflector); + } + if (reflectors.length === 0) { + throw new OperationError("A minimum of one reflector must be supplied"); + } + if (crib.length === 0) { + throw new OperationError("Crib cannot be empty"); + } + if (offset < 0) { + throw new OperationError("Offset cannot be negative"); + } + // For symmetry with the Enigma op, for the input we'll just remove all invalid characters + input = input.replace(/[^A-Za-z]/g, "").toUpperCase(); + crib = crib.replace(/[^A-Za-z]/g, "").toUpperCase(); + const ciphertext = input.slice(offset); + let update; + if (ENVIRONMENT_IS_WORKER()) { + update = this.updateStatus; + } else { + update = undefined; + } + let bombe = undefined; + let msg; + // I could use a proper combinatorics algorithm here... but it would be more code to + // write one, and we don't seem to have one in our existing libraries, so massively nested + // for loop it is + const totalRuns = choose(rotors.length, 3) * 6 * fourthRotors.length * reflectors.length; + let nRuns = 0; + let nStops = 0; + for (const rotor1 of rotors) { + for (const rotor2 of rotors) { + if (rotor2 === rotor1) { + continue; + } + for (const rotor3 of rotors) { + if (rotor3 === rotor2 || rotor3 === rotor1) { + continue; + } + for (const rotor4 of fourthRotors) { + for (const reflector of reflectors) { + nRuns++; + const runRotors = [rotor1, rotor2, rotor3]; + if (rotor4 !== "") { + runRotors.push(rotor4); + } + if (bombe === undefined) { + bombe = new BombeMachine(runRotors, reflector, ciphertext, crib); + msg = `Bombe run on menu with ${bombe.nLoops} loops (2+ desirable). Note: Rotors and rotor positions are listed left to right, ignore stepping and the ring setting, and positions start at the beginning of the crib. One stecker pair is determined. A decryption preview starting at the beginning of the crib and ignoring stepping is also provided. Results:\n`; + } else { + bombe.changeRotors(runRotors, reflector); + } + const result = bombe.run(); + nStops += result.length; + if (update !== undefined) { + update(bombe.nLoops, nStops, nRuns / totalRuns); + } + if (result.length > 0) { + msg += `Rotors: ${runRotors.join(", ")}\nReflector: ${reflector.pairs}\n`; + for (const [setting, stecker, decrypt] of result) { + msg += `Stop: ${setting} (plugboard: ${stecker}): ${decrypt}\n`; + } + } + } + } + } + } + } + return msg; + } +} + +export default MultipleBombe; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index dfd5fb1d..b5b25c0c 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -84,6 +84,7 @@ import "./tests/ParseTLV"; import "./tests/Media"; import "./tests/Enigma"; import "./tests/Bombe"; +import "./tests/MultipleBombe"; // Cannot test operations that use the File type yet //import "./tests/SplitColourChannels"; diff --git a/tests/operations/tests/Bombe.mjs b/tests/operations/tests/Bombe.mjs index 6a96884c..0a8af93f 100644 --- a/tests/operations/tests/Bombe.mjs +++ b/tests/operations/tests/Bombe.mjs @@ -85,7 +85,7 @@ TestRegister.addTests([ { name: "Bombe: 4 rotor", input: "LUOXGJSHGEDSRDOQQX", - expectedMatch: /LHSC \(plugboard: SS\)/, + expectedMatch: /LHSC \(plugboard: SS\): HHHSSSGQUUQPKSEKWK/, recipeConfig: [ { "op": "Bombe", diff --git a/tests/operations/tests/MultipleBombe.mjs b/tests/operations/tests/MultipleBombe.mjs new file mode 100644 index 00000000..5f7f43c4 --- /dev/null +++ b/tests/operations/tests/MultipleBombe.mjs @@ -0,0 +1,47 @@ +/** + * Bombe machine tests. + * @author s2224834 + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ +import TestRegister from "../TestRegister"; + +TestRegister.addTests([ + { + name: "Multi-Bombe: 3 rotor", + input: "BBYFLTHHYIJQAYBBYS", + expectedMatch: /LGA \(plugboard: SS\): VFISUSGTKSTMPSUNAK/, + recipeConfig: [ + { + "op": "Multiple Bombe", + "args": [ + // I, II and III + "User defined", "EKMFLGDQVZNTOWYHXUSPAIBRCJ Date: Thu, 10 Jan 2019 18:44:50 +0000 Subject: [PATCH 035/113] Bombe: Firefox optimisation Switch a couple of for of loops in the critical path for classic fors. This loses about 10% performance in Chrome, but it brings Firefox performance in line with Chrome's, rather than 2.5 times slower. --- src/core/lib/Bombe.mjs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index 8b781b68..7400c98a 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -514,7 +514,8 @@ export class BombeMachine { const idxPair = 26*j + i; this.wires[idxPair] = true; - for (const scrambler of this.scramblers[i]) { + for (let k=0; k Date: Fri, 11 Jan 2019 11:59:13 +0000 Subject: [PATCH 036/113] Add ConvertCoordinates to lib folder --- src/core/lib/ConvertCoordinates.mjs | 210 ++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 src/core/lib/ConvertCoordinates.mjs diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs new file mode 100644 index 00000000..45ab0690 --- /dev/null +++ b/src/core/lib/ConvertCoordinates.mjs @@ -0,0 +1,210 @@ +/** + * Co-ordinate conversion resources. + * + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import geohash from "ngeohash"; +import mgrs from "mgrs"; + +/** + * Co-ordinate formats + */ +export const FORMATS = [ + "Degrees Minutes Seconds", + "Degrees Decimal Minutes", + "Decimal Degrees", + "Geohash", + "Military Grid Reference System" +]; + +/** + * Convert a given latitude and longitude into a different format. + * @param {string} inLat - Input latitude to be converted. Use this for supplying single values for conversion (e.g. geohash) + * @param {string} inLong - Input longitude to be converted + * @param {string} inFormat - Format of the input coordinates + * @param {string} outFormat - Format to convert to + * @param {number} precision - Precision of the result + * @returns {string[]} Array containing the converted latitude and longitude + */ +export function convertCoordinates (inLat, inLong, inFormat, outFormat, precision) { + let convLat = inLat; + let convLong = inLong; + if (inFormat === "Geohash") { + const hash = geohash.decode(inLat); + convLat = hash.latitude.toString(); + convLong = hash.longitude.toString(); + } else if (inFormat === "Military Grid Reference System") { + const result = mgrs.toPoint(inLat.replace(" ", "")); + convLat = result[1]; + convLong = result[0]; + } else { + convLat = convertSingleCoordinate(inLat, inFormat, "Decimal Degrees", 15).split("°"); + convLong = convertSingleCoordinate(inLong, inFormat, "Decimal Degrees", 15).split("°"); + } + + if (outFormat === "Geohash") { + convLat = geohash.encode(parseFloat(convLat), parseFloat(convLong), precision); + } else if (outFormat === "Military Grid Reference System") { + convLat = mgrs.forward([parseFloat(convLong), parseFloat(convLat)], precision); + } else { + convLat = convertSingleCoordinate(convLat.toString(), "Decimal Degrees", outFormat, precision); + convLong = convertSingleCoordinate(convLong.toString(), "Decimal Degrees", outFormat, precision); + } + + return [convLat, convLong]; +} + +/** + * @param {string} input - The input co-ordinate to be converted + * @param {string} inFormat - The format of the input co-ordinates + * @param {string} outFormat - The format which input should be converted to + * @param {boolean} returnRaw - When true, returns the raw float instead of a String + * @returns {string|{Object}} The converted co-ordinate result, as either the raw object or a formatted string + */ +export function convertSingleCoordinate (input, inFormat, outFormat, precision, returnRaw = false){ + let converted; + precision = Math.pow(10, precision); + const convData = splitInput(input); + // Convert everything to decimal degrees first + switch (inFormat) { + case "Degrees Minutes Seconds": + if (convData.length < 3) { + throw "Invalid co-ordinates format."; + } + converted = convDMSToDD(convData[0], convData[1], convData[2], precision); + break; + case "Degrees Decimal Minutes": + if (convData.length < 2) { + throw "Invalid co-ordinates format."; + } + converted = convDDMToDD(convData[0], convData[1], precision); + break; + case "Decimal Degrees": + if (convData.length < 1) { + throw "Invalid co-ordinates format."; + } + converted = convDDToDD(convData[0], precision); + break; + default: + throw "Unknown input format selection."; + } + + // Convert from decimal degrees to the output format + switch (outFormat) { + case "Decimal Degrees": + break; + case "Degrees Minutes Seconds": + converted = convDDToDMS(converted.degrees); + break; + case "Degrees Decimal Minutes": + converted = convDDToDDM(converted.degrees, precision); + break; + default: + throw "Unknown output format selection."; + } + if (returnRaw) { + return converted; + } else { + return converted.string; + } +} + +/** + * Split up the input using a space, and sanitise the result + * @param {string} input - The input data to be split + * @returns {number[]} An array of the different items in the string, stored as floats + */ +function splitInput (input){ + const split = []; + + input.split(" ").forEach(item => { + // Remove any character that isn't a digit + item = item.replace(/[^0-9.-]/g, ""); + if (item.length > 0){ + split.push(parseFloat(item, 10)); + } + }); + return split; +} + +/** + * Convert Degrees Minutes Seconds to Decimal Degrees + * @param {number} degrees - The degrees of the input co-ordinates + * @param {number} minutes - The minutes of the input co-ordinates + * @param {number} seconds - The seconds of the input co-ordinates + * @param {number} precision - The precision the result should be rounded to + * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) + */ +function convDMSToDD (degrees, minutes, seconds, precision){ + const converted = new Object(); + converted.degrees = degrees + (minutes / 60) + (seconds / 3600); + converted.string = (Math.round(converted.degrees * precision) / precision) + "°"; + return converted; +} + +/** + * Convert Decimal Degrees Minutes to Decimal Degrees + * @param {number} degrees - The input degrees to be converted + * @param {number} minutes - The input minutes to be converted + * @param {number} precision - The precision which the result should be rounded to + * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) + */ +function convDDMToDD (degrees, minutes, precision) { + const converted = new Object(); + converted.degrees = degrees + minutes / 60; + converted.string = ((Math.round(converted.degrees * precision) / precision) + "°"); + return converted; +} + +/** + * Convert Decimal Degrees to Decimal Degrees + * Doesn't affect the input, just puts it into an object + * @param {number} degrees - The input degrees to be converted + * @param {number} precision - The precision which the result should be rounded to + * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) + */ +function convDDToDD (degrees, precision) { + const converted = new Object(); + converted.degrees = degrees; + converted.string = Math.round(converted.degrees * precision) / precision + "°"; + return converted; +} + +/** + * Convert Decimal Degrees to Degrees Minutes Seconds + * @param {number} decDegrees - The input data to be converted + * @returns {{string: string, degrees: number, minutes: number, seconds: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes, .seconds), and a formatted string version (obj.string) + */ +function convDDToDMS (decDegrees) { + const degrees = Math.floor(decDegrees); + const minutes = Math.floor(60 * (decDegrees - degrees)); + const seconds = Math.round(3600 * (decDegrees - degrees) - 60 * minutes); + + const converted = new Object(); + converted.degrees = degrees; + converted.minutes = minutes; + converted.seconds = seconds; + converted.string = degrees + "° " + minutes + "' " + seconds + "\""; + return converted; +} + +/** + * Convert Decimal Degrees to Degrees Decimal Minutes + * @param {number} decDegrees - The input degrees to be converted + * @param {number} precision - The precision the input data should be rounded to + * @returns {{string: string, degrees: number, minutes: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes), and a formatted string version (obj.string) + */ +function convDDToDDM (decDegrees, precision) { + const degrees = Math.floor(decDegrees); + const minutes = decDegrees - degrees; + const decMinutes = Math.round((minutes * 60) * precision) / precision; + + const converted = new Object(); + converted.degrees = degrees; + converted.minutes = decMinutes; + converted.string = degrees + "° " + decMinutes + "'"; + return converted; +} From 21335e7d05abad8dda036ac999e761b6392944e2 Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Fri, 11 Jan 2019 13:18:25 +0000 Subject: [PATCH 037/113] Bombe: Add checking machine --- src/core/lib/Bombe.mjs | 168 +++++++++++++++++------ src/core/lib/Enigma.mjs | 3 +- src/core/operations/Bombe.mjs | 12 +- src/core/operations/MultipleBombe.mjs | 13 +- tests/operations/tests/Bombe.mjs | 40 ++++-- tests/operations/tests/Enigma.mjs | 4 +- tests/operations/tests/MultipleBombe.mjs | 4 +- 7 files changed, 178 insertions(+), 66 deletions(-) diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index 7400c98a..4ae0ff7f 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -8,7 +8,7 @@ import OperationError from "../errors/OperationError"; import Utils from "../Utils"; -import {Rotor, a2i, i2a} from "./Enigma"; +import {Rotor, Plugboard, a2i, i2a} from "./Enigma"; /** * Convenience/optimisation subclass of Rotor @@ -302,7 +302,7 @@ export class BombeMachine { * @param {string} crib - Known plaintext for this ciphertext * @param {function} update - Function to call to send status updates (optional) */ - constructor(rotors, reflector, ciphertext, crib, update=undefined) { + constructor(rotors, reflector, ciphertext, crib, check, update=undefined) { if (ciphertext.length < crib.length) { throw new OperationError("Crib overruns supplied ciphertext"); } @@ -324,6 +324,7 @@ export class BombeMachine { this.ciphertext = ciphertext; this.crib = crib; this.initRotors(rotors); + this.check = check; this.updateFn = update; const [mostConnected, edges] = this.makeMenu(); @@ -507,7 +508,6 @@ export class BombeMachine { if (this.wires[idx]) { return; } - this.energiseCount ++; this.wires[idx] = true; // Welchman's diagonal board: if A steckers to B, that implies B steckers to A. Handle // both. @@ -564,16 +564,131 @@ export class BombeMachine { const fastRotor = this.indicator.rotor; const initialPos = fastRotor.pos; const res = []; + const plugboard = new Plugboard(stecker); // The indicator scrambler starts in the right place for the beginning of the ciphertext. for (let i=0; i 1) { + // This is an invalid stop. + return ""; + } else if (count === 0) { + // No information about steckering from this wire + continue; + } + results.add(this.formatPair(i, other)); + } + return [...results].join(" "); + } + + /** + * Check to see if the Bombe has stopped. If so, process the stop. + * @returns {(undefined|string[3])} - Undefined for no stop, or [rotor settings, plugboard settings, decryption preview] + */ + checkStop() { + // Count the energised outputs + let count = 0; + for (let j=26*this.testRegister; j<26*(1+this.testRegister); j++) { + if (this.wires[j]) { + count++; + } + } + if (count === 26) { + return undefined; + } + // If it's not all of them, we have a stop + let steckerPair; + // The Bombe tells us one stecker pair as well. The input wire and test register we + // started with are hypothesised to be a stecker pair. + if (count === 25) { + // Our steckering hypothesis is wrong. Correct value is the un-energised wire. + for (let j=0; j<26; j++) { + if (!this.wires[26*this.testRegister + j]) { + steckerPair = j; + break; + } + } + } else if (count === 1) { + // This means our hypothesis for the steckering is correct. + steckerPair = this.testInput[1]; + } else { + // If this happens a lot it implies the menu isn't good enough. We can't do + // anything useful with it as we don't have a stecker partner, so we'll just drop it + // and move on. This does risk eating the actual stop occasionally, but I've only seen + // this happen when the menu is bad enough we have thousands of stops, so I'm not sure + // it matters. + return undefined; + } + let stecker; + if (this.check) { + stecker = this.checkingMachine(steckerPair); + if (stecker === "") { + // Invalid stop - don't count it, don't return it + return undefined; + } + } else { + stecker = `${i2a(this.testRegister)}${i2a(steckerPair)}`; + } + const testDecrypt = this.tryDecrypt(stecker); + return [this.indicator.getPos(), stecker, testDecrypt]; + } + /** * Having set up the Bombe, do the actual attack run. This tries every possible rotor setting * and attempts to logically invalidate them. If it can't, it's added to the list of candidate @@ -592,45 +707,12 @@ export class BombeMachine { } // Energise the test input, follow the current through each scrambler // (and the diagonal board) - this.energiseCount = 0; this.energise(...this.testInput); - // Count the energised outputs - let count = 0; - for (let j=26*this.testRegister; j<26*(1+this.testRegister); j++) { - if (this.wires[j]) { - count++; - } - } - // If it's not all of them, we have a stop - if (count < 26) { - stops += 1; - let stecker; - // The Bombe tells us one stecker pair as well. The input wire and test register we - // started with are hypothesised to be a stecker pair. - if (count === 25) { - // Our steckering hypothesis is wrong. Correct value is the un-energised wire. - for (let j=0; j<26; j++) { - if (!this.wires[26*this.testRegister + j]) { - stecker = [this.testRegister, j]; - break; - } - } - } else if (count === 1) { - // This means our hypothesis for the steckering is correct. - stecker = [this.testRegister, this.testInput[1]]; - } else { - // Unusual, probably indicative of a poor menu. I'm a little unclear on how - // this was really handled, but we'll return it for the moment. - stecker = undefined; - } - const testDecrypt = this.tryDecrypt(stecker); - let steckerStr; - if (stecker !== undefined) { - steckerStr = `${i2a(stecker[0])}${i2a(stecker[1])}`; - } else { - steckerStr = `?? (wire count: ${count})`; - } - result.push([this.indicator.getPos(), steckerStr, testDecrypt]); + + const stop = this.checkStop(); + if (stop !== undefined) { + stops++; + result.push(stop); } // Step all the scramblers // This loop counts how many rotors have reached their starting position (meaning the diff --git a/src/core/lib/Enigma.mjs b/src/core/lib/Enigma.mjs index 0a083bce..6b6c4d63 100644 --- a/src/core/lib/Enigma.mjs +++ b/src/core/lib/Enigma.mjs @@ -182,7 +182,8 @@ class PairMapBase { } const a = a2i(pair[0]), b = a2i(pair[1]); if (a === b) { - throw new OperationError(`${name}: cannot connect ${pair[0]} to itself`); + // self-stecker + return; } if (this.map.hasOwnProperty(a)) { throw new OperationError(`${name} connects ${pair[0]} more than once`); diff --git a/src/core/operations/Bombe.mjs b/src/core/operations/Bombe.mjs index 9ddd4b7b..292017e8 100644 --- a/src/core/operations/Bombe.mjs +++ b/src/core/operations/Bombe.mjs @@ -23,7 +23,7 @@ class Bombe extends Operation { this.name = "Bombe"; this.module = "Default"; - this.description = "Emulation of the Bombe machine used to attack Enigma.

To run this you need to have a 'crib', which is some known plaintext for a chunk of the target ciphertext, and know the rotors used. (See the 'Bombe (multiple runs)' operation if you don't know the rotors.) The machine will suggest possible configurations of the Enigma. Each suggestion has the rotor start positions (left to right) and one plugboard pair.

Choosing a crib: First, note that Enigma cannot encrypt a letter to itself, which allows you to rule out some positions for possible cribs. Secondly, the Bombe does not simulate the Enigma's middle rotor stepping. The longer your crib, the more likely a step happened within it, which will prevent the attack working. However, other than that, longer cribs are generally better. The attack produces a 'menu' which maps ciphertext letters to plaintext, and the goal is to produce 'loops': for example, with ciphertext ABC and crib CAB, we have the mappings A<->C, B<->A, and C<->B, which produces a loop A-B-C-A. The more loops, the better the crib. The operation will output this: if your menu has too few loops, a large number of incorrect outputs will be produced. Try a different crib. If the menu seems good but the right answer isn't produced, your crib may be wrong, or you may have overlapped the middle rotor stepping - try a different crib.

Output is not sufficient to fully decrypt the data. You will have to recover the rest of the plugboard settings by inspection. And the ring position is not taken into account: this affects when the middle rotor steps. If your output is correct for a bit, and then goes wrong, adjust the ring and start position on the right-hand rotor together until the output improves. If necessary, repeat for the middle rotor."; + this.description = "Emulation of the Bombe machine used to attack Enigma.

To run this you need to have a 'crib', which is some known plaintext for a chunk of the target ciphertext, and know the rotors used. (See the 'Bombe (multiple runs)' operation if you don't know the rotors.) The machine will suggest possible configurations of the Enigma. Each suggestion has the rotor start positions (left to right) and known plugboard pairs.

Choosing a crib: First, note that Enigma cannot encrypt a letter to itself, which allows you to rule out some positions for possible cribs. Secondly, the Bombe does not simulate the Enigma's middle rotor stepping. The longer your crib, the more likely a step happened within it, which will prevent the attack working. However, other than that, longer cribs are generally better. The attack produces a 'menu' which maps ciphertext letters to plaintext, and the goal is to produce 'loops': for example, with ciphertext ABC and crib CAB, we have the mappings A<->C, B<->A, and C<->B, which produces a loop A-B-C-A. The more loops, the better the crib. The operation will output this: if your menu has too few loops, a large number of incorrect outputs will be produced. Try a different crib. If the menu seems good but the right answer isn't produced, your crib may be wrong, or you may have overlapped the middle rotor stepping - try a different crib.

Output is not sufficient to fully decrypt the data. You will have to recover the rest of the plugboard settings by inspection. And the ring position is not taken into account: this affects when the middle rotor steps. If your output is correct for a bit, and then goes wrong, adjust the ring and start position on the right-hand rotor together until the output improves. If necessary, repeat for the middle rotor.

By default this operation runs the checking machine, a manual process to verify the quality of Bombe stops, on each stop, discarding stops which fail. If you want to see how many times the hardware actually stops for a given input, disable the checking machine."; this.infoURL = "https://wikipedia.org/wiki/Bombe"; this.inputType = "string"; this.outputType = "string"; @@ -66,6 +66,11 @@ class Bombe extends Operation { name: "Crib offset", type: "number", value: 0 + }, + { + name: "Use checking machine", + type: "boolean", + value: true } ]; } @@ -90,6 +95,7 @@ class Bombe extends Operation { const reflectorstr = args[4]; let crib = args[5]; const offset = args[6]; + const check = args[7]; const rotors = []; for (let i=0; i<4; i++) { if (i === 3 && args[i] === "") { @@ -120,9 +126,9 @@ class Bombe extends Operation { } else { update = undefined; } - const bombe = new BombeMachine(rotors, reflector, ciphertext, crib, update); + const bombe = new BombeMachine(rotors, reflector, ciphertext, crib, check, update); const result = bombe.run(); - let msg = `Bombe run on menu with ${bombe.nLoops} loops (2+ desirable). Note: Rotor positions are listed left to right and start at the beginning of the crib, and ignore stepping and the ring setting. One stecker pair is determined. A decryption preview starting at the beginning of the crib and ignoring stepping is also provided. Results:\n`; + let msg = `Bombe run on menu with ${bombe.nLoops} loops (2+ desirable). Note: Rotor positions are listed left to right and start at the beginning of the crib, and ignore stepping and the ring setting. Some plugboard settings are determined. A decryption preview starting at the beginning of the crib and ignoring stepping is also provided. Results:\n`; for (const [setting, stecker, decrypt] of result) { msg += `Stop: ${setting} (plugboard: ${stecker}): ${decrypt}\n`; } diff --git a/src/core/operations/MultipleBombe.mjs b/src/core/operations/MultipleBombe.mjs index 6bcd1051..4b3123a4 100644 --- a/src/core/operations/MultipleBombe.mjs +++ b/src/core/operations/MultipleBombe.mjs @@ -163,6 +163,11 @@ class MultipleBombe extends Operation { name: "Crib offset", type: "number", value: 0 + }, + { + name: "Use checking machine", + type: "boolean", + value: true } ]; } @@ -211,7 +216,7 @@ class MultipleBombe extends Operation { const reflectorsStr = args[5]; let crib = args[6]; const offset = args[7]; - // TODO got this far + const check = args[8]; const rotors = []; const fourthRotors = []; const reflectors = []; @@ -279,8 +284,8 @@ class MultipleBombe extends Operation { runRotors.push(rotor4); } if (bombe === undefined) { - bombe = new BombeMachine(runRotors, reflector, ciphertext, crib); - msg = `Bombe run on menu with ${bombe.nLoops} loops (2+ desirable). Note: Rotors and rotor positions are listed left to right, ignore stepping and the ring setting, and positions start at the beginning of the crib. One stecker pair is determined. A decryption preview starting at the beginning of the crib and ignoring stepping is also provided. Results:\n`; + bombe = new BombeMachine(runRotors, reflector, ciphertext, crib, check); + msg = `Bombe run on menu with ${bombe.nLoops} loops (2+ desirable). Note: Rotors and rotor positions are listed left to right, ignore stepping and the ring setting, and positions start at the beginning of the crib. Some plugboard settings are determined. A decryption preview starting at the beginning of the crib and ignoring stepping is also provided. Results:\n`; } else { bombe.changeRotors(runRotors, reflector); } @@ -290,7 +295,7 @@ class MultipleBombe extends Operation { update(bombe.nLoops, nStops, nRuns / totalRuns); } if (result.length > 0) { - msg += `Rotors: ${runRotors.join(", ")}\nReflector: ${reflector.pairs}\n`; + msg += `\nRotors: ${runRotors.join(", ")}\nReflector: ${reflector.pairs}\n`; for (const [setting, stecker, decrypt] of result) { msg += `Stop: ${setting} (plugboard: ${stecker}): ${decrypt}\n`; } diff --git a/tests/operations/tests/Bombe.mjs b/tests/operations/tests/Bombe.mjs index 0a8af93f..fca420d3 100644 --- a/tests/operations/tests/Bombe.mjs +++ b/tests/operations/tests/Bombe.mjs @@ -21,7 +21,7 @@ TestRegister.addTests([ "EKMFLGDQVZNTOWYHXUSPAIBRCJ Date: Fri, 11 Jan 2019 18:24:16 +0000 Subject: [PATCH 038/113] Bombe: wording/docs tweaks --- src/core/lib/Bombe.mjs | 2 +- src/core/operations/Bombe.mjs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index 4ae0ff7f..81581d67 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -693,7 +693,7 @@ export class BombeMachine { * Having set up the Bombe, do the actual attack run. This tries every possible rotor setting * and attempts to logically invalidate them. If it can't, it's added to the list of candidate * solutions. - * @returns {string[][2]} - list of pairs of candidate rotor setting, and calculated stecker pair + * @returns {string[][3]} - list of 3-tuples of candidate rotor setting, plugboard settings, and decryption preview */ run() { let stops = 0; diff --git a/src/core/operations/Bombe.mjs b/src/core/operations/Bombe.mjs index 292017e8..3c29cd8c 100644 --- a/src/core/operations/Bombe.mjs +++ b/src/core/operations/Bombe.mjs @@ -35,19 +35,19 @@ class Bombe extends Operation { defaultIndex: 2 }, { - name: "2nd rotor", + name: "2nd (middle) rotor", type: "editableOption", value: ROTORS, defaultIndex: 1 }, { - name: "3rd rotor", + name: "3rd (left-hand) rotor", type: "editableOption", value: ROTORS, defaultIndex: 0 }, { - name: "4th rotor", + name: "4th (left-most, only some models) rotor", type: "editableOption", value: ROTORS_OPTIONAL, defaultIndex: 10 From 8bba4b297391377eaa0dcd527353742a3f7a0e5e Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 12 Jan 2019 00:20:25 +0000 Subject: [PATCH 039/113] More speedrun stats (literally 10x faster) --- package-lock.json | 6 +++--- package.json | 2 +- src/core/operations/YARARules.mjs | 19 ++++++++++++------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b4d058b..6ba03db3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7756,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.8.tgz", - "integrity": "sha512-ZB+Ya3bEBoanvde47X8RzqpMBHgrPxrTZIJ/UEoatVnOEy2he1IORuotdSkP2o73URRzHGN1jNWDIhTdfbZ3rQ==" + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.9.tgz", + "integrity": "sha512-GTR8GD7q2xCwRzQpoSVh31KPbH145tCKaU3DAnQMpkf4aKqjnVpQlq6gUjyDhvw8RPZ2tamyQad5hg42KsVvRw==" }, "livereload-js": { "version": "2.4.0", diff --git a/package.json b/package.json index 8e21a0a6..a18331d8 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.8", + "libyara-wasm": "0.0.9", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", diff --git a/src/core/operations/YARARules.mjs b/src/core/operations/YARARules.mjs index 094d9f43..58b3576c 100644 --- a/src/core/operations/YARARules.mjs +++ b/src/core/operations/YARARules.mjs @@ -59,16 +59,22 @@ class YARARules extends Operation { * @returns {string} */ run(input, args) { + if (ENVIRONMENT_IS_WORKER()) + self.sendStatusMessage("Instantiating YARA."); const [rules, showStrings, showLengths, showMeta] = args; return new Promise((resolve, reject) => { Yara().then(yara => { + if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Converting data for YARA."); let matchString = ""; - const inpArr = new Uint8Array(input); - const inpVec = new yara.vectorChar(); - for (let i = 0; i < inpArr.length; i++) { - inpVec.push_back(inpArr[i]); - } - const resp = yara.run(inpVec, rules); + + const inpArr = new Uint8Array(input); // Turns out embind knows that JS uint8array <==> C++ std::string + + if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Running YARA matching."); + + const resp = yara.run(inpArr, rules); + + if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Processing data."); + if (resp.compileErrors.size() > 0) { for (let i = 0; i < resp.compileErrors.size(); i++) { const compileError = resp.compileErrors.get(i); @@ -102,7 +108,6 @@ class YARARules extends Operation { } } } - } resolve(matchString); }); From 49f5c94a750c8a11c7a19f29efa3265549df3d8f Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Sat, 12 Jan 2019 01:10:47 +0000 Subject: [PATCH 040/113] Bombe: further optimisation --- src/core/lib/Bombe.mjs | 63 +++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index 81581d67..1e3c5b3e 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -158,27 +158,6 @@ class SharedScrambler { } } - /** - * Get the fully cached result, if present. - * @param {number} pos - Position of the fast rotor - * @param {number} i - Letter - * @returns {number|undefined} - undefined if not cached - */ - fullTransform(pos, i) { - return this.higherCache[pos][i]; - } - - /** - * Add a value to the full result cache. - * @param {number} pos - Position of the fast rotor - * @param {number} i - Letter - * @param {number} val - Transformed letter - */ - addCache(pos, i, val) { - this.higherCache[pos][i] = val; - this.higherCache[pos][val] = i; - } - /** * Map a letter through this (partial) scrambler. * @param {number} i - The letter @@ -209,6 +188,9 @@ class Scrambler { this.changeRotor(rotor); this.end1 = end1; this.end2 = end2; + // For efficiency reasons, we pull the relevant shared cache from the baseScrambler into + // this object - this saves us a few pointer dereferences + this.cache = this.baseScrambler.higherCache[pos]; } /** @@ -233,6 +215,7 @@ class Scrambler { // simplifies caching the state of the majority of the scramblers. The results are the // same, just in a slightly different order. this.rotor.step(); + this.cache = this.baseScrambler.higherCache[this.rotor.pos]; } @@ -243,14 +226,15 @@ class Scrambler { */ transform(i) { let letter = i; - const cached = this.baseScrambler.fullTransform(this.rotor.pos, i); + const cached = this.cache[i]; if (cached !== undefined) { return cached; } letter = this.rotor.transform(letter); letter = this.baseScrambler.transform(letter); letter = this.rotor.revTransform(letter); - this.baseScrambler.addCache(this.rotor.pos, i, letter); + this.cache[i] = letter; + this.cache[letter] = i; return letter; } @@ -513,12 +497,26 @@ export class BombeMachine { // both. const idxPair = 26*j + i; this.wires[idxPair] = true; + if (i === this.testRegister || j === this.testRegister) { + this.energiseCount++; + if (this.energiseCount === 26) { + // no point continuing, bail out + return; + } + } for (let k=0; k Date: Sat, 12 Jan 2019 01:35:24 +0000 Subject: [PATCH 041/113] Bombe: tweaks Twiddle the default rotor sets a bit. Add a time remaining estimate for the multibombe. --- src/core/lib/Enigma.mjs | 8 ++++---- src/core/operations/Bombe.mjs | 4 ++-- src/core/operations/Enigma.mjs | 6 +++--- src/core/operations/MultipleBombe.mjs | 18 ++++++++++++------ 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/core/lib/Enigma.mjs b/src/core/lib/Enigma.mjs index 6b6c4d63..1ed0ea2b 100644 --- a/src/core/lib/Enigma.mjs +++ b/src/core/lib/Enigma.mjs @@ -22,14 +22,14 @@ export const ROTORS = [ {name: "VI", value: "JPGVOUMFYQBENHZRDKASXLICTW 0) { msg += `\nRotors: ${runRotors.join(", ")}\nReflector: ${reflector.pairs}\n`; From eee92aa1aaf2156ba188e450a73efa4704048dde Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Sat, 12 Jan 2019 12:56:21 +0000 Subject: [PATCH 042/113] Bombe: fix some outdated docs --- src/core/lib/Bombe.mjs | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index 1e3c5b3e..03413350 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -19,6 +19,7 @@ import {Rotor, Plugboard, a2i, i2a} from "./Enigma"; class CopyRotor extends Rotor { /** * Return a copy of this Rotor. + * @returns {Object} */ copy() { const clone = { @@ -204,10 +205,9 @@ class Scrambler { } /** - * Step the rotors forward. + * Step the rotor forward. * - * All nodes in the Bombe step in sync. - * @param {number} n - How many rotors to step + * The base SharedScrambler needs to be instructed to step separately. */ step() { // The Bombe steps the slowest rotor on an actual Enigma fastest, for reasons. @@ -284,6 +284,7 @@ export class BombeMachine { * @param {Object} reflector - Reflector object * @param {string} ciphertext - The ciphertext to attack * @param {string} crib - Known plaintext for this ciphertext + * @param {boolean} check - Whether to use the checking machine * @param {function} update - Function to call to send status updates (optional) */ constructor(rotors, reflector, ciphertext, crib, check, update=undefined) { @@ -383,7 +384,7 @@ export class BombeMachine { /** * If we have a way of sending status messages, do so. - * @param {string} msg - Message to send. + * @param {...*} msg - Message to send. */ update(...msg) { if (this.updateFn !== undefined) { @@ -485,7 +486,8 @@ export class BombeMachine { /** * Bombe electrical simulation. Energise a wire. For all connected wires (both via the diagonal * board and via the scramblers), energise them too, recursively. - * @param {number[2]} i - Bombe state wire + * @param {number} i - Bombe wire bundle + * @param {number} j - Bombe stecker hypothesis wire within bundle */ energise(i, j) { const idx = 26*i + j; @@ -535,33 +537,13 @@ export class BombeMachine { } } - /** - * Single-pair steckering. Used for trial decryption rather than building a whole plugboard - * object for one pair - * @param {number[2]} stecker - Known stecker pair. - * @param {number} x - Letter to transform. - * @result number - */ - singleStecker(stecker, x) { - if (stecker === undefined) { - return x; - } - if (x === stecker[0]) { - return stecker[1]; - } - if (x === stecker[1]) { - return stecker[0]; - } - return x; - } - /** * Trial decryption at the current setting. * Used after we get a stop. * This applies the detected stecker pair if we have one. It does not handle the other * steckering or stepping (which is why we limit it to 26 characters, since it's guaranteed to * be wrong after that anyway). - * @param {number[2]} stecker - Known stecker pair. + * @param {string} stecker - Known stecker spec string. * @returns {string} */ tryDecrypt(stecker) { From 68fbbb64dba87ef1d7edf2138870e8b98aa3e9d8 Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 11:49:57 +0000 Subject: [PATCH 043/113] Add new Convert co-ordinate format module. Also added autodetect of co-ordinate format / delimiter --- src/core/config/Categories.json | 1 + src/core/lib/ConvertCoordinates.mjs | 95 ++++++++ .../operations/ConvertCoordinateFormat.mjs | 227 ++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 src/core/operations/ConvertCoordinateFormat.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index e9fe3399..13ea76c9 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -210,6 +210,7 @@ "Convert mass", "Convert speed", "Convert data units", + "Convert co-ordinate format", "Parse UNIX file permissions", "Swap endianness", "Parse colour code", diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index 45ab0690..a9b3d551 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -208,3 +208,98 @@ function convDDToDDM (decDegrees, precision) { converted.string = degrees + "° " + decMinutes + "'"; return converted; } + +/** + * + * @param {string} input - The input data whose format we need to detect + * @param {string} delim - The delimiter separating the data in input + * @returns {string} The input format + */ +export function findFormat (input, delim) { + input = input.trim(); + let testData; + if (delim.includes("Direction")) { + const split = input.split(/[NnEeSsWw]/); + if (split.length > 0) { + if (split[0] === "") { + // Direction Preceding + testData = split[1]; + } else { + // Direction Following + testData = split[0]; + } + } + } else if (delim !== "") { + const split = input.split(delim); + if (!input.includes(delim)) { + testData = input; + } + if (split.length > 0) { + if (split[0] !== "") { + testData = split[0]; + } else if (split.length > 1) { + testData = split[1]; + } + } + } + + // Test MGRS and Geohash + if (input.split(" ").length === 1) { + const mgrsPattern = new RegExp(/^[0-9]{2}[C-HJ-NP-X]{2}[A-Z]+/); + const geohashPattern = new RegExp(/^[0123456789bcdefghjkmnpqrstuvwxyz]+$/); + if (mgrsPattern.test(input.toUpperCase())) { + return "Military Grid Reference System"; + } else if (geohashPattern.test(input.toLowerCase())) { + return "Geohash"; + } + } + + // Test DMS/DDM/DD formats + if (testData !== undefined) { + const split = splitInput(testData); + if (split.length === 3) { + // DMS + return "Degrees Minutes Seconds"; + } else if (split.length === 2) { + // DDM + return "Degrees Decimal Minutes"; + } else if (split.length === 1) { + return "Decimal Degrees"; + } + } + return null; +} + +/** + * Automatically find the delimeter type from the given input + * @param {string} input + * @returns {string} Delimiter type + */ +export function findDelim (input) { + input = input.trim(); + const delims = [",", ";", ":"]; + // Direction + const testDir = input.match(/[NnEeSsWw]/g); + if (testDir !== null && testDir.length > 0 && testDir.length < 3) { + // Possible direction + const splitInput = input.split(/[NnEeSsWw]/); + if (splitInput.length <= 3 && splitInput.length > 0) { + // One of the splits should be an empty string + if (splitInput[0] === "") { + return "Direction Preceding"; + } else if (splitInput[splitInput.length - 1] === "") { + return "Direction Following"; + } + } + } + for (let i = 0; i < delims.length; i++) { + const delim = delims[i]; + if (input.includes(delim)) { + const splitInput = input.split(delim); + if (splitInput.length <= 3 && splitInput.length > 0) { + return delim; + } + } + } + return null; +} diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs new file mode 100644 index 00000000..5f336630 --- /dev/null +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -0,0 +1,227 @@ +/** + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import {FORMATS, convertCoordinates, convertSingleCoordinate, findDelim, findFormat} from "../lib/ConvertCoordinates"; +import Utils from "../Utils"; + +/** + * Convert co-ordinate format operation + */ +class ConvertCoordinateFormat extends Operation { + + /** + * ConvertCoordinateFormat constructor + */ + constructor() { + super(); + + this.name = "Convert co-ordinate format"; + this.module = "Hashing"; + this.description = "Convert geographical coordinates between different formats.

Currently supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
"; + this.infoURL = "https://wikipedia.org/wiki/Geographic_coordinate_conversion"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Input Format", + "type": "option", + "value": ["Auto"].concat(FORMATS) + }, + { + "name": "Input Delimiter", + "type": "option", + "value": [ + "Auto", + "Direction Preceding", // Need better names + "Direction Following", + "\\n", + "Comma", + "Semi-colon", + "Colon" + ] + }, + { + "name": "Output Format", + "type": "option", + "value": FORMATS + }, + { + "name": "Output Delimiter", + "type": "option", + "value": [ + "Space", + "Direction Preceding", // Need better names + "Direction Following", + "\\n", + "Comma", + "Semi-colon", + "Colon" + ] + }, + { + "name": "Precision", + "type": "number", + "value": 3 + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const outFormat = args[2], + outDelim = args[3], + precision = args[4]; + let inFormat = args[0], + inDelim = args[1], + inLat, + inLong, + outLat, + outLong, + latDir = "", + longDir = "", + outSeparator = " "; + + // Autodetect input delimiter + if (inDelim === "Auto") { + inDelim = findDelim(input); + log.error("DATA: " + input + " DELIM: " + inDelim); + if (inDelim === null) { + inDelim = ""; + // throw new OperationError("Could not automatically detect the input delimiter."); + } + } else if (!inDelim.includes("Direction")) { + // Get the actual delimiter from the regex + inDelim = String(Utils.regexRep(inDelim)).slice(1, 2); + } + if (inFormat === "Auto") { + inFormat = findFormat(input, inDelim); + log.error("DATA: " + input + " FORMAT: " + inFormat); + if (inFormat === null) { + throw new OperationError("Could not automatically detect the input"); + } + } + + if (inDelim === "" && (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System")) { + throw new OperationError("Could not automatically detect the input delimiter."); + } + + // Prepare input data + if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { + // Geohash only has one value, so just use the input + inLat = input; + } else if (inDelim === "Direction Preceding") { + // Split on the compass directions + const splitInput = input.split(/[NnEeSsWw]/); + const dir = input.match(/[NnEeSsWw]/g); + if (splitInput.length > 1) { + inLat = splitInput[1]; + if (dir !== null) { + latDir = dir[0]; + } + if (splitInput.length > 2) { + inLong = splitInput[2]; + if (dir !== null && dir.length > 1) { + longDir = dir[1]; + } + } + } + } else if (inDelim === "Direction Following") { + // Split on the compass directions + const splitInput = input.split(/[NnEeSsWw]/); + if (splitInput.length >= 1) { + inLat = splitInput[0]; + if (splitInput.length >= 2) { + inLong = splitInput[1]; + } + } + } else { + // Split on the delimiter + const splitInput = input.split(inDelim); + log.error(splitInput); + if (splitInput.length > 0) { + inLat = splitInput[0]; + if (splitInput.length >= 2) { + inLong = splitInput[1]; + } + } + } + + if (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System" && outDelim.includes("Direction")) { + // Match on compass directions, and store the first 2 matches for the output + const dir = input.match(/[NnEeSsWw]/g); + if (dir !== null) { + latDir = dir[0]; + if (dir.length > 1) { + longDir = dir[1]; + } + } + } else if (outDelim === "\\n") { + outSeparator = "\n"; + } else if (outDelim === "Space") { + outSeparator = " "; + } else if (!outDelim.includes("Direction")) { + // Cut out the regex syntax (/) from the delimiter + outSeparator = String(Utils.regexRep(outDelim)).slice(1, 2); + } + + // Convert the co-ordinates + if (inLat !== undefined) { + if (inLong === undefined) { + if (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System") { + if (outFormat === "Geohash" || outFormat === "Military Grid Reference System"){ + throw new OperationError(`${outFormat} needs both a latitude and a longitude to be calculated`); + } + } + if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { + // Geohash conversion is in convertCoordinates despite needing + // only one input as it needs to output two values + [outLat, outLong] = convertCoordinates(inLat, inLat, inFormat, outFormat, precision); + } else { + outLat = convertSingleCoordinate(inLat, inFormat, outFormat, precision); + } + } else { + [outLat, outLong] = convertCoordinates(inLat, inLong, inFormat, outFormat, precision); + } + } else { + throw new OperationError("No co-ordinates were detected in the input."); + } + + // Output conversion results if successful + if (outLat !== undefined) { + let output = ""; + if (outDelim === "Direction Preceding" && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + output += latDir += " "; + } + output += outLat; + if (outDelim === "Direction Following" && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + output += " " + latDir; + } + output += outSeparator; + + if (outLong !== undefined && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + if (outDelim === "Direction Preceding") { + output += longDir + " "; + } + output += outLong; + if (outDelim === "Direction Following") { + output += " " + longDir; + } + output += outSeparator; + } + return output; + } else { + throw new OperationError("Co-ordinate conversion failed."); + } + } +} + +export default ConvertCoordinateFormat; From 8d1f668fc550f5795a02f5f157411ffe45da89e0 Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 11:56:27 +0000 Subject: [PATCH 044/113] Remove old Geohash modules --- src/core/config/Categories.json | 4 +-- src/core/operations/FromGeohash.mjs | 44 ------------------------ src/core/operations/ToGeohash.mjs | 53 ----------------------------- 3 files changed, 1 insertion(+), 100 deletions(-) delete mode 100644 src/core/operations/FromGeohash.mjs delete mode 100644 src/core/operations/ToGeohash.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 13ea76c9..2224050c 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -302,9 +302,7 @@ "Adler-32 Checksum", "CRC-16 Checksum", "CRC-32 Checksum", - "TCP/IP Checksum", - "To Geohash", - "From Geohash" + "TCP/IP Checksum" ] }, { diff --git a/src/core/operations/FromGeohash.mjs b/src/core/operations/FromGeohash.mjs deleted file mode 100644 index b70273da..00000000 --- a/src/core/operations/FromGeohash.mjs +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @author gchq77703 [] - * @copyright Crown Copyright 2018 - * @license Apache-2.0 - */ - -import Operation from "../Operation"; -import geohash from "ngeohash"; - -/** - * From Geohash operation - */ -class FromGeohash extends Operation { - - /** - * FromGeohash constructor - */ - constructor() { - super(); - - this.name = "From Geohash"; - this.module = "Hashing"; - this.description = "Converts Geohash strings into Lat/Long coordinates. For example, ww8p1r4t8 becomes 37.8324,112.5584."; - this.infoURL = "https://wikipedia.org/wiki/Geohash"; - this.inputType = "string"; - this.outputType = "string"; - this.args = []; - } - - /** - * @param {string} input - * @param {Object[]} args - * @returns {string} - */ - run(input, args) { - return input.split("\n").map(line => { - const coords = geohash.decode(line); - return [coords.latitude, coords.longitude].join(","); - }).join("\n"); - } - -} - -export default FromGeohash; diff --git a/src/core/operations/ToGeohash.mjs b/src/core/operations/ToGeohash.mjs deleted file mode 100644 index 0e7f53ac..00000000 --- a/src/core/operations/ToGeohash.mjs +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @author gchq77703 [] - * @copyright Crown Copyright 2018 - * @license Apache-2.0 - */ - -import Operation from "../Operation"; -import geohash from "ngeohash"; - -/** - * To Geohash operation - */ -class ToGeohash extends Operation { - - /** - * ToGeohash constructor - */ - constructor() { - super(); - - this.name = "To Geohash"; - this.module = "Hashing"; - this.description = "Converts Lat/Long coordinates into a Geohash string. For example, 37.8324,112.5584 becomes ww8p1r4t8."; - this.infoURL = "https://wikipedia.org/wiki/Geohash"; - this.inputType = "string"; - this.outputType = "string"; - this.args = [ - { - name: "Precision", - type: "number", - value: 9 - } - ]; - } - - /** - * @param {string} input - * @param {Object[]} args - * @returns {string} - */ - run(input, args) { - const [precision] = args; - - return input.split("\n").map(line => { - line = line.replace(/ /g, ""); - if (line === "") return ""; - return geohash.encode(...line.split(",").map(num => parseFloat(num)), precision); - }).join("\n"); - } - -} - -export default ToGeohash; From 8b77ad77480b7cf1450e32d2c63dc17f0a6f46ee Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 12:49:28 +0000 Subject: [PATCH 045/113] Stop delimiters breaking MGRS conversion --- src/core/operations/ConvertCoordinateFormat.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index 5f336630..d24b2135 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -117,7 +117,8 @@ class ConvertCoordinateFormat extends Operation { // Prepare input data if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { // Geohash only has one value, so just use the input - inLat = input; + // Replace anything that isn't a valid character in Geohash / MGRS + inLat = input.replace(/[^A-Za-z0-9]/, ""); } else if (inDelim === "Direction Preceding") { // Split on the compass directions const splitInput = input.split(/[NnEeSsWw]/); From 1a88a0164cf8d27d260afc9e8b8b72d5db39fe3a Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 13:00:14 +0000 Subject: [PATCH 046/113] Fix delimiter breaking Geohash detection --- src/core/lib/ConvertCoordinates.mjs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index a9b3d551..9b9d8881 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -45,6 +45,7 @@ export function convertCoordinates (inLat, inLong, inFormat, outFormat, precisio convLong = convertSingleCoordinate(inLong, inFormat, "Decimal Degrees", 15).split("°"); } + // Convert Geohash and MGRS here, as they need both the lat and long values if (outFormat === "Geohash") { convLat = geohash.encode(parseFloat(convLat), parseFloat(convLong), precision); } else if (outFormat === "Military Grid Reference System") { @@ -244,12 +245,14 @@ export function findFormat (input, delim) { } // Test MGRS and Geohash - if (input.split(" ").length === 1) { + if (input.split(" ").length <= 1) { + const filteredInput = input.replace(/[^A-Za-z0-9]/, "").toUpperCase(); const mgrsPattern = new RegExp(/^[0-9]{2}[C-HJ-NP-X]{2}[A-Z]+/); - const geohashPattern = new RegExp(/^[0123456789bcdefghjkmnpqrstuvwxyz]+$/); - if (mgrsPattern.test(input.toUpperCase())) { + const geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/); + log.error(filteredInput); + if (mgrsPattern.test(filteredInput)) { return "Military Grid Reference System"; - } else if (geohashPattern.test(input.toLowerCase())) { + } else if (geohashPattern.test(filteredInput)) { return "Geohash"; } } @@ -292,6 +295,8 @@ export function findDelim (input) { } } } + + // Loop through the standard delimiters, and try to find them in the input for (let i = 0; i < delims.length; i++) { const delim = delims[i]; if (input.includes(delim)) { From b3ac8d0835e90c9301cfd86f58d2f619a1d246c6 Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 13:49:49 +0000 Subject: [PATCH 047/113] Removed some debug logging --- src/core/lib/ConvertCoordinates.mjs | 1 - src/core/operations/ConvertCoordinateFormat.mjs | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index 9b9d8881..0078cfb7 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -249,7 +249,6 @@ export function findFormat (input, delim) { const filteredInput = input.replace(/[^A-Za-z0-9]/, "").toUpperCase(); const mgrsPattern = new RegExp(/^[0-9]{2}[C-HJ-NP-X]{2}[A-Z]+/); const geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/); - log.error(filteredInput); if (mgrsPattern.test(filteredInput)) { return "Military Grid Reference System"; } else if (geohashPattern.test(filteredInput)) { diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index d24b2135..7528fccc 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -22,7 +22,7 @@ class ConvertCoordinateFormat extends Operation { this.name = "Convert co-ordinate format"; this.module = "Hashing"; - this.description = "Convert geographical coordinates between different formats.

Currently supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
"; + this.description = "Convert geographical coordinates between different formats.

Supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
"; this.infoURL = "https://wikipedia.org/wiki/Geographic_coordinate_conversion"; this.inputType = "string"; this.outputType = "string"; @@ -93,10 +93,8 @@ class ConvertCoordinateFormat extends Operation { // Autodetect input delimiter if (inDelim === "Auto") { inDelim = findDelim(input); - log.error("DATA: " + input + " DELIM: " + inDelim); if (inDelim === null) { inDelim = ""; - // throw new OperationError("Could not automatically detect the input delimiter."); } } else if (!inDelim.includes("Direction")) { // Get the actual delimiter from the regex @@ -104,9 +102,8 @@ class ConvertCoordinateFormat extends Operation { } if (inFormat === "Auto") { inFormat = findFormat(input, inDelim); - log.error("DATA: " + input + " FORMAT: " + inFormat); if (inFormat === null) { - throw new OperationError("Could not automatically detect the input"); + throw new OperationError("Could not automatically detect the input format."); } } @@ -147,7 +144,6 @@ class ConvertCoordinateFormat extends Operation { } else { // Split on the delimiter const splitInput = input.split(inDelim); - log.error(splitInput); if (splitInput.length > 0) { inLat = splitInput[0]; if (splitInput.length >= 2) { From 04b0b8c72344aa136dd2cd879fc99f2ffb678ec5 Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 14:58:41 +0000 Subject: [PATCH 048/113] Tidy up code --- src/core/lib/ConvertCoordinates.mjs | 21 +++++++++---------- .../operations/ConvertCoordinateFormat.mjs | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index 0078cfb7..1c6ae7e2 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -259,14 +259,13 @@ export function findFormat (input, delim) { // Test DMS/DDM/DD formats if (testData !== undefined) { const split = splitInput(testData); - if (split.length === 3) { - // DMS - return "Degrees Minutes Seconds"; - } else if (split.length === 2) { - // DDM - return "Degrees Decimal Minutes"; - } else if (split.length === 1) { - return "Decimal Degrees"; + switch (split.length){ + case 3: + return "Degrees Minutes Seconds"; + case 2: + return "Degrees Decimal Minutes"; + case 1: + return "Decimal Degrees"; } } return null; @@ -280,13 +279,12 @@ export function findFormat (input, delim) { export function findDelim (input) { input = input.trim(); const delims = [",", ";", ":"]; - // Direction const testDir = input.match(/[NnEeSsWw]/g); if (testDir !== null && testDir.length > 0 && testDir.length < 3) { - // Possible direction + // Possibly contains a direction const splitInput = input.split(/[NnEeSsWw]/); if (splitInput.length <= 3 && splitInput.length > 0) { - // One of the splits should be an empty string + // If there's 3 splits (one should be empty), then assume we have directions if (splitInput[0] === "") { return "Direction Preceding"; } else if (splitInput[splitInput.length - 1] === "") { @@ -301,6 +299,7 @@ export function findDelim (input) { if (input.includes(delim)) { const splitInput = input.split(delim); if (splitInput.length <= 3 && splitInput.length > 0) { + // Don't want to try and convert more than 2 co-ordinates return delim; } } diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index 7528fccc..b9a9766c 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -37,7 +37,7 @@ class ConvertCoordinateFormat extends Operation { "type": "option", "value": [ "Auto", - "Direction Preceding", // Need better names + "Direction Preceding", "Direction Following", "\\n", "Comma", @@ -55,7 +55,7 @@ class ConvertCoordinateFormat extends Operation { "type": "option", "value": [ "Space", - "Direction Preceding", // Need better names + "Direction Preceding", "Direction Following", "\\n", "Comma", From ee360521bb757c9cae9010b13db0932a46aa1e02 Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 14 Jan 2019 16:41:06 +0000 Subject: [PATCH 049/113] Remove MGRS npm module --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 92fda38b..414e4d50 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,6 @@ "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", - "mgrs": "^1.0.0", "moment": "^2.22.2", "moment-timezone": "^0.5.23", "ngeohash": "^0.6.0", From ffc4b0a0a8ef1576bf0b292e184aed5dace1994c Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Mon, 14 Jan 2019 17:15:54 +0000 Subject: [PATCH 050/113] Bombe: lol --- src/core/operations/Bombe.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/Bombe.mjs b/src/core/operations/Bombe.mjs index 9014fd3a..34a94e53 100644 --- a/src/core/operations/Bombe.mjs +++ b/src/core/operations/Bombe.mjs @@ -50,7 +50,7 @@ class Bombe extends Operation { name: "4th (left-most, only some models) rotor", type: "editableOption", value: ROTORS_FOURTH, - defaultIndex: 10 + defaultIndex: 0 }, { name: "Reflector", From 4d8127a7d98b18309d5d4bc44892627f647adb23 Mon Sep 17 00:00:00 2001 From: Callum Fraser Date: Mon, 14 Jan 2019 22:24:32 +0000 Subject: [PATCH 051/113] Modified description of ToBase64 operation Addresses #472 --- src/core/operations/ToBase64.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/ToBase64.mjs b/src/core/operations/ToBase64.mjs index f9aa569b..cffc3140 100644 --- a/src/core/operations/ToBase64.mjs +++ b/src/core/operations/ToBase64.mjs @@ -20,7 +20,7 @@ class ToBase64 extends Operation { this.name = "To Base64"; this.module = "Default"; - this.description = "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.

This operation decodes data from an ASCII Base64 string back into its raw format.

e.g. aGVsbG8= becomes hello"; + this.description = "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.

This operation encodes raw data into an ASCII Base64 string.

e.g. hello becomes aGVsbG8="; this.infoURL = "https://wikipedia.org/wiki/Base64"; this.inputType = "ArrayBuffer"; this.outputType = "string"; From ad4451a757f0650ee492c41a89014b36ead8993b Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 15 Jan 2019 10:13:11 +0000 Subject: [PATCH 052/113] Rewrite MGRS to use new Geodesy module. Added Ordnance Survey grid reference support --- package.json | 1 + src/core/lib/ConvertCoordinates.mjs | 43 ++++++++++++++++--- .../operations/ConvertCoordinateFormat.mjs | 25 +++++------ 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 414e4d50..4e38e4fa 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "esprima": "^4.0.1", "exif-parser": "^0.1.12", "file-saver": "^2.0.0-rc.4", + "geodesy": "^1.1.3", "highlight.js": "^9.13.1", "jquery": "^3.3.1", "js-crc": "^0.2.0", diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index 1c6ae7e2..06e40f02 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -7,7 +7,7 @@ */ import geohash from "ngeohash"; -import mgrs from "mgrs"; +import geodesy from "geodesy"; /** * Co-ordinate formats @@ -17,7 +17,19 @@ export const FORMATS = [ "Degrees Decimal Minutes", "Decimal Degrees", "Geohash", - "Military Grid Reference System" + "Military Grid Reference System", + "Ordnance Survey National Grid" +]; + +/** + * Formats that are made up of one string + * These formats skip bits like filtering delimiters and + * are outputted differently (only one output) + */ +export const STRING_FORMATS = [ + "Geohash", + "Military Grid Reference System", + "Ordnance Survey National Grid" ]; /** @@ -37,9 +49,22 @@ export function convertCoordinates (inLat, inLong, inFormat, outFormat, precisio convLat = hash.latitude.toString(); convLong = hash.longitude.toString(); } else if (inFormat === "Military Grid Reference System") { - const result = mgrs.toPoint(inLat.replace(" ", "")); - convLat = result[1]; - convLong = result[0]; + const utm = geodesy.Mgrs.parse(inLat).toUtm(); + const result = utm.toLatLonE().toString("d", 4).replace(/[^0-9.,]/g, ""); + const splitResult = result.split(","); + if (splitResult.length === 2) { + convLat = splitResult[0]; + convLong = splitResult[1]; + } + } else if (inFormat === "Ordnance Survey National Grid") { + const osng = geodesy.OsGridRef.parse(inLat); + const latlon = geodesy.OsGridRef.osGridToLatLon(osng, geodesy.LatLonEllipsoidal.datum.WGS84); + const result = latlon.toString("d", 4).replace(/[^0-9.,]/g, ""); + const splitResult = result.split(","); + if (splitResult.length === 2) { + convLat = splitResult[0]; + convLong = splitResult[1]; + } } else { convLat = convertSingleCoordinate(inLat, inFormat, "Decimal Degrees", 15).split("°"); convLong = convertSingleCoordinate(inLong, inFormat, "Decimal Degrees", 15).split("°"); @@ -49,7 +74,13 @@ export function convertCoordinates (inLat, inLong, inFormat, outFormat, precisio if (outFormat === "Geohash") { convLat = geohash.encode(parseFloat(convLat), parseFloat(convLong), precision); } else if (outFormat === "Military Grid Reference System") { - convLat = mgrs.forward([parseFloat(convLong), parseFloat(convLat)], precision); + const utm = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)).toUtm(); + const mgrs = utm.toMgrs(); + convLat = mgrs.toString(); + } else if (outFormat === "Ordnance Survey National Grid") { + const latlon = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)); + const osng = geodesy.OsGridRef.latLonToOsGrid(latlon); + convLat = osng.toString(); } else { convLat = convertSingleCoordinate(convLat.toString(), "Decimal Degrees", outFormat, precision); convLong = convertSingleCoordinate(convLong.toString(), "Decimal Degrees", outFormat, precision); diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index b9a9766c..afc95982 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -6,7 +6,7 @@ import Operation from "../Operation"; import OperationError from "../errors/OperationError"; -import {FORMATS, convertCoordinates, convertSingleCoordinate, findDelim, findFormat} from "../lib/ConvertCoordinates"; +import {FORMATS, STRING_FORMATS, convertCoordinates, convertSingleCoordinate, findDelim, findFormat} from "../lib/ConvertCoordinates"; import Utils from "../Utils"; /** @@ -22,7 +22,7 @@ class ConvertCoordinateFormat extends Operation { this.name = "Convert co-ordinate format"; this.module = "Hashing"; - this.description = "Convert geographical coordinates between different formats.

Supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
"; + this.description = "Convert geographical coordinates between different formats.

Supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
  • Ordnance Survey National Grid (OSNG)
"; this.infoURL = "https://wikipedia.org/wiki/Geographic_coordinate_conversion"; this.inputType = "string"; this.outputType = "string"; @@ -107,14 +107,14 @@ class ConvertCoordinateFormat extends Operation { } } - if (inDelim === "" && (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System")) { + if (inDelim === "" && (!STRING_FORMATS.includes(inFormat))) { throw new OperationError("Could not automatically detect the input delimiter."); } // Prepare input data - if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { + if (STRING_FORMATS.includes(inFormat)) { // Geohash only has one value, so just use the input - // Replace anything that isn't a valid character in Geohash / MGRS + // Replace anything that isn't a valid character in Geohash / MGRS / OSNG inLat = input.replace(/[^A-Za-z0-9]/, ""); } else if (inDelim === "Direction Preceding") { // Split on the compass directions @@ -152,7 +152,7 @@ class ConvertCoordinateFormat extends Operation { } } - if (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System" && outDelim.includes("Direction")) { + if (!STRING_FORMATS.includes(inFormat) && outDelim.includes("Direction")) { // Match on compass directions, and store the first 2 matches for the output const dir = input.match(/[NnEeSsWw]/g); if (dir !== null) { @@ -173,14 +173,15 @@ class ConvertCoordinateFormat extends Operation { // Convert the co-ordinates if (inLat !== undefined) { if (inLong === undefined) { - if (inFormat !== "Geohash" && inFormat !== "Military Grid Reference System") { - if (outFormat === "Geohash" || outFormat === "Military Grid Reference System"){ + if (!STRING_FORMATS.includes(inFormat)) { + if (STRING_FORMATS.includes(outFormat)){ throw new OperationError(`${outFormat} needs both a latitude and a longitude to be calculated`); } } - if (inFormat === "Geohash" || inFormat === "Military Grid Reference System") { + if (STRING_FORMATS.includes(inFormat)) { // Geohash conversion is in convertCoordinates despite needing // only one input as it needs to output two values + inLat = inLat.replace(/[^A-Za-z0-9]/g, ""); [outLat, outLong] = convertCoordinates(inLat, inLat, inFormat, outFormat, precision); } else { outLat = convertSingleCoordinate(inLat, inFormat, outFormat, precision); @@ -195,16 +196,16 @@ class ConvertCoordinateFormat extends Operation { // Output conversion results if successful if (outLat !== undefined) { let output = ""; - if (outDelim === "Direction Preceding" && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + if (outDelim === "Direction Preceding" && !STRING_FORMATS.includes(outFormat)) { output += latDir += " "; } output += outLat; - if (outDelim === "Direction Following" && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + if (outDelim === "Direction Following" && !STRING_FORMATS.includes(outFormat)) { output += " " + latDir; } output += outSeparator; - if (outLong !== undefined && outFormat !== "Geohash" && outFormat !== "Military Grid Reference System") { + if (outLong !== undefined && !STRING_FORMATS.includes(outFormat)) { if (outDelim === "Direction Preceding") { output += longDir + " "; } From 5e68959c03cc460edd93d7dbe9444d8250958711 Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 15 Jan 2019 10:25:49 +0000 Subject: [PATCH 053/113] Catch when OS grid references aren't calculated --- src/core/lib/ConvertCoordinates.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index 06e40f02..b6f9e9bf 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -81,6 +81,9 @@ export function convertCoordinates (inLat, inLong, inFormat, outFormat, precisio const latlon = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)); const osng = geodesy.OsGridRef.latLonToOsGrid(latlon); convLat = osng.toString(); + if (convLat === "") { + throw "Couldn't convert co-ordinates to Ordnance Survey National Grid. Are they out of range?"; + } } else { convLat = convertSingleCoordinate(convLat.toString(), "Decimal Degrees", outFormat, precision); convLong = convertSingleCoordinate(convLong.toString(), "Decimal Degrees", outFormat, precision); From d00b0f4c0e9e06af134de7cf680425b2be3668d9 Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 15 Jan 2019 15:55:49 +0000 Subject: [PATCH 054/113] Basically rewrote the whole thing using the new geodesy module --- src/core/lib/ConvertCoordinates.mjs | 464 +++++++++++++----- .../operations/ConvertCoordinateFormat.mjs | 162 +----- 2 files changed, 353 insertions(+), 273 deletions(-) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index b6f9e9bf..cec2439c 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -18,148 +18,240 @@ export const FORMATS = [ "Decimal Degrees", "Geohash", "Military Grid Reference System", - "Ordnance Survey National Grid" + "Ordnance Survey National Grid", + "Universal Transverse Mercator" ]; /** - * Formats that are made up of one string - * These formats skip bits like filtering delimiters and - * are outputted differently (only one output) + * Formats that should be passed to Geodesy module as-is + * Spaces are still removed */ -export const STRING_FORMATS = [ +const NO_CHANGE = [ "Geohash", "Military Grid Reference System", - "Ordnance Survey National Grid" + "Ordnance Survey National Grid", + "Universal Transverse Mercator", ]; /** * Convert a given latitude and longitude into a different format. - * @param {string} inLat - Input latitude to be converted. Use this for supplying single values for conversion (e.g. geohash) - * @param {string} inLong - Input longitude to be converted + * @param {string} input - Input string to be converted * @param {string} inFormat - Format of the input coordinates + * @param {string} inDelim - The delimiter splitting the lat/long of the input * @param {string} outFormat - Format to convert to + * @param {string} outDelim - The delimiter to separate the output with + * @param {string} includeDir - Whether or not to include the compass direction in the output * @param {number} precision - Precision of the result - * @returns {string[]} Array containing the converted latitude and longitude + * @returns {string} A formatted string of the converted co-ordinates */ -export function convertCoordinates (inLat, inLong, inFormat, outFormat, precision) { - let convLat = inLat; - let convLong = inLong; +export function convertCoordinates (input, inFormat, inDelim, outFormat, outDelim, includeDir, precision) { + let isPair = false, + split, + latlon, + conv, + inLatDir, + inLongDir; + + if (inDelim === "Auto") { + inDelim = findDelim(input); + } else { + inDelim = realDelim(inDelim); + } + if (inFormat === "Auto") { + inFormat = findFormat(input, inDelim); + if (inFormat === null) { + throw "Unable to detect the input format automatically."; + } + } + if (inDelim === null && !inFormat.includes("Direction")) { + throw "Unable to detect the input delimiter automatically."; + } + outDelim = realDelim(outDelim); + + if (!NO_CHANGE.includes(inFormat)) { + split = input.split(inDelim); + if (split.length > 1) { + isPair = true; + } + } else { + input = input.replace(inDelim, ""); + isPair = true; + } + + if (inFormat.includes("Degrees")) { + [inLatDir, inLongDir] = findDirs(input, inDelim); + } + if (inFormat === "Geohash") { - const hash = geohash.decode(inLat); - convLat = hash.latitude.toString(); - convLong = hash.longitude.toString(); + const hash = geohash.decode(input.replace(/[^A-Za-z0-9]/g, "")); + latlon = new geodesy.LatLonEllipsoidal(hash.latitude, hash.longitude); } else if (inFormat === "Military Grid Reference System") { - const utm = geodesy.Mgrs.parse(inLat).toUtm(); - const result = utm.toLatLonE().toString("d", 4).replace(/[^0-9.,]/g, ""); - const splitResult = result.split(","); - if (splitResult.length === 2) { - convLat = splitResult[0]; - convLong = splitResult[1]; - } + const utm = geodesy.Mgrs.parse(input.replace(/[^A-Za-z0-9]/g, "")).toUtm(); + latlon = utm.toLatLonE(); } else if (inFormat === "Ordnance Survey National Grid") { - const osng = geodesy.OsGridRef.parse(inLat); - const latlon = geodesy.OsGridRef.osGridToLatLon(osng, geodesy.LatLonEllipsoidal.datum.WGS84); - const result = latlon.toString("d", 4).replace(/[^0-9.,]/g, ""); - const splitResult = result.split(","); - if (splitResult.length === 2) { - convLat = splitResult[0]; - convLong = splitResult[1]; + const osng = geodesy.OsGridRef.parse(input.replace(/[^A-Za-z0-9]/g, "")); + latlon = geodesy.OsGridRef.osGridToLatLon(osng); + } else if (inFormat === "Universal Transverse Mercator") { + if (/^[\d]{2}[A-Za-z]/.test(input)) { + input = input.slice(0, 2) + " " + input.slice(2); + } + const utm = geodesy.Utm.parse(input); + latlon = utm.toLatLonE(); + } else if (inFormat === "Degrees Minutes Seconds") { + if (isPair) { + split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim(); + split[1] = split[1].replace(/[NnEeSsWw]/g, "").trim(); + const splitLat = split[0].split(/[°′″'"\s]/g), + splitLong = split[1].split(/[°′″'"\s]/g); + + if (splitLat.length >= 3 && splitLong.length >= 3) { + const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2]), 10); + const long = convDMSToDD(parseFloat(splitLong[0]), parseFloat(splitLong[1]), parseFloat(splitLong[2]), 10); + latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees); + } + } else { + // Create a new latlon object anyway, but we can ignore the lon value + split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim(); + const splitLat = split[0].split(/[°′″'"\s]/g); + if (splitLat.length >= 3) { + const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2])); + latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees); + } + } + } else if (inFormat === "Degrees Decimal Minutes") { + if (isPair) { + const splitLat = splitInput(split[0]); + const splitLong = splitInput(split[1]); + if (splitLat.length !== 2 || splitLong.length !== 2) { + throw "Invalid co-ordinate format for Degrees Decimal Minutes."; + } + const lat = convDDMToDD(splitLat[0], splitLat[1], 10); + const long = convDDMToDD(splitLong[0], splitLong[1], 10); + latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees); + } else { + const splitLat = splitInput(input); + if (splitLat.length !== 2) { + throw "Invalid co-ordinate format for Degrees Decimal Minutes."; + } + const lat = convDDMToDD(splitLat[0], splitLat[1], 10); + latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees); + } + } else if (inFormat === "Decimal Degrees") { + if (isPair) { + const splitLat = splitInput(split[0]); + const splitLong = splitInput(split[1]); + if (splitLat.length !== 1 || splitLong.length !== 1) { + throw "Invalid co-ordinate format for Decimal Degrees."; + } + latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLong[0]); + } else { + const splitLat = splitInput(split[0]); + if (splitLat.length !== 1) { + throw "Invalid co-ordinate format for Decimal Degrees."; + } + latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLat[0]); } } else { - convLat = convertSingleCoordinate(inLat, inFormat, "Decimal Degrees", 15).split("°"); - convLong = convertSingleCoordinate(inLong, inFormat, "Decimal Degrees", 15).split("°"); + throw "Invalid input co-ordinate format selected."; } - // Convert Geohash and MGRS here, as they need both the lat and long values - if (outFormat === "Geohash") { - convLat = geohash.encode(parseFloat(convLat), parseFloat(convLong), precision); + // Everything is now a geodesy latlon object + if (outFormat === "Decimal Degrees") { + conv = latlon.toString("d", precision); + if (!isPair) { + conv = conv.split(",")[0]; + } + } else if (outFormat === "Degrees Decimal Minutes") { + conv = latlon.toString("dm", precision); + if (!isPair) { + conv = conv.split(",")[0]; + } + } else if (outFormat === "Degrees Minutes Seconds") { + conv = latlon.toString("dms", precision); + if (!isPair) { + conv = conv.split(",")[0]; + } + } else if (outFormat === "Geohash") { + conv = geohash.encode(latlon.lat.toString(), latlon.lon.toString(), precision); } else if (outFormat === "Military Grid Reference System") { - const utm = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)).toUtm(); + const utm = latlon.toUtm(); const mgrs = utm.toMgrs(); - convLat = mgrs.toString(); + conv = mgrs.toString(precision); } else if (outFormat === "Ordnance Survey National Grid") { - const latlon = new geodesy.LatLonEllipsoidal(parseFloat(convLat), parseFloat(convLong)); const osng = geodesy.OsGridRef.latLonToOsGrid(latlon); - convLat = osng.toString(); - if (convLat === "") { - throw "Couldn't convert co-ordinates to Ordnance Survey National Grid. Are they out of range?"; + if (osng.toString() === "") { + throw "Could not convert co-ordinates to OS National Grid. Are the co-ordinates in range?"; } - } else { - convLat = convertSingleCoordinate(convLat.toString(), "Decimal Degrees", outFormat, precision); - convLong = convertSingleCoordinate(convLong.toString(), "Decimal Degrees", outFormat, precision); + conv = osng.toString(precision); + } else if (outFormat === "Universal Transverse Mercator") { + const utm = latlon.toUtm(); + conv = utm.toString(precision); } - return [convLat, convLong]; + if (conv === undefined) { + throw "Error converting co-ordinates."; + } + if (outFormat.includes("Degrees")) { + let [latDir, longDir] = findDirs(conv, outDelim); + if (inLatDir !== undefined) { + latDir = inLatDir; + } + if (inLongDir !== undefined) { + longDir = inLongDir; + } + // DMS/DDM/DD + conv = conv.replace(", ", outDelim); + // Remove any directions from the current string, + // so we can put them where we want them + conv = conv.replace(/[NnEeSsWw]/g, ""); + if (includeDir !== "None") { + let outConv = ""; + if (!isPair) { + if (includeDir === "Before") { + outConv += latDir + " " + conv; + } else { + outConv += conv + " " + latDir; + } + } else { + const splitConv = conv.split(outDelim); + if (splitConv.length === 2) { + if (includeDir === "Before") { + outConv += latDir + " "; + } + outConv += splitConv[0]; + if (includeDir === "After") { + outConv += " " + latDir; + } + outConv += outDelim; + if (includeDir === "Before") { + outConv += longDir + " "; + } + outConv += splitConv[1]; + if (includeDir === "After") { + outConv += " " + longDir; + } + } + } + conv = outConv; + } + } + + return conv; } /** - * @param {string} input - The input co-ordinate to be converted - * @param {string} inFormat - The format of the input co-ordinates - * @param {string} outFormat - The format which input should be converted to - * @param {boolean} returnRaw - When true, returns the raw float instead of a String - * @returns {string|{Object}} The converted co-ordinate result, as either the raw object or a formatted string - */ -export function convertSingleCoordinate (input, inFormat, outFormat, precision, returnRaw = false){ - let converted; - precision = Math.pow(10, precision); - const convData = splitInput(input); - // Convert everything to decimal degrees first - switch (inFormat) { - case "Degrees Minutes Seconds": - if (convData.length < 3) { - throw "Invalid co-ordinates format."; - } - converted = convDMSToDD(convData[0], convData[1], convData[2], precision); - break; - case "Degrees Decimal Minutes": - if (convData.length < 2) { - throw "Invalid co-ordinates format."; - } - converted = convDDMToDD(convData[0], convData[1], precision); - break; - case "Decimal Degrees": - if (convData.length < 1) { - throw "Invalid co-ordinates format."; - } - converted = convDDToDD(convData[0], precision); - break; - default: - throw "Unknown input format selection."; - } - - // Convert from decimal degrees to the output format - switch (outFormat) { - case "Decimal Degrees": - break; - case "Degrees Minutes Seconds": - converted = convDDToDMS(converted.degrees); - break; - case "Degrees Decimal Minutes": - converted = convDDToDDM(converted.degrees, precision); - break; - default: - throw "Unknown output format selection."; - } - if (returnRaw) { - return converted; - } else { - return converted.string; - } -} - -/** - * Split up the input using a space, and sanitise the result + * Split up the input using a space or degrees signs, and sanitise the result * @param {string} input - The input data to be split * @returns {number[]} An array of the different items in the string, stored as floats */ function splitInput (input){ const split = []; - input.split(" ").forEach(item => { + input.split(/[°′″'"\s]/).forEach(item => { // Remove any character that isn't a digit item = item.replace(/[^0-9.-]/g, ""); if (item.length > 0){ - split.push(parseFloat(item, 10)); + split.push(parseFloat(item)); } }); return split; @@ -245,47 +337,153 @@ function convDDToDDM (decDegrees, precision) { } /** - * + * Finds and returns the compass directions in an input string + * @param {string} input - The input co-ordinates containing the direction + * @param {string} delim - The delimiter separating latitide and longitude + * @returns {string[]} String array containing the latitude and longitude directions + */ +export function findDirs(input, delim) { + const upperInput = input.toUpperCase(); + const dirExp = new RegExp(/[NESW]/g); + + const dirs = upperInput.match(dirExp); + + if (dirExp.test(upperInput)) { + // If there's actually compass directions in the string + if (dirs.length <= 2 && dirs.length >= 1) { + if (dirs.length === 2) { + return [dirs[0], dirs[1]]; + } else { + return [dirs[0], ""]; + } + } + } + // Nothing was returned, so guess the directions + let lat = upperInput, + long, + latDir = "", + longDir = ""; + if (!delim.includes("Direction")) { + if (upperInput.includes(delim)) { + const split = upperInput.split(delim); + if (split.length > 1) { + if (split[0] === "") { + lat = split[1]; + } else { + lat = split[0]; + } + if (split.length > 2) { + if (split[2] !== "") { + long = split[2]; + } + } + } + } + } else { + const split = upperInput.split(dirExp); + if (split.length > 1) { + if (split[0] === "") { + lat = split[1]; + } else { + lat = split[0]; + } + if (split.length > 2) { + if (split[2] !== "") { + long = split[2]; + } + } + } + } + if (lat) { + lat = parseFloat(lat); + if (lat < 0) { + latDir = "S"; + } else { + latDir = "N"; + } + } + if (long) { + long = parseFloat(long); + if (long < 0) { + longDir = "W"; + } else { + longDir = "E"; + } + } + + return [latDir, longDir]; +} + +/** + * Detects the co-ordinate format of the input data * @param {string} input - The input data whose format we need to detect * @param {string} delim - The delimiter separating the data in input * @returns {string} The input format */ export function findFormat (input, delim) { - input = input.trim(); let testData; - if (delim.includes("Direction")) { + const mgrsPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]{1}\s?[A-HJ-NP-Z][A-HJ-NP-V]\s?[0-9\s]+/), + osngPattern = new RegExp(/^[STNHO][A-HJ-Z][0-9]+$/), + geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/), + utmPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]\s[0-9\.]+\s?[0-9\.]+$/), + degPattern = new RegExp(/[°'"]/g); + input = input.trim(); + if (delim !== null && delim.includes("Direction")) { const split = input.split(/[NnEeSsWw]/); - if (split.length > 0) { + if (split.length > 1) { if (split[0] === "") { - // Direction Preceding testData = split[1]; } else { - // Direction Following testData = split[0]; } } - } else if (delim !== "") { - const split = input.split(delim); - if (!input.includes(delim)) { + } else if (delim !== null && delim !== "") { + if (input.includes(delim)) { + const split = input.split(delim); + if (split.length > 1) { + if (split[0] === "") { + testData = split[1]; + } else { + testData = split[0]; + } + } + } else { testData = input; } - if (split.length > 0) { - if (split[0] !== "") { - testData = split[0]; - } else if (split.length > 1) { - testData = split[1]; - } - } } // Test MGRS and Geohash - if (input.split(" ").length <= 1) { - const filteredInput = input.replace(/[^A-Za-z0-9]/, "").toUpperCase(); - const mgrsPattern = new RegExp(/^[0-9]{2}[C-HJ-NP-X]{2}[A-Z]+/); - const geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/); - if (mgrsPattern.test(filteredInput)) { + if (!degPattern.test(input)) { + const filteredInput = input.toUpperCase(); + const isMgrs = mgrsPattern.test(filteredInput); + const isOsng = osngPattern.test(filteredInput); + const isGeohash = geohashPattern.test(filteredInput); + const isUtm = utmPattern.test(filteredInput); + if (isMgrs && (isOsng || isGeohash)) { + if (filteredInput.includes("I")) { + // Only MGRS can have an i! + return "Military Grid Reference System"; + } + } + if (isUtm) { + return "Universal Transverse Mercator"; + } + if (isOsng && isGeohash) { + // Geohash doesn't have A, L or O, but OSNG does. + const testExp = new RegExp(/[ALO]/g); + if (testExp.test(filteredInput)) { + return "Ordnance Survey National Grid"; + } else { + return "Geohash"; + } + } + if (isMgrs) { return "Military Grid Reference System"; - } else if (geohashPattern.test(filteredInput)) { + } + if (isOsng) { + return "Ordnance Survey National Grid"; + } + if (isGeohash) { return "Geohash"; } } @@ -312,7 +510,7 @@ export function findFormat (input, delim) { */ export function findDelim (input) { input = input.trim(); - const delims = [",", ";", ":"]; + const delims = [",", ";", ":", " "]; const testDir = input.match(/[NnEeSsWw]/g); if (testDir !== null && testDir.length > 0 && testDir.length < 3) { // Possibly contains a direction @@ -340,3 +538,19 @@ export function findDelim (input) { } return null; } + +/** + * Gets the real string for a delimiter name. + * @param {string} delim The delimiter to be matched + * @returns {string} + */ +export function realDelim (delim) { + return { + "Auto": "Auto", + "Space": " ", + "\\n": "\n", + "Comma": ",", + "Semi-colon": ";", + "Colon": ":" + }[delim]; +} diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index afc95982..770920f4 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -5,9 +5,7 @@ */ import Operation from "../Operation"; -import OperationError from "../errors/OperationError"; -import {FORMATS, STRING_FORMATS, convertCoordinates, convertSingleCoordinate, findDelim, findFormat} from "../lib/ConvertCoordinates"; -import Utils from "../Utils"; +import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates"; /** * Convert co-ordinate format operation @@ -22,7 +20,7 @@ class ConvertCoordinateFormat extends Operation { this.name = "Convert co-ordinate format"; this.module = "Hashing"; - this.description = "Convert geographical coordinates between different formats.

Supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
  • Ordnance Survey National Grid (OSNG)
"; + this.description = "Convert geographical coordinates between different formats.

Supported formats:
  • Degrees Minutes Seconds (DMS)
  • Degrees Decimal Minutes (DDM)
  • Decimal Degrees (DD)
  • Geohash
  • Military Grid Reference System (MGRS)
  • Ordnance Survey National Grid (OSNG)
  • Universal Transverse Mercator (UTM)

The operation can try to detect the input co-ordinate format and delimiter automatically, but this may not always work correctly."; this.infoURL = "https://wikipedia.org/wiki/Geographic_coordinate_conversion"; this.inputType = "string"; this.outputType = "string"; @@ -39,6 +37,7 @@ class ConvertCoordinateFormat extends Operation { "Auto", "Direction Preceding", "Direction Following", + "Space", "\\n", "Comma", "Semi-colon", @@ -55,14 +54,21 @@ class ConvertCoordinateFormat extends Operation { "type": "option", "value": [ "Space", - "Direction Preceding", - "Direction Following", "\\n", "Comma", "Semi-colon", "Colon" ] }, + { + "name": "Include Compass Directions", + "type": "option", + "value": [ + "None", + "Before", + "After" + ] + }, { "name": "Precision", "type": "number", @@ -77,148 +83,8 @@ class ConvertCoordinateFormat extends Operation { * @returns {string} */ run(input, args) { - const outFormat = args[2], - outDelim = args[3], - precision = args[4]; - let inFormat = args[0], - inDelim = args[1], - inLat, - inLong, - outLat, - outLong, - latDir = "", - longDir = "", - outSeparator = " "; - - // Autodetect input delimiter - if (inDelim === "Auto") { - inDelim = findDelim(input); - if (inDelim === null) { - inDelim = ""; - } - } else if (!inDelim.includes("Direction")) { - // Get the actual delimiter from the regex - inDelim = String(Utils.regexRep(inDelim)).slice(1, 2); - } - if (inFormat === "Auto") { - inFormat = findFormat(input, inDelim); - if (inFormat === null) { - throw new OperationError("Could not automatically detect the input format."); - } - } - - if (inDelim === "" && (!STRING_FORMATS.includes(inFormat))) { - throw new OperationError("Could not automatically detect the input delimiter."); - } - - // Prepare input data - if (STRING_FORMATS.includes(inFormat)) { - // Geohash only has one value, so just use the input - // Replace anything that isn't a valid character in Geohash / MGRS / OSNG - inLat = input.replace(/[^A-Za-z0-9]/, ""); - } else if (inDelim === "Direction Preceding") { - // Split on the compass directions - const splitInput = input.split(/[NnEeSsWw]/); - const dir = input.match(/[NnEeSsWw]/g); - if (splitInput.length > 1) { - inLat = splitInput[1]; - if (dir !== null) { - latDir = dir[0]; - } - if (splitInput.length > 2) { - inLong = splitInput[2]; - if (dir !== null && dir.length > 1) { - longDir = dir[1]; - } - } - } - } else if (inDelim === "Direction Following") { - // Split on the compass directions - const splitInput = input.split(/[NnEeSsWw]/); - if (splitInput.length >= 1) { - inLat = splitInput[0]; - if (splitInput.length >= 2) { - inLong = splitInput[1]; - } - } - } else { - // Split on the delimiter - const splitInput = input.split(inDelim); - if (splitInput.length > 0) { - inLat = splitInput[0]; - if (splitInput.length >= 2) { - inLong = splitInput[1]; - } - } - } - - if (!STRING_FORMATS.includes(inFormat) && outDelim.includes("Direction")) { - // Match on compass directions, and store the first 2 matches for the output - const dir = input.match(/[NnEeSsWw]/g); - if (dir !== null) { - latDir = dir[0]; - if (dir.length > 1) { - longDir = dir[1]; - } - } - } else if (outDelim === "\\n") { - outSeparator = "\n"; - } else if (outDelim === "Space") { - outSeparator = " "; - } else if (!outDelim.includes("Direction")) { - // Cut out the regex syntax (/) from the delimiter - outSeparator = String(Utils.regexRep(outDelim)).slice(1, 2); - } - - // Convert the co-ordinates - if (inLat !== undefined) { - if (inLong === undefined) { - if (!STRING_FORMATS.includes(inFormat)) { - if (STRING_FORMATS.includes(outFormat)){ - throw new OperationError(`${outFormat} needs both a latitude and a longitude to be calculated`); - } - } - if (STRING_FORMATS.includes(inFormat)) { - // Geohash conversion is in convertCoordinates despite needing - // only one input as it needs to output two values - inLat = inLat.replace(/[^A-Za-z0-9]/g, ""); - [outLat, outLong] = convertCoordinates(inLat, inLat, inFormat, outFormat, precision); - } else { - outLat = convertSingleCoordinate(inLat, inFormat, outFormat, precision); - } - } else { - [outLat, outLong] = convertCoordinates(inLat, inLong, inFormat, outFormat, precision); - } - } else { - throw new OperationError("No co-ordinates were detected in the input."); - } - - // Output conversion results if successful - if (outLat !== undefined) { - let output = ""; - if (outDelim === "Direction Preceding" && !STRING_FORMATS.includes(outFormat)) { - output += latDir += " "; - } - output += outLat; - if (outDelim === "Direction Following" && !STRING_FORMATS.includes(outFormat)) { - output += " " + latDir; - } - output += outSeparator; - - if (outLong !== undefined && !STRING_FORMATS.includes(outFormat)) { - if (outDelim === "Direction Preceding") { - output += longDir + " "; - } - output += outLong; - if (outDelim === "Direction Following") { - output += " " + longDir; - } - output += outSeparator; - } - return output; - } else { - throw new OperationError("Co-ordinate conversion failed."); - } + const [inFormat, inDelim, outFormat, outDelim, incDirection, precision] = args; + return convertCoordinates(input, inFormat, inDelim, outFormat, outDelim, incDirection, precision); } } From 0602f457cef97046f03ef8dc81156de2a18ed613 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 15 Jan 2019 16:24:29 +0000 Subject: [PATCH 055/113] Added initial tests & counts support --- src/core/operations/YARARules.mjs | 13 ++++++++++--- tests/operations/index.mjs | 1 + tests/operations/tests/YARA.mjs | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 tests/operations/tests/YARA.mjs diff --git a/src/core/operations/YARARules.mjs b/src/core/operations/YARARules.mjs index 58b3576c..c453d6b8 100644 --- a/src/core/operations/YARARules.mjs +++ b/src/core/operations/YARARules.mjs @@ -49,6 +49,12 @@ class YARARules extends Operation { type: "boolean", hint: "Show the metadata of each rule", value: false + }, + { + name: "Show counts", + type: "boolean", + hint: "Show the number of matches per rule", + value: true } ]; } @@ -61,7 +67,7 @@ class YARARules extends Operation { run(input, args) { if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Instantiating YARA."); - const [rules, showStrings, showLengths, showMeta] = args; + const [rules, showStrings, showLengths, showMeta, showCounts] = args; return new Promise((resolve, reject) => { Yara().then(yara => { if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Converting data for YARA."); @@ -97,10 +103,11 @@ class YARARules extends Operation { } meta = meta.slice(0, -2) + "]"; } + const countString = showCounts ? `${matches.size()} time${matches.size() > 1 ? "s" : ""}` : ""; if (matches.size() === 0 || !(showStrings || showLengths)) { - matchString += `Input matches rule "${rule.ruleName}"${meta}.\n`; + matchString += `Input matches rule "${rule.ruleName}"${meta}${countString.length > 0 ? ` ${countString}`: ""}.\n`; } else { - matchString += `Rule "${rule.ruleName}"${meta} matches:\n`; + matchString += `Rule "${rule.ruleName}"${meta} matches (${countString}):\n`; for (let j = 0; j < matches.size(); j++) { const match = matches.get(j); if (showStrings || showLengths) { diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index da9d41be..48bd08a8 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -82,6 +82,7 @@ import "./tests/TranslateDateTimeFormat"; import "./tests/Magic"; import "./tests/ParseTLV"; import "./tests/Media"; +import "./tests/YARA.mjs"; // Cannot test operations that use the File type yet //import "./tests/SplitColourChannels"; diff --git a/tests/operations/tests/YARA.mjs b/tests/operations/tests/YARA.mjs new file mode 100644 index 00000000..e3c28ef1 --- /dev/null +++ b/tests/operations/tests/YARA.mjs @@ -0,0 +1,24 @@ +/** + * YARA Rules tests. + * + * @author Matt C [matt@artemisbot.uk] + * + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ +import TestRegister from "../TestRegister"; + +TestRegister.addTests([ + { + name: "YARA Match: simple foobar", + input: "foobar foobar bar foo foobar", + expectedOutput: "Rule \"foo\" matches (4 times):\nPos 0, length 3, identifier $re1, data: \"foo\"\nPos 7, length 3, identifier $re1, data: \"foo\"\nPos 18, length 3, identifier $re1, data: \"foo\"\nPos 22, length 3, identifier $re1, data: \"foo\"\nRule \"bar\" matches (4 times):\nPos 3, length 3, identifier $re1, data: \"bar\"\nPos 10, length 3, identifier $re1, data: \"bar\"\nPos 14, length 3, identifier $re1, data: \"bar\"\nPos 25, length 3, identifier $re1, data: \"bar\"\n", + recipeConfig: [ + { + "op": "YARA Rules", + "args": ["rule foo {strings: $re1 = /foo/ condition: $re1} rule bar {strings: $re1 = /bar/ condition: $re1}", true, true, true, true], + } + ], + }, +]); + From fcc39a03973276b96aaac22b6c8c39c217f05664 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 15 Jan 2019 23:42:05 +0000 Subject: [PATCH 056/113] Added File upload support to textarea --- src/web/Manager.mjs | 3 ++ src/web/RecipeWaiter.mjs | 63 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index d33616a4..3f4af771 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -137,6 +137,9 @@ class Manager { this.addDynamicListener("#rec-list li.operation > div", "dblclick", this.recipe.operationChildDblclick, this.recipe); this.addDynamicListener("#rec-list .dropdown-menu.toggle-dropdown a", "click", this.recipe.dropdownToggleClick, this.recipe); this.addDynamicListener("#rec-list", "operationremove", this.recipe.opRemove.bind(this.recipe)); + this.addDynamicListener("textarea.arg", "dragover", this.recipe.textArgDragover, this.recipe); + this.addDynamicListener("textarea.arg", "dragleave", this.recipe.textArgDragLeave, this.recipe); + this.addDynamicListener("textarea.arg", "drop", this.recipe.textArgDrop, this.recipe); // Input this.addMultiEventListener("#input-text", "keyup", this.input.inputChange, this.input); diff --git a/src/web/RecipeWaiter.mjs b/src/web/RecipeWaiter.mjs index b913fede..a7a9e2e3 100755 --- a/src/web/RecipeWaiter.mjs +++ b/src/web/RecipeWaiter.mjs @@ -453,6 +453,69 @@ class RecipeWaiter { window.dispatchEvent(this.manager.statechange); } + /** + * Handler for text argument dragover events. + * Gives the user a visual cue to show that items can be dropped here. + * + * @param {event} e + */ + textArgDragover (e) { + // This will be set if we're dragging an operation + if (e.dataTransfer.effectAllowed === "move") + return false; + + e.stopPropagation(); + e.preventDefault(); + e.target.closest("textarea.arg").classList.add("dropping-file"); + } + + /** + * Handler for text argument dragleave events. + * Removes the visual cue. + * + * @param {event} e + */ + textArgDragLeave (e) { + e.stopPropagation(); + e.preventDefault(); + e.target.classList.remove("dropping-file"); + } + + /** + * Handler for text argument drop events. + * Loads the dragged data into the argument textarea. + * + * @param {event} e + */ + textArgDrop(e) { + // This will be set if we're dragging an operation + if (e.dataTransfer.effectAllowed === "move") + return false; + + e.stopPropagation(); + e.preventDefault(); + const targ = e.target; + const file = e.dataTransfer.files[0]; + const text = e.dataTransfer.getData("Text"); + + targ.classList.remove("dropping-file"); + + if (text) { + targ.value = text; + return; + } + + if (file) { + const reader = new FileReader(); + const self = this; + reader.onload = function (e) { + targ.value = e.target.result; + self.ingChange(); + }; + reader.readAsText(file); + } + } + /** * Sets register values. From 3dfaaf4c2501b236ec5fddfb38df670a188e81f3 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 15 Jan 2019 23:45:40 +0000 Subject: [PATCH 057/113] Update libyara for test pass --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6ba03db3..c7706dcb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7756,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.9.tgz", - "integrity": "sha512-GTR8GD7q2xCwRzQpoSVh31KPbH145tCKaU3DAnQMpkf4aKqjnVpQlq6gUjyDhvw8RPZ2tamyQad5hg42KsVvRw==" + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.10.tgz", + "integrity": "sha512-Hn3u8wb2s6YyH8LYAVB59S05aj4vRDyLe6R3I7LvW0L2Atsc3WfaAfR1i0PgdNXYznE36FCm22pK+xLJ6CJozA==" }, "livereload-js": { "version": "2.4.0", diff --git a/package.json b/package.json index a18331d8..33c8dccd 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.9", + "libyara-wasm": "0.0.10", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", From d1961ca3fa3b6ff37d9333d6e07e0ab580d89b68 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 16 Jan 2019 01:15:51 +0000 Subject: [PATCH 058/113] Marginally reduced size of libyara-wasm --- package-lock.json | 60 ++++++++++++++++++++++------------------------- package.json | 2 +- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 08086ee7..932da37f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1497,7 +1497,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1615,7 +1615,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -1700,7 +1700,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1848,7 +1848,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "dev": true, "requires": { @@ -2286,7 +2286,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2323,7 +2323,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2388,7 +2388,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2551,7 +2551,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -2600,7 +2600,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -3133,7 +3133,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -3146,7 +3146,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -4794,7 +4794,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -5098,14 +5098,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5125,8 +5123,7 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", @@ -5274,7 +5271,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5985,7 +5981,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -6129,7 +6125,7 @@ "dependencies": { "colors": { "version": "1.1.2", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true } @@ -6193,7 +6189,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -7760,9 +7756,9 @@ "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, "libyara-wasm": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.10.tgz", - "integrity": "sha512-Hn3u8wb2s6YyH8LYAVB59S05aj4vRDyLe6R3I7LvW0L2Atsc3WfaAfR1i0PgdNXYznE36FCm22pK+xLJ6CJozA==" + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/libyara-wasm/-/libyara-wasm-0.0.11.tgz", + "integrity": "sha512-rglapPFo0IHPNksWYQXI8oqftXYj5mOGOf4BXtbSySVRX71pro4BehNjJ5qEpjYx+roGvNkcAD9zCsitA08sxw==" }, "livereload-js": { "version": "2.4.0", @@ -8480,7 +8476,7 @@ "dependencies": { "commander": { "version": "2.15.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true, "optional": true @@ -8931,7 +8927,7 @@ "dependencies": { "colors": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" }, "underscore": { @@ -10161,13 +10157,13 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true }, "winston": { "version": "2.1.1", - "resolved": "http://registry.npmjs.org/winston/-/winston-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", "dev": true, "requires": { @@ -10182,7 +10178,7 @@ "dependencies": { "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, @@ -12947,7 +12943,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -13675,14 +13671,14 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true, "optional": true }, "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true, "optional": true diff --git a/package.json b/package.json index 4dd5b07b..dd71321f 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "jsqr": "^1.1.1", "jsrsasign": "8.0.12", "kbpgp": "^2.0.82", - "libyara-wasm": "0.0.10", + "libyara-wasm": "0.0.11", "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", From 69797e58cb6fc5d5622c55dbd42fab83de1ad668 Mon Sep 17 00:00:00 2001 From: j433866 Date: Wed, 16 Jan 2019 16:57:58 +0000 Subject: [PATCH 059/113] Add better error handling. Also now doesn't do anything if there's no input --- src/core/operations/ConvertCoordinateFormat.mjs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/core/operations/ConvertCoordinateFormat.mjs b/src/core/operations/ConvertCoordinateFormat.mjs index 770920f4..09e1620c 100644 --- a/src/core/operations/ConvertCoordinateFormat.mjs +++ b/src/core/operations/ConvertCoordinateFormat.mjs @@ -5,6 +5,7 @@ */ import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates"; /** @@ -37,7 +38,6 @@ class ConvertCoordinateFormat extends Operation { "Auto", "Direction Preceding", "Direction Following", - "Space", "\\n", "Comma", "Semi-colon", @@ -83,8 +83,17 @@ class ConvertCoordinateFormat extends Operation { * @returns {string} */ run(input, args) { - const [inFormat, inDelim, outFormat, outDelim, incDirection, precision] = args; - return convertCoordinates(input, inFormat, inDelim, outFormat, outDelim, incDirection, precision); + if (input.replace(/[\s+]/g, "") !== "") { + const [inFormat, inDelim, outFormat, outDelim, incDirection, precision] = args; + try { + const result = convertCoordinates(input, inFormat, inDelim, outFormat, outDelim, incDirection, precision); + return result; + } catch (error) { + throw new OperationError(error); + } + } else { + return input; + } } } From 02b9dbdee962dfff2df1a6c4c61a62e9414415d9 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 15 Jan 2019 19:03:17 +0000 Subject: [PATCH 060/113] Replaced loading animation with animated Bombe SVG --- src/web/App.mjs | 2 + src/web/OutputWaiter.mjs | 41 ++++- src/web/html/index.html | 10 +- src/web/static/images/bombe.svg | 261 +++++++++++++++++++++++++++++ src/web/stylesheets/layout/_io.css | 32 ++-- src/web/stylesheets/preloader.css | 69 +++----- 6 files changed, 349 insertions(+), 66 deletions(-) create mode 100644 src/web/static/images/bombe.svg diff --git a/src/web/App.mjs b/src/web/App.mjs index 1dab16e6..453fba22 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -51,10 +51,12 @@ class App { */ setup() { document.dispatchEvent(this.manager.appstart); + this.initialiseSplitter(); this.loadLocalStorage(); this.populateOperationsList(); this.manager.setup(); + this.manager.output.saveBombe(); this.resetLayout(); this.setCompileMessage(); diff --git a/src/web/OutputWaiter.mjs b/src/web/OutputWaiter.mjs index 7203a16f..39d6e51b 100755 --- a/src/web/OutputWaiter.mjs +++ b/src/web/OutputWaiter.mjs @@ -334,24 +334,55 @@ class OutputWaiter { /** - * Shows or hides the loading icon. + * Save bombe object before it is removed so that it can be used later + */ + saveBombe() { + this.bombeEl = document.getElementById("bombe").cloneNode(); + this.bombeEl.setAttribute("width", "100%"); + this.bombeEl.setAttribute("height", "100%"); + } + + + /** + * Shows or hides the output loading screen. + * The animated Bombe SVG, whilst quite aesthetically pleasing, is reasonably CPU + * intensive, so we remove it from the DOM when not in use. We only show it if the + * recipe is taking longer than 200ms. We add it to the DOM just before that so that + * it is ready to fade in without stuttering. * - * @param {boolean} value + * @param {boolean} value - true == show loader */ toggleLoader(value) { + clearTimeout(this.appendBombeTimeout); + clearTimeout(this.outputLoaderTimeout); + const outputLoader = document.getElementById("output-loader"), - outputElement = document.getElementById("output-text"); + outputElement = document.getElementById("output-text"), + loader = outputLoader.querySelector(".loader"); if (value) { this.manager.controls.hideStaleIndicator(); - this.bakingStatusTimeout = setTimeout(function() { + + // Start a timer to add the Bombe to the DOM just before we make it + // visible so that there is no stuttering + this.appendBombeTimeout = setTimeout(function() { + loader.appendChild(this.bombeEl); + }.bind(this), 150); + + // Show the loading screen + this.outputLoaderTimeout = setTimeout(function() { outputElement.disabled = true; outputLoader.style.visibility = "visible"; outputLoader.style.opacity = 1; this.manager.controls.toggleBakeButtonFunction(true); }.bind(this), 200); } else { - clearTimeout(this.bakingStatusTimeout); + // Remove the Bombe from the DOM to save resources + this.outputLoaderTimeout = setTimeout(function () { + try { + loader.removeChild(this.bombeEl); + } catch (err) {} + }.bind(this), 500); outputElement.disabled = false; outputLoader.style.opacity = 0; outputLoader.style.visibility = "hidden"; diff --git a/src/web/html/index.html b/src/web/html/index.html index f03590ab..a1f915b6 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -81,7 +81,11 @@ if (!el.classList.contains("loading")) el.classList.add("loading"); // Causes CSS transition on first message el.innerHTML = msg; - } catch (err) {} // Ignore errors if DOM not yet ready + } catch (err) { + // This error was likely caused by the DOM not being ready yet, + // so we wait another second and then try again. + setTimeout(changeLoadingMsg, 1000); + } } changeLoadingMsg(); @@ -138,7 +142,9 @@
-
+
+ +
diff --git a/src/web/static/images/bombe.svg b/src/web/static/images/bombe.svg new file mode 100644 index 00000000..40857bdf --- /dev/null +++ b/src/web/static/images/bombe.svg @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + Z + Y + X + W + V + U + T + S + R + Q + P + O + N + M + L + K + J + I + H + G + F + E + D + C + B + A + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/web/stylesheets/layout/_io.css b/src/web/stylesheets/layout/_io.css index 5b0433f6..0a1e4ec4 100755 --- a/src/web/stylesheets/layout/_io.css +++ b/src/web/stylesheets/layout/_io.css @@ -73,6 +73,28 @@ background-color: var(--primary-background-colour); visibility: hidden; opacity: 0; + display: flex; + justify-content: center; + align-items: center; + + transition: all 0.5s ease; +} + +#output-loader .loader { + width: 60%; + height: 60%; + left: unset; + top: 10%; +} + +#output-loader .loading-msg { + opacity: 1; + font-family: var(--primary-font-family); + line-height: var(--primary-line-height); + color: var(--primary-font-colour); + left: unset; + top: 30%; + position: relative; transition: all 0.5s ease; } @@ -138,16 +160,6 @@ margin-bottom: 5px; } -#output-loader .loading-msg { - opacity: 1; - font-family: var(--primary-font-family); - line-height: var(--primary-line-height); - color: var(--primary-font-colour); - top: 50%; - - transition: all 0.5s ease; -} - #magic { opacity: 1; visibility: visibile; diff --git a/src/web/stylesheets/preloader.css b/src/web/stylesheets/preloader.css index 702d04a6..bce0cd03 100755 --- a/src/web/stylesheets/preloader.css +++ b/src/web/stylesheets/preloader.css @@ -16,57 +16,28 @@ background-color: var(--secondary-border-colour); } +#loader-wrapper div { + animation: fadeIn 1s ease-in 0s; +} + .loader { display: block; - position: relative; - left: 50%; - top: 50%; - width: 150px; - height: 150px; - margin: -75px 0 0 -75px; - - border: 3px solid transparent; - border-top-color: #3498db; - border-radius: 50%; - - animation: spin 2s linear infinite; -} - -.loader:before, -.loader:after { - content: ""; position: absolute; - border: 3px solid transparent; - border-radius: 50%; -} - -.loader:before { - top: 5px; - left: 5px; - right: 5px; - bottom: 5px; - border-top-color: #e74c3c; - animation: spin 3s linear infinite; -} - -.loader:after { - top: 13px; - left: 13px; - right: 13px; - bottom: 13px; - border-top-color: #f9c922; - animation: spin 1.5s linear infinite; + left: calc(50% - 200px); + top: calc(50% - 160px); + width: 400px; + height: 260px; } .loading-msg { display: block; - position: relative; + position: absolute; width: 400px; left: calc(50% - 200px); - top: calc(50% + 50px); + top: calc(50% + 110px); text-align: center; - margin-top: 50px; opacity: 0; + font-size: 18px; } .loading-msg.loading { @@ -145,18 +116,18 @@ /* Animations */ -@keyframes spin { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} - @keyframes bump { from { opacity: 0; transform: translate3d(0, 200px, 0); } } + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} From 87e956fe7df28bb82c0c57e01044154d0eea9996 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Wed, 16 Jan 2019 12:29:34 +0000 Subject: [PATCH 061/113] Added old loading icon back for initial page load. --- src/web/App.mjs | 3 ++ src/web/OutputWaiter.mjs | 13 ++++--- src/web/RecipeWaiter.mjs | 3 +- src/web/html/index.html | 8 ++--- src/web/static/images/bombe.svg | 4 +-- src/web/stylesheets/layout/_io.css | 6 ++-- src/web/stylesheets/preloader.css | 58 ++++++++++++++++++++++++------ 7 files changed, 69 insertions(+), 26 deletions(-) diff --git a/src/web/App.mjs b/src/web/App.mjs index 453fba22..e203b85c 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -124,6 +124,9 @@ class App { // Reset attemptHighlight flag this.options.attemptHighlight = true; + // Remove all current indicators + this.manager.recipe.updateBreakpointIndicator(false); + this.manager.worker.bake( this.getInput(), // The user's input this.getRecipeConfig(), // The configuration of the recipe diff --git a/src/web/OutputWaiter.mjs b/src/web/OutputWaiter.mjs index 39d6e51b..5e7ae7e8 100755 --- a/src/web/OutputWaiter.mjs +++ b/src/web/OutputWaiter.mjs @@ -334,12 +334,11 @@ class OutputWaiter { /** - * Save bombe object before it is removed so that it can be used later + * Save bombe object then remove it from the DOM so that it does not cause performance issues. */ saveBombe() { - this.bombeEl = document.getElementById("bombe").cloneNode(); - this.bombeEl.setAttribute("width", "100%"); - this.bombeEl.setAttribute("height", "100%"); + this.bombeEl = document.getElementById("bombe"); + this.bombeEl.parentNode.removeChild(this.bombeEl); } @@ -358,7 +357,7 @@ class OutputWaiter { const outputLoader = document.getElementById("output-loader"), outputElement = document.getElementById("output-text"), - loader = outputLoader.querySelector(".loader"); + animation = document.getElementById("output-loader-animation"); if (value) { this.manager.controls.hideStaleIndicator(); @@ -366,7 +365,7 @@ class OutputWaiter { // Start a timer to add the Bombe to the DOM just before we make it // visible so that there is no stuttering this.appendBombeTimeout = setTimeout(function() { - loader.appendChild(this.bombeEl); + animation.appendChild(this.bombeEl); }.bind(this), 150); // Show the loading screen @@ -380,7 +379,7 @@ class OutputWaiter { // Remove the Bombe from the DOM to save resources this.outputLoaderTimeout = setTimeout(function () { try { - loader.removeChild(this.bombeEl); + animation.removeChild(this.bombeEl); } catch (err) {} }.bind(this), 500); outputElement.disabled = false; diff --git a/src/web/RecipeWaiter.mjs b/src/web/RecipeWaiter.mjs index b913fede..a8326b27 100755 --- a/src/web/RecipeWaiter.mjs +++ b/src/web/RecipeWaiter.mjs @@ -340,10 +340,11 @@ class RecipeWaiter { /** * Moves or removes the breakpoint indicator in the recipe based on the position. * - * @param {number} position + * @param {number|boolean} position - If boolean, turn off all indicators */ updateBreakpointIndicator(position) { const operations = document.querySelectorAll("#rec-list li.operation"); + if (typeof position === "boolean") position = operations.length; for (let i = 0; i < operations.length; i++) { if (i === position) { operations[i].classList.add("break"); diff --git a/src/web/html/index.html b/src/web/html/index.html index a1f915b6..478d2bb3 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -142,9 +142,7 @@
-
- -
+
@@ -321,7 +319,9 @@
-
+
+ +
diff --git a/src/web/static/images/bombe.svg b/src/web/static/images/bombe.svg index 40857bdf..1fdca842 100644 --- a/src/web/static/images/bombe.svg +++ b/src/web/static/images/bombe.svg @@ -56,13 +56,13 @@ - + diff --git a/src/web/stylesheets/layout/_io.css b/src/web/stylesheets/layout/_io.css index 0a1e4ec4..d4af353f 100755 --- a/src/web/stylesheets/layout/_io.css +++ b/src/web/stylesheets/layout/_io.css @@ -80,11 +80,13 @@ transition: all 0.5s ease; } -#output-loader .loader { +#output-loader-animation { + display: block; + position: absolute; width: 60%; height: 60%; - left: unset; top: 10%; + transition: all 0.5s ease; } #output-loader .loading-msg { diff --git a/src/web/stylesheets/preloader.css b/src/web/stylesheets/preloader.css index bce0cd03..690fe5c1 100755 --- a/src/web/stylesheets/preloader.css +++ b/src/web/stylesheets/preloader.css @@ -16,25 +16,54 @@ background-color: var(--secondary-border-colour); } -#loader-wrapper div { - animation: fadeIn 1s ease-in 0s; -} - .loader { display: block; + position: relative; + left: 50%; + top: 50%; + width: 150px; + height: 150px; + margin: -75px 0 0 -75px; + + border: 3px solid transparent; + border-top-color: #3498db; + border-radius: 50%; + + animation: spin 2s linear infinite; +} + +.loader:before, +.loader:after { + content: ""; position: absolute; - left: calc(50% - 200px); - top: calc(50% - 160px); - width: 400px; - height: 260px; + border: 3px solid transparent; + border-radius: 50%; +} + +.loader:before { + top: 5px; + left: 5px; + right: 5px; + bottom: 5px; + border-top-color: #e74c3c; + animation: spin 3s linear infinite; +} + +.loader:after { + top: 13px; + left: 13px; + right: 13px; + bottom: 13px; + border-top-color: #f9c922; + animation: spin 1.5s linear infinite; } .loading-msg { display: block; - position: absolute; + position: relative; width: 400px; left: calc(50% - 200px); - top: calc(50% + 110px); + top: calc(50% + 50px); text-align: center; opacity: 0; font-size: 18px; @@ -116,6 +145,15 @@ /* Animations */ +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + @keyframes bump { from { opacity: 0; From 439654ed7f11d7c7bf3a4364d0f4bb95dce34aee Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 17 Jan 2019 13:49:36 +0000 Subject: [PATCH 062/113] Add tests for new co-ordinate conversion module. Removed To/From geohash tests --- test/index.mjs | 3 +- .../operations/ConvertCoordinateFormat.mjs | 211 ++++++++++++++++++ test/tests/operations/FromGeohash.mjs | 55 ----- test/tests/operations/ToGeohash.mjs | 55 ----- 4 files changed, 212 insertions(+), 112 deletions(-) create mode 100644 test/tests/operations/ConvertCoordinateFormat.mjs delete mode 100644 test/tests/operations/FromGeohash.mjs delete mode 100644 test/tests/operations/ToGeohash.mjs diff --git a/test/index.mjs b/test/index.mjs index 9c11f6ae..8d7bd798 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -44,7 +44,6 @@ import "./tests/operations/DateTime"; import "./tests/operations/ExtractEmailAddresses"; import "./tests/operations/Fork"; import "./tests/operations/FromDecimal"; -import "./tests/operations/FromGeohash"; import "./tests/operations/Hash"; import "./tests/operations/HaversineDistance"; import "./tests/operations/Hexdump"; @@ -74,10 +73,10 @@ import "./tests/operations/SetIntersection"; import "./tests/operations/SetUnion"; import "./tests/operations/StrUtils"; import "./tests/operations/SymmetricDifference"; -import "./tests/operations/ToGeohash.mjs"; import "./tests/operations/TranslateDateTimeFormat"; import "./tests/operations/Magic"; import "./tests/operations/ParseTLV"; +import "./tests/operations/ConvertCoordinateFormat"; let allTestsPassing = true; const testStatusCounts = { diff --git a/test/tests/operations/ConvertCoordinateFormat.mjs b/test/tests/operations/ConvertCoordinateFormat.mjs new file mode 100644 index 00000000..89690b97 --- /dev/null +++ b/test/tests/operations/ConvertCoordinateFormat.mjs @@ -0,0 +1,211 @@ +/** + * Convert co-ordinate format tests + * + * @author j433866 + * + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +/** + * TEST CO-ORDINATES + * DD: 51.504°,-0.126°, + * DDM: 51° 30.24',-0° 7.56', + * DMS: 51° 30' 14.4",-0° 7' 33.6", + * Geohash: gcpvj0h0x, + * MGRS: 30U XC 99455 09790, + * OSNG: TQ 30163 80005, + * UTM: 30N 699456 5709791, + */ + +import TestRegister from "../../TestRegister"; + +TestRegister.addTests([ + { + name: "Co-ordinates: From Decimal Degrees to Degrees Minutes Seconds", + input: "51.504°,-0.126°,", + expectedOutput: "51° 30' 14.4\",-0° 7' 33.6\",", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Degrees Minutes Seconds", "Comma", "None", 1] + }, + ], + }, + { + name: "Co-ordinates: From Degrees Minutes Seconds to Decimal Degrees", + input: "51° 30' 14.4\",-0° 7' 33.6\",", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Degrees Minutes Seconds", "Comma", "Decimal Degrees", "Comma", "None", 3] + }, + ], + }, + { + name: "Co-ordinates: From Decimal Degrees to Degrees Decimal Minutes", + input: "51.504°,-0.126°,", + expectedOutput: "51° 30.24',-0° 7.56',", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Degrees Decimal Minutes", "Comma", "None", 2] + } + ] + }, + { + name: "Co-ordinates: From Degrees Decimal Minutes to Decimal Degrees", + input: "51° 30.24',-0° 7.56',", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Degrees Decimal Minutes", "Comma", "Decimal Degrees", "Comma", "None", 3] + } + ] + }, + { + name: "Co-ordinates: From Decimal Degrees to Decimal Degrees", + input: "51.504°,-0.126°,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Decimal Degrees", "Comma", "None", 3] + } + ] + }, + { + name: "Co-ordinates: From Decimal Degrees to Geohash", + input: "51.504°,-0.126°,", + expectedOutput: "gcpvj0h0x,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Geohash", "Comma", "None", 9] + }, + ], + }, + { + name: "Co-ordinates: From Geohash to Decimal Degrees", + input: "gcpvj0h0x,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Geohash", "Comma", "Decimal Degrees", "Comma", "None", 3] + }, + ], + }, + { + name: "Co-ordinates: From Decimal Degrees to MGRS", + input: "51.504°,-0.126°,", + expectedOutput: "30U XC 99455 09790,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Military Grid Reference System", "Comma", "None", 10] + }, + ], + }, + { + name: "Co-ordinates: From MGRS to Decimal Degrees", + input: "30U XC 99455 09790,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Military Grid Reference System", "Comma", "Decimal Degrees", "Comma", "None", 3] + } + ] + }, + { + name: "Co-ordinates: From Decimal Degrees to OSNG", + input: "51.504°,-0.126°,", + expectedOutput: "TQ 30163 80005,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Ordnance Survey National Grid", "Comma", "None", 10] + }, + ], + }, + { + name: "Co-ordinates: From OSNG to Decimal Degrees", + input: "TQ 30163 80005,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Ordnance Survey National Grid", "Comma", "Decimal Degrees", "Comma", "None", 3] + }, + ], + }, + { + name: "Co-ordinates: From Decimal Degrees to UTM", + input: "51.504°,-0.126°,", + expectedOutput: "30 N 699456 5709791,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Universal Transverse Mercator", "Comma", "None", 0] + }, + ], + }, + { + name: "Co-ordinates: From UTM to Decimal Degrees", + input: "30 N 699456 5709791,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Universal Transverse Mercator", "Comma", "Decimal Degrees", "Comma", "None", 3] + }, + ], + }, + { + name: "Co-ordinates: Directions in input, not output", + input: "N51.504°,W0.126°,", + expectedOutput: "51.504°,-0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Decimal Degrees", "Comma", "None", 3] + }, + ], + }, + { + name: "Co-ordinates: Directions in input and output", + input: "N51.504°,W0.126°,", + expectedOutput: "N 51.504°,W 0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Decimal Degrees", "Comma", "Before", 3] + }, + ], + }, + { + name: "Co-ordinates: Directions not in input, in output", + input: "51.504°,-0.126°,", + expectedOutput: "N 51.504°,W 0.126°,", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Decimal Degrees", "Comma", "Before", 3] + }, + ], + }, + { + name: "Co-ordinates: Directions not in input, in converted output", + input: "51.504°,-0.126°,", + expectedOutput: "N 51° 30' 14.4\",W 0° 7' 33.6\",", + recipeConfig: [ + { + op: "Convert co-ordinate format", + args: ["Decimal Degrees", "Comma", "Degrees Minutes Seconds", "Comma", "Before", 3] + }, + ], + } +]); diff --git a/test/tests/operations/FromGeohash.mjs b/test/tests/operations/FromGeohash.mjs deleted file mode 100644 index 2ac68c58..00000000 --- a/test/tests/operations/FromGeohash.mjs +++ /dev/null @@ -1,55 +0,0 @@ -/** - * To Geohash tests - * - * @author gchq77703 - * @copyright Crown Copyright 2018 - * @license Apache-2.0 - */ -import TestRegister from "../../TestRegister"; - -TestRegister.addTests([ - { - name: "From Geohash", - input: "ww8p1r4t8", - expectedOutput: "37.83238649368286,112.55838632583618", - recipeConfig: [ - { - op: "From Geohash", - args: [], - }, - ], - }, - { - name: "From Geohash", - input: "ww8p1r", - expectedOutput: "37.83416748046875,112.5604248046875", - recipeConfig: [ - { - op: "From Geohash", - args: [], - }, - ], - }, - { - name: "From Geohash", - input: "ww8", - expectedOutput: "37.265625,113.203125", - recipeConfig: [ - { - op: "From Geohash", - args: [], - }, - ], - }, - { - name: "From Geohash", - input: "w", - expectedOutput: "22.5,112.5", - recipeConfig: [ - { - op: "From Geohash", - args: [], - }, - ], - }, -]); diff --git a/test/tests/operations/ToGeohash.mjs b/test/tests/operations/ToGeohash.mjs deleted file mode 100644 index b50e7280..00000000 --- a/test/tests/operations/ToGeohash.mjs +++ /dev/null @@ -1,55 +0,0 @@ -/** - * To Geohash tests - * - * @author gchq77703 - * @copyright Crown Copyright 2018 - * @license Apache-2.0 - */ -import TestRegister from "../../TestRegister"; - -TestRegister.addTests([ - { - name: "To Geohash", - input: "37.8324,112.5584", - expectedOutput: "ww8p1r4t8", - recipeConfig: [ - { - op: "To Geohash", - args: [9], - }, - ], - }, - { - name: "To Geohash", - input: "37.9324,-112.2584", - expectedOutput: "9w8pv3ruj", - recipeConfig: [ - { - op: "To Geohash", - args: [9], - }, - ], - }, - { - name: "To Geohash", - input: "37.8324,112.5584", - expectedOutput: "ww8", - recipeConfig: [ - { - op: "To Geohash", - args: [3], - }, - ], - }, - { - name: "To Geohash", - input: "37.9324,-112.2584", - expectedOutput: "9w8pv3rujxy5b99", - recipeConfig: [ - { - op: "To Geohash", - args: [15], - }, - ], - }, -]); From 4bd923dc063f901d54237204834ad0b85295964b Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 17 Jan 2019 13:53:42 +0000 Subject: [PATCH 063/113] Improved handling of negative numbers and weirder inputs. Negative numbers shouldn't make it go weird any more. Automatic detection of input formats should be more reliable. --- src/core/lib/ConvertCoordinates.mjs | 352 +++++++++++++++++----------- 1 file changed, 212 insertions(+), 140 deletions(-) diff --git a/src/core/lib/ConvertCoordinates.mjs b/src/core/lib/ConvertCoordinates.mjs index cec2439c..d4641fe6 100644 --- a/src/core/lib/ConvertCoordinates.mjs +++ b/src/core/lib/ConvertCoordinates.mjs @@ -23,8 +23,7 @@ export const FORMATS = [ ]; /** - * Formats that should be passed to Geodesy module as-is - * Spaces are still removed + * Formats that should be passed to the conversion module as-is */ const NO_CHANGE = [ "Geohash", @@ -48,40 +47,50 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli let isPair = false, split, latlon, - conv, - inLatDir, - inLongDir; + convLat, + convLon, + conv; + // Can't have a precision less than 0! + if (precision < 0) { + precision = 0; + } if (inDelim === "Auto") { + // Try to detect a delimiter in the input. inDelim = findDelim(input); + if (inDelim === null) { + throw "Unable to detect the input delimiter automatically."; + } } else { + // Convert the delimiter argument value to the actual character inDelim = realDelim(inDelim); } if (inFormat === "Auto") { + // Try to detect the format of the input data inFormat = findFormat(input, inDelim); if (inFormat === null) { throw "Unable to detect the input format automatically."; } } - if (inDelim === null && !inFormat.includes("Direction")) { - throw "Unable to detect the input delimiter automatically."; - } + // Convert the output delimiter argument to the real character outDelim = realDelim(outDelim); if (!NO_CHANGE.includes(inFormat)) { split = input.split(inDelim); + // Replace any co-ordinate symbols with spaces so we can split on them later + for (let i = 0; i < split.length; i++) { + split[i] = split[i].replace(/[°˝´'"]/g, " "); + } if (split.length > 1) { isPair = true; } } else { + // Remove any delimiters from the input input = input.replace(inDelim, ""); isPair = true; } - if (inFormat.includes("Degrees")) { - [inLatDir, inLongDir] = findDirs(input, inDelim); - } - + // Conversions from the input format into a geodesy latlon object if (inFormat === "Geohash") { const hash = geohash.decode(input.replace(/[^A-Za-z0-9]/g, "")); latlon = new geodesy.LatLonEllipsoidal(hash.latitude, hash.longitude); @@ -92,6 +101,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli const osng = geodesy.OsGridRef.parse(input.replace(/[^A-Za-z0-9]/g, "")); latlon = geodesy.OsGridRef.osGridToLatLon(osng); } else if (inFormat === "Universal Transverse Mercator") { + // Geodesy needs a space between the first 2 digits and the next letter if (/^[\d]{2}[A-Za-z]/.test(input)) { input = input.slice(0, 2) + " " + input.slice(2); } @@ -99,23 +109,25 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli latlon = utm.toLatLonE(); } else if (inFormat === "Degrees Minutes Seconds") { if (isPair) { - split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim(); - split[1] = split[1].replace(/[NnEeSsWw]/g, "").trim(); - const splitLat = split[0].split(/[°′″'"\s]/g), - splitLong = split[1].split(/[°′″'"\s]/g); + // Split up the lat/long into degrees / minutes / seconds values + const splitLat = splitInput(split[0]), + splitLong = splitInput(split[1]); if (splitLat.length >= 3 && splitLong.length >= 3) { - const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2]), 10); - const long = convDMSToDD(parseFloat(splitLong[0]), parseFloat(splitLong[1]), parseFloat(splitLong[2]), 10); + const lat = convDMSToDD(splitLat[0], splitLat[1], splitLat[2], 10); + const long = convDMSToDD(splitLong[0], splitLong[1], splitLong[2], 10); latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees); + } else { + throw "Invalid co-ordinate format for Degrees Minutes Seconds"; } } else { - // Create a new latlon object anyway, but we can ignore the lon value - split[0] = split[0].replace(/[NnEeSsWw]/g, "").trim(); - const splitLat = split[0].split(/[°′″'"\s]/g); + // Not a pair, so only try to convert one set of co-ordinates + const splitLat = splitInput(split[0]); if (splitLat.length >= 3) { - const lat = convDMSToDD(parseFloat(splitLat[0]), parseFloat(splitLat[1]), parseFloat(splitLat[2])); + const lat = convDMSToDD(splitLat[0], splitLat[1], splitLat[2]); latlon = new geodesy.LatLonEllipsoidal(lat.degrees, lat.degrees); + } else { + throw "Invalid co-ordinate format for Degrees Minutes Seconds"; } } } else if (inFormat === "Degrees Decimal Minutes") { @@ -125,10 +137,12 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli if (splitLat.length !== 2 || splitLong.length !== 2) { throw "Invalid co-ordinate format for Degrees Decimal Minutes."; } + // Convert to decimal degrees, and then convert to a geodesy object const lat = convDDMToDD(splitLat[0], splitLat[1], 10); const long = convDDMToDD(splitLong[0], splitLong[1], 10); latlon = new geodesy.LatLonEllipsoidal(lat.degrees, long.degrees); } else { + // Not a pair, so only try to convert one set of co-ordinates const splitLat = splitInput(input); if (splitLat.length !== 2) { throw "Invalid co-ordinate format for Degrees Decimal Minutes."; @@ -145,6 +159,7 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli } latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLong[0]); } else { + // Not a pair, so only try to convert one set of co-ordinates const splitLat = splitInput(split[0]); if (splitLat.length !== 1) { throw "Invalid co-ordinate format for Decimal Degrees."; @@ -152,88 +167,115 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli latlon = new geodesy.LatLonEllipsoidal(splitLat[0], splitLat[0]); } } else { - throw "Invalid input co-ordinate format selected."; + throw `Unknown input format '${inFormat}'`; } // Everything is now a geodesy latlon object + // These store the latitude and longitude as decimal + if (inFormat.includes("Degrees")) { + // If the input string contains directions, we need to check if they're S or W. + // If either of the directions are, we should make the decimal value negative + const dirs = input.match(/[NnEeSsWw]/g); + if (dirs && dirs.length >= 1) { + // Make positive lat/lon values with S/W directions into negative values + if (dirs[0] === "S" || dirs[0] === "W" && latlon.lat > 0) { + latlon.lat = 0 - latlon.lat; + } + if (dirs.length >= 2) { + if (dirs[1] === "S" || dirs[1] === "W" && latlon.lon > 0) { + latlon.lon = 0 - latlon.lon; + } + } + } + } + // Try to find the compass directions of the lat and long + const [latDir, longDir] = findDirs(latlon.lat + "," + latlon.lon, ","); + // Output conversions for each output format if (outFormat === "Decimal Degrees") { - conv = latlon.toString("d", precision); - if (!isPair) { - conv = conv.split(",")[0]; - } + // We could use the built in latlon.toString(), + // but this makes adjusting the output harder + const lat = convDDToDD(latlon.lat, precision); + const lon = convDDToDD(latlon.lon, precision); + convLat = lat.string; + convLon = lon.string; } else if (outFormat === "Degrees Decimal Minutes") { - conv = latlon.toString("dm", precision); - if (!isPair) { - conv = conv.split(",")[0]; - } + const lat = convDDToDDM(latlon.lat, precision); + const lon = convDDToDDM(latlon.lon, precision); + convLat = lat.string; + convLon = lon.string; } else if (outFormat === "Degrees Minutes Seconds") { - conv = latlon.toString("dms", precision); - if (!isPair) { - conv = conv.split(",")[0]; - } + const lat = convDDToDMS(latlon.lat, precision); + const lon = convDDToDMS(latlon.lon, precision); + convLat = lat.string; + convLon = lon.string; } else if (outFormat === "Geohash") { - conv = geohash.encode(latlon.lat.toString(), latlon.lon.toString(), precision); + convLat = geohash.encode(latlon.lat, latlon.lon, precision); } else if (outFormat === "Military Grid Reference System") { const utm = latlon.toUtm(); const mgrs = utm.toMgrs(); - conv = mgrs.toString(precision); + // MGRS wants a precision that's an even number between 2 and 10 + if (precision % 2 !== 0) { + precision = precision + 1; + } + if (precision > 10) { + precision = 10; + } + convLat = mgrs.toString(precision); } else if (outFormat === "Ordnance Survey National Grid") { const osng = geodesy.OsGridRef.latLonToOsGrid(latlon); if (osng.toString() === "") { throw "Could not convert co-ordinates to OS National Grid. Are the co-ordinates in range?"; } - conv = osng.toString(precision); + // OSNG wants a precision that's an even number between 2 and 10 + if (precision % 2 !== 0) { + precision = precision + 1; + } + if (precision > 10) { + precision = 10; + } + convLat = osng.toString(precision); } else if (outFormat === "Universal Transverse Mercator") { const utm = latlon.toUtm(); - conv = utm.toString(precision); + convLat = utm.toString(precision); } - if (conv === undefined) { + if (convLat === undefined) { throw "Error converting co-ordinates."; } + if (outFormat.includes("Degrees")) { - let [latDir, longDir] = findDirs(conv, outDelim); - if (inLatDir !== undefined) { - latDir = inLatDir; + // Format DD/DDM/DMS for output + // If we're outputting a compass direction, remove the negative sign + if (latDir === "S" && includeDir !== "None") { + convLat = convLat.replace("-", ""); } - if (inLongDir !== undefined) { - longDir = inLongDir; + if (longDir === "W" && includeDir !== "None") { + convLon = convLon.replace("-", ""); } - // DMS/DDM/DD - conv = conv.replace(", ", outDelim); - // Remove any directions from the current string, - // so we can put them where we want them - conv = conv.replace(/[NnEeSsWw]/g, ""); - if (includeDir !== "None") { - let outConv = ""; - if (!isPair) { - if (includeDir === "Before") { - outConv += latDir + " " + conv; - } else { - outConv += conv + " " + latDir; - } - } else { - const splitConv = conv.split(outDelim); - if (splitConv.length === 2) { - if (includeDir === "Before") { - outConv += latDir + " "; - } - outConv += splitConv[0]; - if (includeDir === "After") { - outConv += " " + latDir; - } - outConv += outDelim; - if (includeDir === "Before") { - outConv += longDir + " "; - } - outConv += splitConv[1]; - if (includeDir === "After") { - outConv += " " + longDir; - } - } + + let outConv = ""; + if (includeDir === "Before") { + outConv += latDir + " "; + } + + outConv += convLat; + if (includeDir === "After") { + outConv += " " + latDir; + } + outConv += outDelim; + if (isPair) { + if (includeDir === "Before") { + outConv += longDir + " "; } - conv = outConv; + outConv += convLon; + if (includeDir === "After") { + outConv += " " + longDir; + } + outConv += outDelim; } + conv = outConv; + } else { + conv = convLat + outDelim; } return conv; @@ -247,10 +289,11 @@ export function convertCoordinates (input, inFormat, inDelim, outFormat, outDeli function splitInput (input){ const split = []; - input.split(/[°′″'"\s]/).forEach(item => { - // Remove any character that isn't a digit + input.split(/\s+/).forEach(item => { + // Remove any character that isn't a digit, decimal point or negative sign item = item.replace(/[^0-9.-]/g, ""); if (item.length > 0){ + // Turn the item into a float split.push(parseFloat(item)); } }); @@ -266,10 +309,17 @@ function splitInput (input){ * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) */ function convDMSToDD (degrees, minutes, seconds, precision){ - const converted = new Object(); - converted.degrees = degrees + (minutes / 60) + (seconds / 3600); - converted.string = (Math.round(converted.degrees * precision) / precision) + "°"; - return converted; + const absDegrees = Math.abs(degrees); + let conv = absDegrees + (minutes / 60) + (seconds / 3600); + let outString = round(conv, precision) + "°"; + if (isNegativeZero(degrees) || degrees < 0) { + conv = -conv; + outString = "-" + outString; + } + return { + "degrees": conv, + "string": outString + }; } /** @@ -280,10 +330,17 @@ function convDMSToDD (degrees, minutes, seconds, precision){ * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) */ function convDDMToDD (degrees, minutes, precision) { - const converted = new Object(); - converted.degrees = degrees + minutes / 60; - converted.string = ((Math.round(converted.degrees * precision) / precision) + "°"); - return converted; + const absDegrees = Math.abs(degrees); + let conv = absDegrees + minutes / 60; + let outString = round(conv, precision) + "°"; + if (isNegativeZero(degrees) || degrees < 0) { + conv = -conv; + outString = "-" + outString; + } + return { + "degrees": conv, + "string": outString + }; } /** @@ -294,28 +351,34 @@ function convDDMToDD (degrees, minutes, precision) { * @returns {{string: string, degrees: number}} An object containing the raw converted value (obj.degrees), and a formatted string version (obj.string) */ function convDDToDD (degrees, precision) { - const converted = new Object(); - converted.degrees = degrees; - converted.string = Math.round(converted.degrees * precision) / precision + "°"; - return converted; + return { + "degrees": degrees, + "string": round(degrees, precision) + "°" + }; } /** * Convert Decimal Degrees to Degrees Minutes Seconds * @param {number} decDegrees - The input data to be converted + * @param {number} precision - The precision which the result should be rounded to * @returns {{string: string, degrees: number, minutes: number, seconds: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes, .seconds), and a formatted string version (obj.string) */ -function convDDToDMS (decDegrees) { - const degrees = Math.floor(decDegrees); - const minutes = Math.floor(60 * (decDegrees - degrees)); - const seconds = Math.round(3600 * (decDegrees - degrees) - 60 * minutes); - - const converted = new Object(); - converted.degrees = degrees; - converted.minutes = minutes; - converted.seconds = seconds; - converted.string = degrees + "° " + minutes + "' " + seconds + "\""; - return converted; +function convDDToDMS (decDegrees, precision) { + const absDegrees = Math.abs(decDegrees); + let degrees = Math.floor(absDegrees); + const minutes = Math.floor(60 * (absDegrees - degrees)), + seconds = round(3600 * (absDegrees - degrees) - 60 * minutes, precision); + let outString = degrees + "° " + minutes + "' " + seconds + "\""; + if (isNegativeZero(decDegrees) || decDegrees < 0) { + degrees = -degrees; + outString = "-" + outString; + } + return { + "degrees": degrees, + "minutes": minutes, + "seconds": seconds, + "string": outString + }; } /** @@ -325,15 +388,21 @@ function convDDToDMS (decDegrees) { * @returns {{string: string, degrees: number, minutes: number}} An object containing the raw converted value as separate numbers (.degrees, .minutes), and a formatted string version (obj.string) */ function convDDToDDM (decDegrees, precision) { - const degrees = Math.floor(decDegrees); - const minutes = decDegrees - degrees; - const decMinutes = Math.round((minutes * 60) * precision) / precision; + const absDegrees = Math.abs(decDegrees); + let degrees = Math.floor(absDegrees); + const minutes = absDegrees - degrees, + decMinutes = round(minutes * 60, precision); + let outString = degrees + "° " + decMinutes + "'"; + if (decDegrees < 0 || isNegativeZero(decDegrees)) { + degrees = -degrees; + outString = "-" + outString; + } - const converted = new Object(); - converted.degrees = degrees; - converted.minutes = decMinutes; - converted.string = degrees + "° " + decMinutes + "'"; - return converted; + return { + "degrees": degrees, + "minutes": decMinutes, + "string": outString, + }; } /** @@ -348,8 +417,9 @@ export function findDirs(input, delim) { const dirs = upperInput.match(dirExp); - if (dirExp.test(upperInput)) { - // If there's actually compass directions in the string + if (dirs) { + // If there's actually compass directions + // in the input, use these to work out the direction if (dirs.length <= 2 && dirs.length >= 1) { if (dirs.length === 2) { return [dirs[0], dirs[1]]; @@ -366,15 +436,13 @@ export function findDirs(input, delim) { if (!delim.includes("Direction")) { if (upperInput.includes(delim)) { const split = upperInput.split(delim); - if (split.length > 1) { - if (split[0] === "") { - lat = split[1]; - } else { + if (split.length >= 1) { + if (split[0] !== "") { lat = split[0]; } - if (split.length > 2) { - if (split[2] !== "") { - long = split[2]; + if (split.length >= 2) { + if (split[1] !== "") { + long = split[1]; } } } @@ -423,9 +491,9 @@ export function findDirs(input, delim) { export function findFormat (input, delim) { let testData; const mgrsPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]{1}\s?[A-HJ-NP-Z][A-HJ-NP-V]\s?[0-9\s]+/), - osngPattern = new RegExp(/^[STNHO][A-HJ-Z][0-9]+$/), + osngPattern = new RegExp(/^[A-HJ-Z]{2}\s+[0-9\s]+$/), geohashPattern = new RegExp(/^[0123456789BCDEFGHJKMNPQRSTUVWXYZ]+$/), - utmPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]\s[0-9\.]+\s?[0-9\.]+$/), + utmPattern = new RegExp(/^[0-9]{2}\s?[C-HJ-NP-X]\s[0-9.]+\s?[0-9.]+$/), degPattern = new RegExp(/[°'"]/g); input = input.trim(); if (delim !== null && delim.includes("Direction")) { @@ -452,31 +520,16 @@ export function findFormat (input, delim) { } } - // Test MGRS and Geohash + // Test non-degrees formats if (!degPattern.test(input)) { - const filteredInput = input.toUpperCase(); + const filteredInput = input.toUpperCase().replace(delim, ""); const isMgrs = mgrsPattern.test(filteredInput); const isOsng = osngPattern.test(filteredInput); const isGeohash = geohashPattern.test(filteredInput); const isUtm = utmPattern.test(filteredInput); - if (isMgrs && (isOsng || isGeohash)) { - if (filteredInput.includes("I")) { - // Only MGRS can have an i! - return "Military Grid Reference System"; - } - } if (isUtm) { return "Universal Transverse Mercator"; } - if (isOsng && isGeohash) { - // Geohash doesn't have A, L or O, but OSNG does. - const testExp = new RegExp(/[ALO]/g); - if (testExp.test(filteredInput)) { - return "Ordnance Survey National Grid"; - } else { - return "Geohash"; - } - } if (isMgrs) { return "Military Grid Reference System"; } @@ -510,7 +563,7 @@ export function findFormat (input, delim) { */ export function findDelim (input) { input = input.trim(); - const delims = [",", ";", ":", " "]; + const delims = [",", ";", ":"]; const testDir = input.match(/[NnEeSsWw]/g); if (testDir !== null && testDir.length > 0 && testDir.length < 3) { // Possibly contains a direction @@ -554,3 +607,22 @@ export function realDelim (delim) { "Colon": ":" }[delim]; } + +/** + * Returns true if a zero is negative + * @param {number} zero + */ +function isNegativeZero(zero) { + return zero === 0 && (1/zero < 0); +} + +/** + * Rounds a number to a specified number of decimal places + * @param {number} input - The number to be rounded + * @param {precision} precision - The number of decimal places the number should be rounded to + * @returns {number} + */ +function round(input, precision) { + precision = Math.pow(10, precision); + return Math.round(input * precision) / precision; +} From 445a85798b49ae6d3817d7b86baa3b5db7f4e611 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Fri, 18 Jan 2019 14:50:00 +0000 Subject: [PATCH 064/113] 8.22.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 00e57f64..75645253 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.22.0", + "version": "8.22.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 96053213..796e4ec4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.22.0", + "version": "8.22.1", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From 0c14bacea7b7218a80c59cfbb43da6d453c89665 Mon Sep 17 00:00:00 2001 From: j433866 Date: Fri, 18 Jan 2019 15:07:19 +0000 Subject: [PATCH 065/113] Add button to input to allow opening of files using the file prompt. --- src/web/InputWaiter.mjs | 20 ++++++++++++++++++++ src/web/Manager.mjs | 1 + src/web/html/index.html | 4 ++++ 3 files changed, 25 insertions(+) diff --git a/src/web/InputWaiter.mjs b/src/web/InputWaiter.mjs index 37f1134a..245891f7 100755 --- a/src/web/InputWaiter.mjs +++ b/src/web/InputWaiter.mjs @@ -251,6 +251,26 @@ class InputWaiter { } } + /** + * Handler for open input button events + * Loads the opened data into the input textarea + * + * @param {event} e + */ + inputOpen(e) { + e.preventDefault(); + + const file = e.srcElement.files[0]; + + if (file) { + this.closeFile(); + this.loaderWorker = new LoaderWorker(); + this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this)); + this.loaderWorker.postMessage({"file": file}); + this.set(file); + } + } + /** * Handler for messages sent back by the LoaderWorker. diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index d33616a4..c446aba3 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -143,6 +143,7 @@ class Manager { this.addMultiEventListener("#input-text", "paste", this.input.inputPaste, this.input); document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app)); document.getElementById("clr-io").addEventListener("click", this.input.clearIoClick.bind(this.input)); + this.addListeners("#open-file", "change", this.input.inputOpen, this.input); this.addListeners("#input-text,#input-file", "dragover", this.input.inputDragover, this.input); this.addListeners("#input-text,#input-file", "dragleave", this.input.inputDragleave, this.input); this.addListeners("#input-text,#input-file", "drop", this.input.inputDrop, this.input); diff --git a/src/web/html/index.html b/src/web/html/index.html index f03590ab..cc10f4b4 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -225,6 +225,10 @@
+ From acb8c0b5aff0322a93961f809dcf6604ad76118f Mon Sep 17 00:00:00 2001 From: j433866 Date: Fri, 18 Jan 2019 15:12:03 +0000 Subject: [PATCH 066/113] Change icon from folder to input --- src/web/html/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/html/index.html b/src/web/html/index.html index cc10f4b4..74eb0ed8 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -226,7 +226,7 @@
`; break; case "populateOption": + case "populateMultiOption": html += `
${this.hint ? "" + this.hint + "" : ""}
`; - this.manager.addDynamicListener("#" + this.id, "change", this.populateOptionChange, this); + eventFn = this.type === "populateMultiOption" ? + this.populateMultiOptionChange : + this.populateOptionChange; + this.manager.addDynamicListener("#" + this.id, "change", eventFn, this); break; case "editableOption": html += `
@@ -248,6 +255,9 @@ class HTMLIngredient { * @param {event} e */ populateOptionChange(e) { + e.preventDefault(); + e.stopPropagation(); + const el = e.target; const op = el.parentNode.parentNode; const target = op.querySelectorAll(".arg")[this.target]; @@ -260,6 +270,37 @@ class HTMLIngredient { } + /** + * Handler for populate multi option changes. + * Populates the relevant arguments with the specified values. + * + * @param {event} e + */ + populateMultiOptionChange(e) { + e.preventDefault(); + e.stopPropagation(); + + const el = e.target; + const op = el.parentNode.parentNode; + const args = op.querySelectorAll(".arg"); + const targets = this.target.map(i => args[i]); + const vals = JSON.parse(el.childNodes[el.selectedIndex].getAttribute("populate-value")); + const evt = new Event("change"); + + for (let i = 0; i < targets.length; i++) { + targets[i].value = vals[i]; + } + + // Fire change event after all targets have been assigned + this.manager.recipe.ingChange(); + + // Send change event for each target once all have been assigned, to update the label placement. + for (const target of targets) { + target.dispatchEvent(evt); + } + } + + /** * Handler for editable option clicks. * Populates the input box with the selected value. diff --git a/src/web/RecipeWaiter.mjs b/src/web/RecipeWaiter.mjs index a8326b27..4c568c8b 100755 --- a/src/web/RecipeWaiter.mjs +++ b/src/web/RecipeWaiter.mjs @@ -205,6 +205,7 @@ class RecipeWaiter { * @fires Manager#statechange */ ingChange(e) { + if (e && e.target && e.target.classList.contains("no-state-change")) return; window.dispatchEvent(this.manager.statechange); } @@ -392,6 +393,15 @@ class RecipeWaiter { this.buildRecipeOperation(item); document.getElementById("rec-list").appendChild(item); + // Trigger populateOption events + const populateOptions = item.querySelectorAll(".populate-option"); + const evt = new Event("change", {bubbles: true}); + if (populateOptions.length) { + for (const el of populateOptions) { + el.dispatchEvent(evt); + } + } + item.dispatchEvent(this.manager.operationadd); return item; } diff --git a/tests/operations/tests/MultipleBombe.mjs b/tests/operations/tests/MultipleBombe.mjs index f809859c..5c06ece4 100644 --- a/tests/operations/tests/MultipleBombe.mjs +++ b/tests/operations/tests/MultipleBombe.mjs @@ -16,9 +16,10 @@ TestRegister.addTests([ "op": "Multiple Bombe", "args": [ // I, II and III - "User defined", "EKMFLGDQVZNTOWYHXUSPAIBRCJ Date: Fri, 8 Feb 2019 11:53:58 +0000 Subject: [PATCH 097/113] Fixed Bombe svg animation in standalone version --- package-lock.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/package-lock.json b/package-lock.json index 57175720..14b14f1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12074,6 +12074,28 @@ "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, + "svg-url-loader": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/svg-url-loader/-/svg-url-loader-2.3.2.tgz", + "integrity": "sha1-3YaybBn+O5FPBOoQ7zlZTq3gRGQ=", + "dev": true, + "requires": { + "file-loader": "1.1.11", + "loader-utils": "1.1.0" + }, + "dependencies": { + "file-loader": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^0.4.5" + } + } + } + }, "symbol-tree": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", From 1079080f5c024b13fca415ed85cdfb92ecf3e506 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Fri, 8 Feb 2019 15:21:14 +0000 Subject: [PATCH 098/113] Bombe results are now presented in a table --- src/core/operations/Bombe.mjs | 31 +++++++++++++++---- src/core/operations/MultipleBombe.mjs | 43 +++++++++++++++++++++------ 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/src/core/operations/Bombe.mjs b/src/core/operations/Bombe.mjs index 34a94e53..6b277a03 100644 --- a/src/core/operations/Bombe.mjs +++ b/src/core/operations/Bombe.mjs @@ -26,7 +26,8 @@ class Bombe extends Operation { this.description = "Emulation of the Bombe machine used to attack Enigma.

To run this you need to have a 'crib', which is some known plaintext for a chunk of the target ciphertext, and know the rotors used. (See the 'Bombe (multiple runs)' operation if you don't know the rotors.) The machine will suggest possible configurations of the Enigma. Each suggestion has the rotor start positions (left to right) and known plugboard pairs.

Choosing a crib: First, note that Enigma cannot encrypt a letter to itself, which allows you to rule out some positions for possible cribs. Secondly, the Bombe does not simulate the Enigma's middle rotor stepping. The longer your crib, the more likely a step happened within it, which will prevent the attack working. However, other than that, longer cribs are generally better. The attack produces a 'menu' which maps ciphertext letters to plaintext, and the goal is to produce 'loops': for example, with ciphertext ABC and crib CAB, we have the mappings A<->C, B<->A, and C<->B, which produces a loop A-B-C-A. The more loops, the better the crib. The operation will output this: if your menu has too few loops, a large number of incorrect outputs will be produced. Try a different crib. If the menu seems good but the right answer isn't produced, your crib may be wrong, or you may have overlapped the middle rotor stepping - try a different crib.

Output is not sufficient to fully decrypt the data. You will have to recover the rest of the plugboard settings by inspection. And the ring position is not taken into account: this affects when the middle rotor steps. If your output is correct for a bit, and then goes wrong, adjust the ring and start position on the right-hand rotor together until the output improves. If necessary, repeat for the middle rotor.

By default this operation runs the checking machine, a manual process to verify the quality of Bombe stops, on each stop, discarding stops which fail. If you want to see how many times the hardware actually stops for a given input, disable the checking machine."; this.infoURL = "https://wikipedia.org/wiki/Bombe"; this.inputType = "string"; - this.outputType = "string"; + this.outputType = "JSON"; + this.presentType = "html"; this.args = [ { name: "1st (right-hand) rotor", @@ -82,7 +83,7 @@ class Bombe extends Operation { * @param {number} progress - Progress (as a float in the range 0..1) */ updateStatus(nLoops, nStops, progress) { - const msg = `Bombe run with ${nLoops} loops in menu (2+ desirable): ${nStops} stops, ${Math.floor(100 * progress)}% done`; + const msg = `Bombe run with ${nLoops} loop${nLoops === 1 ? "" : "s"} in menu (2+ desirable): ${nStops} stops, ${Math.floor(100 * progress)}% done`; self.sendStatusMessage(msg); } @@ -128,11 +129,29 @@ class Bombe extends Operation { } const bombe = new BombeMachine(rotors, reflector, ciphertext, crib, check, update); const result = bombe.run(); - let msg = `Bombe run on menu with ${bombe.nLoops} loops (2+ desirable). Note: Rotor positions are listed left to right and start at the beginning of the crib, and ignore stepping and the ring setting. Some plugboard settings are determined. A decryption preview starting at the beginning of the crib and ignoring stepping is also provided. Results:\n`; - for (const [setting, stecker, decrypt] of result) { - msg += `Stop: ${setting} (plugboard: ${stecker}): ${decrypt}\n`; + return { + nLoops: bombe.nLoops, + result: result + }; + } + + + /** + * Displays the Bombe results in an HTML table + * + * @param {Object} output + * @param {number} output.nLoops + * @param {Array[]} output.result + * @returns {html} + */ + present(output) { + let html = `Bombe run on menu with ${output.nLoops} loop${output.nLoops === 1 ? "" : "s"} (2+ desirable). Note: Rotor positions are listed left to right and start at the beginning of the crib, and ignore stepping and the ring setting. Some plugboard settings are determined. A decryption preview starting at the beginning of the crib and ignoring stepping is also provided.\n\n`; + html += ""; + for (const [setting, stecker, decrypt] of output.result) { + html += `\n`; } - return msg; + html += "
Rotor stopsPartial plugboardDecryption preview
${setting}${stecker}${decrypt}
"; + return html; } } diff --git a/src/core/operations/MultipleBombe.mjs b/src/core/operations/MultipleBombe.mjs index a453ca34..7a0ae2fd 100644 --- a/src/core/operations/MultipleBombe.mjs +++ b/src/core/operations/MultipleBombe.mjs @@ -56,7 +56,8 @@ class MultipleBombe extends Operation { this.description = "Emulation of the Bombe machine used to attack Enigma. This version carries out multiple Bombe runs to handle unknown rotor configurations.

You should test your menu on the single Bombe operation before running it here. See the description of the Bombe operation for instructions on choosing a crib."; this.infoURL = "https://wikipedia.org/wiki/Bombe"; this.inputType = "string"; - this.outputType = "string"; + this.outputType = "JSON"; + this.presentType = "html"; this.args = [ { "name": "Standard Enigmas", @@ -146,7 +147,7 @@ class MultipleBombe extends Operation { const hours = Math.floor(remaining / 3600); const minutes = `0${Math.floor((remaining % 3600) / 60)}`.slice(-2); const seconds = `0${Math.floor(remaining % 60)}`.slice(-2); - const msg = `Bombe run with ${nLoops} loops in menu (2+ desirable): ${nStops} stops, ${Math.floor(100 * progress)}% done, ${hours}:${minutes}:${seconds} remaining`; + const msg = `Bombe run with ${nLoops} loop${nLoops === 1 ? "" : "s"} in menu (2+ desirable): ${nStops} stops, ${Math.floor(100 * progress)}% done, ${hours}:${minutes}:${seconds} remaining`; self.sendStatusMessage(msg); } @@ -227,7 +228,7 @@ class MultipleBombe extends Operation { update = undefined; } let bombe = undefined; - let msg; + const output = {bombeRuns: []}; // I could use a proper combinatorics algorithm here... but it would be more code to // write one, and we don't seem to have one in our existing libraries, so massively nested // for loop it is @@ -253,7 +254,7 @@ class MultipleBombe extends Operation { } if (bombe === undefined) { bombe = new BombeMachine(runRotors, reflector, ciphertext, crib, check); - msg = `Bombe run on menu with ${bombe.nLoops} loops (2+ desirable). Note: Rotors and rotor positions are listed left to right, ignore stepping and the ring setting, and positions start at the beginning of the crib. Some plugboard settings are determined. A decryption preview starting at the beginning of the crib and ignoring stepping is also provided. Results:\n`; + output.nLoops = bombe.nLoops; } else { bombe.changeRotors(runRotors, reflector); } @@ -263,17 +264,41 @@ class MultipleBombe extends Operation { update(bombe.nLoops, nStops, nRuns / totalRuns, start); } if (result.length > 0) { - msg += `\nRotors: ${runRotors.join(", ")}\nReflector: ${reflector.pairs}\n`; - for (const [setting, stecker, decrypt] of result) { - msg += `Stop: ${setting} (plugboard: ${stecker}): ${decrypt}\n`; - } + output.bombeRuns.push({ + rotors: runRotors, + reflector: reflector.pairs, + result: result + }); } } } } } } - return msg; + return output; + } + + + /** + * Displays the MultiBombe results in an HTML table + * + * @param {Object} output + * @param {number} output.nLoops + * @param {Array[]} output.result + * @returns {html} + */ + present(output) { + let html = `Bombe run on menu with ${output.nLoops} loop${output.nLoops === 1 ? "" : "s"} (2+ desirable). Note: Rotors and rotor positions are listed left to right, ignore stepping and the ring setting, and positions start at the beginning of the crib. Some plugboard settings are determined. A decryption preview starting at the beginning of the crib and ignoring stepping is also provided.\n`; + + for (const run of output.bombeRuns) { + html += `\nRotors: ${run.rotors.join(", ")}\nReflector: ${run.reflector}\n`; + html += ""; + for (const [setting, stecker, decrypt] of run.result) { + html += `\n`; + } + html += "
Rotor stopsPartial plugboardDecryption preview
${setting}${stecker}${decrypt}
\n"; + } + return html; } } From 5a8255a9f488817f44791bbc33cf23eb6988cc92 Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Fri, 8 Feb 2019 19:25:28 +0000 Subject: [PATCH 099/113] Bombe: fix tests after output table patch --- tests/operations/tests/Bombe.mjs | 12 ++++++------ tests/operations/tests/MultipleBombe.mjs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/operations/tests/Bombe.mjs b/tests/operations/tests/Bombe.mjs index fca420d3..0f00f1be 100644 --- a/tests/operations/tests/Bombe.mjs +++ b/tests/operations/tests/Bombe.mjs @@ -11,7 +11,7 @@ TestRegister.addTests([ // Plugboard for this test is BO LC KE GA name: "Bombe: 3 rotor (self-stecker)", input: "BBYFLTHHYIJQAYBBYS", - expectedMatch: /LGA \(plugboard: SS\): VFISUSGTKSTMPSUNAK/, + expectedMatch: /LGA<\/td>SS<\/td>VFISUSGTKSTMPSUNAK<\/td>/, recipeConfig: [ { "op": "Bombe", @@ -30,7 +30,7 @@ TestRegister.addTests([ // This test produces a menu that doesn't use the first letter, which is also a good test name: "Bombe: 3 rotor (other stecker)", input: "JBYALIHDYNUAAVKBYM", - expectedMatch: /LGA \(plugboard: AG\): QFIMUMAFKMQSKMYNGW/, + expectedMatch: /LGA<\/td>AG<\/td>QFIMUMAFKMQSKMYNGW<\/td>/, recipeConfig: [ { "op": "Bombe", @@ -48,7 +48,7 @@ TestRegister.addTests([ { name: "Bombe: crib offset", input: "AAABBYFLTHHYIJQAYBBYS", // first three chars here are faked - expectedMatch: /LGA \(plugboard: SS\): VFISUSGTKSTMPSUNAK/, + expectedMatch: /LGA<\/td>SS<\/td>VFISUSGTKSTMPSUNAK<\/td>/, recipeConfig: [ { "op": "Bombe", @@ -66,7 +66,7 @@ TestRegister.addTests([ { name: "Bombe: multiple stops", input: "BBYFLTHHYIJQAYBBYS", - expectedMatch: /LGA \(plugboard: TT\): VFISUSGTKSTMPSUNAK/, + expectedMatch: /LGA<\/td>TT<\/td>VFISUSGTKSTMPSUNAK<\/td>/, recipeConfig: [ { "op": "Bombe", @@ -84,7 +84,7 @@ TestRegister.addTests([ { name: "Bombe: checking machine", input: "BBYFLTHHYIJQAYBBYS", - expectedMatch: /Stop: LGA \(plugboard: TT AG BO CL EK FF HH II JJ SS YY\): THISISATESTMESSAGE/, + expectedMatch: /LGA<\/td>TT AG BO CL EK FF HH II JJ SS YY<\/td>THISISATESTMESSAGE<\/td>/, recipeConfig: [ { "op": "Bombe", @@ -103,7 +103,7 @@ TestRegister.addTests([ { name: "Bombe: 4 rotor", input: "LUOXGJSHGEDSRDOQQX", - expectedMatch: /LHSC \(plugboard: SS\): HHHSSSGQUUQPKSEKWK/, + expectedMatch: /LHSC<\/td>SS<\/td>HHHSSSGQUUQPKSEKWK<\/td>/, recipeConfig: [ { "op": "Bombe", diff --git a/tests/operations/tests/MultipleBombe.mjs b/tests/operations/tests/MultipleBombe.mjs index 5c06ece4..8e2cc685 100644 --- a/tests/operations/tests/MultipleBombe.mjs +++ b/tests/operations/tests/MultipleBombe.mjs @@ -10,7 +10,7 @@ TestRegister.addTests([ { name: "Multi-Bombe: 3 rotor", input: "BBYFLTHHYIJQAYBBYS", - expectedMatch: /LGA \(plugboard: SS\): VFISUSGTKSTMPSUNAK/, + expectedMatch: /LGA<\/td>SS<\/td>VFISUSGTKSTMPSUNAK<\/td>/, recipeConfig: [ { "op": "Multiple Bombe", @@ -30,7 +30,7 @@ TestRegister.addTests([ { name: "Multi-Bombe: 4 rotor", input: "LUOXGJSHGEDSRDOQQX", - expectedMatch: /LHSC \(plugboard: SS\): HHHSSSGQUUQPKSEKWK/, + expectedMatch: /LHSC<\/td>SS<\/td>HHHSSSGQUUQPKSEKWK<\/td>/, recipeConfig: [ { "op": "Multiple Bombe", From 61fee3122a5eac968ecaa5fa4ed6107581ba07f8 Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Fri, 8 Feb 2019 21:16:42 +0000 Subject: [PATCH 100/113] Bombe: add Rebuild Project to authors --- src/core/lib/Bombe.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index 03413350..a4cf24f4 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -2,6 +2,7 @@ * Emulation of the Bombe machine. * * @author s2224834 + * @author The National Museum of Computing - Bombe Rebuild Project * @copyright Crown Copyright 2019 * @license Apache-2.0 */ From 069d4956aac93021bb1984eb618379c74062da37 Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Sat, 9 Feb 2019 22:57:57 +0000 Subject: [PATCH 101/113] Bombe: Handle boxing stop correctly --- src/core/lib/Bombe.mjs | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index a4cf24f4..ef796cd0 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -651,12 +651,34 @@ export class BombeMachine { // This means our hypothesis for the steckering is correct. steckerPair = this.testInput[1]; } else { - // If this happens a lot it implies the menu isn't good enough. We can't do - // anything useful with it as we don't have a stecker partner, so we'll just drop it - // and move on. This does risk eating the actual stop occasionally, but I've only seen - // this happen when the menu is bad enough we have thousands of stops, so I'm not sure - // it matters. - return undefined; + // This was known as a "boxing stop" - we have a stop but not a single hypothesis. + // If this happens a lot it implies the menu isn't good enough. + // If we have the checking machine enabled, we're going to just check each wire in + // turn. If we get 0 or 1 hit, great. + // If we get multiple hits, or the checking machine is off, the user will just have to + // deal with it. + if (!this.check) { + // We can't draw any conclusions about the steckering (one could maybe suggest + // options in some cases, but too hard to present clearly). + return [this.indicator.getPos(), "??", this.tryDecrypt("")]; + } + let stecker = undefined; + for (let i = 0; i < 26; i++) { + const newStecker = this.checkingMachine(i); + if (newStecker !== "") { + if (stecker !== undefined) { + // Multiple hypotheses can't be ruled out. + return [this.indicator.getPos(), "??", this.tryDecrypt("")]; + } + stecker = newStecker; + } + } + if (stecker === undefined) { + // Checking machine ruled all possibilities out. + return undefined; + } + // If we got here, there was just one possibility allowed by the checking machine. Success. + return [this.indicator.getPos(), stecker, this.tryDecrypt(stecker)]; } let stecker; if (this.check) { From dd9cbbac77ef5a8c8e85b14215b2501aad906b5b Mon Sep 17 00:00:00 2001 From: s2224834 <46319860+s2224834@users.noreply.github.com> Date: Sat, 9 Feb 2019 23:01:52 +0000 Subject: [PATCH 102/113] Bombe: add note about rotor step in crib --- src/core/lib/Bombe.mjs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/core/lib/Bombe.mjs b/src/core/lib/Bombe.mjs index ef796cd0..122edd40 100644 --- a/src/core/lib/Bombe.mjs +++ b/src/core/lib/Bombe.mjs @@ -281,6 +281,14 @@ export class BombeMachine { * ciphertext. It will check that the crib is sane (length is vaguely sensible and there's no * matching characters between crib and ciphertext) but cannot check further - if it's wrong * your results will be wrong! + * + * There is also no handling of rotor stepping - if the target Enigma stepped in the middle of + * your crib, you're out of luck. TODO: Allow specifying a step point - this is fairly easy to + * configure on a real Bombe, but we're not clear on whether it was ever actually done for + * real (there would almost certainly have been better ways of attacking in most situations + * than attempting to exhaust options for the stepping point, but in some circumstances, e.g. + * via Banburismus, the stepping point might have been known). + * * @param {string[]} rotors - list of rotor spec strings (without step points!) * @param {Object} reflector - Reflector object * @param {string} ciphertext - The ciphertext to attack From 4db6199fd947e9a06d6bf59c3ea54070054cd273 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Sun, 10 Feb 2019 21:00:36 +0000 Subject: [PATCH 103/113] Fixed timings for Bombe animation fast rotor --- src/web/static/images/bombe.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/web/static/images/bombe.svg b/src/web/static/images/bombe.svg index 1fdca842..1fd40554 100644 --- a/src/web/static/images/bombe.svg +++ b/src/web/static/images/bombe.svg @@ -23,7 +23,7 @@ const bbox = rotor.getBBox(); const x = bbox.width/2 + bbox.x; const y = bbox.height/2 + bbox.y; - const wait = row === 0 ? speed/26 : row === 1 ? speed : speed*26; + const wait = row === 0 ? speed/26/1.5 : row === 1 ? speed : speed*26; rotor.setAttribute("transform", "rotate(" + startPos + ", " + x + ", " + y + ")"); @@ -50,7 +50,7 @@ break; } } - }, speed/26 - 5); + }, speed/26/1.5 - 5); } // ]]> From d22eac9f357a81fc7863d4af131dd7985a79c98e Mon Sep 17 00:00:00 2001 From: d98762625 Date: Mon, 11 Feb 2019 14:29:29 +0000 Subject: [PATCH 104/113] WIP getting prod working with minicss webpack plugin --- Gruntfile.js | 3 ++- src/web/RecipeWaiter.mjs | 31 ++++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index dd890982..0f6208c8 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -194,7 +194,8 @@ module.exports = function (grunt) { sitemap: "./src/web/static/sitemap.js" }, moduleEntryPoints), output: { - path: __dirname + "/build/prod" + path: __dirname + "/build/prod", + globalObject: "this" }, resolve: { alias: { diff --git a/src/web/RecipeWaiter.mjs b/src/web/RecipeWaiter.mjs index 2b0b0a05..d12005b6 100755 --- a/src/web/RecipeWaiter.mjs +++ b/src/web/RecipeWaiter.mjs @@ -8,6 +8,8 @@ import HTMLOperation from "./HTMLOperation"; import Sortable from "sortablejs"; import Utils from "../core/Utils"; +/*eslint no-console: ["off"] */ + /** * Waiter to handle events related to the recipe. @@ -561,20 +563,35 @@ class RecipeWaiter { this.ingredientChildRuleID = null; // Find relevant rules in the stylesheet - for (const i in document.styleSheets[0].cssRules) { - if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients") { - this.ingredientRuleID = i; - } - if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients > div") { - this.ingredientChildRuleID = i; + // try { + if (document.styleSheets[0].hasOwnProperty("cssRules")) { + for (const i in document.styleSheets[0].cssRules) { + if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients") { + this.ingredientRuleID = i; + } + if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients > div") { + this.ingredientChildRuleID = i; + } } } + // } catch (e) { + // console.log(e.constructor); + // console.warn("Can't read the css rules of: " + document.styleSheets[0], e); + // } } if (!this.ingredientRuleID || !this.ingredientChildRuleID) return; + let ingredientRule, ingredientChildRule; - const ingredientRule = document.styleSheets[0].cssRules[this.ingredientRuleID], + // Chrome 64+ + // try { + if (document.styleSheets[0].hasOwnProperty("cssRules")) { + ingredientRule = document.styleSheets[0].cssRules[this.ingredientRuleID]; ingredientChildRule = document.styleSheets[0].cssRules[this.ingredientChildRuleID]; + } + // } catch (e) { + // console.warn("Can't read the css rules of: " + document.styleSheets[0], e); + // } if (recList.clientWidth < 450) { ingredientRule.style.gridTemplateColumns = "auto auto"; From 13abbd2c5d6576b706b997ea00897829e3218636 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Mon, 11 Feb 2019 14:48:25 +0000 Subject: [PATCH 105/113] Abstracted out loadFile logic to separate function --- src/web/InputWaiter.mjs | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/web/InputWaiter.mjs b/src/web/InputWaiter.mjs index 245891f7..17b48b6f 100755 --- a/src/web/InputWaiter.mjs +++ b/src/web/InputWaiter.mjs @@ -243,11 +243,7 @@ class InputWaiter { } if (file) { - this.closeFile(); - this.loaderWorker = new LoaderWorker(); - this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this)); - this.loaderWorker.postMessage({"file": file}); - this.set(file); + this.loadFile(file); } } @@ -259,16 +255,8 @@ class InputWaiter { */ inputOpen(e) { e.preventDefault(); - const file = e.srcElement.files[0]; - - if (file) { - this.closeFile(); - this.loaderWorker = new LoaderWorker(); - this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this)); - this.loaderWorker.postMessage({"file": file}); - this.set(file); - } + this.loadFile(file); } @@ -326,6 +314,22 @@ class InputWaiter { } + /** + * Loads a file into the input. + * + * @param {File} file + */ + loadFile(file) { + if (file) { + this.closeFile(); + this.loaderWorker = new LoaderWorker(); + this.loaderWorker.addEventListener("message", this.handleLoaderMessage.bind(this)); + this.loaderWorker.postMessage({"file": file}); + this.set(file); + } + } + + /** * Handler for clear IO events. * Resets the input, output and info areas. From b5c655dd70c096e2053625a774146b80c28d3489 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Mon, 11 Feb 2019 14:48:45 +0000 Subject: [PATCH 106/113] 8.24.1 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1340f4ba..257745b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.24.0", + "version": "8.24.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 8cb785a2..2828a5d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.24.0", + "version": "8.24.1", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From a7a2fe243adbbd7c7bfee0b4ade564148c9ccd22 Mon Sep 17 00:00:00 2001 From: d98762625 Date: Mon, 11 Feb 2019 15:40:18 +0000 Subject: [PATCH 107/113] prod working with mini css webpack plugin --- Gruntfile.js | 3 +++ src/web/RecipeWaiter.mjs | 31 ++++++++----------------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 0f6208c8..4a7bb990 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -300,6 +300,9 @@ module.exports = function (grunt) { "./config/modules/OpModules": "./config/modules/Default" } }, + output: { + globalObject: "this", + }, plugins: [ new webpack.DefinePlugin(BUILD_CONSTANTS), new HtmlWebpackPlugin({ diff --git a/src/web/RecipeWaiter.mjs b/src/web/RecipeWaiter.mjs index d12005b6..e533b17c 100755 --- a/src/web/RecipeWaiter.mjs +++ b/src/web/RecipeWaiter.mjs @@ -563,35 +563,20 @@ class RecipeWaiter { this.ingredientChildRuleID = null; // Find relevant rules in the stylesheet - // try { - if (document.styleSheets[0].hasOwnProperty("cssRules")) { - for (const i in document.styleSheets[0].cssRules) { - if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients") { - this.ingredientRuleID = i; - } - if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients > div") { - this.ingredientChildRuleID = i; - } + for (const i in document.styleSheets[0].cssRules) { + if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients") { + this.ingredientRuleID = i; + } + if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients > div") { + this.ingredientChildRuleID = i; } } - // } catch (e) { - // console.log(e.constructor); - // console.warn("Can't read the css rules of: " + document.styleSheets[0], e); - // } } if (!this.ingredientRuleID || !this.ingredientChildRuleID) return; - let ingredientRule, ingredientChildRule; - // Chrome 64+ - // try { - if (document.styleSheets[0].hasOwnProperty("cssRules")) { - ingredientRule = document.styleSheets[0].cssRules[this.ingredientRuleID]; - ingredientChildRule = document.styleSheets[0].cssRules[this.ingredientChildRuleID]; - } - // } catch (e) { - // console.warn("Can't read the css rules of: " + document.styleSheets[0], e); - // } + const ingredientRule = document.styleSheets[0].cssRules[this.ingredientRuleID]; + const ingredientChildRule = document.styleSheets[0].cssRules[this.ingredientChildRuleID]; if (recList.clientWidth < 450) { ingredientRule.style.gridTemplateColumns = "auto auto"; From 2391e08ac13cb05b4a2ac7ac17bc20c88fc4a401 Mon Sep 17 00:00:00 2001 From: d98762625 Date: Mon, 11 Feb 2019 15:47:28 +0000 Subject: [PATCH 108/113] tidy up --- src/core/Recipe.mjs | 3 --- src/web/RecipeWaiter.mjs | 2 -- webpack.config.js | 18 ------------------ 3 files changed, 23 deletions(-) diff --git a/src/core/Recipe.mjs b/src/core/Recipe.mjs index 48ec6c97..ae963844 100755 --- a/src/core/Recipe.mjs +++ b/src/core/Recipe.mjs @@ -185,11 +185,8 @@ class Recipe { log.debug(`[*] Executing recipe of ${this.opList.length} operations, starting at ${startFrom}`); for (let i = startFrom; i < this.opList.length; i++) { - op = this.opList[i]; - log.debug(`[${i}] ${op.name} ${JSON.stringify(op.ingValues)}`); - if (op.disabled) { log.debug("Operation is disabled, skipping"); continue; diff --git a/src/web/RecipeWaiter.mjs b/src/web/RecipeWaiter.mjs index e533b17c..85598609 100755 --- a/src/web/RecipeWaiter.mjs +++ b/src/web/RecipeWaiter.mjs @@ -8,8 +8,6 @@ import HTMLOperation from "./HTMLOperation"; import Sortable from "sortablejs"; import Utils from "../core/Utils"; -/*eslint no-console: ["off"] */ - /** * Waiter to handle events related to the recipe. diff --git a/webpack.config.js b/webpack.config.js index 137cf661..800945f2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -56,24 +56,6 @@ module.exports = { jquery: "jquery/src/jquery" } }, - // optimization: { - // splitChunks: { - // cacheGroups: { - // styles: { - // name: "styles", - // test: /\.css$/, - // chunks: "all", - // enforce: true - // }, - // vendor: { - // name: "vendor", - // test: /\.scss$/, - // chunks: "all", - // enforce: true, - // }, - // } - // } - // }, module: { rules: [ { From c01c0765618d669191404c927b1d2c9011979a89 Mon Sep 17 00:00:00 2001 From: d98762625 Date: Mon, 11 Feb 2019 16:46:54 +0000 Subject: [PATCH 109/113] try/catch to allow chef to run from prod file --- src/web/RecipeWaiter.mjs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/web/RecipeWaiter.mjs b/src/web/RecipeWaiter.mjs index 85598609..98457bb3 100755 --- a/src/web/RecipeWaiter.mjs +++ b/src/web/RecipeWaiter.mjs @@ -561,13 +561,18 @@ class RecipeWaiter { this.ingredientChildRuleID = null; // Find relevant rules in the stylesheet - for (const i in document.styleSheets[0].cssRules) { - if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients") { - this.ingredientRuleID = i; - } - if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients > div") { - this.ingredientChildRuleID = i; + // try/catch for chrome 64+ CORS error on cssRules. + try { + for (const i in document.styleSheets[0].cssRules) { + if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients") { + this.ingredientRuleID = i; + } + if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients > div") { + this.ingredientChildRuleID = i; + } } + } catch (e) { + // Do nothing. } } From 49e2b05a11bfbadff3a0023e01603c71fcefe018 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Mon, 11 Feb 2019 18:44:41 +0000 Subject: [PATCH 110/113] Dynamic module loading is now cached and Webpack includes the import in the main chunk. --- Gruntfile.js | 4 +- package-lock.json | 138 ++++++++++++++++++++++---------------------- src/core/Recipe.mjs | 26 +++++---- webpack.config.js | 2 +- 4 files changed, 85 insertions(+), 85 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 4a7bb990..11b77452 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -395,9 +395,7 @@ module.exports = function (grunt) { generateConfig: { command: [ "echo '\n--- Regenerating config files. ---'", - "mkdir -p src/core/config/modules", - "echo 'export default {};\n' > src/core/config/modules/OpModules.mjs", - "echo '[]\n' > src/core/config/OperationConfig.json", + "echo [] > src/core/config/OperationConfig.json", "node --experimental-modules --no-warnings --no-deprecation src/core/config/scripts/generateOpsIndex.mjs", "node --experimental-modules --no-warnings --no-deprecation src/core/config/scripts/generateConfig.mjs", "echo '--- Config scripts finished. ---\n'" diff --git a/package-lock.json b/package-lock.json index 025b76be..851df7c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1467,7 +1467,7 @@ "dependencies": { "es6-promisify": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { @@ -1631,7 +1631,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -1864,7 +1864,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "dev": true, "requires": { @@ -2334,7 +2334,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2371,7 +2371,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2436,7 +2436,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2590,7 +2590,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -2639,7 +2639,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -3172,7 +3172,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -3185,7 +3185,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -3279,7 +3279,7 @@ }, "json5": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { @@ -3332,7 +3332,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -3355,7 +3355,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true }, @@ -3528,7 +3528,7 @@ }, "deep-eql": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "resolved": "http://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", "dev": true, "requires": { @@ -3700,7 +3700,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -3764,7 +3764,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true }, @@ -3823,7 +3823,7 @@ }, "duplexer": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, @@ -3969,7 +3969,7 @@ }, "entities": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/entities/-/entities-1.0.0.tgz", "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", "dev": true }, @@ -4392,7 +4392,7 @@ }, "eventemitter2": { "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "resolved": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", "dev": true }, @@ -4404,7 +4404,7 @@ }, "events": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, @@ -5057,7 +5057,7 @@ }, "fs-extra": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", "dev": true, "requires": { @@ -5726,7 +5726,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -5868,7 +5868,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -5945,7 +5945,7 @@ }, "grunt-cli": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", "dev": true, "requires": { @@ -5993,7 +5993,7 @@ "dependencies": { "shelljs": { "version": "0.5.3", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", + "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", "integrity": "sha1-xUmCuZbHbvDB5rWfvcWCX1txMRM=", "dev": true } @@ -6013,7 +6013,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -6058,7 +6058,7 @@ }, "grunt-contrib-jshint": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=", "dev": true, "requires": { @@ -6482,7 +6482,7 @@ }, "html-webpack-plugin": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", "dev": true, "requires": { @@ -6538,7 +6538,7 @@ }, "htmlparser2": { "version": "3.8.3", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", "dev": true, "requires": { @@ -6557,7 +6557,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -6607,7 +6607,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -7053,7 +7053,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -7614,7 +7614,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -7725,7 +7725,7 @@ }, "kew": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "resolved": "http://registry.npmjs.org/kew/-/kew-0.7.0.tgz", "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", "dev": true }, @@ -7844,7 +7844,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -7857,7 +7857,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -8221,7 +8221,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -8280,7 +8280,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -8427,7 +8427,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mississippi": { @@ -8501,7 +8501,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -8509,7 +8509,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } @@ -8542,7 +8542,7 @@ "dependencies": { "commander": { "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true, "optional": true @@ -8711,7 +8711,7 @@ }, "ncp": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=", "dev": true }, @@ -8810,7 +8810,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true } @@ -8993,7 +8993,7 @@ "dependencies": { "colors": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz", "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" }, "underscore": { @@ -9287,13 +9287,13 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -9302,7 +9302,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -9526,7 +9526,7 @@ }, "parse-asn1": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { @@ -9612,7 +9612,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -9653,7 +9653,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -9836,7 +9836,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -10207,7 +10207,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" }, "promise-inflight": { @@ -10476,7 +10476,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -10665,7 +10665,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -10716,7 +10716,7 @@ }, "htmlparser2": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "dev": true, "requires": { @@ -10728,7 +10728,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -10995,7 +10995,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -11315,7 +11315,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -11359,7 +11359,7 @@ }, "shelljs": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", "dev": true }, @@ -12080,7 +12080,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -12097,7 +12097,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -12190,7 +12190,7 @@ }, "tar": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { @@ -12348,7 +12348,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -13008,7 +13008,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -13034,7 +13034,7 @@ }, "valid-data-url": { "version": "0.1.6", - "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", + "resolved": "http://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", "integrity": "sha512-FXg2qXMzfAhZc0y2HzELNfUeiOjPr+52hU1DNBWiJJ2luXD+dD1R9NA48Ug5aj0ibbxroeGDc/RJv6ThiGgkDw==", "dev": true }, @@ -13050,7 +13050,7 @@ }, "validator": { "version": "9.4.1", - "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "resolved": "http://registry.npmjs.org/validator/-/validator-9.4.1.tgz", "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==", "dev": true }, @@ -13582,7 +13582,7 @@ }, "webpack-node-externals": { "version": "1.7.2", - "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", + "resolved": "http://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz", "integrity": "sha512-ajerHZ+BJKeCLviLUUmnyd5B4RavLF76uv3cs6KNuO8W+HuQaEs0y0L7o40NQxdPy5w0pcv8Ew7yPUAQG0UdCg==", "dev": true }, @@ -13776,7 +13776,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { diff --git a/src/core/Recipe.mjs b/src/core/Recipe.mjs index ae963844..a11b4d02 100755 --- a/src/core/Recipe.mjs +++ b/src/core/Recipe.mjs @@ -10,6 +10,9 @@ import Operation from "./Operation"; import DishError from "./errors/DishError"; import log from "loglevel"; +// Cache container for modules +let modules = null; + /** * The Recipe controls a list of Operations and the Dish they operate on. */ @@ -36,7 +39,7 @@ class Recipe { * @param {Object} recipeConfig */ _parseConfig(recipeConfig) { - recipeConfig.forEach((c) => { + recipeConfig.forEach(c => { this.opList.push({ name: c.op, module: OperationConfig[c.op].module, @@ -50,15 +53,19 @@ class Recipe { /** * Populate elements of opList with operation instances. - * Dynamic import here removes top-level cyclic dependency issue.For + * Dynamic import here removes top-level cyclic dependency issue. * * @private */ async _hydrateOpList() { - let modules = await import("./config/modules/OpModules"); - modules = modules.default; + if (!modules) { + // Using Webpack Magic Comments to force the dynamic import to be included in the main chunk + // https://webpack.js.org/api/module-methods/ + modules = await import(/* webpackMode: "eager" */ "./config/modules/OpModules"); + modules = modules.default; + } - this.opList = this.opList.map((o) => { + this.opList = this.opList.map(o => { if (o instanceof Operation) { return o; } else { @@ -69,7 +76,6 @@ class Recipe { return op; } }); - } @@ -102,13 +108,10 @@ class Recipe { * @param {Operation[]} operations */ addOperations(operations) { - operations.forEach((o) => { + operations.forEach(o => { if (o instanceof Operation) { - this.opList.push(o); - } else { - this.opList.push({ name: o.name, module: o.module, @@ -116,7 +119,6 @@ class Recipe { breakpoint: o.breakpoint, disabled: o.disabled, }); - } }); } @@ -151,7 +153,7 @@ class Recipe { /** - * Returns true if there is an Flow Control Operation in this Recipe. + * Returns true if there is a Flow Control Operation in this Recipe. * * @returns {boolean} */ diff --git a/webpack.config.js b/webpack.config.js index 800945f2..054152b2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -48,7 +48,7 @@ module.exports = { "process.browser": "true" }), new MiniCssExtractPlugin({ - filename: "[name].css", + filename: "[name].css" }), ], resolve: { From 8148c1a8a8d046e0df9004413b345d25b20ec7f9 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Mon, 11 Feb 2019 18:45:27 +0000 Subject: [PATCH 111/113] 8.24.2 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 851df7c7..55ad6303 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.24.1", + "version": "8.24.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index fae8312c..cb59db38 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "8.24.1", + "version": "8.24.2", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", From c005c86c276eb8a9f16ebb82b21ce1cb94779c66 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Thu, 28 Feb 2019 15:27:35 +0000 Subject: [PATCH 112/113] Added argSelector ingredient type and reversed rotors in Enigma and Bombe operations. --- src/core/operations/Bombe.mjs | 57 ++++++--- src/core/operations/Enigma.mjs | 135 +++++++++++--------- src/web/App.mjs | 3 + src/web/HTMLIngredient.mjs | 48 ++++++++ src/web/RecipeWaiter.mjs | 28 +++-- tests/operations/tests/Bombe.mjs | 84 +++++++------ tests/operations/tests/Enigma.mjs | 198 +++++++++++++++++------------- 7 files changed, 344 insertions(+), 209 deletions(-) diff --git a/src/core/operations/Bombe.mjs b/src/core/operations/Bombe.mjs index 6b277a03..ea3210fa 100644 --- a/src/core/operations/Bombe.mjs +++ b/src/core/operations/Bombe.mjs @@ -30,28 +30,42 @@ class Bombe extends Operation { this.presentType = "html"; this.args = [ { - name: "1st (right-hand) rotor", - type: "editableOption", - value: ROTORS, - defaultIndex: 2 + name: "Model", + type: "argSelector", + value: [ + { + name: "3-rotor", + off: [1] + }, + { + name: "4-rotor", + on: [1] + } + ] }, { - name: "2nd (middle) rotor", + name: "Left-most rotor", + type: "editableOption", + value: ROTORS_FOURTH, + defaultIndex: 0 + }, + { + name: "Left-hand rotor", + type: "editableOption", + value: ROTORS, + defaultIndex: 0 + }, + { + name: "Middle rotor", type: "editableOption", value: ROTORS, defaultIndex: 1 }, { - name: "3rd (left-hand) rotor", + name: "Right-hand rotor", type: "editableOption", value: ROTORS, - defaultIndex: 0 - }, - { - name: "4th (left-most, only some models) rotor", - type: "editableOption", - value: ROTORS_FOURTH, - defaultIndex: 0 + defaultIndex: 2 }, { name: "Reflector", @@ -93,23 +107,26 @@ class Bombe extends Operation { * @returns {string} */ run(input, args) { - const reflectorstr = args[4]; - let crib = args[5]; - const offset = args[6]; - const check = args[7]; + const model = args[0]; + const reflectorstr = args[5]; + let crib = args[6]; + const offset = args[7]; + const check = args[8]; const rotors = []; for (let i=0; i<4; i++) { - if (i === 3 && args[i] === "") { + if (i === 0 && model === "3-rotor") { // No fourth rotor - break; + continue; } - let rstr = args[i]; + let rstr = args[i + 1]; // The Bombe doesn't take stepping into account so we'll just ignore it here if (rstr.includes("<")) { rstr = rstr.split("<", 2)[0]; } rotors.push(rstr); } + // Rotors are handled in reverse + rotors.reverse(); if (crib.length === 0) { throw new OperationError("Crib cannot be empty"); } diff --git a/src/core/operations/Enigma.mjs b/src/core/operations/Enigma.mjs index 4af79993..ace50604 100644 --- a/src/core/operations/Enigma.mjs +++ b/src/core/operations/Enigma.mjs @@ -28,67 +28,81 @@ class Enigma extends Operation { this.outputType = "string"; this.args = [ { - name: "1st (right-hand) rotor", + name: "Model", + type: "argSelector", + value: [ + { + name: "3-rotor", + off: [1, 2, 3] + }, + { + name: "4-rotor", + on: [1, 2, 3] + } + ] + }, + { + name: "Left-most rotor", + type: "editableOption", + value: ROTORS_FOURTH, + defaultIndex: 0 + }, + { + name: "Left-most rotor ring setting", + type: "option", + value: LETTERS + }, + { + name: "Left-most rotor initial value", + type: "option", + value: LETTERS + }, + { + name: "Left-hand rotor", + type: "editableOption", + value: ROTORS, + defaultIndex: 0 + }, + { + name: "Left-hand rotor ring setting", + type: "option", + value: LETTERS + }, + { + name: "Left-hand rotor initial value", + type: "option", + value: LETTERS + }, + { + name: "Middle rotor", + type: "editableOption", + value: ROTORS, + defaultIndex: 1 + }, + { + name: "Middle rotor ring setting", + type: "option", + value: LETTERS + }, + { + name: "Middle rotor initial value", + type: "option", + value: LETTERS + }, + { + name: "Right-hand rotor", type: "editableOption", value: ROTORS, // Default config is the rotors I-III *left to right* defaultIndex: 2 }, { - name: "1st rotor ring setting", + name: "Right-hand rotor ring setting", type: "option", value: LETTERS }, { - name: "1st rotor initial value", - type: "option", - value: LETTERS - }, - { - name: "2nd (middle) rotor", - type: "editableOption", - value: ROTORS, - defaultIndex: 1 - }, - { - name: "2nd rotor ring setting", - type: "option", - value: LETTERS - }, - { - name: "2nd rotor initial value", - type: "option", - value: LETTERS - }, - { - name: "3rd (left-hand) rotor", - type: "editableOption", - value: ROTORS, - defaultIndex: 0 - }, - { - name: "3rd rotor ring setting", - type: "option", - value: LETTERS - }, - { - name: "3rd rotor initial value", - type: "option", - value: LETTERS - }, - { - name: "4th (left-most, only some models) rotor", - type: "editableOption", - value: ROTORS_FOURTH, - defaultIndex: 0 - }, - { - name: "4th rotor ring setting", - type: "option", - value: LETTERS - }, - { - name: "4th rotor initial value", + name: "Right-hand rotor initial value", type: "option", value: LETTERS }, @@ -135,18 +149,21 @@ class Enigma extends Operation { * @returns {string} */ run(input, args) { - const reflectorstr = args[12]; - const plugboardstr = args[13]; - const removeOther = args[14]; + const model = args[0]; + const reflectorstr = args[13]; + const plugboardstr = args[14]; + const removeOther = args[15]; const rotors = []; for (let i=0; i<4; i++) { - if (i === 3 && args[i*3] === "") { - // No fourth rotor - break; + if (i === 0 && model === "3-rotor") { + // Skip the 4th rotor settings + continue; } - const [rotorwiring, rotorsteps] = this.parseRotorStr(args[i*3], 1); - rotors.push(new Rotor(rotorwiring, rotorsteps, args[i*3 + 1], args[i*3 + 2])); + const [rotorwiring, rotorsteps] = this.parseRotorStr(args[i*3 + 1], 1); + rotors.push(new Rotor(rotorwiring, rotorsteps, args[i*3 + 2], args[i*3 + 3])); } + // Rotors are handled in reverse + rotors.reverse(); const reflector = new Reflector(reflectorstr); const plugboard = new Plugboard(plugboardstr); if (removeOther) { diff --git a/src/web/App.mjs b/src/web/App.mjs index e203b85c..04846fb6 100755 --- a/src/web/App.mjs +++ b/src/web/App.mjs @@ -472,6 +472,7 @@ class App { const item = this.manager.recipe.addOperation(recipeConfig[i].op); // Populate arguments + log.debug(`Populating arguments for ${recipeConfig[i].op}`); const args = item.querySelectorAll(".arg"); for (let j = 0; j < args.length; j++) { if (recipeConfig[i].args[j] === undefined) continue; @@ -497,6 +498,8 @@ class App { item.querySelector(".breakpoint").click(); } + this.manager.recipe.triggerArgEvents(item); + this.progress = 0; } diff --git a/src/web/HTMLIngredient.mjs b/src/web/HTMLIngredient.mjs index c7c024fb..19c816ea 100755 --- a/src/web/HTMLIngredient.mjs +++ b/src/web/HTMLIngredient.mjs @@ -240,6 +240,27 @@ class HTMLIngredient { ${this.hint ? "" + this.hint + "" : ""}
`; break; + case "argSelector": + html += `
+ + + ${this.hint ? "" + this.hint + "" : ""} +
`; + + this.manager.addDynamicListener(".arg-selector", "change", this.argSelectorChange, this); + break; default: break; } @@ -321,6 +342,33 @@ class HTMLIngredient { this.manager.recipe.ingChange(); } + + /** + * Handler for argument selector changes. + * Shows or hides the relevant arguments for this operation. + * + * @param {event} e + */ + argSelectorChange(e) { + e.preventDefault(); + e.stopPropagation(); + + const option = e.target.options[e.target.selectedIndex]; + const op = e.target.closest(".operation"); + const args = op.querySelectorAll(".ingredients .form-group"); + const turnon = JSON.parse(option.getAttribute("turnon")); + const turnoff = JSON.parse(option.getAttribute("turnoff")); + + args.forEach((arg, i) => { + if (turnon.includes(i)) { + arg.classList.remove("d-none"); + } + if (turnoff.includes(i)) { + arg.classList.add("d-none"); + } + }); + } + } export default HTMLIngredient; diff --git a/src/web/RecipeWaiter.mjs b/src/web/RecipeWaiter.mjs index 4c568c8b..4eca4af7 100755 --- a/src/web/RecipeWaiter.mjs +++ b/src/web/RecipeWaiter.mjs @@ -393,15 +393,6 @@ class RecipeWaiter { this.buildRecipeOperation(item); document.getElementById("rec-list").appendChild(item); - // Trigger populateOption events - const populateOptions = item.querySelectorAll(".populate-option"); - const evt = new Event("change", {bubbles: true}); - if (populateOptions.length) { - for (const el of populateOptions) { - el.dispatchEvent(evt); - } - } - item.dispatchEvent(this.manager.operationadd); return item; } @@ -439,6 +430,23 @@ class RecipeWaiter { } + /** + * Triggers various change events for operation arguments that have just been initialised. + * + * @param {HTMLElement} op + */ + triggerArgEvents(op) { + // Trigger populateOption and argSelector events + const triggerableOptions = op.querySelectorAll(".populate-option, .arg-selector"); + const evt = new Event("change", {bubbles: true}); + if (triggerableOptions.length) { + for (const el of triggerableOptions) { + el.dispatchEvent(evt); + } + } + } + + /** * Handler for operationadd events. * @@ -448,6 +456,8 @@ class RecipeWaiter { */ opAdd(e) { log.debug(`'${e.target.querySelector(".op-title").textContent}' added to recipe`); + + this.triggerArgEvents(e.target); window.dispatchEvent(this.manager.statechange); } diff --git a/tests/operations/tests/Bombe.mjs b/tests/operations/tests/Bombe.mjs index 0f00f1be..9e5a79c6 100644 --- a/tests/operations/tests/Bombe.mjs +++ b/tests/operations/tests/Bombe.mjs @@ -16,10 +16,11 @@ TestRegister.addTests([ { "op": "Bombe", "args": [ - "BDFHJLCPRTXVZNYEIWGAKMUSQO Date: Thu, 28 Feb 2019 16:56:28 +0000 Subject: [PATCH 113/113] Tweaks for new rotor order --- src/core/lib/Enigma.mjs | 1 - src/core/operations/Bombe.mjs | 2 +- src/core/operations/Enigma.mjs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/lib/Enigma.mjs b/src/core/lib/Enigma.mjs index 1ed0ea2b..39193f69 100644 --- a/src/core/lib/Enigma.mjs +++ b/src/core/lib/Enigma.mjs @@ -25,7 +25,6 @@ export const ROTORS = [ ]; export const ROTORS_FOURTH = [ - {name: "None", value: ""}, {name: "Beta", value: "LEYJVCNIXWPBQMDRTAKZGFUHOS"}, {name: "Gamma", value: "FSOKANUERHMBTIYCWLQPZXVGJD"}, ]; diff --git a/src/core/operations/Bombe.mjs b/src/core/operations/Bombe.mjs index ea3210fa..5e128498 100644 --- a/src/core/operations/Bombe.mjs +++ b/src/core/operations/Bombe.mjs @@ -44,7 +44,7 @@ class Bombe extends Operation { ] }, { - name: "Left-most rotor", + name: "Left-most (4th) rotor", type: "editableOption", value: ROTORS_FOURTH, defaultIndex: 0 diff --git a/src/core/operations/Enigma.mjs b/src/core/operations/Enigma.mjs index ace50604..77333b18 100644 --- a/src/core/operations/Enigma.mjs +++ b/src/core/operations/Enigma.mjs @@ -42,7 +42,7 @@ class Enigma extends Operation { ] }, { - name: "Left-most rotor", + name: "Left-most (4th) rotor", type: "editableOption", value: ROTORS_FOURTH, defaultIndex: 0