diff --git a/.gitignore b/.gitignore index 85311b56..5019a4eb 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules npm-debug.log build/dev +build/test docs/* !docs/*.conf.json !docs/*.ico diff --git a/Gruntfile.js b/Gruntfile.js index 628fcbe8..af83ad98 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -9,10 +9,14 @@ module.exports = function(grunt) { "A persistent task which creates a development build whenever source files are modified.", ["clean:dev", "concat:css", "concat:js", "copy:htmlDev", "copy:staticDev", "chmod:build", "watch"]); + grunt.registerTask("test", + "A task which runs all the tests in test/tests.", + ["clean:test", "concat:jsTest", "copy:htmlTest", "chmod:build", "execute:test"]); + grunt.registerTask("prod", "Creates a production-ready build. Use the --msg flag to add a compile message.", ["eslint", "exec:stats", "clean", "jsdoc", "concat", "copy:htmlDev", "copy:htmlProd", "copy:htmlInline", - "copy:staticDev", "copy:staticProd", "cssmin", "uglify:prod", "inline", "htmlmin", "chmod"]); + "copy:staticDev", "copy:staticProd", "cssmin", "uglify:prod", "inline", "htmlmin", "chmod", "test"]); grunt.registerTask("docs", "Compiles documentation in the /docs directory.", @@ -31,6 +35,7 @@ module.exports = function(grunt) { ["eslint", "exec:stats", "exec:displayStats"]); grunt.registerTask("doc", "docs"); + grunt.registerTask("tests", "test"); grunt.registerTask("lint", "eslint"); @@ -46,11 +51,12 @@ module.exports = function(grunt) { grunt.loadNpmTasks("grunt-inline-alt"); grunt.loadNpmTasks("grunt-chmod"); grunt.loadNpmTasks("grunt-exec"); + grunt.loadNpmTasks("grunt-execute"); grunt.loadNpmTasks("grunt-contrib-watch"); // JS includes - var jsFiles = [ + var jsIncludes = [ // Third party framework libraries "src/js/lib/jquery-2.1.1.js", "src/js/lib/bootstrap-3.3.6.js", @@ -134,6 +140,7 @@ module.exports = function(grunt) { "src/js/lib/vkbeautify.js", "src/js/lib/Sortable.js", "src/js/lib/bootstrap-colorpicker.js", + "src/js/lib/es6-promise.auto.js", "src/js/lib/xpath.js", // Custom libraries @@ -154,10 +161,19 @@ module.exports = function(grunt) { "src/js/views/html/*.js", "!src/js/views/html/main.js", - // Start the app! - "src/js/views/html/main.js", ]; + var jsAppFiles = jsIncludes.concat([ + // Start the main app! + "src/js/views/html/main.js", + ]); + + var jsTestFiles = jsIncludes.concat([ + "test/TestRegister.js", + "test/tests/**/*.js", + "test/TestRunner.js", + ]); + var banner = '/**\n\ * CyberChef - The Cyber Swiss Army Knife\n\ *\n\ @@ -198,6 +214,7 @@ module.exports = function(grunt) { config: ["src/js/config/**/*.js"], views: ["src/js/views/**/*.js"], operations: ["src/js/operations/**/*.js"], + tests: ["test/**/*.js"], }, jsdoc: { options: { @@ -217,6 +234,7 @@ module.exports = function(grunt) { clean: { dev: ["build/dev/*"], prod: ["build/prod/*"], + test: ["build/test/*"], docs: ["docs/*", "!docs/*.conf.json", "!docs/*.ico"], }, concat: { @@ -243,8 +261,15 @@ module.exports = function(grunt) { options: { banner: '"use strict";\n' }, - src: jsFiles, + src: jsAppFiles, dest: "build/dev/scripts.js" + }, + jsTest: { + options: { + banner: '"use strict";\n' + }, + src: jsTestFiles, + dest: "build/test/tests.js" } }, copy: { @@ -257,6 +282,10 @@ module.exports = function(grunt) { src: "src/html/index.html", dest: "build/dev/index.html" }, + htmlTest: { + src: "test/test.html", + dest: "build/test/index.html" + }, htmlProd: { options: { process: function(content, srcpath) { @@ -461,6 +490,9 @@ module.exports = function(grunt) { ].join(";") } }, + execute: { + test: "test/NodeRunner.js" + }, watch: { css: { files: "src/css/**/*.css", diff --git a/README.md b/README.md index 145e83d4..83ae12b5 100755 --- a/README.md +++ b/README.md @@ -79,6 +79,11 @@ An installation walkthrough, how-to guides for adding new operations, descriptio - Submit a pull request. +## Licencing + +CyberChef is released under the [Apache 2.0 Licence](https://www.apache.org/licenses/LICENSE-2.0) and is covered by [Crown Copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/copyright-and-re-use/crown-copyright/). + + [1]: https://gchq.github.io/CyberChef [2]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22From%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%2Ctrue%5D%7D%5D&input=VTI4Z2JHOXVaeUJoYm1RZ2RHaGhibXR6SUdadmNpQmhiR3dnZEdobElHWnBjMmd1 [3]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22Translate%20DateTime%20Format%22%2C%22args%22%3A%5B%22Standard%20date%20and%20time%22%2C%22DD%2FMM%2FYYYY%20HH%3Amm%3Ass%22%2C%22UTC%22%2C%22dddd%20Do%20MMMM%20YYYY%20HH%3Amm%3Ass%20Z%20z%22%2C%22Australia%2FQueensland%22%5D%7D%5D&input=MTUvMDYvMjAxNSAyMDo0NTowMA diff --git a/package.json b/package.json index 96c81e3b..8878ff97 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,10 @@ "grunt-contrib-watch": "~1.0.0", "grunt-eslint": "^19.0.0", "grunt-exec": "~1.0.1", + "grunt-execute": "^0.2.2", "grunt-inline-alt": "~0.3.10", "grunt-jsdoc": "^2.1.0", - "ink-docstrap": "^1.1.4" + "ink-docstrap": "^1.1.4", + "phantomjs-prebuilt": "^2.1.14" } } diff --git a/src/js/.eslintrc.json b/src/js/.eslintrc.json index ecb7dd48..7ea169f3 100755 --- a/src/js/.eslintrc.json +++ b/src/js/.eslintrc.json @@ -109,6 +109,10 @@ "OutputWaiter": false, "RecipeWaiter": false, "SeasonalWaiter": false, - "WindowWaiter": false + "WindowWaiter": false, + + /* tests */ + "TestRegister": false, + "TestRunner": false } -} \ No newline at end of file +} diff --git a/src/js/core/Dish.js b/src/js/core/Dish.js index e4db35bd..27fcdc06 100755 --- a/src/js/core/Dish.js +++ b/src/js/core/Dish.js @@ -139,7 +139,7 @@ Dish.prototype.translate = function(toType) { this.type = Dish.BYTE_ARRAY; break; case Dish.HTML: - this.value = this.value ? Utils.strToByteArray(Utils.stripHtmlTags(this.value, true)) : []; + this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : []; this.type = Dish.BYTE_ARRAY; break; default: diff --git a/src/js/core/FlowControl.js b/src/js/core/FlowControl.js index 7ad46c8c..9d4b9af5 100755 --- a/src/js/core/FlowControl.js +++ b/src/js/core/FlowControl.js @@ -152,7 +152,6 @@ var FlowControl = { maxJumps = ings[1]; if (state.numJumps >= maxJumps) { - state.progress++; return state; } @@ -180,7 +179,6 @@ var FlowControl = { maxJumps = ings[2]; if (state.numJumps >= maxJumps) { - state.progress++; return state; } diff --git a/src/js/core/Utils.js b/src/js/core/Utils.js index 9619f0b2..9ef0b2b1 100755 --- a/src/js/core/Utils.js +++ b/src/js/core/Utils.js @@ -928,6 +928,33 @@ var Utils = { }, + /** + * Unescapes HTML tags in a string to make them render again. + * + * @param {string} str + * @returns string + * + * @example + * // return "A + + diff --git a/test/tests/operations/Base58.js b/test/tests/operations/Base58.js new file mode 100644 index 00000000..403cc305 --- /dev/null +++ b/test/tests/operations/Base58.js @@ -0,0 +1,76 @@ +/** + * Base58 tests. + * + * @author tlwr [toby@toby.codes] + * + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ +TestRegister.addTests([ + { + name: "To Base58 (Bitcoin): nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "To Base58", + args: ["123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"], + }, + ], + }, + { + name: "To Base58 (Ripple): nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "To Base58", + args: ["rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"], + }, + ], + }, + { + name: "To Base58 (Bitcoin): 'hello world'", + input: "hello world", + expectedOutput: "StV1DL6CwTryKyV", + recipeConfig: [ + { + op: "To Base58", + args: ["123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"], + }, + ], + }, + { + name: "To Base58 (Ripple): 'hello world'", + input: "hello world", + expectedOutput: "StVrDLaUATiyKyV", + recipeConfig: [ + { + op: "To Base58", + args: ["rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"], + }, + ], + }, + { + name: "From Base58 (Bitcoin): 'StV1DL6CwTryKyV'", + input: "StV1DL6CwTryKyV", + expectedOutput: "hello world", + recipeConfig: [ + { + op: "From Base58", + args: ["123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"], + }, + ], + }, + { + name: "From Base58 (Ripple): 'StVrDLaUATiyKyV'", + input: "StVrDLaUATiyKyV", + expectedOutput: "hello world", + recipeConfig: [ + { + op: "From Base58", + args: ["rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"], + }, + ], + }, +]); diff --git a/test/tests/operations/FlowControl.js b/test/tests/operations/FlowControl.js new file mode 100644 index 00000000..59c7f965 --- /dev/null +++ b/test/tests/operations/FlowControl.js @@ -0,0 +1,112 @@ +/** + * Flow Control tests. + * + * @author tlwr [toby@toby.codes] + * + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ +TestRegister.addTests([ + { + name: "Fork: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Fork", + args: ["\n", "\n", false], + }, + ], + }, + { + name: "Fork, Merge: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Fork", + args: ["\n", "\n", false], + }, + { + op: "Merge", + args: [], + }, + ], + }, + { + name: "Fork, (expect) Error, Merge", + input: "1\n2\na\n4", + expectedError: true, + recipeConfig: [ + { + op: "Fork", + args: ["\n", "\n", false], + }, + { + op: "To Base", + args: [16], + }, + { + op: "Merge", + args: [], + }, + ], + }, + { + name: "Fork, Conditional Jump, Encodings", + input: "Some data with a 1 in it\nSome data with a 2 in it", + expectedOutput: "U29tZSBkYXRhIHdpdGggYSAxIGluIGl0\n53 6f 6d 65 20 64 61 74 61 20 77 69 74 68 20 61 20 32 20 69 6e 20 69 74\n", + recipeConfig: [ + {"op":"Fork", "args":["\\n", "\\n", false]}, + {"op":"Conditional Jump", "args":["1", "2", "10"]}, + {"op":"To Hex", "args":["Space"]}, + {"op":"Return", "args":[]}, + {"op":"To Base64", "args":["A-Za-z0-9+/="]} + ] + }, + { + name: "Conditional Jump: Skips 0", + input: [ + "match", + "should be changed 1", + "should be changed 2", + ].join("\n"), + expectedOutput: [ + "match", + "should be changed 1 was changed", + "should be changed 2 was changed" + ].join("\n"), + recipeConfig: [ + { + op: "Conditional Jump", + args: ["match", 0, 0], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "should be changed 1" + }, + "should be changed 1 was changed", + true, + true, + true, + ], + }, + { + op: "Find / Replace", + args: [ + { + "option": "Regex", + "string": "should be changed 2" + }, + "should be changed 2 was changed", + true, + true, + true, + ], + }, + ], + }, +]); diff --git a/test/tests/operations/MorseCode.js b/test/tests/operations/MorseCode.js new file mode 100644 index 00000000..fc0bc25c --- /dev/null +++ b/test/tests/operations/MorseCode.js @@ -0,0 +1,32 @@ +/** + * Base58 tests. + * + * @author tlwr [toby@toby.codes] + * + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ +TestRegister.addTests([ + { + name: "To Morse Code: 'SOS'", + input: "SOS", + expectedOutput: "... --- ...", + recipeConfig: [ + { + op: "To Morse Code", + args: ["-/.", "Space", "Line feed"], + }, + ], + }, + { + name: "From Morse Code '... --- ...'", + input: "... --- ...", + expectedOutput: "SOS", + recipeConfig: [ + { + op: "From Morse Code", + args: ["Space", "Line feed"], + }, + ], + }, +]); diff --git a/test/tests/operations/StrUtils.js b/test/tests/operations/StrUtils.js new file mode 100644 index 00000000..0d91d181 --- /dev/null +++ b/test/tests/operations/StrUtils.js @@ -0,0 +1,24 @@ +/** + * StrUtils tests. + * + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ +TestRegister.addTests([ + { + name: "Regex, non-HTML op", + input: "/<>", + expectedOutput: "/<>", + recipeConfig: [ + { + "op": "Regular expression", + "args": ["User defined", "", true, true, false, "Highlight matches"] + }, + { + "op": "Remove whitespace", + "args": [true, true, true, true, true, false] + } + ], + }, +]);