javascript.express.security.audit.express-open-redirect.express-open-redirect

profile photo of semgrepsemgrep
Author
unknown
Download Count*

The application redirects to a URL specified by user-supplied input $REQ that is not validated. This could redirect users to malicious locations. Consider using an allow-list approach to validate URLs, or warn users they are being redirected to a third-party website.

Run Locally

Run in CI

Defintion

rules:
  - id: express-open-redirect
    message: The application redirects to a URL specified by user-supplied input
      `$REQ` that is not validated. This could redirect users to malicious
      locations. Consider using an allow-list approach to validate URLs, or warn
      users they are being redirected to a third-party website.
    metadata:
      technology:
        - express
      references:
        - https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html
      cwe:
        - "CWE-601: URL Redirection to Untrusted Site ('Open Redirect')"
      category: security
      owasp:
        - A01:2021 - Broken Access Control
      subcategory:
        - vuln
      likelihood: HIGH
      impact: MEDIUM
      confidence: HIGH
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Open Redirect
    languages:
      - javascript
      - typescript
    severity: WARNING
    options:
      taint_unify_mvars: true
      symbolic_propagation: true
    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.redirect("$HTTP"+$REQ. ... .$VALUE)
              - pattern: $RES.redirect("$HTTP"+$REQ. ... .$VALUE + $...A)
              - pattern: $RES.redirect(`$HTTP${$REQ. ... .$VALUE}...`)
              - pattern: $RES.redirect("$HTTP"+$REQ.$VALUE[...])
              - pattern: $RES.redirect("$HTTP"+$REQ.$VALUE[...] + $...A)
              - pattern: $RES.redirect(`$HTTP${$REQ.$VALUE[...]}...`)
          - metavariable-regex:
              metavariable: $HTTP
              regex: ^https?:\/\/$
          - pattern-either:
              - pattern: $REQ. ... .$VALUE
      - patterns:
          - pattern-either:
              - pattern: $RES.redirect($REQ. ... .$VALUE)
              - pattern: $RES.redirect($REQ. ... .$VALUE + $...A)
              - pattern: $RES.redirect(`${$REQ. ... .$VALUE}...`)
          - pattern: $REQ. ... .$VALUE
      - patterns:
          - pattern-either:
              - pattern: $RES.redirect($REQ.$VALUE['...'])
              - pattern: $RES.redirect($REQ.$VALUE['...'] + $...A)
              - pattern: $RES.redirect(`${$REQ.$VALUE['...']}...`)
          - pattern: $REQ.$VALUE
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $ASSIGN = $REQ. ... .$VALUE
                  ...
              - pattern-inside: |
                  $ASSIGN = $REQ.$VALUE['...']
                  ...
              - pattern-inside: |
                  $ASSIGN = $REQ. ... .$VALUE + $...A
                  ...
              - pattern-inside: |
                  $ASSIGN = $REQ.$VALUE['...'] + $...A
                  ...     
              - pattern-inside: |
                  $ASSIGN = `${$REQ. ... .$VALUE}...`
                  ...
              - pattern-inside: |
                  $ASSIGN = `${$REQ.$VALUE['...']}...`
                  ...                    
          - pattern-either:
              - pattern: $RES.redirect($ASSIGN)
              - pattern: $RES.redirect($ASSIGN + $...FOO)
              - pattern: $RES.redirect(`${$ASSIGN}...`)
          - focus-metavariable: $ASSIGN

Examples

express-open-redirect.js

module.exports.redirect = function (req, res) {

	// ok: express-open-redirect
	res.redirect(`https://reddit.com/${req.query.url}/fooo`)
	// ok: express-open-redirect
	res.redirect("https://google.com/"+req.query.url)
	// ok: express-open-redirect
	res.redirect(config_value.foo+req.query.url)
	// ok: express-open-redirect
	res.redirect(config_value.foo+req.body.shouldalsonotcatch)
	// ok: express-open-redirect
	res.redirect(config_value.foo+req)

	// ruleid: express-open-redirect
	res.redirect(req.body.url)
	// ruleid: express-open-redirect
	res.redirect(`${req.query.url}/fooo`)
	// ruleid: express-open-redirect
	res.redirect(req.query.url+config_value.url)

	const a = req.body.url
	const b = req.body['url']
	// ruleid: express-open-redirect
	res.redirect(a)
	// ruleid: express-open-redirect
	res.redirect(`${b}/fooo`)
	// ruleid: express-open-redirect
	res.redirect(a+config_value.url)

	// ok: express-open-redirect
	res.redirect(c+a)
	// ok: express-open-redirect
	res.redirect(`${c}${a}/fooo`)
	// ok: express-open-redirect
	res.redirect(c+a+config_value.url)

	// ok: express-open-redirect
	res.redirect(c)
	// ok: express-open-redirect
	res.redirect(`${c}`)
	// ok: express-open-redirect
	res.redirect(c+config_value.url)

	// ruleid: express-open-redirect
	res.redirect(req.body['url'])
	// ruleid: express-open-redirect
	res.redirect(`${req.body['url']}/fooo`)
	// ruleid: express-open-redirect
	res.redirect(req.body['url']+config_value.url)

	// ruleid: express-open-redirect
	res.redirect("https://"+req.body['url'])
	// ruleid: express-open-redirect
	res.redirect(`https://${req.body['url']}/fooo`)
	// ruleid: express-open-redirect
	res.redirect("https://"+req.body['url']+config_value.url)

	// todo: express-open-redirect
	res.redirect("https://google.com"+req.query.url)
}