gchq / gchq/CyberChef-server

Bug report: Unexpected image data when generating PNG format

Open
#61 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
JavaScript
Stars
179
Forks
66
PR merge metrics
No merged PRs in 30d

Description

**Describe the bug**
When Choosing PNG as the output type when generating an image, unexpected data is returned. The tested modules include:

* Generate QR Code
* Generate Image

The returned data is not valid PNG data; instead, it appears to hold some intermediary encoder/decoder JavaScript code (see below).

**To Reproduce**
Execute any of the following:
* ```curl -X POST -H "Content-Type:application/json" -d '{"input":"Test", "recipe":[{"op":"Generate QR Code","args":["PNG",5,4,"Medium"]}], "outputType":"string"}' localhost:3000/bake```
* ```curl -X POST -H "Content-Type:application/json" -d '{"input":"Test", "recipe":[{"op":"Generate Image","args":["Greyscale",8,64]}],"outputType":"string"}' localhost:3000/bake```

**Note:** I'm forcing the "string" output type because of [this](https://github.com/gchq/CyberChef-server/issues/60) bug I previously reported, but the same behavior can be observed for "File" and "List" types.

**Expected behavior**

The expected behavior is getting the result in some form of viewable PNG image, either binary PNG data or an HTML viewable representation of the PNG image.

The actual behavior is the return data containing this:

```
{"value":"\"use strict\";\nvar Buffer = require(\"safer-buffer\").Buffer;\n\n// Single-byte codec. Needs a 'chars' string parameter that contains 256 or 128 chars that\n// correspond to encoded bytes (if 128 - then lower half is ASCII). \n\nexports._sbcs = SBCSCodec;\nfunction SBCSCodec(codecOptions, iconv) {\n if (!codecOptions)\n throw new Error(\"SBCS codec is called without the data.\")\n \n // Prepare char buffer for decoding.\n if (!codecOptions.chars || (codecOptions.chars.length !== 128 && codecOptions.chars.length !== 256))\n throw new Error(\"Encoding '\"+codecOptions.type+\"' has incorrect 'chars' (must be of len 128 or 256)\");\n \n if (codecOptions.chars.length === 128) {\n var asciiString = \"\";\n for (var i = 0; i < 128; i++)\n asciiString += String.fromCharCode(i);\n codecOptions.chars = asciiString + codecOptions.chars;\n }\n\n this.decodeBuf = new Buffer.from(codecOptions.chars, 'ucs2');\n \n // Encoding buffer.\n var encodeBuf = new Buffer.alloc(65536, iconv.defaultCharSingleByte.charCodeAt(0));\n\n for (var i = 0; i < codecOptions.chars.length; i++)\n encodeBuf[codecOptions.chars.charCodeAt(i)] = i;\n\n this.encodeBuf = encodeBuf;\n}\n\nSBCSCodec.prototype.encoder = SBCSEncoder;\nSBCSCodec.prototype.decoder = SBCSDecoder;\n\n\nfunction SBCSEncoder(options, codec) {\n this.encodeBuf = codec.encodeBuf;\n}\n\nSBCSEncoder.prototype.write = function(str) {\n var buf = Buffer.alloc(str.length);\n for (var i = 0; i < str.length; i++)\n buf[i] = this.encodeBuf[str.charCodeAt(i)];\n \n return buf;\n}\n\nSBCSEncoder.prototype.end = function() {\n}\n\n\nfunction SBCSDecoder(options, codec) {\n this.decodeBuf = codec.decodeBuf;\n}\n\nSBCSDecoder.prototype.write = function(buf) {\n // Strings are immutable in JS -> we use ucs2 buffer to speed up computations.\n var decodeBuf = this.decodeBuf;\n var newBuf = Buffer.alloc(buf.length*2);\n var idx1 = 0, idx2 = 0;\n for (var i = 0; i < buf.length; i++) {\n idx1 = buf[i]*2; idx2 = i*2;\n newBuf[idx2] = decodeBuf[idx1];\n newBuf[idx2+1] = decodeBuf[idx1+1];\n }\n return newBuf.toString('ucs2');\n}\n\nSBCSDecoder.prototype.end = function() {\n}\n\u0000\"use strict\";\nvar Buffer = require(\"safer-buffer\").Buffer;\n\n// Single-byte codec. Needs a 'chars' string parameter that contains 256 or 128 chars that\n// correspond to encoded bytes (if 128 - then lower half is ASCII). \n\nexports._sbcs = SBCSCodec;\nfunction SBCSCodec(codecOptions, iconv) {\n if (!codecOptions)\n throw new Error(\"SBCS codec is called without the data.\")\n \n // Prepare char buffer for decoding.\n if (!codecOptions.chars || (codecOptions.chars.length !== 128 && codecOptions.chars.length !== 256))\n throw new Error(\"Encoding '\"+codecOptions.type+\"' has incorrect 'chars' (must be of len 128 or 256)\");\n \n if (codecOptions.chars.length === 128) {\n var asciiString = \"\";\n for (var i = 0; i < 128; i++)\n asciiString += String.fromCharCode(i);\n codecOptions.chars = asciiString + codecOptions.chars;\n }\n\n this.decodeBuf = new Buffer.from(codecOptions.chars, 'ucs2');\n \n // Encoding buffer.\n var encodeBuf = new Buffer.alloc(65536, iconv.defaultCharSingleByte.charCodeAt(0));\n\n for (var i = 0; i < codecOptions.chars.length; i++)\n encodeBuf[codecOptions.chars.charCodeAt(i)] = i;\n\n this.encodeBuf = encodeBuf;\n}\n\nSBCSCodec.prototype.encoder = SBCSEncoder;\nSBCSCodec.prototype.decoder = SBCSDecoder;\n\n\nfunction SBCSEncoder(options, codec) {\n this.encodeBuf = codec.encodeBuf;\n}\n\nSBCSEncoder.prototype.write = function(str) {\n var buf = Buffer.alloc(str.length);\n for (var i = 0; i < str.length; i++)\n buf[i] = this.encodeBuf[str.charCodeAt(i)];\n \n return buf;\n}\n\nSBCSEncoder.prototype.end = function() {\n}\n\n\nfunction SBCSDecoder(options, codec) {\n this.decodeBuf = codec.decodeBuf;\n}\n\nSBCSDecoder.prototype.write = function(buf) {\n // Strings are immutable in JS -> we use ucs2 buffer to speed up computations.\n var decodeBuf = this.decodeBuf;\n var newBuf = Buffer.alloc(buf.length*2);\n var idx1 = 0, idx2 = 0;\n for (var i = 0; i < buf.length; i++) {\n idx1 = buf[i]*2; idx2 = i*2;\n newBuf[idx2] = decodeBuf[idx1];\n newBuf[idx2+1] = decodeBuf[idx1+1];\n }\n return newBuf.toString('ucs2');\n}\n\nSBCSDecoder.prototype.end = function() {\n}\n\u0000Test\u0000\u0000éê\u001bc¼Ì0ZhxT\u0000\u0000\u0000\u0000\u0000\rIHDR\u0000\u0000 ... Unicode characters
```

