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

Author
10,334
Download Count*
License
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
options:
taint_unified_mvars: true
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
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)
}
Short Link: https://sg.run/gL3y