convert the AngularJS model to use DataFlow nodes

This commit is contained in:
Erik Krogh Kristensen
2022-03-30 21:34:34 +02:00
committed by erik-krogh
parent 9bea110d24
commit aa9261f1b1
23 changed files with 260 additions and 198 deletions

View File

@@ -167,7 +167,7 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
/**
* Holds if this expression may refer to the initial value of parameter `p`.
*/
predicate mayReferToParameter(Parameter p) { this.flow().mayReferToParameter(p) }
predicate mayReferToParameter(Parameter p) { DataFlow::parameterNode(p).flowsToExpr(this) }
/**
* Gets the static type of this expression, as determined by the TypeScript type system.

View File

@@ -139,9 +139,12 @@ module DataFlow {
}
/**
* DEPRECATED: Use `DataFlow::ParameterNode::flowsTo()` instead.
* Holds if this expression may refer to the initial value of parameter `p`.
*/
predicate mayReferToParameter(Parameter p) { parameterNode(p).(SourceNode).flowsTo(this) }
deprecated predicate mayReferToParameter(Parameter p) {
parameterNode(p).(SourceNode).flowsTo(this)
}
/**
* Holds if this element is at the specified location.

View File

@@ -472,6 +472,12 @@ class FunctionNode extends DataFlow::ValueNode, DataFlow::SourceNode {
/** Gets a parameter of this function. */
ParameterNode getAParameter() { result = this.getParameter(_) }
/** Gets the parameter named `name` of this function, if any. */
DataFlow::ParameterNode getParameterByName(string name) {
result = getAParameter() and
result.getName() = name
}
/** Gets the number of parameters declared on this function. */
int getNumParameter() { result = count(astNode.getAParameter()) }

View File

