sonarlint-gradle-plugin icon indicating copy to clipboard operation
sonarlint-gradle-plugin copied to clipboard

Gradle plugin for SonarLint code analysis.

sonarlint-gradle-plugin

build workflow Gradle plugin

Gradle plugin for sonarlint code analysis. Supports Java, Node, Kotlin and Scala. But possible to configure it for other languages that sonarlint has plugins for like Ruby and Golang.

  1. Usage
    1. Apply to your project
    2. Tasks in this plugin
    3. Configure sonarlint Plugin
    4. Apply to Java project
    5. Apply to Node project
    6. Apply to Kotlin project
    7. Apply to Scala project
    8. Apply to Xxx project
  2. sonarlint version mapping
  3. sonarlint rules
    1. Suppress rules in Java
    2. Suppress rules in most languages
  4. sonarlint CI reports
    1. Github actions using Spotbugs format
    2. Github actions using SARIF format
    3. AWS CodeCatalyst using SARIF format
    4. Azure DevOps using SARIF format
  5. sonarlint plugins
  6. Release notes

Usage

Apply to your project

Apply the plugin to your project.

plugins {
  id 'se.solrike.sonarlint' version '2.0.0'
}

Gradle 7.0 or later must be used.

Tasks in this plugin

The plugin defines two tasks; one for the actual linting and one to list available rules and configuration for the rules. In a Java project there will be one sonarlint task automatically created for each source set. Typically sonarlintMain and sonarlintTest, see more below. The task for listing the rules sonarlintListRules has to be manually created, see more below.

task sonarlintListRules(type: se.solrike.sonarlint.SonarlintListRules) {
  description = 'List sonarlint rules'
  group = 'verification'
}

Configure sonarlint Plugin

Configure sonarlint extension to configure the behavior of tasks:

sonarlint {
  excludeRules = ['java:S1186']
  includeRules = ['java:S1176', 'java:S1696', 'java:S4266']
  ignoreFailures = false
  maxIssues = 0 // default 0
  reportsDir = 'someFolder' // default build/reports/sonarlint
  // note that rule parameter names are case sensitive
  ruleParameters = [
    'java:S1176' : [
      'forClasses':'**.api.**',      // need javadoc for public methods in package matching 'api'
      'exclusion': '**.private.**'] // do not need javadoc for classes under 'private'. Default is **.internal.**
  ]
  showIssues = true // default true
}

Configure sonarlintPlugins to apply any sonarlint plugin:

dependencies {
  sonarlintPlugins 'org.sonarsource.html:sonar-html-plugin:3.6.0.3106'
  sonarlintPlugins 'org.sonarsource.java:sonar-java-plugin:7.30.1.34514'
  sonarlintPlugins 'org.sonarsource.javascript:sonar-javascript-plugin:10.0.1.20755' // both JS and TS but requires com.github.node-gradle.node
  sonarlintPlugins 'org.sonarsource.kotlin:sonar-kotlin-plugin:2.13.0.2116'
  sonarlintPlugins 'org.sonarsource.php:sonar-php-plugin:3.25.0.9077'
  sonarlintPlugins 'org.sonarsource.python:sonar-python-plugin:3.17.0.10029'
  sonarlintPlugins 'org.sonarsource.slang:sonar-go-plugin:1.12.0.4259'
  sonarlintPlugins 'org.sonarsource.slang:sonar-ruby-plugin:1.11.0.3905'
  sonarlintPlugins 'org.sonarsource.slang:sonar-scala-plugin:1.11.0.3905'
  sonarlintPlugins 'org.sonarsource.sonarlint.omnisharp:sonarlint-omnisharp-plugin:1.4.0.50839'
  sonarlintPlugins 'org.sonarsource.text:sonar-text-plugin:2.0.1.611'
  sonarlintPlugins 'org.sonarsource.xml:sonar-xml-plugin:2.6.1.3686'
  // include a plugin not in Maven repo but can be grabbed from the IDEs
  sonarlintPlugins files("${System.getProperty('user.home')}/.p2/pool/plugins/org.sonarlint.eclipse.core_7.2.1.42550/plugins/sonar-secrets-plugin-1.1.0.36766.jar")
  sonarlintPlugins files("./sonar-cfamily-plugin-6.43.0.61486.jar")
}

