<dependency>
<groupId>org.sonarsource.javascript</groupId>
<artifactId>sonar-javascript-plugin</artifactId>
- <type>sonar-plugin</type>
<version>${javascript.plugin.version}</version>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.6.2</version>
- <scope>compile</scope>
</dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>19.0</version>
- </dependency>
+
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.sonarsource.java</groupId>
+ <groupId>org.sonarsource.javascript</groupId>
<artifactId>javascript-checks-testkit</artifactId>
<version>${javascript.plugin.version}</version>
<scope>test</scope>
<version>1.17</version>
<extensions>true</extensions>
<configuration>
- <!--
- <pluginClass>de.example.plugins.helloworld.HelloWorldPlugin</pluginClass>
- -->
+ <pluginClass>de.example.plugins.custom.javascript.CustomPlugin</pluginClass>
</configuration>
</plugin>
<plugin>
package de.example.custom.javascript.checks;
+import java.util.List;
+
import org.sonar.check.Rule;
-import org.sonar.plugins.javascript.api.visitors.DoubleDispatchVisitorCheck;
+import org.sonar.javascript.tree.impl.expression.CallExpressionTreeImpl;
+import org.sonar.javascript.tree.impl.expression.DotMemberExpressionTreeImpl;
+import org.sonar.javascript.tree.impl.expression.IdentifierTreeImpl;
+import org.sonar.plugins.javascript.api.tree.Tree;
+import org.sonar.plugins.javascript.api.tree.Tree.Kind;
+import org.sonar.plugins.javascript.api.visitors.SubscriptionVisitorCheck;
+
+import com.google.common.collect.ImmutableList;
@Rule(key = "GUJS0001")
-public class AngularJSRootOnEventSubscriptionCheck extends DoubleDispatchVisitorCheck {
+public class AngularJSRootOnEventSubscriptionCheck extends SubscriptionVisitorCheck {
+
+ @Override
+ public List<Kind> nodesToVisit() {
+ return ImmutableList.of(Kind.CALL_EXPRESSION);
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ CallExpressionTreeImpl callExpression = (CallExpressionTreeImpl) tree;
+ if (callExpression.callee() instanceof DotMemberExpressionTreeImpl) {
+ DotMemberExpressionTreeImpl callee = (DotMemberExpressionTreeImpl) callExpression.callee();
+ if (callee.object() instanceof IdentifierTreeImpl) {
+ IdentifierTreeImpl object = (IdentifierTreeImpl) callee.object();
+ String objectName = object.name();
+ String calleeName = callee.property().name();
+ if ("$rootScope".equals(objectName) && "$on".equals(calleeName)) {
+ addIssue(callExpression.getFirstToken(), "Do not use $rootScope.$on because it leaks the Controller instance.");
+ }
+ }
+ }
+
+ }
}
import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.rule.RulesDefinitionAnnotationLoader;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
+import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.io.Resources;
import com.google.gson.Gson;
* org.sonar.plugins.javascript.JavaScriptSquidSensor.
*
* It seems like the SonarQube developers in charge of writing the JavaScript plugin tried to
- * make easy the creation of custom Java plugins.
+ * make easy the creation of custom JavaScript plugins.
*
* So, JavaScriptSquidSensor will be the object that will run my rules (my Checks) whenever it finds JavaScript code.
* I do not have to do anything else, what is great!
private final Gson gson = new Gson();
- @Override
- public String repositoryName() {
- return CheckList.REPOSITORY_NAME;
- }
-
- @Override
- public String repositoryKey() {
- return CheckList.REPOSITORY_KEY;
- }
-
- @Override
- public Class[] checkClasses() {
- return CheckList.getChecks().toArray();
- }
+ @Override
+ public String repositoryName() {
+ return CheckList.REPOSITORY_NAME;
+ }
+
+ @Override
+ public String repositoryKey() {
+ return CheckList.REPOSITORY_KEY;
+ }
+
+ @Override
+ public Class[] checkClasses() {
+ return CheckList.getChecks().stream().toArray(Class[]::new);
+// return CheckList.getChecks().stream().toArray(size -> {
+// return new Class[size];
+// });
+ }
- /**
- * I do not want to use the define method implemented in org.sonar.plugins.javascript.api.CustomJavaScriptRulesDefinition.
- */
+ /**
+ * I do not want to use the define method implemented in
+ * org.sonar.plugins.javascript.api.CustomJavaScriptRulesDefinition.
+ */
@Override
public void define(Context context) {
- NewRepository repository = context
- .createRepository(repositoryKey(), JavaScriptLanguage.KEY)
+ NewRepository repository = context.createRepository(repositoryKey(), JavaScriptLanguage.KEY)
.setName(repositoryName());
List<Class> checks = CheckList.getChecks();
new RulesDefinitionAnnotationLoader().load(repository, Iterables.toArray(checks, Class.class));
throw new IllegalArgumentException("No Rule annotation was found on " + ruleClass);
}
String ruleKey = ruleAnnotation.key();
- if (StringUtils.isEmpty(ruleKey)) {
+ if (Strings.isNullOrEmpty(ruleKey)) {
throw new IllegalArgumentException("No key is defined in Rule annotation of " + ruleClass);
}
NewRule rule = repository.rule(ruleKey);
--- /dev/null
+<p>Custom rule description.</p>
+<h2>Noncompliant Code Example</h2>
+<pre>
+class MyClass {
+
+ int foo1(int value) { return 0; }
+
+ MyClass foo2(MyClass value) { return null; }
+
+ ...
+
+}
+</pre>
+<h2>See</h2>
+<ul>
+ <li> <a href="https://www.securecoding.cert.org/confluence/x/EYBUC">CERT, MSC11-J.</a> - You are doing wrong!!! </li>
+</ul>
+
--- /dev/null
+{
+ "title": "Function names should comply with a naming convention",
+ "status": "ready",
+ "remediation": {
+ "func": "Constant\/Issue",
+ "constantCost": "5min"
+ },
+ "tags": [
+ "bug"
+ ],
+ "defaultSeverity": "Major"
+}
--- /dev/null
+function Controller($rootScope, USERS) {
+ var vm = this;
+
+ $rootScope.$on(USERS.ROOTSCOPE.BROADCAST, usersChildOnRootBroadcast); // Noncompliant {{Do not use $rootScope.$on because it leaks the Controller instance.}}
+
+ function NestedFunction() {
+ $rootScope // Noncompliant {{Do not use $rootScope.$on because it leaks the Controller instance.}}
+
+ .$on(USERS.ROOTSCOPE.BROADCAST, usersChildOnRootBroadcast);
+ }
+
+ function usersChildOnRootBroadcast(events, broadcastUser) {
+ vm.broadcastUser = broadcastUser;
+ }
+}
--- /dev/null
+package de.example.custom.javascript.checks;
+
+import java.io.File;
+
+import org.junit.Test;
+import org.sonar.javascript.checks.verifier.JavaScriptCheckVerifier;
+
+public class AngularJSRootOnEventSubscriptionCheckTest {
+
+ private AngularJSRootOnEventSubscriptionCheck check = new AngularJSRootOnEventSubscriptionCheck();
+
+ @Test
+ public void testDefault() {
+ JavaScriptCheckVerifier.verify(check, new File("src/test/files/checks/AngularJSRootOnEventSubscriptionCheck.js"));
+ }
+}