October 14, 2026

How to add Static Application Security Testing (SAST) with SonarQube Cloud Free Tier to a GitLab CI/ CD pipeline

ESLint catches code quality issues. Typecheckers ensures variables and functions use the correct data types. Prettier automatically formats code for consistent style. But how can we start incorporating security testing into this GitLab CI/CD pipeline we have built? 
For this blog post we will be looking at one way to do Static Application Security Testing (SAST), adding SonarQube to our project.

What is Static Application Security Testing?


Static Application Security Testing (SAST) is a method of security testing that scans an application's source code, bytecode, and binaries for any security flaws. Because it analyzes the code without actually running the application -- meaning the code is "static" -- developers can use it early in the coding phase to catch and fix bugs immediately.

Although source code scanners have been around a while in the computer industry,  the term "Static Application Security Testing (SAST)" originated as a buzzword by Gartner, Inc. Gartner is known for their "Magic Quadrant" a visualization technique they use to map out industry leaders on a four part grid "Completeness of Vision" and "Ability to Execute" showing who are the market leaders, who are the challengers, who are the visionaries, and who are the niche players, as they put it.

Joseph Feiman and Neil Macdonald published in 2009 their paper Magic Quadrant for Static Application Security Testing (See PDF). 

"As attacks become more financially motivated as organizations get better at securing their network, desktop and server infrastructures, there has been a shift in attacks to the application level. To address those new risks, several technology markets for application security have emerged, including static application security testing (SAST)

"[...] This is the first Magic Quadrant for the SAST market. The SAST market leaders are smaller, innovative, security-focused vendors (Fortify Software and Ounce Labs) that provide static security testing tools as their primary offerings. Both of these vendors offer broad language support and integration into a variety of software life cycle (SLC) platforms.

"However, the majority of SLC platform vendors will recognize the need to add security testing capabilities to their platforms and perform this integration over the next several years. Most of the large SLC vendors (for example, HP and IBM) have taken steps in that direction (Microsoft has some basic capabilities). Yet, in all these cases, the offerings fall short of the breadth of coverage options available from dedicated point-solution vendors". 

SAST can also be incorporated into a CI/ CD pipeline, so it can be tested before it is merged into the main branch of a project.  

A good resource to learn about SAST is the OWASP DevSecOps Guidelines chapter on Static Application Security Testing. 

What can SAST find? 


According to OWASP, SAST can detect flaws such as:
  • "Injection (SQL, command, LDAP) from untrusted input reaching dangerous sinks
  • "Cross-site scripting (XSS) and unsafe output handling
  • "Hardcoded secrets and weak cryptography
  • "Insecure deserialization and path traversal
  • "Coding-standard and error-handling violations that lead to security bugs"
There is a limit, though what, SAST tools can find. Since the code is not actually running, it can not see how the code is behaving at runtime... which is why there is DAST, Dynamic Application Security Testing, which simulates real world attacks with tools such as OWASP ZAP, Burp Suite, etc. 

According to OWASP, SAST works by:
  • Pattern matching/ AST Analysis: Source code is turned into an Abstract Syntax Tree (AST), which is how a compiler would understand the structure of the code. It is then matched against known patterns that are vulnerable. Fast, but may miss vulnerabilities that span multiple files. 
  • Dataflow / taint analysis: How untrusted input (tainted data) can flow through the application until it reaches a dangerous operation. Flags if data does not get sanitized. Thorough but slow, and can introduce a lot of false positives. 
  • Control flow analysis: Maps all the ways an app can be executed through a function or a module to see which paths lead to dangerous states.
  • Semantic analysis: understands language specific best practices, and framework conventions rather than just syntax, reducing false positives for framework-specific patterns.
For this post, we will be looking to integrate SonarQube's free tier into our GitLab CI/ CD Pipeline at bun-create-playwright / .gitlab-ci.yml 

What is SonarQube?


SonarSource.com Blog post Seventeen years later, code quality is more relevant than ever mentions that SonarQube grew out of Sonar, an open-source code-analysis project. 

Olivier Gaudin wrote, "as a tech lead at a small company in France, I was frustrated that there wasn’t a tool to let us software engineers ensure the consistency and quality of code as we created it. So I teamed up with two friends equally passionate about software engineering. We launched an open source project to create a code quality review tool, something that could help make coding more consistent and predictable. We didn’t set out to start a business initially, but it became obvious we should. So, on November 13, 2008 [...] we incorporated the company that is now Sonar.