Apply to Java project

Apply this plugin with the java plugin to your project, then Sonarlint task will be generated for each existing sourceSet. E.g. sonarlintMain and sonarlintTest

If the java-test-fixtures plugin is applied then the task will be called sonarlintTestFixtures since there will be a source set called testFixtures.

Configure the sonarlint Task

Configure Sonarlint directly, to set task-specific properties.

// Example to configure HTML report
sonarlintMain {
  reports {
    text.enabled = false // default false
    html {
      enabled = true // default false
      // default location build/reports/sonarlint/sonarlintMain.html
      outputLocation = layout.buildDirectory.file('my_sonarlint_super_report.html')
    }
    xml.enabled = false // default false
    sarif.enabled = false // default false
  }
}
// Example to configure different rules etc for the test source
sonarlintTest {
  excludeRules = ['java:S1001']
  includeRules = ['java:S1002', 'java:S1003']
  ignoreFailures = true
}
// Exclude files from the scan (e.g. generated source code):
sonarlintMain {
  exclude '**/org/example/some/package1/*'
  exclude '**/org/example/some/package2/*'
}

Apply to Node project

Apply this plugin with the com.github.node-gradle.node plugin to your project and configure it to download node executable, then Sonarlint task will be generated for main and test classes E.g. sonarlintNodeMain and sonarlintNodeTest

Unlike with the Java plugin the source sets needs to be assigned manually.

Sonarlint needs a node executable in order to perform the analysis. This plugin will get the location of node executable from the Node plugin and the Node plugin needs to be configured to download node.

Configure the sonarlint Task when using Node plugin

This example has TypeScript code under projects/ and src/

plugins {
  id 'base'
  id 'com.github.node-gradle.node' version '5.0.0'
  id 'se.solrike.sonarlint' version '2.0.0'
}
repositories {
  mavenCentral()
}
node {
  // Version of node to use.
  version = '18.9.1'
  // If empty, the plugin will use the npm command bundled with Node.js
  npmVersion = ''
  // If true, it will download node using above parameters.
  download = true
}
sonarlintNodeMain {
  ignoreFailures = false
  source = fileTree('src')
  exclude '**/*.spec.ts'
}
sonarlintNodeTest {
  ignoreFailures = true
  source = fileTree('src')
  include '**/*.spec.ts'
  isTestSource = true
}

dependencies {
  sonarlintPlugins 'org.sonarsource.html:sonar-html-plugin:3.6.0.3106'
  sonarlintPlugins 'org.sonarsource.javascript:sonar-javascript-plugin:10.0.1.20755' // both JS and TS
  sonarlintPlugins 'org.sonarsource.xml:sonar-xml-plugin:2.6.1.3686'
}

Apply to Kotlin project

If the project has the kotlin plugin applied then that means the Java plugin is applied too. The Sonarlint task will be generated for main and test classes. E.g. sonarlintMain and sonarlintTest. The source code and resources for the 'main' and 'test' source sets will be scanned using all the sonarlint plugins you configure. So if you have both Java and Kotlin source code then configure all the language plugins for your code. Like both org.sonarsource.java:sonar-java-plugin:7.30.1.34514 and org.sonarsource.kotlin:sonar-kotlin-plugin:2.20.0.4382 and any additionally plugin you need.

Typical gradle.build.kts:

plugins {
  // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin.
  id("org.jetbrains.kotlin.jvm") version "1.7.21"
  id("se.solrike.sonarlint") version "2.0.0"
}

repositories {
  mavenCentral()
}

dependencies {
  // Align versions of all Kotlin components
  implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
  // Use the Kotlin JDK 8 standard library.
  implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
  // This dependency is used internally, and not exposed to consumers on their own compile classpath.
  implementation("com.google.guava:guava:31.0.1-jre")
  // Use the Kotlin test library.
  testImplementation("org.jetbrains.kotlin:kotlin-test")
  // Use the Kotlin JUnit integration.
  testImplementation("org.jetbrains.kotlin:kotlin-test-junit")

  sonarlintPlugins("org.sonarsource.kotlin:sonar-kotlin-plugin:2.20.0.4382")
}

