javascript.express.security.express-vm-injection.express-vm-compilefunction-context-injection

profile photo of returntocorpreturntocorp
Author
5,905
Download Count*

Make sure that unverified user data can not reach vm.compileFunction.

Run Locally

Run in CI

Defintion

rules:
  - id: express-vm-compilefunction-context-injection
    message: |
      Make sure that unverified user data can not reach vm.compileFunction.
    severity: ERROR
    languages:
      - javascript
      - typescript
    metadata:
      owasp: "A1: Injection"
      cwe: "CWE-94: Improper Control of Generation of Code (Code Injection)"
      category: security
      technology:
        - express
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
    patterns:
      - pattern-inside: |
          $VM = require('vm');
          ...
      - pattern-either:
          - pattern-inside: function ... ($REQ, $RES) {...}
          - pattern-inside: function ... ($REQ, $RES, $NEXT) {...}
          - pattern-inside: $APP.get(..., function $FUNC($REQ, $RES) {...})
          - pattern-inside: $APP.post(..., function $FUNC($REQ, $RES) {...})
          - pattern-inside: $APP.put(..., function $FUNC($REQ, $RES) {...})
          - pattern-inside: $APP.head(..., function $FUNC($REQ, $RES) {...})
          - pattern-inside: $APP.delete(..., function $FUNC($REQ, $RES) {...})
          - pattern-inside: $APP.options(..., function $FUNC($REQ, $RES) {...})
      - pattern-either:
          - pattern: >
              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <...
              $REQ.$QUERY.$FOO ...>},...);
          - pattern: >
              $CONTEXT = <... $REQ.$QUERY.$FOO ...>;

              ...

              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...);
          - pattern: >
              $CONTEXT = <... {$NAME:$REQ.$QUERY.$FOO} ...>;

              ...

              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...);
          - pattern: >
              $CONTEXT = {$NAME: <... $REQ.$QUERY.$FOO ...>};

              ...

              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...);
          - pattern: >
              $VAR = <... $REQ.$QUERY.$FOO ...>;

              ...

              $CONTEXT = {$NAME: <... $VAR ...>};

              ...

              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...);
          - pattern: |
              $OPTS = {parsingContext: <... $REQ.$QUERY.$FOO ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...);
          - pattern: |
              $CONTEXT = <... $REQ.$QUERY.$FOO ...>;
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...);
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$QUERY.$FOO ...>};
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...);
          - pattern: |
              $VAR = <... $REQ.$QUERY.$FOO ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...);
          - pattern: >
              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $REQ.$BODY
              ...>},...);
          - pattern: >
              $CONTEXT = <... $REQ.$BODY ...>;

              ...

              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...);
          - pattern: >
              $CONTEXT = <... {$NAME:$REQ.$BODY} ...>;

              ...

              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...);
          - pattern: >
              $CONTEXT = {$NAME: <... $REQ.$BODY ...>};

              ...

              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...);
          - pattern: >
              $VAR = <... $REQ.$BODY ...>;

              ...

              $CONTEXT = {$NAME: <... $VAR ...>};

              ...

              $VM.compileFunction($CODE,$PARAMS,{parsingContext: <... $CONTEXT ...>},...);
          - pattern: |
              $OPTS = {parsingContext: <... $REQ.$BODY ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...);
          - pattern: |
              $CONTEXT = <... $REQ.$BODY ...>;
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...);
          - pattern: |
              $CONTEXT = {$NAME: <... $REQ.$BODY ...>};
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...);
          - pattern: |
              $VAR = <... $REQ.$BODY ...>;
              ...
              $CONTEXT = {$NAME: <... $VAR ...>};
              ...
              $OPTS = {parsingContext: <... $CONTEXT ...>};
              ...
              $VM.compileFunction($CODE,$PARAMS,$OPTS,...);

Examples

express-vm-injection.js

const vm = require('vm')

let ctrl1 = function test1(req,res) {
    // ruleid:express-vm-runincontext-context-injection
    var input = req.query.something || ''
    var sandbox = {
        foo: input
    }
    vm.createContext(sandbox)
    vm.runInContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
    res.send('hello world')
}
app.get('/', ctrl1)

app.get('/', (req,res) => {
    // ruleid:express-vm-runincontext-context-injection
    var sandbox = {
        foo: req.query.userInput
    }
    vm.createContext(sandbox)
    vm.runInContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
    res.send('hello world')
})

// ok:express-vm-runincontext-context-injection
function testOk1(userInput) {
    var sandbox = {
        foo: 1
    }
    vm.createContext(sandbox)
    vm.runInContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
}

var ctrl2 = null;
ctrl2 = function test2(req,res) {
    // ruleid:express-vm-runinnewcontext-context-injection
    var input = req.query.something || ''
    var sandbox = {
        foo: input
    }
    vm.runInNewContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
    res.send('hello world')
}
app.get('/', ctrl2)


app.get('/', function (req,res) {
    // ruleid:express-vm-runinnewcontext-context-injection
    var sandbox = {
        foo: req.query.userInput
    }
    vm.runInNewContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
    res.send('hello world')
})

// ok:express-vm-runinnewcontext-context-injection
app.get('/', function testOk1(userInput) {
    var sandbox = {
        foo: 1
    }
    vm.runInNewContext('safeEval(orderLinesData)', sandbox, { timeout: 2000 })
    res.send('hello world')
})

app.get('/', function(req,res) {
    // ruleid:express-vm-code-injection
    const code = `
        var x = ${req.query.userInput};
    `
    vm.runInThisContext(code)
    res.send('hello world')
})

// ok:express-vm-code-injection
app.get('/', function okTest3(req,res) {
    const code = `
        var x = 1;
    `
    vm.runInThisContext(code)
    res.send('hello world')
})

app.get('/', function test4(req,res) {
    const parsingContext = vm.createContext({name: 'world'})
    // ruleid:express-vm-code-injection
    const code = `return 'hello ' + ${req.query.userInput}`
    let fn = vm.compileFunction(code, [], { parsingContext })
    res.send('hello world')
})

// ok:express-vm-code-injection
app.get('/', function okTest4(req,res) {
    const parsingContext = vm.createContext({name: 'world'})
    const code = `return 'hello ' + name`
    const fn = vm.compileFunction(code, [], { parsingContext })
})

app.get('/', (req,res) => {
    // ruleid:express-vm-compilefunction-context-injection
    const context = vm.createContext({name: req.query.userInput})
    let code = `return 'hello ' name`
    const fn = vm.compileFunction(code, [], { parsingContext: context })
    res.send('hello world')
})

// ok:express-vm-compilefunction-context-injection
app.get('/', function okTest5(req, res) {
    const parsingContext = vm.createContext({name: 'world'})
    const code = `return 'hello ' + name`
    const fn = vm.compileFunction(code, [], { parsingContext })
    res.send('hello world')
})

app.get('/', function (req,res) {
    // ruleid:express-vm-code-injection
    const script = new vm.Script(`
        function add(a, b) {
          return a + ${req.query.userInput};
        }

        const x = add(1, 2);
    `);

    script.runInThisContext();
    res.send('hello world')
})

//ok:express-vm-code-injection
app.get('/', function okTest6(req, res) {
    const script = new vm.Script(`
        function add(a, b) {
          return a + b;
        }

        const x = add(1, 2);
    `);

    script.runInThisContext();
    res.send('hello world')
})