diff --git a/sonar-plugin/docs/deployment-of-plugin.md b/sonar-plugin/docs/deployment-of-plugin.md new file mode 100644 index 0000000..4500694 --- /dev/null +++ b/sonar-plugin/docs/deployment-of-plugin.md @@ -0,0 +1,64 @@ +# Introduction +This documentation will cover the deployment aspect of our WSO2 Code Specific plugin and how it can be integrated with GitHub! This plugin allows you to integrate static code analysis into your development workflow, helping you to improve the quality and security of your code. + +The plugin will be deployed on your SonarQube instance, and can be triggered by commenting on a pull request on GitHub. This will initiate a workflow which will run the static analysis on the code and provide feedback to developers. This documentation will also cover how to interpret the results of the analysis and use them to make informed decisions about code changes. Let's get started! + +# How to deploy +To deploy a plugin in SonarQube, you will need to do the following: + +1. Build the plugin: Before deploying the plugin, you will need to build it using Maven. This will create a JAR file that contains the plugin and all of its dependencies. + +1. Copy the plugin JAR file: Next, you will need to copy the plugin JAR file and paste it to the extensions/plugins directory of your SonarQube instance. This is typically located at $SONARQUBE_HOME/extensions/plugins. + +1. Restart SonarQube: After copying the plugin JAR file, you will need to restart your SonarQube instance for the plugin to take effect. You can do this by stopping and starting the SonarQube server, or by running the sonar.sh restart command if you are using the standalone distribution. + +That's it! Your plugin should now be installed and activated in your SonarQube instance. + +# How it works on GitHub +The Sonar plugin can be integrated with GitHub through the use of a workflow. This workflow is triggered when a comment is made on a pull request that includes the words "run analysis" and is made by an owner or collaborator of the repository. Upon triggering the workflow, static analysis is run on the code using the Sonar plugin. The report generated by the analysis can be viewed in SonarQube, where developers can identify and fix any issues that were detected. + +## Workflow file + +```name: Build +on: + issue_comment: + types: [created] +jobs: + build: + concurrency: "1" + if: ${{ github.event.issue.pull_request && github.event.comment.author_association == 'OWNER' && github.event.comment.body == 'run analysis' }} + name: Build and analyze + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Checkout Pull Request + run: hub pr checkout ${{ github.event.issue.number }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v1 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=${{ insert your project key here }} +``` + +## Comment +As specified in the workflow file, static analysis will only be run if the comment on the pull request includes the words "run analysis" and is made by the repository owner. It is possible to configure the workflow to allow analysis to be triggered by comments from both owners and collaborators. To do this, simply modify the workflow file to reflect the desired behavior. diff --git a/sonar-plugin/docs/how-to-write-custom-rules-in-sonar.md b/sonar-plugin/docs/how-to-write-custom-rules-in-sonar.md new file mode 100644 index 0000000..2bd769a --- /dev/null +++ b/sonar-plugin/docs/how-to-write-custom-rules-in-sonar.md @@ -0,0 +1,210 @@ +# Introduction +Welcome to the documentation for writing custom rules in Sonar for WSO2! At WSO2, we are committed to maintaining high code quality standards and continuously improving our development processes. One way we can do this is by using the SonarQube platform to analyze our codebase for potential issues. However, we may have specific coding guidelines or standards that are unique to our company and not covered by the standard checks and plugins. That's where custom rules come in. With custom rules, we can define our own rules to ensure that our code adheres to our specific standards. In this documentation, you will learn how to write custom rules for Sonar and how to integrate them into our workflow at WSO2. By following these guidelines, you can help us maintain high code quality and contribute to the continuous improvement of our development processes. + +# Files Involved +There are three files involved when writing custom rules for Sonar: +1. Test file +1. Test class +1. Rule class + +## Test file - write up some test cases +In SonarQube, test files are used to verify that the rules you have implemented in your custom plugin are working correctly. When you write test cases for your custom rules, you should aim to cover as many scenarios as possible to ensure that the rule is working correctly and providing accurate results. + +In a test file for a custom rule in SonarQube, the "Compliant" and "Noncompliant" keywords are used to specify the expected result of the rule for a given code snippet. The "Compliant" keyword indicates that the rule should not produce any issues for the given code snippet, while the "Noncompliant" keyword indicates that the rule should produce at least one issue. + +Here is an example of how these keywords might be used in a test case which reports an issue if variable name length is less than 2 characters: + + +## Test class - rule’s unit test +Unit tests are used to verify the behavior of a custom rule being implemented. The CheckVerifier class, provided by the Java Analyzer rule-testing API, is used to validate the rule implementation and abstract initialization mechanisms of the analyzer. When a rule is verified, the verifier collects lines marked as noncompliant and checks that the rule raises the expected issues and only those issues. +The following example shows how to write a test that points to your previous file with the test cases: + +``` +@Test +void test() { + + CheckVerifier.newVerifier() + .onFile("src/test/files/MyFirstCustomCheck.java") + .withCheck(new MyFirstCustomCheck()) + .verifyIssues(); +} +``` + +## Rule class +The rule implementation class is the main file that contains the code for your custom rule in SonarQube for Java. It defines the logic that the rule will use to analyze the code and identify any issues. + +Before writing the custom rule, you need to understand some background information. The SonarQube Java Analyzer parses a Java code file and creates a data structure called the Syntax Tree, which represents each element of the Java language. Each element, called a Kind, has a specific interface that describes its particularities. For example, a method declaration is represented by the METHOD Kind and the MethodTree interface. The [Kind enum in the Java Analyzer API](https://github.com/SonarSource/sonar-java/blob/6.13.0.25138/java-frontend/src/main/java/org/sonar/plugins/java/api/tree/Tree.java#L47) lists all of the available Kinds. + +There are two ways in which you can write custom rules, one such way is the IssuableSubscriptionVisitor class from the API provides methods for raising issues and defines the strategy for analyzing a file. It uses a subscription mechanism to specify which types of tree nodes the rule should react to. The nodesToVisit() method is used to specify the node types that the rule should cover. + +The other way is to use the semantic API. The SonarAnalyzer for Java also constructs a semantic model of the code being analyzed, which provides more information about the code. This model includes data about symbols used in the code, such as a method's owner, its usages, parameter and return types, and any exceptions it may throw. You can explore the [semantic package of the API](https://github.com/SonarSource/sonar-java/tree/6.13.0.25138/java-frontend/src/main/java/org/sonar/plugins/java/api/semantic) to see the full range of information that is available during analysis. + +# 3 Tutorial +Before jumping into writing a WSO2 specific custom rule, make sure to try out the [example tutorial](https://github.com/SonarSource/sonar-java/blob/master/docs/CUSTOM_RULES_101.md) given in the sonar documentation to get familiar with the jargon and get some hands-on experience. Afterwards, go through the below section to write a more WSO2 specific check. + +# 4 Let's write a WSO2 Specific Check + +## The Problem +According to our [PR review guidelines](https://docs.google.com/document/d/1WZDZrpNuSVDMoFf0XOAQmtNk59T9R7aDPGY_U0elEq0/edit?usp=sharing), we can not use class level variables in certain services. For example, if any class is a subtype of the WSO2 application authenticator or user store manager then that particular class should not be able to define class level variables. The variables defined should always be static and final. + +So let’s see how we can use sonar to write this very custom check to adhere to WSO2 coding standards and best practices. + +## Approach +Let’s first clone the sonar repo which has a custom template for us already to work with in their ‘docs/java-custom-rules-example’ folder - https://github.com/SonarSource/sonar-java.git. + +Then let's add two artifact items to our pom.xml(in the custom sonar template its called pom_SQ_8_9_LTS.xml): + +``` + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.application.authentication.framework + 5.25.52 + jar + + + org.wso2.carbon + org.wso2.carbon.user.api + 4.9.0-m3 + jar + +``` + +And run: `mvn clean install` or `mvn clean install -f pom_SQ_8_9_LTS.xml` if you are using the template’s pom. +We do this so that, when writing our test cases, the import will get detected by our test file. + +*** + +Then as mentioned above, we will have to create 3 files: + +**Test file** +Let’s create this in the ‘src/test/files' folder and call it ClassLevelVariablesInServicesCheck.java + +In this file, we will be adding test cases that will be covering most of the possible use cases: + +``` +import org.wso2.carbon.identity.application.authentication.framework.AbstractApplicationAuthenticator; +import org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator; +import org.wso2.carbon.identity.application.authentication.framework.LocalApplicationAuthenticator; +import org.wso2.carbon.user.api.UserStoreManager; + +class Registry implements UserStoreManager { + + private int number; // Noncompliant + private static final long serialVersionUID = 123; + + public boolean authenticate(String s, Object o) throws UserStoreException { + return false; + } +} + +class BasicAuthenticator extends AbstractApplicationAuthenticator { + + private int number; // Noncompliant + private static final long serialVersionUID = 123; +} + +class myClass { + + private int number; + private static final long serialVersionUID = 123; +} + +``` +As you can see in the above code, we are marking variables that are not static and final as noncompliant if they are part of a class that contains the ApplicationAuthenticator or UserStoreManager in its inheritance chain. + +*** + +**Test Class** +Next, we shall write the rule’s unit test to verify the behavior of our custom rule and ensure that our rule is functioning correctly. + +Let’s create a class called `ClassLevelVariablesInServicesCheckTest` in the ‘src/test/java/org/sonar/samples/java/checks/’ and add our unit test to it like below(you may have to import class `org.sonar.java.checks.verifier.CheckVerifier`): + +``` +class ClassLevelVariablesInServicesCheckTest { + + @Test + void test() { + CheckVerifier.newVerifier() + .onFile("src/test/sonar/rules/test/files/ClassLevelVariablesInServicesCheck.java") + .withCheck(new ClassLevelVariablesInServicesCheck()) + .withClassPath(FilesUtils.getClassPath("target/test-jars")) + .verifyIssues(); + } +} + +``` + +*** +**Rule Class** +This example shows a custom rule implementation class that extends the BaseTreeVisitor class and overrides the visitMethod(), visitVariable() and visitClass() methods. These method overrides specifies that the rule should visit class, variable and method nodes in the syntax tree and defines the custom logic for analyzing each class. + +In this case, the rule is checking whether the class is a subtype of either an Application Authenticator or User Store Manager and raising an issue if it does not contain the static or finally keywords. It also makes sure that it is not raising an issue for variable declarations inside methods. + +``` +/** +* Custom check to see if class level variables exist in specific services. +*/ +@Rule(key = "ClassLevelVariablesInServicesCheck") +public class ClassLevelVariablesInServicesCheck extends BaseTreeVisitor implements JavaFileScanner { + + private JavaFileScannerContext context; + private boolean subTypeOfService; + private boolean inMethod; + private final List serviceClassesToCheckList = new ArrayList<>(getServicesToCheck()); + private static final String MESSAGE = "Do not use class level variables in Authenticators."; + + @Override + public void scanFile(JavaFileScannerContext context) { + + this.context = context; + scan(context.getTree()); + } + + @Override + public void visitClass(ClassTree tree) { + + subTypeOfService = false; + inMethod = false; + + for (String serviceName : serviceClassesToCheckList) { + if (tree.symbol().type().isSubtypeOf(serviceName)) { + subTypeOfService = true; + } + } + super.visitClass(tree); + } + + @Override + public void visitVariable(VariableTree tree) { + + if (subTypeOfService && (!inMethod) && (!tree.symbol().isStatic() || + !tree.symbol().isFinal())) { + context.reportIssue(this, tree, MESSAGE); + } + + super.visitVariable(tree); + } + + @Override + public void visitMethod(MethodTree tree) { + + inMethod = true; + super.visitMethod(tree); + } + +private List getServicesToCheck() { + + return Collections.unmodifiableList(Arrays.asList( + "org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator", + "org.wso2.carbon.user.api.UserStoreManager")); + } + +} + +``` + +# Summary +Custom rules in SonarQube for Java are designed to help developers avoid common mistakes. It can even be written to target WSO2’s specific needs such as declaring class level variables in certain services. By using custom rules and sonar, we can improve the quality and maintainability of our code and ensure that our specific code standards are maintained at all times. + + + diff --git a/sonar-plugin/docs/overview-of-plugin.md b/sonar-plugin/docs/overview-of-plugin.md new file mode 100644 index 0000000..c3d40a6 --- /dev/null +++ b/sonar-plugin/docs/overview-of-plugin.md @@ -0,0 +1,46 @@ +# Introduction +Welcome to the documentation for the custom Sonar Java plugin for WSO2! This plugin is designed to offload the PR review process and assist in detecting common violations of WSO2’s specific coding standards. + +In WSO2, we have a set of coding best practices and security best practices which are not common in Java. So using default or basic static analysis tools such as find bugs and check style will not be able to analyze and identify these types of bugs. + +So, after completing a thorough comparison between the widely used static analysis tools in the industry, we determined that Sonar is the most ideal static analysis tool to meet our needs. + +With this plugin, we will be able to write custom rules that can be used to detect common WSO2 specific violations and security guidelines. Developers can easily integrate Sonar into their workflow and ensure that their code adheres to WSO2’s coding standards before a code review session. I hope that this plugin will help to streamline the code review process and improve the overall quality of our codebase. + +# How does the plugin work +[Sonar Documentation](https://docs.sonarqube.org/latest/extend/developing-plugin/) + +Entry point of plugin given in pom in the sonar packaging plugin as org.wso2.RulesPlugin + +**RulesPlugin** - entry point of plugin + +RulesPlugin implements the plugin interface. What is meant by entry point - for plugins to inject extensions into sonarqube we need to use this plugin interface + +**RulesDefinitionImpl Class** + +So the custom rules are added as extensions using this RulesDefinitionImpl class (server extension) which implements a RulesDefinition interface from the sonar api that is instantiated at server startup. + +**Define Method** +The define method in this class creates a repository in which our custom rules will be added. + +Then the ruleMetaDataLaoder has addRulesByAnnotatedClass method which maps the particular custom rule to the description based on the rule key. + +**RulesDefintionImplTest Class** +The RulesDefintionImplTest runs unit tests (this file is not mandatory). It verifies the repository name and whether the size of the repository rules matches that of the RulesList.getChecks().size() and whether all our custom classes have been given a description. + +**RulesList Class** +The RulesList class is where we add the names of the custom checks we want to be included in our repository. + +*** + +Back to RulesPlugin, the next extension is where objects are instantiated but this time using the **FileCheckRegistrar** class where the custom rules classes are matched and registered to the repository. + +This provides the checks (classes that we have written that will be executed during code analysis). It implements the **CheckRegistrar** interface + +The register method registers java checks for a given repository + +The objects are instantiated during code analysis and the methods in CheckRegistrar are called to get the classes that will be used to instantiate checks. + +Then the FileCheckRegistrarTest uses the sonar java api CheckRegistrar to see if the size of the classes added matches the amount of custom classes we want to add. (This test class is not mandatory) + + diff --git a/sonar-plugin/plugin/pom.xml b/sonar-plugin/plugin/pom.xml new file mode 100644 index 0000000..44ce67c --- /dev/null +++ b/sonar-plugin/plugin/pom.xml @@ -0,0 +1,256 @@ + + + + + 4.0.0 + + org.wso2 + 1.0-SNAPSHOT + org.wso2.code.quality.sonar.plugin + sonar-plugin + + WSO2 Code Specific Plugin + WSO2 Specific Custom Java Rules + + + 9.3.0.51899 + 7.11.0.29148 + 8 + 1.20.0.405 + + + 1.14.1.690 + 5.7.0 + 3.19.0 + + + 1.21.0.505 + 3.2.4 + 3.8.0 + + + 1.7.30 + 4.0 + 6.0 + 4.3.3.RELEASE + 4.13.2 + 5.25.52 + 4.9.0-m3 + + + + + + + org.sonarsource.sonarqube + sonar-plugin-api + ${sonarqube.version} + provided + + + org.sonarsource.java + java-checks + ${sonarjava.version} + compile + + + org.sonarsource.java + sonar-java-plugin + sonar-plugin + ${sonarjava.version} + provided + + + org.sonarsource.analyzer-commons + sonar-analyzer-commons + ${sonaranalyzer.commons.version} + + + + org.sonarsource.java + java-checks-testkit + ${sonarjava.version} + test + + + org.junit.jupiter + junit-jupiter + ${junit.jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + ${junit.jupiter.version} + test + + + org.assertj + assertj-core + ${assertj.core.version} + test + + + + + + + org.sonarsource.sonar-packaging-maven-plugin + sonar-packaging-maven-plugin + ${sonar.packaging.maven.plugin.version} + true + + java-custom + Java Custom Rules + org.wso2.code.quality.sonar.plugin.RulesPlugin + true + true + 8.9 + java:${sonarjava.version} + + + + + org.apache.maven.plugins + maven-shade-plugin + ${maven.shade.plugin.version} + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + package + + shade + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + 1.8 + 1.8 + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy + test-compile + + copy + + + + + org.slf4j + slf4j-api + ${slf4j.api.version} + jar + + + org.apache.commons + commons-collections4 + ${commons.collections4.version} + jar + + + javax + javaee-api + ${javaee.api.version} + jar + + + org.springframework + spring-webmvc + ${spring.framework.version} + jar + + + org.springframework + spring-web + ${spring.framework.version} + jar + + + org.springframework + spring-context + ${spring.framework.version} + jar + + + junit + junit + ${junit.version} + jar + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.application.authentication.framework + + ${wso2.carbon.identity.framework.version} + jar + + + org.wso2.carbon + org.wso2.carbon.user.api + ${wso2.carbon.user.api.version} + jar + + + target/test-jars + + + + unpack-dependencies + package + + unpack-dependencies + + + system + META-INF/*.SF,META-INF/*.DSA,META-INF/*.RSA + junit,org.mockito,org.hamcrest + target/classes + + + + + + + diff --git a/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/FileCheckRegistrar.java b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/FileCheckRegistrar.java new file mode 100644 index 0000000..3627dd0 --- /dev/null +++ b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/FileCheckRegistrar.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin; + +import org.sonar.plugins.java.api.CheckRegistrar; +import org.sonar.plugins.java.api.JavaCheck; +import org.sonarsource.api.sonarlint.SonarLintSide; + +import java.util.List; + +import static org.wso2.code.quality.sonar.plugin.util.PluginConstants.WSO2_REPOSITORY_KEY; + +/** + * Provide the "checks" (implementations of rules) classes that are going be executed during + * source code analysis. + * + * This class is a batch extension by implementing the {@link org.sonar.plugins.java.api.CheckRegistrar} interface. + */ +@SonarLintSide +public class FileCheckRegistrar implements CheckRegistrar { + + /** + * Register the classes that will be used to instantiate checks during analysis. + */ + @Override + public void register(RegistrarContext registrarContext) { + + // Call to registerClassesForRepository to associate the classes with the correct repository key. + registrarContext.registerClassesForRepository(WSO2_REPOSITORY_KEY, checkClasses(), + testCheckClasses()); + } + + /** + * Lists all the main checks provided by the plugin. + */ + public static List> checkClasses() { + + return RulesList.getJavaChecks(); + } + + /** + * Lists all the test checks provided by the plugin. + */ + public static List> testCheckClasses() { + + return RulesList.getJavaTestChecks(); + } +} diff --git a/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/RulesDefinitionImpl.java b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/RulesDefinitionImpl.java new file mode 100644 index 0000000..2774cb6 --- /dev/null +++ b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/RulesDefinitionImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.code.quality.sonar.plugin; + +import org.sonar.api.server.rule.RulesDefinition; +import org.sonarsource.analyzer.commons.RuleMetadataLoader; + +import static org.wso2.code.quality.sonar.plugin.util.PluginConstants.JAVA; +import static org.wso2.code.quality.sonar.plugin.util.PluginConstants.RESOURCE_PATH; +import static org.wso2.code.quality.sonar.plugin.util.PluginConstants.WSO2_REPOSITORY_KEY; +import static org.wso2.code.quality.sonar.plugin.util.PluginConstants.WSO2_REPOSITORY_NAME; + +import java.util.ArrayList; + +/** + * Declare rule metadata in server repository of rules. + * That allows to list the rules in the page "Rules". + */ +public class RulesDefinitionImpl implements RulesDefinition { + + @Override + public void define(Context context) { + + NewRepository repository = context.createRepository(WSO2_REPOSITORY_KEY, JAVA).setName(WSO2_REPOSITORY_NAME); + RuleMetadataLoader ruleMetadataLoader = new RuleMetadataLoader(RESOURCE_PATH); + ruleMetadataLoader.addRulesByAnnotatedClass(repository, new ArrayList<>(RulesList.getChecks())); + repository.done(); + } +} diff --git a/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/RulesList.java b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/RulesList.java new file mode 100644 index 0000000..737477a --- /dev/null +++ b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/RulesList.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.code.quality.sonar.plugin; + +import org.sonar.plugins.java.api.JavaCheck; +import org.wso2.code.quality.sonar.plugin.custom.check.authenticator.ClassLevelVariablesInServicesCheck; +import org.wso2.code.quality.sonar.plugin.custom.check.licenseheader.IncInCommentsCheck; +import org.wso2.code.quality.sonar.plugin.custom.check.tenantflow.MissingEndTenantFlowCheck; +import org.wso2.code.quality.sonar.plugin.custom.check.tenantflow.TenantFlowCheck; +import org.wso2.code.quality.sonar.plugin.custom.check.variable.VariableLengthCheck; +import org.wso2.code.quality.sonar.plugin.custom.check.variable.classname.ClassNameCheck; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Class that returns the collection of all the custom checks. + */ +public final class RulesList { + + private RulesList() { + + // Preventing objects to be created from this class. + } + + public static List> getChecks() { + + List> checks = new ArrayList<>(getJavaChecks()); + return Collections.unmodifiableList(checks); + } + + /** + * Get the rules which are going to target MAIN code only. + */ + public static List> getJavaChecks() { + + return Collections.unmodifiableList(Arrays.asList( + IncInCommentsCheck.class, + VariableLengthCheck.class, + ClassNameCheck.class, + TenantFlowCheck.class, + MissingEndTenantFlowCheck.class, + ClassLevelVariablesInServicesCheck.class)); + } + + /** + * Get the rules which are going to target TEST code only. + */ + public static List> getJavaTestChecks() { + + return Collections.emptyList(); + } +} diff --git a/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/RulesPlugin.java b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/RulesPlugin.java new file mode 100644 index 0000000..5144755 --- /dev/null +++ b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/RulesPlugin.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.code.quality.sonar.plugin; + +import org.sonar.api.Plugin; + +/** + * Entry point of your plugin containing your custom rules. + */ +public class RulesPlugin implements Plugin { + + @Override + public void define(Context context) { + + // Server extensions -> objects are instantiated during server startup. + context.addExtension(RulesDefinitionImpl.class); + + // Batch extensions -> objects are instantiated during code analysis. + context.addExtension(FileCheckRegistrar.class); + } +} diff --git a/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/authenticator/ClassLevelVariablesInServicesCheck.java b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/authenticator/ClassLevelVariablesInServicesCheck.java new file mode 100644 index 0000000..382f588 --- /dev/null +++ b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/authenticator/ClassLevelVariablesInServicesCheck.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.custom.check.authenticator; + +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.JavaFileScanner; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.semantic.Symbol; +import org.sonar.plugins.java.api.semantic.Type; +import org.sonar.plugins.java.api.tree.BaseTreeVisitor; +import org.sonar.plugins.java.api.tree.ClassTree; +import org.sonar.plugins.java.api.tree.MethodTree; +import org.sonar.plugins.java.api.tree.TypeTree; +import org.sonar.plugins.java.api.tree.VariableTree; + +import java.util.List; + +/** + * Custom check to see if class level variables exist in specific services. + */ +@Rule(key = "ClassLevelVariablesInServicesCheck") +public class ClassLevelVariablesInServicesCheck extends BaseTreeVisitor implements JavaFileScanner { + + private JavaFileScannerContext context; + private boolean inMethod; + private boolean annotationExists; + private static final String MESSAGE = "Do not use class level variables in WSO2 Specific Services."; + + @Override + public void scanFile(JavaFileScannerContext context) { + + this.context = context; + scan(context.getTree()); + } + + @Override + public void visitClass(ClassTree tree) { + + annotationExists = false; + inMethod = false; + + setAnnotationIfExists(tree.symbol()); + checkAnnotationInInterfaces(tree.superInterfaces()); + checkAnnotationInSuperClass(tree.superClass()); + + super.visitClass(tree); + } + + @Override + public void visitVariable(VariableTree tree) { + + if (annotationExists && (!inMethod) && (!tree.symbol().isStatic() || !tree.symbol().isFinal())) { + context.reportIssue(this, tree, MESSAGE); + } + super.visitVariable(tree); + } + + @Override + public void visitMethod(MethodTree tree) { + + inMethod = true; + super.visitMethod(tree); + } + + private void checkAnnotationInInterfaces(List interfaces) { + + // Checks if immediate interfaces implemented have the annotation 'WSO2SpecificService'. + for (TypeTree currentInterface : interfaces) { + setAnnotationIfExists(currentInterface.symbolType().symbol()); + if (annotationExists) { + return; + } + } + } + + private void checkAnnotationInSuperClass(TypeTree superClass) { + + // Checks if any of the superclasses has the annotation 'WSO2SpecificService'. + while (superClass != null) { + setAnnotationIfExists(superClass.symbolType().symbol()); + // Checks if any of the interfaces in the inheritance chain include the annotation 'WSO2 Specific Service'. + checkSuperInterfacesOfSuperClass(superClass.symbolType().symbol().interfaces()); + if (annotationExists) { + return; + } + superClass = superClass.symbolType().symbol().declaration().superClass(); + } + } + + private void checkSuperInterfacesOfSuperClass(List superInterfacesOfSuperClass) { + + for (Type currentInterface : superInterfacesOfSuperClass) { + setAnnotationIfExists(currentInterface.symbol()); + if (annotationExists) { + return; + } + } + } + + private void setAnnotationIfExists(Symbol.TypeSymbol symbol) { + + if (symbol.metadata().isAnnotatedWith("WSO2SpecificService")) { + annotationExists = true; + } + } +} diff --git a/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/licenseheader/IncInCommentsCheck.java b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/licenseheader/IncInCommentsCheck.java new file mode 100644 index 0000000..86a6715 --- /dev/null +++ b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/licenseheader/IncInCommentsCheck.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.custom.check.licenseheader; + +import org.sonar.check.Rule; +import org.sonar.java.checks.CommentContainsPatternChecker; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.tree.SyntaxTrivia; +import org.sonar.plugins.java.api.tree.Tree; + +import java.util.Collections; +import java.util.List; + +/** + * Custom check to see if Inc. exists in license header. + */ +@Rule(key = "IncInComments") +public class IncInCommentsCheck extends IssuableSubscriptionVisitor { + + private static final String PATTERN = "WSO2 Inc."; + private static final String MESSAGE = "Remove usage of this \"WSO2 Inc.\" comment. Replace it with LLC."; + private final CommentContainsPatternChecker checker = new CommentContainsPatternChecker(this, PATTERN, + MESSAGE); + + @Override + public List nodesToVisit() { + + return Collections.singletonList(Tree.Kind.TRIVIA); + } + + @Override + public void visitTrivia(SyntaxTrivia syntaxTrivia) { + + checker.checkTrivia(syntaxTrivia); + } +} diff --git a/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/tenantflow/MissingEndTenantFlowCheck.java b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/tenantflow/MissingEndTenantFlowCheck.java new file mode 100644 index 0000000..7649ec6 --- /dev/null +++ b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/tenantflow/MissingEndTenantFlowCheck.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.custom.check.tenantflow; + +import org.apache.commons.lang3.StringUtils; +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.JavaFileScanner; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.tree.BaseTreeVisitor; +import org.sonar.plugins.java.api.tree.MethodInvocationTree; +import org.sonar.plugins.java.api.tree.TryStatementTree; + +import static org.wso2.code.quality.sonar.plugin.util.PluginConstants.END_TENANT_FLOW; +import static org.wso2.code.quality.sonar.plugin.util.PluginConstants.START_TENANT_FLOW; + +/** + * Custom check to see if endTenantFlow() is missing after a startTenantFlow() invocation. + */ +@Rule(key = "MissingEndTenantFlowCheck") +public class MissingEndTenantFlowCheck extends BaseTreeVisitor implements JavaFileScanner { + + private JavaFileScannerContext context; + private boolean startTenantFlowExists; + private boolean endTenantFlowExists; + private static final String MESSAGE = "endTenantFlow() is missing in the finally block."; + + @Override + public void scanFile(JavaFileScannerContext context) { + + this.context = context; + scan(context.getTree()); + } + + @Override + public void visitTryStatement(TryStatementTree tree) { + + endTenantFlowExists = false; + startTenantFlowExists = false; + scan(tree.block()); + + if (startTenantFlowExists) { + scan(tree.finallyBlock()); + if (!endTenantFlowExists) { + context.reportIssue(this, tree, MESSAGE); + } + } + } + + @Override + public void visitMethodInvocation(MethodInvocationTree tree) { + + String methodInvocationName = tree.symbol().name(); + + if (StringUtils.containsIgnoreCase(methodInvocationName, START_TENANT_FLOW)) { + startTenantFlowExists = true; + return; + } + + if (startTenantFlowExists && StringUtils.containsIgnoreCase(methodInvocationName, END_TENANT_FLOW)) { + endTenantFlowExists = true; + } + } +} diff --git a/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/tenantflow/TenantFlowCheck.java b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/tenantflow/TenantFlowCheck.java new file mode 100644 index 0000000..7d11e6c --- /dev/null +++ b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/tenantflow/TenantFlowCheck.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.custom.check.tenantflow; + +import org.apache.commons.lang3.StringUtils; +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.JavaFileScanner; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.tree.BaseTreeVisitor; +import org.sonar.plugins.java.api.tree.BreakStatementTree; +import org.sonar.plugins.java.api.tree.ContinueStatementTree; +import org.sonar.plugins.java.api.tree.DoWhileStatementTree; +import org.sonar.plugins.java.api.tree.ForEachStatement; +import org.sonar.plugins.java.api.tree.ForStatementTree; +import org.sonar.plugins.java.api.tree.MethodInvocationTree; +import org.sonar.plugins.java.api.tree.MethodTree; +import org.sonar.plugins.java.api.tree.ReturnStatementTree; +import org.sonar.plugins.java.api.tree.SwitchStatementTree; +import org.sonar.plugins.java.api.tree.ThrowStatementTree; +import org.sonar.plugins.java.api.tree.TryStatementTree; +import org.sonar.plugins.java.api.tree.VariableTree; +import org.sonar.plugins.java.api.tree.WhileStatementTree; + +import static org.wso2.code.quality.sonar.plugin.util.PluginConstants.END_TENANT_FLOW; +import static org.wso2.code.quality.sonar.plugin.util.PluginConstants.START_TENANT_FLOW; + +/** + * Custom check to make sure TenantFlow() invocations are inside try/finally blocks. + * Scenario 1 - Checks whether endTenantFlow() is called in the finally block only. + * Scenario 2 - Checks whether startTenantFlow() is in a try block and it is the first line in a try block. + */ +@Rule(key = "TenantFlowCheck") +public class TenantFlowCheck extends BaseTreeVisitor implements JavaFileScanner { + + private JavaFileScannerContext context; + private boolean inTryStatement; + private boolean inFinallyBlock; + private boolean inFirstLine; + private static final String END_TENANT_FLOW_MESSAGE = "endTenantFlow() should be in the finally block only."; + private static final String START_TENANT_FLOW_MESSAGE = "startTenantFlow() should be the first line in a" + + " try block."; + + @Override + public void scanFile(JavaFileScannerContext context) { + + this.context = context; + scan(context.getTree()); + } + + @Override + public void visitTryStatement(TryStatementTree tree) { + + inTryStatement = true; + inFirstLine = true; + scan(tree.block()); + inTryStatement = false; + + scan(tree.catches()); + + inFinallyBlock = true; + scan(tree.finallyBlock()); + inFinallyBlock = false; + } + + @Override + public void visitMethodInvocation(MethodInvocationTree tree) { + + String methodInvocationName = tree.symbol().name(); + + if (StringUtils.containsIgnoreCase(methodInvocationName, END_TENANT_FLOW) && !inFinallyBlock) { + context.reportIssue(this, tree, END_TENANT_FLOW_MESSAGE); + return; + } + + if (StringUtils.containsIgnoreCase(methodInvocationName, START_TENANT_FLOW) + && (!inTryStatement || !inFirstLine)) { + context.reportIssue(this, tree, START_TENANT_FLOW_MESSAGE); + } + + super.visitMethodInvocation(tree); + } + + @Override + public void visitVariable(VariableTree tree) { + + inFirstLine = false; + super.visitVariable(tree); + } + + @Override + public void visitMethod(MethodTree tree) { + + inFirstLine = false; + super.visitMethod(tree); + } + + @Override + public void visitForStatement(ForStatementTree tree) { + + inFirstLine = false; + super.visitForStatement(tree); + } + + @Override + public void visitForEachStatement(ForEachStatement tree) { + + inFirstLine = false; + super.visitForEachStatement(tree); + } + + @Override + public void visitWhileStatement(WhileStatementTree tree) { + + inFirstLine = false; + super.visitWhileStatement(tree); + } + + @Override + public void visitDoWhileStatement(DoWhileStatementTree tree) { + + inFirstLine = false; + super.visitDoWhileStatement(tree); + } + + @Override + public void visitSwitchStatement(SwitchStatementTree tree) { + + inFirstLine = false; + super.visitSwitchStatement(tree); + } + + @Override + public void visitReturnStatement(ReturnStatementTree tree) { + + inFirstLine = false; + super.visitReturnStatement(tree); + } + + @Override + public void visitThrowStatement(ThrowStatementTree tree) { + + inFirstLine = false; + super.visitThrowStatement(tree); + } + + @Override + public void visitContinueStatement(ContinueStatementTree tree) { + + inFirstLine = false; + super.visitContinueStatement(tree); + } + + @Override + public void visitBreakStatement(BreakStatementTree tree) { + + inFirstLine = false; + super.visitBreakStatement(tree); + } +} diff --git a/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/variable/VariableLengthCheck.java b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/variable/VariableLengthCheck.java new file mode 100644 index 0000000..7c8e28d --- /dev/null +++ b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/variable/VariableLengthCheck.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.custom.check.variable; + +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.JavaFileScanner; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.tree.BaseTreeVisitor; +import org.sonar.plugins.java.api.tree.CatchTree; +import org.sonar.plugins.java.api.tree.ForStatementTree; + +import org.sonar.plugins.java.api.tree.VariableTree; + +/** + * Custom check to see if variable name length exceeds 1. + */ +@Rule(key = "VariableLengthCheck") +public class VariableLengthCheck extends BaseTreeVisitor implements JavaFileScanner { + + private int inForOrCatchLevel; + private JavaFileScannerContext context; + + private static final String MESSAGE = "Variable name length should be more than 1."; + + @Override + public void scanFile(JavaFileScannerContext context) { + + this.context = context; + scan(context.getTree()); + } + + @Override + public void visitForStatement(ForStatementTree tree) { + + inForOrCatchLevel++; + super.visitForStatement(tree); + } + + @Override + public void visitCatch(CatchTree tree) { + + inForOrCatchLevel++; + super.visitCatch(tree); + } + + @Override + public void visitVariable(VariableTree tree) { + + if (tree.simpleName().name().length() <= 1 && inForOrCatchLevel < 1) { + context.reportIssue(this, tree.simpleName(), MESSAGE); + } + + if (inForOrCatchLevel >= 1) { + inForOrCatchLevel--; + } + super.visitVariable(tree); + } +} diff --git a/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/variable/classname/ClassNameCheck.java b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/variable/classname/ClassNameCheck.java new file mode 100644 index 0000000..55acd8b --- /dev/null +++ b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/custom/check/variable/classname/ClassNameCheck.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.custom.check.variable.classname; + +import org.apache.commons.lang3.StringUtils; +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.JavaFileScanner; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.tree.BaseTreeVisitor; +import org.sonar.plugins.java.api.tree.ClassTree; + +import java.util.Objects; + +/** + * Custom check to see if class names contain the word 'Class' or 'Enum'. + */ +@SuppressWarnings("all") +@Rule(key = "ClassNameCheck") +public class ClassNameCheck extends BaseTreeVisitor implements JavaFileScanner { + + private JavaFileScannerContext context; + private static final String MESSAGE = + "Don’t use the word ‘Class’ as part of class names or ‘Enum’ as part of enum class names."; + + @Override + public void scanFile(JavaFileScannerContext context) { + + this.context = context; + scan(context.getTree()); + } + + @Override + public void visitClass(ClassTree tree) { + + String className = Objects.requireNonNull(tree.simpleName()).name(); + + if (StringUtils.containsIgnoreCase(className, "class") || StringUtils.containsIgnoreCase(className, "enum")) { + context.reportIssue(this, tree, MESSAGE); + } + super.visitClass(tree); + } +} diff --git a/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/util/PluginConstants.java b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/util/PluginConstants.java new file mode 100644 index 0000000..335139c --- /dev/null +++ b/sonar-plugin/plugin/src/main/java/org/wso2/code/quality/sonar/plugin/util/PluginConstants.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.util; + +/** + * Constants used in WSO2 Code Specific plugin. + */ +public abstract class PluginConstants { + + private PluginConstants() { + + } + + public static final String JAVA = "java"; + public static final String RESOURCE_PATH = "org/wso2/code/quality/sonar/plugin/rules/descriptions"; + public static final String WSO2_REPOSITORY_KEY = "wso2-llc"; + public static final String WSO2_REPOSITORY_NAME = "WSO2 custom rules"; + public static final String START_TENANT_FLOW = "starttenantflow"; + public static final String END_TENANT_FLOW = "endtenantflow"; + public static final String FULLY_QUALIFIED_NAME_APPLICATION_AUTHENTICATOR = "org.wso2.carbon.identity" + + ".application.authentication.framework.ApplicationAuthenticator"; + public static final String FULLY_QUALIFIED_NAME_USER_STORE_MANAGER = "org.wso2.carbon.user.api.UserStoreManager"; +} diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassLevelVariablesInServicesCheck.html b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassLevelVariablesInServicesCheck.html new file mode 100644 index 0000000..2f3789e --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassLevelVariablesInServicesCheck.html @@ -0,0 +1,39 @@ + + +

