Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit e9ae697

Browse files
authored
Merge pull request #251 from gagliardetto/standard-lib-pt-1
Add taint-tracking for archive/tar and archive/zip
2 parents 75d69ef + 437f4b7 commit e9ae697

File tree

8 files changed

+390
-0
lines changed

8 files changed

+390
-0
lines changed

ql/src/semmle/go/frameworks/Stdlib.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
*/
44

55
import go
6+
import semmle.go.frameworks.stdlib.ArchiveTar
7+
import semmle.go.frameworks.stdlib.ArchiveZip
68

79
/** A `String()` method. */
810
class StringMethod extends TaintTracking::FunctionModel, Method {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `archive/tar` package.
3+
*/
4+
5+
import go
6+
7+
/** Provides models of commonly used functions in the `archive/tar` package. */
8+
module ArchiveTar {
9+
private class FunctionModels extends TaintTracking::FunctionModel {
10+
FunctionInput inp;
11+
FunctionOutput outp;
12+
13+
FunctionModels() {
14+
// signature: func FileInfoHeader(fi os.FileInfo, link string) (*Header, error)
15+
hasQualifiedName("archive/tar", "FileInfoHeader") and
16+
(inp.isParameter(0) and outp.isResult(0))
17+
or
18+
// signature: func NewReader(r io.Reader) *Reader
19+
hasQualifiedName("archive/tar", "NewReader") and
20+
(inp.isParameter(0) and outp.isResult())
21+
or
22+
// signature: func NewWriter(w io.Writer) *Writer
23+
hasQualifiedName("archive/tar", "NewWriter") and
24+
(inp.isResult() and outp.isParameter(0))
25+
}
26+
27+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
28+
input = inp and output = outp
29+
}
30+
}
31+
32+
private class MethodModels extends TaintTracking::FunctionModel, Method {
33+
FunctionInput inp;
34+
FunctionOutput outp;
35+
36+
MethodModels() {
37+
// Methods:
38+
// signature: func (*Header).FileInfo() os.FileInfo
39+
this.(Method).hasQualifiedName("archive/tar", "Header", "FileInfo") and
40+
(inp.isReceiver() and outp.isResult())
41+
or
42+
// signature: func (*Reader).Next() (*Header, error)
43+
this.(Method).hasQualifiedName("archive/tar", "Reader", "Next") and
44+
(inp.isReceiver() and outp.isResult(0))
45+
or
46+
// signature: func (*Reader).Read(b []byte) (int, error)
47+
this.(Method).hasQualifiedName("archive/tar", "Reader", "Read") and
48+
(inp.isReceiver() and outp.isParameter(0))
49+
or
50+
// signature: func (*Writer).Write(b []byte) (int, error)
51+
this.(Method).hasQualifiedName("archive/tar", "Writer", "Write") and
52+
(inp.isParameter(0) and outp.isReceiver())
53+
or
54+
// signature: func (*Writer).WriteHeader(hdr *Header) error
55+
this.(Method).hasQualifiedName("archive/tar", "Writer", "WriteHeader") and
56+
(inp.isParameter(0) and outp.isReceiver())
57+
}
58+
59+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
60+
input = inp and output = outp
61+
}
62+
}
63+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `archive/zip` package.
3+
*/
4+
5+
import go
6+
7+
/** Provides models of commonly used functions in the `archive/zip` package. */
8+
module ArchiveZip {
9+
private class FunctionModels extends TaintTracking::FunctionModel {
10+
FunctionInput inp;
11+
FunctionOutput outp;
12+
13+
FunctionModels() {
14+
// signature: func FileInfoHeader(fi os.FileInfo) (*FileHeader, error)
15+
hasQualifiedName("archive/zip", "FileInfoHeader") and
16+
(inp.isParameter(0) and outp.isResult(0))
17+
or
18+
// signature: func NewReader(r io.ReaderAt, size int64) (*Reader, error)
19+
hasQualifiedName("archive/zip", "NewReader") and
20+
(inp.isParameter(0) and outp.isResult(0))
21+
or
22+
// signature: func NewWriter(w io.Writer) *Writer
23+
hasQualifiedName("archive/zip", "NewWriter") and
24+
(inp.isResult() and outp.isParameter(0))
25+
or
26+
// signature: func OpenReader(name string) (*ReadCloser, error)
27+
hasQualifiedName("archive/zip", "OpenReader") and
28+
(inp.isParameter(0) and outp.isResult(0))
29+
}
30+
31+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
32+
input = inp and output = outp
33+
}
34+
}
35+
36+
private class MethodModels extends TaintTracking::FunctionModel, Method {
37+
FunctionInput inp;
38+
FunctionOutput outp;
39+
40+
MethodModels() {
41+
// Methods:
42+
// signature: func (*File).Open() (io.ReadCloser, error)
43+
this.(Method).hasQualifiedName("archive/zip", "File", "Open") and
44+
(inp.isReceiver() and outp.isResult(0))
45+
or
46+
// signature: func (*Writer).Create(name string) (io.Writer, error)
47+
this.(Method).hasQualifiedName("archive/zip", "Writer", "Create") and
48+
(inp.isResult(0) and outp.isReceiver())
49+
or
50+
// signature: func (*Writer).CreateHeader(fh *FileHeader) (io.Writer, error)
51+
this.(Method).hasQualifiedName("archive/zip", "Writer", "CreateHeader") and
52+
(inp.isResult(0) and outp.isReceiver())
53+
}
54+
55+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
56+
input = inp and output = outp
57+
}
58+
}
59+
}

ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/ArchiveTar.go

