|
3 | 3 | private import codeql.ruby.controlflow.CfgNodes::ExprNodes
|
4 | 4 | private import codeql.ruby.DataFlow
|
5 | 5 | private import codeql.ruby.Concepts
|
| 6 | +private import codeql.ruby.AST |
| 7 | +private import codeql.ruby.dataflow.FlowSummary |
6 | 8 |
|
7 | 9 | /** Provides modeling for the Sinatra library. */
|
8 | 10 | module Sinatra {
|
@@ -41,4 +43,85 @@ module Sinatra {
|
41 | 43 | result = Http::Server::parameterInputKind()
|
42 | 44 | }
|
43 | 45 | }
|
| 46 | + |
| 47 | + private class ErbCall extends DataFlow::CallNode { |
| 48 | + private Route route; |
| 49 | + |
| 50 | + ErbCall() { |
| 51 | + this.asExpr().getExpr().getEnclosingCallable() = route.getBody().asCallableAstNode() and |
| 52 | + this.getMethodName() = "erb" |
| 53 | + } |
| 54 | + |
| 55 | + ErbFile getTemplateFile() { |
| 56 | + result.getTemplateName() = |
| 57 | + this.getArgument(0).asExpr().getConstantValue().getStringlikeValue() and |
| 58 | + result.getRelativePath().matches("%views/%") |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + ErbFile getTemplateFile(MethodCall erbCall) { |
| 63 | + result.getTemplateName() = erbCall.getArgument(0).getConstantValue().getStringlikeValue() and |
| 64 | + result.getRelativePath().matches("%views/%") |
| 65 | + } |
| 66 | + |
| 67 | + /** |
| 68 | + * Like `Location.toString`, but displays the relative path rather than the full path. |
| 69 | + */ |
| 70 | + private string locationRelativePathToString(Location loc) { |
| 71 | + result = |
| 72 | + loc.getFile().getRelativePath() + "@" + loc.getStartLine() + ":" + loc.getStartColumn() + ":" + |
| 73 | + loc.getEndLine() + ":" + loc.getEndColumn() |
| 74 | + } |
| 75 | + |
| 76 | + private class ErbLocalsHashSyntheticGlobal extends SummaryComponent::SyntheticGlobal { |
| 77 | + private string id; |
| 78 | + private MethodCall erbCall; |
| 79 | + private ErbFile erbFile; |
| 80 | + |
| 81 | + ErbLocalsHashSyntheticGlobal() { |
| 82 | + this = "SinatraErbLocalsHash(" + id + ")" and |
| 83 | + id = erbFile.getRelativePath() + "," + locationRelativePathToString(erbCall.getLocation()) and |
| 84 | + erbCall.getMethodName() = "erb" and |
| 85 | + erbFile = getTemplateFile(erbCall) |
| 86 | + } |
| 87 | + |
| 88 | + ErbFile getErbFile() { result = erbFile } |
| 89 | + |
| 90 | + string getId() { result = id } |
| 91 | + } |
| 92 | + |
| 93 | + private class ErbLocalsSummary extends SummarizedCallable { |
| 94 | + private ErbLocalsHashSyntheticGlobal global; |
| 95 | + |
| 96 | + ErbLocalsSummary() { this = "Sinatra::Base#erb" } |
| 97 | + |
| 98 | + override MethodCall getACall() { result = any(ErbCall c).asExpr().getExpr() } |
| 99 | + |
| 100 | + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { |
| 101 | + input = "Argument[2]" and output = "SyntheticGlobal[" + global + "]" and preservesValue = true |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + private class ErbLocalsAccessSummary extends SummarizedCallable { |
| 106 | + private ErbLocalsHashSyntheticGlobal global; |
| 107 | + private string local; |
| 108 | + |
| 109 | + ErbLocalsAccessSummary() { |
| 110 | + this = "sinatra_erb_locals_access()" + global.getId() + "#" + local and |
| 111 | + local = any(MethodCall c | c.getLocation().getFile() = global.getErbFile()).getMethodName() and |
| 112 | + local = any(Pair p).getKey().getConstantValue().getStringlikeValue() |
| 113 | + } |
| 114 | + |
| 115 | + override MethodCall getACall() { |
| 116 | + result.getLocation().getFile() = global.getErbFile() and |
| 117 | + result.getMethodName() = local and |
| 118 | + result.getReceiver() instanceof SelfVariableReadAccess |
| 119 | + } |
| 120 | + |
| 121 | + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { |
| 122 | + input = "SyntheticGlobal[" + global + "].Element[:" + local + "]" and |
| 123 | + output = "ReturnValue" and |
| 124 | + preservesValue = true |
| 125 | + } |
| 126 | + } |
44 | 127 | }
|
0 commit comments