Do not declare class level variables in authenticators. Use static and final.

+ +

Noncompliant Code Example

+
+public class BasicAuthenticator extends AbstractApplicationAuthenticator
+        implements LocalApplicationAuthenticator {
+
+    private int number; // Noncompliant
+
+}
+
+
+ +

Compliant Solution

+
+public class BasicAuthenticator extends AbstractApplicationAuthenticator
+        implements LocalApplicationAuthenticator {
+
+    private static final long serialVersionUID = 123; // Compliant
+}
+
diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassLevelVariablesInServicesCheck.json b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassLevelVariablesInServicesCheck.json new file mode 100644 index 0000000..d2212a9 --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassLevelVariablesInServicesCheck.json @@ -0,0 +1,9 @@ +{ + "title": "Do not declare class level variables in authenticators. Use static and final.", + "type": "Bug", + "status": "ready", + "tags": [ + "bugs" + ], + "defaultSeverity": "Critical" +} diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassNameCheck.html b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassNameCheck.html new file mode 100644 index 0000000..700535b --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassNameCheck.html @@ -0,0 +1,33 @@ + + +

Don’t use the word ‘Class’ as part of class names or ‘Enum’ as part of enum class names.

+ +

Noncompliant Code Example

+
+class myClass { }
+class testclass{ }
+enum sampleenum{ }
+enum SampleEnum{ }
+
+ +