"What started as a free and open source tool to solve our own problem has grown to become SonarQube, a product now used by more than 7 million developers around the world to review and improve the quality and security of over 750 billion lines of code every day".


How to Sign Up for SonarQube Cloud


Going to the SonarSource.com Sonarqube Cloud Signup page, I was able to click on GitLab and authorize SonarQube Cloud to connect to my GitLab account. 



Once at the SonarCloud,io Create Organization page, I followed the instructions Getting started with GitLab at https://docs.sonarsource.com/sonarqube-cloud/getting-started/gitlab 

  • Select: Import my personal GitLab group.
  • Click on the link directing you to your GitLab user settings where you can create a personal access token after you sign in. 
  • GitLab: Select "Generate token", and "Legacy token".
  • Token name, I called it "api access", set an expiration for a year from now, and checked off "api", since that was the permissions needed. 
  • Select: Generate token
  • You need to copy this token to the clipboard right away. This will be the last you see it, unless you copy it in a text file or somewhere. Copy and paste it into the "Import my personal GitLab group" ==> Personal Access Token textbox, and hit Continue. 
Since this is just for a demo, I want to be on the free plan. 


Selecting "Available plans", I chose "Switch to free plan", and once again selected "Create free organization". 

Next, I chose to check off "bun-run-playwright" and "Analyze 1 project". It then gave me my own dashboard, on https://sonarcloud.io/summary/overall?id=tjmaher_bun-create-playwright&branch=main 


SonarQube Dashboard

What do all these metrics mean?

From  SonarQube User Guide on Code Metrics, Understanding measures and metrics
  • Security: Tracks security issues on new code, rates the security from A to E, and tracks the security remediation effort to fix the vulnerabilities. [Security-related rules]
  • Reliability: Measure of how software is capable of maintaining its level of performance. 
  • Maintainability: Ease which you can repair, improve, or understand your code. Tracks technical debt. What is a blocker? What is high, medium, or low? 
  • Accepted issues:
  • Coverage:  How much of the code has been covered by unit tests? What lines of code is possible to build coverage? What isn't covered? How many unit tests do you have? Any errors? What is skipped? [See Overview, and Coverage metrics]
  • Duplications: Are there any accidental duplications in the code? How many lines of code? [Duplication metrics]

Now that we have SonarQube, we can look at hooking up Sonar-Scanner-CI into the GitLab pipeline. 

What is Sonar-Scanner-CLI?


Sonar Scanner CLI is the official scanner used to run code analysis on SonarQube, as a command line interface we can set up in GitLab CI/CD. 

According to the docs, "The SonarScanner CLI is the scanner to use when there is no specific scanner for your build system. It supports ARM architecture for macOS and Linux".
You can choose to run SonarScanner CLI from a zip file, or a Docker image. 

Using the "sonar-scanner" command invokes the code analysis scanner built for CI/ CD pipelines. If you are using Maven, Gradle, or .Net, there is a dedicated scanner for them. Don't use the CLI tool. 

Sonar Scanner CLI on Docker


SonarSource has a Docker image stored on DockerHub, called sonarsource/sonar-scanner-cli. 

Add Sonar Scanner to GitLab 


According to the SonarSource Doc for SonarQube, Adding analysis to GitLab CI/CD pipeline, first you need to connect your GitLab account to your new Sonar account, then 
  • Import our project, which we have done with bun-create-playwright. 
  • Configure the project analysis parameters.
  • Add the analysis to your GitLab CI/CD pipeline.
  • Commit and push your code to start the analysis
  • Going into the project, such as bun-create-playwright and follow the instructions in  https://docs.gitlab.com/ci/variables/#for-a-project to create a variable called SONAR_TOKEN, pasting the value you had copied earlier. 
  • Create another one called SONAR_HOST_URL with the value of the SonarQube Server URL. Since I am using SonarCloud, it is https://sonarcloud.io 
Configuring your .gitlab-ci-yml file:

The sample template they list in the documentation for SonarScanner CLI is:
sonarqube-check:
  image:
    name: sonarsource/sonar-scanner-cli:latest
    entrypoint: [""]
  variables:
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"  # Defines the location of the analysis task cache
    GIT_DEPTH: "0"  # Tells git to fetch all the branches of the project, required by the analysis task
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .sonar/cache
  script:
    - sonar-scanner -Dsonar.qualitygate.wait=true
  allow_failure: true
  rules:
    - if: $CI_COMMIT_REF_NAME == 'main' || $CI_PIPELINE_SOURCE == 'merge_request_event'

