Skip to content

res.download building a wrong path when root options is set #4834

@Fischerfredl

Description

@Fischerfredl

Hello everyone!

I just ran into a bug in res.download which is also an inconsistency between res.download and res.sendFile.

The bug

res.download breaks for relative paths when specifying a root in the options argument. Express tries to access the following path: {options.root}/{process.cwd()}/{path}. Expected is {options.root}/{path}

Example

In the example below I try to access the file /dev/null. The following Error is raised when navigating to /download

Error: ENOENT: no such file or directory, stat '/dev/home/user/my-project/null'
const express = require('express')                                                                            
                                                                                                              
const app = express()                                                                                         
  .get('/', (req, res) => {                                                                                   
    res.send(`                                                                                                
<ul>                                                                                                          
  <li><a href="/sendFile">res.sendFile</a></li>                                                               
  <li><a href="/download">res.download</a></li>                                                               
</ul>                                                                                                         
`)                                                                                                            
  })                                                                                                          
  .get('/sendFile', (req, res) => {                                                                           
    res.sendFile('null', { root: '/dev' })                                                                    
  })                                                                                                          
  .get('/download', (req, res) => {                                                                           
    res.download('null', 'null', { root: '/dev' })                                                            
  })                                                                                                          
  .listen(() => {                                                                                             
    console.log(`Listening on http://localhost:${app.address().port}`)                                        
  })   

res.sendFile works as expected.

Expectation

My assumption that res.download should behave like res.sendFile is based on the following line of the documentation

The optional options argument passes through to the underlying res.sendFile() call, and takes the exact same parameters.

Cause

Problematic is the following line in res.download: /lib/response.js:575:

  // Resolve the full path for sendFile
  var fullPath = resolve(path);

The resolve method is imported from the 'path' module: https://nodejs.org/api/path.html#pathresolvepaths This is where the current working directory gets added. Anyway res.sendFile won't allow relative paths at all without a root specified. So I suggest to leave out the path.resolve call at all for a breaking release. For a non breaking fix one could only call path.resolve if root is not set in options.

Research

I have only looked briefly but have not found this fixed in the 5.x branch.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions