mirror of
https://github.com/gchq/CyberChef.git
synced 2024-11-02 14:11:02 +01:00
Ported Image operations + some cleanup
This commit is contained in:
parent
eb3a2502f5
commit
709630f39b
56
src/core/operations/ExtractEXIF.mjs
Normal file
56
src/core/operations/ExtractEXIF.mjs
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import ExifParser from "exif-parser";
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Extract EXIF operation
|
||||
*/
|
||||
class ExtractEXIF extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractEXIF constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract EXIF";
|
||||
this.module = "Image";
|
||||
this.description = "Extracts EXIF data from an image.\n<br><br>\nEXIF data is metadata embedded in images (JPEG, JPG, TIFF) and audio files.\n<br><br>\nEXIF data from photos usually contains information about the image file itself as well as the device used to create it.";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
try {
|
||||
const parser = ExifParser.create(input);
|
||||
const result = parser.parse();
|
||||
|
||||
const lines = [];
|
||||
for (const tagName in result.tags) {
|
||||
const value = result.tags[tagName];
|
||||
lines.push(`${tagName}: ${value}`);
|
||||
}
|
||||
|
||||
const numTags = lines.length;
|
||||
lines.unshift(`Found ${numTags} tags.\n`);
|
||||
return lines.join("\n");
|
||||
} catch (err) {
|
||||
throw new OperationError(`Could not extract EXIF data from image: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default ExtractEXIF;
|
50
src/core/operations/RemoveEXIF.mjs
Normal file
50
src/core/operations/RemoveEXIF.mjs
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import { removeEXIF } from "../vendor/remove-exif";
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
|
||||
/**
|
||||
* Remove EXIF operation
|
||||
*/
|
||||
class RemoveEXIF extends Operation {
|
||||
|
||||
/**
|
||||
* RemoveEXIF constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Remove EXIF";
|
||||
this.module = "Image";
|
||||
this.description = "Removes EXIF data from a JPEG image.\n<br><br>\nEXIF data embedded in photos usually contains information about the image file itself as well as the device used to create it.";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
// Do nothing if input is empty
|
||||
if (input.length === 0) return input;
|
||||
|
||||
try {
|
||||
return removeEXIF(input);
|
||||
} catch (err) {
|
||||
// Simply return input if no EXIF data is found
|
||||
if (err === "Exif not found.") return input;
|
||||
throw new OperationError(`Could not remove EXIF data from image: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RemoveEXIF;
|
91
src/core/operations/RenderImage.mjs
Normal file
91
src/core/operations/RenderImage.mjs
Normal file
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import { fromBase64, toBase64 } from "../lib/Base64";
|
||||
import { fromHex } from "../lib/Hex";
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
import Magic from "../lib/Magic";
|
||||
|
||||
/**
|
||||
* Render Image operation
|
||||
*/
|
||||
class RenderImage extends Operation {
|
||||
|
||||
/**
|
||||
* RenderImage constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Render Image";
|
||||
this.module = "Image";
|
||||
this.description = "Displays the input as an image. Supports the following formats:<br><br><ul><li>jpg/jpeg</li><li>png</li><li>gif</li><li>webp</li><li>bmp</li><li>ico</li></ul>";
|
||||
this.inputType = "string";
|
||||
this.outputType = "html";
|
||||
this.args = [
|
||||
{
|
||||
"name": "Input format",
|
||||
"type": "option",
|
||||
"value": ["Raw", "Base64", "Hex"]
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
{
|
||||
"match": "^(?:\\xff\\xd8\\xff|\\x89\\x50\\x4e\\x47|\\x47\\x49\\x46|.{8}\\x57\\x45\\x42\\x50|\\x42\\x4d)",
|
||||
"flags": "",
|
||||
"args": [
|
||||
"Raw"
|
||||
],
|
||||
"useful": true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
run(input, args) {
|
||||
const inputFormat = args[0];
|
||||
let dataURI = "data:";
|
||||
|
||||
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 && type.mime.indexOf("image") === 0) {
|
||||
dataURI += type.mime + ";";
|
||||
} else {
|
||||
throw "Invalid file type";
|
||||
}
|
||||
|
||||
// Add image data to URI
|
||||
dataURI += "base64," + toBase64(input);
|
||||
|
||||
return "<img src='" + dataURI + "'>";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default RenderImage;
|
@ -1,540 +0,0 @@
|
||||
import {camelCase, kebabCase, snakeCase} from "lodash";
|
||||
import vkbeautify from "vkbeautify";
|
||||
import {DOMParser} from "xmldom";
|
||||
import xpath from "xpath";
|
||||
import jpath from "jsonpath";
|
||||
import nwmatcher from "nwmatcher";
|
||||
import hljs from "highlight.js";
|
||||
|
||||
|
||||
/**
|
||||
* Code operations.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const Code = {
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
LANGUAGES: ["auto detect"].concat(hljs.listLanguages()),
|
||||
|
||||
/**
|
||||
* Syntax highlighter operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
runSyntaxHighlight: function(input, args) {
|
||||
const language = args[0];
|
||||
|
||||
if (language === "auto detect") {
|
||||
return hljs.highlightAuto(input).value;
|
||||
}
|
||||
|
||||
return hljs.highlight(language, input, true).value;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
BEAUTIFY_INDENT: "\\t",
|
||||
|
||||
/**
|
||||
* XML Beautify operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runXmlBeautify: function(input, args) {
|
||||
const indentStr = args[0];
|
||||
return vkbeautify.xml(input, indentStr);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* JSON Beautify operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runJsonBeautify: function(input, args) {
|
||||
const indentStr = args[0];
|
||||
if (!input) return "";
|
||||
return vkbeautify.json(input, indentStr);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* CSS Beautify operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runCssBeautify: function(input, args) {
|
||||
const indentStr = args[0];
|
||||
return vkbeautify.css(input, indentStr);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* SQL Beautify operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runSqlBeautify: function(input, args) {
|
||||
const indentStr = args[0];
|
||||
return vkbeautify.sql(input, indentStr);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
PRESERVE_COMMENTS: false,
|
||||
|
||||
/**
|
||||
* XML Minify operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runXmlMinify: function(input, args) {
|
||||
const preserveComments = args[0];
|
||||
return vkbeautify.xmlmin(input, preserveComments);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* JSON Minify operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runJsonMinify: function(input, args) {
|
||||
if (!input) return "";
|
||||
return vkbeautify.jsonmin(input);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* CSS Minify operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runCssMinify: function(input, args) {
|
||||
const preserveComments = args[0];
|
||||
return vkbeautify.cssmin(input, preserveComments);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* SQL Minify operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runSqlMinify: function(input, args) {
|
||||
return vkbeautify.sqlmin(input);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Generic Code Beautify operation.
|
||||
*
|
||||
* Yeeeaaah...
|
||||
*
|
||||
* I'm not proud of this code, but seriously, try writing a generic lexer and parser that
|
||||
* correctly generates an AST for multiple different languages. I have tried, and I can tell
|
||||
* you it's pretty much impossible.
|
||||
*
|
||||
* This basically works. That'll have to be good enough. It's not meant to produce working code,
|
||||
* just slightly more readable code.
|
||||
*
|
||||
* Things that don't work:
|
||||
* - For loop formatting
|
||||
* - Do-While loop formatting
|
||||
* - Switch/Case indentation
|
||||
* - Bit shift operators
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runGenericBeautify: function(input, args) {
|
||||
let code = input,
|
||||
t = 0,
|
||||
preservedTokens = [],
|
||||
m;
|
||||
|
||||
// Remove strings
|
||||
const sstrings = /'([^'\\]|\\.)*'/g;
|
||||
while ((m = sstrings.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
sstrings.lastIndex = m.index;
|
||||
}
|
||||
|
||||
const dstrings = /"([^"\\]|\\.)*"/g;
|
||||
while ((m = dstrings.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
dstrings.lastIndex = m.index;
|
||||
}
|
||||
|
||||
// Remove comments
|
||||
const scomments = /\/\/[^\n\r]*/g;
|
||||
while ((m = scomments.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
scomments.lastIndex = m.index;
|
||||
}
|
||||
|
||||
const mcomments = /\/\*[\s\S]*?\*\//gm;
|
||||
while ((m = mcomments.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
mcomments.lastIndex = m.index;
|
||||
}
|
||||
|
||||
const hcomments = /(^|\n)#[^\n\r#]+/g;
|
||||
while ((m = hcomments.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
hcomments.lastIndex = m.index;
|
||||
}
|
||||
|
||||
// Remove regexes
|
||||
const regexes = /\/.*?[^\\]\/[gim]{0,3}/gi;
|
||||
while ((m = regexes.exec(code))) {
|
||||
code = preserveToken(code, m, t++);
|
||||
regexes.lastIndex = m.index;
|
||||
}
|
||||
|
||||
code = code
|
||||
// Create newlines after ;
|
||||
.replace(/;/g, ";\n")
|
||||
// Create newlines after { and around }
|
||||
.replace(/{/g, "{\n")
|
||||
.replace(/}/g, "\n}\n")
|
||||
// Remove carriage returns
|
||||
.replace(/\r/g, "")
|
||||
// Remove all indentation
|
||||
.replace(/^\s+/g, "")
|
||||
.replace(/\n\s+/g, "\n")
|
||||
// Remove trailing spaces
|
||||
.replace(/\s*$/g, "")
|
||||
.replace(/\n{/g, "{");
|
||||
|
||||
// Indent
|
||||
let i = 0,
|
||||
level = 0,
|
||||
indent;
|
||||
while (i < code.length) {
|
||||
switch (code[i]) {
|
||||
case "{":
|
||||
level++;
|
||||
break;
|
||||
case "\n":
|
||||
if (i+1 >= code.length) break;
|
||||
|
||||
if (code[i+1] === "}") level--;
|
||||
indent = (level >= 0) ? Array(level*4+1).join(" ") : "";
|
||||
|
||||
code = code.substring(0, i+1) + indent + code.substring(i+1);
|
||||
if (level > 0) i += level*4;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
code = code
|
||||
// Add strategic spaces
|
||||
.replace(/\s*([!<>=+-/*]?)=\s*/g, " $1= ")
|
||||
.replace(/\s*<([=]?)\s*/g, " <$1 ")
|
||||
.replace(/\s*>([=]?)\s*/g, " >$1 ")
|
||||
.replace(/([^+])\+([^+=])/g, "$1 + $2")
|
||||
.replace(/([^-])-([^-=])/g, "$1 - $2")
|
||||
.replace(/([^*])\*([^*=])/g, "$1 * $2")
|
||||
.replace(/([^/])\/([^/=])/g, "$1 / $2")
|
||||
.replace(/\s*,\s*/g, ", ")
|
||||
.replace(/\s*{/g, " {")
|
||||
.replace(/}\n/g, "}\n\n")
|
||||
// Hacky horribleness
|
||||
.replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)\s*\n([^{])/gim, "$1 ($2)\n $3")
|
||||
.replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)([^{])/gim, "$1 ($2) $3")
|
||||
.replace(/else\s*\n([^{])/gim, "else\n $1")
|
||||
.replace(/else\s+([^{])/gim, "else $1")
|
||||
// Remove strategic spaces
|
||||
.replace(/\s+;/g, ";")
|
||||
.replace(/\{\s+\}/g, "{}")
|
||||
.replace(/\[\s+\]/g, "[]")
|
||||
.replace(/}\s*(else|catch|except|finally|elif|elseif|else if)/gi, "} $1");
|
||||
|
||||
// Replace preserved tokens
|
||||
const ptokens = /###preservedToken(\d+)###/g;
|
||||
while ((m = ptokens.exec(code))) {
|
||||
const ti = parseInt(m[1], 10);
|
||||
code = code.substring(0, m.index) + preservedTokens[ti] + code.substring(m.index + m[0].length);
|
||||
ptokens.lastIndex = m.index;
|
||||
}
|
||||
|
||||
return code;
|
||||
|
||||
/**
|
||||
* Replaces a matched token with a placeholder value.
|
||||
*/
|
||||
function preserveToken(str, match, t) {
|
||||
preservedTokens[t] = match[0];
|
||||
return str.substring(0, match.index) +
|
||||
"###preservedToken" + t + "###" +
|
||||
str.substring(match.index + match[0].length);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
XPATH_INITIAL: "",
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
XPATH_DELIMITER: "\\n",
|
||||
|
||||
/**
|
||||
* XPath expression operation.
|
||||
*
|
||||
* @author Mikescher (https://github.com/Mikescher | https://mikescher.com)
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runXpath: function(input, args) {
|
||||
let query = args[0],
|
||||
delimiter = args[1];
|
||||
|
||||
let doc;
|
||||
try {
|
||||
doc = new DOMParser().parseFromString(input, "application/xml");
|
||||
} catch (err) {
|
||||
return "Invalid input XML.";
|
||||
}
|
||||
|
||||
let nodes;
|
||||
try {
|
||||
nodes = xpath.select(query, doc);
|
||||
} catch (err) {
|
||||
return "Invalid XPath. Details:\n" + err.message;
|
||||
}
|
||||
|
||||
const nodeToString = function(node) {
|
||||
return node.toString();
|
||||
};
|
||||
|
||||
return nodes.map(nodeToString).join(delimiter);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
JPATH_INITIAL: "",
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
JPATH_DELIMITER: "\\n",
|
||||
|
||||
/**
|
||||
* JPath expression operation.
|
||||
*
|
||||
* @author Matt C (matt@artemisbot.uk)
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runJpath: function(input, args) {
|
||||
let query = args[0],
|
||||
delimiter = args[1],
|
||||
results,
|
||||
obj;
|
||||
|
||||
try {
|
||||
obj = JSON.parse(input);
|
||||
} catch (err) {
|
||||
return "Invalid input JSON: " + err.message;
|
||||
}
|
||||
|
||||
try {
|
||||
results = jpath.query(obj, query);
|
||||
} catch (err) {
|
||||
return "Invalid JPath expression: " + err.message;
|
||||
}
|
||||
|
||||
return results.map(result => JSON.stringify(result)).join(delimiter);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
CSS_SELECTOR_INITIAL: "",
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
CSS_QUERY_DELIMITER: "\\n",
|
||||
|
||||
/**
|
||||
* CSS selector operation.
|
||||
*
|
||||
* @author Mikescher (https://github.com/Mikescher | https://mikescher.com)
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runCSSQuery: function(input, args) {
|
||||
let query = args[0],
|
||||
delimiter = args[1],
|
||||
parser = new DOMParser(),
|
||||
dom,
|
||||
result;
|
||||
|
||||
if (!query.length || !input.length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
dom = parser.parseFromString(input);
|
||||
} catch (err) {
|
||||
return "Invalid input HTML.";
|
||||
}
|
||||
|
||||
try {
|
||||
const matcher = nwmatcher({document: dom});
|
||||
result = matcher.select(query, dom);
|
||||
} catch (err) {
|
||||
return "Invalid CSS Selector. Details:\n" + err.message;
|
||||
}
|
||||
|
||||
const nodeToString = function(node) {
|
||||
return node.toString();
|
||||
/* xmldom does not return the outerHTML value.
|
||||
switch (node.nodeType) {
|
||||
case node.ELEMENT_NODE: return node.outerHTML;
|
||||
case node.ATTRIBUTE_NODE: return node.value;
|
||||
case node.TEXT_NODE: return node.wholeText;
|
||||
case node.COMMENT_NODE: return node.data;
|
||||
case node.DOCUMENT_NODE: return node.outerHTML;
|
||||
default: throw new Error("Unknown Node Type: " + node.nodeType);
|
||||
}*/
|
||||
};
|
||||
|
||||
return result
|
||||
.map(nodeToString)
|
||||
.join(delimiter);
|
||||
},
|
||||
|
||||
/**
|
||||
* This tries to rename variable names in a code snippet according to a function.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {function} replacer - this function will be fed the token which should be renamed.
|
||||
* @returns {string}
|
||||
*/
|
||||
_replaceVariableNames(input, replacer) {
|
||||
const tokenRegex = /\\"|"(?:\\"|[^"])*"|(\b[a-z0-9\-_]+\b)/ig;
|
||||
|
||||
return input.replace(tokenRegex, (...args) => {
|
||||
let match = args[0],
|
||||
quotes = args[1];
|
||||
|
||||
if (!quotes) {
|
||||
return match;
|
||||
} else {
|
||||
return replacer(match);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* To Snake Case operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runToSnakeCase(input, args) {
|
||||
const smart = args[0];
|
||||
|
||||
if (smart) {
|
||||
return Code._replaceVariableNames(input, snakeCase);
|
||||
} else {
|
||||
return snakeCase(input);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* To Camel Case operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runToCamelCase(input, args) {
|
||||
const smart = args[0];
|
||||
|
||||
if (smart) {
|
||||
return Code._replaceVariableNames(input, camelCase);
|
||||
} else {
|
||||
return camelCase(input);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* To Kebab Case operation.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runToKebabCase(input, args) {
|
||||
const smart = args[0];
|
||||
|
||||
if (smart) {
|
||||
return Code._replaceVariableNames(input, kebabCase);
|
||||
} else {
|
||||
return kebabCase(input);
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default Code;
|
@ -1,125 +0,0 @@
|
||||
import * as ExifParser from "exif-parser";
|
||||
import removeEXIF from "../vendor/remove-exif.js";
|
||||
import Utils from "../Utils.js";
|
||||
import FileType from "./FileType.js";
|
||||
import {fromBase64, toBase64} from "../lib/Base64";
|
||||
import {fromHex} from "../lib/Hex";
|
||||
|
||||
|
||||
/**
|
||||
* Image operations.
|
||||
*
|
||||
* @author tlwr [toby@toby.codes]
|
||||
* @copyright Crown Copyright 2017
|
||||
* @license Apache-2.0
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
const Image = {
|
||||
|
||||
/**
|
||||
* Extract EXIF operation.
|
||||
*
|
||||
* Extracts EXIF data from a byteArray, representing a JPG or a TIFF image.
|
||||
*
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
runExtractEXIF(input, args) {
|
||||
try {
|
||||
const parser = ExifParser.create(input);
|
||||
const result = parser.parse();
|
||||
|
||||
let lines = [];
|
||||
for (let tagName in result.tags) {
|
||||
let value = result.tags[tagName];
|
||||
lines.push(`${tagName}: ${value}`);
|
||||
}
|
||||
|
||||
const numTags = lines.length;
|
||||
lines.unshift(`Found ${numTags} tags.\n`);
|
||||
return lines.join("\n");
|
||||
} catch (err) {
|
||||
throw "Could not extract EXIF data from image: " + err;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Remove EXIF operation.
|
||||
*
|
||||
* Removes EXIF data from a byteArray, representing a JPG.
|
||||
*
|
||||
* @author David Moodie [davidmoodie12@gmail.com]
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
runRemoveEXIF(input, args) {
|
||||
// Do nothing if input is empty
|
||||
if (input.length === 0) return input;
|
||||
|
||||
try {
|
||||
return removeEXIF(input);
|
||||
} catch (err) {
|
||||
// Simply return input if no EXIF data is found
|
||||
if (err === "Exif not found.") return input;
|
||||
throw "Could not remove EXIF data from image: " + err;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @constant
|
||||
* @default
|
||||
*/
|
||||
INPUT_FORMAT: ["Raw", "Base64", "Hex"],
|
||||
|
||||
/**
|
||||
* Render Image operation.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
runRenderImage(input, args) {
|
||||
const inputFormat = args[0];
|
||||
let dataURI = "data:";
|
||||
|
||||
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, null, "byteArray");
|
||||
break;
|
||||
case "Raw":
|
||||
default:
|
||||
input = Utils.strToByteArray(input);
|
||||
break;
|
||||
}
|
||||
|
||||
// Determine file type
|
||||
const type = FileType.magicType(input);
|
||||
if (type && type.mime.indexOf("image") === 0) {
|
||||
dataURI += type.mime + ";";
|
||||
} else {
|
||||
throw "Invalid file type";
|
||||
}
|
||||
|
||||
// Add image data to URI
|
||||
dataURI += "base64," + toBase64(input);
|
||||
|
||||
return "<img src='" + dataURI + "'>";
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default Image;
|
@ -18,10 +18,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import Utils from "../Utils.js";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
// Param jpeg should be a binaryArray
|
||||
function removeEXIF(jpeg) {
|
||||
export function removeEXIF(jpeg) {
|
||||
// Convert binaryArray to char string
|
||||
jpeg = Utils.byteArrayToChars(jpeg);
|
||||
if (jpeg.slice(0, 2) != "\xff\xd8") {
|
||||
@ -149,5 +149,3 @@ function unpack(mark, str) {
|
||||
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
export default removeEXIF;
|
@ -45,7 +45,7 @@ import "./tests/operations/Register";
|
||||
import "./tests/operations/Comment";
|
||||
import "./tests/operations/Hash";
|
||||
import "./tests/operations/Hexdump";
|
||||
// import "./tests/operations/Image";
|
||||
import "./tests/operations/Image";
|
||||
import "./tests/operations/MorseCode";
|
||||
import "./tests/operations/MS";
|
||||
import "./tests/operations/PHP";
|
||||
|
@ -57,7 +57,7 @@ TestRegister.addTests([
|
||||
{
|
||||
name: "Extract EXIF: hello world text (error)",
|
||||
input: "hello world",
|
||||
expectedError: true,
|
||||
expectedOutput: "Could not extract EXIF data from image: Error: Invalid JPEG section offset",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Extract EXIF",
|
||||
@ -129,7 +129,7 @@ TestRegister.addTests([
|
||||
{
|
||||
name: "Remove EXIF: hello world text (error)",
|
||||
input: "hello world",
|
||||
expectedError: true,
|
||||
expectedOutput: "Could not remove EXIF data from image: Given data is not jpeg.",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "Remove EXIF",
|
||||
|
Loading…
Reference in New Issue
Block a user