From 3619230c81144c04b1da3796b43298309b9c2038 Mon Sep 17 00:00:00 2001 From: Aleksandrova Date: Mon, 8 Feb 2021 17:17:46 +0200 Subject: [PATCH 1/2] chore: add csrf token handling --- docs/usage/api.md | 3 + e2e/scenario/fixture/api.spec.js | 22 +- e2e/scenario/fixture/mock/apiServiceMock.js | 33 ++- e2e/scenario/fixture/mock/mockServer.js | 4 + package-lock.json | 250 +++++++++++++++++++- package.json | 2 + src/api/requestPlugin.js | 35 +++ 7 files changed, 345 insertions(+), 4 deletions(-) create mode 100644 e2e/scenario/fixture/mock/mockServer.js diff --git a/docs/usage/api.md b/docs/usage/api.md index 59e92021..3d8a8d5d 100644 --- a/docs/usage/api.md +++ b/docs/usage/api.md @@ -58,6 +58,9 @@ request.get(restServiceMockUrl +'/usersWithAuth') .auth('','').do(); ``` +## CSRF Tokens +// ---todo + # OData Helpers Full OData ORM is out of scope but the following samples can simplify basic OData scenarios. For better oData support, please use [TBD](). diff --git a/e2e/scenario/fixture/api.spec.js b/e2e/scenario/fixture/api.spec.js index 72ab8de3..07ab2710 100644 --- a/e2e/scenario/fixture/api.spec.js +++ b/e2e/scenario/fixture/api.spec.js @@ -77,5 +77,25 @@ describe('api', function() { var res = request.get(restServiceMockUrl +'/usersWithAuth').auth('testUser','testPass'); expect(res).toHaveHTTPBody({status: 'Authenticated'}); }); - + + it('Should set csrf token', function () { + request.post(restServiceMockUrl + '/form').send({ + field: 'value' + }).do().catch(function (err) { + expect(err.status).toBe(403); + }); + + request.csrf({ + url: restServiceMockUrl + '/form' + }).then(function () { + request.post(restServiceMockUrl + '/form').send({ + field: 'value' + }).do().then(function (res) { + expect(res.status).toBe(200); + }).catch(function (err) { + expect(true).toBeFalsy(); + }); + }); + }); + }); diff --git a/e2e/scenario/fixture/mock/apiServiceMock.js b/e2e/scenario/fixture/mock/apiServiceMock.js index baaeaaba..82bf514e 100644 --- a/e2e/scenario/fixture/mock/apiServiceMock.js +++ b/e2e/scenario/fixture/mock/apiServiceMock.js @@ -1,8 +1,13 @@ var express = require('express'); -var bodyParser = require('body-parser') +var bodyParser = require('body-parser'); +var cookieParser = require('cookie-parser'); +var csrf = require('csurf'); module.exports = function() { var app = express(); + var csrfProtection = csrf({ + cookie: true + }); var response = 1; app.get('/user/', function(req, res) { @@ -68,5 +73,31 @@ module.exports = function() { res.send({deleted: req.params.user}); }); + app.use(cookieParser()); + app.use(function (err, req, res, next) { + if (err.code === 'EBADCSRFTOKEN') { + // CSRF token error + res.status(403); + res.send('form tampered with'); + } else { + return next(); + } + }); + + app.get('/form', csrfProtection, function (req, res) { + if (req.headers['x-csrf-token'].toLowerCase() === 'fetch') { + res.set('x-csrf-token', req.csrfToken()); + res.send({ + csrfToken: req.csrfToken() + }); + } else { + res.sendStatus(200); + } + }); + + app.post('/form', csrfProtection, function (req, res) { + res.send('data is being processed') + }); + return app; }; diff --git a/e2e/scenario/fixture/mock/mockServer.js b/e2e/scenario/fixture/mock/mockServer.js new file mode 100644 index 00000000..c01f9cf3 --- /dev/null +++ b/e2e/scenario/fixture/mock/mockServer.js @@ -0,0 +1,4 @@ +var Runner = require('../../../Runner'); +var restServiceMock = require('./apiServiceMock'); + +Runner.startApp(restServiceMock); diff --git a/package-lock.json b/package-lock.json index 11b9a0f1..c753c930 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@ui5/uiveri5", - "version": "1.37.0", + "version": "1.46.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -696,6 +696,58 @@ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", "dev": true }, + "cookie-parser": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", + "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", + "dev": true, + "requires": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6" + }, + "dependencies": { + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + } + } + }, + "cookie-session": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-1.4.0.tgz", + "integrity": "sha512-0hhwD+BUIwMXQraiZP/J7VP2YFzqo6g4WqZlWHtEHQ22t0MeZZrNBSCxC1zcaLAs8ApT3BzAKizx9gW/AP9vNA==", + "dev": true, + "requires": { + "cookies": "0.8.0", + "debug": "2.6.9", + "on-headers": "~1.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + } + } + }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -707,6 +759,24 @@ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" }, + "cookies": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", + "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", + "dev": true, + "requires": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + } + } + }, "core-js": { "version": "2.3.0", "resolved": "http://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", @@ -730,12 +800,74 @@ "which": "^1.2.9" } }, + "csrf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.1.0.tgz", + "integrity": "sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==", + "dev": true, + "requires": { + "rndm": "1.2.0", + "tsscmp": "1.0.6", + "uid-safe": "2.1.5" + } + }, "cssmin": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/cssmin/-/cssmin-0.3.2.tgz", "integrity": "sha1-3c5MVHtRCuDVlKjx+/iq+OLFwA0=", "dev": true }, + "csurf": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/csurf/-/csurf-1.11.0.tgz", + "integrity": "sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==", + "dev": true, + "requires": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "csrf": "3.1.0", + "http-errors": "~1.7.3" + }, + "dependencies": { + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + } + } + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -1203,6 +1335,69 @@ } } }, + "express-session": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz", + "integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==", + "dev": true, + "requires": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.0", + "uid-safe": "~2.1.5" + }, + "dependencies": { + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2149,6 +2344,15 @@ } } }, + "keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "dev": true, + "requires": { + "tsscmp": "1.0.6" + } + }, "kuler": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", @@ -2245,6 +2449,15 @@ "signal-exit": "^3.0.0" } }, + "lusca": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/lusca/-/lusca-1.6.1.tgz", + "integrity": "sha512-+JzvUMH/rsE/4XfHdDOl70bip0beRcHSviYATQM0vtls59uVtdn1JMu4iD7ZShBpAmFG8EnaA+PrYG9sECMIOQ==", + "dev": true, + "requires": { + "tsscmp": "^1.0.5" + } + }, "makeerror": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", @@ -2892,6 +3105,12 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", + "dev": true + }, "range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -3070,6 +3289,12 @@ } } }, + "rndm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz", + "integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w=", + "dev": true + }, "run-async": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", @@ -3669,6 +3894,12 @@ "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", "dev": true }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -3703,6 +3934,12 @@ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "dev": true }, + "tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "dev": true + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -3753,6 +3990,15 @@ "integrity": "sha1-S1v/+Rhu/7qoiOTJ6UvZ/EyUkp0=", "dev": true }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dev": true, + "requires": { + "random-bytes": "~1.0.0" + } + }, "ultron": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", @@ -4082,4 +4328,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 3ca46d17..280fba95 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "yargs": "^3.8.0" }, "devDependencies": { + "cookie-parser": "^1.4.5", + "csurf": "^1.11.0", "eslint": "^5.2.0", "express": "^4.13.4", "grunt": "^1.0.2", diff --git a/src/api/requestPlugin.js b/src/api/requestPlugin.js index 659dc43a..d005f1ac 100644 --- a/src/api/requestPlugin.js +++ b/src/api/requestPlugin.js @@ -1,10 +1,13 @@ var superagent = require('superagent'); +var logger = require('../logger')(3); +var CSRF_HEADER = 'x-csrf-token'; function RequestPlugin() { } RequestPlugin.prototype.setup = function() { + var that = this; var controlFlow = browser.controlFlow(); var flow = function(superagent) { @@ -16,7 +19,39 @@ RequestPlugin.prototype.setup = function() { }; }; + var csrf = function (options) { + options = options || {}; + if (options.token) { + that.csrfToken = options.token; + } else if (options.url) { + return controlFlow.execute(function () { + return this.get(options.url) + .set(CSRF_HEADER, 'Fetch') + .do() + .then(function (res) { + if (res.headers[CSRF_HEADER]) { + that.csrfToken = res.headers[CSRF_HEADER]; + } else { + logger.error('Cannot generate CSRF token: missing X-CSRF-Token header'); + } + }).catch(function (err) { + logger.error('Error in generating CSRF token. Details: ' + err); + }); + }.bind(this)); + } + }; + global.request = superagent.agent().use(flow); + + global.request.csrf = csrf; + + var originalPost = global.request.post; + global.request.post = function () { + if (that.csrfToken) { + this.set(CSRF_HEADER, that.csrfToken); + } + return originalPost.apply(this, arguments); + }; }; module.exports = function () { From 6fbdf644c2ed042d461da76f7eb9065ae8144093 Mon Sep 17 00:00:00 2001 From: tsaleksandrova Date: Tue, 2 Mar 2021 11:24:55 +0200 Subject: [PATCH 2/2] refactor as suggested in code review --- e2e/scenario/api.spec.js | 2 +- e2e/scenario/fixture/api.spec.js | 18 +- e2e/scenario/fixture/mock/apiServiceMock.js | 6 +- package-lock.json | 250 +------------------- src/api/csrfAuthenticator.js | 39 +++ src/api/requestPlugin.js | 48 +--- 6 files changed, 67 insertions(+), 296 deletions(-) create mode 100644 src/api/csrfAuthenticator.js diff --git a/e2e/scenario/api.spec.js b/e2e/scenario/api.spec.js index 1bf1daca..2f4d4678 100644 --- a/e2e/scenario/api.spec.js +++ b/e2e/scenario/api.spec.js @@ -10,7 +10,7 @@ describe('API tests', function() { appMock = app; }) }); - + afterAll(() => { appMock.server.close(); }); diff --git a/e2e/scenario/fixture/api.spec.js b/e2e/scenario/fixture/api.spec.js index 07ab2710..1d1588bb 100644 --- a/e2e/scenario/fixture/api.spec.js +++ b/e2e/scenario/fixture/api.spec.js @@ -85,16 +85,14 @@ describe('api', function() { expect(err.status).toBe(403); }); - request.csrf({ - url: restServiceMockUrl + '/form' - }).then(function () { - request.post(restServiceMockUrl + '/form').send({ - field: 'value' - }).do().then(function (res) { - expect(res.status).toBe(200); - }).catch(function (err) { - expect(true).toBeFalsy(); - }); + request.authenticate(new CsrfAuthenticator({ + csrfFetchUrl: restServiceMockUrl + '/form' + })); + + request.post(restServiceMockUrl + '/form').send({ + field: 'value' + }).do().then(function (res) { + expect(res.status).toBe(200); }); }); diff --git a/e2e/scenario/fixture/mock/apiServiceMock.js b/e2e/scenario/fixture/mock/apiServiceMock.js index 82bf514e..2fdf35e7 100644 --- a/e2e/scenario/fixture/mock/apiServiceMock.js +++ b/e2e/scenario/fixture/mock/apiServiceMock.js @@ -5,6 +5,7 @@ var csrf = require('csurf'); module.exports = function() { var app = express(); + // will also use a _csrf cookie (secret) and validate against it var csrfProtection = csrf({ cookie: true }); @@ -86,9 +87,10 @@ module.exports = function() { app.get('/form', csrfProtection, function (req, res) { if (req.headers['x-csrf-token'].toLowerCase() === 'fetch') { - res.set('x-csrf-token', req.csrfToken()); + var csrfToken = req.csrfToken(); + res.set('x-csrf-token', csrfToken); res.send({ - csrfToken: req.csrfToken() + csrfToken: csrfToken }); } else { res.sendStatus(200); diff --git a/package-lock.json b/package-lock.json index c753c930..11b9a0f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@ui5/uiveri5", - "version": "1.46.1", + "version": "1.37.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -696,58 +696,6 @@ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", "dev": true }, - "cookie-parser": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", - "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", - "dev": true, - "requires": { - "cookie": "0.4.0", - "cookie-signature": "1.0.6" - }, - "dependencies": { - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true - } - } - }, - "cookie-session": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-1.4.0.tgz", - "integrity": "sha512-0hhwD+BUIwMXQraiZP/J7VP2YFzqo6g4WqZlWHtEHQ22t0MeZZrNBSCxC1zcaLAs8ApT3BzAKizx9gW/AP9vNA==", - "dev": true, - "requires": { - "cookies": "0.8.0", - "debug": "2.6.9", - "on-headers": "~1.0.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true - } - } - }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -759,24 +707,6 @@ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" }, - "cookies": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", - "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", - "dev": true, - "requires": { - "depd": "~2.0.0", - "keygrip": "~1.1.0" - }, - "dependencies": { - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - } - } - }, "core-js": { "version": "2.3.0", "resolved": "http://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", @@ -800,74 +730,12 @@ "which": "^1.2.9" } }, - "csrf": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.1.0.tgz", - "integrity": "sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==", - "dev": true, - "requires": { - "rndm": "1.2.0", - "tsscmp": "1.0.6", - "uid-safe": "2.1.5" - } - }, "cssmin": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/cssmin/-/cssmin-0.3.2.tgz", "integrity": "sha1-3c5MVHtRCuDVlKjx+/iq+OLFwA0=", "dev": true }, - "csurf": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/csurf/-/csurf-1.11.0.tgz", - "integrity": "sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==", - "dev": true, - "requires": { - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "csrf": "3.1.0", - "http-errors": "~1.7.3" - }, - "dependencies": { - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true - }, - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - } - } - }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -1335,69 +1203,6 @@ } } }, - "express-session": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz", - "integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==", - "dev": true, - "requires": { - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.0", - "uid-safe": "~2.1.5" - }, - "dependencies": { - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true - } - } - }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2344,15 +2149,6 @@ } } }, - "keygrip": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", - "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", - "dev": true, - "requires": { - "tsscmp": "1.0.6" - } - }, "kuler": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", @@ -2449,15 +2245,6 @@ "signal-exit": "^3.0.0" } }, - "lusca": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/lusca/-/lusca-1.6.1.tgz", - "integrity": "sha512-+JzvUMH/rsE/4XfHdDOl70bip0beRcHSviYATQM0vtls59uVtdn1JMu4iD7ZShBpAmFG8EnaA+PrYG9sECMIOQ==", - "dev": true, - "requires": { - "tsscmp": "^1.0.5" - } - }, "makeerror": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", @@ -3105,12 +2892,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, - "random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", - "dev": true - }, "range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -3289,12 +3070,6 @@ } } }, - "rndm": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz", - "integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w=", - "dev": true - }, "run-async": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", @@ -3894,12 +3669,6 @@ "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", "dev": true }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "dev": true - }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -3934,12 +3703,6 @@ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", "dev": true }, - "tsscmp": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", - "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", - "dev": true - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -3990,15 +3753,6 @@ "integrity": "sha1-S1v/+Rhu/7qoiOTJ6UvZ/EyUkp0=", "dev": true }, - "uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "dev": true, - "requires": { - "random-bytes": "~1.0.0" - } - }, "ultron": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", @@ -4328,4 +4082,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/api/csrfAuthenticator.js b/src/api/csrfAuthenticator.js new file mode 100644 index 00000000..d2c40389 --- /dev/null +++ b/src/api/csrfAuthenticator.js @@ -0,0 +1,39 @@ +var logger = require('../logger')(3); + +var CSRF_HEADER = 'x-csrf-token'; + +function CsrfAuthenticator(options){ + options = options || {}; + this.user = options.user; + this.pass = options.pass; + this.csrfHeader = options.csrfHeader; + this.csrfFetchUrl = options.csrfFetchUrl; +} + +CsrfAuthenticator.prototype.authenticate = function () { + var that = this; + if (this.csrfFetchUrl) { + return request.get(this.csrfFetchUrl) + .set(CSRF_HEADER, 'Fetch') + .do() + .then(function (res) { + if (res.headers[CSRF_HEADER]) { + that.csrfHeader = res.headers[CSRF_HEADER]; + } else { + logger.error('Cannot generate CSRF token: missing X-CSRF-Token header'); + } + }).catch(function (err) { + logger.error('Error in generating CSRF token. Details: ' + err); + }); + } +}; + +CsrfAuthenticator.prototype.modifyCall = function (req) { + if (this.csrfHeader) { + req.set(CSRF_HEADER, this.csrfHeader); + } + // superagent will add the cookies e.g. _csrf cookie + return req; +}; + +module.exports = CsrfAuthenticator; diff --git a/src/api/requestPlugin.js b/src/api/requestPlugin.js index d005f1ac..93833476 100644 --- a/src/api/requestPlugin.js +++ b/src/api/requestPlugin.js @@ -1,13 +1,10 @@ var superagent = require('superagent'); -var logger = require('../logger')(3); - -var CSRF_HEADER = 'x-csrf-token'; +var CsrfAuthenticator = require('./csrfAuthenticator'); function RequestPlugin() { } RequestPlugin.prototype.setup = function() { - var that = this; var controlFlow = browser.controlFlow(); var flow = function(superagent) { @@ -19,39 +16,20 @@ RequestPlugin.prototype.setup = function() { }; }; - var csrf = function (options) { - options = options || {}; - if (options.token) { - that.csrfToken = options.token; - } else if (options.url) { - return controlFlow.execute(function () { - return this.get(options.url) - .set(CSRF_HEADER, 'Fetch') - .do() - .then(function (res) { - if (res.headers[CSRF_HEADER]) { - that.csrfToken = res.headers[CSRF_HEADER]; - } else { - logger.error('Cannot generate CSRF token: missing X-CSRF-Token header'); - } - }).catch(function (err) { - logger.error('Error in generating CSRF token. Details: ' + err); - }); - }.bind(this)); - } - }; + var request = superagent.agent().use(flow); - global.request = superagent.agent().use(flow); - - global.request.csrf = csrf; - - var originalPost = global.request.post; - global.request.post = function () { - if (that.csrfToken) { - this.set(CSRF_HEADER, that.csrfToken); - } - return originalPost.apply(this, arguments); + request.authenticate = function (authenticator) { + var originalPost = request.post; + request.post = function () { + authenticator.modifyCall(this); + return originalPost.apply(this, arguments); + }; + + return authenticator.authenticate(); }; + + global.request = request; + global.CsrfAuthenticator = CsrfAuthenticator; }; module.exports = function () {