javascript.lang.security.audit.detect-non-literal-fs-filename.detect-non-literal-fs-filename

profile photo of semgrepsemgrep
Author
745
Download Count*

Detected that function argument $ARG has entered the fs module. An attacker could potentially control the location of this file, to include going backwards in the directory with '../'. To address this, ensure that user-controlled variables in file paths are validated.

Run Locally

Run in CI

Defintion

rules:
  - id: detect-non-literal-fs-filename
    message: Detected that function argument `$ARG` has entered the fs module. An
      attacker could potentially control the location of this file, to include
      going backwards in the directory with '../'. To address this, ensure that
      user-controlled variables in file paths are validated.
    metadata:
      cwe:
        - "CWE-22: Improper Limitation of a Pathname to a Restricted Directory
          ('Path Traversal')"
      references:
        - https://owasp.org/www-community/attacks/Path_Traversal
      owasp:
        - A05:2017 - Broken Access Control
        - A01:2021 - Broken Access Control
      source-rule-url: https://github.com/nodesecurity/eslint-plugin-security/blob/master/rules/detect-non-literal-fs-filename.js
      category: security
      technology:
        - typescript
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: HIGH
      impact: MEDIUM
      confidence: LOW
      vulnerability_class:
        - Path Traversal
    languages:
      - typescript
      - javascript
    severity: WARNING
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-inside: function ... (..., $ARG,...) {...}
          - focus-metavariable: $ARG
    pattern-sinks:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  $FS = require('fs')
                  ...
              - pattern-inside: |
                  $FS = require('fs/promises')
                  ...
              - pattern-inside: |
                  import * as $FS from 'fs'
                  ...
              - pattern-inside: |
                  import $FS from 'fs'
                  ...
              - pattern-inside: |
                  import * as $FS from 'fs/promises'
                  ...
              - pattern-inside: |
                  import $FS from 'fs/promises'
                  ...
          - pattern-not: $FS. ... .$METHOD("...", ...)
          - pattern-either:
              - pattern: $FS. ... .access($FILE,...)
              - pattern: $FS. ... .appendFile($FILE,...)
              - pattern: $FS. ... .chmod($FILE,...)
              - pattern: $FS. ... .chown($FILE,...)
              - pattern: $FS. ... .close($FILE,...)
              - pattern: $FS. ... .copyFile($FILE,...)
              - pattern: $FS. ... .copyFile($SMTH, $FILE,...)
              - pattern: $FS. ... .cp($FILE, ...)
              - pattern: $FS. ... .cp($SMTH, $FILE, ...)
              - pattern: $FS. ... .createReadStream($FILE,...)
              - pattern: $FS. ... .createWriteStream($FILE,...)
              - pattern: $FS. ... .exists($FILE, ...)
              - pattern: $FS. ... .fchmod($FILE, ...)
              - pattern: $FS. ... .fchown($FILE, ...)
              - pattern: $FS. ... .fdatasync($FILE, ...)
              - pattern: $FS. ... .fstat($FILE, ...)
              - pattern: $FS. ... .fsync($FILE, ...)
              - pattern: $FS. ... .ftruncate($FILE, ...)
              - pattern: $FS. ... .futimes($FILE, ...)
              - pattern: $FS. ... .lchmod($FILE, ...)
              - pattern: $FS. ... .lchown($FILE, ...)
              - pattern: $FS. ... .lutimes($FILE, ...)
              - pattern: $FS. ... .link($FILE, ...)
              - pattern: $FS. ... .link($SMTH, $FILE, ...)
              - pattern: $FS. ... .lstat($FILE, ...)
              - pattern: $FS. ... .mkdir($FILE, ...)
              - pattern: $FS. ... .mkdtemp($FILE, ...)
              - pattern: $FS. ... .open($FILE, ...)
              - pattern: $FS. ... .opendir($FILE, ...)
              - pattern: $FS. ... .read($FILE, ...)
              - pattern: $FS. ... .read($FILE, ...)
              - pattern: $FS. ... .readdir($FILE, ...)
              - pattern: $FS. ... .readFile($FILE, ...)
              - pattern: $FS. ... .readlink($FILE, ...)
              - pattern: $FS. ... .readv($FILE, ...)
              - pattern: $FS. ... .realpath($FILE, ...)
              - pattern: $FS. ... .realpath.native($FILE, ...)
              - pattern: $FS. ... .rename($FILE, ...)
              - pattern: $FS. ... .rename($SMTH, $FILE, ...)
              - pattern: $FS. ... .rmdir($FILE, ...)
              - pattern: $FS. ... .rm($FILE, ...)
              - pattern: $FS. ... .stat($FILE, ...)
              - pattern: $FS. ... .symlink($SMTH, $FILE, ...)
              - pattern: $FS. ... .symlink($FILE, ...)
              - pattern: $FS. ... .truncate($FILE, ...)
              - pattern: $FS. ... .unlink($FILE, ...)
              - pattern: $FS. ... .unwatchFile($FILE, ...)
              - pattern: $FS. ... .utimes($FILE, ...)
              - pattern: $FS. ... .watch($FILE, ...)
              - pattern: $FS. ... .watchFile($FILE, ...)
              - pattern: $FS. ... .write($FILE, ...)
              - pattern: $FS. ... .writeFile($FILE, ...)
              - pattern: $FS. ... .writev($FILE, ...)
              - pattern: $FS. ... .accessSync($FILE, ...)
              - pattern: $FS. ... .appendFileSync($FILE, ...)
              - pattern: $FS. ... .chmodSync($FILE, ...)
              - pattern: $FS. ... .chownSync($FILE, ...)
              - pattern: $FS. ... .closeSync($FILE, ...)
              - pattern: $FS. ... .copyFileSync($FILE, ...)
              - pattern: $FS. ... .copyFileSync($SMTH, $FILE, ...)
              - pattern: $FS. ... .cpSync($FILE, ...)
              - pattern: $FS. ... .cpSync($SMTH, $FILE, ...)
              - pattern: $FS. ... .existsSync($FILE, ...)
              - pattern: $FS. ... .fchmodSync($FILE, ...)
              - pattern: $FS. ... .fchownSync($FILE, ...)
              - pattern: $FS. ... .fdatasyncSync($FILE, ...)
              - pattern: $FS. ... .fstatSync($FILE, ...)
              - pattern: $FS. ... .fsyncSync($FILE, ...)
              - pattern: $FS. ... .ftruncateSync($FILE, ...)
              - pattern: $FS. ... .futimesSync($FILE, ...)
              - pattern: $FS. ... .lchmodSync($FILE, ...)
              - pattern: $FS. ... .lchownSync($FILE, ...)
              - pattern: $FS. ... .lutimesSync($FILE, ...)
              - pattern: $FS. ... .linkSync($FILE, ...)
              - pattern: $FS. ... .linkSync($SMTH, $FILE, ...)
              - pattern: $FS. ... .lstatSync($FILE, ...)
              - pattern: $FS. ... .mkdirSync($FILE, ...)
              - pattern: $FS. ... .mkdtempSync($FILE, ...)
              - pattern: $FS. ... .opendirSync($FILE, ...)
              - pattern: $FS. ... .openSync($FILE, ...)
              - pattern: $FS. ... .readdirSync($FILE, ...)
              - pattern: $FS. ... .readFileSync($FILE, ...)
              - pattern: $FS. ... .readlinkSync($FILE, ...)
              - pattern: $FS. ... .readSync($FILE, ...)
              - pattern: $FS. ... .readSync($FILE, ...)
              - pattern: $FS. ... .readvSync($FILE, ...)
              - pattern: $FS. ... .realpathync($FILE, ...)
              - pattern: $FS. ... .realpathSync.native($FILE, ...)
              - pattern: $FS. ... .renameSync($FILE, ...)
              - pattern: $FS. ... .renameSync($SMTH, $FILE, ...)
              - pattern: $FS. ... .rmdirSync($FILE, ...)
              - pattern: $FS. ... .rmSync($FILE, ...)
              - pattern: $FS. ... .statSync($FILE, ...)
              - pattern: $FS. ... .symlinkSync($FILE, ...)
              - pattern: $FS. ... .symlinkSync($SMTH, $FILE, ...)
              - pattern: $FS. ... .truncateSync($FILE, ...)
              - pattern: $FS. ... .unlinkSync($FILE, ...)
              - pattern: $FS. ... .utimesSync($FILE, ...)
              - pattern: $FS. ... .writeFileSync($FILE, ...)
              - pattern: $FS. ... .writeSync($FILE, ...)
              - pattern: $FS. ... .writevSync($FILE, ...)
          - focus-metavariable: $FILE
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  import 'fs'
                  ...
              - pattern-inside: |
                  import 'fs/promises'
                  ...
          - pattern-not: $METHOD("...", ...)
          - pattern-either:
              - pattern: access($FILE,...)
              - pattern: appendFile($FILE,...)
              - pattern: chmod($FILE,...)
              - pattern: chown($FILE,...)
              - pattern: close($FILE,...)
              - pattern: copyFile($FILE,...)
              - pattern: copyFile($SMTH, $FILE,...)
              - pattern: cp($FILE, ...)
              - pattern: cp($SMTH, $FILE, ...)
              - pattern: createReadStream($FILE,...)
              - pattern: createWriteStream($FILE,...)
              - pattern: exists($FILE, ...)
              - pattern: fchmod($FILE, ...)
              - pattern: fchown($FILE, ...)
              - pattern: fdatasync($FILE, ...)
              - pattern: fstat($FILE, ...)
              - pattern: fsync($FILE, ...)
              - pattern: ftruncate($FILE, ...)
              - pattern: futimes($FILE, ...)
              - pattern: lchmod($FILE, ...)
              - pattern: lchown($FILE, ...)
              - pattern: lutimes($FILE, ...)
              - pattern: link($FILE, ...)
              - pattern: link($SMTH, $FILE, ...)
              - pattern: lstat($FILE, ...)
              - pattern: mkdir($FILE, ...)
              - pattern: mkdtemp($FILE, ...)
              - pattern: open($FILE, ...)
              - pattern: opendir($FILE, ...)
              - pattern: read($FILE, ...)
              - pattern: read($FILE, ...)
              - pattern: readdir($FILE, ...)
              - pattern: readFile($FILE, ...)
              - pattern: readlink($FILE, ...)
              - pattern: readv($FILE, ...)
              - pattern: realpath($FILE, ...)
              - pattern: realpath.native($FILE, ...)
              - pattern: rename($FILE, ...)
              - pattern: rename($SMTH, $FILE, ...)
              - pattern: rmdir($FILE, ...)
              - pattern: rm($FILE, ...)
              - pattern: stat($FILE, ...)
              - pattern: symlink($SMTH, $FILE, ...)
              - pattern: symlink($FILE, ...)
              - pattern: truncate($FILE, ...)
              - pattern: unlink($FILE, ...)
              - pattern: unwatchFile($FILE, ...)
              - pattern: utimes($FILE, ...)
              - pattern: watch($FILE, ...)
              - pattern: watchFile($FILE, ...)
              - pattern: write($FILE, ...)
              - pattern: writeFile($FILE, ...)
              - pattern: writev($FILE, ...)
              - pattern: accessSync($FILE, ...)
              - pattern: appendFileSync($FILE, ...)
              - pattern: chmodSync($FILE, ...)
              - pattern: chownSync($FILE, ...)
              - pattern: closeSync($FILE, ...)
              - pattern: copyFileSync($FILE, ...)
              - pattern: copyFileSync($SMTH, $FILE, ...)
              - pattern: cpSync($FILE, ...)
              - pattern: cpSync($SMTH, $FILE, ...)
              - pattern: existsSync($FILE, ...)
              - pattern: fchmodSync($FILE, ...)
              - pattern: fchownSync($FILE, ...)
              - pattern: fdatasyncSync($FILE, ...)
              - pattern: fstatSync($FILE, ...)
              - pattern: fsyncSync($FILE, ...)
              - pattern: ftruncateSync($FILE, ...)
              - pattern: futimesSync($FILE, ...)
              - pattern: lchmodSync($FILE, ...)
              - pattern: lchownSync($FILE, ...)
              - pattern: lutimesSync($FILE, ...)
              - pattern: linkSync($FILE, ...)
              - pattern: linkSync($SMTH, $FILE, ...)
              - pattern: lstatSync($FILE, ...)
              - pattern: mkdirSync($FILE, ...)
              - pattern: mkdtempSync($FILE, ...)
              - pattern: opendirSync($FILE, ...)
              - pattern: openSync($FILE, ...)
              - pattern: readdirSync($FILE, ...)
              - pattern: readFileSync($FILE, ...)
              - pattern: readlinkSync($FILE, ...)
              - pattern: readSync($FILE, ...)
              - pattern: readSync($FILE, ...)
              - pattern: readvSync($FILE, ...)
              - pattern: realpathync($FILE, ...)
              - pattern: realpathSync.native($FILE, ...)
              - pattern: renameSync($FILE, ...)
              - pattern: renameSync($SMTH, $FILE, ...)
              - pattern: rmdirSync($FILE, ...)
              - pattern: rmSync($FILE, ...)
              - pattern: statSync($FILE, ...)
              - pattern: symlinkSync($FILE, ...)
              - pattern: symlinkSync($SMTH, $FILE, ...)
              - pattern: truncateSync($FILE, ...)
              - pattern: unlinkSync($FILE, ...)
              - pattern: utimesSync($FILE, ...)
              - pattern: writeFileSync($FILE, ...)
              - pattern: writeSync($FILE, ...)
              - pattern: writevSync($FILE, ...)
          - focus-metavariable: $FILE

