JS: Support TemplatePlaceholderTag.getEnclosingExpr

fixup! makeLocation
This commit is contained in:
Asger Feldthaus
2021-07-27 13:58:30 +02:00
parent 0a14de1741
commit 13aa511364
9 changed files with 80 additions and 44 deletions

View File

@@ -6,6 +6,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.Stack; import java.util.Stack;
import java.util.regex.Matcher;
import com.semmle.js.ast.AClass; import com.semmle.js.ast.AClass;
import com.semmle.js.ast.AFunction; import com.semmle.js.ast.AFunction;
@@ -570,6 +571,16 @@ public class ASTExtractor {
regexpExtractor.extract(source.substring(1, source.lastIndexOf('/')), offsets, nd, false); regexpExtractor.extract(source.substring(1, source.lastIndexOf('/')), offsets, nd, false);
} else if (nd.isStringLiteral() && !c.isInsideType() && nd.getRaw().length() < 1000) { } else if (nd.isStringLiteral() && !c.isInsideType() && nd.getRaw().length() < 1000) {
regexpExtractor.extract(valueString, makeStringLiteralOffsets(nd.getRaw()), nd, true); regexpExtractor.extract(valueString, makeStringLiteralOffsets(nd.getRaw()), nd, true);
// Scan the string for template tags, if we're in a context where such tags are relevant.
if (scopeManager.isInTemplateFile()) {
Matcher m = TemplateEngines.TEMPLATE_TAGS.matcher(nd.getRaw());
int offset = nd.getLoc().getStart().getOffset();
while (m.find()) {
Label locationLbl = TemplateEngines.makeLocation(lexicalExtractor.getTextualExtractor(), offset + m.start(), offset + m.end());
trapwriter.addTuple("expr_contains_template_tag_location", key, locationLbl);
}
}
} }
return key; return key;
} }
@@ -2225,7 +2236,8 @@ public class ASTExtractor {
@Override @Override
public Label visit(GeneratedCodeExpr nd, Context c) { public Label visit(GeneratedCodeExpr nd, Context c) {
Label key = super.visit(nd, c); Label key = super.visit(nd, c);
trapwriter.addTuple("generated_code_expr_info", key, nd.getOpeningDelimiter(), nd.getClosingDelimiter(), nd.getBody()); Label templateLbl = TemplateEngines.makeLocation(lexicalExtractor.getTextualExtractor(), nd.getLoc().getStart().getOffset(), nd.getLoc().getEnd().getOffset());
trapwriter.addTuple("expr_contains_template_tag_location", key, templateLbl);
return key; return key;
} }
} }

View File

