diff --git a/.gitignore b/.gitignore index 578a0d0..858990e 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ lib-cov # Coverage directory used by tools like istanbul coverage +.nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt diff --git a/.travis.yml b/.travis.yml index 2af65f7..a46ba0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ language: node_js node_js: - - 4 - - 5 - node # Make sure we have new NPM. diff --git a/README.md b/README.md index c06fce5..379254c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ is-ipfs ==== -[![build status](https://secure.travis-ci.org/ipfs/is-ipfs.svg)](http://travis-ci.org/ipfs/is-ipfs) -[![dignified.js](https://img.shields.io/badge/follows-dignified.js-blue.svg?style=flat-square)](https://github.com/dignifiedquire/dignified.js) +[![](https://img.shields.io/github/release/ipfs/is-ipfs.svg)](https://github.com/ipfs/is-ipfs/releases/latest) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](https://webchat.freenode.net/?channels=%23ipfs) -A set of utilities to help identify [IPFS](https://ipfs.io/) resources. +> A set of utilities to help identify [IPFS](https://ipfs.io/) resources +## Lead Maintainer -## Install +[Marcin Rataj](https://github.com/lidel) + +# Install ### In Node.js through npm @@ -34,7 +37,7 @@ Loading this module through a script tag will make the ```IsIpfs``` obj availabl ``` -## Usage +# Usage ```javascript const isIPFS = require('is-ipfs') @@ -45,6 +48,9 @@ isIPFS.cid('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true (CIDv0) isIPFS.cid('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7') // true (CIDv1) isIPFS.cid('noop') // false +isIPFS.base32cid('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va') // true +isIPFS.base32cid('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // false + isIPFS.url('https://ipfs.io/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // true isIPFS.url('https://ipfs.io/ipns/github.com') // true isIPFS.url('https://github.com/ipfs/js-ipfs/blob/master/README.md') // false @@ -71,9 +77,31 @@ isIPFS.ipfsPath('/ipfs/invalid-hash') // false isIPFS.ipnsPath('/ipfs/QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') // false isIPFS.ipnsPath('/ipns/github.com') // true + +isIPFS.subdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') // true +isIPFS.subdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.ipns.dweb.link') // true +isIPFS.subdomain('http://www.bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') // false +isIPFS.subdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.dweb.link') // false + +isIPFS.ipfsSubdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') // true +isIPFS.ipfsSubdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.dweb.link') // false + +isIPFS.ipnsSubdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.ipns.dweb.link') // true +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) ``` -## API +# API + +A suite of util methods that provides efficient validation. + +Detection of IPFS Paths and identifiers in URLs is a two-stage process: +1. `urlPattern`/`pathPattern`/`subdomainPattern` regex is applied to quickly identify potential candidates +2. proper CID validation is applied to remove false-positives + + +## Utils ### `isIPFS.multihash(hash)` @@ -83,17 +111,15 @@ Returns `true` if the provided string is a valid `multihash` or `false` otherwis Returns `true` if the provided string is a valid `CID` or `false` otherwise. -### `isIPFS.url(url)` +### `isIPFS.base32cid(hash)` -Returns `true` if the provided string is a valid IPFS or IPNS url or `false` otherwise. +Returns `true` if the provided string is a valid `CID` in Base32 encoding or `false` otherwise. -### `isIPFS.path(path)` - -Returns `true` if the provided string is a valid IPFS or IPNS path or `false` otherwise. +## URLs -### `isIPFS.urlOrPath(path)` +### `isIPFS.url(url)` -Returns `true` if the provided string is a valid IPFS or IPNS url or path or `false` otherwise. +Returns `true` if the provided string is a valid IPFS or IPNS url or `false` otherwise. ### `isIPFS.ipfsUrl(url)` @@ -103,6 +129,19 @@ Returns `true` if the provided string is a valid IPFS url or `false` otherwise. Returns `true` if the provided string is a valid IPNS url or `false` otherwise. +## Paths + +Standalone validation of IPFS Paths: `/ip(f|n)s//..` + +### `isIPFS.path(path)` + +Returns `true` if the provided string is a valid IPFS or IPNS path or `false` otherwise. + +### `isIPFS.urlOrPath(path)` + +Returns `true` if the provided string is a valid IPFS or IPNS url or path or `false` otherwise. + + ### `isIPFS.ipfsPath(path)` Returns `true` if the provided string is a valid IPFS path or `false` otherwise. @@ -111,10 +150,23 @@ Returns `true` if the provided string is a valid IPFS path or `false` otherwise. Returns `true` if the provided string is a valid IPNS path or `false` otherwise. +## Subdomains + +Validated subdomain convention: `cidv1b32.ip(f|n)s.domain.tld` + +### `isIPFS.subdomain(url)` + +Returns `true` if the provided string includes a valid IPFS or IPNS subdomain or `false` otherwise. + +### `isIPFS.ipfsSubdomain(url)` + +Returns `true` if the provided string includes a valid IPFS subdomain or `false` otherwise. + +### `isIPFS.ipnsSubdomain(url)` -**Note:** the regex used for these checks is also exported as `isIPFS.urlPattern` +Returns `true` if the provided string includes a valid IPNS subdomain or `false` otherwise. -## License +# License MIT diff --git a/ci/Jenkinsfile b/ci/Jenkinsfile new file mode 100644 index 0000000..c1e3f2c --- /dev/null +++ b/ci/Jenkinsfile @@ -0,0 +1 @@ +javascript() diff --git a/package.json b/package.json index 2dbcceb..5c3cc26 100644 --- a/package.json +++ b/package.json @@ -1,54 +1,56 @@ { "name": "is-ipfs", - "version": "0.3.2", + "version": "0.4.0", "description": "A set of utilities to help identify IPFS resources", "main": "src/index.js", "browser": { "fs": false }, "scripts": { - "test:node": "aegir-test node", - "test:browser": "aegir-test browser", - "test": "aegir-test", - "lint": "aegir-lint", - "release": "aegir-release", - "release-minor": "aegir-release --type minor", - "release-major": "aegir-release --type major", - "build": "aegir-build", - "coverage": "aegir-coverage", - "coverage-publish": "aegir-coverage publish" + "test:node": "aegir test --target node", + "test:browser": "aegir test --target browser", + "test": "aegir test", + "lint": "aegir lint", + "release": "aegir release", + "release-minor": "aegir release --type minor", + "release-major": "aegir release --type major", + "build": "aegir build", + "coverage": "aegir coverage", + "coverage-publish": "aegir coverage --upload" }, "pre-commit": [ "test", "lint" ], "keywords": [ + "js-ipfs", "ipfs" ], "author": "Francisco Dias (http://franciscodias.net/)", "license": "MIT", "dependencies": { - "cids": "~0.5.1", - "bs58": "^4.0.1", - "multihashes": "~0.4.9" + "bs58": "4.0.1", + "cids": "0.5.3", + "multibase": "0.4.0", + "multihashes": "0.4.13" }, "devDependencies": { - "aegir": "^11.0.2", - "chai": "^4.1.2", - "pre-commit": "^1.2.2" + "aegir": "15.0.1", + "chai": "4.1.2", + "pre-commit": "1.2.2" }, "repository": { "type": "git", - "url": "https://github.com/xicombd/is-ipfs.git" + "url": "https://github.com/ipfs/is-ipfs.git" }, "bugs": { - "url": "https://github.com/xicombd/is-ipfs/issues" + "url": "https://github.com/ipfs/is-ipfs/issues" }, - "homepage": "https://github.com/xicombd/is-ipfs", + "homepage": "https://github.com/ipfs/is-ipfs", "contributors": [ "David Dias ", "Francisco Baio Dias ", "Marcin Rataj ", "nginnever " ] -} \ No newline at end of file +} diff --git a/src/index.js b/src/index.js index 516b2db..acfef52 100644 --- a/src/index.js +++ b/src/index.js @@ -2,15 +2,22 @@ const base58 = require('bs58') const multihash = require('multihashes') +const multibase = require('multibase') const CID = require('cids') const urlPattern = /^https?:\/\/[^/]+\/(ip(f|n)s)\/((\w+).*)/ const pathPattern = /^\/(ip(f|n)s)\/((\w+).*)/ +const defaultProtocolMatch = 1 +const defaultHashMath = 4 + +const fqdnPattern = /^https?:\/\/([^/]+)\.(ip(?:f|n)s)\.[^/]+/ +const fqdnHashMatch = 1 +const fqdnProtocolMatch = 2 function isMultihash (hash) { const formatted = convertToString(hash) try { - const buffer = new Buffer(base58.decode(formatted)) + const buffer = Buffer.from(base58.decode(formatted)) multihash.decode(buffer) return true } catch (e) { @@ -18,6 +25,14 @@ function isMultihash (hash) { } } +function isMultibase (hash) { + try { + return multibase.isEncoded(hash) + } catch (e) { + return false + } +} + function isCID (hash) { try { return CID.isCID(new CID(hash)) @@ -26,7 +41,7 @@ function isCID (hash) { } } -function isIpfs (input, pattern) { +function isIpfs (input, pattern, protocolMatch = defaultProtocolMatch, hashMatch = defaultHashMath) { const formatted = convertToString(input) if (!formatted) { return false @@ -37,15 +52,23 @@ function isIpfs (input, pattern) { return false } - if (match[1] !== 'ipfs') { + if (match[protocolMatch] !== 'ipfs') { return false } - const hash = match[4] + let hash = match[hashMatch] + + if (hash && pattern === fqdnPattern) { + // when doing checks for subdomain context + // ensure hash is case-insensitive + // (browsers force-lowercase authority compotent anyway) + hash = hash.toLowerCase() + } + return isCID(hash) } -function isIpns (input, pattern) { +function isIpns (input, pattern, protocolMatch = defaultProtocolMatch, hashMatch) { const formatted = convertToString(input) if (!formatted) { return false @@ -55,10 +78,19 @@ function isIpns (input, pattern) { return false } - if (match[1] !== 'ipns') { + if (match[protocolMatch] !== 'ipns') { return false } + if (hashMatch && pattern === fqdnPattern) { + let hash = match[hashMatch] + // when doing checks for subdomain context + // ensure hash is case-insensitive + // (browsers force-lowercase authority compotent anyway) + hash = hash.toLowerCase() + return isCID(hash) + } + return true } @@ -74,9 +106,17 @@ function convertToString (input) { return false } +const ipfsSubdomain = (url) => isIpfs(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch) +const ipnsSubdomain = (url) => isIpns(url, fqdnPattern, fqdnProtocolMatch, fqdnHashMatch) + module.exports = { multihash: isMultihash, cid: isCID, + base32cid: (cid) => (isMultibase(cid) === 'base32' && isCID(cid)), + ipfsSubdomain: ipfsSubdomain, + ipnsSubdomain: ipnsSubdomain, + subdomain: (url) => (ipfsSubdomain(url) || ipnsSubdomain(url)), + subdomainPattern: fqdnPattern, ipfsUrl: (url) => isIpfs(url, urlPattern), ipnsUrl: (url) => isIpns(url, urlPattern), url: (url) => (isIpfs(url, urlPattern) || isIpns(url, urlPattern)), diff --git a/test/test-cid.spec.js b/test/test-cid.spec.js index 94f66e6..8243a48 100644 --- a/test/test-cid.spec.js +++ b/test/test-cid.spec.js @@ -13,13 +13,13 @@ describe('ipfs cid', () => { }) it('isIPFS.cid should match a valid CIDv0 (multihash) buffer', (done) => { - const actual = isIPFS.cid(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) + const actual = isIPFS.cid(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) expect(actual).to.equal(true) done() }) it('isIPFS.cid should not match a broken CIDv0 buffer', (done) => { - const actual = isIPFS.cid(new Buffer('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE70')) + const actual = isIPFS.cid(Buffer.from('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE70')) expect(actual).to.equal(false) done() }) @@ -36,6 +36,12 @@ describe('ipfs cid', () => { done() }) + it('isIPFS.cid should match a valid CIDv1 in Base32', (done) => { + const actual = isIPFS.cid('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va') + expect(actual).to.equal(true) + done() + }) + it('isIPFS.cid should not match an invalid CIDv1 (with a typo)', (done) => { const actual = isIPFS.cid('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ') expect(actual).to.equal(false) @@ -54,3 +60,41 @@ describe('ipfs cid', () => { done() }) }) + +describe('ipfs base32cid', () => { + it('isIPFS.base32cid should not match a valid CIDv0 (multihash in base58btc)', (done) => { + const actual = isIPFS.base32cid('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.base32cid should not match a valid CIDv1 in base58btc', (done) => { + const actual = isIPFS.cid('zdj7WWeQ43G6JJvLWQWZpyHuAMq6uYWRjkBXFad11vE2LHhQ7') + expect(actual).to.equal(true) + done() + }) + + it('isIPFS.base32cid should match a valid URL-safe CIDv1 in Base32', (done) => { + const actual = isIPFS.base32cid('bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va') + expect(actual).to.equal(true) + done() + }) + + it('isIPFS.base32cid should not match an invalid CID (with a typo)', (done) => { + const actual = isIPFS.base32cid('afybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.base32cid should not match an invalid CID', (done) => { + const actual = isIPFS.base32cid('noop') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.base32cid should not match an invalid CID data type', (done) => { + const actual = isIPFS.base32cid(4) + expect(actual).to.equal(false) + done() + }) +}) diff --git a/test/test-multihash.spec.js b/test/test-multihash.spec.js index 4c23166..d82a823 100644 --- a/test/test-multihash.spec.js +++ b/test/test-multihash.spec.js @@ -13,13 +13,13 @@ describe('ipfs multihash', () => { }) it('isIPFS.multihash should match a valid multihash buffer', (done) => { - const actual = isIPFS.multihash(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) + const actual = isIPFS.multihash(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) expect(actual).to.equal(true) done() }) it('isIPFS.multihash should not match a buffer', (done) => { - const actual = isIPFS.multihash(new Buffer('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE70')) + const actual = isIPFS.multihash(Buffer.from('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE70')) expect(actual).to.equal(false) done() }) diff --git a/test/test-path.spec.js b/test/test-path.spec.js index 87a2b18..e28057a 100644 --- a/test/test-path.spec.js +++ b/test/test-path.spec.js @@ -37,7 +37,7 @@ describe('ipfs path', () => { }) it('isIPFS.ipfsPath should not match a buffer data type', (done) => { - const actual = isIPFS.ipfsPath(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) + const actual = isIPFS.ipfsPath(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) expect(actual).to.equal(false) done() }) @@ -67,7 +67,7 @@ describe('ipfs path', () => { }) it('isIPFS.ipnsPath should not match a buffer data type', (done) => { - const actual = isIPFS.ipnsPath(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) + const actual = isIPFS.ipnsPath(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) expect(actual).to.equal(false) done() }) @@ -97,7 +97,7 @@ describe('ipfs path', () => { }) it('isIPFS.path should not match a buffer data type', (done) => { - const actual = isIPFS.path(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) + const actual = isIPFS.path(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) expect(actual).to.equal(false) done() }) @@ -127,7 +127,7 @@ describe('ipfs path', () => { }) it('isIPFS.urlOrPath should not match a buffer data type', (done) => { - const actual = isIPFS.ipfsPath(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) + const actual = isIPFS.ipfsPath(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) expect(actual).to.equal(false) done() }) diff --git a/test/test-subdomain.spec.js b/test/test-subdomain.spec.js new file mode 100644 index 0000000..23e0ffe --- /dev/null +++ b/test/test-subdomain.spec.js @@ -0,0 +1,145 @@ +/* eslint-env mocha */ +'use strict' + +const base58 = require('bs58') +const isIPFS = require('../src/index') +const expect = require('chai').expect + +describe('ipfs subdomain', () => { + it('isIPFS.ipfsSubdomain should match a cidv1b32', (done) => { + const actual = isIPFS.ipfsSubdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') + expect(actual).to.equal(true) + done() + }) + + it('isIPFS.ipfsSubdomain should match a cidv1b32 with complex ipfs path', (done) => { + const actual = isIPFS.ipfsSubdomain('http://bafybeidvtwx54qr44kidymvhfzefzxhgkieigwth6oswk75zhlzjdmunoy.ipfs.dweb.link/linkify-demo.html') + expect(actual).to.equal(true) + done() + }) + + it('isIPFS.ipfsSubdomain should not match non-cid subdomains', (done) => { + const actual = isIPFS.ipfsSubdomain('http://not-a-cid.ipfs.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.ipfsSubdomain should not match case-sensitive CID subdomains', (done) => { + // Origin forces browsers to lowercase the authority component + // so QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR + // becomes invalid CID: qmbwqxbekc3p8tqskc98xmwnzrzdtrlmimpl8wbutgsmnr + const actual = isIPFS.ipfsSubdomain('http://QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR.ipfs.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.ipfsSubdomain should not match if not under .ipfs. zone', (done) => { + // we require explicit convention of putting cidv1b32 under .ipfs. zone + // to make it clear content can be safely uplifted and loaded over IPFS + const actual = isIPFS.ipfsSubdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.ipfsSubdomain should not match a buffer data type', (done) => { + const actual = isIPFS.ipfsSubdomain(Buffer.from(base58.decode('QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR'))) + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.ipnsSubdomain should not match .ipfs. zone', (done) => { + const actual = isIPFS.ipnsSubdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.ipnsSubdomain should match a .ipns. zone with cidv1b32', (done) => { + const actual = isIPFS.ipnsSubdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.ipns.dweb.link') + expect(actual).to.equal(true) + done() + }) + + it('isIPFS.ipnsSubdomain should not match case-sensitive CID subdomains', (done) => { + // Origin forces browsers to lowercase the authority component + // so QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR + // becomes invalid CID: qmbwqxbekc3p8tqskc98xmwnzrzdtrlmimpl8wbutgsmnr + const actual = isIPFS.ipnsSubdomain('http://QmcNioXSC1bfJj1dcFErhUfyjFzoX2HodkRccsFFVJJvg8.ipns.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.ipnsSubdomain should not match .ipns. zone with non-cid subdomain', (done) => { + // we do not support opaque strings in subdomains, only peerids + const actual = isIPFS.ipnsSubdomain('http://a-dnslink-website.com.ipns.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.ipnsSubdomain should not match without .ipns. zone', (done) => { + const actual = isIPFS.ipnsSubdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.ipnsSubdomain should not match a buffer data type', (done) => { + const actual = isIPFS.ipnsSubdomain(Buffer.from(base58.decode('QmNQuBJ8tg4QN6mSLXHekxBbcToPwKxamWNrDdEugxMTDd'))) + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.subdomain should match an ipfs subdomain', (done) => { + const actual = isIPFS.subdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') + expect(actual).to.equal(true) + done() + }) + + it('isIPFS.subdomain should match an ipns subdomain with PeerID as cidv1b32', (done) => { + const actual = isIPFS.subdomain('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.ipns.dweb.link') + expect(actual).to.equal(true) + done() + }) + + it('isIPFS.subdomain should not match if fqdn does not start with cidv1b32', (done) => { + const actual = isIPFS.subdomain('http://www.bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.subdomain should not match if no ipfs/ipns zone', (done) => { + const actual = isIPFS.subdomain('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.subdomain should not match if ipns peerid is invalid', (done) => { + const actual = isIPFS.subdomain('http://not-a-cid.ipns.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.subdomain should not match a buffer data type', (done) => { + const actual = isIPFS.subdomain(Buffer.from(base58.decode('QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR'))) + expect(actual).to.equal(false) + done() + }) + + /* We keep subdomain logic separate from legacy urlOrPath checks, below is a fail-safe to ensure we keep that behavior */ + + it('isIPFS.urlOrPath should not match ipfs url with cidv1b32 subdomain', (done) => { + const actual = isIPFS.urlOrPath('http://bafybeie5gq4jxvzmsym6hjlwxej4rwdoxt7wadqvmmwbqi7r27fclha2va.ipfs.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.urlOrPath should not match ipns url', (done) => { + const actual = isIPFS.urlOrPath('http://bafybeiabc2xofh6tdi6vutusorpumwcikw3hf3st4ecjugo6j52f6xwc6q.ipns.dweb.link') + expect(actual).to.equal(false) + done() + }) + + it('isIPFS.urlOrPath should not match ipns in subdomain', (done) => { + const actual = isIPFS.urlOrPath('http://a-dnslink-website.com.ipns.dweb.link') + expect(actual).to.equal(false) + done() + }) +}) diff --git a/test/test-url.spec.js b/test/test-url.spec.js index a0e61eb..dcfaf1c 100644 --- a/test/test-url.spec.js +++ b/test/test-url.spec.js @@ -37,7 +37,7 @@ describe('ipfs url', () => { }) it('isIPFS.ipfsUrl should not match a buffer input', (done) => { - const actual = isIPFS.ipfsUrl(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) + const actual = isIPFS.ipfsUrl(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) expect(actual).to.equal(false) done() }) @@ -67,7 +67,7 @@ describe('ipfs url', () => { }) it('isIPFS.ipnsUrl should not match a buffer input', (done) => { - const actual = isIPFS.ipnsUrl(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) + const actual = isIPFS.ipnsUrl(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) expect(actual).to.equal(false) done() }) @@ -97,7 +97,7 @@ describe('ipfs url', () => { }) it('isIPFS.url should not match a buffer input', (done) => { - const actual = isIPFS.url(new Buffer(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) + const actual = isIPFS.url(Buffer.from(base58.decode('QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o'))) expect(actual).to.equal(false) done() })