Merge pull request #528 from sauyon/tuple-map-read

This commit is contained in:
Sauyon Lee
2021-04-21 08:50:40 -07:00
committed by GitHub
10 changed files with 134 additions and 80 deletions

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* Fixed a bug where data flow was not correctly computed through two-value index expressions (for example, `got, ok := myMap[someIndex]`). This may lead to extra results from any dataflow query when an index expression would form part of an important dataflow path.

View File

@@ -747,8 +747,8 @@ module IR {
result = c.getTarget().getResultType(i)
)
or
exists(TypeAssertExpr tae | this.getBase() = evalExprInstruction(tae) |
result = tae.getType().(TupleType).getComponentType(pragma[only_bind_into](i))
exists(Expr e | this.getBase() = evalExprInstruction(e) |
result = e.getType().(TupleType).getComponentType(pragma[only_bind_into](i))
)
or
exists(Type rangeType | rangeType = s.(RangeStmt).getDomain().getType().getUnderlyingType() |

View File

@@ -0,0 +1,6 @@
| test.go:9:2:9:16 | ... := ...[0] | bool |
| test.go:9:2:9:16 | ... := ...[1] | bool |
| test.go:15:2:15:20 | ... := ...[0] | string |
| test.go:15:2:15:20 | ... := ...[1] | bool |
| test.go:21:2:21:22 | ... := ...[0] | string |
| test.go:21:2:21:22 | ... := ...[1] | bool |

View File

@@ -0,0 +1,23 @@
package test
import (
"fmt"
)
func testChannel() {
var ch chan bool
got, ok := <-ch
fmt.Printf("%v %v", got, ok)
}
func testMap() {
var m map[string]string
got, ok := m["key"]
fmt.Printf("%v %v", got, ok)
}
func testTypeAssert() {
var i interface{}
got, ok := i.(string)
fmt.Printf("%v %v", got, ok)
}

View File

@@ -0,0 +1,4 @@
import go
from IR::ExtractTupleElementInstruction extract
select extract, extract.getResultType()

View File

@@ -105,6 +105,9 @@
| main.go:48:3:48:11 | ... += ... | main.go:48:3:48:6 | definition of vals |
| main.go:49:3:49:6 | definition of keys | main.go:47:20:47:20 | keys = phi(def@46:24, def@49:3) |
| main.go:49:3:49:11 | ... += ... | main.go:49:3:49:6 | definition of keys |
| main.go:55:6:55:7 | definition of ch | main.go:56:2:56:3 | ch |
| main.go:55:6:55:7 | definition of ch | main.go:57:4:57:5 | ch |
| main.go:55:6:55:7 | zero value for ch | main.go:55:6:55:7 | definition of ch |
| strings.go:8:12:8:12 | argument corresponding to s | strings.go:8:12:8:12 | definition of s |
| strings.go:8:12:8:12 | definition of s | strings.go:9:24:9:24 | s |
| strings.go:8:12:8:12 | definition of s | strings.go:10:27:10:27 | s |

View File

@@ -50,3 +50,9 @@ func test10(xs []int) (keys int, vals int) {
}
return
}
func testch() {
var ch chan bool
ch <- true
<-ch
}

View File

@@ -1,19 +1,20 @@
edges
| ReflectedXss.go:13:15:13:20 | selection of Form : Values | ReflectedXss.go:16:44:16:51 | username |
| ReflectedXss.go:48:2:48:38 | ... := ...[0] : pointer type | ReflectedXss.go:49:10:49:57 | type conversion |
| ReflectedXss.go:52:2:52:44 | ... := ...[0] : File | ReflectedXss.go:54:10:54:57 | type conversion |
| ReflectedXss.go:52:2:52:44 | ... := ...[1] : pointer type | ReflectedXss.go:55:10:55:62 | type conversion |
| ReflectedXss.go:52:2:52:44 | ... := ...[1] : pointer type | ReflectedXss.go:55:46:55:51 | implicit dereference : FileHeader |
| ReflectedXss.go:55:46:55:51 | implicit dereference : FileHeader | ReflectedXss.go:55:10:55:62 | type conversion |
| ReflectedXss.go:55:46:55:51 | implicit dereference : FileHeader | ReflectedXss.go:55:46:55:51 | implicit dereference : FileHeader |
| ReflectedXss.go:59:2:59:35 | ... := ...[0] : pointer type | ReflectedXss.go:65:10:65:55 | type conversion |
| ReflectedXss.go:59:2:59:35 | ... := ...[0] : pointer type | ReflectedXss.go:66:10:66:18 | byteSlice |
| ReflectedXss.go:11:15:11:20 | selection of Form : Values | ReflectedXss.go:14:44:14:51 | username |
| contenttype.go:11:11:11:16 | selection of Form : Values | contenttype.go:17:11:17:22 | type conversion |
| contenttype.go:49:11:49:16 | selection of Form : Values | contenttype.go:53:34:53:37 | data |
| contenttype.go:63:10:63:28 | call to FormValue : string | contenttype.go:64:52:64:55 | data |
| contenttype.go:73:10:73:28 | call to FormValue : string | contenttype.go:79:11:79:14 | data |
| contenttype.go:88:10:88:28 | call to FormValue : string | contenttype.go:91:4:91:7 | data |
| contenttype.go:113:10:113:28 | call to FormValue : string | contenttype.go:114:50:114:53 | data |
| reflectedxsstest.go:27:2:27:38 | ... := ...[0] : pointer type | reflectedxsstest.go:28:10:28:57 | type conversion |
| reflectedxsstest.go:31:2:31:44 | ... := ...[0] : File | reflectedxsstest.go:33:10:33:57 | type conversion |
| reflectedxsstest.go:31:2:31:44 | ... := ...[1] : pointer type | reflectedxsstest.go:34:10:34:62 | type conversion |
| reflectedxsstest.go:31:2:31:44 | ... := ...[1] : pointer type | reflectedxsstest.go:34:46:34:51 | implicit dereference : FileHeader |
| reflectedxsstest.go:34:46:34:51 | implicit dereference : FileHeader | reflectedxsstest.go:34:10:34:62 | type conversion |
| reflectedxsstest.go:34:46:34:51 | implicit dereference : FileHeader | reflectedxsstest.go:34:46:34:51 | implicit dereference : FileHeader |
| reflectedxsstest.go:38:2:38:35 | ... := ...[0] : pointer type | reflectedxsstest.go:44:10:44:55 | type conversion |
| reflectedxsstest.go:38:2:38:35 | ... := ...[0] : pointer type | reflectedxsstest.go:45:10:45:18 | byteSlice |
| reflectedxsstest.go:51:14:51:18 | selection of URL : pointer type | reflectedxsstest.go:54:11:54:21 | type conversion |
| tst.go:14:15:14:20 | selection of Form : Values | tst.go:18:12:18:39 | type conversion |
| tst.go:48:14:48:19 | selection of Form : Values | tst.go:53:12:53:26 | type conversion |
| websocketXss.go:30:7:30:10 | definition of xnet : slice type | websocketXss.go:32:24:32:27 | xnet |
@@ -23,18 +24,8 @@ edges
| websocketXss.go:50:3:50:10 | definition of gorilla2 : slice type | websocketXss.go:52:24:52:31 | gorilla2 |
| websocketXss.go:54:3:54:38 | ... := ...[1] : slice type | websocketXss.go:55:24:55:31 | gorilla3 |
nodes
| ReflectedXss.go:13:15:13:20 | selection of Form : Values | semmle.label | selection of Form : Values |
| ReflectedXss.go:16:44:16:51 | username | semmle.label | username |
| ReflectedXss.go:48:2:48:38 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
| ReflectedXss.go:49:10:49:57 | type conversion | semmle.label | type conversion |
| ReflectedXss.go:52:2:52:44 | ... := ...[0] : File | semmle.label | ... := ...[0] : File |
| ReflectedXss.go:52:2:52:44 | ... := ...[1] : pointer type | semmle.label | ... := ...[1] : pointer type |
| ReflectedXss.go:54:10:54:57 | type conversion | semmle.label | type conversion |
| ReflectedXss.go:55:10:55:62 | type conversion | semmle.label | type conversion |
| ReflectedXss.go:55:46:55:51 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
| ReflectedXss.go:59:2:59:35 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
| ReflectedXss.go:65:10:65:55 | type conversion | semmle.label | type conversion |
| ReflectedXss.go:66:10:66:18 | byteSlice | semmle.label | byteSlice |
| ReflectedXss.go:11:15:11:20 | selection of Form : Values | semmle.label | selection of Form : Values |
| ReflectedXss.go:14:44:14:51 | username | semmle.label | username |
| contenttype.go:11:11:11:16 | selection of Form : Values | semmle.label | selection of Form : Values |
| contenttype.go:17:11:17:22 | type conversion | semmle.label | type conversion |
| contenttype.go:49:11:49:16 | selection of Form : Values | semmle.label | selection of Form : Values |
@@ -47,6 +38,18 @@ nodes
| contenttype.go:91:4:91:7 | data | semmle.label | data |
| contenttype.go:113:10:113:28 | call to FormValue : string | semmle.label | call to FormValue : string |
| contenttype.go:114:50:114:53 | data | semmle.label | data |
| reflectedxsstest.go:27:2:27:38 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
| reflectedxsstest.go:28:10:28:57 | type conversion | semmle.label | type conversion |
| reflectedxsstest.go:31:2:31:44 | ... := ...[0] : File | semmle.label | ... := ...[0] : File |
| reflectedxsstest.go:31:2:31:44 | ... := ...[1] : pointer type | semmle.label | ... := ...[1] : pointer type |
| reflectedxsstest.go:33:10:33:57 | type conversion | semmle.label | type conversion |
| reflectedxsstest.go:34:10:34:62 | type conversion | semmle.label | type conversion |
| reflectedxsstest.go:34:46:34:51 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
| reflectedxsstest.go:38:2:38:35 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
| reflectedxsstest.go:44:10:44:55 | type conversion | semmle.label | type conversion |
| reflectedxsstest.go:45:10:45:18 | byteSlice | semmle.label | byteSlice |
| reflectedxsstest.go:51:14:51:18 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
| reflectedxsstest.go:54:11:54:21 | type conversion | semmle.label | type conversion |
| tst.go:14:15:14:20 | selection of Form : Values | semmle.label | selection of Form : Values |
| tst.go:18:12:18:39 | type conversion | semmle.label | type conversion |
| tst.go:48:14:48:19 | selection of Form : Values | semmle.label | selection of Form : Values |
@@ -64,18 +67,19 @@ nodes
| websocketXss.go:54:3:54:38 | ... := ...[1] : slice type | semmle.label | ... := ...[1] : slice type |
| websocketXss.go:55:24:55:31 | gorilla3 | semmle.label | gorilla3 |
#select
| ReflectedXss.go:16:44:16:51 | username | ReflectedXss.go:13:15:13:20 | selection of Form : Values | ReflectedXss.go:16:44:16:51 | username | Cross-site scripting vulnerability due to $@. | ReflectedXss.go:13:15:13:20 | selection of Form | user-provided value | ReflectedXss.go:0:0:0:0 | ReflectedXss.go | |
| ReflectedXss.go:49:10:49:57 | type conversion | ReflectedXss.go:48:2:48:38 | ... := ...[0] : pointer type | ReflectedXss.go:49:10:49:57 | type conversion | Cross-site scripting vulnerability due to $@. | ReflectedXss.go:48:2:48:38 | ... := ...[0] | user-provided value | ReflectedXss.go:0:0:0:0 | ReflectedXss.go | |
| ReflectedXss.go:54:10:54:57 | type conversion | ReflectedXss.go:52:2:52:44 | ... := ...[0] : File | ReflectedXss.go:54:10:54:57 | type conversion | Cross-site scripting vulnerability due to $@. | ReflectedXss.go:52:2:52:44 | ... := ...[0] | user-provided value | ReflectedXss.go:0:0:0:0 | ReflectedXss.go | |
| ReflectedXss.go:55:10:55:62 | type conversion | ReflectedXss.go:52:2:52:44 | ... := ...[1] : pointer type | ReflectedXss.go:55:10:55:62 | type conversion | Cross-site scripting vulnerability due to $@. | ReflectedXss.go:52:2:52:44 | ... := ...[1] | user-provided value | ReflectedXss.go:0:0:0:0 | ReflectedXss.go | |
| ReflectedXss.go:65:10:65:55 | type conversion | ReflectedXss.go:59:2:59:35 | ... := ...[0] : pointer type | ReflectedXss.go:65:10:65:55 | type conversion | Cross-site scripting vulnerability due to $@. | ReflectedXss.go:59:2:59:35 | ... := ...[0] | user-provided value | ReflectedXss.go:0:0:0:0 | ReflectedXss.go | |
| ReflectedXss.go:66:10:66:18 | byteSlice | ReflectedXss.go:59:2:59:35 | ... := ...[0] : pointer type | ReflectedXss.go:66:10:66:18 | byteSlice | Cross-site scripting vulnerability due to $@. | ReflectedXss.go:59:2:59:35 | ... := ...[0] | user-provided value | ReflectedXss.go:0:0:0:0 | ReflectedXss.go | |
| ReflectedXss.go:14:44:14:51 | username | ReflectedXss.go:11:15:11:20 | selection of Form : Values | ReflectedXss.go:14:44:14:51 | username | Cross-site scripting vulnerability due to $@. | ReflectedXss.go:11:15:11:20 | selection of Form | user-provided value | ReflectedXss.go:0:0:0:0 | ReflectedXss.go | |
| contenttype.go:17:11:17:22 | type conversion | contenttype.go:11:11:11:16 | selection of Form : Values | contenttype.go:17:11:17:22 | type conversion | Cross-site scripting vulnerability due to $@. | contenttype.go:11:11:11:16 | selection of Form | user-provided value | contenttype.go:0:0:0:0 | contenttype.go | |
| contenttype.go:53:34:53:37 | data | contenttype.go:49:11:49:16 | selection of Form : Values | contenttype.go:53:34:53:37 | data | Cross-site scripting vulnerability due to $@. | contenttype.go:49:11:49:16 | selection of Form | user-provided value | contenttype.go:0:0:0:0 | contenttype.go | |
| contenttype.go:64:52:64:55 | data | contenttype.go:63:10:63:28 | call to FormValue : string | contenttype.go:64:52:64:55 | data | Cross-site scripting vulnerability due to $@. | contenttype.go:63:10:63:28 | call to FormValue | user-provided value | contenttype.go:0:0:0:0 | contenttype.go | |
| contenttype.go:79:11:79:14 | data | contenttype.go:73:10:73:28 | call to FormValue : string | contenttype.go:79:11:79:14 | data | Cross-site scripting vulnerability due to $@. | contenttype.go:73:10:73:28 | call to FormValue | user-provided value | contenttype.go:0:0:0:0 | contenttype.go | |
| contenttype.go:91:4:91:7 | data | contenttype.go:88:10:88:28 | call to FormValue : string | contenttype.go:91:4:91:7 | data | Cross-site scripting vulnerability due to $@. | contenttype.go:88:10:88:28 | call to FormValue | user-provided value | contenttype.go:0:0:0:0 | contenttype.go | |
| contenttype.go:114:50:114:53 | data | contenttype.go:113:10:113:28 | call to FormValue : string | contenttype.go:114:50:114:53 | data | Cross-site scripting vulnerability due to $@. | contenttype.go:113:10:113:28 | call to FormValue | user-provided value | contenttype.go:0:0:0:0 | contenttype.go | |
| reflectedxsstest.go:28:10:28:57 | type conversion | reflectedxsstest.go:27:2:27:38 | ... := ...[0] : pointer type | reflectedxsstest.go:28:10:28:57 | type conversion | Cross-site scripting vulnerability due to $@. | reflectedxsstest.go:27:2:27:38 | ... := ...[0] | user-provided value | reflectedxsstest.go:0:0:0:0 | reflectedxsstest.go | |
| reflectedxsstest.go:33:10:33:57 | type conversion | reflectedxsstest.go:31:2:31:44 | ... := ...[0] : File | reflectedxsstest.go:33:10:33:57 | type conversion | Cross-site scripting vulnerability due to $@. | reflectedxsstest.go:31:2:31:44 | ... := ...[0] | user-provided value | reflectedxsstest.go:0:0:0:0 | reflectedxsstest.go | |
| reflectedxsstest.go:34:10:34:62 | type conversion | reflectedxsstest.go:31:2:31:44 | ... := ...[1] : pointer type | reflectedxsstest.go:34:10:34:62 | type conversion | Cross-site scripting vulnerability due to $@. | reflectedxsstest.go:31:2:31:44 | ... := ...[1] | user-provided value | reflectedxsstest.go:0:0:0:0 | reflectedxsstest.go | |
| reflectedxsstest.go:44:10:44:55 | type conversion | reflectedxsstest.go:38:2:38:35 | ... := ...[0] : pointer type | reflectedxsstest.go:44:10:44:55 | type conversion | Cross-site scripting vulnerability due to $@. | reflectedxsstest.go:38:2:38:35 | ... := ...[0] | user-provided value | reflectedxsstest.go:0:0:0:0 | reflectedxsstest.go | |
| reflectedxsstest.go:45:10:45:18 | byteSlice | reflectedxsstest.go:38:2:38:35 | ... := ...[0] : pointer type | reflectedxsstest.go:45:10:45:18 | byteSlice | Cross-site scripting vulnerability due to $@. | reflectedxsstest.go:38:2:38:35 | ... := ...[0] | user-provided value | reflectedxsstest.go:0:0:0:0 | reflectedxsstest.go | |
| reflectedxsstest.go:54:11:54:21 | type conversion | reflectedxsstest.go:51:14:51:18 | selection of URL : pointer type | reflectedxsstest.go:54:11:54:21 | type conversion | Cross-site scripting vulnerability due to $@. | reflectedxsstest.go:51:14:51:18 | selection of URL | user-provided value | reflectedxsstest.go:0:0:0:0 | reflectedxsstest.go | |
| tst.go:18:12:18:39 | type conversion | tst.go:14:15:14:20 | selection of Form : Values | tst.go:18:12:18:39 | type conversion | Cross-site scripting vulnerability due to $@. | tst.go:14:15:14:20 | selection of Form | user-provided value | tst.go:0:0:0:0 | tst.go | |
| tst.go:53:12:53:26 | type conversion | tst.go:48:14:48:19 | selection of Form : Values | tst.go:53:12:53:26 | type conversion | Cross-site scripting vulnerability due to $@. | tst.go:48:14:48:19 | selection of Form | user-provided value | tst.go:0:0:0:0 | tst.go | |
| websocketXss.go:32:24:32:27 | xnet | websocketXss.go:30:7:30:10 | definition of xnet : slice type | websocketXss.go:32:24:32:27 | xnet | Cross-site scripting vulnerability due to $@. | websocketXss.go:30:7:30:10 | definition of xnet | user-provided value | websocketXss.go:0:0:0:0 | websocketXss.go | |

View File

@@ -1,9 +1,7 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
@@ -15,56 +13,8 @@ func serve() {
// BAD: a request parameter is incorporated without validation into the response
fmt.Fprintf(w, "%q is an unknown user", username)
} else {
// TODO: do something exciting
// TODO: Handle successful login
}
})
http.ListenAndServe(":80", nil)
}
func encode(s string) ([]byte, error) {
return json.Marshal(s)
}
func ServeJsonIndirect(w http.ResponseWriter, r http.Request) {
tainted := r.Header.Get("Origin")
noLongerTainted, _ := encode(tainted)
w.Write(noLongerTainted)
}
func ServeJsonDirect(w http.ResponseWriter, r http.Request) {
tainted := r.Header.Get("Origin")
noLongerTainted, _ := json.Marshal(tainted)
w.Write(noLongerTainted)
}
func ErrTest(w http.ResponseWriter, r http.Request) {
cookie, err := r.Cookie("somecookie")
w.Write([]byte(fmt.Sprintf("Cookie result: %v", cookie))) // BAD: Cookie's value is user-controlled
w.Write([]byte(fmt.Sprintf("Cookie check error: %v", err))) // GOOD: Cookie's err return is harmless
file, header, err := r.FormFile("someFile")
content, err2 := ioutil.ReadAll(file)
w.Write([]byte(fmt.Sprintf("File content: %v", content))) // BAD: file content is user-controlled
w.Write([]byte(fmt.Sprintf("File name: %v", header.Filename))) // BAD: file header is user-controlled
w.Write([]byte(fmt.Sprintf("FormFile error: %v", err))) // GOOD: FormFile's err return is harmless
w.Write([]byte(fmt.Sprintf("FormFile error: %v", err2))) // GOOD: ReadAll's err return is harmless
reader, err := r.MultipartReader()
part, err2 := reader.NextPart()
partName := part.FileName()
byteSlice := make([]byte, 100)
part.Read(byteSlice)
w.Write([]byte(fmt.Sprintf("Part name: %v", partName))) // BAD: part name is user-controlled
w.Write(byteSlice) // BAD: part contents are user-controlled
w.Write([]byte(fmt.Sprintf("MultipartReader error: %v", err))) // GOOD: MultipartReader's err return is harmless
w.Write([]byte(fmt.Sprintf("MultipartReader error: %v", err2))) // GOOD: NextPart's err return is harmless
}

