javascript.express.security.audit.res-render-injection.res-render-injection

profile photo of semgrepsemgrep
Author
3,077
Download Count*

User controllable data $REQ enters $RES.render(...) this can lead to the loading of other HTML/templating pages that they may not be authorized to render. An attacker may attempt to use directory traversal techniques e.g. ../folder/index to access other HTML pages on the file system. Where possible, do not allow users to define what should be loaded in $RES.render or use an allow list for the existing application.

Run Locally

Run in CI

Defintion

rules:
  - id: res-render-injection
    message: User controllable data `$REQ` enters `$RES.render(...)` this can lead
      to the loading of other HTML/templating pages that they may not be
      authorized to render. An attacker may attempt to use directory traversal
      techniques e.g. `../folder/index` to access other HTML pages on the file
      system. Where possible, do not allow users to define what should
      be  loaded in $RES.render or use an allow list for the existing
      application.
    options:
      interfile: true
    metadata:
      interfile: true
      owasp:
        - A01:2021 - Broken Access Control
      cwe:
        - "CWE-706: Use of Incorrectly-Resolved Name or Reference"
      category: security
      technology:
        - express
      references:
        - http://expressjs.com/en/4x/api.html#res.render
      subcategory:
        - vuln
      likelihood: MEDIUM
      impact: MEDIUM
      confidence: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Improper Authorization
    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) => {...}
          - focus-metavariable: $REQ
          - pattern-either:
              - pattern: params
              - pattern: query
              - pattern: cookies
              - pattern: headers
              - pattern: body
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern: $RES.render($SINK, ...)
          - focus-metavariable: $SINK

Examples

res-render-injection.js

const express = require('express')
const app = express()
const port = 3000

const hardcodedPath = 'lib/layout'

function testController1(req, res) {
    // ruleid: res-render-injection
    return res.render(`tpl.${req.query.path}`, {foo: bar})
};

app.get('/test1', testController1)

app.get('/test2', (req, res) => {
    // ruleid: res-render-injection
    return res.render('tpl.' + req.query.path + '.smth-else', {foo: bar})
})

app.get('/ok-test', (req, res) => {
    // ok: res-render-injection
    return res.render(hardcodedPath, {foo: bar})
})

app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))