Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2ba98da

Browse files
committed
JS: Only extract local vars in TemplateTopLevel
Angular template expressions cannot refer to global variables, any unqualified identifier is a reference to a property provided by the component. We extract them as implicitly declared local variables which the QL model can then connect with data flow steps.
1 parent faad466 commit 2ba98da

13 files changed

Lines changed: 93 additions & 36 deletions

File tree

javascript/extractor/src/com/semmle/js/extractor/ASTExtractor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,10 @@ public Label visit(Program nd, Context c) {
704704
+ locationManager.getStartLine()
705705
+ ","
706706
+ locationManager.getStartColumn());
707-
scopeManager.enterScope(ScopeKind.module, moduleScopeKey, toplevelLabel);
707+
Scope moduleScope = scopeManager.enterScope(ScopeKind.module, moduleScopeKey, toplevelLabel);
708+
if (sourceType.hasNoGlobalScope()) {
709+
scopeManager.setImplicitVariableScope(moduleScope);
710+
}
708711
scopeManager.addVariables(
709712
sourceType.getPredefinedLocals(platform, locationManager.getSourceFileExtension()));
710713
trapwriter.addTuple("is_module", toplevelLabel);

javascript/extractor/src/com/semmle/js/extractor/ExtractorConfig.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ public boolean hasLocalScope() {
113113
return this != SCRIPT;
114114
}
115115