Compliant Solution

+
+class student { }
+enum Season{ }
+
diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassNameCheck.json b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassNameCheck.json new file mode 100644 index 0000000..a88f700 --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/ClassNameCheck.json @@ -0,0 +1,9 @@ +{ + "title": "Don’t use the word ‘Class’ as part of class names or ‘Enum’ as part of enum class names.", + "type": "Bug", + "status": "ready", + "tags": [ + "bugs" + ], + "defaultSeverity": "Critical" +} diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/IncInComments.html b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/IncInComments.html new file mode 100644 index 0000000..ce488c2 --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/IncInComments.html @@ -0,0 +1,29 @@ + + +

For a comment having WSO2, should be followed by 'LLC.' and not 'Inc.'.

+ +

Noncompliant Code Example

+
+    // WSO2 Inc.
+
+ +

Compliant Solution

+
+WSO2 LLC.
+
diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/IncInComments.json b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/IncInComments.json new file mode 100644 index 0000000..db6b56c --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/IncInComments.json @@ -0,0 +1,9 @@ +{ + "title": "WSO2 should not be followed by Inc.", + "type": "Bug", + "status": "ready", + "tags": [ + "bugs" + ], + "defaultSeverity": "Critical" +} diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/MissingEndTenantFlowCheck.html b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/MissingEndTenantFlowCheck.html new file mode 100644 index 0000000..1a5eacc --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/MissingEndTenantFlowCheck.html @@ -0,0 +1,46 @@ + + +

