ruby.rails.security.audit.avoid-tainted-ftp-call.avoid-tainted-ftp-call

profile photo of semgrepsemgrep
Author
unknown
Download Count*

Using user input when accessing files is potentially dangerous. A malicious actor could use this to modify or access files they have no right to.

Run Locally

Run in CI

Defintion

rules:
  - id: avoid-tainted-ftp-call
    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')"
      references:
        - https://github.com/presidentbeef/brakeman/blob/main/docs/warning_types/file_access/index.markdown
      category: security
      technology:
        - rails
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: HIGH
      impact: MEDIUM
      confidence: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Path Traversal
    message: Using user input when accessing files is potentially dangerous. A
      malicious actor could use this to modify or access files they have no
      right to.
    languages:
      - ruby
    severity: WARNING
    mode: taint
    pattern-sources:
      - pattern: params
      - pattern: cookies
      - pattern: request.env
    pattern-sinks:
      - pattern-either:
          - pattern: Net::FTP.$X(...)
          - patterns:
              - pattern-inside: |
                  $FTP = Net::FTP.$OPEN(...)
                  ...
                  $FTP.$METHOD(...)
              - pattern: $FTP.$METHOD(...)

Examples

avoid-tainted-ftp-call.rb

require 'net/ftp'

def foo

  host = params[:host]
  # ruleid: avoid-tainted-ftp-call
  ftp = Net::FTP.new(host)

  # ruleid: avoid-tainted-ftp-call
  ftp = Net::FTP.open(params[:host])

  ftp = Net::FTP.new()
  # ruleid: avoid-tainted-ftp-call
  ftp.connect(params[:host])

  # ruleid: avoid-tainted-ftp-call
  ftp.get("/tmp/#{params[:file]}")

  # ruleid: avoid-tainted-ftp-call
  ftp.getbinaryfile("/tmp/#{params[:file]}")

  # ruleid: avoid-tainted-ftp-call
  ftp.gettextfile("/tmp/#{params[:file]}")

  # ruleid: avoid-tainted-ftp-call
  ftp.put("/tmp/#{params[:file]}")

  # ruleid: avoid-tainted-ftp-call
  ftp.putbinaryfile("/tmp/#{params[:file]}")

  # ruleid: avoid-tainted-ftp-call
  ftp.puttextfile("/tmp/#{params[:file]}")

  # ruleid: avoid-tainted-ftp-call
  ftp.delete("/tmp/#{params[:file]}")

  # ruleid: avoid-tainted-ftp-call
  ftp.storlines(params[:cmd], "/tmp/log")

  # ruleid: avoid-tainted-ftp-call
  ftp.storbinary(params[:cmd], "/tmp/log")

  # ruleid: avoid-tainted-ftp-call
  ftp.sendcmd(params[:cmd])

  # ruleid: avoid-tainted-ftp-call
  ftp.retrlines(params[:cmd])

  # ruleid: avoid-tainted-ftp-call
  ftp.retrbinary(params[:cmd], 1024)

  # ok: avoid-tainted-ftp-call
  ftp = Net::FTP.new("example.com")

  # ok: avoid-tainted-ftp-call
  ftp = Net::FTP.open("example.com")

  ftp = Net::FTP.new()
  # ok: avoid-tainted-ftp-call
  ftp.connect("example.com")

  # ok: avoid-tainted-ftp-call
  ftp.get("/tmp/file")

  # ok: avoid-tainted-ftp-call
  ftp.getbinaryfile("/tmp/file")

  # ok: avoid-tainted-ftp-call
  ftp.gettextfile("/tmp/file")

  # ok: avoid-tainted-ftp-call
  ftp.put("/tmp/file")

  # ok: avoid-tainted-ftp-call
  ftp.putbinaryfile("/tmp/file")

  # ok: avoid-tainted-ftp-call
  ftp.puttextfile("/tmp/file")

  # ok: avoid-tainted-ftp-call
  ftp.delete("/tmp/file")

  # ok: avoid-tainted-ftp-call
  ftp.storlines("ls -al", "/tmp/log")

  # ok: avoid-tainted-ftp-call
  ftp.storbinary("ls -al", "/tmp/log")

  # ok: avoid-tainted-ftp-call
  ftp.sendcmd("ls -al")

  # ok: avoid-tainted-ftp-call
  ftp.retrlines("ls -al")

end