Skip to content

Commit e183897

Browse files
committed
Implement stdin models
Unfortunately due to how variable and varargs work, these are better done in QL
1 parent 5fb61b0 commit e183897

File tree

9 files changed

+120
-0
lines changed

9 files changed

+120
-0
lines changed

go/ql/lib/semmle/go/frameworks/stdlib/Fmt.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ module Fmt {
112112
Scanner() { this.hasQualifiedName("fmt", ["Scan", "Scanf", "Scanln"]) }
113113
}
114114

115+
private class ScannerSource extends SourceNode {
116+
ScannerSource() {
117+
// All of the arguments which are sources are varargs.
118+
this.asExpr() = any(Scanner s).getACall().getAnImplicitVarargsArgument().asExpr()
119+
}
120+
121+
override string getThreatModel() { result = "stdin" }
122+
}
123+
115124
/**
116125
* The `Fscan` function or one of its variants,
117126
* all of which read from a specified `io.Reader`.

go/ql/lib/semmle/go/frameworks/stdlib/Os.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,14 @@ module Os {
4343
input = inp and output = outp
4444
}
4545
}
46+
47+
private class Stdin extends SourceNode {
48+
Stdin() {
49+
exists(Variable osStdin | osStdin.hasQualifiedName("os", "Stdin") |
50+
this.asExpr() = osStdin.getARead().asExpr()
51+
)
52+
}
53+
54+
override string getThreatModel() { result = "stdin" }
55+
}
4656
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module test
2+
3+
go 1.22.6
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extensions:
2+
3+
- addsTo:
4+
pack: codeql/threat-models
5+
extensible: threatModelConfiguration
6+
data:
7+
- ["stdin", true, 0]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import go
2+
import ModelValidation
3+
import TestUtilities.InlineExpectationsTest
4+
5+
module SourceTest implements TestSig {
6+
string getARelevantTag() { result = "source" }
7+
8+
predicate hasActualResult(Location location, string element, string tag, string value) {
9+
exists(ThreatModelFlowSource s |
10+
s.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
11+
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
12+
element = s.toString() and
13+
value = "" and
14+
tag = "source"
15+
)
16+
}
17+
}
18+
19+
import MakeTest<SourceTest>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
testFailures
2+
invalidModelRow
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extensions:
2+
3+
- addsTo:
4+
pack: codeql/threat-models
5+
extensible: threatModelConfiguration
6+
data:
7+
- ["stdin", true, 0]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package test
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"os"
7+
)
8+
9+
func sink(string) {
10+
11+
}
12+
13+
func readStdinBuffer() {
14+
buf := make([]byte, 1024)
15+
n, err := os.Stdin.Read(buf) // $source
16+
if err != nil {
17+
return
18+
}
19+
sink(string(buf[:n])) // $hasTaintFlow="type conversion"
20+
}
21+
22+
func readStdinBuffReader() {
23+
buf := make([]byte, 1024)
24+
r := bufio.NewReader(os.Stdin) // $source
25+
n, err := r.Read(buf)
26+
if err != nil {
27+
return
28+
}
29+
sink(string(buf[:n])) // $hasTaintFlow="type conversion"
30+
}
31+
32+
func scan() {
33+
var username, email string
34+
fmt.Scan(&username, &email) // $source
35+
sink(username) // $hasTaintFlow="username"
36+
}
37+
38+
func scanf() {
39+
var s string
40+
fmt.Scanf("%s", &s) // $source
41+
sink(s) // $hasTaintFlow="s"
42+
}
43+
44+
func scanl() {
45+
var s string
46+
fmt.Scanln(&s) // $source
47+
sink(s) // $hasTaintFlow="s"
48+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import go
2+
import semmle.go.dataflow.ExternalFlow
3+
import ModelValidation
4+
import experimental.frameworks.CleverGo
5+
import TestUtilities.InlineFlowTest
6+
7+
module Config implements DataFlow::ConfigSig {
8+
predicate isSource(DataFlow::Node source) { source instanceof ThreatModelFlowSource }
9+
10+
predicate isSink(DataFlow::Node sink) {
11+
sink.asExpr() = any(CallExpr c | c.getTarget().getName() = "sink").getArgument(0)
12+
}
13+
}
14+
15+
import TaintFlowTest<Config>

0 commit comments

Comments
 (0)