How to run CodeQL scans on Jenkins

Question

My repository requires scans be run on Jenkins. How can I configure Jenkins to run the scans?

This is Step #5 for configuring CodeQL scans for Jenkins CI. This step is performed after the remove codeql-analysis workflow and is the final step in configuring CodeQL scans for Jenkins CI.

Answer

If you require scans to be run on Jenkins, you will need to load the CodeQL Jenkins Shared Library into your Jenkins Global Library, or include the CodeQL Jenkins Shared Library in your pipelines dynamically.

If you wish to load the library as a Global Shared Library, you may add the CodeQL Jenkins Shared Library to your Jenkins Global Library by navigating to Manage Jenkins > Configure System > Global Pipeline Libraries and adding the following library:

  • Repository URL: https://github.com/department-of-veterans-affairs/codeql-tools
  • Default version: main
  • Repository path:
    • For Linux-based Jenkins pipelines: jenkins/shared-libraries/linux
    • For Windows-based Jenkins pipelines: jenkins/shared-libraries/windows

If you intend to dynamically load the library instead of adding it as a Global Shared Library, you will need to add the following to the top of a new or exising Jenkinsfile or pipeline:

library identifier: 'codeql-<linux_or_windows>@main', retriever: modernSCM(
    scm: [$class: 'GitSCMSource',
        remote: 'https://github.com/department-of-veterans-affairs/codeql-tools.git',
        credentialsId: '<credential-id>'], 
    libraryPath: 'jenkins/shared-libraries/<linux_or_windows>')

The remainder of this technical note covers the following topics:


Using the Shared Library

The Jenkins Shared Library contains a single function, ExecuteCodeQL, which will execute the CodeQL scan. The function accepts the following parameters:

  • org - The GitHub organization name
  • repo - The GitHub repository name
  • branch - The branch to scan
  • language - The language you are scanning, see supported languages
  • buildCommand - The command to build your source code. For compiled languages, this is generally required. For interpreted languages, this is optional.
  • token - The GitHub Personal Access Token with full repo scope to use for the scan, or a GitHub Fine-Grained Token with contents: read and code-scanning-alerts: read-and-write permissions, pulled from the Jenkins credentials store. Must be of type Secret Text.
  • installCodeQL - Whether to download CodeQL at runtime, defaults to false

Note: You should not reference your GitHub token directly in your pipeline, but instead pull it from the Jenkins credentials store.
To do this, navigate to Manage Jenkins > Manage Credentials > System > Global credentials (unrestricted) > Add Credentials and add a new Secret Text credential containing your GitHub token. You may then reference the credential in your Jenkinsfile by using the withCredentials function.


Calling the shared library ExecuteCodeQL function

Once you’ve loaded the CodeQL Jenkins Shared Library, running a scan is as simple as calling the ExecuteCodeQL function in your Jenkinsfile. An example of calling the ExecuteCodeQL function is as follows:

steps {
    checkout scm
    withCredentials([string(credentialsId: '<credential-id>', variable: 'TOKEN')]) {
       ExecuteCodeQL('<github_org>', '<github_repo>', env.BRANCH_NAME, '<language>', '<build_command>', env.TOKEN, <install_codeql>)
    }
}

Note: The build_command field must be a single command, it cannot contain ampersand (&), pipe (|), or semicolon (;) characters. If you have a complex build process that requires multiple commands, you may create a script to execute the commands and call the script in the build_command field. If your build must be executed from a specific directory, you may use the dir or cd function to change the working directory before executing the build command.


Uploading the CodeQL scan results as an artifact

During the CodeQL scan, the Shared Library will generate a CSV file containing the results of the scan. You may archive the CSV file by using the archiveArtifacts function. An example of archiving the CSV file is as follows:

archiveArtifacts artifacts: 'codeql-scan-results-<language>.csv', fingerprint: true

Example Pipeline With Jenkinsfile Using Global Shared Library

The below snippet contains a full example of a Jenkinsfile using the CodeQL Jenkins Shared Library as a Global Shared Library to run a CodeQL scan:

@Library('codeql-linux')_

pipeline {
    agent { node { label 'linux' } }
    
    stages {
        stage('CodeQL - java') {
            steps {
                checkout scm
                withCredentials([string(credentialsId: '<github-pat-credential-id>', variable: 'TOKEN')]) {
                   ExecuteCodeQL('department-of-veterans-affairs', '<repo-name>', env.BRANCH_NAME, '<codeql-language>', '<build-command>', env.TOKEN, <install-codeql>)
                }
                archiveArtifacts artifacts: 'codeql-scan-results-<language>.csv', fingerprint: true
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
    }
}

CodeQL Scan Frequency

At a minimum you must scan every eligible language in each of your repositories at least once weekly. You may wish to add additional triggers such as scanning on pull request or on merges to your production branch.

Notice: To remain compliant you must leverage the provided ExecuteCodeQL function via the Jenkins Shared Library, which will ensure that the scans are run in compliance with the VA’s code review policy.


Updating CodeQL CLI Bundle

The CodeQL Jenkins Shared Library depends on the CodeQL CLI being installed on your Jenkins build agents. If you choose to install the CLI yourself in your image, you must ensure you are keeping the CLI up to date. The easiest way to do this is to automate the process of installing the CLI in your image by downloading it using the latest release URL from the CodeQL Action GitHub repository. Once you’ve done this, building your base image on a regular basis will ensure that you are always using the latest version of the CLI.

You may also subscribe to notifications for new releases by navigating to the CodeQL Action GitHub repository in your browser and clicking on the Watch button in the upper right hand corner of the page. In the Watch menu, select Custom and then select Releases. This will ensure that you are notified of new releases via email.


Return to enable CodeQL using Jenkins CI to continue with the next step.