View File

@@ -0,0 +1,56 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func encode(s string) ([]byte, error) {
return json.Marshal(s)
}
func ServeJsonIndirect(w http.ResponseWriter, r http.Request) {
tainted := r.Header.Get("Origin")
noLongerTainted, _ := encode(tainted)
w.Write(noLongerTainted)
}
func ServeJsonDirect(w http.ResponseWriter, r http.Request) {
tainted := r.Header.Get("Origin")
noLongerTainted, _ := json.Marshal(tainted)
w.Write(noLongerTainted)
}
func ErrTest(w http.ResponseWriter, r http.Request) {
cookie, err := r.Cookie("somecookie")
w.Write([]byte(fmt.Sprintf("Cookie result: %v", cookie))) // BAD: Cookie's value is user-controlled
w.Write([]byte(fmt.Sprintf("Cookie check error: %v", err))) // GOOD: Cookie's err return is harmless
file, header, err := r.FormFile("someFile")
content, err2 := ioutil.ReadAll(file)
w.Write([]byte(fmt.Sprintf("File content: %v", content))) // BAD: file content is user-controlled
w.Write([]byte(fmt.Sprintf("File name: %v", header.Filename))) // BAD: file header is user-controlled
w.Write([]byte(fmt.Sprintf("FormFile error: %v", err))) // GOOD: FormFile's err return is harmless
w.Write([]byte(fmt.Sprintf("FormFile error: %v", err2))) // GOOD: ReadAll's err return is harmless
reader, err := r.MultipartReader()
part, err2 := reader.NextPart()
partName := part.FileName()
byteSlice := make([]byte, 100)
part.Read(byteSlice)
w.Write([]byte(fmt.Sprintf("Part name: %v", partName))) // BAD: part name is user-controlled
w.Write(byteSlice) // BAD: part contents are user-controlled
w.Write([]byte(fmt.Sprintf("MultipartReader error: %v", err))) // GOOD: MultipartReader's err return is harmless
w.Write([]byte(fmt.Sprintf("MultipartReader error: %v", err2))) // GOOD: NextPart's err return is harmless
}
func QueryMapTest(w http.ResponseWriter, r http.Request) {
keys, ok := r.URL.Query()["data_id"]
if ok && len(keys[0]) > 0 {
key := keys[0]
w.Write([]byte(key)) // BAD: query string is user-controlled
}
}