CVE-2020-7687 (JavaScript)

The fast-http server is vulnerable to path traversal when translating an HTTP request URL into the filesystem path passed to fs.readFile in index.js. An attacker can control the requested URL path and include parent-directory traversal components or equivalent path forms that cause the resolved filename to escape the server's configured document root. This can allow disclosure of files outside the intended web root.

Weakness:
CWE-22: name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.

Repository: https://github.com/cedced19/fast-http

Vulnerable code:
file_path: index.js
snippet:     var server = http.createServer(function (req, res) {

        var uri = url.parse(req.url).pathname,
            filename = path.join(root, uri);

        if (uri.charAt(uri.length - 1) === '/') {
            filename += 'index.html';
        }

        if (wordy) console.log('GET:' + uri + ' -> ' + filename);

        fs.exists(filename, function (exists) {
            if (!exists) {
                if (wordy) console.log('Error 404');
                res.writeHead(404, {'Content-Type': 'text/plain'});
                res.end('Error 404');
                return;
            }
            if (fs.statSync(filename).isDirectory() && uri.charAt(uri.length - 1) !== '/') {
                if (wordy) console.log('Redirection 303');
                res.writeHead(303,{ 'Location': uri + '/' });
                res.end('Redirecting to: ' + uri + '/');
                return;
            }
            fs.readFile(filename, 'binary', function(err, file) {
                if(err) {
                    if (wordy) console.log('Error 500');
                    res.writeHead(500, {'Content-Type': 'text/plain'});
                    res.end('Error 500');
                    return;
                }
                if (wordy) console.log('Ok 200');
                res.writeHead(200, {
                    'Content-Type': mime.lookup(filename) + ';' + mime.charsets.lookup(mime.lookup(filename)) 
                });
                res.write(file, 'binary');
                res.end();
            });
        });
    });

Fixed code:
file_path: index.js
snippet: module.exports = function (port, root, wordy) {
    if (wordy === undefined) wordy = false;
    if (port === undefined) port = 80;
    var server = http.createServer(function (req, res) {
        var reqUrl = req.url.replace(/(\.\.\/?)/g, '');

        var uri = url.parse(reqUrl).pathname,
            filename = path.join(root, uri);

        if (uri.charAt(uri.length - 1) === '/') {
            filename += 'index.html';
        }

        if (wordy) console.log('GET:' + uri + ' -> ' + filename);

        fs.exists(filename, function (exists) {
            if (!exists) {
                if (wordy) console.log('Error 404');
                res.writeHead(404, {'Content-Type': 'text/plain'});
                res.end('Error 404');
                return;
            }
            if (fs.statSync(filename).isDirectory() && uri.charAt(uri.length - 1) !== '/') {
                if (wordy) console.log('Redirection 303');
                res.writeHead(303,{ 'Location': uri + '/' });
                res.end('Redirecting to: ' + uri + '/');
                return;
            }
            fs.readFile(filename, 'binary', function(err, file) {
                if(err) {
                    if (wordy) console.log('Error 500');
                    res.writeHead(500, {'Content-Type': 'text/plain'});
                    res.end('Error 500');
                    return;
                }
                if (wordy) console.log('Ok 200');
                res.writeHead(200, {
                    'Content-Type': mime.lookup(filename) + ';' + mime.charsets.lookup(mime.lookup(filename)) 
                });
                res.write(file, 'binary');
                res.end();
            });
        });
    });
    console.log('The server is now launch on: http://localhost:' + port);
    server.listen(port);
    return server;
}