116+
/**
117+
* Returns true if this source type cannot access the global scope directly, and undeclared
118+
* variables are implicitly declared in its local scope. Implies {@link #hasLocalScope()}.
119+
*/
120+
public boolean hasNoGlobalScope() {
121+
return this == ANGULAR_TEMPLATE;
122+
}
123+
116124
/** Returns true if this source is implicitly in strict mode. */
117125
public boolean isStrictMode() {
118126
return this == MODULE;

javascript/extractor/src/com/semmle/js/extractor/HTMLExtractor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public void handleElement(Element elt, HtmlPopulator.Context context) {
118118
}
119119
}
120120
extractSnippet(
121-
TopLevelKind.eventHandler,
121+
TopLevelKind.angularTemplate,
122122
config.withSourceType(SourceType.ANGULAR_TEMPLATE),
123123
scopeManager,
124124
textualExtractor,

javascript/extractor/src/com/semmle/js/extractor/ScopeManager.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,21 @@ public Label lookupNamespace(String name) {
102102
private final Scope toplevelScope;
103103
private final ECMAVersion ecmaVersion;
104104
private final Set<String> implicitGlobals = new LinkedHashSet<String>();
105+
private Scope implicitVariableScope;
105106

106107
public ScopeManager(TrapWriter trapWriter, ECMAVersion ecmaVersion) {
107108
this.trapWriter = trapWriter;
108109
this.toplevelScope = enterScope(ScopeKind.global, trapWriter.globalID("global_scope"), null);
109110
this.ecmaVersion = ecmaVersion;
111+
this.implicitVariableScope = toplevelScope;
112+
}
113+
114+
/**
115+
* Sets the scope in which to declare variables that are referenced without
116+
* being declared. This defaults to the global scope.
117+
*/
118+
public void setImplicitVariableScope(Scope implicitVariableScope) {
119+
this.implicitVariableScope = implicitVariableScope;
110120
}
111121

112122
/**
@@ -193,12 +203,12 @@ public Scope getToplevelScope() {
193203

194204
/**
195205
* Get the label for a given variable in the current scope; if it cannot be found, add it to the
196-
* global scope.
206+
* implicit variable scope (usually the global scope).
197207
*/
198208
public Label getVarKey(String name) {
199209
Label lbl = curScope.lookupVariable(name);
200210
if (lbl == null) {
201-
lbl = addVariable(name, toplevelScope);
211+
lbl = addVariable(name, implicitVariableScope);
202212
implicitGlobals.add(name);
203213
}
204214
return lbl;

javascript/extractor/src/com/semmle/js/extractor/TopLevelKind.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ public enum TopLevelKind {
77
script(0),
88
inlineScript(1),
99
eventHandler(2),
10-
javascriptUrl(3);
10+
javascriptUrl(3),
11+
angularTemplate(4);
1112

1213
private int value;
1314

javascript/ql/src/semmle/javascript/AST.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ class InlineScript extends @inline_script, Script { }
300300
* ```
301301
*/
302302
class CodeInAttribute extends TopLevel {
303-
CodeInAttribute() { this instanceof @event_handler or this instanceof @javascript_url }
303+
CodeInAttribute() { this instanceof @event_handler or this instanceof @javascript_url or this instanceof @angular_template_toplevel }
304304
}
305305

306306
/**

javascript/ql/src/semmle/javascript/dataflow/Sources.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,8 @@ module SourceNode {
309309
astNode instanceof ImportSpecifier or
310310
astNode instanceof ImportMetaExpr or
311311
astNode instanceof TaggedTemplateExpr or
312-
astNode instanceof Angular2::PipeRefExpr
312+
astNode instanceof Angular2::PipeRefExpr or
313+
astNode instanceof Angular2::TemplateVarRefExpr
313314
)
314315
or
315316
DataFlow::parameterNode(this, _)

javascript/ql/src/semmle/javascript/dataflow/internal/VariableTypeInference.qll

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,11 @@ class AnalyzedNegativeConditionGuard extends AnalyzedRefinement {
307307
}
308308
}
309309

310+
/** Holds if `v` is a variable in an Angular template. */
311+
private predicate isAngularTemplateVariable(LocalVariable v) {
312+
v = any(Angular2::TemplateTopLevel tl).getScope().getAVariable()
313+
}
314+
310315
/**
311316
* Gets the abstract value representing the initial value of variable `v`.
312317
*
@@ -325,7 +330,10 @@ private AbstractValue getImplicitInitValue(LocalVariable v) {
325330
then
326331
// model hoisting
327332
result = TAbstractFunction(getAFunDecl(v))
328-
else result = TAbstractUndefined()
333+
else
334+
if isAngularTemplateVariable(v)
335+
then result = TIndefiniteAbstractValue("heap")
336+
else result = TAbstractUndefined()
329337
}
330338

331339
/**

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

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,33 @@ module Angular2 {
241241
}
242242
}
243243

244+
/**
245+
* A reference to a variable in a template expression, corresponding
246+
* to a property on the component class.
247+
*/
248+
class TemplateVarRefExpr extends Expr {
249+
TemplateVarRefExpr() {
250+
this = any(TemplateTopLevel tl).getScope().getAVariable().getAnAccess()
251+
}
252+
}
253+
254+
/** The top-level containing an Angular expression. */
255+
class TemplateTopLevel extends TopLevel, @angular_template_toplevel {
256+
/** Gets the expression in this top-level. */
257+
Expr getExpression() {
258+
result = getChildStmt(0).(ExprStmt).getExpr()
259+
}
260+
261+
/** Gets the data flow node representing the initialization of the given variable in this scope. */
262+
DataFlow::Node getVariableInit(string name) {
263+
result = DataFlow::ssaDefinitionNode(SSA::implicitInit(getScope().getVariable(name)))
264+
}
265+
266+
DataFlow::SourceNode getAVariableUse(string name) {
267+
result = getScope().getVariable(name).getAnAccess().flow()
268+
}
269+
}
270+
244271
/** The RHS of a `templateUrl` property, seen as a path expression. */
245272
private class TemplateUrlPath extends PathExpr {
246273
TemplateUrlPath() {
@@ -264,18 +291,8 @@ module Angular2 {
264291
attrib.getName().matches("*ng%")
265292
}
266293

267-
/**
268-
* Gets a global variable access to `name` within the given attribute.
269-
*/
270-
pragma[noinline]
271-
private GlobalVarAccess getAGlobalVarAccessInAttribute(CodeInAttribute code, string name) {
272-
exists(ComponentClass cls) and // do not materialize for non-Angular codebases
273-
result.getTopLevel() = code and
274-
result.getName() = name
275-
}
276-
277294
private DataFlow::Node getAttributeValueAsNode(HTML::Attribute attrib) {
278-
result = attrib.getCodeInAttribute().getChildStmt(0).(ExprStmt).getExpr().flow()
295+
result = attrib.getCodeInAttribute().(TemplateTopLevel).getExpression().flow()
279296
}
280297

281298
/**
@@ -361,11 +378,7 @@ module Angular2 {
361378
* Gets an access to the variable `name` in the template body.
362379
*/
363380
DataFlow::Node getATemplateVarAccess(string name) {
364-
exists(HTML::Attribute attrib |
365-
attrib = getATemplateElement().getAnAttribute() and
366-
isAngularExpressionAttribute(attrib) and
367-
result = getAGlobalVarAccessInAttribute(attrib.getCodeInAttribute(), name).flow()
368-
)
381+
result = getATemplateElement().getAnAttribute().getCodeInAttribute().(TemplateTopLevel).getAVariableUse(name)
369382
}
370383
}
371384

@@ -450,11 +463,7 @@ module Angular2 {
450463

451464
/** Gets a reference to the iterator variable. */
452465
DataFlow::Node getAnIteratorAccess() {
453-
exists(HTML::Attribute attrib |
454-
attrib = getAnElementInScope().getAnAttribute() and
455-
isAngularExpressionAttribute(attrib) and
456-
result = getAGlobalVarAccessInAttribute(attrib.getCodeInAttribute(), getIteratorName()).flow()
457-
)
466+
result = getAnElementInScope().getAnAttribute().getCodeInAttribute().(TemplateTopLevel).getAVariableUse(getIteratorName())
458467
}
459468
}
460469

@@ -474,4 +483,13 @@ module Angular2 {
474483
succ = attrib.getAnIteratorAccess()
475484
}
476485
}
486+
487+
private class AnyCastStep extends TaintTracking::AdditionalTaintStep, DataFlow::CallNode {
488+
AnyCastStep() { this = any(TemplateTopLevel tl).getAVariableUse("$any").getACall() }
489+
490+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
491+
pred = getArgument(0) and
492+
succ = this
493+
}
494+
}
477495
}

javascript/ql/src/semmlecode.javascript.dbscheme

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ case @toplevel.kind of
124124
0 = @script
125125
| 1 = @inline_script
126126
| 2 = @event_handler
127-
| 3 = @javascript_url;
127+
| 3 = @javascript_url
128+
| 4 = @angular_template_toplevel;
128129

129130
is_module (int tl: @toplevel ref);
130131
is_nodejs (int tl: @toplevel ref);

0 commit comments

Comments
 (0)