// configure the tasks directly since the overloaded "extensions" like sonarlintMain will not work in Kotlin DSL
tasks.sonarlintMain {
  maxIssues.set(1)
}

Apply to Scala project

If the project has the scala plugin applied then that means the Java plugin is applied too. The Sonarlint task will be generated for main and test classes. E.g. sonarlintMain and sonarlintTest. The source code and resources for the 'main' and 'test' source sets will be scanned using all the sonarlint plugins you configure. So if you have both Java and Scala source code then configure all the language plugins for your code. Like both org.sonarsource.java:sonar-java-plugin:7.17.0.31219 and org.sonarsource.slang:sonar-scala-plugin:1.11.0.3905 and any additionally plugin you need.

plugins {
  id 'scala'
  id 'se.solrike.sonarlint' version '2.0.0'
}

repositories {
  mavenCentral()
}

dependencies {
  // Use Scala 2.13 in our library project
  implementation 'org.scala-lang:scala-library:2.13.7'
  // This dependency is used internally, and not exposed to consumers on their own compile classpath.
  implementation 'com.google.guava:guava:31.0.1-jre'
  // Use Scalatest for testing our library
  testImplementation 'junit:junit:4.13.2'
  testImplementation 'org.scalatest:scalatest_2.13:3.2.10'
  testImplementation 'org.scalatestplus:junit-4-13_2.13:3.2.2.0'
  // Need scala-xml at test runtime
  testRuntimeOnly 'org.scala-lang.modules:scala-xml_2.13:1.2.0'

  sonarlintPlugins 'org.sonarsource.slang:sonar-scala-plugin:1.11.0.3905'
}

sonarlintMain {
  excludeRules = ['scala:S1481']
  ignoreFailures = true
}

Apply to Xxx project

Only a subset of languages are supported by Sonarlint. For other languages the plugin will not be auto applied. Instead it has to be defined explicitly in the build.gradle.

Create the sonarlint task when it is not auto created

plugins {
  id 'base'
  id 'se.solrike.sonarlint' version '2.0.0'
}
repositories {
  mavenCentral()
}
task sonarlintMain(type: se.solrike.sonarlint.Sonarlint) {
  description = 'Runs sonarlint on main code'
  group = 'verification'
  ignoreFailures = false
  source = fileTree('my_src_folder')
}
check.dependsOn = ['sonarlintMain']

dependencies {
  // configure the sonarlint secrets plugin for finding secret (like hardcoded access keys for AWS) in the code.
  sonarlintPlugins files("${System.getProperty('user.home')}/.p2/pool/plugins/org.sonarlint.eclipse.core_7.2.1.42550/plugins/sonar-secrets-plugin-1.1.0.36766.jar")
}

sonarlint version mapping

By default, this Gradle Plugin uses the sonarlint core version listed in this table.

Gradle Plugin sonarlint
1.0.0 8.0.2.42487
2.0.0 9.6.1.76766

sonarlint rules

By default sonarlint has different rules for production code and test code. For instance for test code there is a rule that checks for asserts in unit tests.

Rules are described here. Note that some rules are for SonarQube or SonarCloud only.

To list all the rules in your configured plugins you will have to create the task manually. Complete example:

