javascript.express.security.injection.tainted-sql-string.tainted-sql-string

profile photo of semgrepsemgrep
Author
unknown
Download Count*

Detected user input used to manually construct a SQL string. This is usually bad practice because manual construction could accidentally result in a SQL injection. An attacker could use a SQL injection to steal or modify contents of the database. Instead, use a parameterized query which is available by default in most database engines. Alternatively, consider using an object-relational mapper (ORM) such as Sequelize which will protect your queries.

Run Locally

Run in CI

Defintion

rules:
  - id: tainted-sql-string
    message: Detected user input used to manually construct a SQL string. This is
      usually bad practice because manual construction could accidentally result
      in a SQL injection. An attacker could use a SQL injection to steal or
      modify contents of the database. Instead, use a parameterized query which
      is available by default in most database engines. Alternatively, consider
      using an object-relational mapper (ORM) such as Sequelize which will
      protect your queries.
    metadata:
      owasp:
        - A07:2017 - Cross-Site Scripting (XSS)
        - A03:2021 - Injection
      cwe:
        - "CWE-79: Improper Neutralization of Input During Web Page Generation
          ('Cross-site Scripting')"
      references:
        - https://owasp.org/www-community/attacks/SQL_Injection
      category: security
      technology:
        - express
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: HIGH
      impact: MEDIUM
      confidence: MEDIUM
      vulnerability_class:
        - Cross-Site-Scripting (XSS)
    languages:
      - javascript
      - typescript
    severity: ERROR
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern-inside: function ... ($REQ, $RES) {...}
              - pattern-inside: function ... ($REQ, $RES, $NEXT) {...}
              - 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:
              - patterns:
                  - pattern-either:
                      - pattern-inside: |
                          "$SQLSTR" + $EXPR
                      - pattern-inside: |
                          "$SQLSTR".concat($EXPR)
                      - pattern: util.format($SQLSTR, $EXPR)
                      - pattern: |
                          `$SQLSTR${$EXPR}...`
                  - metavariable-regex:
                      metavariable: $SQLSTR
                      regex: .*\b(?i)(select|delete|insert|create|update|alter|drop)\b.*
          - focus-metavariable: $EXPR

Examples

tainted-sql-string.js

const express = require('express')
const app = express()
const port = 3000
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:')
const util = require('util')

app.get('/test', (req, res) => {
  // ruleid: tainted-sql-string
  const query = "SELECT * FROM `users`" + " WHERE id = '" + req.query.message + "'"
  const [results, metadata] = await sequelize.query(query);
  res.send(results)
})

app.get('/test1', (req, res) => {
  // ruleid: tainted-sql-string
  const [results, metadata] = await sequelize.query("SELECT * FROM `users`" + " WHERE id = '" + req.query.message + "'");
  res.send(results)
})

app.get('/test2', (req, res) => {
  // ruleid: tainted-sql-string
  let query = `SELECT * FROM users WHERE id = '${req.query.message}'`
  const [results, metadata] = await sequelize.query(query);
  res.send(results)
})

app.get('/test3', (req, res) => {
  let query = "SELECT * FROM `users` WHERE id = '"
  // ruleid: tainted-sql-string
  query = query.concat(req.query.message)
  query = query.concat("'")
  const [results, metadata] = await sequelize.query(query);
  res.send(results)
})

app.get('/test4', (req, res) => {
  // ruleid: tainted-sql-string
  const query = util.format("SELECT * FROM users WHERE id = '%s'", req.query.message)
  const [results, metadata] = await sequelize.query(query);
  res.send(results)
})

app.get('/ok', async (req, res) => {
    // ok: tainted-sql-string
    res.send("message: " + req.query.message);
})

app.post('/ok2', async (req, res) => {
    // ok: tainted-sql-string
    res.send(`message: ${req.query.message}`);
})

app.post('/ok3', async (req, res) => {
    // ok: tainted-sql-string
    var data = "message: " + req.query.message;
    res.send(data);
})

app.post('/ok4', async (req, res) => {
    var data = "message: "
    // ok: tainted-sql-string
    data = data.concat(req.query.message)
    res.send(data);
})

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