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 <lball@redhat.com>
This commit is contained in:
Lance Ball 2022-06-22 15:27:41 -04:00 committed by GitHub
parent 847f6bfcc7
commit ea94a4d779
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 32 additions and 7 deletions

View File

@ -18,10 +18,11 @@ export function validateCloudEvent<T>(event: CloudEventV1<T>): 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;

View File

@ -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", () => {

View File

@ -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", () => {

View File

@ -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", () => {

View File

@ -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", () => {