plugins {
  id 'se.solrike.sonarlint' version '2.0.0'
  id 'com.github.node-gradle.node' version '5.0.0'
}
repositories {
  mavenCentral()
}
dependencies {
  sonarlintPlugins 'org.sonarsource.html:sonar-html-plugin:3.6.0.3106'
  sonarlintPlugins 'org.sonarsource.java:sonar-java-plugin:7.17.0.31219'
  sonarlintPlugins 'org.sonarsource.javascript:sonar-javascript-plugin:10.0.1.20755' // both JS and TS
  sonarlintPlugins 'org.sonarsource.kotlin:sonar-kotlin-plugin:2.20.0.4382'
  sonarlintPlugins 'org.sonarsource.php:sonar-php-plugin:3.25.0.9077'
  sonarlintPlugins 'org.sonarsource.python:sonar-python-plugin:3.17.0.10029'
  sonarlintPlugins 'org.sonarsource.slang:sonar-ruby-plugin:1.11.0.3905'
  sonarlintPlugins 'org.sonarsource.slang:sonar-scala-plugin:1.11.0.3905'
  sonarlintPlugins 'org.sonarsource.sonarlint.omnisharp:sonarlint-omnisharp-plugin:1.4.0.50839'
  sonarlintPlugins 'org.sonarsource.xml:sonar-xml-plugin:2.6.1.3686'
  sonarlintPlugins files("./sonar-secrets-plugin-1.1.0.36766.jar")
}
node {
  version = '18.9.1'
  // If empty, the plugin will use the npm command bundled with Node.js
  npmVersion = ''
  download = true
}

task sonarlintListRules(type: se.solrike.sonarlint.SonarlintListRules) {
  description = 'List sonarlint rules'
  group = 'verification'
}

sonarlint {
  excludeRules = ['java:S1185']
  includeRules = ['java:S1176', 'Web:LongJavaScriptCheck']
  ruleParameters = [
    'java:S1176' : [
      'forClasses':'**.api.**',
      'exclusion': '**.private.**']  // default **.internal.**
  ]
}

When the task sonarlintListRules is executed the list on the console will be similar to (shorted):

...
[x] csharpsquid:S4423 - Weak SSL/TLS protocols should not be used - [cwe, owasp-a3, owasp-a6, owasp-m3, privacy, sans-top25-porous] - C#
[x] csharpsquid:S4426 - Cryptographic keys should be robust - [cwe, owasp-a3, owasp-a6, owasp-m5, privacy] - C#
...
[x] java:S1175 - The signature of "finalize()" should match that of "Object.finalize()" - [pitfall] - Java
[x] java:S1176 - Public types, methods and fields (API) should be documented with Javadoc - [convention] - Java
    forClasses : **.api.**
    exclusion : **.private.**
[x] java:S1181 - Throwable and Error should not be caught - [bad-practice, cert, cwe, error-handling] - Java
[x] java:S1182 - Classes that override "clone" should be "Cloneable" and call "super.clone()" - [cert, convention, cwe] - Java
[ ] java:S1185 - Overriding methods should do more than simply call the same method in the super class - [clumsy, redundant] - Java
[x] java:S1186 - Methods should not be empty - [suspicious] - Java
[ ] java:S1188 - Anonymous classes should not have too many lines - [] - Java
    Max : 20
[x] java:S1190 - Future keywords should not be used as names - [obsolete, pitfall] - Java
...
[ ] xml:S1120 - Source code should be indented consistently - [convention] - XML
    tabSize : 2
    indentSize : 2
[x] xml:S1134 - Track uses of "FIXME" tags - [cwe] - XML
[x] xml:S1135 - Track uses of "TODO" tags - [cwe] - XML
...

The first column indicates if the rule is enabled/included or not.

Since most of the rules have tags like 'aws', 'tests', 'regex' so you can easily grep on those. E.g.:

./gradlew sonarlintListRules | grep aws

And the result will be:

[x] java:S6241 - Region should be set explicitly when creating a new "AwsClient" - [aws, startup-time] - Java
[x] java:S6242 - Credentials Provider should be set explicitly when creating a new "AwsClient" - [aws, startup-time] - Java
[x] java:S6243 - Reusable resources should be initialized at construction time of Lambda functions - [aws] - Java
[x] java:S6244 - Consumer Builders should be used - [aws] - Java
[x] java:S6246 - Lambdas should not invoke other lambdas synchronously - [aws] - Java
[x] java:S6262 - AWS region should not be set with a hardcoded String - [aws] - Java

Suppress rules in Java

If you need to deactivate a rule for a project then add the rule to the excludeRules list. If you need to just suppress an issue in a file you can use @SuppressWarnings("all") or @SuppressWarnings with rule keys: @SuppressWarnings("java:S2077") or @SuppressWarnings({"java:S1118", "java:S3546"}). (In Eclipse you might need to suppress the warning about unhandled token in the annotation.)