Disable Automatic Analysis on SonarQube Cloud

  • Log into your SonarQube Cloud Dashboard.
  • Select the project, such as: bun-create-playwright.
  • Go to Administration in the left-hand navigation pane, the one with the gear.
  • Click on Analysis Method.
  • Find the toggle switch for Automatic Analysis and turn it OFF.

Follow Steps for Project Onboarding

Once you turn Automatic Analysis to OFF, you will see a new menu:
  • Click on "GitLab CI" to select it as an alternative to the automatic analysis by default
  • Follow the steps to add environment variables like SONAR_TOKEN, the SonarQube URL.
  • View the "Create or update your .gitlab-ci.yml" code generated for you, adding that to the GitLab project. This is a minimal base configuration. 
  •  Create a sonar-project.properties file in the root of your GitLab project, copying the base configuration. 

Creating a Sonar Project Properties File

Here is what I created as a sonar-project.properties file after a lot of back-and-forth with Google AI:

Much of the information can be retrieved from your SonarCloud Project Information / About this Project page such as https://sonarcloud.io/project/information?id=tjmaher_bun-create-playwright 

# SonarCloud Dashboard: https://sonarcloud.io/summary/overall?
id=tjmaher_bun-create-playwright&branch=main

# Required: Must be unique across your entire SonarQube instance
sonar.projectKey=tjmaher_bun-create-playwright

# Recommended: The display name of your project in the SonarQube UI
sonar.projectName=bun-create-playwright

# Required for SonarQube Cloud: Your organization key found on your dashboard
sonar.organization=tjmaher

# Recommended: The version of your project (e.g., from package.json or pom.xml)
sonar.projectVersion=1.0.0

# Required: Paths to the source directories, relative to this file (comma-separated)
#   Pointing to the root directory, or "."
sonar.sources=.

# Tell SonarQube where your Playwright tests live
sonar.tests=tests

# Prevent SonarQube from wasting time scanning dependencies or test reports
sonar.exclusions=tests/**, **/node_modules/**, **/playwright-report/**,
**/test-results/**

# Recommended: Source code character encoding
sonar.sourceEncoding=UTF-8

# Required: Point the scanner to the cloud instance instead of localhost
sonar.host.url=https://sonarcloud.io


Building Out the SAST Stage with a Sonarqube job


This project runs SonarSource's `sonar-scanner` CLI in the `sast` stage of GitLab CI for merge requests and the default branch. The job is informational (`allow_failure: true`), so its result does not block the pipeline.

Our GitLab CI/ CD pipeline is going to have four stages:
stages:
  - quality
  - sast
  - test
  - report


The new SAST stage will have a SonarQube job. Let's start off making in non-blocking, to see if we find the data it brings to be helpful. 
 
sonarqube:
  stage: sast
  image:
    name: sonarsource/sonar-scanner-cli:latest
    entrypoint: [""]
  variables:
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
    GIT_DEPTH: "0"  # Fetches complete history so SonarQube can map blame/authors
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .sonar/cache
  script:
    - sonar-scanner -Dsonar.qualitygate.wait=true
  allow_failure: true  #
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

  • GitLab will run the job in an official Docker container called sonarsource/sonar-scanner-cli, and will use the latest version. This Docker image is pre-loaded with the SonarQube command-line scanner.
Now, the GitLab CI/ CD pipeline looks like this:


What were the results? There is an embedded link in the SAST stage, in the sonarqube job:


What about Coverage?


Since we don't really have unit tests... this is a UI test framework, not a software application with unit tests, we really can't compute the coverage we have. 

What about reviewing issues?

Currently, there aren't any issues with the code. If there were, we can use the SonarQube Server Users Guide section, Reviewing Issues at https://docs.sonarsource.com/sonarqube-server/10.6/user-guide/issues/reviewing

Whew! That was a lot to go through! Snyk was a lot easier to set up for other projects. 

Maybe I can add Snyk setup with the SAST stage? 

Until then... Happy Testing!








-T.J. Maher
Software Engineer in Test

BlueSky | YouTube | LinkedIn | Articles

No comments:

Post a Comment