This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javascript | |
import DataFlow | |
import DataFlow::PathGraph | |
class SSRFConfiguration extends TaintTracking::Configuration { | |
SSRFConfiguration() { this = "SSRFConfiguration" } | |
override predicate isSource(DataFlow::Node source) { | |
exists(DataFlow::SourceNode req | | |
isHTTPRequest(req) and | |
( | |
exists(MethodCallNode call | | |
source = call and | |
call = req.getALocalSource().getAMethodCall("get") and | |
call.getAnArgument().getStringValue() = "Host" | |
) or | |
source = req.getALocalSource().getAPropertyRead("headers").getAPropertyRead("hostname") | |
) | |
) | |
} | |
override predicate isSink(DataFlow::Node sink) { sink instanceof SSRFSink } | |
} | |
// In next.js, req argument of getInitialProps and getServerSideProps function is HTTP request object | |
predicate isHTTPRequest(DataFlow::Node source) { | |
exists(FunctionNode func, ParameterNode param | | |
isServerSidePropsFunc(func) and | |
param = func.getAParameter() and | |
( | |
param.getAPropertyRead("req") = source or | |
param.getAPropertyRead("ctx").getAPropertyRead("req") = source | |
) | |
) or exists(HTTP::Servers::RequestSource req | source = req) | |
} | |
predicate isServerSidePropsFunc(DataFlow::Node source) { | |
exists(FunctionNode func | | |
source = func and | |
( | |
func.getName() = "getServerSideProps" or | |
func.getName() = "getInitialProps" | |
) | |
) or | |
exists(SourceNode page | | |
page.getAPropertyWrite("getServerSideProps").getAFunctionValue() = source | |
) | |
} | |
abstract class SSRFSink extends DataFlow::Node { } | |
class SSRFAxiosSink extends SSRFSink { | |
SSRFAxiosSink() { | |
exists(CallNode func, ModuleImportNode axiosImport | | |
axiosImport = moduleImport(["axios"]) and | |
( | |
func = axiosImport.getAMemberCall("get") | |
or | |
func = axiosImport.getAMemberCall("post") | |
or | |
func = axiosImport.getAMemberCall("put") | |
or | |
func = axiosImport.getAMemberCall("patch") | |
or | |
func = axiosImport.getAMemberCall("delete") | |
or | |
func = axiosImport.getACall() | |
) and | |
this = func.getArgument(0) | |
) | |
} | |
} | |
class SSRFFetchSink extends SSRFSink { | |
SSRFFetchSink() { | |
exists(CallNode compile | | |
compile = moduleImport(["isomorphic-fetch"]).getACall() and | |
this = compile.getArgument(0) | |
) | |
} | |
} | |
from DataFlow::PathNode source, DataFlow::PathNode sink, SSRFConfiguration c | |
where c.hasFlowPath(source, sink) | |
select sink.getNode(), source, sink, | |
"$@ flows to here and unsafely used as part of fetch URL", source.getNode(), | |
"User-provided value" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment