Skip to content

Commit

Permalink
prevent xss for 404
Browse files Browse the repository at this point in the history
  • Loading branch information
srinandan committed Dec 3, 2018
1 parent 6998b99 commit bba8cc2
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 1 deletion.
4 changes: 3 additions & 1 deletion lib/config-proxy-middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const checkNoProxy = require('./no-proxy-parser')
const proxyPathMatcher = require('./proxy-path-matcher');
const getProxyFromBasePath = proxyPathMatcher.getProxyFromBasePath;
const generateMatchingRegex = proxyPathMatcher.generateMatchingRegex;
const serialize = require('./serialize-javascript');

/**
* adds proxy to request
Expand Down Expand Up @@ -110,8 +111,9 @@ module.exports = function () {

if (!proxy) {
res.statusCode = 404; // No matching path found
res.setHeader('Content-Type', 'application/json; charset=utf-8');
debug('dropped', res.statusCode, req.method, req.url, req.headers, 'no match found for ' + reqUrl.pathname);
const nomatch = Error('no match found for ' + reqUrl.pathname);
const nomatch = Error('no match found for ' + serialize(reqUrl.pathname));
nomatch.status = res.statusCode;
return next(nomatch);
}
Expand Down
119 changes: 119 additions & 0 deletions lib/serialize-javascript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
Copyright (c) 2014, Yahoo! Inc. All rights reserved.
Copyrights licensed under the New BSD License.
See the accompanying LICENSE file for terms.
*/

'use strict';

// Generate an internal UID to make the regexp pattern harder to guess.
var UID = Math.floor(Math.random() * 0x10000000000).toString(16);
var PLACE_HOLDER_REGEXP = new RegExp('"@__(F|R|D)-' + UID + '-(\\d+)__@"', 'g');

var IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/g;
var UNSAFE_CHARS_REGEXP = /[<>\/\u2028\u2029]/g;

// Mapping of unsafe HTML and invalid JavaScript line terminator chars to their
// Unicode char counterparts which are safe to use in JavaScript strings.
var ESCAPED_CHARS = {
'<' : '\\u003C',
'>' : '\\u003E',
'/' : '\\u002F',
'\u2028': '\\u2028',
'\u2029': '\\u2029'
};

function escapeUnsafeChars(unsafeChar) {
return ESCAPED_CHARS[unsafeChar];
}

module.exports = function serialize(obj, options) {
options || (options = {});

// Backwards-compatibility for `space` as the second argument.
if (typeof options === 'number' || typeof options === 'string') {
options = {space: options};
}

var functions = [];
var regexps = [];
var dates = [];

// Returns placeholders for functions and regexps (identified by index)
// which are later replaced by their string representation.
function replacer(key, value) {
if (!value) {
return value;
}

// If the value is an object w/ a toJSON method, toJSON is called before
// the replacer runs, so we use this[key] to get the non-toJSONed value.
var origValue = this[key];
var type = typeof origValue;

if (type === 'object') {
if(origValue instanceof RegExp) {
return '@__R-' + UID + '-' + (regexps.push(origValue) - 1) + '__@';
}

if(origValue instanceof Date) {
return '@__D-' + UID + '-' + (dates.push(origValue) - 1) + '__@';
}
}

if (type === 'function') {
return '@__F-' + UID + '-' + (functions.push(origValue) - 1) + '__@';
}

return value;
}

var str;

// Creates a JSON string representation of the value.
// NOTE: Node 0.12 goes into slow mode with extra JSON.stringify() args.
if (options.isJSON && !options.space) {
str = JSON.stringify(obj);
} else {
str = JSON.stringify(obj, options.isJSON ? null : replacer, options.space);
}

// Protects against `JSON.stringify()` returning `undefined`, by serializing
// to the literal string: "undefined".
if (typeof str !== 'string') {
return String(str);
}

// Replace unsafe HTML and invalid JavaScript line terminator chars with
// their safe Unicode char counterpart. This _must_ happen before the
// regexps and functions are serialized and added back to the string.
if (options.unsafe !== true) {
str = str.replace(UNSAFE_CHARS_REGEXP, escapeUnsafeChars);
}

if (functions.length === 0 && regexps.length === 0 && dates.length === 0) {
return str;
}

// Replaces all occurrences of function, regexp and date placeholders in the
// JSON string with their string representations. If the original value can
// not be found, then `undefined` is used.
return str.replace(PLACE_HOLDER_REGEXP, function (match, type, valueIndex) {
if (type === 'D') {
return "new Date(\"" + dates[valueIndex].toISOString() + "\")";
}

if (type === 'R') {
return regexps[valueIndex].toString();
}

var fn = functions[valueIndex];
var serializedFn = fn.toString();

if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) {
throw new TypeError('Serializing native function: ' + fn.name);
}

return serializedFn;
});
}

0 comments on commit bba8cc2

Please sign in to comment.