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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions sonar-plugin/docs/deployment-of-plugin.md
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 sonar-plugin/docs/how-to-write-custom-rules-in-sonar.md
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.

***

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.



46 changes: 46 additions & 0 deletions sonar-plugin/docs/overview-of-plugin.md
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)


Loading