Repository: vapor/vapor
File: Sources/Vapor/Middleware/FileMiddleware.swift (Swift)

Code before:
        }

        // path must be relative.
        path = path.removeLeadingSlashes()

        // protect against relative paths
        guard !path.contains("../") else {
            return request.eventLoop.makeFailedFuture(Abort(.forbidden))
        }

        // create absolute path
        var absPath = self.publicDirectory + path

        // check if path exists and whether it is a directory
        var isDir: ObjCBool = false
        guard FileManager.default.fileExists(atPath: absPath, isDirectory: &isDir) else {
            return next.respond(to: request)
        }
        
        if isDir.boolValue {
            guard absPath.hasSuffix("/") else {
                switch directoryAction.kind {
                case .redirect:
                    let queryStr = request.getQueryString()
                    return request.eventLoop.future(
                        request.redirect(to: request.url.path + "/" + queryStr, redirectType: .permanent)
                    )
                case .none:
                    return next.respond(to: request)
                }
            }
            
            // If a directory, check for the default file
            guard let defaultFile = defaultFile else {
                return next.respond(to: request)
            }
            
            if defaultFile.isAbsolute() {
                absPath = self.publicDirectory + defaultFile.removeLeadingSlashes()
            } else {
                absPath = absPath + defaultFile
            }
            
            // If the default file doesn't exist, pass on request
            guard FileManager.default.fileExists(atPath: absPath) else {
                return next.respond(to: request)
            }
        }
        
        // stream the file
        let res = request.fileio.streamFile(at: absPath)

Review comment (security):
Why not redirect the entire URL (with the modified path) so as to also preserve authority parameters (username, password) and other components such as the fragment (text following a `#`)?

Code after:
        }

        // path must be relative.
        path = path.removeLeadingSlashes()

        // protect against relative paths
        guard !path.contains("../") else {
            return request.eventLoop.makeFailedFuture(Abort(.forbidden))
        }

        // create absolute path
        var absPath = self.publicDirectory + path

        // check if path exists and whether it is a directory
        var isDir: ObjCBool = false
        guard FileManager.default.fileExists(atPath: absPath, isDirectory: &isDir) else {
            return next.respond(to: request)
        }
        
        if isDir.boolValue {
            guard absPath.hasSuffix("/") else {
                switch directoryAction.kind {
                case .redirect:                    
                    var redirectUrl = request.url
                    redirectUrl.path += "/"
                    return request.eventLoop.future(
                        request.redirect(to: redirectUrl.string, redirectType: .permanent)
                    )
                case .none:
                    return next.respond(to: request)
                }
            }
            
            // If a directory, check for the default file
            guard let defaultFile = defaultFile else {
                return next.respond(to: request)
            }
            
            if defaultFile.isAbsolute() {
                absPath = self.publicDirectory + defaultFile.removeLeadingSlashes()
            } else {
                absPath = absPath + defaultFile
            }
            
            // If the default file doesn't exist, pass on request
            guard FileManager.default.fileExists(atPath: absPath) else {
                return next.respond(to: request)
            }
        }
        
        // stream the file