Csrf.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. no-unused-vars,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * DS207: Consider shorter variations of null checks
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. const csurf = require('csurf')
  15. const csrf = csurf()
  16. const { promisify } = require('util')
  17. const Settings = require('@overleaf/settings')
  18. const logger = require('@overleaf/logger')
  19. // Wrapper for `csurf` middleware that provides a list of routes that can be excluded from csrf checks.
  20. //
  21. // Include with `Csrf = require('./Csrf')`
  22. //
  23. // Add the middleware to the router with:
  24. // myRouter.csrf = new Csrf()
  25. // myRouter.use webRouter.csrf.middleware
  26. // When building routes, specify a route to exclude from csrf checks with:
  27. // myRouter.csrf.disableDefaultCsrfProtection "/path" "METHOD"
  28. //
  29. // To validate the csrf token in a request to ensure that it's valid, you can use `validateRequest`, which takes a
  30. // request object and calls a callback with an error if invalid.
  31. class Csrf {
  32. constructor() {
  33. this.middleware = this.middleware.bind(this)
  34. this.excluded_routes = {}
  35. }
  36. static blockCrossOriginRequests() {
  37. return function (req, res, next) {
  38. const { origin } = req.headers
  39. // NOTE: Only cross-origin requests must have an origin header set.
  40. if (origin && !Settings.allowedOrigins.includes(origin)) {
  41. logger.warn({ req }, 'blocking cross-origin request')
  42. return res.sendStatus(403)
  43. }
  44. next()
  45. }
  46. }
  47. disableDefaultCsrfProtection(route, method) {
  48. if (!this.excluded_routes[route]) {
  49. this.excluded_routes[route] = {}
  50. }
  51. return (this.excluded_routes[route][method] = 1)
  52. }
  53. middleware(req, res, next) {
  54. // We want to call the middleware for all routes, even if excluded, because csurf sets up a csrfToken() method on
  55. // the request, to get a new csrf token for any rendered forms. For excluded routes we'll then ignore a 'bad csrf
  56. // token' error from csurf and continue on...
  57. // check whether the request method is excluded for the specified route
  58. if (
  59. (this.excluded_routes[req.path] != null
  60. ? this.excluded_routes[req.path][req.method]
  61. : undefined) === 1
  62. ) {
  63. // ignore the error if it's due to a bad csrf token, and continue
  64. return csrf(req, res, err => {
  65. if (err && err.code !== 'EBADCSRFTOKEN') {
  66. return next(err)
  67. } else {
  68. return next()
  69. }
  70. })
  71. } else {
  72. return csrf(req, res, next)
  73. }
  74. }
  75. static validateRequest(req, cb) {
  76. // run a dummy csrf check to see if it returns an error
  77. if (cb == null) {
  78. cb = function (valid) {}
  79. }
  80. return csrf(req, null, err => cb(err))
  81. }
  82. static validateToken(token, session, cb) {
  83. if (token == null) {
  84. return cb(new Error('missing token'))
  85. }
  86. // run a dummy csrf check to see if it returns an error
  87. // use this to simulate a csrf check regardless of req method, headers &c.
  88. const req = {
  89. body: {
  90. _csrf: token,
  91. },
  92. headers: {},
  93. method: 'POST',
  94. session,
  95. }
  96. return Csrf.validateRequest(req, cb)
  97. }
  98. }
  99. Csrf.promises = {
  100. validateRequest: promisify(Csrf.validateRequest),
  101. }
  102. module.exports = Csrf