-
Notifications
You must be signed in to change notification settings - Fork 27
WSO2 Code Specific Plugin #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
areebniyas
wants to merge
14
commits into
wso2:sonar-custom-rules
Choose a base branch
from
areebniyas:sonar-custom-rules
base: sonar-custom-rules
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
32bb28a
adding sonar plugin directory
areebniyas 229abf6
reformat code
areebniyas 248457d
adding and updating license headers
areebniyas 6df4bc8
refactored package name
areebniyas 40648be
adding comments to test files
areebniyas 893ede1
added docs and renamed file structure
areebniyas 67a5314
adding 3 md files
areebniyas 0f760ff
included all versions to properties
areebniyas 58d8b0b
added line spacing and clearer comments
areebniyas ec1e373
Update sonar-plugin/docs/how-to-write-custom-rules-in-sonar.md
areebniyas 6bf5f16
Update sonar-plugin/docs/how-to-write-custom-rules-in-sonar.md
areebniyas 20f6ed4
Update sonar-plugin/docs/how-to-write-custom-rules-in-sonar.md
areebniyas 5fb213c
Update sonar-plugin/docs/how-to-write-custom-rules-in-sonar.md
areebniyas dae3a36
Updating docs and formatiing
areebniyas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
210 changes: 210 additions & 0 deletions
210
sonar-plugin/docs/how-to-write-custom-rules-in-sonar.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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): | ||
|
|
||
| ``` | ||
| <artifactItem> | ||
| <groupId>org.wso2.carbon.identity.framework</groupId> | ||
| <artifactId>org.wso2.carbon.identity.application.authentication.framework</artifactId> | ||
| <version>5.25.52</version> | ||
| <type>jar</type> | ||
| </artifactItem> | ||
| <artifactItem> | ||
| <groupId>org.wso2.carbon</groupId> | ||
| <artifactId>org.wso2.carbon.user.api</artifactId> | ||
| <version>4.9.0-m3</version> | ||
| <type>jar</type> | ||
| </artifactItem> | ||
| ``` | ||
|
|
||
| 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. | ||
|
|
||
| *** | ||
areebniyas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| 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<String> 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<String> 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. | ||
|
|
||
|
|
||
|
|
||
areebniyas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <pluginClass>org.wso2.RulesPlugin</pluginClass> | ||
|
|
||
| **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) | ||
|
|
||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.