Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multiaddr checks #27

Merged
merged 6 commits into from
Mar 3, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ isIPFS.ipnsSubdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j5
isIPFS.ipnsSubdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.dweb.link') // false
isIPFS.ipnsSubdomain('http://QmcNioXSC1bfJj1dcFErhUfyjFzoX2HodkRccsFFVJJvg8.ipns.dweb.link') // false
isIPFS.ipnsSubdomain('http://foo-bar.ipns.dweb.link') // false (not a PeerID)

isIPFS.multiaddr('/ip4/127.0.0.1/udp/1234') // true
isIPFS.multiaddr('/ip4/127.0.0.1/udp/1234/http') // true
isIPFS.multiaddr('/ip6/::1/udp/1234') // true
isIPFS.multiaddr('ip6/::1/udp/1234') // false
isIPFS.multiaddr('/yoloinvalid/::1/udp/1234') // false

isIPFS.peerMultiaddr('/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4') // true
isIPFS.peerMultiaddr('/ip4/127.0.0.1/tcp/1234/ws/ipfs/QmUjNmr8TgJCn1Ao7DvMy4cjoZU15b9bwSCBLE3vwXiwgj') // true
isIPFS.peerMultiaddr('/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4/p2p-circuit/ipfs/QmUjNmr8TgJCn1Ao7DvMy4cjoZU15b9bwSCBLE3vwXiwgj') // true
isIPFS.peerMultiaddr('/ip4/127.0.0.1/udp/1234') // false
```

# API
Expand All @@ -115,7 +126,7 @@ Returns `true` if the provided string is a valid `multihash` or `false` otherwis

### `isIPFS.cid(hash)`

Returns `true` if the provided string is a valid `CID` or `false` otherwise.
Returns `true` if the provided string or [`CID`](https://github.com/ipld/js-cid) represents a valid [CID](https://docs.ipfs.io/guides/concepts/cid/) or `false` otherwise.

### `isIPFS.base32cid(hash)`

Expand Down Expand Up @@ -172,6 +183,15 @@ Returns `true` if the provided string includes a valid IPFS subdomain or `false`

Returns `true` if the provided string includes a valid IPNS subdomain or `false` otherwise.

## Multiaddrs

### `isIPFS.multiaddr(addr)`

Returns `true` if the provided `string`, [`Multiaddr`](https://github.com/multiformats/js-multiaddr) or `Buffer` represents a valid [multiaddr](https://multiformats.io/multiaddr/) or `false` otherwise.

### `isIPFS.peerMultiaddr(addr)`

Returns `true` if the provided `string`, [`Multiaddr`](https://github.com/multiformats/js-multiaddr) or `Buffer` represents a valid "IPFS Peer" multiaddr (matching [`IPFS` format from `mafmt`](https://github.com/multiformats/js-mafmt#api)) or `false` otherwise.

# License

Expand Down
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@
"author": "Francisco Dias <francisco@baiodias.com> (http://franciscodias.net/)",
"license": "MIT",
"dependencies": {
"bs58": "4.0.1",
"bs58": "^4.0.1",
"cids": "~0.5.6",
"mafmt": "^6.0.6",
"multiaddr": "^6.0.4",
"multibase": "~0.6.0",
"multihashes": "~0.4.13"
},
"devDependencies": {
"aegir": "15.0.1",
"chai": "4.1.2",
"pre-commit": "1.2.2"
"aegir": "^18.2.0",
"chai": "^4.2.0",
"pre-commit": "^1.2.2"
},
"repository": {
"type": "git",
Expand Down
27 changes: 27 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
const base58 = require('bs58')
const multihash = require('multihashes')
const multibase = require('multibase')
const Multiaddr = require('multiaddr')
const mafmt = require('mafmt')
const CID = require('cids')

const urlPattern = /^https?:\/\/[^/]+\/(ip(f|n)s)\/((\w+).*)/
Expand Down Expand Up @@ -42,6 +44,29 @@ function isCID (hash) {
}
}

function isMultiaddr (input) {
if (!input) return false
if (isString(input) || input instanceof Buffer) {
try {
new Multiaddr(input) // eslint-disable-line no-new
return true
} catch (e) {
return false
}
}
if (Multiaddr.isMultiaddr(input)) return true
return false
}

function isPeerMultiaddr (input) {
if (!isMultiaddr(input)) return false
if (input instanceof Buffer) {
// mafmt does not support Buffer input
input = new Multiaddr(input)
}
return mafmt.IPFS.matches(input)
}

function isIpfs (input, pattern, protocolMatch = defaultProtocolMatch, hashMatch = defaultHashMath) {
const formatted = convertToString(input)
if (!formatted) {
Expand Down Expand Up @@ -116,6 +141,8 @@ const ipnsSubdomain = (url) => isIpns(url, fqdnPattern, fqdnProtocolMatch, fqdnH

module.exports = {
multihash: isMultihash,
multiaddr: isMultiaddr,
peerMultiaddr: isPeerMultiaddr,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR but I'm musing on naming, I would ideally like to do something like const { isMultiaddr } = require('is-ipfs') or import { isMultiaddr } from 'is-ipfs' rather than have to import the whole library.

If we eventually start using es-modules we can tree shake the stuff we're not using but as it is now we're encouraging importing the whole library since (for example) const { cid } = require('is-ipfs') would likely conflict with cid instance vars we have in code and it's not descriptive to validate a cid like if (cid(myCid)).

Copy link
Member Author

@lidel lidel Feb 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good point, let's track this in #28

cid: isCID,
base32cid: (cid) => (isMultibase(cid) === 'base32' && isCID(cid)),
ipfsSubdomain: ipfsSubdomain,
Expand Down
8 changes: 8 additions & 0 deletions test/test-cid.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,16 @@
const base58 = require('bs58')
const expect = require('chai').expect
const isIPFS = require('../src/index')
const CID = require('cids')

describe('ipfs cid', () => {
it('isIPFS.cid should match a valid CID instance', (done) => {
const cid = new CID('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')
const actual = isIPFS.cid(cid)
expect(actual).to.equal(true)
done()
})

it('isIPFS.cid should match a valid CIDv0 (multihash)', (done) => {
const actual = isIPFS.cid('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o')
expect(actual).to.equal(true)
Expand Down
143 changes: 143 additions & 0 deletions test/test-multiaddr.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/* eslint-env mocha */
'use strict'

const expect = require('chai').expect
const Multiaddr = require('multiaddr')
const isIPFS = require('../src/index')

describe('ipfs multiaddr', () => {
it('isIPFS.multiaddr should match a string with valid ip4 multiaddr', (done) => {
const actual = isIPFS.multiaddr('/ip4/127.0.0.1/udp/1234/http')
expect(actual).to.equal(true)
done()
})

it('isIPFS.multiaddr should match a string with valid ip6 multiaddr', (done) => {
const actual = isIPFS.multiaddr('/ip6/::1/udp/1234/http')
expect(actual).to.equal(true)
done()
})

it('isIPFS.multiaddr should match a valid Multiaddr instance', (done) => {
const ma = new Multiaddr('/ip6/::1/udp/1234/http')
const actual = isIPFS.multiaddr(ma)
expect(actual).to.equal(true)
done()
})

it('isIPFS.multiaddr should match a Buffer with multiaddr', (done) => {
const ma = new Multiaddr('/ip6/::1/udp/1234/http')
const actual = isIPFS.multiaddr(Buffer.from(ma.buffer))
expect(actual).to.equal(true)
done()
})

it('isIPFS.multiaddr should not match random Buffer', (done) => {
const actual = isIPFS.multiaddr(Buffer.from('randombuffer'))
expect(actual).to.equal(false)
done()
})

it('isIPFS.multiaddr should not match an invalid multiaddr (no initial slash)', (done) => {
const actual = isIPFS.multiaddr('ip4/127.0.0.1/udp/1234/http')
expect(actual).to.equal(false)
done()
})

it('isIPFS.multiaddr should not match an invalid multiaddr (unknown namespace)', (done) => {
const actual = isIPFS.multiaddr('/yoloinvalid/127.0.0.1/udp/1234/http')
expect(actual).to.equal(false)
done()
})

it('isIPFS.multiaddr should not match an invalid multiaddr', (done) => {
const actual = isIPFS.multiaddr('noop')
expect(actual).to.equal(false)
done()
})

it('isIPFS.multiaddr should not match an invalid multiaddr data type', (done) => {
const actual = isIPFS.multiaddr(4)
expect(actual).to.equal(false)
done()
})
})

describe('ipfs peerMultiaddr', () => {
// https://github.com/multiformats/js-mafmt/blob/v6.0.6/test/index.spec.js#L137
const goodCircuit = [
'/p2p-circuit',
'/p2p-circuit/ipfs/QmUjNmr8TgJCn1Ao7DvMy4cjoZU15b9bwSCBLE3vwXiwgj',
'/p2p-circuit/ip4/127.0.0.1/tcp/20008/ws/ipfs/QmUjNmr8TgJCn1Ao7DvMy4cjoZU15b9bwSCBLE3vwXiwgj',
'/p2p-circuit/ip4/1.2.3.4/tcp/3456/ws/p2p-webrtc-star/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4',
'/p2p-circuit/ip4/1.2.3.4/tcp/3456/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4',
'/p2p-circuit/ip4/127.0.0.1/tcp/4002/ipfs/QmddWMcQX6orJGHpETYMyPgXrCXCtYANMFVDCvhKoDwLqA',
'/p2p-circuit/ipfs/QmddWMcQX6orJGHpETYMyPgXrCXCtYANMFVDCvhKoDwLqA',
'/p2p-circuit/ip4/127.0.0.1/tcp/20008/ws/ipfs/QmUjNmr8TgJCn1Ao7DvMy4cjoZU15b9bwSCBLE3vwXiwgj/' +
'p2p-circuit/ipfs/QmUjNmr8TgJCn1Ao7DvMy4cjoZU15b9bwSCBLE3vwXiwgj'
]
// https://github.com/multiformats/js-mafmt/blob/v6.0.6/test/index.spec.js#L157
const validPeerMultiaddrs = [
'/ip4/127.0.0.1/tcp/20008/ws/ipfs/QmUjNmr8TgJCn1Ao7DvMy4cjoZU15b9bwSCBLE3vwXiwgj',
'/ip4/1.2.3.4/tcp/3456/ws/p2p-webrtc-star/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4',
'/ip4/1.2.3.4/tcp/3456/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4',
'/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4/p2p-circuit',
'/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4/p2p-circuit/ipfs/QmUjNmr8TgJCn1Ao7DvMy4cjoZU15b9bwSCBLE3vwXiwgj'
].concat(goodCircuit)

it('isIPFS.peerMultiaddr should match a string with a valid IPFS peer', (done) => {
for (let addr of validPeerMultiaddrs) {
const actual = isIPFS.peerMultiaddr(addr)
expect(actual, `isIPFS.peerMultiaddr(${addr})`).to.equal(true)
}
done()
})

it('isIPFS.peerMultiaddr should match a valid Multiaddr instance', (done) => {
for (let addr of validPeerMultiaddrs) {
const ma = new Multiaddr(addr)
const actual = isIPFS.peerMultiaddr(ma)
expect(actual, `isIPFS.peerMultiaddr(${addr})`).to.equal(true)
}
done()
})

it('isIPFS.peerMultiaddr should match a Buffer with multiaddr', (done) => {
for (let addr of validPeerMultiaddrs) {
const ma = new Multiaddr(addr)
const actual = isIPFS.peerMultiaddr((Buffer.from(ma.buffer)))
expect(actual, `isIPFS.peerMultiaddr(${addr})`).to.equal(true)
}
done()
})

it('isIPFS.peerMultiaddr should not match random Buffer', (done) => {
const actual = isIPFS.peerMultiaddr(Buffer.from('randombuffer'))
expect(actual).to.equal(false)
done()
})

it('isIPFS.peerMultiaddr should not match an invalid multiaddr (no initial slash)', (done) => {
const actual = isIPFS.peerMultiaddr('ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4')
expect(actual).to.equal(false)
done()
})

it('isIPFS.peerMultiaddr should not match an invalid multiaddr (unknown namespace)', (done) => {
const actual = isIPFS.peerMultiaddr('/yoloinvalid/1.2.3.4/tcp/3456/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSoooo4')
expect(actual).to.equal(false)
done()
})

it('isIPFS.peerMultiaddr should not match an invalid multiaddr', (done) => {
const actual = isIPFS.peerMultiaddr('noop')
expect(actual).to.equal(false)
done()
})

it('isIPFS.peerMultiaddr should not match an invalid multiaddr data type', (done) => {
const actual = isIPFS.peerMultiaddr(4)
expect(actual).to.equal(false)
done()
})
})