Ruby: track flow from sinatra routes to erb files

This commit is contained in:
Harry Maclean
2023-02-01 10:22:02 +13:00
parent c82b4638c6
commit eada3b91df

View File

@@ -3,6 +3,8 @@
private import codeql.ruby.controlflow.CfgNodes::ExprNodes
private import codeql.ruby.DataFlow
private import codeql.ruby.Concepts
private import codeql.ruby.AST
private import codeql.ruby.dataflow.FlowSummary
/** Provides modeling for the Sinatra library. */
module Sinatra {
@@ -41,4 +43,85 @@ module Sinatra {
result = Http::Server::parameterInputKind()
}
}
private class ErbCall extends DataFlow::CallNode {
private Route route;
ErbCall() {
this.asExpr().getExpr().getEnclosingCallable() = route.getBody().asCallableAstNode() and
this.getMethodName() = "erb"
}
ErbFile getTemplateFile() {
result.getTemplateName() =
this.getArgument(0).asExpr().getConstantValue().getStringlikeValue() and
result.getRelativePath().matches("%views/%")
}
}
ErbFile getTemplateFile(MethodCall erbCall) {
result.getTemplateName() = erbCall.getArgument(0).getConstantValue().getStringlikeValue() and
result.getRelativePath().matches("%views/%")
}
/**
* Like `Location.toString`, but displays the relative path rather than the full path.
*/
private string locationRelativePathToString(Location loc) {
result =
loc.getFile().getRelativePath() + "@" + loc.getStartLine() + ":" + loc.getStartColumn() + ":" +
loc.getEndLine() + ":" + loc.getEndColumn()
}
private class ErbLocalsHashSyntheticGlobal extends SummaryComponent::SyntheticGlobal {
private string id;
private MethodCall erbCall;
private ErbFile erbFile;
ErbLocalsHashSyntheticGlobal() {
this = "SinatraErbLocalsHash(" + id + ")" and
id = erbFile.getRelativePath() + "," + locationRelativePathToString(erbCall.getLocation()) and
erbCall.getMethodName() = "erb" and
erbFile = getTemplateFile(erbCall)
}
ErbFile getErbFile() { result = erbFile }
string getId() { result = id }
}
private class ErbLocalsSummary extends SummarizedCallable {
private ErbLocalsHashSyntheticGlobal global;
ErbLocalsSummary() { this = "Sinatra::Base#erb" }
override MethodCall getACall() { result = any(ErbCall c).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[2]" and output = "SyntheticGlobal[" + global + "]" and preservesValue = true
}
}
private class ErbLocalsAccessSummary extends SummarizedCallable {
private ErbLocalsHashSyntheticGlobal global;
private string local;
ErbLocalsAccessSummary() {
this = "sinatra_erb_locals_access()" + global.getId() + "#" + local and
local = any(MethodCall c | c.getLocation().getFile() = global.getErbFile()).getMethodName() and
local = any(Pair p).getKey().getConstantValue().getStringlikeValue()
}
override MethodCall getACall() {
result.getLocation().getFile() = global.getErbFile() and
result.getMethodName() = local and
result.getReceiver() instanceof SelfVariableReadAccess
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "SyntheticGlobal[" + global + "].Element[:" + local + "]" and
output = "ReturnValue" and
preservesValue = true
}
}
}