@@ -66,6 +66,47 @@ module Templating {
6666
6767 /** Gets the top-level containing the template expression to be inserted at this placeholder. */
6868 TemplateTopLevel getInnerTopLevel ( ) { toplevel_parent_xml_node ( result , this ) }
69+
70+ /**
71+ * Holds if this performs raw interpolation, that is, inserts its result
72+ * in the output without escaping it.
73+ */
74+ predicate isRawInterpolation ( ) {
75+ getRawText ( ) .regexpMatch ( getLikelyTemplateSyntax ( getFile ( ) ) .getRawInterpolationRegexp ( ) )
76+ }
77+
78+ /** Holds if this occurs in a `script` tag. */
79+ predicate isInScriptTag ( ) {
80+ getParent ( ) instanceof HTML:: ScriptElement
81+ }
82+
83+ /**
84+ * Holds if this occurs in an attribute value that is interepted as JavaScript.
85+ *
86+ * Unlike in script tags, HTML entities in attributes are expanded prior to JS parsing,
87+ * which cancels out the benefit of HTML escaping.
88+ */
89+ predicate isInCodeAttribute ( ) {
90+ exists ( TopLevel code |
91+ code = getParent ( ) .( HTML:: Attribute ) .getCodeInAttribute ( )
92+ |
93+ code instanceof EventHandlerCode or
94+ code instanceof JavaScriptURL
95+ )
96+ }
97+
98+ /** Holds if this placeholder occurs in JS code. */
99+ predicate isInCodeContext ( ) {
100+ isInScriptTag ( ) or isInCodeAttribute ( )
101+ }
102+
103+ /**
104+ * Holds if this occurs in generated code as an expression or statement,
105+ * that is, without being enclosed in a string literal or similar.
106+ */
107+ predicate isInPlainCodeContext ( ) {
108+ this = any ( GeneratedCodeExpr e ) .getPlaceholderTag ( )
109+ }
69110 }
70111
71112 /**
@@ -387,4 +428,107 @@ module Templating {
387428 private TemplateFile getBestMatchingTarget ( TemplateFileReferenceString ref ) {
388429 result = max ( getAMatchingTarget ( ref ) as f order by getRankOfMatchingTarget ( f , ref ) )
389430 }
431+
432+ /**
433+ * A syntax type implemented by one or more supported templating engines.
434+ *
435+ * The syntax type determines which templating tags perform implicit escaping.
436+ *
437+ * Since this varies between templating engines, it is important to recognize the
438+ * templating engine correctly.
439+ */
440+ abstract class TemplateSyntax extends string {
441+ bindingset [ this ]
442+ TemplateSyntax ( ) { this = this }
443+
444+ /**
445+ * Gets a regular expression matching the full text of a placeholder tag
446+ * using raw interpolation, that is, without HTML escaping.
447+ */
448+ abstract string getRawInterpolationRegexp ( ) ;
449+
450+ /**
451+ * Gets a regular expression matching the full text of a placeholder tag
452+ * that performs HTML escaping on its output.
453+ */
454+ abstract string getEscapingInterpolationRegexp ( ) ;
455+
456+ /** Gets a file extension that is specific to this templating engine. */
457+ abstract string getAFileExtension ( ) ;
458+
459+ /** Gets the name of an NPM package providing this templating syntax. */
460+ abstract string getAPackageName ( ) ;
461+ }
462+
463+ /**
464+ * Mustache-style syntax, using `{{ }}` for safe interpolation, and (in some dialects)
465+ * `{{{ x }}}` for raw interpolation.
466+ */
467+ private class MustacheStyleSyntax extends TemplateSyntax {
468+ MustacheStyleSyntax ( ) { this = "mustache" }
469+
470+ override string getRawInterpolationRegexp ( ) {
471+ result = "(?s)\\{\\{\\{(.*?)\\}\\}\\}"
472+ }
473+
474+ override string getEscapingInterpolationRegexp ( ) {
475+ result = "(?s)\\{\\{[^{](.*?)\\}\\}"
476+ }
477+
478+ override string getAFileExtension ( ) {
479+ result = "hbs"
480+ }
481+
482+ override string getAPackageName ( ) {
483+ result = [ "mustache" , "handlebars" , "hbs" , "express-hbs" , "swig" , "swig-templates" , "hogan" , "hogan.js" , "nunjucks" ]
484+ }
485+ }
486+
487+ /**
488+ * EJS-style syntax, using `<%= x %>` for safe interpolation, and `<%- x %>` for
489+ * unsafe interpolation.
490+ */
491+ private class EjsStyleSyntax extends TemplateSyntax {
492+ EjsStyleSyntax ( ) { this = "ejs" }
493+
494+ override string getRawInterpolationRegexp ( ) {
495+ result = "(?s)<%-(.*?)%>"
496+ }
497+
498+ override string getEscapingInterpolationRegexp ( ) {
499+ result = "(?s)<%=(.*?)%>"
500+ }
501+
502+ override string getAFileExtension ( ) {
503+ result = "ejs"
504+ }
505+
506+ override string getAPackageName ( ) {
507+ result = "ejs"
508+ }
509+ }
510+
511+ private TemplateSyntax getOwnTemplateSyntaxInFolder ( Folder f ) {
512+ exists ( PackageDependencies deps |
513+ deps .getADependency ( result .getAPackageName ( ) , _) and
514+ f = deps .getFile ( ) .getParentContainer ( )
515+ )
516+ }
517+
518+ private TemplateSyntax getTemplateSyntaxInFolder ( Folder f ) {
519+ result = getOwnTemplateSyntaxInFolder ( f )
520+ or
521+ not exists ( getOwnTemplateSyntaxInFolder ( f ) ) and
522+ result = getTemplateSyntaxInFolder ( f .getParentContainer ( ) )
523+ }
524+
525+ /**
526+ * Gets a template syntax likely to be used in the given file.
527+ */
528+ TemplateSyntax getLikelyTemplateSyntax ( TemplateFile file ) {
529+ result .getAFileExtension ( ) = file .getExtension ( )
530+ or
531+ not file .getExtension ( ) = any ( TemplateSyntax s ) .getAFileExtension ( ) and
532+ result = getTemplateSyntaxInFolder ( file .getParentContainer ( ) )
533+ }
390534}
0 commit comments