Skip to content

Commit bc73d9f

Browse files
committed
JS: Support templates importing each other
1 parent bb80fdd commit bc73d9f

1 file changed

Lines changed: 49 additions & 1 deletion

File tree

javascript/ql/src/semmle/javascript/frameworks/Templating.qll

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ module Templating {
214214
exists(TemplateInstantiaton inst, API::Node base, string name |
215215
base.getARhs() = inst.getTemplateParamsNode() and
216216
result = base.getMember(name) and
217-
succ = inst.getTemplateFile().getAPlaceholder().getInnerTopLevel().getAVariableUse(name)
217+
succ = inst.getTemplateFile().getAnImportedFile*().getAPlaceholder().getInnerTopLevel().getAVariableUse(name)
218218
)
219219
or
220220
exists(string prop, DataFlow::SourceNode prev |
@@ -260,6 +260,11 @@ module Templating {
260260
abstract class TemplateFile extends File {
261261
/** Gets a placeholder tag in this file. */
262262
final TemplatePlaceholderTag getAPlaceholder() { result.getFile() = this }
263+
264+
/** Gets a template file referenced by this one via a template inclusion tag, such as `{% include foo %}` */
265+
TemplateFile getAnImportedFile() {
266+
result = getAPlaceholder().(TemplateInclusionTag).getImportedFile()
267+
}
263268
}
264269

265270
/** Any HTML file, seen as a possible target for template instantiation. */
@@ -601,4 +606,47 @@ module Templating {
601606

602607
override DataFlow::Node getARhs() { none() }
603608
}
609+
610+
/**
611+
* A template tag which causes another template file to be instantiated using the same variables as the current one.
612+
*
613+
* Examples:
614+
* - `{% include foo/bar %}`
615+
* - `{% include "../foo/bar.html" %}`
616+
* - `<% include foo/bar %>`
617+
* - `{{!< foo/bar }}`
618+
* - `{{> foo/bar }}`
619+
*/
620+
class TemplateInclusionTag extends TemplatePlaceholderTag {
621+
string rawPath;
622+
623+
TemplateInclusionTag() {
624+
rawPath = getRawText().regexpCapture("[{<]% *(?:import|include|extend|require)s? *(?:[(] *)?['\"]?(.*?)['\"]? *(?:[)] *)?%[}>]", 1)
625+
or
626+
rawPath = getRawText().regexpCapture("\\{\\{!?[<>](.*?)\\}\\}", 1)
627+
}
628+
629+
/** Gets the imported path (normalized). */
630+
string getPath() {
631+
result = rawPath.trim().replaceAll("\\", "/").regexpReplaceAll("^\\./", "")
632+
}
633+
634+
/** Gets the file referenced by this inclusion tag. */
635+
TemplateFile getImportedFile() {
636+
result = getPath().(TemplateFileReferenceString).getTemplateFile()
637+
}
638+
}
639+
640+
/** The imported string from a template inclusion tag. */
641+
private class TemplateInclusionPathString extends TemplateFileReferenceString {
642+
TemplateInclusionTag tag;
643+
644+
TemplateInclusionPathString() {
645+
this = tag.getPath()
646+
}
647+
648+
override Folder getContextFolder() {
649+
result = tag.getFile().getParentContainer()
650+
}
651+
}
604652
}

0 commit comments

Comments
 (0)