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

Commit 362d210

Browse files
authored
Merge pull request #330 from smowton/smowton/admin/standard-lib-pt-21-with-sanitiser
Move `strconv` and `strings` packages' taint-tracking to stdlib, and expand them + sanitise substrings of the HTTP Authorization header
2 parents b8d36b9 + b9b306a commit 362d210

File tree

9 files changed

+942
-100
lines changed

9 files changed

+942
-100
lines changed

ql/src/Security/CWE-681/IncorrectIntegerConversion.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
101101
// If we are reading a variable, check if it is
102102
// `strconv.IntSize`, and use 0 if it is.
103103
exists(DataFlow::Node rawBitSize | rawBitSize = ip.getTargetBitSizeInput().getNode(c) |
104-
if rawBitSize = any(StrConv::IntSize intSize).getARead()
104+
if rawBitSize = any(Strconv::IntSize intSize).getARead()
105105
then apparentBitSize = 0
106106
else apparentBitSize = rawBitSize.getIntValue()
107107
)

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

Lines changed: 2 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import semmle.go.frameworks.stdlib.MimeQuotedprintable
1818
import semmle.go.frameworks.stdlib.Path
1919
import semmle.go.frameworks.stdlib.PathFilepath
2020
import semmle.go.frameworks.stdlib.Reflect
21+
import semmle.go.frameworks.stdlib.Strconv
22+
import semmle.go.frameworks.stdlib.Strings
2123
import semmle.go.frameworks.stdlib.TextScanner
2224
import semmle.go.frameworks.stdlib.TextTabwriter
2325
import semmle.go.frameworks.stdlib.TextTemplate
@@ -483,105 +485,6 @@ module IntegerParser {
483485
}
484486
}
485487

