diff --git a/packages/ipfs-unixfs/src/errors.ts b/packages/ipfs-unixfs/src/errors.ts index 949caf24..6ac59fed 100644 --- a/packages/ipfs-unixfs/src/errors.ts +++ b/packages/ipfs-unixfs/src/errors.ts @@ -8,3 +8,14 @@ export class InvalidTypeError extends Error { super(message) } } + +export class InvalidUnixFSMessageError extends Error { + static name = 'InvalidUnixFSMessageError' + static code = 'ERR_INVALID_MESSAGE' + name = InvalidUnixFSMessageError.name + code = InvalidUnixFSMessageError.code + + constructor (message = 'Invalid message') { + super(message) + } +} diff --git a/packages/ipfs-unixfs/src/index.ts b/packages/ipfs-unixfs/src/index.ts index 8cc1ad72..82f76823 100644 --- a/packages/ipfs-unixfs/src/index.ts +++ b/packages/ipfs-unixfs/src/index.ts @@ -90,7 +90,7 @@ * ``` */ -import { InvalidTypeError } from './errors.js' +import { InvalidTypeError, InvalidUnixFSMessageError } from './errors.js' import { Data as PBData } from './unixfs.js' export interface Mtime { @@ -117,6 +117,9 @@ const dirTypes = [ const DEFAULT_FILE_MODE = parseInt('0644', 8) const DEFAULT_DIRECTORY_MODE = parseInt('0755', 8) +// https://github.com/ipfs/boxo/blob/364c5040ec91ec8e2a61446e9921e9225704c34d/ipld/unixfs/hamt/hamt.go#L778 +const MAX_FANOUT = BigInt(1 << 10) + export interface UnixFSOptions { type?: string data?: Uint8Array @@ -134,6 +137,10 @@ class UnixFS { static unmarshal (marshaled: Uint8Array): UnixFS { const message = PBData.decode(marshaled) + if (message.fanout != null && message.fanout > MAX_FANOUT) { + throw new InvalidUnixFSMessageError(`Fanout size was too large - ${message.fanout} > ${MAX_FANOUT}`) + } + const data = new UnixFS({ type: types[message.Type != null ? message.Type.toString() : 'File'], data: message.Data, diff --git a/packages/ipfs-unixfs/test/unixfs-format.spec.ts b/packages/ipfs-unixfs/test/unixfs-format.spec.ts index a15d0f2e..12dae55b 100644 --- a/packages/ipfs-unixfs/test/unixfs-format.spec.ts +++ b/packages/ipfs-unixfs/test/unixfs-format.spec.ts @@ -431,4 +431,17 @@ describe('unixfs-format', () => { expect(marshaled).to.deep.equal(Uint8Array.from([0x08, 0x02, 0x18, 0x00])) }) + + it('should limit maximum fanout size', () => { + const data = new UnixFS({ + type: 'hamt-sharded-directory', + fanout: 1025n + }) + const marshaled = data.marshal() + + expect(() => { + UnixFS.unmarshal(marshaled) + }).to.throw() + .with.property('name', 'InvalidUnixFSMessageError') + }) })