From d1247afb06e6cfeb40c3aa6e4dc45aa74258a36c Mon Sep 17 00:00:00 2001 From: Mikey Date: Tue, 9 May 2023 19:19:00 +1200 Subject: [PATCH] more strict validation (#1) in an attempt to preempt problems with non-deterministic (de-)serialization. --- lib/feed-v1/validation.js | 40 +++++++++++++++++++++++++++++---------- protospec.md | 4 ++-- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/lib/feed-v1/validation.js b/lib/feed-v1/validation.js index 87ae79d..869a3b5 100644 --- a/lib/feed-v1/validation.js +++ b/lib/feed-v1/validation.js @@ -36,7 +36,12 @@ function validateShape(msg) { function validateWho(msg) { try { - base58.decode(msg.metadata.who) + const whoBuf = base58.decode(msg.metadata.who) + if (whoBuf.length !== 32) { + return new Error( + `invalid message: decoded "who" should be 32 bytes but was ${whoBuf.length}` + ) + } } catch (err) { return new Error('invalid message: must have "who" as base58 string') } @@ -44,7 +49,12 @@ function validateWho(msg) { function validateMsgHash(str) { try { - base58.decode(str) + const hashBuf = Buffer.from(base58.decode(str)) + if (hashBuf.length !== 16) { + return new Error( + `invalid message: decoded hash should be 16 bytes but was ${hashBuf.length}` + ) + } } catch (err) { return new Error( `invalid message: msgHash ${str} should have been a base58 string` @@ -52,21 +62,30 @@ function validateMsgHash(str) { } } +function validateSize(msg) { + const { + metadata: { size }, + } = msg + if (!Number.isSafeInteger(size) || size < 0) { + return new Error(`invalid message: "size" should be an unsigned integer`) + } +} + function validateSignature(msg) { const { sig } = msg if (typeof sig !== 'string') { return new Error('invalid message: must have sig as a string') } + let sigBuf try { - base58.decode(sig) + sigBuf = Buffer.from(base58.decode(sig)) + if (sigBuf.length !== 64) { + // prettier-ignore + return new Error('invalid message: sig should be 64 bytes but was ' + sigBuf.length + ', on feed: ' + msg.metadata.who); + } } catch (err) { return new Error('invalid message: sig must be a base58 string') } - const sigBuf = Buffer.from(base58.decode(sig)) - if (sigBuf.length !== 64) { - // prettier-ignore - return new Error('invalid message: sig should be 64 bytes but was ' + sigBuf.length + ', on feed: ' + msg.metadata.who); - } const publicKeyBuf = Buffer.from(base58.decode(msg.metadata.who)) const signableBuf = Buffer.from(stringify(msg.metadata), 'utf8') @@ -93,9 +112,9 @@ function validateTangle(msg, tangle, tangleId) { // prettier-ignore return new Error('invalid message: prev must be an array, on feed: ' + msg.metadata.who); } - if (typeof depth !== 'number') { + if (!Number.isSafeInteger(depth) || depth <= 0) { // prettier-ignore - return new Error('invalid message: depth must be a number, on feed: ' + msg.metadata.who); + return new Error('invalid message: depth must be a positive integer, on feed: ' + msg.metadata.who); } if (tangle.isFeed()) { const { type, who } = tangle.getFeed() @@ -202,6 +221,7 @@ function validate(msg, tangle, msgHash, rootHash) { let err if ((err = validateShape(msg))) return err if ((err = validateWho(msg))) return err + if ((err = validateSize(msg))) return err if (tangle.size() === 0) { if ((err = validateTangleRoot(msg, msgHash, rootHash))) return err } else { diff --git a/protospec.md b/protospec.md index 025a33f..cd086e5 100644 --- a/protospec.md +++ b/protospec.md @@ -7,11 +7,11 @@ interface Msg { content: any | null, // any object, or null metadata: { hash: ContentHash, // blake3 hash of the `content` object serialized - size: number, // byte size of the `content` object serialized + size: number, // byte size (unsigned integer) of the `content` object serialized tangles: { // for each tangle this msg belongs to, identified by the tangle's root [rootMsgHash: string]: { - depth: number, // maximum distance from this msg to the root + depth: number, // maximum distance (positive integer) from this msg to the root prev: Array, // list of msg hashes of existing msgs }, },