mirror of https://codeberg.org/pzp/pzp-sync.git
Validate all incoming payloads in Stream (#6)
This commit is contained in:
parent
93f00dbd04
commit
5fef427ebb
14
lib/range.js
14
lib/range.js
|
@ -1,3 +1,14 @@
|
|||
/**
|
||||
* @param {any} range
|
||||
* @return {range is Range}
|
||||
*/
|
||||
function isRange(range) {
|
||||
if (!Array.isArray(range)) return false
|
||||
if (range.length !== 2) return false
|
||||
if (!Number.isInteger(range[0]) || !Number.isInteger(range[1])) return false
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {[number, number]} Range
|
||||
*/
|
||||
|
@ -26,7 +37,8 @@ function estimateMsgCount(range) {
|
|||
const EMPTY_RANGE = /** @type {Range} */ ([1, 0])
|
||||
|
||||
module.exports = {
|
||||
isRange,
|
||||
isEmptyRange,
|
||||
estimateMsgCount,
|
||||
EMPTY_RANGE
|
||||
EMPTY_RANGE,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// @ts-ignore
|
||||
const Pipeable = require('push-stream/pipeable')
|
||||
const { isEmptyRange } = require('./range')
|
||||
const { isRange, isEmptyRange } = require('./range')
|
||||
const { isMsgId, isBloom, isMsgIds, isMsgs } = require('./util')
|
||||
|
||||
/**
|
||||
* @typedef {ReturnType<import('ppppp-goals').init>} PPPPPGoals
|
||||
|
@ -449,18 +450,34 @@ class SyncStream extends Pipeable {
|
|||
* @param {Data} data
|
||||
*/
|
||||
write(data) {
|
||||
if (!data) return this.#debug('Invalid data from remote peer: missing data')
|
||||
// prettier-ignore
|
||||
if (typeof data !== 'object') return this.#debug('Invalid data from remote peer: not an object')
|
||||
// prettier-ignore
|
||||
if (Array.isArray(data)) return this.#debug('Invalid data from remote peer: is an array')
|
||||
const { id, phase, payload } = data
|
||||
// prettier-ignore
|
||||
if (typeof phase !== 'number') return this.#debug("Invalid data from remote peer: phase isn't a number")
|
||||
// prettier-ignore
|
||||
if (!isMsgId(id)) return this.#debug('Invalid data from remote peer: id is not a valid msg id')
|
||||
// prettier-ignore
|
||||
if (phase !== 0 && !payload) return this.#debug('Invalid data from remote peer: payload is missing')
|
||||
|
||||
// TODO: validate that each data objects has the exact correct shape
|
||||
switch (phase) {
|
||||
case 0: {
|
||||
return this.#sendLocalHave(id)
|
||||
}
|
||||
case 1: {
|
||||
// prettier-ignore
|
||||
if (!isRange(payload)) return this.#debug('Invalid data from remote peer: payload is not a range in phase 1')
|
||||
|
||||
return this.#sendLocalHaveAndWant(id, payload)
|
||||
}
|
||||
case 2: {
|
||||
const { haveRange, wantRange } = payload
|
||||
// prettier-ignore
|
||||
if (!isRange(haveRange) || !isRange(wantRange)) return this.#debug('Invalid data from remote peer: haveRange or wantRange is not a range in phase 2')
|
||||
|
||||
if (isEmptyRange(haveRange)) {
|
||||
// prettier-ignore
|
||||
this.#debug('%s Stream IN2: received remote have-range %o and want-range %o for %s', this.#myId, haveRange, wantRange, id)
|
||||
|
@ -471,6 +488,11 @@ class SyncStream extends Pipeable {
|
|||
}
|
||||
case 3: {
|
||||
const { wantRange, bloom } = payload
|
||||
// prettier-ignore
|
||||
if (!isRange(wantRange)) return this.#debug('Invalid data from remote peer: wantRange is not a range in phase 3')
|
||||
// prettier-ignore
|
||||
if (!isBloom(bloom)) return this.#debug('Invalid data from remote peer: bloom is not a bloom in phase 3')
|
||||
|
||||
const haveRange = this.#remoteHave.get(id)
|
||||
if (haveRange && isEmptyRange(haveRange)) {
|
||||
// prettier-ignore
|
||||
|
@ -482,32 +504,62 @@ class SyncStream extends Pipeable {
|
|||
}
|
||||
case 4: {
|
||||
const { bloom, msgIDs } = payload
|
||||
// prettier-ignore
|
||||
if (!isBloom(bloom)) return this.#debug('Invalid data from remote peer: bloom is not a bloom in phase 4')
|
||||
// prettier-ignore
|
||||
if (!isMsgIds(msgIDs)) return this.#debug('Invalid data from remote peer: msgIDs is not an array of msg ids in phase 4')
|
||||
|
||||
return this.#sendBloomReq(id, phase + 1, 1, bloom, msgIDs)
|
||||
}
|
||||
case 5: {
|
||||
const { bloom, msgIDs } = payload
|
||||
// prettier-ignore
|
||||
if (!isBloom(bloom)) return this.#debug('Invalid data from remote peer: bloom is not a bloom in phase 5')
|
||||
// prettier-ignore
|
||||
if (!isMsgIds(msgIDs)) return this.#debug('Invalid data from remote peer: msgIDs is not an array of msg ids in phase 5')
|
||||
|
||||
return this.#sendBloomRes(id, phase + 1, 1, bloom, msgIDs)
|
||||
}
|
||||
case 6: {
|
||||
const { bloom, msgIDs } = payload
|
||||
// prettier-ignore
|
||||
if (!isBloom(bloom)) return this.#debug('Invalid data from remote peer: bloom is not a bloom in phase 6')
|
||||
// prettier-ignore
|
||||
if (!isMsgIds(msgIDs)) return this.#debug('Invalid data from remote peer: msgIDs is not an array of msg ids in phase 6')
|
||||
|
||||
return this.#sendBloomReq(id, phase + 1, 2, bloom, msgIDs)
|
||||
}
|
||||
case 7: {
|
||||
const { bloom, msgIDs } = payload
|
||||
// prettier-ignore
|
||||
if (!isBloom(bloom)) return this.#debug('Invalid data from remote peer: bloom is not a bloom in phase 7')
|
||||
// prettier-ignore
|
||||
if (!isMsgIds(msgIDs)) return this.#debug('Invalid data from remote peer: msgIDs is not an array of msg ids in phase 7')
|
||||
|
||||
return this.#sendMissingMsgsReq(id, 2, bloom, msgIDs)
|
||||
}
|
||||
case 8: {
|
||||
const { bloom, msgs } = payload
|
||||
// prettier-ignore
|
||||
if (!isBloom(bloom)) return this.#debug('Invalid data from remote peer: bloom is not a bloom in phase 8')
|
||||
// prettier-ignore
|
||||
if (!isMsgs(msgs)) return this.#debug('Invalid data from remote peer: msgs is not an array of msgs in phase 8')
|
||||
|
||||
return this.#sendMissingMsgsRes(id, 2, bloom, msgs)
|
||||
}
|
||||
case 9: {
|
||||
// prettier-ignore
|
||||
if (!isMsgs(payload)) return this.#debug('Invalid data from remote peer: payload is not an array of msgs in phase 9')
|
||||
|
||||
// prettier-ignore
|
||||
this.#debug('%s Stream IN9: got %s msgs in %s', this.#myId, payload.length, id)
|
||||
return this.#consumeMissingMsgs(id, payload)
|
||||
}
|
||||
default: {
|
||||
// prettier-ignore
|
||||
return this.#debug('Invalid data from remote peer: phase is an invalid number')
|
||||
}
|
||||
}
|
||||
|
||||
this.#debug('Stream IN: unknown %o', data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
const bs58 = require('bs58')
|
||||
/**
|
||||
* @typedef {import('./range').Range} Range
|
||||
* @typedef {import('ppppp-db/msg-v4').Msg} Msg
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {any} msgId
|
||||
* @return {msgId is string}
|
||||
*/
|
||||
function isMsgId(msgId) {
|
||||
try {
|
||||
const d = bs58.decode(msgId)
|
||||
return d.length === 32
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} msgIds
|
||||
* @return {msgIds is Array<string>}
|
||||
*/
|
||||
function isMsgIds(msgIds) {
|
||||
if (!Array.isArray(msgIds)) return false
|
||||
return msgIds.every(isMsgId)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} msgs
|
||||
* @return {msgs is Array<Msg>}
|
||||
*/
|
||||
function isMsgs(msgs) {
|
||||
if (!Array.isArray(msgs)) return false
|
||||
return msgs.every(isMsg)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} bloom
|
||||
* @return {bloom is string}
|
||||
*/
|
||||
function isBloom(bloom) {
|
||||
// TODO: validate when blooming is stabilized
|
||||
return !!bloom
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} msg
|
||||
* @returns {msg is Msg}
|
||||
*/
|
||||
function isMsg(msg) {
|
||||
if (!msg || typeof msg !== 'object') {
|
||||
return false
|
||||
}
|
||||
if (!('data' in msg)) {
|
||||
return false
|
||||
}
|
||||
if (!msg.metadata || typeof msg.metadata !== 'object') {
|
||||
return false
|
||||
}
|
||||
if (!('dataHash' in msg.metadata)) {
|
||||
return false
|
||||
}
|
||||
if (!('dataSize' in msg.metadata)) {
|
||||
return false
|
||||
}
|
||||
if (!('account' in msg.metadata)) {
|
||||
return false
|
||||
}
|
||||
if (!('accountTips' in msg.metadata)) {
|
||||
return false
|
||||
}
|
||||
if (!('tangles' in msg.metadata)) {
|
||||
return false
|
||||
}
|
||||
if (!('domain' in msg.metadata)) {
|
||||
return false
|
||||
}
|
||||
if (msg.metadata.v !== 4) {
|
||||
return false
|
||||
}
|
||||
if (typeof msg.sig !== 'string') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isMsgId,
|
||||
isMsgIds,
|
||||
isMsgs,
|
||||
isBloom,
|
||||
isMsg,
|
||||
}
|
|
@ -24,6 +24,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"bloom-filters": "^3.0.0",
|
||||
"bs58": "^5.0.0",
|
||||
"debug": "^4.3.4",
|
||||
"promisify-4loc": "^1.0.0",
|
||||
"pull-stream": "^3.7.0",
|
||||
|
@ -35,7 +36,6 @@
|
|||
"@types/debug": "^4.1.9",
|
||||
"@types/pull-stream": "3.6.3",
|
||||
"@types/node": "16.x",
|
||||
"bs58": "^5.0.0",
|
||||
"c8": "7",
|
||||
"ppppp-caps": "github:staltz/ppppp-caps#93fa810b9a40b78aef4872d4c2a8412cccb52929",
|
||||
"ppppp-db": "github:staltz/ppppp-db#cf1532965ea1d16929ed2291a9b737a4ce74caac",
|
||||
|
|
Loading…
Reference in New Issue