From 1d867505d778eb2f915df884dbc4de9171385d46 Mon Sep 17 00:00:00 2001 From: Thomas Wehr Date: Fri, 5 Apr 2024 09:22:39 +0200 Subject: [PATCH] add checks for more parameters --- packages/core/src/codecs/octetstream-codec.ts | 77 +++++++++++++++---- packages/core/test/ContentSerdesTest.ts | 72 +++++++++++++++++ 2 files changed, 135 insertions(+), 14 deletions(-) diff --git a/packages/core/src/codecs/octetstream-codec.ts b/packages/core/src/codecs/octetstream-codec.ts index c9b8f523b..e1bb481c6 100644 --- a/packages/core/src/codecs/octetstream-codec.ts +++ b/packages/core/src/codecs/octetstream-codec.ts @@ -59,14 +59,41 @@ export default class OctetstreamCodec implements ContentCodec { debug("OctetstreamCodec parsing", bytes); debug("Parameters", parameters); - const bigEndian = !(parameters.byteSeq?.includes(Endianness.LITTLE_ENDIAN) === true); // default to big endian - let signed = parameters.signed !== "false"; // default to signed + const length = + parameters.length != null + ? parseInt(parameters.length) + : (warn("Missing 'length' parameter necessary for write. I'll do my best"), undefined); + + if (length !== undefined) { + if (isNaN(length) || length < 0) { + throw new Error("'length' parameter must be a non-negative number"); + } + if (length !== bytes.length) { + throw new Error(`Lengths do not match, required: ${length} provided: ${bytes.length}`); + } + } + + let signed = true; // default to signed + if (parameters.signed !== undefined) { + if (parameters.signed !== "true" && parameters.signed !== "false") { + throw new Error("'signed' parameter must be 'true' or 'false'"); + } + signed = parameters.signed === "true"; + } + + let bitLength = schema?.["ex:bitLength"] !== undefined ? parseInt(schema["ex:bitLength"]) : bytes.length * 8; + + if (isNaN(bitLength) || bitLength < 0) { + throw new Error("'ex:bitLength' must be a non-negative number"); + } + const offset = schema?.["ex:bitOffset"] !== undefined ? parseInt(schema["ex:bitOffset"]) : 0; - if (parameters.length != null && parseInt(parameters.length) !== bytes.length) { - throw new Error("Lengths do not match, required: " + parameters.length + " provided: " + bytes.length); + + if (isNaN(offset) || offset < 0) { + throw new Error("'ex:bitOffset' must be a non-negative number"); } - let bitLength: number = - schema?.["ex:bitLength"] !== undefined ? parseInt(schema["ex:bitLength"]) : bytes.length * 8; + + const bigEndian = !(parameters.byteSeq?.includes(Endianness.LITTLE_ENDIAN) === true); // default to big endian let dataType: string = schema?.type; if (!dataType) { @@ -223,16 +250,38 @@ export default class OctetstreamCodec implements ContentCodec { valueToBytes(value: unknown, schema?: DataSchema, parameters: { [key: string]: string | undefined } = {}): Buffer { debug(`OctetstreamCodec serializing '${value}'`); - if (parameters.length == null) { - warn("Missing 'length' parameter necessary for write. I'll do my best"); + const bigEndian = !(parameters.byteSeq?.includes(Endianness.LITTLE_ENDIAN) === true); // default to big endian + + let signed = true; // default to true + + if (parameters.signed !== undefined) { + if (parameters.signed !== "true" && parameters.signed !== "false") { + throw new Error("'signed' parameter must be 'true' or 'false'"); + } + signed = parameters.signed === "true"; + } + + let length = + parameters.length != null + ? parseInt(parameters.length) + : (warn("Missing 'length' parameter necessary for write. I'll do my best"), undefined); + + if (length !== undefined && (isNaN(length) || length < 0)) { + throw new Error("'length' parameter must be a non-negative number"); } - const bigEndian = !(parameters.byteSeq?.includes(Endianness.LITTLE_ENDIAN) === true); // default to big endian - let signed = parameters.signed !== "false"; // default to signed - // byte length of the buffer to be returned - let length = parameters.length != null ? parseInt(parameters.length) : undefined; let bitLength = schema?.["ex:bitLength"] !== undefined ? parseInt(schema["ex:bitLength"]) : undefined; + + if (bitLength !== undefined && (isNaN(bitLength) || bitLength < 0)) { + throw new Error("'ex:bitLength' must be a non-negative number"); + } + const offset = schema?.["ex:bitOffset"] !== undefined ? parseInt(schema["ex:bitOffset"]) : 0; + + if (isNaN(offset) || offset < 0) { + throw new Error("'ex:bitOffset' must be a non-negative number"); + } + let dataType: string = schema?.type ?? undefined; if (value === undefined) { @@ -547,11 +596,11 @@ export default class OctetstreamCodec implements ContentCodec { const offset = schema["ex:bitOffset"] !== undefined ? parseInt(schema["ex:bitOffset"]) : 0; if (isNaN(offset) || offset < 0) { - throw new Error("ex:bitOffset must be a non-negative number"); + throw new Error("'ex:bitOffset' must be a non-negative number"); } if (offset > length * 8) { - throw new Error(`ex:bitOffset ${offset} exceeds length ${length}`); + throw new Error(`'ex:bitOffset' ${offset} exceeds 'length' ${length}`); } result = result ?? Buffer.alloc(length); diff --git a/packages/core/test/ContentSerdesTest.ts b/packages/core/test/ContentSerdesTest.ts index 95355c286..d67b460a7 100644 --- a/packages/core/test/ContentSerdesTest.ts +++ b/packages/core/test/ContentSerdesTest.ts @@ -584,6 +584,55 @@ class SerdesOctetTests { { type: "uint8" } ) ).to.throw(Error, "Type is unsigned but 'signed' is true"); + + expect(() => + ContentSerdes.contentToValue( + { type: `application/octet-stream;length=test`, body: Buffer.from([0x36]) }, + { type: "integer" } + ) + ).to.throw(Error, "'length' parameter must be a non-negative number"); + + expect(() => + ContentSerdes.contentToValue( + { type: `application/octet-stream;length=-1`, body: Buffer.from([0x36]) }, + { type: "integer" } + ) + ).to.throw(Error, "'length' parameter must be a non-negative number"); + + expect(() => + ContentSerdes.contentToValue( + { type: `application/octet-stream;signed=invalid`, body: Buffer.from([0x36]) }, + { type: "integer" } + ) + ).to.throw(Error, "'signed' parameter must be 'true' or 'false'"); + + expect(() => + ContentSerdes.contentToValue( + { type: `application/octet-stream`, body: Buffer.from([0x36]) }, + { type: "integer", "ex:bitOffset": "invalid" } + ) + ).to.throw(Error, "'ex:bitOffset' must be a non-negative number"); + + expect(() => + ContentSerdes.contentToValue( + { type: `application/octet-stream`, body: Buffer.from([0x36]) }, + { type: "integer", "ex:bitOffset": -1 } + ) + ).to.throw(Error, "'ex:bitOffset' must be a non-negative number"); + + expect(() => + ContentSerdes.contentToValue( + { type: `application/octet-stream`, body: Buffer.from([0x36]) }, + { type: "integer", "ex:bitLength": "invalid" } + ) + ).to.throw(Error, "'ex:bitLength' must be a non-negative number"); + + expect(() => + ContentSerdes.contentToValue( + { type: `application/octet-stream`, body: Buffer.from([0x36]) }, + { type: "integer", "ex:bitLength": -1 } + ) + ).to.throw(Error, "'ex:bitLength' must be a non-negative number"); } @test async "value to OctetStream"() { @@ -937,6 +986,29 @@ class SerdesOctetTests { Error, "Missing 'type' property in schema" ); + expect(() => ContentSerdes.valueToContent(10, { type: "int8" }, "application/octet-stream;signed=8")).to.throw( + Error, + "'signed' parameter must be 'true' or 'false'" + ); + expect(() => + ContentSerdes.valueToContent(10, { type: "int8" }, "application/octet-stream;length=-1;") + ).to.throw(Error, "'length' parameter must be a non-negative number"); + expect(() => ContentSerdes.valueToContent(10, { type: "int8" }, "application/octet-stream;length=x;")).to.throw( + Error, + "'length' parameter must be a non-negative number" + ); + expect(() => + ContentSerdes.valueToContent(10, { type: "integer", "ex:bitOffset": -16 }, "application/octet-stream") + ).to.throw(Error, "'ex:bitOffset' must be a non-negative number"); + expect(() => + ContentSerdes.valueToContent(10, { type: "integer", "ex:bitOffset": "foo" }, "application/octet-stream") + ).to.throw(Error, "'ex:bitOffset' must be a non-negative number"); + expect(() => + ContentSerdes.valueToContent(10, { type: "integer", "ex:bitLength": -8 }, "application/octet-stream") + ).to.throw(Error, "'ex:bitLength' must be a non-negative number"); + expect(() => + ContentSerdes.valueToContent(10, { type: "integer", "ex:bitLength": "foo" }, "application/octet-stream") + ).to.throw(Error, "'ex:bitLength' must be a non-negative number"); } }