mirror of
https://github.com/gchq/CyberChef
synced 2025-12-05 23:53:27 +00:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2325306db | ||
|
|
dfe31980b7 | ||
|
|
9734b78aeb | ||
|
|
4ee0800990 | ||
|
|
387008bd9c | ||
|
|
bf24547202 | ||
|
|
c2f6b8df66 | ||
|
|
d0e428b728 | ||
|
|
ae5128a33a | ||
|
|
ed25017e2d | ||
|
|
e5b2b84073 | ||
|
|
1953d9a4c8 | ||
|
|
b3113c109b | ||
|
|
8c3569ea63 | ||
|
|
ae28d476de | ||
|
|
01c4cfdc8d | ||
|
|
8c6c3a1c01 | ||
|
|
f367c1f78b | ||
|
|
13d87d397d | ||
|
|
ed2c886359 | ||
|
|
6337e84708 | ||
|
|
ae20a951be | ||
|
|
866dd546c8 | ||
|
|
2070e1a96b | ||
|
|
952f49e2e1 | ||
|
|
f600571c6d | ||
|
|
5d4c7244e1 | ||
|
|
b6bdcaa71f | ||
|
|
c8eab5d218 | ||
|
|
8ab56a29ac | ||
|
|
973b5f3f5c | ||
|
|
076a1f97c2 | ||
|
|
d96ef37d81 | ||
|
|
367d79e820 | ||
|
|
283d7f2159 | ||
|
|
dacb3ef6c3 | ||
|
|
3f7059a235 | ||
|
|
71c743ff5a |
18
CHANGELOG.md
18
CHANGELOG.md
@@ -2,6 +2,15 @@
|
||||
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.17.0] - 2018-12-25
|
||||
- 'Generate QR Code' and 'Parse QR Code' operations added [@j433866] | [#448]
|
||||
|
||||
### [8.16.0] - 2018-12-19
|
||||
- 'Play Media' operation added [@anthony-arnold] | [#446]
|
||||
|
||||
### [8.15.0] - 2018-12-18
|
||||
- 'Text Encoding Brute Force' operation added [@Cynser] | [#439]
|
||||
|
||||
### [8.14.0] - 2018-12-18
|
||||
- 'To Base62' and 'From Base62' operations added [@tcode2k16] | [#443]
|
||||
|
||||
@@ -73,6 +82,9 @@ All major and minor version changes will be documented in this file. Details of
|
||||
|
||||
|
||||
|
||||
[8.17.0]: https://github.com/gchq/CyberChef/releases/tag/v8.17.0
|
||||
[8.16.0]: https://github.com/gchq/CyberChef/releases/tag/v8.16.0
|
||||
[8.15.0]: https://github.com/gchq/CyberChef/releases/tag/v8.15.0
|
||||
[8.14.0]: https://github.com/gchq/CyberChef/releases/tag/v8.14.0
|
||||
[8.13.0]: https://github.com/gchq/CyberChef/releases/tag/v8.13.0
|
||||
[8.12.0]: https://github.com/gchq/CyberChef/releases/tag/v8.12.0
|
||||
@@ -95,6 +107,7 @@ All major and minor version changes will be documented in this file. Details of
|
||||
|
||||
[@n1474335]: https://github.com/n1474335
|
||||
[@d98762625]: https://github.com/d98762625
|
||||
[@j433866]: https://github.com/j433866
|
||||
[@GCHQ77703]: https://github.com/GCHQ77703
|
||||
[@artemisbot]: https://github.com/artemisbot
|
||||
[@picapi]: https://github.com/picapi
|
||||
@@ -107,6 +120,8 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[@bwhitn]: https://github.com/bwhitn
|
||||
[@jarmovanlenthe]: https://github.com/jarmovanlenthe
|
||||
[@tcode2k16]: https://github.com/tcode2k16
|
||||
[@Cynser]: https://github.com/Cynser
|
||||
[@anthony-arnold]: https://github.com/anthony-arnold
|
||||
|
||||
[#95]: https://github.com/gchq/CyberChef/pull/299
|
||||
[#173]: https://github.com/gchq/CyberChef/pull/173
|
||||
@@ -130,5 +145,8 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#387]: https://github.com/gchq/CyberChef/pull/387
|
||||
[#394]: https://github.com/gchq/CyberChef/pull/394
|
||||
[#428]: https://github.com/gchq/CyberChef/pull/428
|
||||
[#439]: https://github.com/gchq/CyberChef/pull/439
|
||||
[#441]: https://github.com/gchq/CyberChef/pull/441
|
||||
[#443]: https://github.com/gchq/CyberChef/pull/443
|
||||
[#446]: https://github.com/gchq/CyberChef/pull/446
|
||||
[#448]: https://github.com/gchq/CyberChef/pull/448
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = function(api) {
|
||||
api.cache.forever();
|
||||
|
||||
return {
|
||||
return {
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
"targets": {
|
||||
|
||||
706
package-lock.json
generated
706
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "8.14.0",
|
||||
"version": "8.17.0",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
@@ -92,6 +92,7 @@
|
||||
"exif-parser": "^0.1.12",
|
||||
"file-saver": "^2.0.0-rc.4",
|
||||
"highlight.js": "^9.13.1",
|
||||
"jimp": "^0.6.0",
|
||||
"jquery": "^3.3.1",
|
||||
"js-crc": "^0.2.0",
|
||||
"js-sha3": "^0.8.0",
|
||||
@@ -99,6 +100,7 @@
|
||||
"jsesc": "^2.5.1",
|
||||
"jsonpath": "^1.0.0",
|
||||
"jsonwebtoken": "^8.3.0",
|
||||
"jsqr": "^1.1.1",
|
||||
"jsrsasign": "8.0.12",
|
||||
"kbpgp": "^2.0.82",
|
||||
"lodash": "^4.17.11",
|
||||
@@ -113,6 +115,7 @@
|
||||
"nwmatcher": "^1.4.4",
|
||||
"otp": "^0.1.3",
|
||||
"popper.js": "^1.14.4",
|
||||
"qr-image": "^3.2.0",
|
||||
"scryptsy": "^2.0.0",
|
||||
"snackbarjs": "^1.1.0",
|
||||
"sortablejs": "^1.7.0",
|
||||
|
||||
@@ -25,6 +25,7 @@ class Ingredient {
|
||||
this.hint = "";
|
||||
this.toggleValues = [];
|
||||
this.target = null;
|
||||
this.defaultIndex = 0;
|
||||
|
||||
if (ingredientConfig) {
|
||||
this._parseConfig(ingredientConfig);
|
||||
@@ -46,6 +47,7 @@ class Ingredient {
|
||||
this.hint = ingredientConfig.hint || false;
|
||||
this.toggleValues = ingredientConfig.toggleValues;
|
||||
this.target = typeof ingredientConfig.target !== "undefined" ? ingredientConfig.target : null;
|
||||
this.defaultIndex = typeof ingredientConfig.defaultIndex !== "undefined" ? ingredientConfig.defaultIndex : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -181,6 +181,7 @@ class Operation {
|
||||
if (ing.hint) conf.hint = ing.hint;
|
||||
if (ing.disabled) conf.disabled = ing.disabled;
|
||||
if (ing.target) conf.target = ing.target;
|
||||
if (ing.defaultIndex) conf.defaultIndex = ing.defaultIndex;
|
||||
return conf;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"Change IP format",
|
||||
"Encode text",
|
||||
"Decode text",
|
||||
"Text Encoding Brute Force",
|
||||
"Swap endianness",
|
||||
"To MessagePack",
|
||||
"From MessagePack",
|
||||
@@ -340,23 +341,31 @@
|
||||
"From MessagePack"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Forensics",
|
||||
"ops": [
|
||||
"Detect File Type",
|
||||
"Scan for Embedded Files",
|
||||
"Remove EXIF",
|
||||
"Extract EXIF",
|
||||
"Render Image",
|
||||
"Play Media"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Other",
|
||||
"ops": [
|
||||
"Entropy",
|
||||
"Frequency distribution",
|
||||
"Chi Square",
|
||||
"Detect File Type",
|
||||
"Scan for Embedded Files",
|
||||
"Disassemble x86",
|
||||
"Pseudo-Random Number Generator",
|
||||
"Generate UUID",
|
||||
"Generate TOTP",
|
||||
"Generate HOTP",
|
||||
"Generate QR Code",
|
||||
"Parse QR Code",
|
||||
"Haversine distance",
|
||||
"Render Image",
|
||||
"Remove EXIF",
|
||||
"Extract EXIF",
|
||||
"Numberwang",
|
||||
"XKCD Random Number"
|
||||
]
|
||||
|
||||
@@ -265,9 +265,10 @@ class Magic {
|
||||
* performance)
|
||||
* @param {Object[]} [recipeConfig=[]] - The recipe configuration up to this point
|
||||
* @param {boolean} [useful=false] - Whether the current recipe should be scored highly
|
||||
* @param {string} [crib=null] - The regex crib provided by the user, for filtering the operation output
|
||||
* @returns {Object[]} - A sorted list of the recipes most likely to result in correct decoding
|
||||
*/
|
||||
async speculativeExecution(depth=0, extLang=false, intensive=false, recipeConfig=[], useful=false) {
|
||||
async speculativeExecution(depth=0, extLang=false, intensive=false, recipeConfig=[], useful=false, crib=null) {
|
||||
if (depth < 0) return [];
|
||||
|
||||
// Find any operations that can be run on this data
|
||||
@@ -284,9 +285,9 @@ class Magic {
|
||||
isUTF8: this.isUTF8(),
|
||||
entropy: this.calcEntropy(),
|
||||
matchingOps: matchingOps,
|
||||
useful: useful
|
||||
useful: useful,
|
||||
matchesCrib: crib && crib.test(this.inputStr)
|
||||
});
|
||||
|
||||
const prevOp = recipeConfig[recipeConfig.length - 1];
|
||||
|
||||
// Execute each of the matching operations, then recursively call the speculativeExecution()
|
||||
@@ -305,7 +306,7 @@ class Magic {
|
||||
|
||||
const magic = new Magic(output, this.opPatterns),
|
||||
speculativeResults = await magic.speculativeExecution(
|
||||
depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful);
|
||||
depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, crib);
|
||||
|
||||
results = results.concat(speculativeResults);
|
||||
}));
|
||||
@@ -317,7 +318,7 @@ class Magic {
|
||||
await Promise.all(bfEncodings.map(async enc => {
|
||||
const magic = new Magic(enc.data, this.opPatterns),
|
||||
bfResults = await magic.speculativeExecution(
|
||||
depth-1, extLang, false, [...recipeConfig, enc.conf]);
|
||||
depth-1, extLang, false, [...recipeConfig, enc.conf], false, crib);
|
||||
|
||||
results = results.concat(bfResults);
|
||||
}));
|
||||
|
||||
119
src/core/operations/GenerateQRCode.mjs
Normal file
119
src/core/operations/GenerateQRCode.mjs
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import qr from "qr-image";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import Magic from "../lib/Magic";
|
||||
import Utils from "../Utils";
|
||||
|
||||
/**
|
||||
* Generate QR Code operation
|
||||
*/
|
||||
class GenerateQRCode extends Operation {
|
||||
|
||||
/**
|
||||
* GenerateQRCode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Generate QR Code";
|
||||
this.module = "Image";
|
||||
this.description = "Generates a Quick Response (QR) code from the input text.<br><br>A QR code is a type of matrix barcode (or two-dimensional barcode) first designed in 1994 for the automotive industry in Japan. A barcode is a machine-readable optical label that contains information about the item to which it is attached.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/QR_code";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Image Format",
|
||||
"type": "option",
|
||||
"value": ["PNG", "SVG", "EPS", "PDF"]
|
||||
},
|
||||
{
|
||||
"name": "Module size (px)",
|
||||
"type": "number",
|
||||
"value": 5
|
||||
},
|
||||
{
|
||||
"name": "Margin (num modules)",
|
||||
"type": "number",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"name": "Error correction",
|
||||
"type": "option",
|
||||
"value": ["Low", "Medium", "Quartile", "High"],
|
||||
"defaultIndex": 1
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [format, size, margin, errorCorrection] = args;
|
||||
|
||||
// Create new QR image from the input data, and convert it to a buffer
|
||||
const qrImage = qr.imageSync(input, {
|
||||
type: format,
|
||||
size: size,
|
||||
margin: margin,
|
||||
"ec_level": errorCorrection.charAt(0).toUpperCase()
|
||||
});
|
||||
|
||||
if (qrImage == null) {
|
||||
throw new OperationError("Error generating QR code.");
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case "SVG":
|
||||
case "EPS":
|
||||
case "PDF":
|
||||
return [...Buffer.from(qrImage)];
|
||||
case "PNG":
|
||||
// Return the QR image buffer as a byte array
|
||||
return [...qrImage];
|
||||
default:
|
||||
throw new OperationError("Unsupported QR code format.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the QR image using HTML for web apps
|
||||
*
|
||||
* @param {byteArray} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data, args) {
|
||||
if (!data.length) return "";
|
||||
|
||||
const [format] = args;
|
||||
|
||||
if (format === "PNG") {
|
||||
let dataURI = "data:";
|
||||
const type = Magic.magicFileType(data);
|
||||
if (type && type.mime.indexOf("image") === 0){
|
||||
dataURI += type.mime + ";";
|
||||
} else {
|
||||
throw new OperationError("Invalid PNG file generated by QR image");
|
||||
}
|
||||
dataURI += "base64," + toBase64(data);
|
||||
|
||||
return `<img src="${dataURI}">`;
|
||||
}
|
||||
|
||||
return Utils.byteArrayToChars(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default GenerateQRCode;
|
||||
@@ -23,7 +23,7 @@ class Magic extends Operation {
|
||||
this.name = "Magic";
|
||||
this.flowControl = true;
|
||||
this.module = "Default";
|
||||
this.description = "The Magic operation attempts to detect various properties of the input data and suggests which operations could help to make more sense of it.<br><br><b>Options</b><br><u>Depth:</u> If an operation appears to match the data, it will be run and the result will be analysed further. This argument controls the maximum number of levels of recursion.<br><br><u>Intensive mode:</u> When this is turned on, various operations like XOR, bit rotates, and character encodings are brute-forced to attempt to detect valid data underneath. To improve performance, only the first 100 bytes of the data is brute-forced.<br><br><u>Extensive language support:</u> At each stage, the relative byte frequencies of the data will be compared to average frequencies for a number of languages. The default set consists of ~40 of the most commonly used languages on the Internet. The extensive list consists of 284 languages and can result in many languages matching the data if their byte frequencies are similar.";
|
||||
this.description = "The Magic operation attempts to detect various properties of the input data and suggests which operations could help to make more sense of it.<br><br><b>Options</b><br><u>Depth:</u> If an operation appears to match the data, it will be run and the result will be analysed further. This argument controls the maximum number of levels of recursion.<br><br><u>Intensive mode:</u> When this is turned on, various operations like XOR, bit rotates, and character encodings are brute-forced to attempt to detect valid data underneath. To improve performance, only the first 100 bytes of the data is brute-forced.<br><br><u>Extensive language support:</u> At each stage, the relative byte frequencies of the data will be compared to average frequencies for a number of languages. The default set consists of ~40 of the most commonly used languages on the Internet. The extensive list consists of 284 languages and can result in many languages matching the data if their byte frequencies are similar.<br><br>Optionally enter a regular expression to match a string you expect to find to filter results (crib).";
|
||||
this.infoURL = "https://github.com/gchq/CyberChef/wiki/Automatic-detection-of-encoded-data-using-CyberChef-Magic";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "JSON";
|
||||
@@ -43,6 +43,11 @@ class Magic extends Operation {
|
||||
"name": "Extensive language support",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"name": "Crib (known plaintext string or regex)",
|
||||
"type": "string",
|
||||
"value": ""
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -56,10 +61,16 @@ class Magic extends Operation {
|
||||
*/
|
||||
async run(state) {
|
||||
const ings = state.opList[state.progress].ingValues,
|
||||
[depth, intensive, extLang] = ings,
|
||||
[depth, intensive, extLang, crib] = ings,
|
||||
dish = state.dish,
|
||||
magic = new MagicLib(await dish.get(Dish.ARRAY_BUFFER)),
|
||||
options = await magic.speculativeExecution(depth, extLang, intensive);
|
||||
cribRegex = (crib && crib.length) ? new RegExp(crib, "i") : null;
|
||||
let options = await magic.speculativeExecution(depth, extLang, intensive, [], false, cribRegex);
|
||||
|
||||
// Filter down to results which matched the crib
|
||||
if (cribRegex) {
|
||||
options = options.filter(option => option.matchesCrib);
|
||||
}
|
||||
|
||||
// Record the current state for use when presenting
|
||||
this.state = state;
|
||||
|
||||
107
src/core/operations/ParseQRCode.mjs
Normal file
107
src/core/operations/ParseQRCode.mjs
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @author j433866 [j433866@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Magic from "../lib/Magic";
|
||||
import jsqr from "jsqr";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
* Parse QR Code operation
|
||||
*/
|
||||
class ParseQRCode extends Operation {
|
||||
|
||||
/**
|
||||
* ParseQRCode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Parse QR Code";
|
||||
this.module = "Image";
|
||||
this.description = "Reads an image file and attempts to detect and read a Quick Response (QR) code from the image.<br><br><u>Normalise Image</u><br>Attempts to normalise the image before parsing it to improve detection of a QR code.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/QR_code";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Normalise image",
|
||||
"type": "boolean",
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
async run(input, args) {
|
||||
const type = Magic.magicFileType(input);
|
||||
const [normalise] = args;
|
||||
|
||||
// Make sure that the input is an image
|
||||
if (type && type.mime.indexOf("image") === 0) {
|
||||
let image = input;
|
||||
|
||||
if (normalise) {
|
||||
// Process the image to be easier to read by jsqr
|
||||
// Disables the alpha channel
|
||||
// Sets the image default background to white
|
||||
// Normalises the image colours
|
||||
// Makes the image greyscale
|
||||
// Converts image to a JPEG
|
||||
image = await new Promise((resolve, reject) => {
|
||||
jimp.read(Buffer.from(input))
|
||||
.then(image => {
|
||||
image
|
||||
.rgba(false)
|
||||
.background(0xFFFFFFFF)
|
||||
.normalize()
|
||||
.greyscale()
|
||||
.getBuffer(jimp.MIME_JPEG, (error, result) => {
|
||||
resolve(result);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
reject(new OperationError("Error reading the image file."));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (image instanceof OperationError) {
|
||||
throw image;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
jimp.read(Buffer.from(image))
|
||||
.then(image => {
|
||||
if (image.bitmap != null) {
|
||||
const qrData = jsqr(image.bitmap.data, image.getWidth(), image.getHeight());
|
||||
if (qrData != null) {
|
||||
resolve(qrData.data);
|
||||
} else {
|
||||
reject(new OperationError("Couldn't read a QR code from the image."));
|
||||
}
|
||||
} else {
|
||||
reject(new OperationError("Error reading the image file."));
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
reject(new OperationError("Error reading the image file."));
|
||||
});
|
||||
});
|
||||
} else {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ParseQRCode;
|
||||
102
src/core/operations/PlayMedia.mjs
Normal file
102
src/core/operations/PlayMedia.mjs
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @author anthony-arnold [anthony.arnold@uqconnect.edu.au]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import { fromBase64, toBase64 } from "../lib/Base64";
|
||||
import { fromHex } from "../lib/Hex";
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Utils from "../Utils";
|
||||
import Magic from "../lib/Magic";
|
||||
|
||||
/**
|
||||
* PlayMedia operation
|
||||
*/
|
||||
class PlayMedia extends Operation {
|
||||
|
||||
/**
|
||||
* PlayMedia constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Play Media";
|
||||
this.module = "Default";
|
||||
this.description = "Plays the input as audio or video depending on the type.<br><br>Tags: sound, movie, mp3, mp4, mov, webm, wav, ogg";
|
||||
this.infoURL = "";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Input format",
|
||||
"type": "option",
|
||||
"value": ["Raw", "Base64", "Hex"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray} The multimedia data as bytes.
|
||||
*/
|
||||
run(input, args) {
|
||||
const [inputFormat] = args;
|
||||
|
||||
if (!input.length) return [];
|
||||
|
||||
// Convert input to raw bytes
|
||||
switch (inputFormat) {
|
||||
case "Hex":
|
||||
input = fromHex(input);
|
||||
break;
|
||||
case "Base64":
|
||||
// Don't trust the Base64 entered by the user.
|
||||
// Unwrap it first, then re-encode later.
|
||||
input = fromBase64(input, undefined, "byteArray");
|
||||
break;
|
||||
case "Raw":
|
||||
default:
|
||||
input = Utils.strToByteArray(input);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// Determine file type
|
||||
const type = Magic.magicFileType(input);
|
||||
if (!(type && /^audio|video/.test(type.mime))) {
|
||||
throw new OperationError("Invalid or unrecognised file type");
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an audio or video element that may be able to play the media
|
||||
* file.
|
||||
*
|
||||
* @param data {byteArray} Data containing an audio or video file.
|
||||
* @returns {string} Markup to display a media player.
|
||||
*/
|
||||
async present(data) {
|
||||
if (!data.length) return "";
|
||||
|
||||
const type = Magic.magicFileType(data);
|
||||
const matches = /^audio|video/.exec(type.mime);
|
||||
if (!matches) {
|
||||
throw new OperationError("Invalid file type");
|
||||
}
|
||||
const dataURI = `data:${type.mime};base64,${toBase64(data)}`;
|
||||
const element = matches[0];
|
||||
|
||||
let html = `<${element} src='${dataURI}' type='${type.mime}' controls>`;
|
||||
html += "<p>Unsupported media type.</p>";
|
||||
html += `</${element}>`;
|
||||
return html;
|
||||
}
|
||||
}
|
||||
|
||||
export default PlayMedia;
|
||||
92
src/core/operations/TextEncodingBruteForce.mjs
Normal file
92
src/core/operations/TextEncodingBruteForce.mjs
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @author Cynser
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import cptable from "../vendor/js-codepage/cptable.js";
|
||||
import {IO_FORMAT} from "../lib/ChrEnc";
|
||||
|
||||
/**
|
||||
* Text Encoding Brute Force operation
|
||||
*/
|
||||
class TextEncodingBruteForce extends Operation {
|
||||
|
||||
/**
|
||||
* TextEncodingBruteForce constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Text Encoding Brute Force";
|
||||
this.module = "CharEnc";
|
||||
this.description = [
|
||||
"Enumerates all supported text encodings for the input, allowing you to quickly spot the correct one.",
|
||||
"<br><br>",
|
||||
"Supported charsets are:",
|
||||
"<ul>",
|
||||
Object.keys(IO_FORMAT).map(e => `<li>${e}</li>`).join("\n"),
|
||||
"</ul>"
|
||||
].join("\n");
|
||||
this.infoURL = "https://wikipedia.org/wiki/Character_encoding";
|
||||
this.inputType = "string";
|
||||
this.outputType = "json";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
name: "Mode",
|
||||
type: "option",
|
||||
value: ["Encode", "Decode"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {json}
|
||||
*/
|
||||
run(input, args) {
|
||||
const output = {},
|
||||
charsets = Object.keys(IO_FORMAT),
|
||||
mode = args[0];
|
||||
|
||||
charsets.forEach(charset => {
|
||||
try {
|
||||
if (mode === "Decode") {
|
||||
output[charset] = cptable.utils.decode(IO_FORMAT[charset], input);
|
||||
} else {
|
||||
output[charset] = Utils.arrayBufferToStr(cptable.utils.encode(IO_FORMAT[charset], input));
|
||||
}
|
||||
} catch (err) {
|
||||
output[charset] = "Could not decode.";
|
||||
}
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the encodings in an HTML table for web apps.
|
||||
*
|
||||
* @param {Object[]} encodings
|
||||
* @returns {html}
|
||||
*/
|
||||
present(encodings) {
|
||||
let table = "<table class='table table-hover table-sm table-bordered table-nonfluid'><tr><th>Encoding</th><th>Value</th></tr>";
|
||||
|
||||
for (const enc in encodings) {
|
||||
const value = Utils.printable(encodings[enc], true);
|
||||
table += `<tr><td>${enc}</td><td>${value}</td></tr>`;
|
||||
}
|
||||
|
||||
table += "<table>";
|
||||
return table;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default TextEncodingBruteForce;
|
||||
@@ -23,7 +23,7 @@ class ToBase62 extends Operation {
|
||||
this.name = "To Base62";
|
||||
this.module = "Default";
|
||||
this.description = "Base62 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. The high number base results in shorter strings than with the decimal or hexadecimal system.";
|
||||
this.infoURL = "https://en.wikipedia.org/wiki/List_of_numeral_systems";
|
||||
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
|
||||
@@ -26,6 +26,7 @@ class HTMLIngredient {
|
||||
this.disabled = config.disabled || false;
|
||||
this.hint = config.hint || false;
|
||||
this.target = config.target;
|
||||
this.defaultIndex = config.defaultIndex || 0;
|
||||
this.toggleValues = config.toggleValues;
|
||||
this.id = "ing-" + this.app.nextIngId();
|
||||
}
|
||||
@@ -133,7 +134,7 @@ class HTMLIngredient {
|
||||
} else if ((m = this.value[i].match(/\[\/([a-z0-9 -()^]+)\]/i))) {
|
||||
html += "</optgroup>";
|
||||
} else {
|
||||
html += `<option>${this.value[i]}</option>`;
|
||||
html += `<option ${this.defaultIndex === i ? "selected" : ""}>${this.value[i]}</option>`;
|
||||
}
|
||||
}
|
||||
html += `</select>
|
||||
@@ -154,7 +155,7 @@ class HTMLIngredient {
|
||||
} else if ((m = this.value[i].name.match(/\[\/([a-z0-9 -()^]+)\]/i))) {
|
||||
html += "</optgroup>";
|
||||
} else {
|
||||
html += `<option populate-value='${this.value[i].value}'>${this.value[i].name}</option>`;
|
||||
html += `<option populate-value="${this.value[i].value}">${this.value[i].name}</option>`;
|
||||
}
|
||||
}
|
||||
html += `</select>
|
||||
@@ -170,7 +171,7 @@ class HTMLIngredient {
|
||||
class="form-control arg inline"
|
||||
id="${this.id}"
|
||||
arg-name="${this.name}"
|
||||
value="${this.value[0].value}"
|
||||
value="${this.value[this.defaultIndex].value}"
|
||||
${this.disabled ? "disabled" : ""}>
|
||||
${this.hint ? "<span class='bmd-help'>" + this.hint + "</span>" : ""}
|
||||
<div class="input-group-append inline">
|
||||
|
||||
@@ -64,6 +64,7 @@ import "./tests/operations/OTP";
|
||||
import "./tests/operations/PGP";
|
||||
import "./tests/operations/PHP";
|
||||
import "./tests/operations/ParseIPRange";
|
||||
import "./tests/operations/ParseQRCode";
|
||||
import "./tests/operations/PowerSet";
|
||||
import "./tests/operations/Regex";
|
||||
import "./tests/operations/Register";
|
||||
@@ -75,10 +76,12 @@ import "./tests/operations/SetIntersection";
|
||||
import "./tests/operations/SetUnion";
|
||||
import "./tests/operations/StrUtils";
|
||||
import "./tests/operations/SymmetricDifference";
|
||||
import "./tests/operations/TextEncodingBruteForce";
|
||||
import "./tests/operations/ToGeohash.mjs";
|
||||
import "./tests/operations/TranslateDateTimeFormat";
|
||||
import "./tests/operations/Magic";
|
||||
import "./tests/operations/ParseTLV";
|
||||
import "./tests/operations/Media";
|
||||
|
||||
let allTestsPassing = true;
|
||||
const testStatusCounts = {
|
||||
|
||||
43
test/tests/operations/Media.mjs
Normal file
43
test/tests/operations/Media.mjs
Normal file
File diff suppressed because one or more lines are too long
71
test/tests/operations/ParseQRCode.mjs
Normal file
71
test/tests/operations/ParseQRCode.mjs
Normal file
File diff suppressed because one or more lines are too long
35
test/tests/operations/TextEncodingBruteForce.mjs
Normal file
35
test/tests/operations/TextEncodingBruteForce.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Text Encoding Brute Force tests.
|
||||
*
|
||||
* @author Cynser
|
||||
*
|
||||
* @copyright Crown Copyright 2018
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../TestRegister";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "Text Encoding Brute Force - Encode",
|
||||
input: "Булкі праз ляніва сабаку.",
|
||||
expectedMatch: /Windows-1251 Cyrillic \(1251\).{1,10}Булкі праз ляніва сабаку\./,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Text Encoding Brute Force",
|
||||
args: ["Encode"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Text Encoding Brute Force - Decode",
|
||||
input: "Áóëê³ ïðàç ëÿí³âà ñàáàêó.",
|
||||
expectedMatch: /Windows-1251 Cyrillic \(1251\).{1,10}Булкі праз ляніва сабаку\./,
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Text Encoding Brute Force",
|
||||
args: ["Decode"],
|
||||
},
|
||||
],
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -46,6 +46,9 @@ module.exports = {
|
||||
raw: true,
|
||||
entryOnly: true
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
"process.browser": "true"
|
||||
}),
|
||||
vendorCSS,
|
||||
projectCSS
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user