From ea94a4d779d0744ef40abc81d08ab8b7e93e9133 Mon Sep 17 00:00:00 2001 From: Lance Ball Date: Wed, 22 Jun 2022 15:27:41 -0400 Subject: [PATCH] fix: improve validation on extension attribute (#502) * fix: improve validation on extension attribute Fixes: https://github.com/cloudevents/sdk-javascript/issues/500 Adds a regular expression check to the attribute name validation code to ensure that attribute names only use a-z0-9 (except for `data_base64`, which apparently is an exception to the rule. Signed-off-by: Lance Ball --- src/event/spec.ts | 7 ++++--- test/integration/kafka_tests.ts | 2 +- test/integration/message_test.ts | 18 ++++++++++++++++-- test/integration/mqtt_tests.ts | 2 +- test/integration/spec_1_tests.ts | 10 ++++++++++ 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/event/spec.ts b/src/event/spec.ts index 25def7d..bfab4f3 100644 --- a/src/event/spec.ts +++ b/src/event/spec.ts @@ -18,10 +18,11 @@ export function validateCloudEvent(event: CloudEventV1): boolean { } else { return false; } - // attribute names must all be lowercase + // attribute names must all be [a-z|0-9] + const validation = /^[a-z0-9]+$/; for (const key in event) { - if (key !== key.toLowerCase()) { - throw new ValidationError(`invalid attribute name: ${key}`); + if (validation.test(key) === false && key !== "data_base64") { + throw new ValidationError(`invalid attribute name: "${key}"`); } } return true; diff --git a/test/integration/kafka_tests.ts b/test/integration/kafka_tests.ts index 8870d47..ac0e0c4 100644 --- a/test/integration/kafka_tests.ts +++ b/test/integration/kafka_tests.ts @@ -131,7 +131,7 @@ describe("Kafka transport", () => { expect(event.LUNCH).to.equal("tacos"); expect(function () { event.validate(); - }).to.throw("invalid attribute name: LUNCH"); + }).to.throw("invalid attribute name: \"LUNCH\""); }); it("Can detect CloudEvent binary Messages with weird versions", () => { diff --git a/test/integration/message_test.ts b/test/integration/message_test.ts index b1afad4..46ab21f 100644 --- a/test/integration/message_test.ts +++ b/test/integration/message_test.ts @@ -41,7 +41,21 @@ const imageData = new Uint32Array(fs.readFileSync(path.join(process.cwd(), "test const image_base64 = asBase64(imageData); describe("HTTP transport", () => { - + it("validates extension attribute names for incoming messages", () => { + // create a new Message + const msg: Message = { + headers: { + "ce-id": "213", + "ce-source": "test", + "ce-type": "test", + "ce-bad-extension": "value" + }, + body: undefined + }; + const evt = HTTP.toEvent(msg) as CloudEvent; + expect(() => evt.validate()).to.throw(TypeError); + }); + it("Includes extensions in binary mode when type is 'boolean' with a false value", () => { const evt = new CloudEvent({ source: "test", type: "test", extboolean: false }); expect(evt.hasOwnProperty("extboolean")).to.equal(true); @@ -129,7 +143,7 @@ describe("HTTP transport", () => { expect(event.LUNCH).to.equal("tacos"); expect(function () { event.validate(); - }).to.throw("invalid attribute name: LUNCH"); + }).to.throw("invalid attribute name: \"LUNCH\""); }); it("Can detect CloudEvent binary Messages with weird versions", () => { diff --git a/test/integration/mqtt_tests.ts b/test/integration/mqtt_tests.ts index 411a9cd..69b77e5 100644 --- a/test/integration/mqtt_tests.ts +++ b/test/integration/mqtt_tests.ts @@ -134,7 +134,7 @@ describe("MQTT transport", () => { expect(event.LUNCH).to.equal("tacos"); expect(function () { event.validate(); - }).to.throw("invalid attribute name: LUNCH"); + }).to.throw("invalid attribute name: \"LUNCH\""); }); it("Can detect CloudEvent binary Messages with weird versions", () => { diff --git a/test/integration/spec_1_tests.ts b/test/integration/spec_1_tests.ts index 7bd2248..9759d8f 100644 --- a/test/integration/spec_1_tests.ts +++ b/test/integration/spec_1_tests.ts @@ -99,6 +99,16 @@ describe("CloudEvents Spec v1.0", () => { it("should be ok when the type is an string converted from an object", () => { expect(cloudevent.cloneWith({ objectextension: JSON.stringify({ some: "object" }) }).validate()).to.equal(true); }); + + it("should only allow a-z|0-9 in the attribute names", () => { + const testCases = [ + "an extension", "an_extension", "an-extension", "an.extension", "an+extension" + ]; + testCases.forEach((testCase) => { + const evt = cloudevent.cloneWith({ [testCase]: "a value"}, false); + expect(() => evt.validate()).to.throw(ValidationError); + }); + }); }); describe("The Constraints check", () => {