Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
72 changes: 31 additions & 41 deletions javascript/ql/src/AlertSuppression.ql
Original file line number Diff line number Diff line change
Expand Up @@ -12,80 +12,70 @@ import javascript
*/
class SuppressionComment extends Locatable {
string text;

string annotation;

SuppressionComment() {
(
text = this.(LineComment).getText() or
text = this.(HTML::CommentNode).getText()
)
and
text = this.(LineComment).getText() or
text = this.(HTML::CommentNode).getText()
) and
(
// match `lgtm[...]` anywhere in the comment
annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
or
// match `lgtm` at the start of the comment and after semicolon
annotation = text.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim()
// match `lgtm[...]` anywhere in the comment
annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
or
// match `lgtm` at the start of the comment and after semicolon
annotation = text.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim()
)
}

/** Gets the text of this suppression comment, not including delimiters. */
string getText() {
result = text
}
string getText() { result = text }

/** Gets the suppression annotation in this comment. */
string getAnnotation() {
result = annotation
}
string getAnnotation() { result = annotation }

/**
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
* to column `endcolumn` of line `endline` in file `filepath`.
*/
* Holds if this comment applies to the range from column `startcolumn` of line `startline`
* to column `endcolumn` of line `endline` in file `filepath`.
*/
predicate covers(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
this.getLocation().hasLocationInfo(filepath, startline, _, endline, endcolumn) and
startcolumn = 1
}

/** Gets the scope of this suppression. */
SuppressionScope getScope() {
this = result.getSuppressionComment()
}
SuppressionScope getScope() { this = result.getSuppressionComment() }
}

