go.gorilla.security.audit.handler-assignment-from-multiple-sources.handler-assignment-from-multiple-sources

Community Favorite
profile photo of semgrepsemgrep
Author
10,334
Download Count*

Variable $VAR is assigned from two different sources: '$Y' and '$R'. Make sure this is intended, as this could cause logic bugs if they are treated as they are the same object.

Run Locally

Run in CI

Defintion

rules:
  - id: handler-assignment-from-multiple-sources
    metadata:
      cwe:
        - "CWE-289: Authentication Bypass by Alternate Name"
      category: security
      technology:
        - gorilla
      confidence: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      references:
        - https://cwe.mitre.org/data/definitions/289.html
      subcategory:
        - audit
      impact: MEDIUM
      likelihood: LOW
      vulnerability_class:
        - Improper Authentication
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-inside: |
              func $HANDLER(..., $R *http.Request, ...) {
                ...
              }
          - focus-metavariable: $R
          - pattern-either:
              - pattern: $R.query
    pattern-sinks:
      - patterns:
          - pattern: |
              $Y, err := store.Get(...)
              ...
              $VAR := $Y.Values[...]
              ...
              $VAR = $R
          - focus-metavariable: $R
      - patterns:
          - pattern: |
              $Y, err := store.Get(...)
              ...
              var $VAR $INT = $Y.Values["..."].($INT)
              ...
              $VAR = $R
          - focus-metavariable: $R
    message: "Variable $VAR is assigned from two different sources: '$Y' and '$R'.
      Make sure this is intended, as this could cause logic bugs if they are
      treated as they are the same object."
    languages:
      - go
    severity: WARNING

Examples

handler-assignment-from-multiple-sources.go

package main
import (
    "net/http"
    "github.com/gorilla/sessions"
)

type User struct {
    user_id int
    account_id string
}


func ValidateUser(user_id int) bool {
    return true
}

func RetrieveUser(user_id int) User {
    return User{user_id, "0000"}
}

var store = sessions.NewCookieStore([]byte("blah-blah-blah"))

func MyHandler(w http.ResponseWriter, r *http.Request) {
    session, err := store.Get(r, "blah-session")
    user_id := session.Values["user_id"]

    if !ValidateUser(user_id) {
        http.Error(w, "Error", http.StatusInternalServerError)
        return
    }
    // ruleid: handler-assignment-from-multiple-sources
    user_id = r.query.params.user_id
    user_obj := RetrieveUser(user_id)
    user_obj.account_id = r.query.params.account_id
    user_obj.save()
}

func MyHandlerExplicit(w http.ResponseWriter, r *http.Request) {
    session, err := store.Get(r, "blah-session")
    var user_id int = session.Values["user_id"].(int)

    if !ValidateUser(user_id) {
        http.Error(w, "Error", http.StatusInternalServerError)
        return
    }
    // ruleid: handler-assignment-from-multiple-sources
    user_id = r.query.params.user_id
    user_obj := RetrieveUser(user_id)
    user_obj.account_id = r.query.params.account_id
    user_obj.save()
}

func augment(user_id int, augment_string string) int {
    return user_id
}

func MyHandlerOK(w http.ResponseWriter, r *http.Request) {
    // ok: handler-assignment-from-multiple-sources
    session, err := store.Get(r, "blah-session")
    user_id := session.Values["user_id"]

    if !ValidateUser(user_id) {
        http.Error(w, "Error", http.StatusInternalServerError)
        return
    }

    user_id = augment(user_id, "hello, world")
    user_obj := RetrieveUser(user_id)
    user_obj.account_id = r.query.params.account_id
    user_obj.save()
}

func (sc *http.serverConn) runHandler(rw *http.responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) {
        // ok: handler-assignment-from-multiple-sources
	didPanic := true
	defer func() {
		rw.rws.stream.cancelCtx()
		if didPanic {
			e := recover()
			sc.writeFrameFromHandler(FrameWriteRequest{
				write:  handlerPanicRST{rw.rws.stream.id},
				stream: rw.rws.stream,
			})
			// Same as net/http:
			if e != nil && e != http.ErrAbortHandler {
				const size = 64 << 10
                                // ok: handler-assignment-from-multiple-sources
				buf := make([]byte, size)
				buf = buf[:runtime.Stack(buf, false)]
				sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf)
			}
			return
		}
		rw.handlerDone()
	}()
	handler(rw, req)
	didPanic = false
}

func main() {
    http.HandleFunc("/account", MyHandler)

    http.ListenAndServe(":8080", nil)
}