Merge pull request #17177 from owen-mc/go/mad/convert-sink-3

Go: convert regex-use, url-redirection sinks to use models-as-data
This commit is contained in:
Owen Mansel-Chan
2024-08-12 16:21:48 +01:00
committed by GitHub
28 changed files with 228 additions and 221 deletions

View File

@@ -0,0 +1,12 @@
extensions:
- addsTo:
pack: codeql/go-all
extensible: packageGrouping
data:
- ["clever-go", "clevergo.tech/clevergo"]
- ["clever-go", "github.com/clevergo/clevergo"]
- addsTo:
pack: codeql/go-all
extensible: sinkModel
data:
- ["group:clever-go", "Context", True, "Redirect", "", "", "Argument[1]", "url-redirection[receiver]", "manual"]

View File

@@ -10,7 +10,10 @@ extensions:
pack: codeql/go-all
extensible: sinkModel
data:
# path-injection
- ["group:beego-context", "BeegoOutput", False, "Download", "", "", "Argument[0]", "path-injection", "manual"]
# url-redirection
- ["group:beego-context", "Context", True, "Redirect", "", "", "Argument[1]", "url-redirection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel

View File

@@ -27,6 +27,8 @@ extensions:
- ["group:beego", "Controller", False, "SaveToFile", "", "", "Argument[1]", "path-injection", "manual"]
- ["group:beego", "Controller", False, "SaveToFileWithBuffer", "", "", "Argument[1]", "path-injection", "manual"] # only exists in v2
- ["group:beego", "FileSystem", False, "Open", "", "", "Argument[0]", "path-injection", "manual"]
# url-redirection
- ["group:beego", "Controller", True, "Redirect", "", "", "Argument[0]", "url-redirection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel

View File

@@ -3,7 +3,10 @@ extensions:
pack: codeql/go-all
extensible: sinkModel
data:
# path-injection
- ["github.com/gofiber/fiber", "Ctx", False, "SendFile", "", "", "Argument[0]", "path-injection", "manual"]
- ["github.com/gofiber/fiber", "Ctx", False, "Download", "", "", "Argument[0]", "path-injection", "manual"]
- ["github.com/gofiber/fiber", "Ctx", False, "SaveFile", "", "", "Argument[1]", "path-injection", "manual"]
- ["github.com/gofiber/fiber", "Ctx", False, "SaveFileToStorage", "", "", "Argument[1]", "path-injection", "manual"] # does not exist in v1
# url-redirection
- ["github.com/gofiber/fiber", "Ctx", True, "Redirect", "", "", "Argument[0]", "url-redirection[receiver]", "manual"]

View File

@@ -3,8 +3,11 @@ extensions:
pack: codeql/go-all
extensible: sinkModel
data:
# path-injection
- ["github.com/labstack/echo", "Context", False, "Attachment", "", "", "Argument[0]", "path-injection", "manual"]
- ["github.com/labstack/echo", "Context", False, "File", "", "", "Argument[0]", "path-injection", "manual"]
# url-redirection
- ["github.com/labstack/echo", "Context", True, "Redirect", "", "", "Argument[1]", "url-redirection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel

View File

@@ -5,6 +5,11 @@ extensions:
data:
- ["revel", "github.com/revel/revel"]
- ["revel", "github.com/robfig/revel"]
- addsTo:
pack: codeql/go-all
extensible: sinkModel
data:
- ["group:revel", "Controller", True, "Redirect", "", "", "Argument[0]", "url-redirection", "manual"] # It is currently assumed that a tainted `value` in `Redirect(url, value)`, which calls `Sprintf(url, value)` internally, cannot lead to an open redirect vulnerability.
- addsTo:
pack: codeql/go-all
extensible: sourceModel

View File

@@ -38,6 +38,9 @@ extensions:
- ["github.com/valyala/fasthttp", "RequestCtx", False, "SendFile", "", "", "Argument[0]", "path-injection", "manual"]
- ["github.com/valyala/fasthttp", "RequestCtx", False, "SendFileBytes", "", "", "Argument[0]", "path-injection", "manual"]
- ["github.com/valyala/fasthttp", "Response", False, "SendFile", "", "", "Argument[0]", "path-injection", "manual"]
# url-redirection
- ["github.com/valyala/fasthttp", "RequestCtx", True, "Redirect", "", "", "Argument[0]", "url-redirection", "manual"]
- ["github.com/valyala/fasthttp", "RequestCtx", True, "RedirectBytes", "", "", "Argument[0]", "url-redirection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel

View File

@@ -1,4 +1,9 @@
extensions:
- addsTo:
pack: codeql/go-all
extensible: sinkModel
data:
- ["gopkg.in/macaron", "Context", True, "Redirect", "", "", "Argument[0]", "url-redirection[receiver]", "manual"]
- addsTo:
pack: codeql/go-all
extensible: sourceModel

View File

@@ -3,7 +3,10 @@ extensions:
pack: codeql/go-all
extensible: sinkModel
data:
# path-injection
- ["net/http", "", False, "ServeFile", "", "", "Argument[2]", "path-injection", "manual"]
# url-redirection
- ["net/http", "", True, "Redirect", "", "", "Argument[2]", "url-redirection[0]", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel

View File

@@ -1,4 +1,18 @@
extensions:
- addsTo:
pack: codeql/go-all
extensible: sinkModel
data:
- ["regexp", "", True, "Compile", "", "", "Argument[0]", "regex-use[c]", "manual"]
- ["regexp", "", True, "CompilePOSIX", "", "", "Argument[0]", "regex-use[c]", "manual"]
- ["regexp", "", True, "MustCompile", "", "", "Argument[0]", "regex-use[c]", "manual"]
- ["regexp", "", True, "MustCompilePOSIX", "", "", "Argument[0]", "regex-use[c]", "manual"]
- ["regexp", "", True, "Match", "", "", "Argument[0]", "regex-use[1]", "manual"]
- ["regexp", "", True, "MatchReader", "", "", "Argument[0]", "regex-use[1]", "manual"]
- ["regexp", "", True, "MatchString", "", "", "Argument[0]", "regex-use[1]", "manual"]
- ["regexp", "Regexp", True, "Match", "", "", "Argument[receiver]", "regex-use[0]", "manual"]
- ["regexp", "Regexp", True, "MatchReader", "", "", "Argument[receiver]", "regex-use[0]", "manual"]
- ["regexp", "Regexp", True, "MatchString", "", "", "Argument[receiver]", "regex-use[0]", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel

View File

@@ -305,6 +305,47 @@ module Http {
* open redirect exploits; for example, a form field submitted in a POST request.
*/
abstract class UnexploitableSource extends DataFlow::Node { }
private predicate sinkKindInfo(string kind, int rw) {
kind = "url-redirection" and
rw = -2
or
kind = "url-redirection[receiver]" and
rw = -1
or
sinkModel(_, _, _, _, _, _, _, kind, _, _) and
exists(string rwStr |
rwStr.toInt() = rw and
kind = "url-redirection[" + rwStr + "]"
)
}
private class DefaultHttpRedirect extends Range, DataFlow::CallNode {
DataFlow::ArgumentNode url;
int rw;
DefaultHttpRedirect() {
this = url.getCall() and
exists(string kind |
sinkKindInfo(kind, rw) and
sinkNode(url, kind)
)
}
override DataFlow::Node getUrl() {
not url instanceof DataFlow::ImplicitVarargsSlice and
result = url
or
url instanceof DataFlow::ImplicitVarargsSlice and
result = this.getAnImplicitVarargsArgument()
}
override Http::ResponseWriter getResponseWriter() {
rw = -1 and result.getANode() = this.getReceiver()
or
rw >= 0 and result.getANode() = this.getArgument(rw)
}
}
}
/**

View File

@@ -173,29 +173,6 @@ module Beego {
}
}
private class RedirectMethods extends Http::Redirect::Range, DataFlow::CallNode {
string className;
RedirectMethods() {
exists(string package |
(
package = packagePath() and className = "Controller"
or
package = contextPackagePath() and className = "Context"
) and
this = any(Method m | m.hasQualifiedName(package, className, "Redirect")).getACall()
)
}
override DataFlow::Node getUrl() {
className = "Controller" and result = this.getArgument(0)
or
className = "Context" and result = this.getArgument(1)
}
override Http::ResponseWriter getResponseWriter() { none() }
}
private class UtilsTaintPropagators extends TaintTracking::FunctionModel {
UtilsTaintPropagators() { this.hasQualifiedName(utilsPackagePath(), "GetDisplayString") }

View File

@@ -54,21 +54,6 @@ private module Echo {
override DataFlow::Node getAContentTypeNode() { result = callNode.getArgument(1) }
}
/**
* The `echo.Context.Redirect` method.
*/
private class EchoRedirectMethod extends Http::Redirect::Range, DataFlow::CallNode {
EchoRedirectMethod() {
exists(Method m | m.hasQualifiedName(packagePath(), "Context", "Redirect") |
this = m.getACall()
)
}
override DataFlow::Node getUrl() { result = this.getArgument(1) }
override Http::ResponseWriter getResponseWriter() { none() }
}
/**
* DEPRECATED: Use `FileSystemAccess::Range` instead.
*

View File

@@ -496,22 +496,6 @@ module Fasthttp {
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
}
/**
* The Methods that can be dangerous if they take user controlled URL as their first argument.
*/
class Redirect extends Http::Redirect::Range, DataFlow::CallNode {
Redirect() {
exists(Method m |
m.hasQualifiedName(packagePath(), "RequestCtx", ["Redirect", "RedirectBytes"]) and
this = m.getACall()
)
}
override DataFlow::Node getUrl() { result = this.getArgument(0) }
override Http::ResponseWriter getResponseWriter() { none() }
}
/**
* DEPRECATED: Use `RemoteFlowSource` instead.
*/

View File

@@ -17,14 +17,4 @@ private module Macaron {
override DataFlow::Node getANode() { result = v.similar().getAUse().getASuccessor*() }
}
private class RedirectCall extends Http::Redirect::Range, DataFlow::MethodCallNode {
RedirectCall() {
this.getTarget().hasQualifiedName("gopkg.in/macaron.v1", "Context", "Redirect")
}
override DataFlow::Node getUrl() { result = this.getArgument(0) }
override Http::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() }
}
}

View File

@@ -87,24 +87,6 @@ module Revel {
override string getAContentType() { result = contentType }
}
/**
* The `revel.Controller.Redirect` method.
*
* It is currently assumed that a tainted `value` in `Redirect(url, value)`, which calls `Sprintf(url, value)`
* internally, cannot lead to an open redirect vulnerability.
*/
private class ControllerRedirectMethod extends Http::Redirect::Range, DataFlow::CallNode {
ControllerRedirectMethod() {
exists(Method m | m.hasQualifiedName(packagePath(), "Controller", "Redirect") |
this = m.getACall()
)
}
override DataFlow::Node getUrl() { result = this.getArgument(0) }
override Http::ResponseWriter getResponseWriter() { none() }
}
/**
* A read in a Revel template that uses Revel's `raw` function.
*/

View File

@@ -175,14 +175,6 @@ module NetHttp {
override Http::ResponseWriter getResponseWriter() { result.getANode() = responseWriter }
}
private class RedirectCall extends Http::Redirect::Range, DataFlow::CallNode {
RedirectCall() { this.getTarget().hasQualifiedName("net/http", "Redirect") }
override DataFlow::Node getUrl() { result = this.getArgument(2) }
override Http::ResponseWriter getResponseWriter() { result.getANode() = this.getArgument(0) }
}
/** A call to a function in the `net/http` package that performs an HTTP request to a URL. */
private class RequestCall extends Http::ClientRequest::Range, DataFlow::CallNode {
RequestCall() {

View File

@@ -6,13 +6,46 @@ import go
/** Provides models of commonly used functions in the `regexp` package. */
module Regexp {
private class Pattern extends RegexpPattern::Range, DataFlow::ArgumentNode {
string fnName;
/**
* Holds if `kind` is an external sink kind that is relevant for regex flow.
* `strArg` is the index of the argument to methods with this sink kind that
* contain the string to be matched against, where "receiver" indicates the
* receiver; or -2 if no such argument exists and the function compiles the
* regex; or -3 if no such argument exists and the function does not compile
* the regex.
*
* So `regex-use[0]` indicates that argument 0 contains the string to matched
* against, `regex-use[c]` indicates that there is no string to be matched
* against and the return value of the function is a compiled regex, and
* `regex-use` means that there is no string to be matched against and the
* function does not compile the regex.
*/
private predicate regexSinkKindInfo(string kind, int strArg) {
strArg = -3 and
kind = "regex-use"
or
sinkModel(_, _, _, _, _, _, _, kind, _, _) and
exists(string strArgStr |
strArg >= 0 and
strArgStr.toInt() = strArg
or
strArg = -1 and
strArgStr = "receiver"
or
strArg = -2 and
strArgStr = "c"
|
kind = "regex-use[" + strArgStr + "]"
)
}
Pattern() {
exists(Function fn | fnName.matches("Match%") or fnName.matches("%Compile%") |
fn.hasQualifiedName("regexp", fnName) and
this = fn.getACall().getArgument(0)
private class DefaultRegexpPattern extends RegexpPattern::Range, DataFlow::ArgumentNode {
int strArg;
DefaultRegexpPattern() {
exists(string kind |
regexSinkKindInfo(kind, strArg) and
sinkNode(this, kind)
)
}
@@ -21,40 +54,39 @@ module Regexp {
override string getPattern() { result = this.asExpr().getStringValue() }
override DataFlow::Node getAUse() {
fnName.matches("MustCompile%") and
result = this.getCall().getASuccessor*()
or
fnName.matches("Compile%") and
strArg = -2 and
result = this.getCall().getResult(0).getASuccessor*()
or
result = this
}
}
private class MatchFunction extends RegexpMatchFunction::Range, Function {
MatchFunction() {
exists(string fn | fn.matches("Match%") | this.hasQualifiedName("regexp", fn))
private class DefaultRegexpMatchFunction extends RegexpMatchFunction::Range, Function {
int patArg;
int strArg;
DefaultRegexpMatchFunction() {
exists(DefaultRegexpPattern drp, string kind |
drp.getCall() = this.getACall() and
sinkNode(drp, kind)
|
patArg = drp.getPosition() and
regexSinkKindInfo(kind, strArg) and
strArg >= -1
)
}
override FunctionInput getRegexpArg() { result.isParameter(0) }
override FunctionInput getRegexpArg() {
patArg = -1 and result.isReceiver()
or
patArg >= 0 and result.isParameter(patArg)
}
override FunctionInput getValue() { result.isParameter(1) }
override FunctionInput getValue() { result.isParameter(strArg) }
override FunctionOutput getResult() { result.isResult(0) }
}
private class MatchMethod extends RegexpMatchFunction::Range, Method {
MatchMethod() {
exists(string fn | fn.matches("Match%") | this.hasQualifiedName("regexp", "Regexp", fn))
}
override FunctionInput getRegexpArg() { result.isReceiver() }
override FunctionInput getValue() { result.isParameter(0) }
override FunctionOutput getResult() { result.isResult() }
}
private class ReplaceFunction extends RegexpReplaceFunction::Range, Method {
ReplaceFunction() {
exists(string fn | fn.matches("ReplaceAll%") | this.hasQualifiedName("regexp", "Regexp", fn))

View File

@@ -171,25 +171,6 @@ private module CleverGo {
}
}
/**
* Models HTTP redirects.
*/
private class HttpRedirect extends Http::Redirect::Range, DataFlow::CallNode {
DataFlow::Node urlNode;
HttpRedirect() {
// HTTP redirect models for package: clevergo.tech/clevergo@v0.5.2
// Receiver type: Context
// signature: func (*Context) Redirect(code int, url string) error
this = any(Method m | m.hasQualifiedName(packagePath(), "Context", "Redirect")).getACall() and
urlNode = this.getArgument(1)
}
override DataFlow::Node getUrl() { result = urlNode }
override Http::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() }
}
/**
* Models HTTP ResponseBody where the content-type is static and non-modifiable.
*/

View File

@@ -126,25 +126,6 @@ private module Fiber {
}
}
/**
* Models HTTP redirects.
*/
private class Redirect extends Http::Redirect::Range, DataFlow::CallNode {
DataFlow::Node urlNode;
Redirect() {
// HTTP redirect models for package: github.com/gofiber/fiber@v1.14.6
// Receiver type: Ctx
// signature: func (*Ctx) Redirect(location string, status ...int)
this = any(Method m | m.hasQualifiedName(fiberPackagePath(), "Ctx", "Redirect")).getACall() and
urlNode = this.getArgument(0)
}
override DataFlow::Node getUrl() { result = urlNode }
override Http::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() }
}
/**
* Models HTTP header writers.
* The write is done with a call where you can set both the key and the value of the header.

View File

@@ -1,3 +1,6 @@
| file://:0:0:0:0 | Match | stdlib.go:16:30:16:37 | "posix?" | stdlib.go:28:11:28:24 | type conversion | stdlib.go:28:2:28:25 | call to Match |
| file://:0:0:0:0 | Match | stdlib.go:28:2:28:3 | re | stdlib.go:28:11:28:24 | type conversion | stdlib.go:28:2:28:25 | call to Match |
| file://:0:0:0:0 | MatchReader | stdlib.go:16:30:16:37 | "posix?" | stdlib.go:29:17:29:37 | call to NewReader | stdlib.go:29:2:29:38 | call to MatchReader |
| file://:0:0:0:0 | MatchReader | stdlib.go:29:2:29:3 | re | stdlib.go:29:17:29:37 | call to NewReader | stdlib.go:29:2:29:38 | call to MatchReader |
| file://:0:0:0:0 | MatchString | stdlib.go:16:30:16:37 | "posix?" | stdlib.go:17:17:17:22 | "posi" | stdlib.go:17:2:17:23 | call to MatchString |
| file://:0:0:0:0 | MatchString | stdlib.go:17:2:17:3 | re | stdlib.go:17:17:17:22 | "posi" | stdlib.go:17:2:17:23 | call to MatchString |

View File

@@ -2,16 +2,17 @@
| test.go:173:20:173:24 | param | test.go:172:11:172:32 | call to Param | test.go:173:20:173:24 | param | This path to an untrusted URL redirection depends on a $@. | test.go:172:11:172:32 | call to Param | user-provided value |
| test.go:182:20:182:28 | ...+... | test.go:178:11:178:32 | call to Param | test.go:182:20:182:28 | ...+... | This path to an untrusted URL redirection depends on a $@. | test.go:178:11:178:32 | call to Param | user-provided value |
edges
| test.go:172:11:172:32 | call to Param | test.go:173:20:173:24 | param | provenance | Src:MaD:1 |
| test.go:178:11:178:32 | call to Param | test.go:182:24:182:28 | param | provenance | Src:MaD:1 |
| test.go:182:24:182:28 | param | test.go:182:20:182:28 | ...+... | provenance | Config |
| test.go:172:11:172:32 | call to Param | test.go:173:20:173:24 | param | provenance | Src:MaD:2 Sink:MaD:1 |
| test.go:178:11:178:32 | call to Param | test.go:182:24:182:28 | param | provenance | Src:MaD:2 |
| test.go:182:24:182:28 | param | test.go:182:20:182:28 | ...+... | provenance | Config Sink:MaD:1 |
| test.go:190:9:190:26 | star expression | test.go:190:10:190:26 | selection of URL | provenance | Config |
| test.go:190:9:190:26 | star expression | test.go:193:21:193:23 | url | provenance | |
| test.go:190:10:190:26 | selection of URL | test.go:190:9:190:26 | star expression | provenance | Src:MaD:2 Config |
| test.go:193:21:193:23 | url | test.go:193:21:193:32 | call to String | provenance | Config |
| test.go:190:10:190:26 | selection of URL | test.go:190:9:190:26 | star expression | provenance | Src:MaD:3 Config |
| test.go:193:21:193:23 | url | test.go:193:21:193:32 | call to String | provenance | Config Sink:MaD:1 |
models
| 1 | Source: github.com/labstack/echo; Context; true; Param; ; ; ReturnValue[0]; remote; manual |
| 2 | Source: net/http; Request; true; URL; ; ; ; remote; manual |
| 1 | Sink: github.com/labstack/echo; Context; true; Redirect; ; ; Argument[1]; url-redirection; manual |
| 2 | Source: github.com/labstack/echo; Context; true; Param; ; ; ReturnValue[0]; remote; manual |
| 3 | Source: net/http; Request; true; URL; ; ; ; remote; manual |
nodes
| test.go:172:11:172:32 | call to Param | semmle.label | call to Param |
| test.go:173:20:173:24 | param | semmle.label | param |

View File

@@ -3,11 +3,12 @@
edges
| EndToEnd.go:94:20:94:27 | implicit dereference | EndToEnd.go:94:20:94:27 | selection of Params | provenance | Config |
| EndToEnd.go:94:20:94:27 | implicit dereference | EndToEnd.go:94:20:94:32 | selection of Form | provenance | Config |
| EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:27 | implicit dereference | provenance | Src:MaD:1 Config |
| EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:32 | selection of Form | provenance | Src:MaD:1 Config |
| EndToEnd.go:94:20:94:32 | selection of Form | EndToEnd.go:94:20:94:49 | call to Get | provenance | Config |
| EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:27 | implicit dereference | provenance | Src:MaD:2 Config |
| EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:32 | selection of Form | provenance | Src:MaD:2 Config |
| EndToEnd.go:94:20:94:32 | selection of Form | EndToEnd.go:94:20:94:49 | call to Get | provenance | Config Sink:MaD:1 |
models
| 1 | Source: group:revel; Controller; true; Params; ; ; ; remote; manual |
| 1 | Sink: group:revel; Controller; true; Redirect; ; ; Argument[0]; url-redirection; manual |
| 2 | Source: group:revel; Controller; true; Params; ; ; ; remote; manual |
nodes
| EndToEnd.go:94:20:94:27 | implicit dereference | semmle.label | implicit dereference |
| EndToEnd.go:94:20:94:27 | selection of Params | semmle.label | selection of Params |

View File

@@ -1,7 +1,17 @@
#select
| IncompleteHostnameRegexp.go:11:8:11:36 | "^((www\|beta).)?example.com/" | IncompleteHostnameRegexp.go:11:8:11:36 | "^((www\|beta).)?example.com/" | IncompleteHostnameRegexp.go:12:38:12:39 | re | This regular expression has an unescaped dot before ')?example.com', so it might match more hosts than expected when $@. | IncompleteHostnameRegexp.go:12:38:12:39 | re | the regular expression is used |
| main.go:40:60:40:79 | "^test2.github.com$" | main.go:40:60:40:79 | "^test2.github.com$" | main.go:40:60:40:79 | "^test2.github.com$" | This regular expression has an unescaped dot before 'github.com', so it might match more hosts than expected when $@. | main.go:40:60:40:79 | "^test2.github.com$" | the regular expression is used |
| main.go:45:15:45:39 | `https://www.example.com` | main.go:45:15:45:39 | `https://www.example.com` | main.go:45:15:45:39 | `https://www.example.com` | This regular expression has an unescaped dot before 'example.com', so it might match more hosts than expected when $@. | main.go:45:15:45:39 | `https://www.example.com` | the regular expression is used |
| main.go:49:21:49:45 | `https://www.example.com` | main.go:49:21:49:45 | `https://www.example.com` | main.go:65:15:65:23 | localVar3 | This regular expression has an unescaped dot before 'example.com', so it might match more hosts than expected when $@. | main.go:65:15:65:23 | localVar3 | the regular expression is used |
| main.go:56:15:56:34 | ...+... | main.go:56:15:56:34 | ...+... | main.go:56:15:56:34 | ...+... | This regular expression has an unescaped dot before 'example.com', so it might match more hosts than expected when $@. | main.go:56:15:56:34 | ...+... | the regular expression is used |
| main.go:58:15:58:42 | ...+... | main.go:58:15:58:42 | ...+... | main.go:58:15:58:42 | ...+... | This regular expression has an unescaped dot before 'example.com', so it might match more hosts than expected when $@. | main.go:58:15:58:42 | ...+... | the regular expression is used |
edges
| IncompleteHostnameRegexp.go:11:8:11:36 | "^((www\|beta).)?example.com/" | IncompleteHostnameRegexp.go:12:38:12:39 | re | provenance | |
| IncompleteHostnameRegexp.go:11:8:11:36 | "^((www\|beta).)?example.com/" | IncompleteHostnameRegexp.go:12:38:12:39 | re | provenance | Sink:MaD:2 |
| main.go:49:21:49:45 | `https://www.example.com` | main.go:62:15:62:25 | sourceConst | provenance | |
| main.go:62:15:62:25 | sourceConst | main.go:65:15:65:23 | localVar3 | provenance | |
| main.go:62:15:62:25 | sourceConst | main.go:65:15:65:23 | localVar3 | provenance | Sink:MaD:1 |
models
| 1 | Sink: regexp; ; true; Match; ; ; Argument[0]; regex-use[1]; manual |
| 2 | Sink: regexp; ; true; MatchString; ; ; Argument[0]; regex-use[1]; manual |
nodes
| IncompleteHostnameRegexp.go:11:8:11:36 | "^((www\|beta).)?example.com/" | semmle.label | "^((www\|beta).)?example.com/" |
| IncompleteHostnameRegexp.go:12:38:12:39 | re | semmle.label | re |
@@ -13,10 +23,3 @@ nodes
| main.go:62:15:62:25 | sourceConst | semmle.label | sourceConst |
| main.go:65:15:65:23 | localVar3 | semmle.label | localVar3 |
subpaths
#select
| IncompleteHostnameRegexp.go:11:8:11:36 | "^((www\|beta).)?example.com/" | IncompleteHostnameRegexp.go:11:8:11:36 | "^((www\|beta).)?example.com/" | IncompleteHostnameRegexp.go:12:38:12:39 | re | This regular expression has an unescaped dot before ')?example.com', so it might match more hosts than expected when $@. | IncompleteHostnameRegexp.go:12:38:12:39 | re | the regular expression is used |
| main.go:40:60:40:79 | "^test2.github.com$" | main.go:40:60:40:79 | "^test2.github.com$" | main.go:40:60:40:79 | "^test2.github.com$" | This regular expression has an unescaped dot before 'github.com', so it might match more hosts than expected when $@. | main.go:40:60:40:79 | "^test2.github.com$" | the regular expression is used |
| main.go:45:15:45:39 | `https://www.example.com` | main.go:45:15:45:39 | `https://www.example.com` | main.go:45:15:45:39 | `https://www.example.com` | This regular expression has an unescaped dot before 'example.com', so it might match more hosts than expected when $@. | main.go:45:15:45:39 | `https://www.example.com` | the regular expression is used |
| main.go:49:21:49:45 | `https://www.example.com` | main.go:49:21:49:45 | `https://www.example.com` | main.go:65:15:65:23 | localVar3 | This regular expression has an unescaped dot before 'example.com', so it might match more hosts than expected when $@. | main.go:65:15:65:23 | localVar3 | the regular expression is used |
| main.go:56:15:56:34 | ...+... | main.go:56:15:56:34 | ...+... | main.go:56:15:56:34 | ...+... | This regular expression has an unescaped dot before 'example.com', so it might match more hosts than expected when $@. | main.go:56:15:56:34 | ...+... | the regular expression is used |
| main.go:58:15:58:42 | ...+... | main.go:58:15:58:42 | ...+... | main.go:58:15:58:42 | ...+... | This regular expression has an unescaped dot before 'example.com', so it might match more hosts than expected when $@. | main.go:58:15:58:42 | ...+... | the regular expression is used |

View File

@@ -1 +1,2 @@
Security/CWE-020/IncompleteHostnameRegexp.ql
query: Security/CWE-020/IncompleteHostnameRegexp.ql
postprocess: TestUtilities/PrettyPrintModels.ql

View File

@@ -11,25 +11,26 @@
edges
| BadRedirectCheck.go:3:18:3:22 | argument corresponding to redir | BadRedirectCheck.go:5:10:5:14 | redir | provenance | |
| BadRedirectCheck.go:3:18:3:22 | definition of redir | BadRedirectCheck.go:5:10:5:14 | redir | provenance | |
| BadRedirectCheck.go:5:10:5:14 | redir | main.go:11:25:11:45 | call to sanitizeUrl | provenance | |
| cves.go:14:23:14:25 | argument corresponding to url | cves.go:16:26:16:28 | url | provenance | |
| cves.go:33:14:33:34 | call to Get | cves.go:37:25:37:32 | redirect | provenance | |
| cves.go:41:14:41:34 | call to Get | cves.go:45:25:45:32 | redirect | provenance | |
| BadRedirectCheck.go:5:10:5:14 | redir | main.go:11:25:11:45 | call to sanitizeUrl | provenance | Sink:MaD:1 |
| cves.go:14:23:14:25 | argument corresponding to url | cves.go:16:26:16:28 | url | provenance | Sink:MaD:1 |
| cves.go:33:14:33:34 | call to Get | cves.go:37:25:37:32 | redirect | provenance | Sink:MaD:1 |
| cves.go:41:14:41:34 | call to Get | cves.go:45:25:45:32 | redirect | provenance | Sink:MaD:1 |
| main.go:10:18:10:25 | argument corresponding to redirect | main.go:11:37:11:44 | redirect | provenance | |
| main.go:11:37:11:44 | redirect | BadRedirectCheck.go:3:18:3:22 | definition of redir | provenance | |
| main.go:11:37:11:44 | redirect | main.go:11:25:11:45 | call to sanitizeUrl | provenance | |
| main.go:32:24:32:26 | argument corresponding to url | main.go:34:26:34:28 | url | provenance | |
| main.go:11:37:11:44 | redirect | main.go:11:25:11:45 | call to sanitizeUrl | provenance | Sink:MaD:1 |
| main.go:32:24:32:26 | argument corresponding to url | main.go:34:26:34:28 | url | provenance | Sink:MaD:1 |
| main.go:68:17:68:24 | argument corresponding to redirect | main.go:73:20:73:27 | redirect | provenance | |
| main.go:68:17:68:24 | definition of redirect | main.go:73:20:73:27 | redirect | provenance | |
| main.go:73:9:73:28 | call to Clean | main.go:77:25:77:39 | call to getTarget1 | provenance | |
| main.go:73:20:73:27 | redirect | main.go:73:9:73:28 | call to Clean | provenance | MaD:1 |
| main.go:73:20:73:27 | redirect | main.go:73:9:73:28 | call to Clean | provenance | MaD:1 |
| main.go:73:9:73:28 | call to Clean | main.go:77:25:77:39 | call to getTarget1 | provenance | Sink:MaD:1 |
| main.go:73:20:73:27 | redirect | main.go:73:9:73:28 | call to Clean | provenance | MaD:2 |
| main.go:73:20:73:27 | redirect | main.go:73:9:73:28 | call to Clean | provenance | MaD:2 |
| main.go:76:19:76:21 | argument corresponding to url | main.go:77:36:77:38 | url | provenance | |
| main.go:77:36:77:38 | url | main.go:68:17:68:24 | definition of redirect | provenance | |
| main.go:77:36:77:38 | url | main.go:77:25:77:39 | call to getTarget1 | provenance | MaD:1 |
| main.go:87:9:87:14 | selection of Path | main.go:91:25:91:39 | call to getTarget2 | provenance | |
| main.go:77:36:77:38 | url | main.go:77:25:77:39 | call to getTarget1 | provenance | MaD:2 Sink:MaD:1 |
| main.go:87:9:87:14 | selection of Path | main.go:91:25:91:39 | call to getTarget2 | provenance | Sink:MaD:1 |
models
| 1 | Summary: path; ; false; Clean; ; ; Argument[0]; ReturnValue; taint; manual |
| 1 | Sink: net/http; ; true; Redirect; ; ; Argument[2]; url-redirection[0]; manual |
| 2 | Summary: path; ; false; Clean; ; ; Argument[0]; ReturnValue; taint; manual |
nodes
| BadRedirectCheck.go:3:18:3:22 | argument corresponding to redir | semmle.label | argument corresponding to redir |
| BadRedirectCheck.go:3:18:3:22 | definition of redir | semmle.label | definition of redir |

View File

@@ -11,24 +11,24 @@
| stdlib.go:192:23:192:33 | selection of Path | stdlib.go:190:36:190:56 | call to FormValue | stdlib.go:192:23:192:33 | selection of Path | This path to an untrusted URL redirection depends on a $@. | stdlib.go:190:36:190:56 | call to FormValue | user-provided value |
| stdlib.go:194:23:194:42 | call to EscapedPath | stdlib.go:190:36:190:56 | call to FormValue | stdlib.go:194:23:194:42 | call to EscapedPath | This path to an untrusted URL redirection depends on a $@. | stdlib.go:190:36:190:56 | call to FormValue | user-provided value |
edges
| OpenUrlRedirect.go:10:23:10:28 | selection of Form | OpenUrlRedirect.go:10:23:10:42 | call to Get | provenance | Src:MaD:2 Config |
| stdlib.go:13:13:13:18 | selection of Form | stdlib.go:13:13:13:32 | call to Get | provenance | Src:MaD:2 Config |
| OpenUrlRedirect.go:10:23:10:28 | selection of Form | OpenUrlRedirect.go:10:23:10:42 | call to Get | provenance | Src:MaD:3 Config Sink:MaD:1 |
| stdlib.go:13:13:13:18 | selection of Form | stdlib.go:13:13:13:32 | call to Get | provenance | Src:MaD:3 Config |
| stdlib.go:13:13:13:32 | call to Get | stdlib.go:15:30:15:35 | target | provenance | |
| stdlib.go:22:13:22:18 | selection of Form | stdlib.go:22:13:22:32 | call to Get | provenance | Src:MaD:2 Config |
| stdlib.go:22:13:22:18 | selection of Form | stdlib.go:22:13:22:32 | call to Get | provenance | Src:MaD:3 Config |
| stdlib.go:22:13:22:32 | call to Get | stdlib.go:24:30:24:35 | target | provenance | |
| stdlib.go:31:13:31:18 | selection of Form | stdlib.go:31:13:31:32 | call to Get | provenance | Src:MaD:2 Config |
| stdlib.go:31:13:31:18 | selection of Form | stdlib.go:31:13:31:32 | call to Get | provenance | Src:MaD:3 Config |
| stdlib.go:31:13:31:32 | call to Get | stdlib.go:35:34:35:39 | target | provenance | |
| stdlib.go:35:34:35:39 | target | stdlib.go:35:30:35:39 | ...+... | provenance | Config |
| stdlib.go:44:13:44:18 | selection of Form | stdlib.go:44:13:44:32 | call to Get | provenance | Src:MaD:2 Config |
| stdlib.go:44:13:44:32 | call to Get | stdlib.go:46:23:46:28 | target | provenance | |
| stdlib.go:64:13:64:18 | selection of Form | stdlib.go:64:13:64:32 | call to Get | provenance | Src:MaD:2 Config |
| stdlib.go:44:13:44:18 | selection of Form | stdlib.go:44:13:44:32 | call to Get | provenance | Src:MaD:3 Config |
| stdlib.go:44:13:44:32 | call to Get | stdlib.go:46:23:46:28 | target | provenance | Sink:MaD:1 |
| stdlib.go:64:13:64:18 | selection of Form | stdlib.go:64:13:64:32 | call to Get | provenance | Src:MaD:3 Config |
| stdlib.go:64:13:64:32 | call to Get | stdlib.go:67:23:67:28 | target | provenance | |
| stdlib.go:67:23:67:28 | target | stdlib.go:67:23:67:37 | ...+... | provenance | Config |
| stdlib.go:67:23:67:37 | ...+... | stdlib.go:67:23:67:40 | ...+... | provenance | Config |
| stdlib.go:89:13:89:18 | selection of Form | stdlib.go:89:13:89:32 | call to Get | provenance | Src:MaD:2 Config |
| stdlib.go:67:23:67:37 | ...+... | stdlib.go:67:23:67:40 | ...+... | provenance | Config Sink:MaD:1 |
| stdlib.go:89:13:89:18 | selection of Form | stdlib.go:89:13:89:32 | call to Get | provenance | Src:MaD:3 Config |
| stdlib.go:89:13:89:32 | call to Get | stdlib.go:90:3:90:8 | target | provenance | |
| stdlib.go:90:3:90:8 | target | stdlib.go:90:3:90:25 | ... += ... | provenance | Config |
| stdlib.go:90:3:90:25 | ... += ... | stdlib.go:92:23:92:28 | target | provenance | |
| stdlib.go:90:3:90:25 | ... += ... | stdlib.go:92:23:92:28 | target | provenance | Sink:MaD:1 |
| stdlib.go:107:54:107:54 | definition of r [pointer, URL, pointer] | stdlib.go:112:4:112:4 | r [pointer, URL, pointer] | provenance | |
| stdlib.go:107:54:107:54 | definition of r [pointer, URL] | stdlib.go:112:4:112:4 | r [pointer, URL] | provenance | |
| stdlib.go:107:54:107:54 | definition of r [pointer, URL] | stdlib.go:113:24:113:24 | r [pointer, URL] | provenance | |
@@ -40,35 +40,36 @@ edges
| stdlib.go:112:4:112:4 | r [pointer, URL] | stdlib.go:112:4:112:4 | implicit dereference [URL] | provenance | |
| stdlib.go:112:4:112:8 | implicit dereference | stdlib.go:112:4:112:8 | selection of URL | provenance | Config |
| stdlib.go:112:4:112:8 | implicit dereference | stdlib.go:112:4:112:8 | selection of URL [pointer] | provenance | |
| stdlib.go:112:4:112:8 | selection of URL | stdlib.go:112:4:112:4 | implicit dereference [URL] | provenance | Src:MaD:3 |
| stdlib.go:112:4:112:8 | selection of URL | stdlib.go:112:4:112:8 | implicit dereference | provenance | Src:MaD:3 Config |
| stdlib.go:112:4:112:8 | selection of URL | stdlib.go:112:4:112:4 | implicit dereference [URL] | provenance | Src:MaD:4 |
| stdlib.go:112:4:112:8 | selection of URL | stdlib.go:112:4:112:8 | implicit dereference | provenance | Src:MaD:4 Config |
| stdlib.go:112:4:112:8 | selection of URL [pointer] | stdlib.go:112:4:112:4 | implicit dereference [URL, pointer] | provenance | |
| stdlib.go:112:4:112:8 | selection of URL [pointer] | stdlib.go:112:4:112:8 | implicit dereference | provenance | |
| stdlib.go:113:24:113:24 | implicit dereference [URL] | stdlib.go:113:24:113:28 | selection of URL | provenance | |
| stdlib.go:113:24:113:24 | r [pointer, URL] | stdlib.go:113:24:113:24 | implicit dereference [URL] | provenance | |
| stdlib.go:113:24:113:28 | selection of URL | stdlib.go:113:24:113:37 | call to String | provenance | Src:MaD:3 Config |
| stdlib.go:146:13:146:18 | selection of Form | stdlib.go:146:13:146:32 | call to Get | provenance | Src:MaD:2 Config |
| stdlib.go:146:13:146:32 | call to Get | stdlib.go:152:23:152:28 | target | provenance | |
| stdlib.go:113:24:113:28 | selection of URL | stdlib.go:113:24:113:37 | call to String | provenance | Src:MaD:4 Config Sink:MaD:1 |
| stdlib.go:146:13:146:18 | selection of Form | stdlib.go:146:13:146:32 | call to Get | provenance | Src:MaD:3 Config |
| stdlib.go:146:13:146:32 | call to Get | stdlib.go:152:23:152:28 | target | provenance | Sink:MaD:1 |
| stdlib.go:159:10:159:15 | star expression | stdlib.go:159:11:159:15 | selection of URL | provenance | Config |
| stdlib.go:159:10:159:15 | star expression | stdlib.go:162:24:162:26 | url | provenance | |
| stdlib.go:159:11:159:15 | selection of URL | stdlib.go:159:10:159:15 | star expression | provenance | Src:MaD:3 Config |
| stdlib.go:162:24:162:26 | url | stdlib.go:162:24:162:35 | call to String | provenance | Config |
| stdlib.go:173:35:173:39 | selection of URL | stdlib.go:173:35:173:52 | call to RequestURI | provenance | Src:MaD:3 Config |
| stdlib.go:173:35:173:52 | call to RequestURI | stdlib.go:173:24:173:52 | ...+... | provenance | Config |
| stdlib.go:182:13:182:33 | call to FormValue | stdlib.go:184:23:184:28 | target | provenance | Src:MaD:1 |
| stdlib.go:159:11:159:15 | selection of URL | stdlib.go:159:10:159:15 | star expression | provenance | Src:MaD:4 Config |
| stdlib.go:162:24:162:26 | url | stdlib.go:162:24:162:35 | call to String | provenance | Config Sink:MaD:1 |
| stdlib.go:173:35:173:39 | selection of URL | stdlib.go:173:35:173:52 | call to RequestURI | provenance | Src:MaD:4 Config |
| stdlib.go:173:35:173:52 | call to RequestURI | stdlib.go:173:24:173:52 | ...+... | provenance | Config Sink:MaD:1 |
| stdlib.go:182:13:182:33 | call to FormValue | stdlib.go:184:23:184:28 | target | provenance | Src:MaD:2 Sink:MaD:1 |
| stdlib.go:190:3:190:8 | definition of target | stdlib.go:192:23:192:28 | target | provenance | |
| stdlib.go:190:3:190:8 | definition of target | stdlib.go:194:23:194:28 | target | provenance | |
| stdlib.go:190:3:190:57 | ... := ...[0] | stdlib.go:190:3:190:8 | definition of target | provenance | |
| stdlib.go:190:36:190:56 | call to FormValue | stdlib.go:190:3:190:57 | ... := ...[0] | provenance | Src:MaD:1 Config |
| stdlib.go:190:36:190:56 | call to FormValue | stdlib.go:190:3:190:57 | ... := ...[0] | provenance | Src:MaD:2 Config |
| stdlib.go:192:23:192:28 | implicit dereference | stdlib.go:190:3:190:8 | definition of target | provenance | Config |
| stdlib.go:192:23:192:28 | implicit dereference | stdlib.go:192:23:192:33 | selection of Path | provenance | Config |
| stdlib.go:192:23:192:28 | implicit dereference | stdlib.go:192:23:192:33 | selection of Path | provenance | Config Sink:MaD:1 |
| stdlib.go:192:23:192:28 | target | stdlib.go:192:23:192:28 | implicit dereference | provenance | Config |
| stdlib.go:192:23:192:28 | target | stdlib.go:192:23:192:33 | selection of Path | provenance | Config |
| stdlib.go:194:23:194:28 | target | stdlib.go:194:23:194:42 | call to EscapedPath | provenance | Config |
| stdlib.go:192:23:192:28 | target | stdlib.go:192:23:192:33 | selection of Path | provenance | Config Sink:MaD:1 |
| stdlib.go:194:23:194:28 | target | stdlib.go:194:23:194:42 | call to EscapedPath | provenance | Config Sink:MaD:1 |
models
| 1 | Source: net/http; Request; true; FormValue; ; ; ReturnValue; remote; manual |
| 2 | Source: net/http; Request; true; Form; ; ; ; remote; manual |
| 3 | Source: net/http; Request; true; URL; ; ; ; remote; manual |
| 1 | Sink: net/http; ; true; Redirect; ; ; Argument[2]; url-redirection[0]; manual |
| 2 | Source: net/http; Request; true; FormValue; ; ; ReturnValue; remote; manual |
| 3 | Source: net/http; Request; true; Form; ; ; ; remote; manual |
| 4 | Source: net/http; Request; true; URL; ; ; ; remote; manual |
nodes
| OpenUrlRedirect.go:10:23:10:28 | selection of Form | semmle.label | selection of Form |
| OpenUrlRedirect.go:10:23:10:42 | call to Get | semmle.label | call to Get |

View File

@@ -28,13 +28,13 @@ module KindValidation<KindValidationConfigSig Config> {
// shared
"code-injection", "command-injection", "environment-injection", "file-content-store",
"html-injection", "js-injection", "ldap-injection", "log-injection", "path-injection",
"request-forgery", "sql-injection", "url-redirection",
"request-forgery", "sql-injection", "url-redirection", "xpath-injection",
// Java-only currently, but may be shared in the future
"bean-validation", "fragment-injection", "groovy-injection", "hostname-verification",
"information-leak", "intent-redirection", "jexl-injection", "jndi-injection",
"mvel-injection", "notification", "ognl-injection", "pending-intents",
"response-splitting", "trust-boundary-violation", "template-injection", "url-forward",
"xpath-injection", "xslt-injection",
"xslt-injection",
// JavaScript-only currently, but may be shared in the future
"mongodb.sink", "nosql-injection", "unsafe-deserialization",
// Swift-only currently, but may be shared in the future
@@ -48,13 +48,11 @@ module KindValidation<KindValidationConfigSig Config> {
or
this.matches([
// shared
"credentials-%", "encryption-%", "qltest%", "test-%",
// Java-only currently, but may be shared in the future
"regex-use%",
"credentials-%", "encryption-%", "qltest%", "test-%", "regex-use%",
// Swift-only currently, but may be shared in the future
"%string-%length", "weak-hash-input-%",
// Go-only currently, but may be shared in the future
"request-forgery%"
"request-forgery[%]", "url-redirection[%]"
])
}
}