/**
* The scope of an alert suppression comment.
*/
class SuppressionScope extends @locatable {
SuppressionScope() {
this instanceof SuppressionComment
}
SuppressionScope() { this instanceof SuppressionComment }

/** Gets a suppression comment with this scope. */
SuppressionComment getSuppressionComment() {
result = this
}
SuppressionComment getSuppressionComment() { result = this }

/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [LGTM locations](https://lgtm.com/help/ql/locations).
*/
predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [LGTM locations](https://lgtm.com/help/ql/locations).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.(SuppressionComment).covers(filepath, startline, startcolumn, endline, endcolumn)
}

/** Gets a textual representation of this element. */
string toString() {
result = "suppression range"
}
string toString() { result = "suppression range" }
}

from SuppressionComment c
select c, // suppression comment
c.getText(), // text of suppression comment (excluding delimiters)
c.getAnnotation(), // text of suppression annotation
c.getScope() // scope of suppression
select c, // suppression comment
c.getText(), // text of suppression comment (excluding delimiters)
c.getAnnotation(), // text of suppression annotation
c.getScope() // scope of suppression
40 changes: 19 additions & 21 deletions javascript/ql/src/AngularJS/DeadAngularJSEventListener.ql
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,23 @@ import javascript
predicate isABuiltinEventName(string name) {
// $rootScope.Scope
name = "$destroy" or

// $location
name = "$locationChangeStart" or
name = "$locationChangeSuccess" or

// ngView
name = "$viewContentLoaded" or

// angular-ui/ui-router
name = "$stateChangeStart" or
name = "$stateNotFound" or
name = "$stateChangeSuccess" or
name = "$stateChangeError" or
name = "$viewContentLoading " or
name = "$viewContentLoaded " or

// $route
name = "$routeChangeStart" or
name = "$routeChangeSuccess" or
name = "$routeChangeError" or
name = "$routeUpdate" or

// ngInclude
name = "$includeContentRequested" or
name = "$includeContentLoaded" or
Expand All @@ -49,20 +44,21 @@ 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" |
exists(string methodName, MethodCallExpr mce | methodName = "$emit" or methodName = "$broadcast" |
mce.getArgument(0).mayHaveStringValue(name) and
(
// dataflow based scope resolution
mce = any(AngularJS::ScopeServiceReference scope).getAMethodCall(methodName) or
mce = any(AngularJS::ScopeServiceReference scope).getAMethodCall(methodName)
or
// heuristic scope resolution: assume parameters like `$scope` or `$rootScope` are AngularJS scope objects
exists(SimpleParameter param |
param.getName() = any(AngularJS::ScopeServiceReference scope).getName() and
mce.getReceiver().mayReferToParameter(param) and
mce.getMethodName() = methodName
) or
)
or
// a call in an AngularJS expression
exists (AngularJS::NgCallExpr call |
exists(AngularJS::NgCallExpr call |
call.getCallee().(AngularJS::NgVarExpr).getName() = methodName and
call.getArgument(0).(AngularJS::NgString).getStringValue() = name
)
Expand All @@ -71,14 +67,16 @@ predicate isAUserDefinedEventName(string name) {
}

from AngularJS::ScopeServiceReference scope, MethodCallExpr mce, string eventName
where mce = scope.getAMethodCall("$on") and
mce.getArgument(0).mayHaveStringValue(eventName) and
not (
isAUserDefinedEventName(eventName) or
isABuiltinEventName(eventName) or
// external, namespaced
eventName.regexpMatch(".*[.:].*") or
// from other event system (DOM: onClick et al)
eventName.regexpMatch("on[A-Z][a-zA-Z]+") // camelCased with 'on'-prefix
)
select mce.getArgument(1), "This event listener is dead, the event '" + eventName + "' is not emitted anywhere."
where
mce = scope.getAMethodCall("$on") and
mce.getArgument(0).mayHaveStringValue(eventName) and
not (
isAUserDefinedEventName(eventName) or
isABuiltinEventName(eventName) or
// external, namespaced
eventName.regexpMatch(".*[.:].*") or
// from other event system (DOM: onClick et al)
eventName.regexpMatch("on[A-Z][a-zA-Z]+") // camelCased with 'on'-prefix
)
select mce.getArgument(1),
"This event listener is dead, the event '" + eventName + "' is not emitted anywhere."
27 changes: 14 additions & 13 deletions javascript/ql/src/AngularJS/DependencyMismatch.ql
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@
import javascript

from AngularJS::InjectableFunction f, SimpleParameter p, string msg
where p = f.asFunction().getAParameter() and
(
not p = f.getDependencyParameter(_) and
msg = "This parameter has no injected dependency."
or
exists (string n | p = f.getDependencyParameter(n) |
p.getName() != n and
exists(f.getDependencyParameter(p.getName())) and
msg = "This parameter is named '" + p.getName() + "', " +
"but actually refers to dependency '" + n + "'."
)
)
select p, msg
where
p = f.asFunction().getAParameter() and
(
not p = f.getDependencyParameter(_) and
msg = "This parameter has no injected dependency."
or
exists(string n | p = f.getDependencyParameter(n) |
p.getName() != n and
exists(f.getDependencyParameter(p.getName())) and
msg = "This parameter is named '" + p.getName() + "', " +
"but actually refers to dependency '" + n + "'."
)
)
select p, msg
7 changes: 4 additions & 3 deletions javascript/ql/src/AngularJS/DisablingSce.ql
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
import javascript

from MethodCallExpr mce, AngularJS::BuiltinServiceReference service
where service.getName() ="$sceProvider" and
mce = service.getAMethodCall( "enabled") and
mce.getArgument(0).mayHaveBooleanValue(false)
where
service.getName() = "$sceProvider" and
mce = service.getAMethodCall("enabled") and
mce.getArgument(0).mayHaveBooleanValue(false)
select mce, "Disabling SCE is strongly discouraged."
16 changes: 10 additions & 6 deletions javascript/ql/src/AngularJS/DoubleCompilation.ql
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@
import javascript

from AngularJS::ServiceReference compile, SimpleParameter elem, CallExpr c
where compile.getName() = "$compile" and
elem = any(AngularJS::CustomDirective d).getALinkFunction().(AngularJS::LinkFunction).getElementParameter() and
c = compile.getACall() and
c.getArgument(0).mayReferToParameter(elem) and
// don't flag $compile calls that specify a `maxPriority`
c.getNumArgument() < 3
where
compile.getName() = "$compile" and
elem = any(AngularJS::CustomDirective d)
.getALinkFunction()
.(AngularJS::LinkFunction)
.getElementParameter() and
c = compile.getACall() and
c.getArgument(0).mayReferToParameter(elem) 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 + "'."
11 changes: 7 additions & 4 deletions javascript/ql/src/AngularJS/DuplicateDependency.ql
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ import javascript
import semmle.javascript.RestrictedLocations

predicate isRepeatedDependency(AngularJS::InjectableFunction f, string name, ASTNode location) {
exists(int i, int j | i < j and
exists(int i, int j |
i < j and
exists(f.getDependencyDeclaration(i, name)) and
location = f.getDependencyDeclaration(j, name)
)
}

from AngularJS::InjectableFunction f, ASTNode 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 (FirstLineOf)f.asFunction(), "This function has a duplicate dependency '$@'.", node, 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
Loading