From efe466ac7daa5c83c3f4e7a44efb854c519ce382 Mon Sep 17 00:00:00 2001 From: Lance Ball Date: Thu, 25 Mar 2021 12:37:05 -0400 Subject: [PATCH] fix: ensure loose validation for isEvent and toEvent (#394) The `HTTP.isEvent()` and `HTTP.toEvent()` functions both had some validation code that violated the principle of loose validation when receiving an event over HTTP. As discussed, the validation should be managed by the receiver in this case with `event.validate()` Signed-off-by: Lance Ball --- src/message/http/index.ts | 30 +++++++++--------------------- test/integration/message_test.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/message/http/index.ts b/src/message/http/index.ts index bd90eb5..f162660 100644 --- a/src/message/http/index.ts +++ b/src/message/http/index.ts @@ -1,7 +1,14 @@ import { CloudEvent, CloudEventV03, CloudEventV1, CONSTANTS, Mode, Version } from "../.."; import { Message, Headers } from ".."; -import { headersFor, sanitize, v03structuredParsers, v1binaryParsers, v1structuredParsers } from "./headers"; +import { + headersFor, + sanitize, + v03binaryParsers, + v03structuredParsers, + v1binaryParsers, + v1structuredParsers, +} from "./headers"; import { isStringOrObjectOrThrow, ValidationError } from "../../event/validation"; import { JSONParser, MappedParser, Parser, parserByContentType } from "../../parsers"; @@ -122,23 +129,12 @@ function parseBinary(message: Message, version: Version): CloudEvent { let body = message.body; if (!headers) throw new ValidationError("headers is null or undefined"); - if (body) { - isStringOrObjectOrThrow(body, new ValidationError("payload must be an object or a string")); - } - - if ( - headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] && - headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] !== Version.V03 && - headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] !== Version.V1 - ) { - throw new ValidationError(`invalid spec version ${headers[CONSTANTS.CE_HEADERS.SPEC_VERSION]}`); - } // Clone and low case all headers names const sanitizedHeaders = sanitize(headers); const eventObj: { [key: string]: unknown | string | Record } = {}; - const parserMap: Record = version === Version.V1 ? v1binaryParsers : v1binaryParsers; + const parserMap: Record = version === Version.V1 ? v1binaryParsers : v03binaryParsers; for (const header in parserMap) { if (sanitizedHeaders[header]) { @@ -186,14 +182,6 @@ function parseStructured(message: Message, version: Version): CloudEvent { if (!headers) throw new ValidationError("headers is null or undefined"); isStringOrObjectOrThrow(payload, new ValidationError("payload must be an object or a string")); - if ( - headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] && - headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] != Version.V03 && - headers[CONSTANTS.CE_HEADERS.SPEC_VERSION] != Version.V1 - ) { - throw new ValidationError(`invalid spec version ${headers[CONSTANTS.CE_HEADERS.SPEC_VERSION]}`); - } - // Clone and low case all headers names const sanitizedHeaders = sanitize(headers); diff --git a/test/integration/message_test.ts b/test/integration/message_test.ts index 837854e..127f6f6 100644 --- a/test/integration/message_test.ts +++ b/test/integration/message_test.ts @@ -59,6 +59,32 @@ describe("HTTP transport", () => { expect(HTTP.isEvent(message)).to.be.true; }); + it("Can detect CloudEvent binary Messages with weird versions", () => { + // Now create a message that is an event + const message = { + body: `{ "greeting": "hello" }`, + headers: { + [CONSTANTS.CE_HEADERS.ID]: "1234", + [CONSTANTS.CE_HEADERS.SOURCE]: "test", + [CONSTANTS.CE_HEADERS.TYPE]: "test.event", + [CONSTANTS.CE_HEADERS.SPEC_VERSION]: "11.8", + }, + }; + expect(HTTP.isEvent(message)).to.be.true; + expect(HTTP.toEvent(message)).not.to.throw; + }); + + it("Can detect CloudEvent structured Messages with weird versions", () => { + // Now create a message that is an event + const message = { + body: `{ "source": "test", "type": "test.event", "specversion": "11.8"}`, + headers: { + [CONSTANTS.CE_HEADERS.ID]: "1234", + }, + }; + expect(HTTP.isEvent(message)).to.be.true; + expect(HTTP.toEvent(message)).not.to.throw; + }); // Allow for external systems to send bad events - do what we can // to accept them it("Does not throw an exception when converting an invalid Message to a CloudEvent", () => {