Upon beautifying, the initial code looks like this (then it is repeated after a null byte and then the binary data starts):

```
"use strict";
var Buffer = require("safer-buffer").Buffer;

// Single-byte codec. Needs a 'chars' string parameter that contains 256 or 128 chars that
// correspond to encoded bytes (if 128 - then lower half is ASCII).

exports._sbcs = SBCSCodec;
function SBCSCodec(codecOptions, iconv) {
if (!codecOptions)
throw new Error("SBCS codec is called without the data.")

// Prepare char buffer for decoding.
if (!codecOptions.chars || (codecOptions.chars.length !== 128 && codecOptions.chars.length !== 256))
throw new Error("Encoding '"+codecOptions.type+"' has incorrect 'chars' (must be of len 128 or 256)");

if (codecOptions.chars.length === 128) {
var asciiString = "";
for (var i = 0; i < 128; i++)
asciiString += String.fromCharCode(i);
codecOptions.chars = asciiString + codecOptions.chars;
}

this.decodeBuf = new Buffer.from(codecOptions.chars, 'ucs2');

// Encoding buffer.
var encodeBuf = new Buffer.alloc(65536, iconv.defaultCharSingleByte.charCodeAt(0));

for (var i = 0; i < codecOptions.chars.length; i++)
encodeBuf[codecOptions.chars.charCodeAt(i)] = i;

this.encodeBuf = encodeBuf;
}

SBCSCodec.prototype.encoder = SBCSEncoder;
SBCSCodec.prototype.decoder = SBCSDecoder;

function SBCSEncoder(options, codec) {
this.encodeBuf = codec.encodeBuf;
}

SBCSEncoder.prototype.write = function(str) {
var buf = Buffer.alloc(str.length);
for (var i = 0; i < str.length; i++)
buf[i] = this.encodeBuf[str.charCodeAt(i)];

return buf;
}

SBCSEncoder.prototype.end = function() {
}

function SBCSDecoder(options, codec) {
this.decodeBuf = codec.decodeBuf;
}

SBCSDecoder.prototype.write = function(buf) {
// Strings are immutable in JS -> we use ucs2 buffer to speed up computations.
var decodeBuf = this.decodeBuf;
var newBuf = Buffer.alloc(buf.length*2);
var idx1 = 0, idx2 = 0;
for (var i = 0; i < buf.length; i++) {
idx1 = buf[i]*2; idx2 = i*2;
newBuf[idx2] = decodeBuf[idx1];
newBuf[idx2+1] = decodeBuf[idx1+1];
}
return newBuf.toString('ucs2');
}

SBCSDecoder.prototype.end = function() {
}
```

**Notes:**
Other types, like PDF and SVG, seem to be working fine.

**Node version:**
v18.12.1

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the issue through the /bake endpoint using the provided Generate QR Code and Generate Image curl requests, testing PNG with string, File, and List output types. Trace the output handling for these operations and compare it with the working PDF and SVG cases; done means the response contains viewable PNG data rather than intermediary JavaScript and binary content embedded as text.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.