Examples

detect-non-literal-fs-filename.js

const {readFile} = require('fs/promises')
const fs = require('fs')

function test1(fileName) {
  // ruleid:detect-non-literal-fs-filename
  readFile(fileName)
    .then((resolve, reject) => {
      foobar()
    })
}

async function test2(fileName) {
  // ruleid:detect-non-literal-fs-filename
  const data = await fs.promises.mkdir(fileName, {})
  foobar(data)
}

function test3(fileName) {
  const data = new Uint8Array(Buffer.from('Hello Node.js'));
  // ruleid:detect-non-literal-fs-filename
  fs.writeFile(fileName, data, (err) => {
    if (err) throw err;
    console.log('The file has been saved!');
  });
}

function okTest1(data) {
  const data = new Uint8Array(Buffer.from('Hello Node.js'));
  // ok:detect-non-literal-fs-filename
  fs.writeFile('message.txt', data, (err) => {
    if (err) throw err;
    console.log('The file has been saved!');
  });
}

async function okTest2() {
  let filehandle;
  try {
    // ok:detect-non-literal-fs-filename
    filehandle = await fs.promises.open('thefile.txt', 'r');
  } finally {
    if (filehandle !== undefined)
      await filehandle.close();
  }
}

async function okTest3() {
  let filehandle;
  try {
    // ok:detect-non-literal-fs-filename
    filehandle = await this.open();
  } finally {
    if (filehandle !== undefined)
      await filehandle.close();
  }
}

