javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal

profile photo of semgrepsemgrep
Author
4,565
Download Count*

Detected possible user input going into a path.join or path.resolve function. This could possibly lead to a path traversal vulnerability, where the attacker can access arbitrary files stored in the file system. Instead, be sure to sanitize or validate user input first.

Run Locally

Run in CI

Defintion

rules:
  - id: path-join-resolve-traversal
    message: Detected possible user input going into a `path.join` or `path.resolve`
      function. This could possibly lead to a path traversal
      vulnerability,  where the attacker can access arbitrary files stored in
      the file system. Instead, be sure to sanitize or validate user input
      first.
    metadata:
      owasp:
        - A05:2017 - Broken Access Control
        - A01:2021 - Broken Access Control
      cwe:
        - "CWE-22: Improper Limitation of a Pathname to a Restricted Directory
          ('Path Traversal')"
      category: security
      references:
        - https://owasp.org/www-community/attacks/Path_Traversal
      technology:
        - javascript
        - node.js
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: HIGH
      impact: MEDIUM
      confidence: LOW
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Path Traversal
    languages:
      - javascript
      - typescript
    severity: WARNING
    mode: taint
    pattern-sources:
      - patterns:
          - focus-metavariable: $X
          - pattern-either:
              - pattern-inside: |
                  function ... (...,$X,...) {...}
              - pattern-inside: |
                  function ... (...,{...,$X,...},...) {...}
    pattern-sinks:
      - patterns:
          - focus-metavariable: $SINK
          - pattern-either:
              - pattern-inside: |
                  $PATH = require('path');
                  ...
              - pattern-inside: |
                  import $PATH from 'path';
                  ...
          - pattern-either:
              - pattern: $PATH.join(...,$SINK,...)
              - pattern: $PATH.resolve(...,$SINK,...)
      - patterns:
          - focus-metavariable: $SINK
          - pattern-inside: |
              import 'path';
              ...
          - pattern-either:
              - pattern-inside: path.join(...,$SINK,...)
              - pattern-inside: path.resolve(...,$SINK,...)
    pattern-sanitizers:
      - pattern: $Y.replace(...)
      - pattern: $Y.indexOf(...)
      - pattern: |
          function ... (...) {
              ...
              <... $Y.indexOf(...) ...>
              ...
          }
      - patterns:
          - pattern: $FUNC(...)
          - metavariable-regex:
              metavariable: $FUNC
              regex: sanitize

Examples

path-join-resolve-traversal.js

var path = require('path');
var sanitizer = require('./util/sanitizer');

function test1() {
    function someFunc(entry) {
        // ruleid:path-join-resolve-traversal
        var extractPath = path.join(opts.path, entry.path);
        return extractFile(extractPath);
    }
    someFunc();
}

function test2() {
    function someFunc(val) {
        createFile({
            // ruleid:path-join-resolve-traversal
            filePath: path.resolve(opts.path, val)
        })
        return true
    }
    someFunc()
}

function test3(req,res) {
    let somePath = req.body.path;
    // ruleid:path-join-resolve-traversal
    return path.join(opts.path, somePath);
}

function test4(req,res) {
    let data = req.body.path;
    data.forEach((entry) => {
    // ruleid:path-join-resolve-traversal
        var pth = path.join(opts.path, entry);
        doSmth(pth);
    })
}

function okTest1(req,res) {
    let data = ['one', 'two', 'three'];
    for (let x of data) {
        // ok:path-join-resolve-traversal
        var pth = path.join(opts.path, x);
        doSmth(pth);
    }
}

function okTest2() {
    function someFunc() {
        createFile({
            // ok:path-join-resolve-traversal
            filePath: path.join(__dirname, 'val')
        })
        return true
    }
    someFunc()
}

function okTest3(req,res) {
    let somePath = req.body.path;
    somePath = somePath.replace(/^(\.\.(\/|\\|$))+/, '');
    // ok:path-join-resolve-traversal
    return path.join(opts.path, somePath);
}

function okTest4(req,res) {
    let somePath = sanitizer(req.body.path);
    // ok:path-join-resolve-traversal
    return path.join(opts.path, somePath);
}

function okTest5(req,res) {
    let somePath = req.body.path;
    // ok:path-join-resolve-traversal
    let result = path.join(opts.path, somePath);  
    if (result.indexOf(opts.path) === 0) {
        return path;
    }
    return null
}

path-join-resolve-traversal.ts

import { join, resolve } from 'path';
import sanitizer from './util/sanitizer';

function test1() {
    function someFunc(entry) {
        // ruleid:path-join-resolve-traversal
        var extractPath = join(opts.path, entry.path);
        return extractFile(extractPath);
    }
    someFunc();
}

function test2() {
    function someFunc(val) {
        createFile({
            // ruleid:path-join-resolve-traversal
            filePath: resolve(opts.path, val)
        })
        return true
    }
    someFunc()
}

function test3(req,res) {
    let somePath = req.body.path;
    // ruleid:path-join-resolve-traversal
    return join(opts.path, somePath);
}

function test4(req,res) {
    let data = req.body.path;
    data.forEach((entry) => {
    // ruleid:path-join-resolve-traversal
        var pth = join(opts.path, entry);
        doSmth(pth);
    })
}


function test5(req,res) {
    let data = req.body.path;
    for (let i = 0; i < data.length; i++) {
        // ruleid:path-join-resolve-traversal
        var pth = join(opts.path, data[i]);
        doSmth(pth);
    }
}

function okTest1(req,res) {
    let data = ['one', 'two', 'three'];
    for (let x of data) {
        // ok:path-join-resolve-traversal
        var pth = join(opts.path, x);
        doSmth(pth);
    }
}

function okTest2() {
    function someFunc() {
        createFile({
            // ok:path-join-resolve-traversal
            filePath: join(__dirname, 'val')
        })
        return true
    }
    someFunc()
}

function okTest3(req,res) {
    let somePath = req.body.path;
    somePath = somePath.replace(/^(\.\.(\/|\\|$))+/, '');
    // ok:path-join-resolve-traversal
    return join(opts.path, somePath);
}

function okTest4(req,res) {
    let somePath = sanitizer(req.body.path);
    // ok:path-join-resolve-traversal
    return join(opts.path, somePath);
}

function okTest5(req,res) {
    let somePath = req.body.path;
    // ok:path-join-resolve-traversal
    let result = join(opts.path, somePath);  
    if (result.indexOf(opts.path) === 0) {
        return path;
    }
    return null
}