javascript.lang.security.detect-child-process.detect-child-process

Community Favorite
profile photo of semgrepsemgrep
Author
33,580
Download Count*

Detected calls to child_process from a function argument $FUNC. This could lead to a command injection if the input is user controllable. Try to avoid calls to child_process, and if it is needed ensure user input is correctly sanitized or sandboxed.

Run Locally

Run in CI

Defintion

rules:
  - id: detect-child-process
    message: "Detected calls to child_process from a function argument `$FUNC`. This
      could lead to a command injection if the input is user controllable. Try
      to avoid calls to child_process, and if it is needed ensure user input is
      correctly sanitized or sandboxed. "
    metadata:
      cwe:
        - "CWE-78: Improper Neutralization of Special Elements used in an OS
          Command ('OS Command Injection')"
      owasp:
        - A01:2017 - Injection
        - A03:2021 - Injection
      references:
        - https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html#do-not-use-dangerous-functions
      source-rule-url: https://github.com/nodesecurity/eslint-plugin-security/blob/master/rules/detect-child-process.js
      category: security
      technology:
        - javascript
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - audit
      likelihood: LOW
      impact: HIGH
      confidence: LOW
      vulnerability_class:
        - Command Injection
    languages:
      - javascript
      - typescript
    severity: ERROR
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-inside: |
              function ... (...,$FUNC,...) {
                ...
              }
          - focus-metavariable: $FUNC
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $CP = require('child_process')
                  ...
              - pattern-inside: |
                  import * as $CP from 'child_process'
                  ...
              - pattern-inside: |
                  import $CP from 'child_process'
                  ...
          - pattern-either:
              - pattern: $CP.exec($CMD,...)
              - pattern: $CP.execSync($CMD,...)
              - pattern: $CP.spawn($CMD,...)
              - pattern: $CP.spawnSync($CMD,...)
          - pattern-not-inside: $CP.$EXEC("...",...)
          - pattern-not-inside: $CP.$EXEC(["...",...],...)
          - pattern-not-inside: |
              $CMD = "..."
              ...
          - pattern-not-inside: |
              $CMD = ["...",...]
              ...
          - focus-metavariable: $CMD
      - patterns:
          - pattern-either:
              - pattern: child_process.exec($CMD,...)
              - pattern: child_process.execSync($CMD,...)
              - pattern: child_process.spawn($CMD,...)
              - pattern: child_process.spawnSync($CMD,...)
          - pattern-not-inside: child_process.$EXEC("...",...)
          - pattern-not-inside: child_process.$EXEC(["...",...],...)
          - pattern-not-inside: |
              $CMD = "..."
              ...
          - pattern-not-inside: |
              $CMD = ["...",...]
              ...
          - focus-metavariable: $CMD

Examples

detect-child-process.js

const {exec, spawnSync} = require('child_process');
const cp = require('child_process'); 

function a(args) {
  // ruleid:detect-child-process
  exec(`cat *.js ${args[0]}| wc -l`, (error, stdout, stderr) => {
    console.log(stdout)
  });
}

function a(userInput) {
  // ruleid:detect-child-process
  cp.spawnSync(userInput);
}

// ok:detect-child-process
exec('ls')

const parentMachine = createMachine({
  id: 'parent',
  initial: 'waiting',
  context: {
    localOne: null
  },
  states: {
    waiting: {
      entry: assign({
        // ok:detect-child-process
        localOne: () => spawn(remoteMachine)  // <--
      }),
      on: {
        'LOCAL.WAKE': {
          actions: send({ type: 'WAKE' }, { to: (context) => context.localOne })
        },
        'REMOTE.ONLINE': { target: 'connected' }
      }
    },
    connected: {}
  }
});

detect-child-process.ts

import { exec } from 'child_process'
import * as cp from 'child_process';

function a(args) {
  // ruleid:detect-child-process
  exec(`cat *.js ${args[0]}| wc -l`, (error, stdout, stderr) => {
    console.log(stdout)
  });
}

function a(userInput) {
  // ruleid:detect-child-process
  cp.spawnSync(userInput);
}

// ok:detect-child-process
exec('ls')

const parentMachine = createMachine({
  id: 'parent',
  initial: 'waiting',
  context: {
    localOne: null
  },
  states: {
    waiting: {
      entry: assign({
        // ok:detect-child-process
        localOne: () => spawn(remoteMachine)  // <--
      }),
      on: {
        'LOCAL.WAKE': {
          actions: send({ type: 'WAKE' }, { to: (context) => context.localOne })
        },
        'REMOTE.ONLINE': { target: 'connected' }
      }
    },
    connected: {}
  }
});