go.gorm.security.audit.gorm-dangerous-methods-usage.gorm-dangerous-method-usage

profile photo of semgrepsemgrep
Author
unknown
Download Count*

Detected usage of dangerous method $METHOD which does not escape inputs (see link in references). If the argument is user-controlled, this can lead to SQL injection. When using $METHOD function, do not trust user-submitted data and only allow approved list of input (possibly, use an allowlist approach).

Run Locally

Run in CI

Defintion

rules:
  - id: gorm-dangerous-method-usage
    message: Detected usage of dangerous method $METHOD which does not escape inputs
      (see link in references). If the argument is user-controlled, this can
      lead to SQL injection. When using $METHOD function, do not trust
      user-submitted data and only allow approved list of input (possibly, use
      an allowlist approach).
    severity: WARNING
    languages:
      - go
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern: |
                  ($REQUEST : http.Request).$ANYTHING
              - pattern: |
                  ($REQUEST : *http.Request).$ANYTHING
          - metavariable-regex:
              metavariable: $ANYTHING
              regex: ^(BasicAuth|Body|Cookie|Cookies|Form|FormValue|GetBody|Host|MultipartReader|ParseForm|ParseMultipartForm|PostForm|PostFormValue|Referer|RequestURI|Trailer|TransferEncoding|UserAgent|URL)$
    pattern-sinks:
      - patterns:
          - pattern-inside: |
              import ("gorm.io/gorm")
              ...
          - patterns:
              - pattern-inside: |
                  func $VAL(..., $GORM *gorm.DB,... ) {
                    ...
                  }
              - pattern-either:
                  - pattern: |
                      $GORM. ... .$METHOD($VALUE)
                  - pattern: |
                      $DB := $GORM. ... .$ANYTHING(...)
                      ...
                      $DB. ... .$METHOD($VALUE)
          - focus-metavariable: $VALUE
          - metavariable-regex:
              metavariable: $METHOD
              regex: ^(Order|Exec|Raw|Group|Having|Distinct|Select|Pluck)$
    pattern-sanitizers:
      - pattern-either:
          - pattern: strconv.Atoi(...)
          - pattern: |
              ($X: bool)
    options:
      interfile: true
    metadata:
      category: security
      technology:
        - gorm
      cwe:
        - "CWE-89: Improper Neutralization of Special Elements used in an SQL
          Command ('SQL Injection')"
      owasp:
        - A01:2017 - Injection
        - A03:2021 - Injection
      references:
        - https://gorm.io/docs/security.html#SQL-injection-Methods
        - https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
      confidence: HIGH
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: HIGH
      impact: MEDIUM
      interfile: true
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - SQL Injection

Examples

gorm-dangerous-methods-usage.go

package main

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	FirstName      string
	LastName       string
	Email          string `gorm:"unique_index:user_email_index"`
	Password       string
	Token          string
	TokenExpiresAt uint
}

func testInjection(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	param := r.Cookie("foo")
	if param != "" {
		table := db.Table("users")
		var u User
		//ruleid: gorm-dangerous-method-usage
		table.Order(param).Find(&u)

	}
}

func testInjection2(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	param := r.URL.Query().Get("orderBy")
	if param != "" {
		table := db.Table("users")
		var u User
		//ruleid: gorm-dangerous-method-usage
		table.Order(param + " " + "ASC").Find(&u)
	}
}

func testNoInjection(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	table := db.Table("users")
	var u User
	//ok: gorm-dangerous-method-usage
	table.Order("email").Find(&u)
}

func testNoInjection2(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	table := db.Table("users")
	var orderBy = "email"
	var u User
	//ok: gorm-dangerous-method-usage
	table.Order(orderBy).Find(&u)
}

func testNoInjection3(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
	param := r.URL.Query().Get("orderBy")
	if param != "" {
		table := db.Table("users")
		var u User
		//ok: gorm-dangerous-method-usage
		table.Order((param != "param") + " " + "ASC").Find(&u)
	}
}

func main() {
	dsn := "dbuser:password@tcp(127.0.0.1:3306)/users?charset=utf8&parseTime=True"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
	}
	db.AutoMigrate(&User{})
	myRouter := mux.NewRouter().StrictSlash(true)
	myRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		testInjection(w, r, db)
	}).Methods("GET")
	http.ListenAndServe(":10000", myRouter)

}