3

I have an Express Request Handler that takes a request, which includes user form input (email), makes a request to another one of my (trusted) endpoints (via newFunctionWithRequest), and then returns data from that new response (newResponse).

export const Handler = async (req: Request, res: Response, next: NextFunction) => {
  const { newResponse } = await newFunctionWithRequest(req)
  res.send(newResponse.data) // Snyk identifies this line as the problem
}

Snyk has identified an XSS vulnerability:

Unsanitized input from the HTTP request body flows into send, where it is used to render an HTML page returned to the user. This may result in a `Cross-Site Scripting attack (XSS)`

How can I fix this vulnerability?

Tyler2P
  • 2,324
  • 26
  • 22
  • 31
grabury
  • 4,797
  • 14
  • 67
  • 125

3 Answers3

0

I would recommend getting the specific elements from the request object req, and then passing them into the newFunctionWithRequest function. This means that the function is not receiving anything it isn't expecting.

For example:

export const Handler = async (req: Request, res: Response, next: NextFunction) => {
    const { email, password } = req.body;
    const { newResponse } = await newFunctionWithRequest({ email, password });
    res.send(newResponse.data);
}

You could also validate that it is the correct type, and sanitise it too if you wanted.

Another option is you could use a library like Joi to validate your incoming requests.

Josh
  • 1
  • 2
0

When I had similar requirements, I created custom middleware that you can integrate into any endpoint (or globally for every request), and it will check if any of the inputs in the request body is problematic.

It will check if:

  • The input contains some embedded HTML code (scripts of images for example).
  • First non-white character is =, which make Excel imports vulnerable.

Middleware

const containsHtml = /<[^>]*>/;
const startsWithEqual = /^\s*=/;

const isInvalidInput = (input) => {
  return containsHtml.test(input) || startsWithEqual.test(input);
}

exports.inputValidator = (req,res,next) => {
  let invalid_input_count = 0;
  const obj = req.body;
  const stack = [obj];
  while (stack?.length > 0) {
    const currentObj = stack.pop();
    Object.keys(currentObj).forEach(key => {
      if (typeof currentObj[key] === 'object' && currentObj[key] !== null) {
        stack.push(currentObj[key]);
      } else {
        if (typeof currentObj[key] === 'string' && isInvalidInput(currentObj[key])) {
          invalid_input_count++;
        }
      }
    });
  }

  if(invalid_input_count === 0) {
    return next();
  } else{
    return res.status(400).json({ success: false, error_code: 'invalid_input'});
  }
}

Usage

const express = require('express');
const { inputValidator } = require('./util/input-validator');

...

const app = express();
app.use(inputValidator); // Check every request for vulnerable inputs 
NeNaD
  • 18,172
  • 8
  • 47
  • 89
-2

DOMPurify works server-side or client-side.

I believe that is the sort of library you are looking for.

https://github.com/cure53/DOMPurify

Tony B
  • 159
  • 6
  • I believe DOMPurify expects a DOM node or a string. In my example I am passing data from a response object. – grabury Jun 26 '23 at 03:46