diff --git a/.travis.yml b/.travis.yml index 3cfad188..a67e99b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: node_js node_js: - lts/dubnium cache: npm +os: linux addons: chrome: stable install: npm install @@ -19,16 +20,16 @@ before_deploy: - grunt copy:ghPages deploy: - provider: pages - skip_cleanup: true - github_token: $GITHUB_TOKEN + edge: true + token: $GITHUB_TOKEN local_dir: build/prod/ target_branch: gh-pages on: repo: gchq/CyberChef branch: master - provider: releases - skip_cleanup: true - api_key: + edge: true + token: secure: "HV1WSKv4l/0Y2bKKs1iBJocBcmLj08PCRUeEM/jTwA4jqJ8EiLHWiXtER/D5sEg2iibRVKd2OQjfrmS6bo4AiwdeVgAKmv0FtS2Jw+391N8Nd5AkEANHa5Om/IpHLTL2YRAjpJTsDpY72bMUTJIwjQA3TFJkgrpOw6KYfohOcgbxLpZ4XuNJRU3VL4Hsxdv5V9aOVmfFOmMOVPQlakXy7NgtW5POp1f2WJwgcZxylkR1CjwaqMyXmSoVl46pyH3tr5+dptsQoKSGdi6sIHGA60oDotFPcm+0ifa47wZw+vapuuDi4tdNxhrHGaDMG8xiE0WFDHwQUDlk2/+W7j9SEX0H3Em7us371JXRp56EDwEcDa34VpVkC6i8HGcHK55hnxVbMZXGf3qhOFD8wY7qMbjMRvIpucrMHBi86OfkDfv0vDj2LyvIl5APj/AX50BrE0tfH1MZbH26Jkx4NdlkcxQ14GumarmUqfmVvbX/fsoA6oUuAAE9ZgRRi3KHO4wci6KUcRfdm+XOeUkaBFsL86G3EEYIvrtBTuaypdz+Cx7nd1iPZyWMx5Y1gXnVzha4nBdV4+7l9JIsFggD8QVpw2uHXQiS1KXFjOeqA3DBD8tjMB7q26Fl2fD3jkOo4BTbQ2NrRIZUu/iL+fOmMPsyMt2qulB0yaSBCfkbEq8xrUA=" file_glob: true file: @@ -38,9 +39,9 @@ deploy: repo: gchq/CyberChef tags: true - provider: npm - skip_cleanup: true + edge: true email: "n1474335@gmail.com" - api_key: + api_token: secure: "UnDQL3Kh+GK2toL0TK3FObO0ujVssU3Eg4BBuYdjwLB81GhiGE5/DTh7THdZPOpbLo6wQeOwfZDuMeKC1OU+0Uf4NsdYFu1aq6xMO20qBQ4qUfgsyiK4Qgywj9gk0p1+OFZdGAZ/j1CNRAaF71XQIY6iV84c+SO4WoizXYrNT0Jh4sr2DA4/97G2xmJtPi0qOzYrJ09R56ZUozmqeik5G0pMRIuJRbpjS/7bZXV+N7WV0ombZc9RkUaetbabEVOLQ+Xx5YAIVq+VuEeMe9VBSnxY/FfCLmy1wJsjGzpLCyBI9nbrG4nw8Wgc2m8NfK9rcpIvBTGner9r2j60NVDkZ8kLZPrqXhq6AZMwa+oz6K5UQCqRo2RRQzSGwXxg67HY5Tcq+oNmjd+DqpPg4LZ3eGlluyP5XfG+hpSr9Ya4d8q8SrUWLxkoLHI6ZKMtoKFbTCSSQPiluW5hsZxjz3yDkkjsJw64M/EM8UyJrgaXqDklQu+7rBGKLfsK6os7RDiqjBWpQ7gwpo8HvY0O8yqEAabPz+QGkanpjcCOZCXFbSkzWxYy37RMAPu88iINVZVlZE4l+WJenCpZY95ueyy0mG9cyMSzVRPyX6A+/n4H6VMFPFjpGDLTD588ACEjY1lmHfS/eXwXJcgqPPD2gW0XdRdUheU/ssqlfCfGWQMTDXs=" on: tags: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 999967c9..86ba6848 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master). +### [9.13.0] - 2020-02-13 +- 'Rail Fence Cipher Encode' and 'Rail Fence Cipher Decode' operations added [@Flavsditz] | [#948] + +### [9.12.0] - 2019-12-20 +- 'Normalise Unicode' operation added [@matthieuxyz] | [#912] + ### [9.11.0] - 2019-11-06 - Implemented CFB, OFB, and CTR modes for Blowfish operations [@cbeuw] | [#653] @@ -197,6 +203,8 @@ All major and minor version changes will be documented in this file. Details of +[9.13.0]: https://github.com/gchq/CyberChef/releases/tag/v9.13.0 +[9.12.0]: https://github.com/gchq/CyberChef/releases/tag/v9.12.0 [9.11.0]: https://github.com/gchq/CyberChef/releases/tag/v9.11.0 [9.10.0]: https://github.com/gchq/CyberChef/releases/tag/v9.10.0 [9.9.0]: https://github.com/gchq/CyberChef/releases/tag/v9.9.0 @@ -281,6 +289,8 @@ All major and minor version changes will be documented in this file. Details of [@jarrodconnolly]: https://github.com/jarrodconnolly [@VirtualColossus]: https://github.com/VirtualColossus [@cbeuw]: https://github.com/cbeuw +[@matthieuxyz]: https://github.com/matthieuxyz +[@Flavsditz]: https://github.com/Flavsditz [#95]: https://github.com/gchq/CyberChef/pull/299 [#173]: https://github.com/gchq/CyberChef/pull/173 @@ -344,3 +354,5 @@ All major and minor version changes will be documented in this file. Details of [#632]: https://github.com/gchq/CyberChef/pull/632 [#653]: https://github.com/gchq/CyberChef/pull/653 [#865]: https://github.com/gchq/CyberChef/pull/865 +[#912]: https://github.com/gchq/CyberChef/pull/912 +[#948]: https://github.com/gchq/CyberChef/pull/948 diff --git a/Gruntfile.js b/Gruntfile.js index 1a504d82..1a9e3f2f 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -219,6 +219,7 @@ module.exports = function (grunt) { options: { webpack: webpackConfig, host: "0.0.0.0", + port: grunt.option("port") || 8080, disableHostCheck: true, overlay: true, inline: false, @@ -275,7 +276,7 @@ module.exports = function (grunt) { connect: { prod: { options: { - port: 8000, + port: grunt.option("port") || 8000, base: "build/prod/" } } diff --git a/package-lock.json b/package-lock.json index 91167e68..2ee42038 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "9.11.13", + "version": "9.13.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2514,6 +2514,38 @@ "core-js": "^3.4.1" } }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@testim/chrome-version": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.0.7.tgz", + "integrity": "sha512-8UT/J+xqCYfn3fKtOznAibsHpiuDshCb0fwgWxRazTT19Igp9ovoXMPhXyLD6m3CKQGTMHgqoxaFfMWaL40Rnw==", + "dev": true + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -2868,6 +2900,24 @@ } } }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + } + } + }, "ajv": { "version": "6.5.5", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.5.tgz", @@ -4146,18 +4196,59 @@ } }, "chromedriver": { - "version": "78.0.1", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-78.0.1.tgz", - "integrity": "sha512-eOsyFk4xb9EECs1VMrDbxO713qN+Bu1XUE8K9AuePc3839TPdAegg72kpXSzkeNqRNZiHbnJUItIVCLFkDqceA==", + "version": "80.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-80.0.1.tgz", + "integrity": "sha512-VfRtZUpBUIjeypS+xM40+VD9g4Drv7L2VibG/4+0zX3mMx4KayN6gfKETycPfO6JwQXTLSxEr58fRcrsa8r5xQ==", "dev": true, "requires": { - "del": "^4.1.1", + "@testim/chrome-version": "^1.0.7", + "axios": "^0.19.2", + "del": "^5.1.0", "extract-zip": "^1.6.7", - "mkdirp": "^0.5.1", - "request": "^2.88.0", + "mkdirp": "^1.0.3", "tcp-port-used": "^1.0.1" }, "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "dev": true, + "requires": { + "follow-redirects": "1.5.10" + } + }, + "del": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-5.1.0.tgz", + "integrity": "sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA==", + "dev": true, + "requires": { + "globby": "^10.0.1", + "graceful-fs": "^4.2.2", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.1", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, "extract-zip": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", @@ -4168,7 +4259,94 @@ "debug": "2.6.9", "mkdirp": "0.5.1", "yauzl": "2.4.1" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + } } + }, + "globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz", + "integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==", + "dev": true + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true } } }, @@ -4220,6 +4398,12 @@ "source-map": "~0.6.0" } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -6709,6 +6893,73 @@ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", "dev": true }, + "fast-glob": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.1.tgz", + "integrity": "sha512-nTCREpBY8w8r+boyFYAx21iL6faSsQynliPHM4Uf56SbkyohCNxpVPEH9xrF5TXKy+IsjkPUHDKiUkzBVRXn9g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "glob-parent": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -6720,6 +6971,15 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, + "fastq": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz", + "integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==", + "dev": true, + "requires": { + "reusify": "^1.0.0" + } + }, "faye-websocket": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", @@ -10214,6 +10474,12 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "dev": true + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -10778,9 +11044,9 @@ "dev": true }, "nightwatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-1.3.2.tgz", - "integrity": "sha512-1Lcte2Su/JrzET62va4oVLbSXwZkZaTmxTdjShylio3U+woY6U250iNuQz/bOkL+Qvuw+9Vfp+5T13yT0YfsKg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/nightwatch/-/nightwatch-1.3.4.tgz", + "integrity": "sha512-27zWPjmBsHu3IDKc6QTQdRKbTWx4WuTUXbCOT1Wa8Ovxbv+3TZ7GCd9Dt9wm14ElO9Db9/PKVk8JIWThkqGRZA==", "dev": true, "requires": { "assertion-error": "^1.1.0", @@ -11391,9 +11657,9 @@ "dev": true }, "ansi-styles": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.0.tgz", - "integrity": "sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { "@types/color-name": "^1.1.1", @@ -11945,6 +12211,12 @@ "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==" }, + "picomatch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", + "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", + "dev": true + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -13050,6 +13322,12 @@ "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", "dev": true }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, "revalidator": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", @@ -13084,6 +13362,12 @@ "is-promise": "^2.1.0" } }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, "run-queue": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", @@ -14394,9 +14678,9 @@ } }, "terser": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.5.1.tgz", - "integrity": "sha512-lH9zLIbX8PRBEFCTvfHGCy0s9HEKnNso1Dx9swSopF3VUnFLB8DpQ61tHxoofovNC/sG0spajJM3EIIRSTByiQ==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz", + "integrity": "sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==", "dev": true, "requires": { "commander": "^2.20.0", @@ -14947,6 +15231,11 @@ "normalize-path": "^2.1.1" } }, + "unorm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", + "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==" + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index 988e610c..b7fc6ea8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cyberchef", - "version": "9.11.13", + "version": "9.13.0", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "author": "n1474335 ", "homepage": "https://gchq.github.io/CyberChef", @@ -43,7 +43,7 @@ "babel-eslint": "^10.0.3", "babel-loader": "^8.0.6", "babel-plugin-dynamic-import-node": "^2.3.0", - "chromedriver": "^78.0.1", + "chromedriver": "^80.0.1", "colors": "^1.4.0", "copy-webpack-plugin": "^5.0.5", "css-loader": "^3.2.1", @@ -65,7 +65,7 @@ "html-webpack-plugin": "^3.2.0", "imports-loader": "^0.8.0", "mini-css-extract-plugin": "^0.8.0", - "nightwatch": "^1.3.2", + "nightwatch": "^1.3.4", "node-sass": "^4.13.0", "postcss-css-variables": "^0.14.0", "postcss-import": "^12.0.1", @@ -147,6 +147,7 @@ "ssdeep.js": "0.0.2", "tesseract.js": "^2.0.0-alpha.15", "ua-parser-js": "^0.7.20", + "unorm": "^1.6.0", "utf8": "^3.0.0", "vkbeautify": "^0.99.3", "xmldom": "^0.1.27", diff --git a/src/core/Chef.mjs b/src/core/Chef.mjs index ca02a32d..4d1ff994 100755 --- a/src/core/Chef.mjs +++ b/src/core/Chef.mjs @@ -73,10 +73,10 @@ class Chef { // The threshold is specified in KiB. const threshold = (options.ioDisplayThreshold || 1024) * 1024; const returnType = - this.dish.size > threshold ? - Dish.ARRAY_BUFFER : - this.dish.type === Dish.HTML ? - Dish.HTML : + this.dish.type === Dish.HTML ? + Dish.HTML : + this.dish.size > threshold ? + Dish.ARRAY_BUFFER : Dish.STRING; return { diff --git a/src/core/Ingredient.mjs b/src/core/Ingredient.mjs index f74b42d6..d64bf763 100755 --- a/src/core/Ingredient.mjs +++ b/src/core/Ingredient.mjs @@ -113,6 +113,7 @@ class Ingredient { return data; } case "number": + if (data === null) return data; number = parseFloat(data); if (isNaN(number)) { const sample = Utils.truncate(data.toString(), 10); diff --git a/src/core/Utils.mjs b/src/core/Utils.mjs index 0f10cb8a..c99eccc9 100755 --- a/src/core/Utils.mjs +++ b/src/core/Utils.mjs @@ -591,6 +591,44 @@ class Utils { return utf8 ? Utils.byteArrayToUtf8(arr) : Utils.byteArrayToChars(arr); } + /** + * Calculates the Shannon entropy for a given set of data. + * + * @param {Uint8Array|ArrayBuffer} input + * @returns {number} + */ + static calculateShannonEntropy(data) { + if (data instanceof ArrayBuffer) { + data = new Uint8Array(data); + } + const prob = [], + occurrences = new Array(256).fill(0); + + // Count occurrences of each byte in the input + let i; + for (i = 0; i < data.length; i++) { + occurrences[data[i]]++; + } + + // Store probability list + for (i = 0; i < occurrences.length; i++) { + if (occurrences[i] > 0) { + prob.push(occurrences[i] / data.length); + } + } + + // Calculate Shannon entropy + let entropy = 0, + p; + + for (i = 0; i < prob.length; i++) { + p = prob[i]; + entropy += p * Math.log(p) / Math.log(2); + } + + return -entropy; + } + /** * Parses CSV data and returns it as a two dimensional array or strings. diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index f663e16d..0d68efe8 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -39,6 +39,7 @@ "URL Decode", "Escape Unicode Characters", "Unescape Unicode Characters", + "Normalise Unicode", "To Quoted Printable", "From Quoted Printable", "To Punycode", @@ -94,6 +95,8 @@ "Affine Cipher Decode", "A1Z26 Cipher Encode", "A1Z26 Cipher Decode", + "Rail Fence Cipher Encode", + "Rail Fence Cipher Decode", "Atbash Cipher", "Substitute", "Derive PBKDF2 key", diff --git a/src/core/dishTypes/DishListFile.mjs b/src/core/dishTypes/DishListFile.mjs index 198a974b..9c34c151 100644 --- a/src/core/dishTypes/DishListFile.mjs +++ b/src/core/dishTypes/DishListFile.mjs @@ -5,7 +5,7 @@ */ import DishType from "./DishType.mjs"; -import { isNodeEnvironment } from "../Utils.mjs"; +import Utils, { isNodeEnvironment } from "../Utils.mjs"; /** @@ -16,13 +16,14 @@ class DishListFile extends DishType { /** * convert the given value to a ArrayBuffer */ - static toArrayBuffer() { + static async toArrayBuffer() { DishListFile.checkForValue(this.value); if (isNodeEnvironment()) { this.value = this.value.map(file => Uint8Array.from(file.data)); + } else { + this.value = await DishListFile.concatenateTypedArraysWithTypedElements(...this.value); } - this.value = DishListFile.concatenateTypedArrays(...this.value).buffer; } /** @@ -33,6 +34,27 @@ class DishListFile extends DishType { this.value = [new File(this.value, "unknown")]; } + /** + * Concatenates a list of typed elements together. + * + * @param {Uint8Array[]} arrays + * @returns {Uint8Array} + */ + static async concatenateTypedArraysWithTypedElements(...arrays) { + let totalLength = 0; + for (const arr of arrays) { + totalLength += arr.size; + } + const myArray = new Uint8Array(totalLength); + + let offset = 0; + for (const arr of arrays) { + const data = await Utils.readFile(arr); + myArray.set(data, offset); + offset += data.length; + } + return myArray; + } /** * Concatenates a list of Uint8Arrays together diff --git a/src/core/lib/ChrEnc.mjs b/src/core/lib/ChrEnc.mjs index a472706b..c5cb5605 100644 --- a/src/core/lib/ChrEnc.mjs +++ b/src/core/lib/ChrEnc.mjs @@ -164,3 +164,15 @@ export const IO_FORMAT = { "Simplified Chinese GB18030 (54936)": 54936, }; +/** + * Unicode Normalisation Forms + * + * @author Matthieu [m@tthieu.xyz] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +/** + * Character encoding format mappings. + */ +export const UNICODE_NORMALISATION_FORMS = ["NFD", "NFC", "NFKD", "NFKC"]; diff --git a/src/core/lib/FileSignatures.mjs b/src/core/lib/FileSignatures.mjs index 17b17f23..1900ce4e 100644 --- a/src/core/lib/FileSignatures.mjs +++ b/src/core/lib/FileSignatures.mjs @@ -280,7 +280,7 @@ export const FILE_SIGNATURES = { 9: 0x0, 10: [0x0, 0x1] }, - extractor: null + extractor: extractICO }, { name: "Radiance High Dynamic Range image", @@ -1008,8 +1008,7 @@ export const FILE_SIGNATURES = { 0: 0x7b, 1: 0x5c, 2: 0x72, - 3: 0x74, - 4: 0x66 + 3: 0x74 }, extractor: extractRTF }, @@ -2933,6 +2932,32 @@ export function extractBMP(bytes, offset) { } +/** + * ICO extractor. + * + * @param {Uint8Array} bytes + * @param {number} offset + */ +export function extractICO(bytes, offset) { + const stream = new Stream(bytes.slice(offset)); + + // Move to number of files there are. + stream.moveTo(4); + + // Read the number of files stored in the ICO + const numberFiles = stream.readInt(2, "le"); + + // Move forward to the last file header. + stream.moveForwardsBy(8 + ((numberFiles-1) * 16)); + const fileSize = stream.readInt(4, "le"); + const fileOffset = stream.readInt(4, "le"); + + // Move to the end of the last file. + stream.moveTo(fileOffset + fileSize); + return stream.carve(); +} + + /** * WAV extractor. * @@ -2947,7 +2972,7 @@ export function extractWAV(bytes, offset) { stream.moveTo(4); // Move to file size. - stream.moveTo(stream.readInt(4, "le") - 4); + stream.moveTo(stream.readInt(4, "le")); return stream.carve(); } @@ -3075,15 +3100,127 @@ export function extractSQLITE(bytes, offset) { export function extractPListXML(bytes, offset) { const stream = new Stream(bytes.slice(offset)); - // Find closing tag () - stream.continueUntil([0x3c, 0x2f, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x3e]); - stream.moveForwardsBy(8); + let braceCount = 0; + + // Continue to the first ( 0 && stream.hasMore()) { + if (stream.readInt(1) === 0x3c) { + + // If we hit an . + if (stream.getBytes(7).join("") === [0x2f, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x3e].join("")) { + braceCount--; + } else { + stream.moveBackwardsBy(7); + } + } + } stream.consumeIf(0x0a); return stream.carve(); } +/** + * OLE2 extractor. + * + * @param {Uint8Array} bytes + * @param {number} offset + * @returns {Uint8Array} + */ +export function extractOLE2(bytes, offset) { + const stream = new Stream(bytes.slice(offset)); + const entries = [ + [[0x52, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x20, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x72, 0x00, 0x79], 19, "Root Entry"], + [[0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6b], 15, "Workbook"], + [[0x43, 0x00, 0x75, 0x00, 0x72, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x55, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72], 23, "Current User"], + [[0x50, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x65, 0x00, 0x72, 0x00, 0x50, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74], 37, "PowerPoint Document"], + [[0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74], 23, "WordDocument"], + [[0x44, 0x00, 0x61, 0x00, 0x74, 0x00, 0x61], 7, "Data"], + [[0x50, 0x00, 0x69, 0x00, 0x63, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73], 15, "Pictures"], + [[0x31, 0x00, 0x54, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65], 11, "1Table"], + [[0x05, 0x00, 0x53, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e], 37, "SummaryInformation"], + [[0x05, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x53, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e], 53, "DocumentSummaryInformation"], + [[0x43, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x4f, 0x00, 0x62, 0x00, 0x6a], 13, "Comp Obj"], + [[0x01, 0x00], 2, "Entry"] + ]; + let endianness = "le"; + + // Move to endianess field. + stream.moveForwardsBy(28); + if (stream.readInt(2, endianness) === 0xfffe) + endianness = "be"; + + // Calculate the size of the normal sectors. + const sizeOfSector = 2 ** stream.readInt(2, endianness); + + // Move to root directory offset field. + stream.moveTo(48); + + // Read root directory offset. + const rootStuff = stream.readInt(4, endianness); + + // Calculate root directory offset. + let total = 512 + (rootStuff * sizeOfSector); + stream.moveTo(total); + + // While valid directory entries. + let found = true; + while (found) { + found = false; + + // Attempt to determine what directory entry it is. + for (const element of entries) { + + // If the byte pattern matches. + if (stream.getBytes(element[1]).join("") === element[0].join("")) { + stream.moveBackwardsBy(element[1]); + found = true; + + // Move forwards by the size of the comp obj. + if (element[2] === "Comp Obj") { + + // The size of the Comp Obj entry - 128. Since we add 128 later. + total += 128 * 6; + stream.moveTo(total); + } else if (element[2] === "Entry") { + + // If there is an entry move backwards by 126 to then move forwards by 128. Hence a total displacement of 2. + stream.moveBackwardsBy(126); + } + break; + } + stream.moveBackwardsBy(element[1]); + } + + // If we have found a valid entry, move forwards by 128. + if (found) { + + // Every entry is at least 128 in size, some are bigger which is dealt with by the above if statement. + total += 128; + stream.moveForwardsBy(128); + } + } + + // Round up to a multiple of 512. + total = Math.ceil(total / 512) * 512; + + stream.moveTo(total); + return stream.carve(); +} + + /** * GZIP extractor. * diff --git a/src/core/lib/Hex.mjs b/src/core/lib/Hex.mjs index 5ae06a7e..9c70e91c 100644 --- a/src/core/lib/Hex.mjs +++ b/src/core/lib/Hex.mjs @@ -23,25 +23,39 @@ import Utils from "../Utils.mjs"; * * // returns "0a:14:1e" * toHex([10,20,30], ":"); + * + * // returns "0x0a,0x14,0x1e" + * toHex([10,20,30], "0x", 2, ",") */ -export function toHex(data, delim=" ", padding=2) { +export function toHex(data, delim=" ", padding=2, extraDelim="", lineSize=0) { if (!data) return ""; if (data instanceof ArrayBuffer) data = new Uint8Array(data); let output = ""; + const prepend = (delim === "0x" || delim === "\\x"); for (let i = 0; i < data.length; i++) { - output += data[i].toString(16).padStart(padding, "0") + delim; + const hex = data[i].toString(16).padStart(padding, "0"); + output += prepend ? delim + hex : hex + delim; + + if (extraDelim) { + output += extraDelim; + } + // Add LF after each lineSize amount of bytes but not at the end + if ((i !== data.length - 1) && ((i + 1) % lineSize === 0)) { + output += "\n"; + } } - // Add \x or 0x to beginning - if (delim === "0x") output = "0x" + output; - if (delim === "\\x") output = "\\x" + output; - - if (delim.length) - return output.slice(0, -delim.length); - else + // Remove the extraDelim at the end (if there is one) + // and remove the delim at the end, but if it's prepended there's nothing to remove + const rTruncLen = extraDelim.length + (prepend ? 0 : delim.length); + if (rTruncLen) { + // If rTruncLen === 0 then output.slice(0,0) will be returned, which is nothing + return output.slice(0, -rTruncLen); + } else { return output; + } } @@ -87,7 +101,7 @@ export function toHexFast(data) { */ export function fromHex(data, delim="Auto", byteLen=2) { if (delim !== "None") { - const delimRegex = delim === "Auto" ? /[^a-f\d]/gi : Utils.regexRep(delim); + const delimRegex = delim === "Auto" ? /[^a-f\d]|(0x)/gi : Utils.regexRep(delim); data = data.replace(delimRegex, ""); } @@ -102,7 +116,7 @@ export function fromHex(data, delim="Auto", byteLen=2) { /** * To Hexadecimal delimiters. */ -export const TO_HEX_DELIM_OPTIONS = ["Space", "Percent", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"]; +export const TO_HEX_DELIM_OPTIONS = ["Space", "Percent", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "0x with comma", "\\x", "None"]; /** diff --git a/src/core/lib/Magic.mjs b/src/core/lib/Magic.mjs index e9db99d4..c27ec3f3 100644 --- a/src/core/lib/Magic.mjs +++ b/src/core/lib/Magic.mjs @@ -458,7 +458,7 @@ class Magic { await recipe.execute(dish); // Return an empty buffer if the recipe did not run to completion if (recipe.lastRunOp === recipe.opList[recipe.opList.length - 1]) { - return dish.get(Dish.ARRAY_BUFFER); + return await dish.get(Dish.ARRAY_BUFFER); } else { return new ArrayBuffer(); } diff --git a/src/core/operations/Gunzip.mjs b/src/core/operations/Gunzip.mjs index 52efd6d7..d78e29a2 100644 --- a/src/core/operations/Gunzip.mjs +++ b/src/core/operations/Gunzip.mjs @@ -47,8 +47,8 @@ class Gunzip extends Operation { * @returns {File} */ run(input, args) { - const gunzip = new Zlib.Gunzip(new Uint8Array(input)); - return new Uint8Array(gunzip.decompress()).buffer; + const gzipObj = new Zlib.Gunzip(new Uint8Array(input)); + return new Uint8Array(gzipObj.decompress()).buffer; } } diff --git a/src/core/operations/Gzip.mjs b/src/core/operations/Gzip.mjs index 5f9fa474..093ae6a4 100644 --- a/src/core/operations/Gzip.mjs +++ b/src/core/operations/Gzip.mjs @@ -6,9 +6,9 @@ import Operation from "../Operation.mjs"; import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib.mjs"; -import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min.js"; +import gzip from "zlibjs/bin/gzip.min.js"; -const Zlib = zlibAndGzip.Zlib; +const Zlib = gzip.Zlib; /** * Gzip operation @@ -73,12 +73,15 @@ class Gzip extends Operation { options.filename = filename; } if (comment.length) { - options.flags.fcommenct = true; + options.flags.comment = true; options.comment = comment; } - - const gzip = new Zlib.Gzip(new Uint8Array(input), options); - return new Uint8Array(gzip.compress()).buffer; + const gzipObj = new Zlib.Gzip(new Uint8Array(input), options); + const compressed = new Uint8Array(gzipObj.compress()); + if (options.flags.comment && !(compressed[3] & 0x10)) { + compressed[3] |= 0x10; + } + return compressed.buffer; } } diff --git a/src/core/operations/NormaliseUnicode.mjs b/src/core/operations/NormaliseUnicode.mjs new file mode 100644 index 00000000..7de8625f --- /dev/null +++ b/src/core/operations/NormaliseUnicode.mjs @@ -0,0 +1,62 @@ +/** + * @author Matthieu [m@tthieu.xyz] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {UNICODE_NORMALISATION_FORMS} from "../lib/ChrEnc.mjs"; +import unorm from "unorm"; + +/** + * Normalise Unicode operation + */ +class NormaliseUnicode extends Operation { + + /** + * NormaliseUnicode constructor + */ + constructor() { + super(); + + this.name = "Normalise Unicode"; + this.module = "Encodings"; + this.description = "Transform Unicode characters to one of the Normalisation Forms"; + this.infoURL = "https://wikipedia.org/wiki/Unicode_equivalence#Normal_forms"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Normal Form", + type: "option", + value: UNICODE_NORMALISATION_FORMS + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [normalForm] = args; + + switch (normalForm) { + case "NFD": + return unorm.nfd(input); + case "NFC": + return unorm.nfc(input); + case "NFKD": + return unorm.nfkd(input); + case "NFKC": + return unorm.nfc(input); + default: + throw new OperationError("Unknown Normalisation Form"); + } + } + +} + +export default NormaliseUnicode; diff --git a/src/core/operations/RailFenceCipherDecode.mjs b/src/core/operations/RailFenceCipherDecode.mjs new file mode 100644 index 00000000..d98742b8 --- /dev/null +++ b/src/core/operations/RailFenceCipherDecode.mjs @@ -0,0 +1,88 @@ +/** + * @author Flavio Diez [flaviofdiez+cyberchef@gmail.com] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * Rail Fence Cipher Decode operation + */ +class RailFenceCipherDecode extends Operation { + + /** + * RailFenceCipherDecode constructor + */ + constructor() { + super(); + + this.name = "Rail Fence Cipher Decode"; + this.module = "Ciphers"; + this.description = "Decodes Strings that were created using the Rail fence Cipher provided a key and an offset"; + this.infoURL = "https://wikipedia.org/wiki/Rail_fence_cipher"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Key", + type: "number", + value: 2 + }, + { + name: "Offset", + type: "number", + value: 0 + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [key, offset] = args; + + let cipher = input; + + if (key < 2) { + throw new OperationError("Key has to be bigger than 2"); + } else if (key > cipher.length) { + throw new OperationError("Key should be smaller than the cipher's length"); + } + + if (offset < 0) { + throw new OperationError("Offset has to be a positive integer"); + } + + const cycle = (key - 1) * 2; + + const rest = cipher.length % key; + + if (rest !== 0) { + cipher = cipher + (" ".repeat(key - rest)); + } + + const plaintext = new Array(cipher.length); + + let j = 0; + let x, y; + + for (y = 0; y < key; y++) { + for (x = 0; x < cipher.length; x++) { + if ((y + x + offset) % cycle === 0 || (y - x - offset) % cycle === 0) { + plaintext[x] = cipher[j++]; + } + } + } + + return plaintext.join("").trim(); + } + +} + + +export default RailFenceCipherDecode; diff --git a/src/core/operations/RailFenceCipherEncode.mjs b/src/core/operations/RailFenceCipherEncode.mjs new file mode 100644 index 00000000..03651f85 --- /dev/null +++ b/src/core/operations/RailFenceCipherEncode.mjs @@ -0,0 +1,74 @@ +/** + * @author Flavio Diez [flaviofdiez+cyberchef@gmail.com] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * Rail Fence Cipher Encode operation + */ +class RailFenceCipherEncode extends Operation { + + /** + * RailFenceCipherEncode constructor + */ + constructor() { + super(); + + this.name = "Rail Fence Cipher Encode"; + this.module = "Ciphers"; + this.description = "Encodes Strings using the Rail fence Cipher provided a key and an offset"; + this.infoURL = "https://wikipedia.org/wiki/Rail_fence_cipher"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Key", + type: "number", + value: 2 + }, + { + name: "Offset", + type: "number", + value: 0 + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [key, offset] = args; + + const plaintext = input; + if (key < 2) { + throw new OperationError("Key has to be bigger than 2"); + } else if (key > plaintext.length) { + throw new OperationError("Key should be smaller than the plain text's length"); + } + + if (offset < 0) { + throw new OperationError("Offset has to be a positive integer"); + } + + const cycle = (key - 1) * 2; + const rows = new Array(key).fill(""); + + for (let pos = 0; pos < plaintext.length; pos++) { + const rowIdx = key - 1 - Math.abs(cycle / 2 - (pos + offset) % cycle); + + rows[rowIdx] += plaintext[pos]; + } + + return rows.join("").trim(); + } + +} + +export default RailFenceCipherEncode; diff --git a/src/core/operations/ToCaseInsensitiveRegex.mjs b/src/core/operations/ToCaseInsensitiveRegex.mjs index 28bd3dc9..77b14172 100644 --- a/src/core/operations/ToCaseInsensitiveRegex.mjs +++ b/src/core/operations/ToCaseInsensitiveRegex.mjs @@ -1,10 +1,12 @@ /** * @author masq [github.cyberchef@masq.cc] + * @author n1073645 * @copyright Crown Copyright 2018 * @license Apache-2.0 */ import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; /** * To Case Insensitive Regex operation @@ -32,7 +34,61 @@ class ToCaseInsensitiveRegex extends Operation { * @returns {string} */ run(input, args) { - return input.replace(/[a-z]/ig, m => `[${m.toLowerCase()}${m.toUpperCase()}]`); + + /** + * Simulates look behind behaviour since javascript doesn't support it. + * + * @param {string} input + * @returns {string} + */ + function preProcess(input) { + let result = ""; + for (let i = 0; i < input.length; i++) { + const temp = input.charAt(i); + if (temp.match(/[a-zA-Z]/g) && (input.charAt(i-1) !== "-") && (input.charAt(i+1) !== "-")) + result += "[" + temp.toLowerCase() + temp.toUpperCase() + "]"; + else + result += temp; + } + return result; + } + + try { + RegExp(input); + } catch (error) { + throw new OperationError("Invalid Regular Expression (Please note this version of node does not support look behinds)."); + } + + // Example: [test] -> [[tT][eE][sS][tT]] + return preProcess(input) + + // Example: [A-Z] -> [A-Za-z] + .replace(/([A-Z]-[A-Z]|[a-z]-[a-z])/g, m => `${m[0].toUpperCase()}-${m[2].toUpperCase()}${m[0].toLowerCase()}-${m[2].toLowerCase()}`) + + // Example: [H-d] -> [A-DH-dh-z] + .replace(/[A-Z]-[a-z]/g, m => `A-${m[2].toUpperCase()}${m}${m[0].toLowerCase()}-z`) + + // Example: [!-D] -> [!-Da-d] + .replace(/\\?[ -@]-[A-Z]/g, m => `${m}a-${m[2].toLowerCase()}`) + + // Example: [%-^] -> [%-^a-z] + .replace(/\\?[ -@]-\\?[[-`]/g, m => `${m}a-z`) + + // Example: [K-`] -> [K-`k-z] + .replace(/[A-Z]-\\?[[-`]/g, m => `${m}${m[0].toLowerCase()}-z`) + + // Example: [[-}] -> [[-}A-Z] + .replace(/\\?[[-`]-\\?[{-~]/g, m => `${m}A-Z`) + + // Example: [b-}] -> [b-}B-Z] + .replace(/[a-z]-\\?[{-~]/g, m => `${m}${m[0].toUpperCase()}-Z`) + + // Example: [<-j] -> [<-z] + .replace(/\\?[ -@]-[a-z]/g, m => `${m[0]}-z`) + + // Example: [^-j] -> [A-J^-j] + .replace(/\\?[[-`]-[a-z]/g, m => `A-${m[2].toUpperCase()}${m}`); + } } diff --git a/src/core/operations/ToHex.mjs b/src/core/operations/ToHex.mjs index 6ae48da9..2e97de8f 100644 --- a/src/core/operations/ToHex.mjs +++ b/src/core/operations/ToHex.mjs @@ -30,6 +30,11 @@ class ToHex extends Operation { name: "Delimiter", type: "option", value: TO_HEX_DELIM_OPTIONS + }, + { + name: "Bytes per line", + type: "number", + value: 0 } ]; } @@ -40,8 +45,16 @@ class ToHex extends Operation { * @returns {string} */ run(input, args) { - const delim = Utils.charRep(args[0] || "Space"); - return toHex(new Uint8Array(input), delim, 2); + let delim, comma; + if (args[0] === "0x with comma") { + delim = "0x"; + comma = ","; + } else { + delim = Utils.charRep(args[0] || "Space"); + } + const lineSize = args[1]; + + return toHex(new Uint8Array(input), delim, 2, comma, lineSize); } /** @@ -54,17 +67,31 @@ class ToHex extends Operation { * @returns {Object[]} pos */ highlight(pos, args) { - const delim = Utils.charRep(args[0] || "Space"), - len = delim === "\r\n" ? 1 : delim.length; - - pos[0].start = pos[0].start * (2 + len); - pos[0].end = pos[0].end * (2 + len) - len; - - // 0x and \x are added to the beginning if they are selected, so increment the positions accordingly - if (delim === "0x" || delim === "\\x") { - pos[0].start += 2; - pos[0].end += 2; + let delim, commaLen; + if (args[0] === "0x with comma") { + delim = "0x"; + commaLen = 1; + } else { + delim = Utils.charRep(args[0] || "Space"); } + + const lineSize = args[1], + len = (delim === "\r\n" ? 1 : delim.length) + commaLen; + + const countLF = function(p) { + // Count the number of LFs from 0 upto p + return (p / lineSize | 0) - (p >= lineSize && p % lineSize === 0); + }; + + pos[0].start = pos[0].start * (2 + len) + countLF(pos[0].start); + pos[0].end = pos[0].end * (2 + len) + countLF(pos[0].end); + + // if the deliminators are not prepended, trim the trailing deliminator + if (!(delim === "0x" || delim === "\\x")) { + pos[0].end -= delim.length; + } + // if there is comma, trim the trailing comma + pos[0].end -= commaLen; return pos; } @@ -78,20 +105,26 @@ class ToHex extends Operation { * @returns {Object[]} pos */ highlightReverse(pos, args) { - const delim = Utils.charRep(args[0] || "Space"), - len = delim === "\r\n" ? 1 : delim.length, - width = len + 2; - - // 0x and \x are added to the beginning if they are selected, so increment the positions accordingly - if (delim === "0x" || delim === "\\x") { - if (pos[0].start > 1) pos[0].start -= 2; - else pos[0].start = 0; - if (pos[0].end > 1) pos[0].end -= 2; - else pos[0].end = 0; + let delim, commaLen; + if (args[0] === "0x with comma") { + delim = "0x"; + commaLen = 1; + } else { + delim = Utils.charRep(args[0] || "Space"); } - pos[0].start = pos[0].start === 0 ? 0 : Math.round(pos[0].start / width); - pos[0].end = pos[0].end === 0 ? 0 : Math.ceil(pos[0].end / width); + const lineSize = args[1], + len = (delim === "\r\n" ? 1 : delim.length) + commaLen, + width = len + 2; + + const countLF = function(p) { + // Count the number of LFs from 0 up to p + const lineLength = width * lineSize; + return (p / lineLength | 0) - (p >= lineLength && p % lineLength === 0); + }; + + pos[0].start = pos[0].start === 0 ? 0 : Math.round((pos[0].start - countLF(pos[0].start)) / width); + pos[0].end = pos[0].end === 0 ? 0 : Math.ceil((pos[0].end - countLF(pos[0].end)) / width); return pos; } } diff --git a/src/core/vendor/gost/gostEngine.mjs b/src/core/vendor/gost/gostEngine.mjs index fb68a791..b54819c6 100755 --- a/src/core/vendor/gost/gostEngine.mjs +++ b/src/core/vendor/gost/gostEngine.mjs @@ -383,7 +383,7 @@ if (root.importScripts) { * method with data parameter: algorithm, method and arg.
* Call method execute and postMessage() results to onmessage event handler * in the main process.
- * If error occured onerror event handler executed in main process. + * If error occurred onerror event handler executed in main process. * * @memberOf gostEngine * @name onmessage diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index cb579721..e1e07dfd 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -197,6 +197,7 @@ class Manager { this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter); this.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, this.highlighter); this.addDynamicListener("#output-file-download", "click", this.output.downloadFile, this.output); + this.addDynamicListener("#output-file-show-all", "click", this.output.showAllFile, this.output); this.addDynamicListener("#output-file-slice i", "click", this.output.displayFileSlice, this.output); document.getElementById("show-file-overlay").addEventListener("click", this.output.showFileOverlayClick.bind(this.output)); this.addDynamicListener(".extract-file,.extract-file i", "click", this.output.extractFileClick, this.output); diff --git a/src/web/html/index.html b/src/web/html/index.html index 81f5402b..121f0780 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -355,15 +355,17 @@
Size:
+
- - - +
to
- + +
KiB
@@ -489,6 +491,15 @@ +
+ + +
+
@@ -573,13 +584,6 @@ Keep the current tab in sync between the input and output
- -
- -