Browse Source

Merge branch 'artemisbot-features/colour-channel'

n1474335 6 years ago
parent
commit
7a4eff0f5c

+ 5 - 0
CHANGELOG.md

@@ -2,6 +2,9 @@
 All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master).
 
 
+### [8.18.0] - 2018-12-26
+- 'Split Colour Channels' operation added [@artemisbot] | [#449]
+
 ### [8.17.0] - 2018-12-25
 - 'Generate QR Code' and 'Parse QR Code' operations added [@j433866] | [#448]
 
@@ -82,6 +85,7 @@ All major and minor version changes will be documented in this file. Details of
 
 
 
+[8.18.0]: https://github.com/gchq/CyberChef/releases/tag/v8.18.0
 [8.17.0]: https://github.com/gchq/CyberChef/releases/tag/v8.17.0
 [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
@@ -150,3 +154,4 @@ All major and minor version changes will be documented in this file. Details of
 [#443]: https://github.com/gchq/CyberChef/pull/443
 [#446]: https://github.com/gchq/CyberChef/pull/446
 [#448]: https://github.com/gchq/CyberChef/pull/448
+[#449]: https://github.com/gchq/CyberChef/pull/449

+ 13 - 2
src/core/Utils.mjs

@@ -5,7 +5,7 @@
  */
 
 import utf8 from "utf8";
-import {fromBase64} from "./lib/Base64";
+import {fromBase64, toBase64} from "./lib/Base64";
 import {fromHex} from "./lib/Hex";
 import {fromDecimal} from "./lib/Decimal";
 import {fromBinary} from "./lib/Binary";
@@ -817,6 +817,17 @@ class Utils {
             return html;
         };
 
+        const formatContent = function (buff, type) {
+            if (type.startsWith("image")) {
+                let dataURI = "data:";
+                dataURI += type + ";";
+                dataURI += "base64," + toBase64(buff);
+                return "<img style='max-width: 100%;' src='" + dataURI + "'>";
+            } else {
+                return `<pre>${Utils.escapeHtml(Utils.arrayBufferToStr(buff.buffer))}</pre>`;
+            }
+        };
+
         const formatFile = async function(file, i) {
             const buff = await Utils.readFile(file);
             const blob = new Blob(
@@ -846,7 +857,7 @@ class Utils {
                     </div>
                     <div id='collapse${i}' class='collapse' aria-labelledby='heading${i}' data-parent="#files">
                         <div class='card-body'>
-                            <pre>${Utils.escapeHtml(Utils.arrayBufferToStr(buff.buffer))}</pre>
+                            ${formatContent(buff, file.type)}
                         </div>
                     </div>
                 </div>`;

+ 10 - 2
src/core/config/Categories.json

@@ -347,9 +347,17 @@
             "Detect File Type",
             "Scan for Embedded Files",
             "Remove EXIF",
-            "Extract EXIF",
+            "Extract EXIF"
+        ]
+    },
+    {
+        "name": "Multimedia",
+        "ops": [
             "Render Image",
-            "Play Media"
+            "Play Media",
+            "Remove EXIF",
+            "Extract EXIF",
+            "Split Colour Channels"
         ]
     },
     {

+ 105 - 0
src/core/operations/SplitColourChannels.mjs

@@ -0,0 +1,105 @@
+/**
+ * @author Matt C [matt@artemisbot.uk]
+ * @copyright Crown Copyright 2018
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation";
+import OperationError from "../errors/OperationError";
+import Utils from "../Utils";
+import Magic from "../lib/Magic";
+
+import jimp from "jimp";
+
+/**
+ * Split Colour Channels operation
+ */
+class SplitColourChannels extends Operation {
+
+    /**
+     * SplitColourChannels constructor
+     */
+    constructor() {
+        super();
+
+        this.name = "Split Colour Channels";
+        this.module = "Image";
+        this.description = "Splits the given image into its red, green and blue colour channels.";
+        this.infoURL = "https://wikipedia.org/wiki/Channel_(digital_image)";
+        this.inputType = "byteArray";
+        this.outputType = "List<File>";
+        this.presentType = "html";
+        this.args = [];
+    }
+
+    /**
+     * @param {byteArray} input
+     * @param {Object[]} args
+     * @returns {List<File>}
+     */
+    async run(input, args) {
+        const type = Magic.magicFileType(input);
+        // Make sure that the input is an image
+        if (type && type.mime.indexOf("image") === 0) {
+            const parsedImage = await jimp.read(Buffer.from(input));
+
+            const red = new Promise(async (resolve, reject) => {
+                try {
+                    const split = parsedImage
+                        .clone()
+                        .color([
+                            {apply: "blue", params: [-255]},
+                            {apply: "green", params: [-255]}
+                        ])
+                        .getBufferAsync(jimp.MIME_PNG);
+                    resolve(new File([new Uint8Array((await split).values())], "red.png", {type: "image/png"}));
+                } catch (err) {
+                    reject(new OperationError(`Could not split red channel: ${err}`));
+                }
+            });
+
+            const green = new Promise(async (resolve, reject) => {
+                try {
+                    const split = parsedImage.clone()
+                        .color([
+                            {apply: "red", params: [-255]},
+                            {apply: "blue", params: [-255]},
+                        ]).getBufferAsync(jimp.MIME_PNG);
+                    resolve(new File([new Uint8Array((await split).values())], "green.png", {type: "image/png"}));
+                } catch (err) {
+                    reject(new OperationError(`Could not split green channel: ${err}`));
+                }
+            });
+
+            const blue = new Promise(async (resolve, reject) => {
+                try {
+                    const split = parsedImage
+                        .color([
+                            {apply: "red", params: [-255]},
+                            {apply: "green", params: [-255]},
+                        ]).getBufferAsync(jimp.MIME_PNG);
+                    resolve(new File([new Uint8Array((await split).values())], "blue.png", {type: "image/png"}));
+                } catch (err) {
+                    reject(new OperationError(`Could not split blue channel: ${err}`));
+                }
+            });
+
+            return await Promise.all([red, green, blue]);
+        } else {
+            throw new OperationError("Invalid file type.");
+        }
+    }
+
+    /**
+     * Displays the files in HTML for web apps.
+     *
+     * @param {File[]} files
+     * @returns {html}
+     */
+    async present(files) {
+        return await Utils.displayFilesAsHTML(files);
+    }
+
+}
+
+export default SplitColourChannels;

+ 3 - 0
test/index.mjs

@@ -83,6 +83,9 @@ import "./tests/operations/Magic";
 import "./tests/operations/ParseTLV";
 import "./tests/operations/Media";
 
+// Cannot test operations that use the File type yet
+//import "./tests/operations/SplitColourChannels";
+
 let allTestsPassing = true;
 const testStatusCounts = {
     total: 0,

File diff suppressed because it is too large
+ 10 - 0
test/tests/operations/SplitColourChannels.mjs


Some files were not shown because too many files changed in this diff