486-
/**
487-
* Provides classes for some functions in the `strconv` package for
488-
* converting strings to numbers.
489-
*/
490-
module StrConv {
491-
/** The `Atoi` function. */
492-
class Atoi extends IntegerParser::Range {
493-
Atoi() { this.hasQualifiedName("strconv", "Atoi") }
494-
495-
override int getTargetBitSize() { result = 0 }
496-
}
497-
498-
/** The `ParseInt` function. */
499-
class ParseInt extends IntegerParser::Range {
500-
ParseInt() { this.hasQualifiedName("strconv", "ParseInt") }
501-
502-
override FunctionInput getTargetBitSizeInput() { result.isParameter(2) }
503-
}
504-
505-
/** The `ParseUint` function. */
506-
class ParseUint extends IntegerParser::Range {
507-
ParseUint() { this.hasQualifiedName("strconv", "ParseUint") }
508-
509-
override FunctionInput getTargetBitSizeInput() { result.isParameter(2) }
510-
}
511-
512-
/**
513-
* The `IntSize` constant, that gives the size in bits of an `int` or
514-
* `uint` value on the current architecture (32 or 64).
515-
*/
516-
class IntSize extends DeclaredConstant {
517-
IntSize() { this.hasQualifiedName("strconv", "IntSize") }
518-
}
519-
}
520-
521-
/** Provides models of commonly used functions in the `strings` package. */
522-
module Strings {
523-
/** The `Join` function. */
524-
class Join extends TaintTracking::FunctionModel {
525-
Join() { hasQualifiedName("strings", "Join") }
526-
527-
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
528-
inp.isParameter([0 .. 1]) and outp.isResult()
529-
}
530-
}
531-
532-
/** The `Repeat` function. */
533-
class Repeat extends TaintTracking::FunctionModel {
534-
Repeat() { hasQualifiedName("strings", "Repeat") }
535-
536-
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
537-
inp.isParameter(0) and outp.isResult()
538-
}
539-
}
540-
541-
/** The `Replace` or `ReplaceAll` function. */
542-
class Replacer extends TaintTracking::FunctionModel {
543-
Replacer() {
544-
hasQualifiedName("strings", "Replace") or hasQualifiedName("strings", "ReplaceAll")
545-
}
546-
547-
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
548-
(inp.isParameter(0) or inp.isParameter(2)) and
549-
outp.isResult()
550-
}
551-
}
552-
553-
/** The `Split` function or one of its variants. */
554-
class Splitter extends TaintTracking::FunctionModel {
555-
Splitter() {
556-
exists(string split | split.matches("Split%") | hasQualifiedName("strings", split))
557-
}
558-
559-
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
560-
inp.isParameter(0) and outp.isResult()
561-
}
562-
}
563-
564-
/** One of the case-converting functions in the `strings` package. */
565-
class CaseConverter extends TaintTracking::FunctionModel {
566-
CaseConverter() {
567-
exists(string conv | conv.matches("To%") | hasQualifiedName("strings", conv))
568-
}
569-
570-
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
571-
inp.isParameter(getNumParameter() - 1) and outp.isResult()
572-
}
573-
}
574-
575-
/** The `Trim` function or one of its variants. */
576-
class Trimmer extends TaintTracking::FunctionModel {
577-
Trimmer() { exists(string split | split.matches("Trim%") | hasQualifiedName("strings", split)) }
578-
579-
override predicate hasTaintFlow(FunctionInput inp, FunctionOutput outp) {
580-
inp.isParameter(0) and outp.isResult()
581-
}
582-
}
583-
}
584-
585488
/** Provides models of commonly used functions in the `net/url` package. */
586489
module URL {
587490
/** The `PathEscape` or `QueryEscape` function. */
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `strconv` package.
3+
*/
4+
5+
import go
6+
7+
/** Provides models of commonly used functions in the `strconv` package. */
8+
module Strconv {
9+
/** The `Atoi` function. */
10+
class Atoi extends IntegerParser::Range {
11+
Atoi() { this.hasQualifiedName("strconv", "Atoi") }
12+
13+
override int getTargetBitSize() { result = 0 }
14+
}
15+
16+
/** The `ParseInt` function. */
17+
class ParseInt extends IntegerParser::Range {
18+
ParseInt() { this.hasQualifiedName("strconv", "ParseInt") }
19+
20+
override FunctionInput getTargetBitSizeInput() { result.isParameter(2) }
21+
}
22+
23+
/** The `ParseUint` function. */
24+
class ParseUint extends IntegerParser::Range {
25+
ParseUint() { this.hasQualifiedName("strconv", "ParseUint") }
26+
27+
override FunctionInput getTargetBitSizeInput() { result.isParameter(2) }
28+
}
29+
30+
/**
31+
* The `IntSize` constant, that gives the size in bits of an `int` or
32+
* `uint` value on the current architecture (32 or 64).
33+
*/
34+
class IntSize extends DeclaredConstant {
35+
IntSize() { this.hasQualifiedName("strconv", "IntSize") }
36+
}
37+
38+
private class FunctionModels extends TaintTracking::FunctionModel {
39+
FunctionInput inp;
40+
FunctionOutput outp;
41+
42+
FunctionModels() {
43+
// signature: func AppendQuote(dst []byte, s string) []byte
44+
hasQualifiedName("strconv", "AppendQuote") and
45+
(inp.isParameter(_) and outp.isResult())
46+
or
47+
// signature: func AppendQuoteToASCII(dst []byte, s string) []byte
48+
hasQualifiedName("strconv", "AppendQuoteToASCII") and
49+
(inp.isParameter(_) and outp.isResult())
50+
or
51+
// signature: func AppendQuoteToGraphic(dst []byte, s string) []byte
52+
hasQualifiedName("strconv", "AppendQuoteToGraphic") and
53+
(inp.isParameter(_) and outp.isResult())
54+
or
55+
// signature: func Quote(s string) string
56+
hasQualifiedName("strconv", "Quote") and
57+
(inp.isParameter(0) and outp.isResult())
58+
or
59+
// signature: func QuoteToASCII(s string) string
60+
hasQualifiedName("strconv", "QuoteToASCII") and
61+
(inp.isParameter(0) and outp.isResult())
62+
or
63+
// signature: func QuoteToGraphic(s string) string
64+
hasQualifiedName("strconv", "QuoteToGraphic") and
65+
(inp.isParameter(0) and outp.isResult())
66+
or
67+
// signature: func Unquote(s string) (string, error)
68+
hasQualifiedName("strconv", "Unquote") and
69+
(inp.isParameter(0) and outp.isResult(0))
70+
or
71+
// signature: func UnquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error)
72+
hasQualifiedName("strconv", "UnquoteChar") and
73+
(inp.isParameter(0) and outp.isResult(2))
74+
}
75+
76+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
77+
input = inp and output = outp
78+
}
79+
}
80+
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/**
2+
* Provides classes modeling security-relevant aspects of the `strings` package.
3+
*/
4+
5+
import go
6+
7+
/** Provides models of commonly used functions in the `strings` package. */
8+
module Strings {
9+
private class FunctionModels extends TaintTracking::FunctionModel {
10+
FunctionInput inp;
11+
FunctionOutput outp;
12+
13+
FunctionModels() {
14+
// signature: func Fields(s string) []string
15+
hasQualifiedName("strings", "Fields") and
16+
(inp.isParameter(0) and outp.isResult())
17+
or
18+
// signature: func FieldsFunc(s string, f func(rune) bool) []string
19+
hasQualifiedName("strings", "FieldsFunc") and
20+
(inp.isParameter(0) and outp.isResult())
21+
or
22+
// signature: func Join(elems []string, sep string) string
23+
hasQualifiedName("strings", "Join") and
24+
(inp.isParameter(_) and outp.isResult())
25+
or
26+
// signature: func Map(mapping func(rune) rune, s string) string
27+
hasQualifiedName("strings", "Map") and
28+
(inp.isParameter(1) and outp.isResult())
29+
or
30+
// signature: func NewReader(s string) *Reader
31+
hasQualifiedName("strings", "NewReader") and
32+
(inp.isParameter(0) and outp.isResult())
33+
or
34+
// signature: func NewReplacer(oldnew ...string) *Replacer
35+
hasQualifiedName("strings", "NewReplacer") and
36+
(inp.isParameter(_) and outp.isResult())
37+
or
38+
// signature: func Repeat(s string, count int) string
39+
hasQualifiedName("strings", "Repeat") and
40+
(inp.isParameter(0) and outp.isResult())
41+
or
42+
// signature: func Replace(s string, old string, new string, n int) string
43+
hasQualifiedName("strings", "Replace") and
44+
(inp.isParameter([0, 2]) and outp.isResult())
45+
or
46+
// signature: func ReplaceAll(s string, old string, new string) string
47+
hasQualifiedName("strings", "ReplaceAll") and
48+
(inp.isParameter([0, 2]) and outp.isResult())
49+
or
50+
// signature: func Split(s string, sep string) []string
51+
hasQualifiedName("strings", "Split") and
52+
(inp.isParameter(0) and outp.isResult())
53+
or
54+
// signature: func SplitAfter(s string, sep string) []string
55+
hasQualifiedName("strings", "SplitAfter") and
56+
(inp.isParameter(0) and outp.isResult())
57+
or
58+
// signature: func SplitAfterN(s string, sep string, n int) []string
59+
hasQualifiedName("strings", "SplitAfterN") and
60+
(inp.isParameter(0) and outp.isResult())
61+
or
62+
// signature: func SplitN(s string, sep string, n int) []string
63+
hasQualifiedName("strings", "SplitN") and
64+
(inp.isParameter(0) and outp.isResult())
65+
or
66+
// signature: func Title(s string) string
67+
hasQualifiedName("strings", "Title") and
68+
(inp.isParameter(0) and outp.isResult())
69+
or
70+
// signature: func ToLower(s string) string
71+
hasQualifiedName("strings", "ToLower") and
72+
(inp.isParameter(0) and outp.isResult())
73+
or
74+
// signature: func ToLowerSpecial(c unicode.SpecialCase, s string) string
75+
hasQualifiedName("strings", "ToLowerSpecial") and
76+
(inp.isParameter(1) and outp.isResult())
77+
or
78+
// signature: func ToTitle(s string) string
79+
hasQualifiedName("strings", "ToTitle") and
80+
(inp.isParameter(0) and outp.isResult())
81+
or
82+
// signature: func ToTitleSpecial(c unicode.SpecialCase, s string) string
83+
hasQualifiedName("strings", "ToTitleSpecial") and
84+
(inp.isParameter(1) and outp.isResult())
85+
or
86+
// signature: func ToUpper(s string) string
87+
hasQualifiedName("strings", "ToUpper") and
88+
(inp.isParameter(0) and outp.isResult())
89+
or
90+
// signature: func ToUpperSpecial(c unicode.SpecialCase, s string) string
91+
hasQualifiedName("strings", "ToUpperSpecial") and
92+
(inp.isParameter(1) and outp.isResult())
93+
or
94+
// signature: func ToValidUTF8(s string, replacement string) string
95+
hasQualifiedName("strings", "ToValidUTF8") and
96+
(inp.isParameter(_) and outp.isResult())
97+
or
98+
// signature: func Trim(s string, cutset string) string
99+
hasQualifiedName("strings", "Trim") and
100+
(inp.isParameter(0) and outp.isResult())
101+
or
102+
// signature: func TrimFunc(s string, f func(rune) bool) string
103+
hasQualifiedName("strings", "TrimFunc") and
104+
(inp.isParameter(0) and outp.isResult())
105+
or
106+
// signature: func TrimLeft(s string, cutset string) string
107+
hasQualifiedName("strings", "TrimLeft") and
108+
(inp.isParameter(0) and outp.isResult())
109+
or
110+
// signature: func TrimLeftFunc(s string, f func(rune) bool) string
111+
hasQualifiedName("strings", "TrimLeftFunc") and
112+
(inp.isParameter(0) and outp.isResult())
113+
or
114+
// signature: func TrimPrefix(s string, prefix string) string
115+
hasQualifiedName("strings", "TrimPrefix") and
116+
(inp.isParameter(0) and outp.isResult())
117+
or
118+
// signature: func TrimRight(s string, cutset string) string
119+
hasQualifiedName("strings", "TrimRight") and
120+
(inp.isParameter(0) and outp.isResult())
121+
or
122+
// signature: func TrimRightFunc(s string, f func(rune) bool) string
123+
hasQualifiedName("strings", "TrimRightFunc") and
124+
(inp.isParameter(0) and outp.isResult())
125+
or
126+
// signature: func TrimSpace(s string) string
127+
hasQualifiedName("strings", "TrimSpace") and
128+
(inp.isParameter(0) and outp.isResult())
129+
or
130+
// signature: func TrimSuffix(s string, suffix string) string
131+
hasQualifiedName("strings", "TrimSuffix") and
132+
(inp.isParameter(0) and outp.isResult())
133+
}
134+
135+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
136+
input = inp and output = outp
137+
}
138+
}
139+
140+
private class MethodModels extends TaintTracking::FunctionModel, Method {
141+
FunctionInput inp;
142+
FunctionOutput outp;
143+
144+
MethodModels() {
145+
// signature: func (*Builder).String() string
146+
this.hasQualifiedName("strings", "Builder", "String") and
147+
(inp.isReceiver() and outp.isResult())
148+
or
149+
// signature: func (*Builder).Write(p []byte) (int, error)
150+
this.hasQualifiedName("strings", "Builder", "Write") and
151+
(inp.isParameter(0) and outp.isReceiver())
152+
or
153+
// signature: func (*Builder).WriteString(s string) (int, error)
154+
this.hasQualifiedName("strings", "Builder", "WriteString") and
155+
(inp.isParameter(0) and outp.isReceiver())
156+
or
157+
// signature: func (*Reader).Read(b []byte) (n int, err error)
158+
this.hasQualifiedName("strings", "Reader", "Read") and
159+
(inp.isReceiver() and outp.isParameter(0))
160+
or
161+
// signature: func (*Reader).ReadAt(b []byte, off int64) (n int, err error)
162+
this.hasQualifiedName("strings", "Reader", "ReadAt") and
163+
(inp.isReceiver() and outp.isParameter(0))
164+
or
165+
// signature: func (*Reader).Reset(s string)
166+
this.hasQualifiedName("strings", "Reader", "Reset") and
167+
(inp.isParameter(0) and outp.isReceiver())
168+
or
169+
// signature: func (*Reader).WriteTo(w io.Writer) (n int64, err error)
170+
this.hasQualifiedName("strings", "Reader", "WriteTo") and
171+
(inp.isReceiver() and outp.isParameter(0))
172+
or
173+
// signature: func (*Replacer).Replace(s string) string
174+
this.hasQualifiedName("strings", "Replacer", "Replace") and
175+
(inp.isParameter(0) and outp.isResult())
176+
or
177+
// signature: func (*Replacer).WriteString(w io.Writer, s string) (n int, err error)
178+
this.hasQualifiedName("strings", "Replacer", "WriteString") and
179+
(inp.isParameter(1) and outp.isParameter(0))
180+
}
181+
182+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
183+
input = inp and output = outp
184+
}
185+
}
186+
}

0 commit comments

Comments
 (0)