Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

Commit

Permalink
fix: reject requests when cors origin list is empty (#3674) (#3676)
Browse files Browse the repository at this point in the history
* fix: reject requests when cors origin list is empty (#3674)

If CORS origin list is empty, Hapi throws an error as it considers
that to be invalid configuration.

We want to reject requests that send and origin or a referer when
no allowed origins have been configured, so when these headers are
sent, reject the request if no allowed origins are present in the
config.

Co-authored-by: Vasco Santos <[email protected]>
Co-authored-by: Marcin Rataj <[email protected]>

* chore: ts-ignore until #3655 lands

Co-authored-by: Vasco Santos <[email protected]>
Co-authored-by: Marcin Rataj <[email protected]>
  • Loading branch information
3 people authored May 7, 2021
1 parent 511147b commit 3e67f0a
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 3 deletions.
1 change: 1 addition & 0 deletions packages/ipfs-grpc-client/src/utils/load-services.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const CONVERSION_OPTS = {
* @returns {object}
*/
module.exports = function loadServices () {
// @ts-ignore protobuf's generated types are incompatible with it's own types
const root = protobuf.Root.fromJSON(protocol)
const output = {}

Expand Down
1 change: 1 addition & 0 deletions packages/ipfs-grpc-server/src/utils/load-services.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const CONVERSION_OPTS = {
}

module.exports = function loadServices () {
// @ts-ignore protobuf's generated types are incompatible with it's own types
const root = protobuf.Root.fromJSON(protocol)
const output = {}

Expand Down
42 changes: 39 additions & 3 deletions packages/ipfs-http-server/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,36 @@ async function serverCreator (serverAddrs, createServer, ipfs, cors) {
return servers
}

/**
* @param {string} [str]
* @param {string[]} [allowedOrigins]
*/
function isAllowedOrigin (str, allowedOrigins = []) {
if (!str) {
return false
}

let origin

try {
origin = (new URL(str)).origin
} catch {
return false
}

for (const allowedOrigin of allowedOrigins) {
if (allowedOrigin === '*') {
return true
}

if (allowedOrigin === origin) {
return true
}
}

return false
}

class HttpApi {
constructor (ipfs, options = {}) {
this._ipfs = ipfs
Expand Down Expand Up @@ -150,11 +180,17 @@ class HttpApi {

const headers = request.headers || {}
const origin = headers.origin || ''
const referrer = headers.referrer || ''
const referer = headers.referer || ''
const userAgent = headers['user-agent'] || ''

// If these are set, we leave up to CORS and CSRF checks.
if (origin || referrer) {
// If these are set, check them against the configured list
if (origin || referer) {
if (!isAllowedOrigin(origin || referer, cors.origin)) {
// Hapi will not allow an empty CORS origin list so we have to manually
// reject the request if CORS origins have not been configured
throw Boom.forbidden()
}

return h.continue
}

Expand Down
111 changes: 111 additions & 0 deletions packages/ipfs-http-server/test/cors.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,23 @@ describe('cors', () => {
}
}, { ipfs, cors: { origin: [origin] } })

expect(res).to.have.property('statusCode', 200)
expect(res).to.have.nested.property('headers.access-control-allow-origin', origin)
})

it('allows request when referer is supplied in request', async () => {
const origin = 'http://localhost:8080'
const res = await http({
method: 'POST',
url: '/api/v0/id',
headers: {
referer: origin + '/index.html'
}
}, { ipfs, cors: { origin: [origin] } })

expect(res).to.have.property('statusCode', 200)
})

it('does not allow credentials when omitted in config', async () => {
const origin = 'http://localhost:8080'
const res = await http({
Expand Down Expand Up @@ -149,5 +163,102 @@ describe('cors', () => {

expect(res).to.have.property('statusCode', 404)
})

it('rejects requests when cors origin list is empty and origin is sent', async () => {
const origin = 'http://localhost:8080'
const res = await http({
method: 'POST',
url: '/api/v0/id',
headers: {
origin
}
}, {
ipfs,
cors: { origin: [] }
})

expect(res).to.have.property('statusCode', 403)
})

it('rejects requests when cors origin list is empty and referer is sent', async () => {
const referer = 'http://localhost:8080/index.html'
const res = await http({
method: 'POST',
url: '/api/v0/id',
headers: {
referer
}
}, {
ipfs,
cors: { origin: [] }
})

expect(res).to.have.property('statusCode', 403)
})

it('rejects requests when cors origin list is empty and referer and origin are sent', async () => {
const referer = 'http://localhost:8080/index.html'
const res = await http({
method: 'POST',
url: '/api/v0/id',
headers: {
referer,
origin: 'http://localhost:8080'
}
}, {
ipfs,
cors: { origin: [] }
})

expect(res).to.have.property('statusCode', 403)
})

it('rejects requests when cors origin list is empty and origin is sent as "null" (e.g. data urls and sandboxed iframes)', async () => {
const origin = 'null'
const res = await http({
method: 'POST',
url: '/api/v0/id',
headers: {
origin
}
}, {
ipfs,
cors: { origin: [] }
})

expect(res).to.have.property('statusCode', 403)
})

it('rejects requests when cors origin list does not contain the correct origin and origin is sent', async () => {
const origin = 'http://localhost:8080'
const res = await http({
method: 'POST',
url: '/api/v0/id',
headers: {
origin
}
}, {
ipfs,
cors: { origin: ['http://example.com:8080'] }
})

expect(res).to.have.property('statusCode', 403)
})

it('rejects requests when cors origin list does not contain the correct origin and referer is sent', async () => {
const referer = 'http://localhost:8080/index.html'
const res = await http({
method: 'POST',
url: '/api/v0/id',
headers: {
referer
}
}, {
ipfs,
cors: { origin: ['http://example.com:8080'] }
})

expect(res).to.have.property('statusCode', 403)
})
})
})

0 comments on commit 3e67f0a

Please sign in to comment.