@@ -39,7 +39,7 @@ public class HTMLExtractor implements IExtractor {
this.textualExtractor = textualExtractor; this.textualExtractor = textualExtractor;
this.scopeManager = this.scopeManager =
new ScopeManager(textualExtractor.getTrapwriter(), config.getEcmaVersion()); new ScopeManager(textualExtractor.getTrapwriter(), config.getEcmaVersion(), true);
} }
/* /*
@@ -382,21 +382,6 @@ public class HTMLExtractor implements IExtractor {
writer.addTuple("toplevel_parent_xml_node", topLevelLabel, htmlNodeLabel); writer.addTuple("toplevel_parent_xml_node", topLevelLabel, htmlNodeLabel);
} }
private static final String MUSTACHE_TAG_DOUBLE = "\\{\\{(?!\\{)(.*?)\\}\\}"; // {{ x }}
private static final String MUSTACHE_TAG_TRIPLE = "\\{\\{\\{(.*?)\\}\\}\\}"; // {{{ x }}}
private static final String MUSTACHE_TAG_PERCENT = "\\{%(?!>)(.*?)%\\}"; // {% x %}
private static final String EJS_TAG = "<%(?![%<>}])[-=]?(.*?)[_-]?%>"; // <% x %>
/** Pattern for a template tag whose contents should be parsed as an expression */
private static final Pattern TEMPLATE_EXPR_OPENING_TAG =
Pattern.compile("^(?:\\{\\{\\{?|<%[-=])"); // {{, {{{, <%=, <%-
private static final Pattern TEMPLATE_TAGS =
Pattern.compile(
StringUtil.glue(
"|", MUSTACHE_TAG_DOUBLE, MUSTACHE_TAG_TRIPLE, MUSTACHE_TAG_PERCENT, EJS_TAG),
Pattern.DOTALL);
private void extractTemplateTags( private void extractTemplateTags(
TextualExtractor textualExtractor, TextualExtractor textualExtractor,
ScopeManager scopeManager, ScopeManager scopeManager,
@@ -407,9 +392,8 @@ public class HTMLExtractor implements IExtractor {
if (start >= end) return; if (start >= end) return;
if (isEmbedded) return; // Do not extract template tags for HTML snippets embedded in a JS file if (isEmbedded) return; // Do not extract template tags for HTML snippets embedded in a JS file
LocationManager locationManager = textualExtractor.getLocationManager();
TrapWriter trapwriter = textualExtractor.getTrapwriter(); TrapWriter trapwriter = textualExtractor.getTrapwriter();
Matcher m = TEMPLATE_TAGS.matcher(textualExtractor.getSource()).region(start, end); Matcher m = TemplateEngines.TEMPLATE_TAGS.matcher(textualExtractor.getSource()).region(start, end);
while (m.find()) { while (m.find()) {
int startOffset = m.start(); int startOffset = m.start();
int endOffset = m.end(); int endOffset = m.end();
@@ -424,15 +408,12 @@ public class HTMLExtractor implements IExtractor {
String rawText = m.group(); String rawText = m.group();
trapwriter.addTuple("template_placeholder_tag_info", lbl, parentLabel.get(), rawText); trapwriter.addTuple("template_placeholder_tag_info", lbl, parentLabel.get(), rawText);
// Emit location // Emit location entity
Position startPos = textualExtractor.getSourceMap().getStart(startOffset); Label locationLbl = TemplateEngines.makeLocation(textualExtractor, startOffset, endOffset);
Position endPos = textualExtractor.getSourceMap().getEnd(endOffset - 1); trapwriter.addTuple("hasLocation", lbl, locationLbl);
int endColumn = endPos.getColumn() - 1; // Convert to inclusive end position (still 1-based)
locationManager.emitFileLocation(
lbl, startPos.getLine(), startPos.getColumn(), endPos.getLine(), endColumn);
// Parse the contents as a template expression, if the delimiter expects an expression. // Parse the contents as a template expression, if the delimiter expects an expression.
Matcher delimMatcher = TEMPLATE_EXPR_OPENING_TAG.matcher(rawText); Matcher delimMatcher = TemplateEngines.TEMPLATE_EXPR_OPENING_TAG.matcher(rawText);
if (delimMatcher.find()) { if (delimMatcher.find()) {
// The body of the template tag is stored in the first capture group of each pattern // The body of the template tag is stored in the first capture group of each pattern
int bodyGroup = getNonNullCaptureGroup(m); int bodyGroup = getNonNullCaptureGroup(m);

View File

@@ -103,12 +103,22 @@ public class ScopeManager {
private final ECMAVersion ecmaVersion; private final ECMAVersion ecmaVersion;
private final Set<String> implicitGlobals = new LinkedHashSet<String>(); private final Set<String> implicitGlobals = new LinkedHashSet<String>();
private Scope implicitVariableScope; private Scope implicitVariableScope;
private final boolean isInTemplateScope;
public ScopeManager(TrapWriter trapWriter, ECMAVersion ecmaVersion) { public ScopeManager(TrapWriter trapWriter, ECMAVersion ecmaVersion, boolean isInTemplateScope) {
this.trapWriter = trapWriter; this.trapWriter = trapWriter;
this.toplevelScope = enterScope(ScopeKind.GLOBAL, trapWriter.globalID("global_scope"), null); this.toplevelScope = enterScope(ScopeKind.GLOBAL, trapWriter.globalID("global_scope"), null);
this.ecmaVersion = ecmaVersion; this.ecmaVersion = ecmaVersion;
this.implicitVariableScope = toplevelScope; this.implicitVariableScope = toplevelScope;
this.isInTemplateScope = isInTemplateScope;
}
/**
* Returns true the current scope is potentially in a template file, and may contain
* relevant template tags.
*/
public boolean isInTemplateFile() {
return isInTemplateScope;
} }
/** /**

View File

@@ -77,7 +77,7 @@ public class ScriptExtractor implements IExtractor {
} }
ScopeManager scopeManager = ScopeManager scopeManager =
new ScopeManager(textualExtractor.getTrapwriter(), config.getEcmaVersion()); new ScopeManager(textualExtractor.getTrapwriter(), config.getEcmaVersion(), false);
Label toplevelLabel = null; Label toplevelLabel = null;
LoCInfo loc; LoCInfo loc;
try { try {

View File

@@ -0,0 +1,37 @@
package com.semmle.js.extractor;
import java.util.regex.Pattern;
import com.semmle.util.data.StringUtil;
import com.semmle.util.locations.Position;
import com.semmle.util.trap.TrapWriter.Label;
public class TemplateEngines {
private static final String MUSTACHE_TAG_TRIPLE = "\\{\\{\\{(.*?)\\}\\}\\}"; // {{{ x }}}
private static final String MUSTACHE_TAG_DOUBLE = "\\{\\{(?!\\{)(.*?)\\}\\}"; // {{ x }}}
private static final String MUSTACHE_TAG_PERCENT = "\\{%(?!>)(.*?)%\\}"; // {% x %}
private static final String EJS_TAG = "<%(?![%<>}])[-=]?(.*?)[_-]?%>"; // <% x %>
/** Pattern for a template tag whose contents should be parsed as an expression */
public static final Pattern TEMPLATE_EXPR_OPENING_TAG =
Pattern.compile("^(?:\\{\\{\\{?|<%[-=])"); // {{, {{{, <%=, <%-
/**
* Pattern matching a template tag from a supported template engine.
*/
public static final Pattern TEMPLATE_TAGS =
Pattern.compile(
StringUtil.glue(
"|", TemplateEngines.MUSTACHE_TAG_DOUBLE, MUSTACHE_TAG_TRIPLE, MUSTACHE_TAG_PERCENT, EJS_TAG),
Pattern.DOTALL);
/**
* Returns the location label for a template tag at the given offsets.
*/
public static Label makeLocation(TextualExtractor extractor, int startOffset, int endOffset) {
Position startPos = extractor.getSourceMap().getStart(startOffset);
Position endPos = extractor.getSourceMap().getEnd(endOffset - 1);
int endColumn = endPos.getColumn() - 1; // Convert to inclusive end position (still 1-based)
return extractor.getLocationManager().emitLocationsDefault(startPos.getLine(), startPos.getColumn(), endPos.getLine(), endColumn);
}
}

View File

@@ -23,7 +23,7 @@ public class TypeScriptExtractor implements IExtractor {
File sourceFile = textualExtractor.getExtractedFile(); File sourceFile = textualExtractor.getExtractedFile();
Result res = state.getTypeScriptParser().parse(sourceFile, source, textualExtractor.getMetrics()); Result res = state.getTypeScriptParser().parse(sourceFile, source, textualExtractor.getMetrics());
ScopeManager scopeManager = ScopeManager scopeManager =
new ScopeManager(textualExtractor.getTrapwriter(), ECMAVersion.ECMA2017); new ScopeManager(textualExtractor.getTrapwriter(), ECMAVersion.ECMA2017, false);
try { try {
FileSnippet snippet = state.getSnippets().get(sourceFile.toPath()); FileSnippet snippet = state.getSnippets().get(sourceFile.toPath());
SourceType sourceType = snippet != null ? snippet.getSourceType() : jsExtractor.establishSourceType(source, false); SourceType sourceType = snippet != null ? snippet.getSourceType() : jsExtractor.establishSourceType(source, false);

View File

@@ -2893,18 +2893,9 @@ class ImportMetaExpr extends @import_meta_expr, Expr {
* ``` * ```
*/ */
class GeneratedCodeExpr extends @generated_code_expr, Expr { class GeneratedCodeExpr extends @generated_code_expr, Expr {
/** Gets the opening delimiter, such as `{{` or `{{{`. */
string getOpeningDelimiter() { generated_code_expr_info(this, result, _, _) }
/** Gets the closing delimiter, such as `}}` or `}}}`. */
string getClosingDelimiter() { generated_code_expr_info(this, _, result, _) }
/** Gets the text between the delimiters, including any surrounding whitespace, such as the `x` in `{{x}}`. */
string getBody() { generated_code_expr_info(this, _, _, result) }
/** Gets the placeholder tag that was parsed as an expression. */ /** Gets the placeholder tag that was parsed as an expression. */
Templating::TemplatePlaceholderTag getPlaceholderTag() { Templating::TemplatePlaceholderTag getPlaceholderTag() {
result.getLocation() = this.getLocation() this = result.getEnclosingExpr()
} }
override string getAPrimaryQlClass() { result = "GeneratedCodeExpr" } override string getAPrimaryQlClass() { result = "GeneratedCodeExpr" }

View File

@@ -114,6 +114,13 @@ module Templating {
* that is, without being enclosed in a string literal or similar. * that is, without being enclosed in a string literal or similar.
*/ */
predicate isInPlainCodeContext() { this = any(GeneratedCodeExpr e).getPlaceholderTag() } predicate isInPlainCodeContext() { this = any(GeneratedCodeExpr e).getPlaceholderTag() }
/**
* Gets the innermost JavaScript expression containing this template tag, if any.
*/
Expr getEnclosingExpr() {
expr_contains_template_tag_location(result, getLocation())
}
} }
/** /**

View File

@@ -417,11 +417,9 @@ case @expr.kind of
@e4x_xml_attribute_selector = @e4x_xml_static_attribute_selector | @e4x_xml_dynamic_attribute_selector; @e4x_xml_attribute_selector = @e4x_xml_static_attribute_selector | @e4x_xml_dynamic_attribute_selector;
@e4x_xml_qualident = @e4x_xml_static_qualident | @e4x_xml_dynamic_qualident; @e4x_xml_qualident = @e4x_xml_static_qualident | @e4x_xml_dynamic_qualident;
generated_code_expr_info( expr_contains_template_tag_location(
unique int expr: @generated_code_expr ref, int expr: @expr ref,
varchar(900) openingDelimiter: string ref, int location: @location ref
varchar(900) closingDelimiter: string ref,
varchar(900) body: string ref
); );
@template_placeholder_tag_parent = @xmlelement | @xmlattribute | @file; @template_placeholder_tag_parent = @xmlelement | @xmlattribute | @file;