Suppress rules in most languages

In for instance TypeScript you can disable a rule in a specifc file by add // NOSONAR or //NOSONAR at the end of the line with the issue. E.g.

const key = 'AKIATCHLSJSHD' // NOSONAR AWS access key must be used here.

sonarlint CI reports

The sonarlint gradle plugin can generate Spotbugs/Findbugs compatible XML files and also SARIF compatible JSON files.

Enable as follows:

sonarlintMain {
  reports {
    xml.enabled = true // default false
    sarif.enabled = true // default false
  }
}

Github actions using Spotbugs format

If you are using Github actions you can use the same action for sonarlint as you are using for Spotbugs. (Remember to customize name and title for the second definition of the spotbugs action.)

name: build
run-name: ${{ github.actor }} is building the gradle project
on: [push]
jobs:
  build-main-artifact:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-java@v3
        with:
          java-version-file: ./.java-version
          distribution: temurin
          cache: gradle
      - run: ./gradlew build --no-daemon
      - name: Publish Test Results
        uses: EnricoMi/publish-unit-test-result-action@v2
        if: always()
        with:
          files: |
            build/test-results/test/*.xml
      - name: Publish Spotbugs Results
        uses: jwgmeligmeyling/[email protected]
        with:
          name: Spotbugs
          path: build/reports/spotbugs/*.xml
      - name: Publish Sonarlint Results
        uses: jwgmeligmeyling/[email protected]
        with:
          name: Sonarlint
          title: Sonarlint report
          path: build/reports/sonarlint/*.xml
      - uses: actions/upload-artifact@v3
        with:
          name: Package
          path: build/libs

Github actions using SARIF format

If you are using Github actions you can use the generic SARIF plugin to let Github display the issues in the "Security" tab.

name: build
run-name: ${{ github.actor }} is building the gradle project
on: [push]
jobs:
  build-main-artifact:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-java@v3
        with:
          java-version-file: ./.java-version
          distribution: temurin
          cache: gradle
      - run: ./gradlew build --no-daemon
      - name: Publish Test Results
        uses: EnricoMi/publish-unit-test-result-action@v2
        if: always()
        with:
          files: |
            build/test-results/test/*.xml
      - name: Sonarlint
        uses: github/codeql-action/upload-sarif@v2
        # The issues will be visible in the security tab in github
        with:
          # Path to SARIF file relative to the root of the repository or path to a folder with sarif files
          # wildcard doesnt work!
          sarif_file: build/reports/sonarlint/sonarlintMain.sarif
          # Optional category for the results
          # Used to differentiate multiple results for one commit
          category: Sonarlint
      - name: Spotbugs
        uses: github/codeql-action/upload-sarif@v2
        # The issues will be visible in the security tab in github
        with:
          sarif_file: build/reports/spotbugs/main.sarif
          category: Spotbugs
      - name: Archive main artifacts
        uses: actions/upload-artifact@v3
        with:
          name: Artifacts
          path: build/libs

AWS CodeCatalyst using SARIF format

If you are using AWS CodeCatalyst you can turn on "auto discover" for reports and the Sonarlint report will be under the "Reports" tab.

Name: Workflow_7f90
SchemaVersion: "1.0"
Triggers:
  - Type: PUSH
    Branches:
      - main
Actions:
  Build_50:
    Identifier: aws/[email protected]
    Outputs:
      AutoDiscoverReports:
        Enabled: true
        ReportNamePrefix: rpt
    Compute:
      Type: EC2
      Fleet: Linux.x86-64.Large
    Inputs:
      Sources:
        - WorkflowSource
    Configuration:
      Steps:
        - Run: ./gradlew build --no-daemon

Azure DevOps using SARIF format

You must install "SARIF SAST Scans Tab" from the marketplace into the Azure DevOps organization. Then there will be a "Scans" tab next to the "Tests" tab.

... snippet
# The SARIF files need to go to a artifact called "CodeAnalysisLogs"
- task: PublishBuildArtifacts@1
  displayName: 'Sonarlint report'
  inputs:
    # Wildcards are not supported!!!
    pathToPublish: build/reports/sonarlint/sonarlintMain.sarif
    artifactName: CodeAnalysisLogs
  condition: succeededOrFailed()

sonarlint plugins

Release notes

2.0.0

Support for Sonarlint core 9.6 which means that newer plugins can be used. Like org.sonarsource.java:sonar-java-plugin:7.30.1.34514.

1.0.0-beta.17

If any problems with the sonarlint plugins are found, the build will break. For instance using a too new plugin version or missing dependencies or runtime (like NodeJS). This address issue issue 5 where Sonarlint silently continues even if some plugins fail to load.

1.0.0-beta.16

Support for Gradle configuration cache. Contributed by Lukas Gräf.

1.0.0-beta.15

Fix bug in detecting CPU architecture for selecting correct node binaries.

Improve the documentation.

1.0.0-beta.14

Adding support for reports in Static Analysis Results Interchange Format (SARIF) format by OASIS. See https://sarifweb.azurewebsites.net. This means that a standard format is used for reporting static code analysis findings. Github actions, Azure DevOps and AWS CodeCatalyst are supporting this format.

Fix formating of the description. Sonarlint only offers HTML based description and it renders nice the Github action but not so nice in the Security tab.

1.0.0-beta.9

Adding option to generate Spotbugs/Findbugs XML for the issues so for instance Jenkins' code quality reports can be used. Also Github action from https://github.com/jwgmeligmeyling/spotbugs-github-action can be used. Include reference to the Golang sonarlint plugin.

1.0.0-beta.8

Adding task to list all the rules and how they are configured.

Re-think about the Kotlin support. In fact the sonarlintMain task created will cover Kotlin code too since the Kotlin plugin is also applying the Java plugin. So there is no need for dedicated Sonarlint task for Kotlin.

1.0.0-beta.7

Add support for Kotlin projects. The tasks for Kotlin will be automatically created.

1.0.0-beta.6

The working folder for the Sonarlint engine is now properly under the project's build folder. Sonarlint's user home is the project's folder.

1.0.0-beta.5

Fix typo in printouts. Thanks @doofy for contributing!

1.0.0-beta.4

Fix OS and architecture detection for node executable when running on amd64.

1.0.0-beta.3

  • when using test fixture plugin the source set wasn't marked as test source so wrong set of sonarlint rules was applied.
  • include summary and TOC in the html report

1.0.0-beta.2

  • Fix html report issue with rule Web:S5256. Some rules description has html tags which are not escaped.
  • When a report is generate the path will be printed on the console.

1.0.0-beta.1

Sonarlint analysis for Java and Node(JavaScript/TypeScript). For Node projects the node plugin com.github.node-gradle.node needs to be configured to download node. This plugin picks the node executable from that but since the node path contains info about the OS and architecture it is a bit messy to get that correct on all platforms. Right now it is only tested on mac and linux on x86 platform.

future

Improvements that might be implemented are:

  • Support for windows when using node.
  • Support to specify sonarlint properties. For instance the node exec can be configured that way using 'sonar.nodejs.executable'.
  • Support to find node on the $PATH using org.sonarsource.sonarlint.core.NodeJsHelper
  • Support for a list of suppressed issues like Checkstyle and Spotbug have.
  • specify stylesheet for the html reports
  • Be able to specify the source sets in the sonarlint DSL
  • specify the sonarlint-core version via a toolsversion property and invoke it via WorkerAPI
  • make sure up-to-date checks are resonable
  • link to rules description in the report
  • it might exists more issue types: "SECURITY_HOTSPOT" but they are not in sonarlint

SARIF

  • partialFingerprints on the result A set of strings used to track the unique identity of the result. Code scanning uses partialFingerprints to accurately identify which results are the same across commits and branches. Note: Code scanning only uses the primaryLocationLineHash.

        "partialFingerprints": {
         "primaryLocationLineHash": "39fa2ee980eb94b0:1"
       }