From bd9d07394fc2dcad573dd7f3b44ee692d0ea93e8 Mon Sep 17 00:00:00 2001 From: "fryorcraken.eth" Date: Mon, 3 Apr 2023 20:15:31 +1000 Subject: [PATCH] feat: ensure content topic is defined Waku Messages are considered invalid if the content topic is undefined or an empty string. Avoid user error by throwing. --- .../core/src/lib/message/version_0.spec.ts | 43 ++++++++++++++--- packages/core/src/lib/message/version_0.ts | 12 ++++- packages/message-encryption/src/ecies.spec.ts | 46 ++++++++++++++++--- packages/message-encryption/src/ecies.ts | 6 ++- .../message-encryption/src/symmetric.spec.ts | 46 ++++++++++++++++--- packages/message-encryption/src/symmetric.ts | 6 ++- 6 files changed, 137 insertions(+), 22 deletions(-) diff --git a/packages/core/src/lib/message/version_0.spec.ts b/packages/core/src/lib/message/version_0.spec.ts index e1158fe0a9..10af85e933 100644 --- a/packages/core/src/lib/message/version_0.spec.ts +++ b/packages/core/src/lib/message/version_0.spec.ts @@ -8,8 +8,8 @@ describe("Waku Message version 0", function () { it("Round trip binary serialization", async function () { await fc.assert( fc.asyncProperty( - fc.string(), - fc.string(), + fc.string({ minLength: 1 }), + fc.string({ minLength: 1 }), fc.uint8Array({ minLength: 1 }), async (contentTopic, pubSubTopic, payload) => { const encoder = createEncoder({ @@ -37,8 +37,8 @@ describe("Waku Message version 0", function () { it("Ephemeral field set to true", async function () { await fc.assert( fc.asyncProperty( - fc.string(), - fc.string(), + fc.string({ minLength: 1 }), + fc.string({ minLength: 1 }), fc.uint8Array({ minLength: 1 }), async (contentTopic, pubSubTopic, payload) => { const encoder = createEncoder({ @@ -62,8 +62,8 @@ describe("Waku Message version 0", function () { it("Meta field set when metaSetter is specified", async function () { await fc.assert( fc.asyncProperty( - fc.string(), - fc.string(), + fc.string({ minLength: 1 }), + fc.string({ minLength: 1 }), fc.uint8Array({ minLength: 1 }), async (contentTopic, pubSubTopic, payload) => { // Encode the length of the payload @@ -106,3 +106,34 @@ describe("Waku Message version 0", function () { ); }); }); + +describe("Ensures content topic is defined", () => { + it("Encoder throws on undefined content topic", () => { + const wrapper = function (): void { + createEncoder({ contentTopic: undefined as unknown as string }); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); + it("Encoder throws on empty string content topic", () => { + const wrapper = function (): void { + createEncoder({ contentTopic: "" }); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); + it("Decoder throws on undefined content topic", () => { + const wrapper = function (): void { + createDecoder(undefined as unknown as string); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); + it("Decoder throws on empty string content topic", () => { + const wrapper = function (): void { + createDecoder(""); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); +}); diff --git a/packages/core/src/lib/message/version_0.ts b/packages/core/src/lib/message/version_0.ts index 0a5376eec0..7268bc5cf8 100644 --- a/packages/core/src/lib/message/version_0.ts +++ b/packages/core/src/lib/message/version_0.ts @@ -71,7 +71,11 @@ export class Encoder implements IEncoder { public contentTopic: string, public ephemeral: boolean = false, public metaSetter?: IMetaSetter - ) {} + ) { + if (!contentTopic || contentTopic === "") { + throw new Error("Content topic must be specified"); + } + } async toWire(message: IMessage): Promise { return proto.WakuMessage.encode(await this.toProtoObj(message)); @@ -117,7 +121,11 @@ export function createEncoder({ } export class Decoder implements IDecoder { - constructor(public contentTopic: string) {} + constructor(public contentTopic: string) { + if (!contentTopic || contentTopic === "") { + throw new Error("Content topic must be specified"); + } + } fromWireToProtoObj(bytes: Uint8Array): Promise { const protoMessage = proto.WakuMessage.decode(bytes); diff --git a/packages/message-encryption/src/ecies.spec.ts b/packages/message-encryption/src/ecies.spec.ts index 698ecddeb0..814b4fa2bd 100644 --- a/packages/message-encryption/src/ecies.spec.ts +++ b/packages/message-encryption/src/ecies.spec.ts @@ -9,8 +9,8 @@ describe("Ecies Encryption", function () { it("Round trip binary encryption [ecies, no signature]", async function () { await fc.assert( fc.asyncProperty( - fc.string(), - fc.string(), + fc.string({ minLength: 1 }), + fc.string({ minLength: 1 }), fc.uint8Array({ minLength: 1 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), async (pubSubTopic, contentTopic, payload, privateKey) => { @@ -44,8 +44,8 @@ describe("Ecies Encryption", function () { await fc.assert( fc.asyncProperty( - fc.string(), - fc.string(), + fc.string({ minLength: 1 }), + fc.string({ minLength: 1 }), fc.uint8Array({ minLength: 1 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), @@ -86,8 +86,8 @@ describe("Ecies Encryption", function () { it("Check meta is set [ecies]", async function () { await fc.assert( fc.asyncProperty( - fc.string(), - fc.string(), + fc.string({ minLength: 1 }), + fc.string({ minLength: 1 }), fc.uint8Array({ minLength: 1 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), async (pubSubTopic, contentTopic, payload, privateKey) => { @@ -130,3 +130,37 @@ describe("Ecies Encryption", function () { ); }); }); + +describe("Ensures content topic is defined", () => { + it("Encoder throws on undefined content topic", () => { + const wrapper = function (): void { + createEncoder({ + contentTopic: undefined as unknown as string, + publicKey: new Uint8Array(), + }); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); + it("Encoder throws on empty string content topic", () => { + const wrapper = function (): void { + createEncoder({ contentTopic: "", publicKey: new Uint8Array() }); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); + it("Decoder throws on undefined content topic", () => { + const wrapper = function (): void { + createDecoder(undefined as unknown as string, new Uint8Array()); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); + it("Decoder throws on empty string content topic", () => { + const wrapper = function (): void { + createDecoder("", new Uint8Array()); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); +}); diff --git a/packages/message-encryption/src/ecies.ts b/packages/message-encryption/src/ecies.ts index 98d241e6e2..1ef2009bb7 100644 --- a/packages/message-encryption/src/ecies.ts +++ b/packages/message-encryption/src/ecies.ts @@ -37,7 +37,11 @@ class Encoder implements IEncoder { private sigPrivKey?: Uint8Array, public ephemeral: boolean = false, public metaSetter?: IMetaSetter - ) {} + ) { + if (!contentTopic || contentTopic === "") { + throw new Error("Content topic must be specified"); + } + } async toWire(message: IMessage): Promise { const protoMessage = await this.toProtoObj(message); diff --git a/packages/message-encryption/src/symmetric.spec.ts b/packages/message-encryption/src/symmetric.spec.ts index cf724d2fa0..890fdb57b5 100644 --- a/packages/message-encryption/src/symmetric.spec.ts +++ b/packages/message-encryption/src/symmetric.spec.ts @@ -9,8 +9,8 @@ describe("Symmetric Encryption", function () { it("Round trip binary encryption [symmetric, no signature]", async function () { await fc.assert( fc.asyncProperty( - fc.string(), - fc.string(), + fc.string({ minLength: 1 }), + fc.string({ minLength: 1 }), fc.uint8Array({ minLength: 1 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), async (pubSubTopic, contentTopic, payload, symKey) => { @@ -40,8 +40,8 @@ describe("Symmetric Encryption", function () { it("Round trip binary encryption [symmetric, signature]", async function () { await fc.assert( fc.asyncProperty( - fc.string(), - fc.string(), + fc.string({ minLength: 1 }), + fc.string({ minLength: 1 }), fc.uint8Array({ minLength: 1 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), @@ -75,8 +75,8 @@ describe("Symmetric Encryption", function () { it("Check meta is set [symmetric]", async function () { await fc.assert( fc.asyncProperty( - fc.string(), - fc.string(), + fc.string({ minLength: 1 }), + fc.string({ minLength: 1 }), fc.uint8Array({ minLength: 1 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), async (pubSubTopic, contentTopic, payload, symKey) => { @@ -118,3 +118,37 @@ describe("Symmetric Encryption", function () { ); }); }); + +describe("Ensures content topic is defined", () => { + it("Encoder throws on undefined content topic", () => { + const wrapper = function (): void { + createEncoder({ + contentTopic: undefined as unknown as string, + symKey: new Uint8Array(), + }); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); + it("Encoder throws on empty string content topic", () => { + const wrapper = function (): void { + createEncoder({ contentTopic: "", symKey: new Uint8Array() }); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); + it("Decoder throws on undefined content topic", () => { + const wrapper = function (): void { + createDecoder(undefined as unknown as string, new Uint8Array()); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); + it("Decoder throws on empty string content topic", () => { + const wrapper = function (): void { + createDecoder("", new Uint8Array()); + }; + + expect(wrapper).to.throw("Content topic must be specified"); + }); +}); diff --git a/packages/message-encryption/src/symmetric.ts b/packages/message-encryption/src/symmetric.ts index 53bf05ee38..4ad039e3b6 100644 --- a/packages/message-encryption/src/symmetric.ts +++ b/packages/message-encryption/src/symmetric.ts @@ -32,7 +32,11 @@ class Encoder implements IEncoder { private sigPrivKey?: Uint8Array, public ephemeral: boolean = false, public metaSetter?: IMetaSetter - ) {} + ) { + if (!contentTopic || contentTopic === "") { + throw new Error("Content topic must be specified"); + } + } async toWire(message: IMessage): Promise { const protoMessage = await this.toProtoObj(message);