Skip to content

Commit

Permalink
feat!: input validation (#343)
Browse files Browse the repository at this point in the history
Co-authored-by: Attila Gazso <[email protected]>
Co-authored-by: nugaon <[email protected]>
  • Loading branch information
3 people authored Jun 9, 2021
1 parent 572253c commit 1a33932
Show file tree
Hide file tree
Showing 18 changed files with 1,146 additions and 57 deletions.
8 changes: 6 additions & 2 deletions src/bee-debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,19 @@ export class BeeDebug {
return connectivity.getPeers(this.url)
}

async removePeer(peer: string): Promise<RemovePeerResponse> {
async removePeer(peer: string | Address): Promise<RemovePeerResponse> {
assertAddress(peer)

return connectivity.removePeer(this.url, peer)
}

async getTopology(): Promise<Topology> {
return connectivity.getTopology(this.url)
}

async pingPeer(peer: string): Promise<PingResponse> {
async pingPeer(peer: string | Address): Promise<PingResponse> {
assertAddress(peer)

return connectivity.pingPeer(this.url, peer)
}

Expand Down
78 changes: 70 additions & 8 deletions src/bee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,20 @@ import { createFeedManifest } from './modules/feed'
import { assertBeeUrl, stripLastSlash } from './utils/url'
import { EthAddress, makeEthAddress, makeHexEthAddress } from './utils/eth'
import { wrapBytesWithHelpers } from './utils/bytes'
import { assertBatchId, assertNonNegativeInteger, assertReference } from './utils/type'
import {
assertAddressPrefix,
assertBatchId,
assertCollectionUploadOptions,
assertData,
assertFileData,
assertFileUploadOptions,
assertNonNegativeInteger,
assertPssMessageHandler,
assertPublicKey,
assertReference,
assertUploadOptions,
isTag,
} from './utils/type'
import { setJsonData, getJsonData } from './feed/json'
import { makeCollectionFromFS, makeCollectionFromFileList } from './utils/collection'
import { PostageBatchOptions, STAMPS_DEPTH_MAX, STAMPS_DEPTH_MIN } from './types'
Expand Down Expand Up @@ -95,6 +108,9 @@ export class Bee {
options?: UploadOptions,
): Promise<Reference> {
assertBatchId(postageBatchId)
assertData(data)

if (options) assertUploadOptions(options)

return bytes.upload(this.url, data, postageBatchId, options)
}
Expand Down Expand Up @@ -139,12 +155,19 @@ export class Bee {
options?: FileUploadOptions,
): Promise<Reference> {
assertBatchId(postageBatchId)
assertFileData(data)

if (options) assertFileUploadOptions(options)

if (name && typeof name !== 'string') {
throw new TypeError('name has to be string or undefined!')
}

if (isFile(data)) {
const fileData = await fileArrayBuffer(data)
const fileName = name || data.name
const fileName = name ?? data.name
const contentType = data.type
const fileOptions = options !== undefined ? { contentType, ...options } : { contentType }
const fileOptions = { contentType, ...options }

return bzz.uploadFile(this.url, fileData, postageBatchId, fileName, fileOptions)
} else {
Expand Down Expand Up @@ -193,6 +216,9 @@ export class Bee {
options?: CollectionUploadOptions,
): Promise<Reference> {
assertBatchId(postageBatchId)

if (options) assertCollectionUploadOptions(options)

const data = await makeCollectionFromFileList(fileList)

return bzz.uploadCollection(this.url, data, postageBatchId, options)
Expand All @@ -215,6 +241,8 @@ export class Bee {
options?: CollectionUploadOptions,
): Promise<Reference> {
assertBatchId(postageBatchId)

if (options) assertCollectionUploadOptions(options)
const data = await makeCollectionFromFS(dir)

return bzz.uploadCollection(this.url, data, postageBatchId, options)
Expand All @@ -233,6 +261,14 @@ export class Bee {
* @param tagUid UID or tag object to be retrieved
*/
async retrieveTag(tagUid: number | Tag): Promise<Tag> {
if (isTag(tagUid)) {
tagUid = tagUid.uid
} else if (typeof tagUid === 'number') {
assertNonNegativeInteger(tagUid, 'UID')
} else {
throw new TypeError('tagUid has to be either Tag or a number (UID)!')
}

return tag.retrieveTag(this.url, tagUid)
}

Expand Down Expand Up @@ -313,11 +349,23 @@ export class Bee {
topic: string,
target: AddressPrefix,
data: string | Uint8Array,
recipient?: PublicKey,
recipient?: string | PublicKey,
): Promise<void> {
assertData(data)
assertBatchId(postageBatchId)
assertAddressPrefix(target)

if (typeof topic !== 'string') {
throw new TypeError('topic has to be an string!')
}

if (recipient) {
assertPublicKey(recipient)

return pss.send(this.url, topic, target, data, postageBatchId, recipient)
return pss.send(this.url, topic, target, data, postageBatchId, recipient)
} else {
return pss.send(this.url, topic, target, data, postageBatchId)
}
}

/**
Expand All @@ -329,6 +377,12 @@ export class Bee {
* @returns Subscription to a given topic
*/
pssSubscribe(topic: string, handler: PssMessageHandler): PssSubscription {
assertPssMessageHandler(handler)

if (typeof topic !== 'string') {
throw new TypeError('topic has to be an string!')
}

const ws = pss.subscribe(this.url, topic)

let cancelled = false
Expand Down Expand Up @@ -386,6 +440,14 @@ export class Bee {
* @returns Message in byte array
*/
async pssReceive(topic: string, timeoutMsec = 0): Promise<Data> {
if (typeof topic !== 'string') {
throw new TypeError('topic has to be an string!')
}

if (typeof timeoutMsec !== 'number') {
throw new TypeError('timeoutMsc parameter has to be a number!')
}

return new Promise((resolve, reject) => {
let timeout: number | undefined
const subscription = this.pssSubscribe(topic, {
Expand Down Expand Up @@ -523,7 +585,7 @@ export class Bee {
const feedType = options?.type ?? DEFAULT_FEED_TYPE

if (options?.signer && options?.address) {
return Promise.reject(new BeeError('Both options "signer" and "address" can not be specified at one time!'))
throw new BeeError('Both options "signer" and "address" can not be specified at one time!')
}

let address: EthAddress
Expand All @@ -535,9 +597,9 @@ export class Bee {
address = this.resolveSigner(options?.signer).address
} catch (e) {
if (e instanceof BeeError) {
return Promise.reject(new BeeError('Either address, signer or default signer has to be specified!'))
throw new BeeError('Either address, signer or default signer has to be specified!')
} else {
return Promise.reject(e)
throw e
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/chunk/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export function makePrivateKeySigner(privateKey: PrivateKeyBytes): Signer {
}

export function assertSigner(signer: unknown): asserts signer is Signer {
if (typeof signer !== 'object' || signer === null) {
if (typeof signer !== 'object' || signer === null || Array.isArray(signer)) {
throw new TypeError('Signer must be an object or string!')
}

Expand Down
7 changes: 2 additions & 5 deletions src/feed/json.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { FeedWriter, FeedReader, AnyJson, Address, Reference } from '../types'
import { FeedWriter, FeedReader, AnyJson, BatchId, Reference } from '../types'
import { Bee } from '../bee'
import { assertAddress } from '../utils/type'

function serializeJson(data: AnyJson): Uint8Array {
try {
Expand All @@ -23,11 +22,9 @@ export async function getJsonData<T extends AnyJson>(bee: Bee, reader: FeedReade
export async function setJsonData(
bee: Bee,
writer: FeedWriter,
postageBatchId: string | Address,
postageBatchId: BatchId,
data: AnyJson,
): Promise<Reference> {
assertAddress(postageBatchId)

const serializedData = serializeJson(data)
const reference = await bee.uploadData(postageBatchId, serializedData)

Expand Down
4 changes: 4 additions & 0 deletions src/feed/topic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,9 @@ export function makeTopic(topic: Uint8Array | string): Topic {
}

export function makeTopicFromString(s: string): Topic {
if (typeof s !== 'string') {
throw new TypeError('topic has to be string!')
}

return bytesToHex(keccak256Hash(s), TOPIC_HEX_LENGTH)
}
5 changes: 2 additions & 3 deletions src/modules/tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@ export async function createTag(url: string): Promise<Tag> {
* Retrieve tag information from Bee node
*
* @param url Bee tag URL
* @param tag UID or tag object to be retrieved
* @param uid UID of tag to be retrieved
*/
export async function retrieveTag(url: string, tag: Tag | number): Promise<Tag> {
const uid = typeof tag === 'number' ? tag : tag?.uid
export async function retrieveTag(url: string, uid: number): Promise<Tag> {
const response = await safeAxios<Tag>({
url: `${url}${endpoint}/${uid}`,
})
Expand Down
9 changes: 6 additions & 3 deletions src/types/debug.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { PublicKey } from './index'
import { HexEthAddress } from '../utils/eth'

export interface Settlements {
peer: string
received: bigint
Expand All @@ -13,9 +16,9 @@ export interface AllSettlements {
export interface NodeAddresses {
overlay: string
underlay: string[]
ethereum: string
publicKey: string
pssPublicKey: string
ethereum: HexEthAddress
publicKey: PublicKey
pssPublicKey: PublicKey
}

export interface Peer {
Expand Down
3 changes: 2 additions & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface Dictionary<T> {
}

export const ADDRESS_HEX_LENGTH = 64
export const PUBKEY_HEX_LENGTH = 66
export const BATCH_ID_HEX_LENGTH = 64
export const REFERENCE_HEX_LENGTH = 64
export const ENCRYPTED_REFERENCE_HEX_LENGTH = 128
Expand All @@ -31,7 +32,7 @@ export const STAMPS_DEPTH_MIN = 16
export const STAMPS_DEPTH_MAX = 255

export type Reference = HexString<typeof REFERENCE_HEX_LENGTH> | HexString<typeof ENCRYPTED_REFERENCE_HEX_LENGTH>
export type PublicKey = string
export type PublicKey = HexString<typeof PUBKEY_HEX_LENGTH>

export type Address = HexString<typeof ADDRESS_HEX_LENGTH>

Expand Down
23 changes: 16 additions & 7 deletions src/utils/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { BeeArgumentError } from './error'
import path from 'path'
import fs from 'fs'
import { fileArrayBuffer } from './file'

function isUint8Array(obj: unknown): obj is Uint8Array {
return obj instanceof Uint8Array
}
import { isUint8Array } from './type'

export function isCollection(data: unknown): data is Collection<Uint8Array> {
if (!Array.isArray(data)) {
Expand All @@ -29,6 +26,14 @@ export function assertCollection(data: unknown): asserts data is Collection<Uint
* @param dir absolute path to the directory
*/
export async function makeCollectionFromFS(dir: string): Promise<Collection<Uint8Array>> {
if (typeof dir !== 'string') {
throw new TypeError('dir has to be string!')
}

if (dir === '') {
throw new TypeError('dir must not be empty string!')
}

return buildCollectionRelative(dir, '')
}

Expand Down Expand Up @@ -66,12 +71,16 @@ interface WebkitFile extends File {
readonly webkitRelativePath?: string
}

function filePath(file: WebkitFile) {
function makeFilePath(file: WebkitFile) {
if (file.webkitRelativePath && file.webkitRelativePath !== '') {
return file.webkitRelativePath.replace(/.*?\//i, '')
}

return file.name
if (file.name) {
return file.name
}

throw new TypeError('file is not valid File object')
}

export async function makeCollectionFromFileList(fileList: FileList | File[]): Promise<Collection<Uint8Array>> {
Expand All @@ -82,7 +91,7 @@ export async function makeCollectionFromFileList(fileList: FileList | File[]): P

if (file) {
collection.push({
path: filePath(file),
path: makeFilePath(file),
data: new Uint8Array(await fileArrayBuffer(file)),
})
}
Expand Down
11 changes: 7 additions & 4 deletions src/utils/hex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,20 +152,22 @@ export function isPrefixedHexString(s: unknown): s is PrefixedHexString {
*
* @param s string input
* @param len expected length of the HexString
* @param name optional name for the asserted value
* @returns HexString or throws error
*/
export function assertHexString<Length extends number = number>(
s: unknown,
len?: number,
name = 'value',
): asserts s is HexString<Length> {
if (!isHexString(s, len)) {
if (isPrefixedHexString(s)) {
throw new TypeError(`Not valid non prefixed hex string (has 0x prefix): ${s}`)
throw new TypeError(`${name} not valid non prefixed hex string (has 0x prefix): ${s}`)
}

// Don't display length error if no length specified in order not to confuse user
const lengthMsg = len ? ` of length ${len}` : ''
throw new TypeError(`Not valid hex string${lengthMsg}: ${s}`)
throw new TypeError(`${name} not valid hex string${lengthMsg}: ${s}`)
}
}

Expand All @@ -174,10 +176,11 @@ export function assertHexString<Length extends number = number>(
*
* @param s string input
* @param len expected length of the HexString
* @param name optional name for the asserted value
* @returns HexString or throws error
*/
export function assertPrefixedHexString(s: string): asserts s is PrefixedHexString {
export function assertPrefixedHexString(s: string, name = 'value'): asserts s is PrefixedHexString {
if (!isPrefixedHexString(s)) {
throw new TypeError(`Not valid prefixed hex string: ${s}`)
throw new TypeError(`${name} not valid prefixed hex string: ${s}`)
}
}
Loading

0 comments on commit 1a33932

Please sign in to comment.