From 91a482b2d7ecbce3e882b969a47618978de7e8d0 Mon Sep 17 00:00:00 2001 From: Alan Shaw Date: Fri, 17 Aug 2018 08:47:40 +0100 Subject: [PATCH] fix: improper input validation (#1506) * fix: callback with error for invalid CIDs * fix: handle invalid path for files.get* * fix: handling for invalid pin type * fix: specify async functions to use * fix: handle invalid bandwidth poll interval * refactor: allow libp2p to handle multiaddr validation * fix: differentiate between invalid multihash and invalid CID License: MIT Signed-off-by: Alan Shaw --- package.json | 1 + src/core/components/bitswap.js | 19 ++++++--- src/core/components/block.js | 20 +++++++-- src/core/components/dag.js | 30 +++++++++++-- src/core/components/dht.js | 15 ++++++- src/core/components/files.js | 40 ++++++++++------- src/core/components/object.js | 20 +++++++-- src/core/components/pin-set.js | 9 ++-- src/core/components/pin.js | 44 ++++++++++++------- src/core/components/stats.js | 5 ++- src/core/components/swarm.js | 9 ---- test/core/bitswap.spec.js | 10 +++++ test/core/block.spec.js | 69 ++++++++++++++++++++++++++++++ test/core/dag.spec.js | 67 +++++++++++++++++++++++++++++ test/core/dht.spec.js | 49 +++++++++++++++++++++ test/core/files.spec.js | 78 ++++++++++++++++++++++++++++++++++ test/core/object.spec.js | 59 +++++++++++++++++++++++++ test/core/pin.spec.js | 57 +++++++++++++++++++++++++ test/core/stats.spec.js | 53 +++++++++++++++++++++++ 19 files changed, 592 insertions(+), 62 deletions(-) create mode 100644 test/core/block.spec.js create mode 100644 test/core/dag.spec.js create mode 100644 test/core/dht.spec.js create mode 100644 test/core/files.spec.js create mode 100644 test/core/object.spec.js create mode 100644 test/core/pin.spec.js create mode 100644 test/core/stats.spec.js diff --git a/package.json b/package.json index 3cd1773ac3..d4510258a1 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "byteman": "^1.3.5", "cids": "~0.5.3", "debug": "^3.1.0", + "err-code": "^1.1.2", "file-type": "^8.1.0", "filesize": "^3.6.1", "fnv1a": "^1.0.1", diff --git a/src/core/components/bitswap.js b/src/core/components/bitswap.js index bd59eb3b16..652135c70a 100644 --- a/src/core/components/bitswap.js +++ b/src/core/components/bitswap.js @@ -6,6 +6,7 @@ const setImmediate = require('async/setImmediate') const Big = require('big.js') const CID = require('cids') const PeerId = require('peer-id') +const errCode = require('err-code') function formatWantlist (list) { return Array.from(list).map((e) => ({ '/': e[1].cid.toBaseEncodedString() })) @@ -69,12 +70,18 @@ module.exports = function bitswap (self) { if (!Array.isArray(keys)) { keys = [keys] } - keys = keys.map((key) => { - if (CID.isCID(key)) { - return key - } - return new CID(key) - }) + + try { + keys = keys.map((key) => { + if (CID.isCID(key)) { + return key + } + return new CID(key) + }) + } catch (err) { + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_CID'))) + } + return setImmediate(() => callback(null, self._bitswap.unwant(keys))) }) } diff --git a/src/core/components/block.js b/src/core/components/block.js index babb0e0752..f350363a01 100644 --- a/src/core/components/block.js +++ b/src/core/components/block.js @@ -5,7 +5,9 @@ const multihash = require('multihashes') const multihashing = require('multihashing-async') const CID = require('cids') const waterfall = require('async/waterfall') +const setImmediate = require('async/setImmediate') const promisify = require('promisify-es6') +const errCode = require('err-code') module.exports = function block (self) { return { @@ -17,7 +19,11 @@ module.exports = function block (self) { options = options || {} - cid = cleanCid(cid) + try { + cid = cleanCid(cid) + } catch (err) { + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_CID'))) + } if (options.preload !== false) { self._preload(cid) @@ -74,7 +80,11 @@ module.exports = function block (self) { ], callback) }), rm: promisify((cid, callback) => { - cid = cleanCid(cid) + try { + cid = cleanCid(cid) + } catch (err) { + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_CID'))) + } self._blockService.delete(cid, callback) }), stat: promisify((cid, options, callback) => { @@ -83,7 +93,11 @@ module.exports = function block (self) { options = {} } - cid = cleanCid(cid) + try { + cid = cleanCid(cid) + } catch (err) { + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_CID'))) + } if (options.preload !== false) { self._preload(cid) diff --git a/src/core/components/dag.js b/src/core/components/dag.js index c33539d97c..317d6acb8a 100644 --- a/src/core/components/dag.js +++ b/src/core/components/dag.js @@ -4,7 +4,9 @@ const promisify = require('promisify-es6') const CID = require('cids') const pull = require('pull-stream') const mapAsync = require('async/map') +const setImmediate = require('async/setImmediate') const flattenDeep = require('lodash/flattenDeep') +const errCode = require('err-code') module.exports = function dag (self) { return { @@ -52,7 +54,13 @@ module.exports = function dag (self) { if (typeof cid === 'string') { const split = cid.split('/') - cid = new CID(split[0]) + + try { + cid = new CID(split[0]) + } catch (err) { + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_CID'))) + } + split.shift() if (split.length > 0) { @@ -64,7 +72,7 @@ module.exports = function dag (self) { try { cid = new CID(cid) } catch (err) { - return callback(err) + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_CID'))) } } @@ -96,7 +104,13 @@ module.exports = function dag (self) { if (typeof cid === 'string') { const split = cid.split('/') - cid = new CID(split[0]) + + try { + cid = new CID(split[0]) + } catch (err) { + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_CID'))) + } + split.shift() if (split.length > 0) { @@ -127,7 +141,15 @@ module.exports = function dag (self) { options = options || {} - self.dag.get(new CID(multihash), '', options, (err, res) => { + let cid + + try { + cid = new CID(multihash) + } catch (err) { + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_CID'))) + } + + self.dag.get(cid, '', options, (err, res) => { if (err) { return callback(err) } mapAsync(res.value.links, (link, cb) => { diff --git a/src/core/components/dht.js b/src/core/components/dht.js index e5edf281db..848b2e0621 100644 --- a/src/core/components/dht.js +++ b/src/core/components/dht.js @@ -5,7 +5,9 @@ const every = require('async/every') const PeerId = require('peer-id') const CID = require('cids') const each = require('async/each') +const setImmediate = require('async/setImmediate') // const bsplit = require('buffer-split') +const errCode = require('err-code') module.exports = (self) => { return { @@ -57,8 +59,19 @@ module.exports = (self) => { * @returns {Promise|void} */ findprovs: promisify((key, opts, callback) => { + if (typeof opts === 'function') { + callback = opts + opts = {} + } + + opts = opts || {} + if (typeof key === 'string') { - key = new CID(key) + try { + key = new CID(key) + } catch (err) { + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_CID'))) + } } if (typeof opts === 'function') { diff --git a/src/core/components/files.js b/src/core/components/files.js index 8877e19b77..de0b01c562 100644 --- a/src/core/components/files.js +++ b/src/core/components/files.js @@ -17,6 +17,7 @@ const Duplex = require('readable-stream').Duplex const OtherBuffer = require('buffer').Buffer const CID = require('cids') const toB58String = require('multihashes').toB58String +const errCode = require('err-code') const WRAPPER = 'wrapper/' @@ -348,7 +349,14 @@ module.exports = function files (self) { options = options || {} if (options.preload !== false) { - const pathComponents = normalizePath(ipfsPath).split('/') + let pathComponents + + try { + pathComponents = normalizePath(ipfsPath).split('/') + } catch (err) { + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_PATH'))) + } + self._preload(pathComponents[0]) } @@ -376,7 +384,14 @@ module.exports = function files (self) { options = options || {} if (options.preload !== false) { - const pathComponents = normalizePath(ipfsPath).split('/') + let pathComponents + + try { + pathComponents = normalizePath(ipfsPath).split('/') + } catch (err) { + return toStream.source(pull.error(errCode(err, 'ERR_INVALID_PATH'))) + } + self._preload(pathComponents[0]) } @@ -399,7 +414,14 @@ module.exports = function files (self) { options = options || {} if (options.preload !== false) { - const pathComponents = normalizePath(ipfsPath).split('/') + let pathComponents + + try { + pathComponents = normalizePath(ipfsPath).split('/') + } catch (err) { + return pull.error(errCode(err, 'ERR_INVALID_PATH')) + } + self._preload(pathComponents[0]) } @@ -414,11 +436,6 @@ module.exports = function files (self) { options = options || {} - if (options.preload !== false) { - const pathComponents = normalizePath(ipfsPath).split('/') - self._preload(pathComponents[0]) - } - pull( _lsPullStreamImmutable(ipfsPath, options), pull.collect((err, values) => { @@ -432,13 +449,6 @@ module.exports = function files (self) { }), lsReadableStreamImmutable: (ipfsPath, options) => { - options = options || {} - - if (options.preload !== false) { - const pathComponents = normalizePath(ipfsPath).split('/') - self._preload(pathComponents[0]) - } - return toStream.source(_lsPullStreamImmutable(ipfsPath, options)) }, diff --git a/src/core/components/object.js b/src/core/components/object.js index 3b6012ed35..d83deae62a 100644 --- a/src/core/components/object.js +++ b/src/core/components/object.js @@ -9,6 +9,7 @@ const DAGLink = dagPB.DAGLink const CID = require('cids') const mh = require('multihashes') const Unixfs = require('ipfs-unixfs') +const errCode = require('err-code') function normalizeMultihash (multihash, enc) { if (typeof multihash === 'string') { @@ -188,7 +189,13 @@ module.exports = function object (self) { } function next () { - const cid = new CID(node.multihash) + let cid + + try { + cid = new CID(node.multihash) + } catch (err) { + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_CID'))) + } self._ipld.put(node, { cid }, (err) => { if (err) { @@ -210,14 +217,19 @@ module.exports = function object (self) { options = {} } - let mh + let mh, cid try { mh = normalizeMultihash(multihash, options.enc) } catch (err) { - return callback(err) + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_MULTIHASH'))) + } + + try { + cid = new CID(mh) + } catch (err) { + return setImmediate(() => callback(errCode(err, 'ERR_INVALID_CID'))) } - let cid = new CID(mh) if (options.cidVersion === 1) { cid = cid.toV1() diff --git a/src/core/components/pin-set.js b/src/core/components/pin-set.js index d100b85311..1f31697825 100644 --- a/src/core/components/pin-set.js +++ b/src/core/components/pin-set.js @@ -6,7 +6,8 @@ const protobuf = require('protons') const fnv1a = require('fnv1a') const varint = require('varint') const { DAGNode, DAGLink } = require('ipld-dag-pb') -const async = require('async') +const some = require('async/some') +const eachOf = require('async/eachOf') const pbSchema = require('./pin.proto') @@ -67,7 +68,7 @@ exports = module.exports = function (dag) { return searchChildren(root, callback) function searchChildren (root, cb) { - async.some(root.links, ({ multihash }, someCb) => { + some(root.links, ({ multihash }, someCb) => { const bs58Link = toB58String(multihash) if (bs58Link === childhash) { return someCb(null, true) } if (bs58Link in seen) { return someCb(null, false) } @@ -150,7 +151,7 @@ exports = module.exports = function (dag) { return bins }, {}) - async.eachOf(bins, (bin, idx, eachCb) => { + eachOf(bins, (bin, idx, eachCb) => { storePins( bin, depth + 1, @@ -203,7 +204,7 @@ exports = module.exports = function (dag) { return callback(err) } - async.eachOf(node.links, (link, idx, eachCb) => { + eachOf(node.links, (link, idx, eachCb) => { if (idx < pbh.header.fanout) { // the first pbh.header.fanout links are fanout bins // if a fanout bin is not 'empty', dig into and walk its DAGLinks diff --git a/src/core/components/pin.js b/src/core/components/pin.js index d1d7456fb6..196ca1a67b 100644 --- a/src/core/components/pin.js +++ b/src/core/components/pin.js @@ -4,8 +4,16 @@ const promisify = require('promisify-es6') const { DAGNode, DAGLink } = require('ipld-dag-pb') const CID = require('cids') -const async = require('async') +const map = require('async/map') +const mapSeries = require('async/mapSeries') +const series = require('async/series') +const parallel = require('async/parallel') +const eachLimit = require('async/eachLimit') +const waterfall = require('async/waterfall') +const someLimit = require('async/someLimit') +const setImmediate = require('async/setImmediate') const { Key } = require('interface-datastore') +const errCode = require('err-code') const createPinSet = require('./pin-set') const { resolvePath } = require('../utils') @@ -18,6 +26,11 @@ function toB58String (hash) { return new CID(hash).toBaseEncodedString() } +function invalidPinTypeErr (type) { + const errMsg = `Invalid type '${type}', must be one of {direct, indirect, recursive, all}` + return errCode(new Error(errMsg), 'ERR_INVALID_PIN_TYPE') +} + module.exports = (self) => { const repo = self._repo const dag = self.dag @@ -39,7 +52,7 @@ module.exports = (self) => { function getIndirectKeys (callback) { const indirectKeys = new Set() - async.eachLimit(recursiveKeys(), concurrencyLimit, (multihash, cb) => { + eachLimit(recursiveKeys(), concurrencyLimit, (multihash, cb) => { dag._getRecursive(multihash, (err, nodes) => { if (err) { return cb(err) } @@ -62,16 +75,16 @@ module.exports = (self) => { // a DAGNode holding those as DAGLinks, a kind of root pin function flushPins (callback) { let dLink, rLink, root - async.series([ + series([ // create a DAGLink to the node with direct pins - cb => async.waterfall([ + cb => waterfall([ cb => pinset.storeSet(directKeys(), cb), (node, cb) => DAGLink.create(types.direct, node.size, node.multihash, cb), (link, cb) => { dLink = link; cb(null) } ], cb), // create a DAGLink to the node with recursive pins - cb => async.waterfall([ + cb => waterfall([ cb => pinset.storeSet(recursiveKeys(), cb), (node, cb) => DAGLink.create(types.recursive, node.size, node.multihash, cb), (link, cb) => { rLink = link; cb(null) } @@ -114,7 +127,7 @@ module.exports = (self) => { if (err) { return callback(err) } // verify that each hash can be pinned - async.map(mhs, (multihash, cb) => { + map(mhs, (multihash, cb) => { const key = toB58String(multihash) if (recursive) { if (recursivePins.has(key)) { @@ -174,7 +187,7 @@ module.exports = (self) => { if (err) { return callback(err) } // verify that each hash can be unpinned - async.map(mhs, (multihash, cb) => { + map(mhs, (multihash, cb) => { pin._isPinnedWithType(multihash, types.all, (err, res) => { if (err) { return cb(err) } const { pinned, reason } = res @@ -235,12 +248,13 @@ module.exports = (self) => { paths = null } if (options && options.type) { + if (typeof options.type !== 'string') { + return setImmediate(() => callback(invalidPinTypeErr(options.type))) + } type = options.type.toLowerCase() } - if (!types[type]) { - return callback(new Error( - `Invalid type '${type}', must be one of {direct, indirect, recursive, all}` - )) + if (!Object.keys(types).includes(type)) { + return setImmediate(() => callback(invalidPinTypeErr(type))) } if (paths) { @@ -248,7 +262,7 @@ module.exports = (self) => { resolvePath(self.object, paths, (err, mhs) => { if (err) { return callback(err) } - async.mapSeries(mhs, (multihash, cb) => { + mapSeries(mhs, (multihash, cb) => { pin._isPinnedWithType(multihash, types.all, (err, res) => { if (err) { return cb(err) } const { pinned, reason } = res @@ -336,7 +350,7 @@ module.exports = (self) => { // check each recursive key to see if multihash is under it // arbitrary limit, enables handling 1000s of pins. let foundPin - async.someLimit(recursiveKeys(), concurrencyLimit, (key, cb) => { + someLimit(recursiveKeys(), concurrencyLimit, (key, cb) => { dag.get(new CID(key), (err, res) => { if (err) { return cb(err) } @@ -354,7 +368,7 @@ module.exports = (self) => { }), _load: promisify(callback => { - async.waterfall([ + waterfall([ // hack for CLI tests (cb) => repo.closed ? repo.datastore.open(cb) : cb(null, null), (_, cb) => repo.datastore.has(pinDataStoreKey, cb), @@ -371,7 +385,7 @@ module.exports = (self) => { } } - async.parallel([ + parallel([ cb => pinset.loadSet(pinRoot.value, types.recursive, cb), cb => pinset.loadSet(pinRoot.value, types.direct, cb) ], (err, keys) => { diff --git a/src/core/components/stats.js b/src/core/components/stats.js index ad87cf981e..010a01431d 100644 --- a/src/core/components/stats.js +++ b/src/core/components/stats.js @@ -5,6 +5,7 @@ const Big = require('big.js') const Pushable = require('pull-pushable') const human = require('human-to-milliseconds') const toStream = require('pull-stream-to-stream') +const errCode = require('err-code') function bandwidthStats (self, opts) { return new Promise((resolve, reject) => { @@ -49,7 +50,9 @@ module.exports = function stats (self) { if (opts.poll) { human(opts.interval || '1s', (err, value) => { - if (err) throw err + if (err) { + return stream.end(errCode(err, 'ERR_INVALID_POLL_INTERVAL')) + } interval = setInterval(() => { bandwidthStats(self, opts) diff --git a/src/core/components/swarm.js b/src/core/components/swarm.js index 5d1c8d26b6..c6c4d3073c 100644 --- a/src/core/components/swarm.js +++ b/src/core/components/swarm.js @@ -1,6 +1,5 @@ 'use strict' -const multiaddr = require('multiaddr') const promisify = require('promisify-es6') const values = require('lodash/values') @@ -67,10 +66,6 @@ module.exports = function swarm (self) { return callback(new Error(OFFLINE_ERROR)) } - if (typeof maddr === 'string') { - maddr = multiaddr(maddr) - } - self._libp2pNode.dial(maddr, callback) }), @@ -79,10 +74,6 @@ module.exports = function swarm (self) { return callback(new Error(OFFLINE_ERROR)) } - if (typeof maddr === 'string') { - maddr = multiaddr(maddr) - } - self._libp2pNode.hangUp(maddr, callback) }), diff --git a/test/core/bitswap.spec.js b/test/core/bitswap.spec.js index ebadb2b99b..5c0ce9f976 100644 --- a/test/core/bitswap.spec.js +++ b/test/core/bitswap.spec.js @@ -239,4 +239,14 @@ describe('bitswap', function () { }) }) }) + + describe('unwant', () => { + it('should callback with error for invalid CID input', (done) => { + inProcNode.bitswap.unwant('INVALID CID', (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_CID') + done() + }) + }) + }) }) diff --git a/test/core/block.spec.js b/test/core/block.spec.js new file mode 100644 index 0000000000..fc47d3bf39 --- /dev/null +++ b/test/core/block.spec.js @@ -0,0 +1,69 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) + +const IPFSFactory = require('ipfsd-ctl') +const IPFS = require('../../src/core') + +describe('block', () => { + let ipfsd, ipfs + + before(function (done) { + this.timeout(20 * 1000) + + const factory = IPFSFactory.create({ type: 'proc' }) + + factory.spawn({ + exec: IPFS, + initOptions: { bits: 512 } + }, (err, _ipfsd) => { + expect(err).to.not.exist() + ipfsd = _ipfsd + ipfs = _ipfsd.api + done() + }) + }) + + after((done) => { + if (ipfsd) { + ipfsd.stop(done) + } else { + done() + } + }) + + describe('get', () => { + it('should callback with error for invalid CID input', (done) => { + ipfs.block.get('INVALID CID', (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_CID') + done() + }) + }) + }) + + describe('rm', () => { + it('should callback with error for invalid CID input', (done) => { + ipfs.block.rm('INVALID CID', (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_CID') + done() + }) + }) + }) + + describe('stat', () => { + it('should callback with error for invalid CID input', (done) => { + ipfs.block.stat('INVALID CID', (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_CID') + done() + }) + }) + }) +}) diff --git a/test/core/dag.spec.js b/test/core/dag.spec.js new file mode 100644 index 0000000000..1c71d79943 --- /dev/null +++ b/test/core/dag.spec.js @@ -0,0 +1,67 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) + +const IPFSFactory = require('ipfsd-ctl') +const IPFS = require('../../src/core') + +describe('dag', () => { + let ipfsd, ipfs + + before(function (done) { + this.timeout(20 * 1000) + + const factory = IPFSFactory.create({ type: 'proc' }) + + factory.spawn({ + exec: IPFS, + initOptions: { bits: 512 } + }, (err, _ipfsd) => { + expect(err).to.not.exist() + ipfsd = _ipfsd + ipfs = _ipfsd.api + done() + }) + }) + + after((done) => { + if (ipfsd) { + ipfsd.stop(done) + } else { + done() + } + }) + + describe('get', () => { + it('should callback with error for invalid string CID input', (done) => { + ipfs.dag.get('INVALID CID', (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_CID') + done() + }) + }) + + it('should callback with error for invalid buffer CID input', (done) => { + ipfs.dag.get(Buffer.from('INVALID CID'), (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_CID') + done() + }) + }) + }) + + describe('tree', () => { + it('should callback with error for invalid CID input', (done) => { + ipfs.dag.tree('INVALID CID', (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_CID') + done() + }) + }) + }) +}) diff --git a/test/core/dht.spec.js b/test/core/dht.spec.js new file mode 100644 index 0000000000..0f08b15bb1 --- /dev/null +++ b/test/core/dht.spec.js @@ -0,0 +1,49 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) + +const IPFSFactory = require('ipfsd-ctl') +const IPFS = require('../../src/core') + +describe('dht', () => { + let ipfsd, ipfs + + before(function (done) { + this.timeout(20 * 1000) + + const factory = IPFSFactory.create({ type: 'proc' }) + + factory.spawn({ + exec: IPFS, + initOptions: { bits: 512 } + }, (err, _ipfsd) => { + expect(err).to.not.exist() + ipfsd = _ipfsd + ipfs = _ipfsd.api + done() + }) + }) + + after((done) => { + if (ipfsd) { + ipfsd.stop(done) + } else { + done() + } + }) + + describe('findprovs', () => { + it('should callback with error for invalid CID input', (done) => { + ipfs.dht.findprovs('INVALID CID', (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_CID') + done() + }) + }) + }) +}) diff --git a/test/core/files.spec.js b/test/core/files.spec.js new file mode 100644 index 0000000000..e34ecbed2c --- /dev/null +++ b/test/core/files.spec.js @@ -0,0 +1,78 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) + +const pull = require('pull-stream') +const IPFSFactory = require('ipfsd-ctl') +const IPFS = require('../../src/core') + +describe('files', () => { + let ipfsd, ipfs + + before(function (done) { + this.timeout(20 * 1000) + + const factory = IPFSFactory.create({ type: 'proc' }) + + factory.spawn({ + exec: IPFS, + initOptions: { bits: 512 } + }, (err, _ipfsd) => { + expect(err).to.not.exist() + ipfsd = _ipfsd + ipfs = _ipfsd.api + done() + }) + }) + + after((done) => { + if (ipfsd) { + ipfsd.stop(done) + } else { + done() + } + }) + + describe('get', () => { + it('should callback with error for invalid IPFS path input', (done) => { + const invalidPath = null + ipfs.files.get(invalidPath, (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_PATH') + done() + }) + }) + }) + + describe('getReadableStream', () => { + it('should return erroring stream for invalid IPFS path input', (done) => { + const invalidPath = null + const stream = ipfs.files.getReadableStream(invalidPath) + + stream.on('error', (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_PATH') + done() + }) + }) + }) + + describe('getPullStream', () => { + it('should return erroring stream for invalid IPFS path input', (done) => { + const invalidPath = null + pull( + ipfs.files.getPullStream(invalidPath), + pull.collect((err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_PATH') + done() + }) + ) + }) + }) +}) diff --git a/test/core/object.spec.js b/test/core/object.spec.js new file mode 100644 index 0000000000..d126bb94b6 --- /dev/null +++ b/test/core/object.spec.js @@ -0,0 +1,59 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) + +const IPFSFactory = require('ipfsd-ctl') +const IPFS = require('../../src/core') + +describe('object', () => { + let ipfsd, ipfs + + before(function (done) { + this.timeout(20 * 1000) + + const factory = IPFSFactory.create({ type: 'proc' }) + + factory.spawn({ + exec: IPFS, + initOptions: { bits: 512 } + }, (err, _ipfsd) => { + expect(err).to.not.exist() + ipfsd = _ipfsd + ipfs = _ipfsd.api + done() + }) + }) + + after((done) => { + if (ipfsd) { + ipfsd.stop(done) + } else { + done() + } + }) + + describe('get', () => { + it('should callback with error for invalid CID input', (done) => { + ipfs.object.get('INVALID CID', (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_CID') + done() + }) + }) + }) + + describe('put', () => { + it('should callback with error for invalid CID input', (done) => { + ipfs.object.put({ multihash: 'INVALID CID' }, (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_CID') + done() + }) + }) + }) +}) diff --git a/test/core/pin.spec.js b/test/core/pin.spec.js new file mode 100644 index 0000000000..3b530db147 --- /dev/null +++ b/test/core/pin.spec.js @@ -0,0 +1,57 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) + +const IPFSFactory = require('ipfsd-ctl') +const IPFS = require('../../src/core') + +describe('pin', () => { + let ipfsd, ipfs + + before(function (done) { + this.timeout(20 * 1000) + + const factory = IPFSFactory.create({ type: 'proc' }) + + factory.spawn({ + exec: IPFS, + initOptions: { bits: 512 } + }, (err, _ipfsd) => { + expect(err).to.not.exist() + ipfsd = _ipfsd + ipfs = _ipfsd.api + done() + }) + }) + + after((done) => { + if (ipfsd) { + ipfsd.stop(done) + } else { + done() + } + }) + + describe('ls', () => { + it('should callback with error for invalid non-string pin type option', (done) => { + ipfs.pin.ls({ type: 6 }, (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_PIN_TYPE') + done() + }) + }) + + it('should callback with error for invalid string pin type option', (done) => { + ipfs.pin.ls({ type: '__proto__' }, (err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_PIN_TYPE') + done() + }) + }) + }) +}) diff --git a/test/core/stats.spec.js b/test/core/stats.spec.js new file mode 100644 index 0000000000..15e872efaf --- /dev/null +++ b/test/core/stats.spec.js @@ -0,0 +1,53 @@ +/* eslint max-nested-callbacks: ["error", 8] */ +/* eslint-env mocha */ +'use strict' + +const chai = require('chai') +const dirtyChai = require('dirty-chai') +const expect = chai.expect +chai.use(dirtyChai) + +const pull = require('pull-stream') +const IPFSFactory = require('ipfsd-ctl') +const IPFS = require('../../src/core') + +describe('stats', () => { + let ipfsd, ipfs + + before(function (done) { + this.timeout(20 * 1000) + + const factory = IPFSFactory.create({ type: 'proc' }) + + factory.spawn({ + exec: IPFS, + initOptions: { bits: 512 } + }, (err, _ipfsd) => { + expect(err).to.not.exist() + ipfsd = _ipfsd + ipfs = _ipfsd.api + done() + }) + }) + + after((done) => { + if (ipfsd) { + ipfsd.stop(done) + } else { + done() + } + }) + + describe('bwPullStream', () => { + it('should return erroring stream for invalid interval option', (done) => { + pull( + ipfs.stats.bwPullStream({ poll: true, interval: 'INVALID INTERVAL' }), + pull.collect((err) => { + expect(err).to.exist() + expect(err.code).to.equal('ERR_INVALID_POLL_INTERVAL') + done() + }) + ) + }) + }) +})