-
Notifications
You must be signed in to change notification settings - Fork 8.5k
[4.6] Separate xsrf handling and version checking #8152
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import expect from 'expect.js'; | ||
| import { fromNode } from 'bluebird'; | ||
| import { resolve } from 'path'; | ||
| import * as kbnTestServer from '../../../../test/utils/kbn_server'; | ||
|
|
||
| const src = resolve.bind(null, __dirname, '../../../../src'); | ||
|
|
||
| const versionHeader = 'kbn-version'; | ||
| const version = require(src('../package.json')).version; | ||
|
|
||
| describe('version_check request filter', function () { | ||
| function makeRequest(kbnServer, opts) { | ||
| return fromNode(cb => { | ||
| kbnTestServer.makeRequest(kbnServer, opts, (resp) => { | ||
| cb(null, resp); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| async function makeServer() { | ||
| const kbnServer = kbnTestServer.createServer(); | ||
|
|
||
| await kbnServer.ready(); | ||
|
|
||
| kbnServer.server.route({ | ||
| path: '/version_check/test/route', | ||
| method: 'GET', | ||
| handler: function (req, reply) { | ||
| reply(null, 'ok'); | ||
| } | ||
| }); | ||
|
|
||
| return kbnServer; | ||
| }; | ||
|
|
||
| let kbnServer; | ||
| beforeEach(async () => kbnServer = await makeServer()); | ||
| afterEach(async () => await kbnServer.close()); | ||
|
|
||
| it('accepts requests with the correct version passed in the version header', async function () { | ||
| const resp = await makeRequest(kbnServer, { | ||
| url: '/version_check/test/route', | ||
| method: 'GET', | ||
| headers: { | ||
| [versionHeader]: version, | ||
| }, | ||
| }); | ||
|
|
||
| expect(resp.statusCode).to.be(200); | ||
| expect(resp.payload).to.be('ok'); | ||
| }); | ||
|
|
||
| it('rejects requests with an incorrect version passed in the version header', async function () { | ||
| const resp = await makeRequest(kbnServer, { | ||
| url: '/version_check/test/route', | ||
| method: 'GET', | ||
| headers: { | ||
| [versionHeader]: `invalid:${version}`, | ||
| }, | ||
| }); | ||
|
|
||
| expect(resp.statusCode).to.be(400); | ||
| expect(resp.headers).to.have.property(versionHeader, version); | ||
| expect(resp.payload).to.match(/"Browser client is out of date/); | ||
| }); | ||
|
|
||
| it('accepts requests that do not include a version header', async function () { | ||
| const resp = await makeRequest(kbnServer, { | ||
| url: '/version_check/test/route', | ||
| method: 'GET' | ||
| }); | ||
|
|
||
| expect(resp.statusCode).to.be(200); | ||
| expect(resp.payload).to.be('ok'); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { badRequest } from 'boom'; | ||
|
|
||
| export default function (kbnServer, server, config) { | ||
| const versionHeader = 'kbn-version'; | ||
| const actualVersion = config.get('pkg.version'); | ||
|
|
||
| server.ext('onPostAuth', function (req, reply) { | ||
| const versionRequested = req.headers[versionHeader]; | ||
|
|
||
| if (versionRequested && versionRequested !== actualVersion) { | ||
| return reply(badRequest('Browser client is out of date, please refresh the page', { | ||
| expected: actualVersion, | ||
| got: versionRequested | ||
| })); | ||
| } | ||
|
|
||
| return reply.continue(); | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,21 +1,23 @@ | ||
| import { badRequest } from 'boom'; | ||
|
|
||
| export default function (kbnServer, server, config) { | ||
| const version = config.get('pkg.version'); | ||
| const disabled = config.get('server.xsrf.disableProtection'); | ||
| const header = 'kbn-version'; | ||
| // COMPAT: We continue to check on the kbn-version header for backwards | ||
| // compatibility since all existing consumers have been required to use it. | ||
| const versionHeader = 'kbn-version'; | ||
| const xsrfHeader = 'kbn-xsrf'; | ||
|
|
||
| server.ext('onPostAuth', function (req, reply) { | ||
| const noHeaderGet = (req.method === 'get' || req.method === 'head') && !req.headers[header]; | ||
| if (disabled || noHeaderGet) return reply.continue(); | ||
| if (disabled) { | ||
| return reply.continue(); | ||
| } | ||
|
|
||
| const isSafeMethod = req.method === 'get' || req.method === 'head'; | ||
| const hasVersionHeader = versionHeader in req.headers; | ||
|
||
| const hasXsrfHeader = xsrfHeader in req.headers; | ||
|
|
||
| const submission = req.headers[header]; | ||
| if (!submission) return reply(badRequest(`Missing ${header} header`)); | ||
| if (submission !== version) { | ||
| return reply(badRequest('Browser client is out of date, please refresh the page', { | ||
| expected: version, | ||
| got: submission | ||
| })); | ||
| if (!isSafeMethod && !hasVersionHeader && !hasXsrfHeader) { | ||
|
||
| return reply(badRequest(`Request must contain an ${xsrfHeader} header`)); | ||
| } | ||
|
|
||
| return reply.continue(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side quest: I'd love to replace the
kbnTestServer.makeRequest()function with something like this. Or update it to usekbnServer.inject()which already promisifies thekbnServer.server.inject().There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, but not in this PR