endTenantFlow() is missing in the finally block.

+ +

Noncompliant Code Example

+
+try // Noncompliant
+{
+    startTenantFlow();
+    int i;
+
+} catch(Exception e) {
+
+} finally {
+
+}
+
+ +

Compliant Solution

+
+try {
+    startTenantFlow();
+    int i;
+
+} catch(Exception e) {
+
+} finally {
+    endTenantFlow();
+}
+
diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/MissingEndTenantFlowCheck.json b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/MissingEndTenantFlowCheck.json new file mode 100644 index 0000000..6649f01 --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/MissingEndTenantFlowCheck.json @@ -0,0 +1,9 @@ +{ + "title": "endTenantFlow() is missing in the finally block.", + "type": "Bug", + "status": "ready", + "tags": [ + "bugs" + ], + "defaultSeverity": "Critical" +} diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/TenantFlowCheck.html b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/TenantFlowCheck.html new file mode 100644 index 0000000..6070bf0 --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/TenantFlowCheck.html @@ -0,0 +1,52 @@ + + +

startTenantFlow() should be the first line in a try-block. endTenantFlow() should be in a finally block.

+ +

Noncompliant Code Example

+
+void foo(){
+    startTenantFlow(); // Noncompliant
+    endTenantFlow(); // Noncompliant
+
+    try {
+        String myString;
+        int myNum;
+        startTenantFlow(); // Noncompliant
+    } catch(Exception e) {
+        endTenantFlow(); // Noncompliant
+
+    } finally {
+
+    }
+}
+
+
+ +