Lines changed: 105 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/ArchiveZip.go

Lines changed: 95 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ql/test/library-tests/semmle/go/frameworks/StdlibTaintFlow/StdlibTaintFlow.expected

Whitespace-only changes.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @kind problem
3+
*/
4+
5+
import go
6+
7+
/* A special helper function used inside the test code */
8+
class Link extends TaintTracking::FunctionModel {
9+
Link() { hasQualifiedName(_, "link") }
10+
11+
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
12+
inp.isParameter(0) and outp.isParameter(1)
13+
}
14+
}
15+
16+
predicate isSource(DataFlow::Node source, DataFlow::CallNode call) {
17+
exists(Function fn | fn.hasQualifiedName(_, "newSource") |
18+
call = fn.getACall() and source = call.getResult()
19+
)
20+
}
21+
22+
predicate isSink(DataFlow::Node sink, DataFlow::CallNode call) {
23+
exists(Function fn | fn.hasQualifiedName(_, "sink") |
24+
call = fn.getACall() and sink = call.getArgument(1)
25+
)
26+
}
27+
28+
class FlowConf extends TaintTracking::Configuration {
29+
FlowConf() { this = "FlowConf" }
30+
31+
override predicate isSource(DataFlow::Node source) { isSource(source, _) }
32+
33+
override predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
34+
}
35+
36+
/**
37+
* True if the result of the provided sourceCall flows to the corresponding sink,
38+
* both marked by the same numeric first argument.
39+
*/
40+
predicate flowsToSink(DataFlow::CallNode sourceCall) {
41+
exists(
42+
FlowConf cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::CallNode sinkCall
43+
|
44+
cfg.hasFlowPath(source, sink) and
45+
(
46+
isSource(source.getNode(), sourceCall) and
47+
isSink(sink.getNode(), sinkCall) and
48+
sourceCall.getArgument(0).getIntValue() = sinkCall.getArgument(0).getIntValue()
49+
)
50+
)
51+
}
52+
53+
/* Show only flow sources that DON'T flow to their dedicated sink. */
54+
from DataFlow::CallNode sourceCall
55+
where isSource(_, sourceCall) and not flowsToSink(sourceCall)
56+
select sourceCall, "No flow to its sink"

0 commit comments

Comments
 (0)