javascript.express.security.audit.express-res-sendfile.express-res-sendfile

Author
unknown
Download Count*
License
The application processes user-input, this is passed to res.sendFile which can allow an attacker to arbitrarily read files on the system through path traversal. It is recommended to perform input validation in addition to canonicalizing the path. This allows you to validate the path against the intended directory it should be accessing.
Run Locally
Run in CI
Defintion
rules:
- id: express-res-sendfile
message: The application processes user-input, this is passed to res.sendFile
which can allow an attacker to arbitrarily read files on the system
through path traversal. It is recommended to perform input validation in
addition to canonicalizing the path. This allows you to validate the path
against the intended directory it should be accessing.
metadata:
references:
- https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html
technology:
- express
category: security
cwe:
- "CWE-73: External Control of File Name or Path"
owasp:
- A04:2021 - Insecure Design
subcategory:
- vuln
likelihood: HIGH
impact: MEDIUM
confidence: MEDIUM
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
vulnerability_class:
- Path Traversal
languages:
- javascript
- typescript
severity: WARNING
mode: taint
pattern-sources:
- patterns:
- pattern-either:
- pattern-inside: function ... ($REQ, $RES) {...}
- pattern-inside: function ... ($REQ, $RES, $NEXT) {...}
- patterns:
- pattern-either:
- pattern-inside: $APP.$METHOD(..., function $FUNC($REQ, $RES) {...})
- pattern-inside: $APP.$METHOD(..., function $FUNC($REQ, $RES, $NEXT) {...})
- metavariable-regex:
metavariable: $METHOD
regex: ^(get|post|put|head|delete|options)$
- pattern-either:
- pattern: $REQ.query
- pattern: $REQ.body
- pattern: $REQ.params
- pattern: $REQ.cookies
- pattern: $REQ.headers
- patterns:
- pattern-either:
- pattern-inside: |
({ $REQ }: Request,$RES: Response, $NEXT: NextFunction) =>
{...}
- pattern-inside: |
({ $REQ }: Request,$RES: Response) => {...}
- pattern-either:
- pattern: params
- pattern: query
- pattern: cookies
- pattern: headers
- pattern: body
- patterns:
- pattern-either:
- patterns:
- pattern-either:
- pattern-inside: |
function ... (...,$REQ: $TYPE, ...) {...}
- metavariable-regex:
metavariable: $TYPE
regex: ^(string|String)
pattern-sinks:
- patterns:
- pattern-either:
- pattern: $RES.$METH($QUERY,...)
- pattern-not-inside: $RES.$METH($QUERY,$OPTIONS)
- metavariable-regex:
metavariable: $METH
regex: ^(sendfile|sendFile)$
- focus-metavariable: $QUERY
Examples
express-res-sendfile.ts
import path = require('path')
import { Request, Response, NextFunction } from 'express'
module.exports = function badNormal () {
return (req: Request, res: Response, next: NextFunction) => {
const file = req.params.file
// ruleid: express-res-sendfile
res.sendFile(path.resolve('ftp/', file))
// ruleid: express-res-sendfile
res.sendFile(path.join('/ftp/', file))
// ruleid: express-res-sendfile
res.sendFile(file)
}
}
module.exports = function goodNormal () {
return (req: Request, res: Response, next: NextFunction) => {
const file = 'foo'
// ok: express-res-sendfile
res.sendFile(path.resolve('ftp/', file))
// ok: express-res-sendfile
res.sendfile(req.app.get('staticFilePath') + '/index-test.html');
// diffrent rule
// ok: express-res-sendfile
res.sendfile(req.params.foo, {root: '/'});
// ok: express-res-sendfile
res.sendfile(req.params.foo, options);
}
}
module.exports = function badWithTypes () {
return ({ params, query }: Request, res: Response, next: NextFunction) => {
const file = params.file
// ruleid: express-res-sendfile
res.sendFile(path.resolve('ftp/', file))
// ruleid: express-res-sendfile
res.sendFile(path.join('/ftp/', file))
// ruleid: express-res-sendfile
res.sendFile(file)
// diffrent rule
// ok: express-res-sendfile
res.sendfile(file, {root: '/'});
}
}
module.exports = function goodWithTypes () {
return ({ params, query, session }: Request, res: Response, next: NextFunction) => {
const file = session
// ok: express-res-sendfile
res.sendFile(path.resolve('ftp/', file))
}
}
module.exports = function advanced () {
return ({ params, query }: Request, res: Response, next: NextFunction) => {
const file = params.file
if (!file.includes('/')) {
joinModeOrDeepSemgrep(file, res, next)
}
}
function joinModeOrDeepSemgrep (file: string, res: Response, next: NextFunction) {
// ruleid: express-res-sendfile
res.sendFile(path.resolve('ftp/', file))
}
}
Short Link: https://sg.run/7DJk