Compliant Solution

+
+try {
+    startTenantFlow();
+    int i;
+
+} catch(Exception e) {
+
+} finally {
+    endTenantFlow();
+}
+
diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/TenantFlowCheck.json b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/TenantFlowCheck.json new file mode 100644 index 0000000..3aebc60 --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/TenantFlowCheck.json @@ -0,0 +1,9 @@ +{ + "title": "startTenantFlow() should be the first line in a try-block. endTenantFlow() should be in a finally block.", + "type": "Bug", + "status": "ready", + "tags": [ + "bugs" + ], + "defaultSeverity": "Critical" +} diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/VariableLengthCheck.html b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/VariableLengthCheck.html new file mode 100644 index 0000000..d13624c --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/VariableLengthCheck.html @@ -0,0 +1,34 @@ + + +

Don’t use one letter variable names (except ‘e’ for exceptions, ‘i, j’ in for loops)

+ +

Noncompliant Code Example

+
+int i;
+String l;
+boolean p;
+float k;
+char a;
+
+ +

Compliant Solution

+
+int number;
+String name;
+
diff --git a/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/VariableLengthCheck.json b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/VariableLengthCheck.json new file mode 100644 index 0000000..fea42f5 --- /dev/null +++ b/sonar-plugin/plugin/src/main/resources/org/wso2/code/quality/sonar/plugin/rules/descriptions/VariableLengthCheck.json @@ -0,0 +1,9 @@ +{ + "title": "Don’t use one letter variable names (except ‘e’ for exceptions, ‘i, j’ in for loops)", + "type": "Bug", + "status": "ready", + "tags": [ + "bugs" + ], + "defaultSeverity": "Critical" +} diff --git a/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/FileCheckRegistrarTest.java b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/FileCheckRegistrarTest.java new file mode 100644 index 0000000..fa6974a --- /dev/null +++ b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/FileCheckRegistrarTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.code.quality.sonar.plugin; + +import org.junit.jupiter.api.Test; +import org.sonar.plugins.java.api.CheckRegistrar; + +import static org.assertj.core.api.Assertions.assertThat; + +class FileCheckRegistrarTest { + + @Test + void checkNumberRules() { + + CheckRegistrar.RegistrarContext context = new CheckRegistrar.RegistrarContext(); + FileCheckRegistrar registrar = new FileCheckRegistrar(); + registrar.register(context); + + assertThat(context.checkClasses()).hasSize(6); + } + +} diff --git a/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/RulesDefinitionImplTest.java b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/RulesDefinitionImplTest.java new file mode 100644 index 0000000..dc2650c --- /dev/null +++ b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/RulesDefinitionImplTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.code.quality.sonar.plugin; + +import org.junit.jupiter.api.Test; +import org.sonar.api.server.rule.RulesDefinition.Param; +import org.sonar.api.server.rule.RulesDefinition.Repository; +import org.sonar.api.server.rule.RulesDefinition.Rule; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.wso2.code.quality.sonar.plugin.util.PluginConstants.WSO2_REPOSITORY_KEY; +import static org.wso2.code.quality.sonar.plugin.util.PluginConstants.WSO2_REPOSITORY_NAME; + +class RulesDefinitionImplTest { + + @Test + void test() { + + RulesDefinitionImpl rulesDefinitionImpl = new RulesDefinitionImpl(); + org.sonar.api.server.rule.RulesDefinition.Context context = new org.sonar.api.server.rule.RulesDefinition.Context(); + rulesDefinitionImpl.define(context); + Repository repository = context.repository(WSO2_REPOSITORY_KEY); + + assert repository != null; + assertThat(repository.name()).isEqualTo(WSO2_REPOSITORY_NAME); + assertThat(repository.language()).isEqualTo("java"); + assertThat(repository.rules()).hasSize(RulesList.getChecks().size()); + assertThat(repository.rules().stream().filter(Rule::template)).isEmpty(); + assertAllRuleParametersHaveDescription(repository); + } + + private static void assertAllRuleParametersHaveDescription(Repository repository) { + + for (Rule rule : repository.rules()) { + for (Param param : rule.params()) { + assertThat(param.description()).as("description for " + param.key()).isNotEmpty(); + } + } + } + +} diff --git a/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/RulesPluginTest.java b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/RulesPluginTest.java new file mode 100644 index 0000000..3922264 --- /dev/null +++ b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/RulesPluginTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.code.quality.sonar.plugin; + +import org.junit.jupiter.api.Test; + +import org.sonar.api.Plugin; +import org.sonar.api.SonarEdition; +import org.sonar.api.SonarProduct; +import org.sonar.api.SonarQubeSide; +import org.sonar.api.SonarRuntime; +import org.sonar.api.utils.Version; + +import static org.assertj.core.api.Assertions.assertThat; + +class RulesPluginTest { + + @Test + void testName() { + + Plugin.Context context = new Plugin.Context(new MockedSonarRuntime()); + new RulesPlugin().define(context); + assertThat(context.getExtensions()).hasSize(2); + } + + private static class MockedSonarRuntime implements SonarRuntime { + + @Override + public Version getApiVersion() { + + return Version.create(7, 9); + } + + @Override + public SonarProduct getProduct() { + + return SonarProduct.SONARQUBE; + } + + @Override + public SonarQubeSide getSonarQubeSide() { + + return SonarQubeSide.SCANNER; + } + + @Override + public SonarEdition getEdition() { + + return SonarEdition.COMMUNITY; + } + } + +} diff --git a/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/ClassLevelVariablesInServicesCheckTest.java b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/ClassLevelVariablesInServicesCheckTest.java new file mode 100644 index 0000000..dda2fac --- /dev/null +++ b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/ClassLevelVariablesInServicesCheckTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.custom.check; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; +import org.sonar.java.checks.verifier.FilesUtils; +import org.wso2.code.quality.sonar.plugin.custom.check.authenticator.ClassLevelVariablesInServicesCheck; + +class ClassLevelVariablesInServicesCheckTest { + + @Test + void test() { + + CheckVerifier.newVerifier() + .onFile("src/test/sonar/rules/test/files/ClassLevelVariablesInServicesCheck.java") + .withCheck(new ClassLevelVariablesInServicesCheck()) + .withClassPath(FilesUtils.getClassPath("target/test-jars")) + .verifyIssues(); + } +} diff --git a/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/ClassNameCheckTest.java b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/ClassNameCheckTest.java new file mode 100644 index 0000000..4fab654 --- /dev/null +++ b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/ClassNameCheckTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.custom.check; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; +import org.wso2.code.quality.sonar.plugin.custom.check.variable.classname.ClassNameCheck; + +class ClassNameCheckTest { + + @Test + void test() { + + CheckVerifier.newVerifier() + .onFile("src/test/sonar/rules/test/files/ClassNameCheck.java") + .withCheck(new ClassNameCheck()) + .verifyIssues(); + } +} diff --git a/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/IncInCommentsCheckTest.java b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/IncInCommentsCheckTest.java new file mode 100644 index 0000000..e744afe --- /dev/null +++ b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/IncInCommentsCheckTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.code.quality.sonar.plugin.custom.check; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; +import org.wso2.code.quality.sonar.plugin.custom.check.licenseheader.IncInCommentsCheck; + +class IncInCommentsCheckTest { + + @Test + void test() { + + CheckVerifier.newVerifier() + .onFile("src/test/sonar/rules/test/files/IncInCommentsCheck.java") + .withCheck(new IncInCommentsCheck()) + .verifyIssues(); + } +} diff --git a/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/MissingEndTenantFlowCheckTest.java b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/MissingEndTenantFlowCheckTest.java new file mode 100644 index 0000000..895491a --- /dev/null +++ b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/MissingEndTenantFlowCheckTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.custom.check; + + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; +import org.wso2.code.quality.sonar.plugin.custom.check.tenantflow.MissingEndTenantFlowCheck; + +class MissingEndTenantFlowCheckTest { + + @Test + void test() { + CheckVerifier.newVerifier() + .onFile("src/test/sonar/rules/test/files/MissingEndTenantFlowCheck.java") + .withCheck(new MissingEndTenantFlowCheck()) + .verifyIssues(); + } +} diff --git a/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/TenantFlowCheckTest.java b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/TenantFlowCheckTest.java new file mode 100644 index 0000000..114aeed --- /dev/null +++ b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/TenantFlowCheckTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.custom.check; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; +import org.wso2.code.quality.sonar.plugin.custom.check.tenantflow.TenantFlowCheck; + +class TenantFlowCheckTest { + + @Test + void test() { + + CheckVerifier.newVerifier() + .onFile("src/test/sonar/rules/test/files/TenantFlowCheck.java") + .withCheck(new TenantFlowCheck()) + .verifyIssues(); + } +} diff --git a/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/VariableLengthCheckTest.java b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/VariableLengthCheckTest.java new file mode 100644 index 0000000..49bcc52 --- /dev/null +++ b/sonar-plugin/plugin/src/test/java/org/wso2/code/quality/sonar/plugin/custom/check/VariableLengthCheckTest.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.code.quality.sonar.plugin.custom.check; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; +import org.wso2.code.quality.sonar.plugin.custom.check.variable.VariableLengthCheck; + +class VariableLengthCheckTest { + + @Test + void test() { + + CheckVerifier.newVerifier() + .onFile("src/test/sonar/rules/test/files/VariableLengthCheck.java") + .withCheck(new VariableLengthCheck()) + .verifyIssues(); + } +} diff --git a/sonar-plugin/plugin/src/test/sonar/rules/test/files/ClassLevelVariablesInServicesCheck.java b/sonar-plugin/plugin/src/test/sonar/rules/test/files/ClassLevelVariablesInServicesCheck.java new file mode 100644 index 0000000..c0217e1 --- /dev/null +++ b/sonar-plugin/plugin/src/test/sonar/rules/test/files/ClassLevelVariablesInServicesCheck.java @@ -0,0 +1,56 @@ +/** + *This file is the sample code against we run our unit test. + *It is placed src/test/sonar/rules/test/files in order to not be part of the maven compilation. + **/ +interface SecondInterface extends MyInterface { + + int number; + static final long serialVersionUID; +} + +@WSO2SpecificService +class WSO2SpecificServiceClass { + + private int number; // Noncompliant + private static final long serialVersionUID = 123; +} + +class TestClass implements MyInterface { + private int number; // Noncompliant + private static final long serialVersionUID = 123; +} + +class TestClass2 extends TestClass { + private int number; // Noncompliant + private static final long serialVersionUID = 123; +} + +class TestClass3 extends TestClass2 { + private int number; // Noncompliant + private static final long serialVersionUID = 123; +} + +@WSO2SpecificService +interface MyInterface { + + int number; + static final long serialVersionUID; +} + +public class SecondClass implements MyInterface { + + private int number; // Noncompliant + private static final long serialVersionUID = 123; + + @WSO2SpecificService + boolean myMethod() { + return true; + } +} + +@interface WSO2SpecificService{ } + +class Registry extends SecondClass { + private int number; // Noncompliant + private static final long serialVersionUID = 123; +} diff --git a/sonar-plugin/plugin/src/test/sonar/rules/test/files/ClassNameCheck.java b/sonar-plugin/plugin/src/test/sonar/rules/test/files/ClassNameCheck.java new file mode 100644 index 0000000..64088a9 --- /dev/null +++ b/sonar-plugin/plugin/src/test/sonar/rules/test/files/ClassNameCheck.java @@ -0,0 +1,15 @@ +/** + *This file is the sample code against we run our unit test. + *It is placed src/test/sonar/rules/test/files in order to not be part of the maven compilation. + **/ +class student { } // Compliant + +class myClass { } // Noncompliant + +class testclass extends myclass{ } // Noncompliant + +enum sampleenum{ } // Noncompliant + +enum SampleEnum{ } // Noncompliant + +enum Season{ } // Compliant diff --git a/sonar-plugin/plugin/src/test/sonar/rules/test/files/IncInCommentsCheck.java b/sonar-plugin/plugin/src/test/sonar/rules/test/files/IncInCommentsCheck.java new file mode 100644 index 0000000..dac1342 --- /dev/null +++ b/sonar-plugin/plugin/src/test/sonar/rules/test/files/IncInCommentsCheck.java @@ -0,0 +1,19 @@ +/** + *This file is the sample code against we run our unit test. + *It is placed src/test/sonar/rules/test/files in order to not be part of the maven compilation. + **/ +// foo + +// Noncompliant@+1 +// Noncompliant@+1 {{Remove usage of this "WSO2 Inc." comment. Replace it with LLC.}} +// wso2 inc. + +// Noncompliant@+1 +// WSO2 Inc. + +// Noncompliant@+1 +// [WSO2 Inc.] + +// WSO2 LLC +// wso2 llc +// WSo2 LLC diff --git a/sonar-plugin/plugin/src/test/sonar/rules/test/files/MissingEndTenantFlowCheck.java b/sonar-plugin/plugin/src/test/sonar/rules/test/files/MissingEndTenantFlowCheck.java new file mode 100644 index 0000000..0f32cf8 --- /dev/null +++ b/sonar-plugin/plugin/src/test/sonar/rules/test/files/MissingEndTenantFlowCheck.java @@ -0,0 +1,61 @@ +/** + *This file is the sample code against we run our unit test. + *It is placed src/test/sonar/rules/test/files in order to not be part of the maven compilation. + **/ +class myClass { + + int num; + + void startTenantFlow() { + // Do nothing. + } + + void endTenantFlow() { + // Do nothing. + } + + void foo() { + // Do nothing. + } + + void mainMethod() { + + foo(); + endTenantFlow(); + + try // Noncompliant + { + startTenantFlow(); + int i; + + + } catch (Exception e) { + + } finally { + } + + try { + String myString; + int myNum; + startTenantFlow(); + + } catch (Exception e) { + + } finally { + endTenantFlow(); // Compliant + } + + try // Noncompliant + { + String myString; + int myNum; + startTenantFlow(); + + } catch (Exception e) { + + } finally { + int i; + } + + } +} diff --git a/sonar-plugin/plugin/src/test/sonar/rules/test/files/TenantFlowCheck.java b/sonar-plugin/plugin/src/test/sonar/rules/test/files/TenantFlowCheck.java new file mode 100644 index 0000000..bb48fcf --- /dev/null +++ b/sonar-plugin/plugin/src/test/sonar/rules/test/files/TenantFlowCheck.java @@ -0,0 +1,71 @@ +/** + *This file is the sample code against we run our unit test. + *It is placed src/test/sonar/rules/test/files in order to not be part of the maven compilation. + **/ +class myClass { + + int num; + + void startTenantFlow(){ + // Do nothing. + } + + void endTenantFlow(){ + // Do nothing. + } + + void mainMethod() { + + startTenantFlow(); // Noncompliant + endTenantFlow(); // Noncompliant + + try { + startTenantFlow(); + int i; + + + } catch(Exception e) { + + } finally { + endTenantFlow(); + } + + try { + endTenantFlow(); // Noncompliant + + } catch(Exception e) { + startTenantFlow(); // Noncompliant + endTenantFlow(); // Noncompliant + + } finally { + endTenantFlow(); + } + + } + + void anotherMethod() { + + try { + startTenantFlow(); + int i; + + } catch(Exception e) { + startTenantFlow(); // Noncompliant + + } finally { + endTenantFlow(); + } + + try { + String myString; + int myNum; + startTenantFlow(); // Noncompliant + + } catch(Exception e) { + + } finally { + endTenantFlow(); + } + + } +} diff --git a/sonar-plugin/plugin/src/test/sonar/rules/test/files/VariableLengthCheck.java b/sonar-plugin/plugin/src/test/sonar/rules/test/files/VariableLengthCheck.java new file mode 100644 index 0000000..25f5eb9 --- /dev/null +++ b/sonar-plugin/plugin/src/test/sonar/rules/test/files/VariableLengthCheck.java @@ -0,0 +1,46 @@ +/** + *This file is the sample code against we run our unit test. + *It is placed src/test/sonar/rules/test/files in order to not be part of the maven compilation. + **/ +class myClass { + + int a; // Noncompliant + boolean b; // Noncompliant + char c; // Noncompliant + + float aa; // Compliant + int bb; // Compliant + boolean cc; // Compliant + + void foo() { + for (int i = 0; i < 10; i++) { + // Do something. + } + } + + void foo() { + int s = 3; // Noncompliant + int sb; + + for (int i = 0; i < 10; i++) { + int ab; + int a; // Noncompliant + + for (int z = 0; z < 10; z++) { + // Do something. + int zz; + int z; // Noncompliant + } + } + } + + void foo() { + try { + + } catch (Exception e) { + + } finally { + + } + } +}