detect-non-literal-fs-filename.ts

import * as fs from 'fs/promises';
import {readFile} from 'fs';

function test1(fileName) {
  // ruleid:detect-non-literal-fs-filename
  readFile(fileName)
    .then((resolve, reject) => {
      foobar()
    })
}

async function test2(fileName) {
  // ruleid:detect-non-literal-fs-filename
  const data = await fs.promises.mkdir(fileName, {})
  foobar(data)
}

function test3(fileName) {
  const data = new Uint8Array(Buffer.from('Hello Node.js'));
  // ruleid:detect-non-literal-fs-filename
  fs.writeFile(fileName, data, (err) => {
    if (err) throw err;
    console.log('The file has been saved!');
  });
}

function okTest1(data) {
  const data = new Uint8Array(Buffer.from('Hello Node.js'));
  // ok:detect-non-literal-fs-filename
  fs.writeFile('message.txt', data, (err) => {
    if (err) throw err;
    console.log('The file has been saved!');
  });
}

async function okTest2() {
  let filehandle;
  try {
    // ok:detect-non-literal-fs-filename
    filehandle = await fs.promises.open('thefile.txt', 'r');
  } finally {
    if (filehandle !== undefined)
      await filehandle.close();
  }
}

async function okTest3() {
  let filehandle;
  try {
    // ok:detect-non-literal-fs-filename
    filehandle = await this.open();
  } finally {
    if (filehandle !== undefined)
      await filehandle.close();
  }
}