@@ -105,8 +105,8 @@ class AngularModule extends TAngularModule {
/**
* Get the array of dependencies from this module's definition.
*/
ArrayExpr getDependencyArray() {
this.getADefinition().getArgument(1).getALocalSource().asExpr() = result
DataFlow::ArrayCreationNode getDependencyArray() {
this.getADefinition().getArgument(1).getALocalSource() = result
}
/**
@@ -160,14 +160,10 @@ class ModuleApiCall extends DataFlow::CallNode {
string getMethodName() { result = methodName }
}
class ModuleApiCallDependencyInjection extends DependencyInjection {
ModuleApiCall call;
class ModuleApiCallDependencyInjection extends DependencyInjection instanceof ModuleApiCall {
string methodName;
ModuleApiCallDependencyInjection() {
this = call and
methodName = call.getMethodName()
}
ModuleApiCallDependencyInjection() { methodName = super.getMethodName() }
/**
* Gets the argument position for this method call that expects an injectable function.
@@ -183,7 +179,7 @@ class ModuleApiCallDependencyInjection extends DependencyInjection {
}
override DataFlow::Node getAnInjectableFunction() {
result = call.getArgument(this.injectableArgPos())
result = super.getArgument(this.injectableArgPos())
}
}
@@ -266,10 +262,10 @@ abstract class CustomDirective extends DirectiveInstance {
DataFlow::SourceNode getMember(string name) { result.flowsTo(this.getMemberInit(name)) }
/** Gets the method `name` of this directive. */
Function getMethod(string name) { DataFlow::valueNode(result) = this.getMember(name) }
DataFlow::FunctionNode getMethod(string name) { result = this.getMember(name) }
/** Gets a link function of this directive. */
abstract Function getALinkFunction();
abstract DataFlow::FunctionNode getALinkFunction();
/** Holds if this directive's properties are bound to the controller. */
abstract predicate bindsToController();
@@ -332,10 +328,10 @@ class GeneralDirective extends CustomDirective, MkCustomDirective {
/** Gets a node that contributes to the return value of the factory function. */
override DataFlow::SourceNode getAnInstantiation() {
exists(Function factory, InjectableFunction f |
exists(DataFlow::FunctionNode factory, InjectableFunction f |
f = definition.getAFactoryFunction() and
factory = f.asFunction() and
result.flowsToExpr(factory.getAReturnedExpr())
result.flowsTo(factory.getAReturn())
)
}
@@ -344,7 +340,7 @@ class GeneralDirective extends CustomDirective, MkCustomDirective {
}
/** Gets the compile function of this directive, if any. */
Function getCompileFunction() { result = this.getMethod("compile") }
DataFlow::FunctionNode getCompileFunction() { result = this.getMethod("compile") }
/**
* Gets a pre/post link function of this directive defined on its definition object.
@@ -365,9 +361,8 @@ class GeneralDirective extends CustomDirective, MkCustomDirective {
result = this.getMember("link").getAPropertySource(kind)
or
// { compile: function() { ... return link; } }
exists(Expr compileReturn, DataFlow::SourceNode compileReturnSrc |
compileReturn = this.getCompileFunction().getAReturnedExpr() and
compileReturnSrc.flowsToExpr(compileReturn)
exists(DataFlow::SourceNode compileReturnSrc |
compileReturnSrc.flowsTo(this.getCompileFunction().getAReturn())
|
// link = function postLink() { ... }
kind = "post" and
@@ -380,12 +375,12 @@ class GeneralDirective extends CustomDirective, MkCustomDirective {
}
/** Gets the pre-link function of this directive. */
Function getPreLinkFunction() { result = this.getLinkFunction("pre").getAstNode() }
DataFlow::FunctionNode getPreLinkFunction() { result = this.getLinkFunction("pre") }
/** Gets the post-link function of this directive. */
Function getPostLinkFunction() { result = this.getLinkFunction("post").getAstNode() }
DataFlow::FunctionNode getPostLinkFunction() { result = this.getLinkFunction("post") }
override Function getALinkFunction() { result = this.getLinkFunction(_).getAstNode() }
override DataFlow::FunctionNode getALinkFunction() { result = this.getLinkFunction(_) }
override predicate bindsToController() {
this.getMemberInit("bindToController").asExpr().mayHaveBooleanValue(true)
@@ -412,7 +407,7 @@ class ComponentDirective extends CustomDirective, MkCustomComponent {
comp.getConfig().hasPropertyWrite(name, result)
}
override Function getALinkFunction() { none() }
override DataFlow::FunctionNode getALinkFunction() { none() }
override predicate bindsToController() { none() }
@@ -561,13 +556,12 @@ class DirectiveTargetName extends string {
*
* See https://docs.angularjs.org/api/ng/service/$location for details.
*/
private class LocationFlowSource extends RemoteFlowSource {
private class LocationFlowSource extends RemoteFlowSource instanceof DataFlow::MethodCallNode {
LocationFlowSource() {
exists(ServiceReference service, MethodCallExpr mce, string m, int n |
exists(ServiceReference service, string m, int n |
service.getName() = "$location" and
this.asExpr() = mce and
mce = service.getAMethodCall(m) and
n = mce.getNumArgument()
this = service.getAMethodCall(m) and
n = super.getNumArgument()
|
m = "search" and n < 2
or
@@ -605,7 +599,7 @@ private class JQLiteObject extends JQuery::ObjectSource::Range {
JQLiteObject() {
this = angular().getAMemberCall("element")
or
exists(Parameter param | this = DataFlow::parameterNode(param) |
exists(DataFlow::ParameterNode param | this = param |
// element parameters to user-functions invoked by AngularJS
param = any(LinkFunction link).getElementParameter()
or
@@ -613,13 +607,13 @@ private class JQLiteObject extends JQuery::ObjectSource::Range {
param = d.getCompileFunction().getParameter(0)
or
exists(DataFlow::FunctionNode f, int i |
f.flowsToExpr(d.getCompileFunction().getAReturnedExpr()) and i = 1
f.flowsTo(d.getCompileFunction().getAReturn()) and i = 1
or
f = d.getMember("template") and i = 0
or
f = d.getMember("templateUrl") and i = 0
|
param = f.getAstNode().(Function).getParameter(i)
param = f.getParameter(i)
)
)
)
@@ -631,99 +625,111 @@ private class JQLiteObject extends JQuery::ObjectSource::Range {
}
/**
* DEPRECATED: Use `AngularJSCallNode` instead.
* A call to an AngularJS function.
*
* Used for exposing behavior that is similar to the behavior of other libraries.
*/
abstract class AngularJSCall extends CallExpr {
deprecated class AngularJSCall extends CallExpr {
AngularJSCallNode node;
AngularJSCall() { this.flow() = node }
/** Holds if `e` is an argument that this call interprets as HTML. */
deprecated predicate interpretsArgumentAsHtml(Expr e) { node.interpretsArgumentAsHtml(e.flow()) }
/** Holds if `e` is an argument that this call stores globally, e.g. in a cookie. */
deprecated predicate storesArgumentGlobally(Expr e) { node.storesArgumentGlobally(e.flow()) }
/** Holds if `e` is an argument that this call interprets as code. */
deprecated predicate interpretsArgumentAsCode(Expr e) { node.interpretsArgumentAsCode(e.flow()) }
}
/**
* A call to an AngularJS function.
*
* Used for exposing behavior that is similar to the behavior of other libraries.
*/
abstract class AngularJSCallNode extends DataFlow::CallNode {
/**
* Holds if `e` is an argument that this call interprets as HTML.
*/
abstract predicate interpretsArgumentAsHtml(Expr e);
abstract predicate interpretsArgumentAsHtml(DataFlow::Node e);
/**
* Holds if `e` is an argument that this call stores globally, e.g. in a cookie.
*/
abstract predicate storesArgumentGlobally(Expr e);
abstract predicate storesArgumentGlobally(DataFlow::Node e);
/**
* Holds if `e` is an argument that this call interprets as code.
*/
abstract predicate interpretsArgumentAsCode(Expr e);
abstract predicate interpretsArgumentAsCode(DataFlow::Node e);
}
/**
* A call to a method on the AngularJS object itself.
*/
private class AngularMethodCall extends AngularJSCall {
MethodCallExpr mce;
private class AngularMethodCall extends AngularJSCallNode {
AngularMethodCall() { this = angular().getAMemberCall(_) }
AngularMethodCall() {
mce = angular().getAMemberCall(_).asExpr() and
mce = this
override predicate interpretsArgumentAsHtml(DataFlow::Node e) {
super.getCalleeName() = "element" and
e = super.getArgument(0)
}
override predicate interpretsArgumentAsHtml(Expr e) {
mce.getMethodName() = "element" and
e = mce.getArgument(0)
}
override predicate storesArgumentGlobally(DataFlow::Node e) { none() }
override predicate storesArgumentGlobally(Expr e) { none() }
override predicate interpretsArgumentAsCode(Expr e) { none() }
override predicate interpretsArgumentAsCode(DataFlow::Node e) { none() }
}
/**
* A call to a builtin service or one of its methods.
*/
private class BuiltinServiceCall extends AngularJSCall {
CallExpr call;
private class BuiltinServiceCall extends AngularJSCallNode {
BuiltinServiceCall() {
exists(BuiltinServiceReference service |
service.getAMethodCall(_) = this or
service.getACall() = this
|
call = this
)
}
override predicate interpretsArgumentAsHtml(Expr e) {
override predicate interpretsArgumentAsHtml(DataFlow::Node e) {
exists(ServiceReference service, string methodName |
service.getName() = "$sce" and
call = service.getAMethodCall(methodName)
this = service.getAMethodCall(methodName)
|
// specialized call
(methodName = "trustAsHtml" or methodName = "trustAsCss") and
e = call.getArgument(0)
e = this.getArgument(0)
or
// generic call with enum argument
methodName = "trustAs" and
exists(DataFlow::PropRead prn |
prn.asExpr() = call.getArgument(0) and
prn = this.getArgument(0) and
(prn = service.getAPropertyAccess("HTML") or prn = service.getAPropertyAccess("CSS")) and
e = call.getArgument(1)
e = this.getArgument(1)
)
)
}
override predicate storesArgumentGlobally(Expr e) {
override predicate storesArgumentGlobally(DataFlow::Node e) {
exists(ServiceReference service, string serviceName, string methodName |
service.getName() = serviceName and
call = service.getAMethodCall(methodName)
this = service.getAMethodCall(methodName)
|
// AngularJS caches (only available during runtime, so similar to sessionStorage)
(serviceName = "$cacheFactory" or serviceName = "$templateCache") and
methodName = "put" and
e = call.getArgument(1)
e = this.getArgument(1)
or
serviceName = "$cookies" and
(methodName = "put" or methodName = "putObject") and
e = call.getArgument(1)
e = this.getArgument(1)
)
}
override predicate interpretsArgumentAsCode(Expr e) {
override predicate interpretsArgumentAsCode(DataFlow::Node e) {
exists(ScopeServiceReference scope, string methodName |
methodName =
[
@@ -731,22 +737,21 @@ private class BuiltinServiceCall extends AngularJSCall {
"$watchGroup"
]
|
call = scope.getAMethodCall(methodName) and
e = call.getArgument(0)
this = scope.getAMethodCall(methodName) and
e = this.getArgument(0)
)
or
exists(ServiceReference service | service.getName() = ["$compile", "$parse", "$interpolate"] |
call = service.getACall() and
e = call.getArgument(0)
this = service.getACall() and
e = this.getArgument(0)
)
or
exists(ServiceReference service, CallExpr filterInvocation |
exists(ServiceReference service |
// `$filter('orderBy')(collection, expression)`
service.getName() = "$filter" and
call = service.getACall() and
call.getArgument(0).mayHaveStringValue("orderBy") and
filterInvocation.getCallee() = call and
e = filterInvocation.getArgument(1)
this = service.getACall() and
this.getArgument(0).mayHaveStringValue("orderBy") and
e = this.getACall().getArgument(1)
)
}
}
@@ -754,33 +759,33 @@ private class BuiltinServiceCall extends AngularJSCall {
/**
* A link-function used in a custom AngularJS directive.
*/
class LinkFunction extends Function {
class LinkFunction extends DataFlow::FunctionNode {
LinkFunction() { this = any(GeneralDirective d).getALinkFunction() }
/**
* Gets the scope parameter of this function.
*/
Parameter getScopeParameter() { result = this.getParameter(0) }
DataFlow::ParameterNode getScopeParameter() { result = this.getParameter(0) }
/**
* Gets the element parameter of this function (contains a jqLite-wrapped DOM element).
*/
Parameter getElementParameter() { result = this.getParameter(1) }
DataFlow::ParameterNode getElementParameter() { result = this.getParameter(1) }
/**
* Gets the attributes parameter of this function.
*/
Parameter getAttributesParameter() { result = this.getParameter(2) }
DataFlow::ParameterNode getAttributesParameter() { result = this.getParameter(2) }
/**
* Gets the controller parameter of this function.
*/
Parameter getControllerParameter() { result = this.getParameter(3) }
DataFlow::ParameterNode getControllerParameter() { result = this.getParameter(3) }
/**
* Gets the transclude-function parameter of this function.
*/
Parameter getTranscludeFnParameter() { result = this.getParameter(4) }
DataFlow::ParameterNode getTranscludeFnParameter() { result = this.getParameter(4) }
}
/**
@@ -807,29 +812,29 @@ class AngularScope extends TAngularScope {
/**
* Gets an access to this scope object.
*/
Expr getAnAccess() {
DataFlow::Node getAnAccess() {
exists(CustomDirective d | this = d.getAScope() |
exists(Parameter p |
exists(DataFlow::ParameterNode p |
p = d.getController().getDependencyParameter("$scope") or
p = d.getALinkFunction().getParameter(0)
|
result.mayReferToParameter(p)
p.flowsTo(result)
)
or
exists(DataFlow::ThisNode dis |
dis.flowsToExpr(result) and
dis.getBinder().getAstNode() = d.getController().asFunction() and
dis.flowsTo(result) and
dis.getBinder() = d.getController().asFunction() and
d.bindsToController()
)
or
d.hasIsolateScope() and result = d.getMember("scope").asExpr()
d.hasIsolateScope() and result = d.getMember("scope")
)
or
exists(DirectiveController c, DOM::ElementDefinition elem, Parameter p |
exists(DirectiveController c, DOM::ElementDefinition elem, DataFlow::ParameterNode p |
c.boundTo(elem) and
this.mayApplyTo(elem) and
p = c.getFactoryFunction().getDependencyParameter("$scope") and
result.mayReferToParameter(p)
p.flowsTo(result)
)
}
@@ -1022,15 +1027,12 @@ private class RouteInstantiatedController extends Controller {
/**
* Dataflow for the arguments of AngularJS dependency-injected functions.
*/
private class DependencyInjectedArgumentInitializer extends DataFlow::AnalyzedNode {
private class DependencyInjectedArgumentInitializer extends DataFlow::AnalyzedNode instanceof DataFlow::ParameterNode {
DataFlow::AnalyzedNode service;
DependencyInjectedArgumentInitializer() {
exists(
AngularJS::InjectableFunction f, Parameter param, AngularJS::CustomServiceDefinition def
|
this = DataFlow::parameterNode(param) and
def.getServiceReference() = f.getAResolvedDependency(param) and
exists(AngularJS::InjectableFunction f, AngularJS::CustomServiceDefinition def |
def.getServiceReference() = f.getAResolvedDependency(this) and
service = def.getAService()
)
}

View File

@@ -41,33 +41,35 @@ abstract class DependencyInjection extends DataFlow::ValueNode {
*/
abstract class InjectableFunction extends DataFlow::ValueNode {
/** Gets the parameter corresponding to dependency `name`. */
abstract Parameter getDependencyParameter(string name);
abstract DataFlow::ParameterNode getDependencyParameter(string name);
/**
* Gets the `i`th dependency declaration, which is also named `name`.
*/
abstract AstNode getDependencyDeclaration(int i, string name);
abstract DataFlow::Node getDependencyDeclaration(int i, string name);
/**
* Gets an ASTNode for the `name` dependency declaration.
* Gets a node for the `name` dependency declaration.
*/
AstNode getADependencyDeclaration(string name) { result = getDependencyDeclaration(_, name) }
DataFlow::Node getADependencyDeclaration(string name) {
result = getDependencyDeclaration(_, name)
}
/**
* Gets the ASTNode for the `i`th dependency declaration.
* Gets the dataflow node for the `i`th dependency declaration.
*/
AstNode getDependencyDeclaration(int i) { result = getDependencyDeclaration(i, _) }
DataFlow::Node getDependencyDeclaration(int i) { result = getDependencyDeclaration(i, _) }
/** Gets the function underlying this injectable function. */
abstract Function asFunction();
abstract DataFlow::FunctionNode asFunction();
/** Gets a location where this function is explicitly dependency injected. */
abstract AstNode getAnExplicitDependencyInjection();
/** Gets a node where this function is explicitly dependency injected. */
abstract DataFlow::Node getAnExplicitDependencyInjection();
/**
* Gets a service corresponding to the dependency-injected `parameter`.
*/
ServiceReference getAResolvedDependency(Parameter parameter) {
ServiceReference getAResolvedDependency(DataFlow::ParameterNode parameter) {
exists(string name, InjectableFunctionServiceRequest request |
this = request.getAnInjectedFunction() and
parameter = getDependencyParameter(name) and
@@ -79,7 +81,7 @@ abstract class InjectableFunction extends DataFlow::ValueNode {
* Gets a Custom service corresponding to the dependency-injected `parameter`.
* (this is a convenience variant of `getAResolvedDependency`)
*/
DataFlow::Node getCustomServiceDependency(Parameter parameter) {
DataFlow::Node getCustomServiceDependency(DataFlow::ParameterNode parameter) {
exists(CustomServiceDefinition custom |
custom.getServiceReference() = getAResolvedDependency(parameter) and
result = custom.getAService()
@@ -91,100 +93,88 @@ abstract class InjectableFunction extends DataFlow::ValueNode {
* An injectable function that does not explicitly list its dependencies,
* instead relying on implicit matching by parameter names.
*/
private class FunctionWithImplicitDependencyAnnotation extends InjectableFunction {
override Function astNode;
private class FunctionWithImplicitDependencyAnnotation extends InjectableFunction instanceof DataFlow::FunctionNode {
FunctionWithImplicitDependencyAnnotation() {
this.(DataFlow::FunctionNode).flowsTo(any(DependencyInjection d).getAnInjectableFunction()) and
not exists(getAPropertyDependencyInjection(astNode))
not exists(getAPropertyDependencyInjection(this))
}
override Parameter getDependencyParameter(string name) {
result = astNode.getParameterByName(name)
override DataFlow::ParameterNode getDependencyParameter(string name) {
result = super.getParameterByName(name)
}
override Parameter getDependencyDeclaration(int i, string name) {
override DataFlow::ParameterNode getDependencyDeclaration(int i, string name) {
result.getName() = name and
result = astNode.getParameter(i)
result = super.getParameter(i)
}
override Function asFunction() { result = astNode }
override DataFlow::FunctionNode asFunction() { result = this }
override AstNode getAnExplicitDependencyInjection() { none() }
override DataFlow::Node getAnExplicitDependencyInjection() { none() }
}
private DataFlow::PropWrite getAPropertyDependencyInjection(Function function) {
exists(DataFlow::FunctionNode ltf |
ltf.getAstNode() = function and
result = ltf.getAPropertyWrite("$inject")
)
private DataFlow::PropWrite getAPropertyDependencyInjection(DataFlow::FunctionNode function) {
result = function.getAPropertyWrite("$inject")
}
/**
* An injectable function with an `$inject` property that lists its
* dependencies.
*/
private class FunctionWithInjectProperty extends InjectableFunction {
override Function astNode;
private class FunctionWithInjectProperty extends InjectableFunction instanceof DataFlow::FunctionNode {
DataFlow::ArrayCreationNode dependencies;
FunctionWithInjectProperty() {
(
this.(DataFlow::FunctionNode).flowsTo(any(DependencyInjection d).getAnInjectableFunction()) or
exists(FunctionWithExplicitDependencyAnnotation f | f.asFunction() = astNode)
exists(FunctionWithExplicitDependencyAnnotation f | f.asFunction() = this)
) and
exists(DataFlow::PropWrite pwn |
pwn = getAPropertyDependencyInjection(astNode) and
pwn = getAPropertyDependencyInjection(this) and
pwn.getRhs().getALocalSource() = dependencies
)
}
override Parameter getDependencyParameter(string name) {
exists(int i | exists(getDependencyDeclaration(i, name)) | result = astNode.getParameter(i))
override DataFlow::ParameterNode getDependencyParameter(string name) {
exists(int i | exists(getDependencyDeclaration(i, name)) | result = super.getParameter(i))
}
override AstNode getDependencyDeclaration(int i, string name) {
exists(DataFlow::ValueNode decl |
decl = dependencies.getElement(i) and
decl.mayHaveStringValue(name) and
result = decl.getAstNode()
)
override DataFlow::Node getDependencyDeclaration(int i, string name) {
result = dependencies.getElement(i) and
result.mayHaveStringValue(name)
}
override Function asFunction() { result = astNode }
override DataFlow::FunctionNode asFunction() { result = this }
override AstNode getAnExplicitDependencyInjection() {
result = getAPropertyDependencyInjection(astNode).getAstNode()
override DataFlow::Node getAnExplicitDependencyInjection() {
result = getAPropertyDependencyInjection(this)
}
}
/**
* An injectable function embedded in an array of dependencies.
*/
private class FunctionWithExplicitDependencyAnnotation extends InjectableFunction {
private class FunctionWithExplicitDependencyAnnotation extends InjectableFunction instanceof DataFlow::ArrayCreationNode {
DataFlow::FunctionNode function;
override ArrayExpr astNode;
FunctionWithExplicitDependencyAnnotation() {
this.(DataFlow::SourceNode).flowsTo(any(DependencyInjection d).getAnInjectableFunction()) and
function.flowsToExpr(astNode.getElement(astNode.getSize() - 1))
function.flowsTo(super.getElement(super.getSize() - 1))
}
override Parameter getDependencyParameter(string name) {
exists(int i | astNode.getElement(i).mayHaveStringValue(name) |
result = asFunction().getParameter(i)
)
override DataFlow::ParameterNode getDependencyParameter(string name) {
exists(int i | super.getElement(i).mayHaveStringValue(name) | result = function.getParameter(i))
}
override AstNode getDependencyDeclaration(int i, string name) {
result = astNode.getElement(i) and
result.(Expr).mayHaveStringValue(name)
override DataFlow::Node getDependencyDeclaration(int i, string name) {
result = super.getElement(i) and
result.mayHaveStringValue(name)
}
override Function asFunction() { result = function.getAstNode() }
override DataFlow::FunctionNode asFunction() { result = function }
override AstNode getAnExplicitDependencyInjection() {
result = astNode or
override DataFlow::Node getAnExplicitDependencyInjection() {
result = this or
result = function.(InjectableFunction).getAnExplicitDependencyInjection()
}
}

View File

@@ -38,25 +38,25 @@ abstract class ServiceReference extends TServiceReference {
* Gets a data flow node that may refer to this service.
*/
DataFlow::SourceNode getAReference() {
result = DataFlow::parameterNode(any(ServiceRequest request).getDependencyParameter(this))
result = any(ServiceRequestNode request).getDependencyParameter(this)
}
/**
* Gets an access to the referenced service.
*/
Expr getAnAccess() {
result.mayReferToParameter(any(ServiceRequest request).getDependencyParameter(this))
DataFlow::Node getAnAccess() {
any(ServiceRequestNode request).getDependencyParameter(this).flowsTo(result)
}
/**
* Gets a call that invokes the referenced service.
*/
CallExpr getACall() { result.getCallee() = getAnAccess() }
DataFlow::CallNode getACall() { result.getCalleeNode() = getAnAccess() }
/**
* Gets a method call that invokes method `methodName` on the referenced service.
*/
MethodCallExpr getAMethodCall(string methodName) {
DataFlow::MethodCallNode getAMethodCall(string methodName) {
result.getReceiver() = getAnAccess() and
result.getMethodName() = methodName
}
@@ -65,7 +65,7 @@ abstract class ServiceReference extends TServiceReference {
* Gets an access to property `propertyName` on the referenced service.
*/
DataFlow::PropRef getAPropertyAccess(string propertyName) {
result.getBase().asExpr() = getAnAccess() and
result.getBase() = getAnAccess() and
result.getPropertyName() = propertyName
}
@@ -93,7 +93,7 @@ class BuiltinServiceReference extends ServiceReference, MkBuiltinServiceReferenc
DataFlow::ParameterNode builtinServiceRef(string serviceName) {
exists(InjectableFunction f, BuiltinServiceReference service |
service.getName() = serviceName and
result = DataFlow::parameterNode(f.getDependencyParameter(serviceName))
result = f.getDependencyParameter(serviceName)
)
}
@@ -338,7 +338,7 @@ class FilterDefinition extends CustomSpecialServiceDefinition {
override DataFlow::SourceNode getAService() {
exists(InjectableFunction f |
f = factoryFunction.getALocalSource() and
result.flowsToExpr(f.asFunction().getAReturnedExpr())
result.flowsTo(f.asFunction().getAReturn())
)
}
@@ -428,7 +428,7 @@ class AnimationDefinition extends CustomSpecialServiceDefinition {
override DataFlow::SourceNode getAService() {
exists(InjectableFunction f |
f = factoryFunction.getALocalSource() and
result.flowsToExpr(f.asFunction().getAReturnedExpr())
result.flowsTo(f.asFunction().getAReturn())
)
}
@@ -446,22 +446,37 @@ BuiltinServiceReference getBuiltinServiceOfKind(string kind) {
}
/**
* DEPRECATED: Use `ServiceRequestNode` instead.
* A request for one or more AngularJS services.
*/
abstract class ServiceRequest extends Expr {
deprecated class ServiceRequest extends Expr {
ServiceRequestNode node;
ServiceRequest() { this.flow() = node }
/** Gets the parameter of this request into which `service` is injected. */
deprecated Parameter getDependencyParameter(ServiceReference service) {
result.flow() = node.getDependencyParameter(service)
}
}
/**
* A request for one or more AngularJS services.
*/
abstract class ServiceRequestNode extends DataFlow::Node {
/**
* Gets the parameter of this request into which `service` is injected.
*/
abstract Parameter getDependencyParameter(ServiceReference service);
abstract DataFlow::ParameterNode getDependencyParameter(ServiceReference service);
}
/**
* The request for a scope service in the form of the link-function of a directive.
*/
private class LinkFunctionWithScopeInjection extends ServiceRequest {
private class LinkFunctionWithScopeInjection extends ServiceRequestNode {
LinkFunctionWithScopeInjection() { this instanceof LinkFunction }
override Parameter getDependencyParameter(ServiceReference service) {
override DataFlow::ParameterNode getDependencyParameter(ServiceReference service) {
service instanceof ScopeServiceReference and
result = this.(LinkFunction).getScopeParameter()
}
@@ -470,10 +485,10 @@ private class LinkFunctionWithScopeInjection extends ServiceRequest {
/**
* A request for a service, in the form of a dependency-injected function.
*/
class InjectableFunctionServiceRequest extends ServiceRequest {
class InjectableFunctionServiceRequest extends ServiceRequestNode {
InjectableFunction injectedFunction;
InjectableFunctionServiceRequest() { injectedFunction.getAstNode() = this }
InjectableFunctionServiceRequest() { injectedFunction = this }
/**
* Gets the function of this request.
@@ -494,16 +509,16 @@ class InjectableFunctionServiceRequest extends ServiceRequest {
result.isInjectable()
}
override Parameter getDependencyParameter(ServiceReference service) {
override DataFlow::ParameterNode getDependencyParameter(ServiceReference service) {
service = injectedFunction.getAResolvedDependency(result)
}
}
private DataFlow::SourceNode getFactoryFunctionResult(RecipeDefinition def) {
exists(Function factoryFunction, InjectableFunction f |
exists(DataFlow::FunctionNode factoryFunction, InjectableFunction f |
f = def.getAFactoryFunction() and
factoryFunction = f.asFunction() and
result.flowsToExpr(factoryFunction.getAReturnedExpr())
result.flowsTo(factoryFunction.getAReturn())
)
}
@@ -562,7 +577,7 @@ class ServiceRecipeDefinition extends RecipeDefinition {
exists(InjectableFunction f |
f = getAFactoryFunction() and
result.getAstNode() = f.asFunction()
result = f.asFunction()
)
}
}
@@ -608,7 +623,7 @@ class ProviderRecipeDefinition extends RecipeDefinition {
exists(DataFlow::ThisNode thiz, InjectableFunction f |
f = getAFactoryFunction() and
thiz.getBinder().getFunction() = f.asFunction() and
thiz.getBinder() = f.asFunction() and
result = thiz.getAPropertySource("$get")
)
}

View File

@@ -67,8 +67,6 @@ module CleartextStorage {
* An expression stored by AngularJS.
*/
class AngularJSStorageSink extends Sink {
AngularJSStorageSink() {
any(AngularJS::AngularJSCall call).storesArgumentGlobally(this.asExpr())
}
AngularJSStorageSink() { any(AngularJS::AngularJSCallNode call).storesArgumentGlobally(this) }
}
}

View File

@@ -121,7 +121,7 @@ module ClientSideUrlRedirect {
// A redirection using the AngularJS `$location` service
exists(AngularJS::ServiceReference service |
service.getName() = "$location" and
this.asExpr() = service.getAMethodCall("url").getArgument(0)
this = service.getAMethodCall("url").getArgument(0)
) and
xss = false
}

View File

@@ -37,7 +37,7 @@ module CodeInjection {
*/
class AngularJSExpressionSink extends Sink, DataFlow::ValueNode {
AngularJSExpressionSink() {
any(AngularJS::AngularJSCall call).interpretsArgumentAsCode(this.asExpr())
any(AngularJS::AngularJSCallNode call).interpretsArgumentAsCode(this)
}
}

View File

@@ -31,7 +31,7 @@ module DomBasedXss {
)
or
// call to an Angular method that interprets its argument as HTML
any(AngularJS::AngularJSCall call).interpretsArgumentAsHtml(this.asExpr())
any(AngularJS::AngularJSCallNode call).interpretsArgumentAsHtml(this)
or
// call to a WinJS function that interprets its argument as HTML
exists(DataFlow::MethodCallNode mcn, string m |

View File

@@ -31,8 +31,8 @@ module XmlBomb {
/**
* An access to `document.location`, considered as a flow source for XML bomb vulnerabilities.
*/
class LocationAsSource extends Source, DataFlow::ValueNode {
LocationAsSource() { isLocation(astNode) }
class LocationAsSource extends Source {
LocationAsSource() { isLocationNode(this) }
}
/**

View File

@@ -31,8 +31,8 @@ module Xxe {
/**
* An access to `document.location`, considered as a flow source for XXE vulnerabilities.
*/
class LocationAsSource extends Source, DataFlow::ValueNode {
LocationAsSource() { isLocation(astNode) }
class LocationAsSource extends Source {
LocationAsSource() { isLocationNode(this) }
}
/**

View File

@@ -42,17 +42,19 @@ predicate isABuiltinEventName(string name) {
* Holds if user code emits or broadcasts an event named `name`.
*/
predicate isAUserDefinedEventName(string name) {
exists(string methodName, MethodCallExpr mce | methodName = "$emit" or methodName = "$broadcast" |
mce.getArgument(0).mayHaveStringValue(name) and
exists(string methodName, DataFlow::MethodCallNode mcn |
methodName = "$emit" or methodName = "$broadcast"
|
mcn.getArgument(0).mayHaveStringValue(name) and
(
// dataflow based scope resolution
mce = any(AngularJS::ScopeServiceReference scope).getAMethodCall(methodName)
mcn = any(AngularJS::ScopeServiceReference scope).getAMethodCall(methodName)
or
// heuristic scope resolution: assume parameters like `$scope` or `$rootScope` are AngularJS scope objects
exists(SimpleParameter param |
exists(DataFlow::ParameterNode param |
param.getName() = any(AngularJS::ScopeServiceReference scope).getName() and
mce.getReceiver().mayReferToParameter(param) and
mce.getMethodName() = methodName
param.getAMethodCall() = mcn and
mcn.getMethodName() = methodName
)
or
// a call in an AngularJS expression
@@ -64,7 +66,7 @@ predicate isAUserDefinedEventName(string name) {
)
}
from AngularJS::ScopeServiceReference scope, MethodCallExpr mce, string eventName
from AngularJS::ScopeServiceReference scope, DataFlow::MethodCallNode mce, string eventName
where
mce = scope.getAMethodCall("$on") and
mce.getArgument(0).mayHaveStringValue(eventName) and

View File

@@ -14,7 +14,7 @@
import javascript
from AngularJS::InjectableFunction f, SimpleParameter p, string msg
from AngularJS::InjectableFunction f, DataFlow::ParameterNode p, string msg
where
p = f.asFunction().getAParameter() and
(

View File

@@ -14,7 +14,7 @@
import javascript
from MethodCallExpr mce, AngularJS::BuiltinServiceReference service
from DataFlow::MethodCallNode mce, AngularJS::BuiltinServiceReference service
where
service.getName() = "$sceProvider" and
mce = service.getAMethodCall("enabled") and

View File

@@ -15,7 +15,7 @@
import javascript
from AngularJS::ServiceReference compile, SimpleParameter elem, CallExpr c
from AngularJS::ServiceReference compile, DataFlow::ParameterNode elem, DataFlow::CallNode c
where
compile.getName() = "$compile" and
elem =
@@ -24,7 +24,7 @@ where
.(AngularJS::LinkFunction)
.getElementParameter() and
c = compile.getACall() and
c.getArgument(0).mayReferToParameter(elem) and
elem.flowsTo(c.getArgument(0)) and
// don't flag $compile calls that specify a `maxPriority`
c.getNumArgument() < 3
select c, "This call to $compile may cause double compilation of '" + elem + "'."

View File

@@ -12,16 +12,17 @@
import javascript
import semmle.javascript.RestrictedLocations
predicate isRepeatedDependency(AngularJS::InjectableFunction f, string name, AstNode location) {
predicate isRepeatedDependency(AngularJS::InjectableFunction f, string name, DataFlow::Node node) {
exists(int i, int j |
i < j and
exists(f.getDependencyDeclaration(i, name)) and
location = f.getDependencyDeclaration(j, name)
node = f.getDependencyDeclaration(j, name)
)
}
from AngularJS::InjectableFunction f, AstNode node, string name
from AngularJS::InjectableFunction f, DataFlow::Node node, string name
where
isRepeatedDependency(f, name, node) and
not count(f.asFunction().getParameterByName(name)) > 1 // avoid duplicating reports from js/duplicate-parameter-name
select f.asFunction().(FirstLineOf), "This function has a duplicate dependency '$@'.", node, name
select f.asFunction().getFunction().(FirstLineOf), "This function has a duplicate dependency '$@'.",
node, name

View File

@@ -23,7 +23,7 @@ predicate isResourceUrlWhitelist(
) {
exists(AngularJS::ServiceReference service |
service.getName() = "$sceDelegateProvider" and
setupCall.asExpr() = service.getAMethodCall("resourceUrlWhitelist") and
setupCall = service.getAMethodCall("resourceUrlWhitelist") and
list.flowsTo(setupCall.getArgument(0))
)
}

View File

@@ -12,9 +12,9 @@
import javascript
import semmle.javascript.RestrictedLocations
from AngularJS::InjectableFunction f, AstNode explicitInjection
from AngularJS::InjectableFunction f, DataFlow::Node explicitInjection
where
count(f.getAnExplicitDependencyInjection()) > 1 and
explicitInjection = f.getAnExplicitDependencyInjection()
select f.asFunction().(FirstLineOf), "This function has $@ defined in multiple places.",
explicitInjection, "dependency injections"
select f.asFunction().getFunction().(FirstLineOf),
"This function has $@ defined in multiple places.", explicitInjection, "dependency injections"

View File

@@ -13,9 +13,9 @@ import javascript
import Declarations.UnusedParameter
import semmle.javascript.RestrictedLocations
predicate isUnusedParameter(Function f, string msg, Parameter parameter) {
predicate isUnusedParameter(DataFlow::FunctionNode f, string msg, Parameter parameter) {
exists(Variable pv |
isUnused(f, parameter, pv, _) and
isUnused(f.getFunction(), parameter, pv, _) and
not isAnAccidentallyUnusedParameter(parameter) and // avoid double alerts
msg = "Unused dependency " + pv.getName() + "."
)

View File

@@ -1,5 +1,5 @@
import javascript
from AngularJS::ScopeServiceReference s, MethodCallExpr mce
from AngularJS::ScopeServiceReference s, DataFlow::MethodCallNode mce
where mce = s.getAMethodCall(_)
select mce

View File

@@ -1,6 +1,6 @@
import javascript
private import AngularJS
from InjectableFunction f, SimpleParameter p, DataFlow::Node nd
from InjectableFunction f, DataFlow::ParameterNode p, DataFlow::Node nd
where nd = f.getCustomServiceDependency(p)
select p.getName(), nd

View File

@@ -1,92 +1,137 @@
| isolate scope for directive1 | scope-access.js:4:41:4:45 | scope |
| isolate scope for directive1 | scope-access.js:4:41:4:45 | scope |
| isolate scope for directive1 | scope-access.js:5:17:5:21 | scope |
| isolate scope for directive1 | scope-access.js:7:20:7:21 | {} |
| isolate scope for directive2 | scope-access.js:12:34:12:39 | $scope |
| isolate scope for directive2 | scope-access.js:12:34:12:39 | $scope |
| isolate scope for directive2 | scope-access.js:13:17:13:22 | $scope |
| isolate scope for directive2 | scope-access.js:15:20:15:21 | {} |
| isolate scope for directive3 | scope-access.js:20:39:20:44 | $scope |
| isolate scope for directive3 | scope-access.js:20:39:20:44 | $scope |
| isolate scope for directive3 | scope-access.js:21:17:21:22 | $scope |
| isolate scope for directive3 | scope-access.js:23:20:23:21 | {} |
| isolate scope for directive4 | scope-access.js:28:45:28:45 | a |
| isolate scope for directive4 | scope-access.js:28:45:28:45 | a |
| isolate scope for directive4 | scope-access.js:29:17:29:17 | a |
| isolate scope for directive4 | scope-access.js:31:20:31:21 | {} |
| isolate scope for directive5 | scope-access.js:36:25:36:24 | this |
| isolate scope for directive5 | scope-access.js:37:17:37:20 | this |
| isolate scope for directive5 | scope-access.js:39:20:39:21 | {} |
| isolate scope for directive6 | scope-access.js:45:25:45:24 | this |
| isolate scope for directive6 | scope-access.js:46:18:46:26 | return of anonymous function |
| isolate scope for directive6 | scope-access.js:46:23:46:26 | this |
| isolate scope for directive6 | scope-access.js:48:20:48:21 | {} |
| isolate scope for myCustomer | dev-guide-5.js:11:12:13:5 | { // Sc ... y\\n } |
| isolate scope for myCustomer | dev-guide-6.js:11:12:13:5 | { // Sc ... y\\n } |
| scope for <directive7>...</> | scope-access.js:54:34:54:39 | $scope |
| scope for <directive7>...</> | scope-access.js:54:34:54:39 | $scope |
| scope for <directive7>...</> | scope-access.js:55:17:55:22 | $scope |
| scope for <div>...</> | dev-guide-1.js:4:49:4:54 | $scope |
| scope for <div>...</> | dev-guide-1.js:4:49:4:54 | $scope |
| scope for <div>...</> | dev-guide-1.js:4:49:4:54 | $scope |
| scope for <div>...</> | dev-guide-1.js:5:3:5:8 | $scope |
| scope for <div>...</> | dev-guide-1.js:7:3:7:8 | $scope |
| scope for <div>...</> | dev-guide-1.js:7:21:7:20 | $scope |
| scope for <div>...</> | dev-guide-1.js:8:5:8:10 | $scope |
| scope for <div>...</> | dev-guide-1.js:8:34:8:39 | $scope |
| scope for <div>...</> | dev-guide-2.js:4:66:4:71 | $scope |
| scope for <div>...</> | dev-guide-2.js:4:66:4:71 | $scope |
| scope for <div>...</> | dev-guide-2.js:5:3:5:8 | $scope |
| scope for <div>...</> | dev-guide-2.js:8:51:8:56 | $scope |
| scope for <div>...</> | dev-guide-2.js:8:51:8:56 | $scope |
| scope for <div>...</> | dev-guide-2.js:9:3:9:8 | $scope |
| scope for <div>...</> | dev-guide-3.js:4:52:4:57 | $scope |
| scope for <div>...</> | dev-guide-3.js:4:52:4:57 | $scope |
| scope for <div>...</> | dev-guide-3.js:4:52:4:57 | $scope |
| scope for <div>...</> | dev-guide-3.js:5:3:5:8 | $scope |
| scope for <div>...</> | dev-guide-3.js:6:3:6:8 | $scope |
| scope for <div>...</> | dev-guide-3.js:6:25:6:24 | $scope |
| scope for <div>...</> | dev-guide-3.js:7:5:7:10 | $scope |
| scope for <div>...</> | dev-guide-4.js:4:52:4:57 | $scope |
| scope for <div>...</> | dev-guide-4.js:4:52:4:57 | $scope |
| scope for <div>...</> | dev-guide-4.js:5:3:5:8 | $scope |
| scope for <div>...</> | dev-guide-4.js:10:51:10:56 | $scope |
| scope for <div>...</> | dev-guide-4.js:10:51:10:56 | $scope |
| scope for <div>...</> | dev-guide-4.js:11:3:11:8 | $scope |
| scope for <div>...</> | dev-guide-5.js:4:47:4:52 | $scope |
| scope for <div>...</> | dev-guide-5.js:4:47:4:52 | $scope |
| scope for <div>...</> | dev-guide-5.js:4:47:4:52 | $scope |
| scope for <div>...</> | dev-guide-5.js:4:47:4:52 | $scope |
| scope for <div>...</> | dev-guide-5.js:5:3:5:8 | $scope |
| scope for <div>...</> | dev-guide-5.js:5:3:5:8 | $scope |
| scope for <div>...</> | dev-guide-5.js:6:3:6:8 | $scope |
| scope for <div>...</> | dev-guide-5.js:6:3:6:8 | $scope |
| scope for <div>...</> | dev-guide-6.js:4:47:4:52 | $scope |
| scope for <div>...</> | dev-guide-6.js:4:47:4:52 | $scope |
| scope for <div>...</> | dev-guide-6.js:4:47:4:52 | $scope |
| scope for <div>...</> | dev-guide-6.js:4:47:4:52 | $scope |
| scope for <div>...</> | dev-guide-6.js:5:3:5:8 | $scope |
| scope for <div>...</> | dev-guide-6.js:5:3:5:8 | $scope |
| scope for <div>...</> | dev-guide-6.js:6:3:6:8 | $scope |
| scope for <div>...</> | dev-guide-6.js:6:3:6:8 | $scope |
| scope for <elementthatusescontroller1>...</> | scope-access.js:59:52:59:57 | $scope |
| scope for <elementthatusescontroller1>...</> | scope-access.js:59:52:59:57 | $scope |
| scope for <elementthatusescontroller1>...</> | scope-access.js:60:9:60:14 | $scope |
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
| scope for <li>...</> | dev-guide-3.js:4:52:4:57 | $scope |
| scope for <li>...</> | dev-guide-3.js:5:3:5:8 | $scope |
| scope for <li>...</> | dev-guide-3.js:5:3:5:8 | $scope |
| scope for <li>...</> | dev-guide-3.js:6:3:6:8 | $scope |
| scope for <li>...</> | dev-guide-3.js:6:3:6:8 | $scope |
| scope for <li>...</> | dev-guide-3.js:6:25:6:24 | $scope |
| scope for <li>...</> | dev-guide-3.js:6:25:6:24 | $scope |
| scope for <li>...</> | dev-guide-3.js:7:5:7:10 | $scope |
| scope for <li>...</> | dev-guide-3.js:7:5:7:10 | $scope |
| scope in dev-guide-1.html | dev-guide-1.js:4:49:4:54 | $scope |
| scope in dev-guide-1.html | dev-guide-1.js:4:49:4:54 | $scope |
| scope in dev-guide-1.html | dev-guide-1.js:4:49:4:54 | $scope |
| scope in dev-guide-1.html | dev-guide-1.js:5:3:5:8 | $scope |
| scope in dev-guide-1.html | dev-guide-1.js:7:3:7:8 | $scope |
| scope in dev-guide-1.html | dev-guide-1.js:7:21:7:20 | $scope |
| scope in dev-guide-1.html | dev-guide-1.js:8:5:8:10 | $scope |
| scope in dev-guide-1.html | dev-guide-1.js:8:34:8:39 | $scope |
| scope in dev-guide-2.html | dev-guide-2.js:4:66:4:71 | $scope |
| scope in dev-guide-2.html | dev-guide-2.js:4:66:4:71 | $scope |
| scope in dev-guide-2.html | dev-guide-2.js:5:3:5:8 | $scope |
| scope in dev-guide-2.html | dev-guide-2.js:8:51:8:56 | $scope |
| scope in dev-guide-2.html | dev-guide-2.js:8:51:8:56 | $scope |
| scope in dev-guide-2.html | dev-guide-2.js:9:3:9:8 | $scope |
| scope in dev-guide-3.html | dev-guide-3.js:4:52:4:57 | $scope |
| scope in dev-guide-3.html | dev-guide-3.js:4:52:4:57 | $scope |
| scope in dev-guide-3.html | dev-guide-3.js:4:52:4:57 | $scope |
| scope in dev-guide-3.html | dev-guide-3.js:5:3:5:8 | $scope |
| scope in dev-guide-3.html | dev-guide-3.js:6:3:6:8 | $scope |
| scope in dev-guide-3.html | dev-guide-3.js:6:25:6:24 | $scope |
| scope in dev-guide-3.html | dev-guide-3.js:7:5:7:10 | $scope |
| scope in dev-guide-4.html | dev-guide-4.js:4:52:4:57 | $scope |
| scope in dev-guide-4.html | dev-guide-4.js:4:52:4:57 | $scope |
| scope in dev-guide-4.html | dev-guide-4.js:5:3:5:8 | $scope |
| scope in dev-guide-4.html | dev-guide-4.js:10:51:10:56 | $scope |
| scope in dev-guide-4.html | dev-guide-4.js:10:51:10:56 | $scope |
| scope in dev-guide-4.html | dev-guide-4.js:11:3:11:8 | $scope |
| scope in dev-guide-5.html | dev-guide-5.js:4:47:4:52 | $scope |
| scope in dev-guide-5.html | dev-guide-5.js:4:47:4:52 | $scope |
| scope in dev-guide-5.html | dev-guide-5.js:5:3:5:8 | $scope |
| scope in dev-guide-5.html | dev-guide-5.js:6:3:6:8 | $scope |
| scope in dev-guide-5.html | dev-guide-6.js:4:47:4:52 | $scope |
| scope in dev-guide-5.html | dev-guide-6.js:4:47:4:52 | $scope |
| scope in dev-guide-5.html | dev-guide-6.js:5:3:5:8 | $scope |
| scope in dev-guide-5.html | dev-guide-6.js:6:3:6:8 | $scope |
| scope in dev-guide-6.html | dev-guide-5.js:4:47:4:52 | $scope |
| scope in dev-guide-6.html | dev-guide-5.js:4:47:4:52 | $scope |
| scope in dev-guide-6.html | dev-guide-5.js:5:3:5:8 | $scope |
| scope in dev-guide-6.html | dev-guide-5.js:6:3:6:8 | $scope |
| scope in dev-guide-6.html | dev-guide-6.js:4:47:4:52 | $scope |
| scope in dev-guide-6.html | dev-guide-6.js:4:47:4:52 | $scope |
| scope in dev-guide-6.html | dev-guide-6.js:5:3:5:8 | $scope |
| scope in dev-guide-6.html | dev-guide-6.js:6:3:6:8 | $scope |
| scope in scope-access.html | scope-access.js:54:34:54:39 | $scope |
| scope in scope-access.html | scope-access.js:54:34:54:39 | $scope |
| scope in scope-access.html | scope-access.js:55:17:55:22 | $scope |
| scope in scope-access.html | scope-access.js:59:52:59:57 | $scope |
| scope in scope-access.html | scope-access.js:59:52:59:57 | $scope |
| scope in scope-access.html | scope-access.js:60:9:60:14 | $scope |