Skip to content

Commit 80ebfed

Browse files
authored
Merge pull request #336 from github/improve-getTemplateFile
Improve `RenderCall#getTemplateFile` performance and accuracy
2 parents 06e91c1 + e35ad02 commit 80ebfed

File tree

4 files changed

+46
-34
lines changed

4 files changed

+46
-34
lines changed

ql/lib/codeql/ruby/ast/Erb.qll

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,28 @@ class ErbFile extends File {
260260
*/
261261
predicate isPartial() { this.getStem().charAt(0) = "_" }
262262

263+
/**
264+
* Gets the base template name associated with this ERB file.
265+
* For instance, a file named `foo.html.erb` has a template name of `foo`.
266+
* A partial template file named `_item.html.erb` has a template name of `item`.
267+
*/
268+
string getTemplateName() { none() }
269+
263270
/**
264271
* Gets the erb template contained within this file.
265272
*/
266273
ErbTemplate getTemplate() { result = template }
267274
}
275+
276+
private class PartialErbFile extends ErbFile {
277+
PartialErbFile() { this.isPartial() }
278+
279+
// Drop the leading underscore
280+
override string getTemplateName() { result = this.getStem().splitAt(".", 0).suffix(1) }
281+
}
282+
283+
private class FullErbFile extends ErbFile {
284+
FullErbFile() { not this.isPartial() }
285+
286+
override string getTemplateName() { result = this.getStem().splitAt(".", 0) }
287+
}

ql/lib/codeql/ruby/frameworks/ActionView.qll

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -73,48 +73,30 @@ private class ActionViewParamsCall extends ActionViewContextCall, ParamsCall { }
7373
abstract class RenderCall extends MethodCall {
7474
RenderCall() { this.getMethodName() = "render" }
7575

76-
private string getWorkingDirectory() {
77-
result = this.getLocation().getFile().getParentContainer().getAbsolutePath()
76+
private Expr getTemplatePathArgument() {
77+
// TODO: support other ways of specifying paths (e.g. `file`)
78+
result = [this.getKeywordArgument(["partial", "template", "action"]), this.getArgument(0)]
7879
}
7980

80-
bindingset[templatePath]
81-
private string templatePathPattern(string templatePath) {
82-
exists(string basename, string relativeRoot |
83-
// everything after the final slash, or the whole string if there is no slash
84-
basename = templatePath.regexpCapture("^(?:.*/)?([^/]*)$", 1) and
85-
// everything up to and including the final slash
86-
relativeRoot = templatePath.regexpCapture("^(.*/)?(?:[^/]*?)$", 1)
87-
|
88-
(
89-
// path relative to <source_prefix>/app/views/
90-
result = "%/app/views/" + relativeRoot + "%" + basename + "%"
91-
or
92-
// relative to file containing call
93-
result = this.getWorkingDirectory() + "%" + templatePath + "%"
94-
)
95-
)
81+
private string getTemplatePathValue() { result = this.getTemplatePathArgument().getValueText() }
82+
83+
// everything up to and including the final slash, but ignoring any leading slash
84+
private string getSubPath() {
85+
result = this.getTemplatePathValue().regexpCapture("^/?(.*/)?(?:[^/]*?)$", 1)
9686
}
9787

98-
private string getTemplatePathPatterns() {
99-
exists(string templatePath |
100-
exists(Expr arg |
101-
// TODO: support other ways of specifying paths (e.g. `file`)
102-
arg = this.getKeywordArgument("partial") or
103-
arg = this.getKeywordArgument("template") or
104-
arg = this.getKeywordArgument("action") or
105-
arg = this.getArgument(0)
106-
|
107-
templatePath = arg.(StringlikeLiteral).getValueText()
108-
)
109-
|
110-
result = this.templatePathPattern(templatePath)
111-
)
88+
// everything after the final slash, or the whole string if there is no slash
89+
private string getBaseName() {
90+
result = this.getTemplatePathValue().regexpCapture("^/?(?:.*/)?([^/]*?)$", 1)
11291
}
11392

11493
/**
115-
* Get the template file to be rendered by this call, if any.
94+
* Gets the template file to be rendered by this call, if any.
11695
*/
117-
ErbFile getTemplateFile() { result.getAbsolutePath().matches(this.getTemplatePathPatterns()) }
96+
ErbFile getTemplateFile() {
97+
result.getTemplateName() = this.getBaseName() and
98+
result.getRelativePath().matches("%app/views/" + this.getSubPath() + "%")
99+
}
118100

119101
/**
120102
* Get the local variables passed as context to the renderer
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<%# This file is not rendered. There should be no flow into it. %>
2+
3+
<%= display_text.html_safe %>
4+
<%= local_assigns[:display_text].html_safe %>
5+
<%= @instance_text.html_safe %>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<%# This file is not rendered. There should be no flow into it. %>
2+
3+
<%= display_text.html_safe %>
4+
<%= local_assigns[:display_text].html_safe %>
5+
<%= @instance_text.html_safe %>

0 commit comments

Comments
 (0)