diff --git a/src/http/api/routes/webui.js b/src/http/api/routes/webui.js index 9d6926f6aa..c621c75a22 100644 --- a/src/http/api/routes/webui.js +++ b/src/http/api/routes/webui.js @@ -1,9 +1,25 @@ 'use strict' const Joi = require('@hapi/joi') +const Boom = require('@hapi/boom') const resources = require('../../gateway/resources') +const webuiPath = '/ipfs/QmQNHd1suZTktPRhP7DD4nKWG46ZRSxkwHocycHVrK3dYW' + +const failAction = (request, h, err) => { + // match go-ipfs and return 404 without any details if path validation failed + if (err.name === 'ValidationError') throw Boom.notFound() + return err +} + module.exports = [ + { + method: '*', + path: '/webui', + handler (request, h) { + return h.redirect(webuiPath) + } + }, { method: '*', path: '/ipfs/{path*}', @@ -11,8 +27,9 @@ module.exports = [ handler: resources.gateway.handler, validate: { params: { - path: Joi.string().required() - } + path: Joi.string().regex(new RegExp(webuiPath.replace('/ipfs/', '^'))).required() + }, + failAction }, response: { ranges: false // disable built-in support, handler does it manually @@ -24,9 +41,25 @@ module.exports = [ }, { method: '*', - path: '/webui', - handler (request, h) { - return h.redirect('/ipfs/QmQNHd1suZTktPRhP7DD4nKWG46ZRSxkwHocycHVrK3dYW') + path: '/ipns/{path*}', + options: { + handler: resources.gateway.handler, + validate: { + params: { + path: Joi.alternatives().try( + // be careful here, someone could register webui.ipfs.io.evil.com + Joi.string().regex(/^webui\.ipfs\.io\//), // ends with '/'' + Joi.string().regex(/^webui\.ipfs\.io$/) // redirect will add '/' + ).required() + }, + failAction + }, + response: { + ranges: false // disable built-in support, handler does it manually + }, + ext: { + onPostHandler: { method: resources.gateway.afterHandler } + } } } ] diff --git a/test/http-api/inject/webui.js b/test/http-api/inject/webui.js new file mode 100644 index 0000000000..f9af5c2998 --- /dev/null +++ b/test/http-api/inject/webui.js @@ -0,0 +1,66 @@ +/* eslint-env mocha */ +'use strict' + +const expect = require('chai').expect + +module.exports = (http) => { + describe('Web UI', function () { + let api + + before(() => { + api = http.api._httpApi._apiServers[0] + }) + + it('allow /webui', async () => { + const res = await api.inject({ + method: 'GET', + url: '/webui' + }) + // it should return a redirect + expect(res.statusCode).to.equal(302) + expect(res.headers.location).to.exist() + }) + + it('disallow /ipfs/ paths that are not webui', async () => { + const res = await api.inject({ + method: 'GET', + url: '/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn' // empty dir + }) + expect(res.statusCode).to.equal(404) + }) + + it('disallow /ipns/ paths that are not webui', async () => { + const res = await api.inject({ + method: 'GET', + url: '/ipns/ipfs.io' // empty dir + }) + expect(res.statusCode).to.equal(404) + }) + + /* DNSLink + fetching actual webui is too slow to include in the test :'-( + it('/ipns/webui.ipfs.io', async () => { + const res = await api.inject({ + method: 'GET', + url: '/ipns/webui.ipfs.io' + }) + expect(res.statusCode).to.equal(302) + expect(res.headers.location).to.exist() + }) + + it('/ipns/webui.ipfs.io/', async () => { + const res = await api.inject({ + method: 'GET', + url: '/ipns/webui.ipfs.io/' + }) + expect(res.statusCode).to.equal(200) + }) + it('/ipns/ipfs.io/', async () => { + const res = await api.inject({ + method: 'GET', + url: '/ipns/ipfs.io/' + }) + expect(res.statusCode).to.equal(404) + }) + */ + }) +}