mirror of
https://github.com/github/codeql.git
synced 2026-01-30 06:42:57 +01:00
RequestForgery: Add a safe URL sanitizer
This commit is contained in:
@@ -13,9 +13,13 @@ import go
|
||||
import semmle.go.security.RequestForgery::RequestForgery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node request
|
||||
from
|
||||
Configuration cfg, SafeUrlConfiguration scfg, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
DataFlow::Node request
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
request = sink.getNode().(Sink).getARequest()
|
||||
request = sink.getNode().(Sink).getARequest() and
|
||||
// this excludes flow from safe parts of request URLs, for example the full URL
|
||||
not scfg.hasFlow(_, sink.getNode())
|
||||
select request, source, sink, "The $@ of this request depends on $@.", sink.getNode(),
|
||||
sink.getNode().(Sink).getKind(), source, "a user-provided value"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import go
|
||||
import OpenUrlRedirectCustomizations
|
||||
|
||||
module RequestForgery {
|
||||
import RequestForgeryCustomizations::RequestForgery
|
||||
@@ -39,4 +40,42 @@ module RequestForgery {
|
||||
super.isSanitizerGuard(guard) or guard instanceof SanitizerGuard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow configuration for reasoning about safe URLs for request forgery.
|
||||
*/
|
||||
class SafeUrlConfiguration extends TaintTracking::Configuration {
|
||||
SafeUrlConfiguration() { this = "SafeUrlFlow" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.(DataFlow::FieldReadNode).getField().hasQualifiedName("net/http", "Request", "URL")
|
||||
or
|
||||
source.(DataFlow::FieldReadNode).getField().hasQualifiedName("net/http", "Request", "Host")
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// propagate to a URL when its host is assigned to
|
||||
exists(Write w, Field f, SsaWithFields v | f.hasQualifiedName("net/url", "URL", "Host") |
|
||||
w.writesField(v.getAUse(), f, pred) and succ = v.getAUse()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizerOut(DataFlow::Node node) {
|
||||
// block propagation of this safe value when its host is overwritten
|
||||
exists(Write w, Field f | f.hasQualifiedName("net/url", "URL", "Host") |
|
||||
w.writesField(node.getASuccessor(), f, _)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::FieldReadNode frn, string name |
|
||||
(name = "RawQuery" or name = "Fragment" or name = "User") and
|
||||
frn.getField().hasQualifiedName("net/url", "URL")
|
||||
|
|
||||
node = frn.getBase()
|
||||
)
|
||||
or
|
||||
TaintTracking::functionModelStep(any(OpenUrlRedirect::UnsafeUrlMethod um), node, _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,10 @@ module RequestForgery {
|
||||
override string getKind() { result = "URL" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A value that is the result of prepending a string that prevents any value from controlling the
|
||||
* host of a URL.
|
||||
*/
|
||||
private class HostnameSanitizer extends SanitizerEdge {
|
||||
HostnameSanitizer() { hostnameSanitizingPrefixEdge(this, _) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user