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.
This commit is contained in:
fryorcraken.eth 2023-04-03 20:15:31 +10:00
parent 6abee4880f
commit bd9d07394f
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
6 changed files with 137 additions and 22 deletions

View File

@ -8,8 +8,8 @@ describe("Waku Message version 0", function () {
it("Round trip binary serialization", async function () { it("Round trip binary serialization", async function () {
await fc.assert( await fc.assert(
fc.asyncProperty( fc.asyncProperty(
fc.string(), fc.string({ minLength: 1 }),
fc.string(), fc.string({ minLength: 1 }),
fc.uint8Array({ minLength: 1 }), fc.uint8Array({ minLength: 1 }),
async (contentTopic, pubSubTopic, payload) => { async (contentTopic, pubSubTopic, payload) => {
const encoder = createEncoder({ const encoder = createEncoder({
@ -37,8 +37,8 @@ describe("Waku Message version 0", function () {
it("Ephemeral field set to true", async function () { it("Ephemeral field set to true", async function () {
await fc.assert( await fc.assert(
fc.asyncProperty( fc.asyncProperty(
fc.string(), fc.string({ minLength: 1 }),
fc.string(), fc.string({ minLength: 1 }),
fc.uint8Array({ minLength: 1 }), fc.uint8Array({ minLength: 1 }),
async (contentTopic, pubSubTopic, payload) => { async (contentTopic, pubSubTopic, payload) => {
const encoder = createEncoder({ const encoder = createEncoder({
@ -62,8 +62,8 @@ describe("Waku Message version 0", function () {
it("Meta field set when metaSetter is specified", async function () { it("Meta field set when metaSetter is specified", async function () {
await fc.assert( await fc.assert(
fc.asyncProperty( fc.asyncProperty(
fc.string(), fc.string({ minLength: 1 }),
fc.string(), fc.string({ minLength: 1 }),
fc.uint8Array({ minLength: 1 }), fc.uint8Array({ minLength: 1 }),
async (contentTopic, pubSubTopic, payload) => { async (contentTopic, pubSubTopic, payload) => {
// Encode the length of the 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");
});
});

View File

@ -71,7 +71,11 @@ export class Encoder implements IEncoder {
public contentTopic: string, public contentTopic: string,
public ephemeral: boolean = false, public ephemeral: boolean = false,
public metaSetter?: IMetaSetter public metaSetter?: IMetaSetter
) {} ) {
if (!contentTopic || contentTopic === "") {
throw new Error("Content topic must be specified");
}
}
async toWire(message: IMessage): Promise<Uint8Array> { async toWire(message: IMessage): Promise<Uint8Array> {
return proto.WakuMessage.encode(await this.toProtoObj(message)); return proto.WakuMessage.encode(await this.toProtoObj(message));
@ -117,7 +121,11 @@ export function createEncoder({
} }
export class Decoder implements IDecoder<DecodedMessage> { export class Decoder implements IDecoder<DecodedMessage> {
constructor(public contentTopic: string) {} constructor(public contentTopic: string) {
if (!contentTopic || contentTopic === "") {
throw new Error("Content topic must be specified");
}
}
fromWireToProtoObj(bytes: Uint8Array): Promise<IProtoMessage | undefined> { fromWireToProtoObj(bytes: Uint8Array): Promise<IProtoMessage | undefined> {
const protoMessage = proto.WakuMessage.decode(bytes); const protoMessage = proto.WakuMessage.decode(bytes);

View File

@ -9,8 +9,8 @@ describe("Ecies Encryption", function () {
it("Round trip binary encryption [ecies, no signature]", async function () { it("Round trip binary encryption [ecies, no signature]", async function () {
await fc.assert( await fc.assert(
fc.asyncProperty( fc.asyncProperty(
fc.string(), fc.string({ minLength: 1 }),
fc.string(), fc.string({ minLength: 1 }),
fc.uint8Array({ minLength: 1 }), fc.uint8Array({ minLength: 1 }),
fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }),
async (pubSubTopic, contentTopic, payload, privateKey) => { async (pubSubTopic, contentTopic, payload, privateKey) => {
@ -44,8 +44,8 @@ describe("Ecies Encryption", function () {
await fc.assert( await fc.assert(
fc.asyncProperty( fc.asyncProperty(
fc.string(), fc.string({ minLength: 1 }),
fc.string(), fc.string({ minLength: 1 }),
fc.uint8Array({ minLength: 1 }), fc.uint8Array({ minLength: 1 }),
fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }),
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 () { it("Check meta is set [ecies]", async function () {
await fc.assert( await fc.assert(
fc.asyncProperty( fc.asyncProperty(
fc.string(), fc.string({ minLength: 1 }),
fc.string(), fc.string({ minLength: 1 }),
fc.uint8Array({ minLength: 1 }), fc.uint8Array({ minLength: 1 }),
fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }),
async (pubSubTopic, contentTopic, payload, privateKey) => { 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");
});
});

View File

@ -37,7 +37,11 @@ class Encoder implements IEncoder {
private sigPrivKey?: Uint8Array, private sigPrivKey?: Uint8Array,
public ephemeral: boolean = false, public ephemeral: boolean = false,
public metaSetter?: IMetaSetter public metaSetter?: IMetaSetter
) {} ) {
if (!contentTopic || contentTopic === "") {
throw new Error("Content topic must be specified");
}
}
async toWire(message: IMessage): Promise<Uint8Array | undefined> { async toWire(message: IMessage): Promise<Uint8Array | undefined> {
const protoMessage = await this.toProtoObj(message); const protoMessage = await this.toProtoObj(message);

View File

@ -9,8 +9,8 @@ describe("Symmetric Encryption", function () {
it("Round trip binary encryption [symmetric, no signature]", async function () { it("Round trip binary encryption [symmetric, no signature]", async function () {
await fc.assert( await fc.assert(
fc.asyncProperty( fc.asyncProperty(
fc.string(), fc.string({ minLength: 1 }),
fc.string(), fc.string({ minLength: 1 }),
fc.uint8Array({ minLength: 1 }), fc.uint8Array({ minLength: 1 }),
fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }),
async (pubSubTopic, contentTopic, payload, symKey) => { async (pubSubTopic, contentTopic, payload, symKey) => {
@ -40,8 +40,8 @@ describe("Symmetric Encryption", function () {
it("Round trip binary encryption [symmetric, signature]", async function () { it("Round trip binary encryption [symmetric, signature]", async function () {
await fc.assert( await fc.assert(
fc.asyncProperty( fc.asyncProperty(
fc.string(), fc.string({ minLength: 1 }),
fc.string(), fc.string({ minLength: 1 }),
fc.uint8Array({ minLength: 1 }), fc.uint8Array({ minLength: 1 }),
fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }),
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 () { it("Check meta is set [symmetric]", async function () {
await fc.assert( await fc.assert(
fc.asyncProperty( fc.asyncProperty(
fc.string(), fc.string({ minLength: 1 }),
fc.string(), fc.string({ minLength: 1 }),
fc.uint8Array({ minLength: 1 }), fc.uint8Array({ minLength: 1 }),
fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }), fc.uint8Array({ min: 1, minLength: 32, maxLength: 32 }),
async (pubSubTopic, contentTopic, payload, symKey) => { 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");
});
});

View File

@ -32,7 +32,11 @@ class Encoder implements IEncoder {
private sigPrivKey?: Uint8Array, private sigPrivKey?: Uint8Array,
public ephemeral: boolean = false, public ephemeral: boolean = false,
public metaSetter?: IMetaSetter public metaSetter?: IMetaSetter
) {} ) {
if (!contentTopic || contentTopic === "") {
throw new Error("Content topic must be specified");
}
}
async toWire(message: IMessage): Promise<Uint8Array | undefined> { async toWire(message: IMessage): Promise<Uint8Array | undefined> {
const protoMessage = await this.toProtoObj(message); const protoMessage = await this.toProtoObj(message);