CyberChef-server icon indicating copy to clipboard operation
CyberChef-server copied to clipboard

Bug report: Unexpected image data when generating PNG format

Open amohar opened this issue 1 year ago • 0 comments

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 bug I previously reported, but the same behavior can be observed for "File" and "List<File>" 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

amohar avatar Jan 21 '24 20:01 amohar