From 82b0510774491b0fb381e42a46768c2959a5dd82 Mon Sep 17 00:00:00 2001 From: Dorian Niemiec Date: Wed, 28 Aug 2024 16:17:06 +0200 Subject: [PATCH] Optimize URL sanitizer --- src/utils/urlSanitizer.js | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/utils/urlSanitizer.js b/src/utils/urlSanitizer.js index 904e2de..b2ada45 100644 --- a/src/utils/urlSanitizer.js +++ b/src/utils/urlSanitizer.js @@ -1,43 +1,50 @@ // SVR.JS path sanitizer function function sanitizeURL(resource, allowDoubleSlashes) { - if (resource == "*" || resource == "") return resource; + if (resource === "*" || resource === "") return resource; + // Remove null characters resource = resource.replace(/%00|\0/g, ""); + // Check if URL is malformed (e.g. %c0%af or %u002f or simply %as) if (resource.match(/%(?:c[01]|f[ef]|(?![0-9a-f]{2}).{2}|.{0,1}$)/i)) throw new URIError("URI malformed"); + // Decode URL-encoded characters while preserving certain characters resource = resource.replace(/%([0-9a-f]{2})/gi, (match, hex) => { const decodedChar = String.fromCharCode(parseInt(hex, 16)); - return /(?!["<>^`{|}?#%])[!-~]/.test(decodedChar) ? decodedChar : "%" + hex; + return /[!$&-;=@-\]_a-z~]/.test(decodedChar) ? decodedChar : match; }); + // Encode certain characters - resource = resource.replace(/[<>^`{|}]]/g, (character) => { + resource = resource.replace(/[<>^`{|}]/g, (character) => { const charCode = character.charCodeAt(0); return ( "%" + (charCode < 16 ? "0" : "") + charCode.toString(16).toUpperCase() ); }); - let sanitizedResource = resource; + // Ensure the resource starts with a slash - if (resource[0] != "/") sanitizedResource = "/" + sanitizedResource; + if (resource[0] !== "/") resource = "/" + resource; + // Convert backslashes to slashes and handle duplicate slashes - sanitizedResource = sanitizedResource + resource = resource .replace(/\\/g, "/") .replace(allowDoubleSlashes ? /\/{3,}/g : /\/+/g, "/"); - // Handle relative navigation (e.g., "/./", "/../", "../", "./"), also remove trailing dots in paths - sanitizedResource = sanitizedResource + + // Handle relative navigation (e.g., "/./", "/../", "../", "./") and remove trailing dots in paths + resource = resource .replace(/\/\.(?:\.{2,})?(?=\/|$)/g, "") .replace(/([^./])\.+(?=\/|$)/g, "$1"); - while (sanitizedResource.match(/\/(?!\.\.\/)[^/]+\/\.\.(?=\/|$)/)) { - sanitizedResource = sanitizedResource.replace( - /\/(?!\.\.\/)[^/]+\/\.\.(?=\/|$)/g, - "", - ); + + // Remove remaining "../" + while (resource.match(/\/(?!\.\.\/)[^/]+\/\.\.(?=\/|$)/)) { + resource = resource.replace(/\/(?!\.\.\/)[^/]+\/\.\.(?=\/|$)/g, ""); } - sanitizedResource = sanitizedResource.replace(/\/\.\.(?=\/|$)/g, ""); - if (sanitizedResource.length == 0) return "/"; - else return sanitizedResource; + resource = resource.replace(/\/\.\.(?=\/|$)/g, ""); + + // If the result has length of 0, return "/", else return the sanitized URL + if (resource.length == 0) return "/"; + else return resource; } module.exports = sanitizeURL;