Skip to main content

Jenkins integration

The rl-secure CLI tool integrates with Jenkins to provide automated software security scans for all your CI/CD builds. The integration relies on the official rl-scanner Docker image which is used to install and run the Spectra Assure CLI, scan build artifacts, and generate analysis reports.

In this guide, you will learn how to integrate rl-secure as a stage in a Jenkins pipeline. The guide covers a basic scan-only workflow for analyzing build artifacts with rl-secure and displaying analysis reports on Jenkins. For each Jenkins job, one artifact is scanned and the latest scan result displayed on the job level.

The examples and instructions in this guide have been tested only with Jenkins Pipeline and the GitHub build integration. The guide does not cover Multibranch use cases.

Jenkins experience required

If you're not a Jenkins administrator or an experienced user, we strongly recommend you learn at least the basics of Jenkins before proceeding with this guide.

Throughout this guide, we'll refer to the official Jenkins documentation for additional context and details.

Jenkins integration workflow overviewโ€‹

  1. When users push a new commit to a GitHub repository, Jenkins starts a pipeline job.
  2. The pipeline job executes on a Jenkins node and produces an artifact as part of the "build" stage.
  3. In the "scan" stage, a valid rl-secure license is provided using environment variables.
  4. The artifact is copied to a directory that is mounted into a Docker container. The container is started and rl-secure installed inside it.
  5. In the container, rl-secure scans the artifact and generates reports. When the scan is complete, the container automatically shuts down.
  6. The full HTML report is displayed in the Jenkins interface after the "scan" stage.

Prerequisitesโ€‹

To successfully integrate rl-secure with Jenkins, you need:

  1. A working Jenkins installation with a Jenkins Node configured to use Docker. This is required to use the rl-scanner Docker image in your pipeline. Contact your Jenkins administrator for help if you can't set it up directly.

  2. The following Jenkins plugins installed and ready to use:

    Check with your Jenkins administrator if these plugins are already set up.

  3. A Jenkins Pipeline that produces artifacts for rl-secure to access and scan. You can modify an existing pipeline or create a new one.

  4. A valid rl-secure license with site key. You must convert your license file into a Base64-encoded string for this integration. On Linux-based systems, you can do this with the base64 command from the GNU coreutils package. On Windows, you can use either the certutil command or the ToBase64String() method. If you don't already have a site-wide deployment license, follow the instructions in the licensing guide to get it from ReversingLabs.

1. Set up the pipelineโ€‹

In this step, you should set up a Jenkins Pipeline that will produce a build artifact in one stage ("build"), and use rl-secure to scan the artifact in the next stage ("scan").

The exact procedure of setting up the pipeline depends on your organization's CI/CD practices and processes. You may want to use a Configuration-as-Code approach, or you may feel more comfortable working with pipelines in the Jenkins UI.

Regardless of the approach you choose, make sure to configure the pipeline to listen to git push events from a GitHub repository. This requires setting up the integration between Jenkins and GitHub. Every push event should trigger the pipeline job so that your code changes are built and scanned with rl-secure.

2. Save rl-secure license keys as secretsโ€‹

In this step, you should make sure the pipeline can use the rl-secure license required for the integration.

To do this, store the license site key and Base64-encoded license file as secrets in your Jenkins configuration.

When they're configured, license keys must be provided to the pipeline as the following environment variables:

  • RLSECURE_SITE_KEY = the site key string you received with your rl-secure license
  • RLSECURE_ENCODED_LICENSE = the string you get when you Base64-encode your rl-secure license file

This can be done in the "scan" stage as illustrated in the following example:

stage('# run rl-secure') {
environment {
RLSECURE_SITE_KEY = credentials('your-rl-secure-site-key')
RLSECURE_ENCODED_LICENSE = credentials('your-rl-secure-encoded-license-file')
}

3. Add the artifact scan stageโ€‹

In this step, you should define the "scan" stage in the pipeline and make sure it can access the artifact from the "build" stage.

The "scan" stage will run rl-secure in a Docker container. You must specify the artifact to scan and where to store the analysis reports. You can only specify one artifact per scan execution.

Both the input directory (containing the artifact) and the output directory (containing the reports) must be mounted as volumes to the Docker container.

You can directly copy the artifact from the "build" stage, or publish it in a central artifact repository and retrieve it from there. In both cases, the artifact must exist in the input directory when the container is started.

The output directory must be empty before starting the container. If it's not empty, you will get an error.

The following example illustrates what the "scan" stage in your pipeline could look like.

You can choose custom names for the input-directory and output-directory. It's recommended to map them to the default names packages and report when mounting them to the container.

stage('# run rl-secure') {
environment {
RLSECURE_SITE_KEY = credentials('your-rl-secure-site-key')
RLSECURE_ENCODED_LICENSE = credentials('your-rl-secure-encoded-license-file')
}

steps {
sh '''
echo "prepare"
rm -rf output-directory # must be empty
mkdir -p input-directory output-directory
cp "${MY_ARTIFACT_TO_SCAN}" ./input-directory/
ls -l ./input-directory
'''

sh '''
docker run --pull always --rm \
-u $(id -u):$(id -g) \
-v "$(pwd)/input-directory:/packages:ro" \
-v "$(pwd)/output-directory:/report" \
-e RLSECURE_ENCODED_LICENSE="${RLSECURE_ENCODED_LICENSE}" \
-e RLSECURE_SITE_KEY="${RLSECURE_SITE_KEY}" \
reversinglabs/rl-scanner \
rl-scan \
--package-path=/packages/"${MY_ARTIFACT_TO_SCAN}" \
--report-path=/report \
--report-format=all

'''
}
}

When configured in this way, the "scan" stage:

  • Maps your rl-secure license to environment variables
  • Ensures the output directory exists and is empty
  • Copies the build artifact into the input directory
  • Pulls the official reversinglabs/rl-scanner Docker image which includes a command-line tool called rl-scan
  • Sets user and group mapping to make sure the CI user has access to all data in the input and output directories
  • Mounts the input and output directories as volumes
  • Provides your rl-secure license as environment variables
  • Starts the container and runs the rl-scan tool inside it
  • Scans the artifact from the input directory and stores the HTML report in the ./rl-html/sdlc.html file in the provided output directory
Choosing the report format

The --report-format parameter in the rl-scan tool accepts any of the following values: cyclonedx, rl-checks, rl-cve, rl-html, rl-json, sarif, spdx, all

To publish the HTML report on Jenkins after the "scan" stage, you must choose either all or rl-html as the report format.

4. Display the HTML report in Jenkinsโ€‹

In this step, you should embed the rl-secure HTML report into the Jenkins UI so that everyone with access to your pipeline can conveniently preview the report for every build. This is done with the HTML Publisher plugin that allows you to add a link to the HTML report on the Jenkins Job page.

Ideally, the reports should always be published, even if the scan fails. To ensure this, you have to configure the pipeline so that it doesn't exit when the scan fails.

The following example illustrates how to define the publishing step with the post {} block.

The directory specified in reportDir must be the same output-directory previously defined as the destination for saving the reports.

post {
always {

echo "# Publish the rl-secure scan report"

publishHTML (
target: [
allowMissing: true,
alwaysLinkToLastBuild: true,
keepAll: false,
reportDir: 'output-directory',
reportFiles: 'rl-html/sdlc.html',
reportName: 'rl-secure_SDLC_report'
]
)
}
}

When configured in this way, the post block:

  • Ensures the report is published even if the "scan" stage fails
  • Archives the entire output directory specified in reportDir
  • Publishes the HTML report file specified in reportFiles
  • Uses the reportName as the name of the report link in the Jenkins UI

You can modify any of the supported configuration options to change the behavior of the HTML Publisher plugin.

If your pipeline is correctly configured and successfully executed, you will be able to access the HTML report on the Job page in the Jenkins UI. The report link will be available on the Job level, but not on the individual run level.

โœ… Available https://<jenkins server name>/job/<name of your job>/

โŒ Unavailable https://<jenkins server name>/job/<name of your job>/<number>/

Reports for failed builds

If your Jenkins job is configured not to keep failed builds, the report will not be published for failed builds.

For example, if your first build fails and the second build is successful, only the second report will be saved and displayed in Jenkins.

Troubleshooting the report displayโ€‹

In some cases, you may get a message "You need to enable JavaScript to run this app." when trying to display the HTML report in Jenkins.

To resolve this issue, you will have to customize the default CSP (Content Security Policy) in your Jenkins configuration. Consult your Jenkins administrator for help if necessary.

To permanently customize the CSP, place the following code into the $JENKINS_HOME/init.groovy.d/customize-content-security-policy.groovy file:

System.clearProperty("hudson.model.DirectoryBrowserSupport.CSP");
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "default-src 'self' ; script-src 'self' blob: ; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src *");

This approach relies on init hooks to fix the CSP security issue after each reboot when Jenkins starts.

To immediately enforce the change without having to restart Jenkins, you can execute the following code in Manage Jenkins > Script Console at https://<your jenkins dns host name>/manage/script:

System.clearProperty("hudson.model.DirectoryBrowserSupport.CSP");
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "default-src 'self' ; script-src 'self' blob: ; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src *");

This will apply the CSP fix immediately, but you must clear your browser cache to make it operational. When applied in this way, the fix will not persist after Jenkins restarts.

Example Jenkinsfileโ€‹

The following Jenkinsfile illustrates how the steps from this guide could be put together to create a pipeline that integrates rl-secure with Jenkins.

As it is only for illustrative purposes, this example contains multiple placeholder values.

To get the most value from this example, you should adapt it to your use case and according to your organization's best practices.

pipeline {
agent {
label 'scan-rl-secure-dev'
}

environment {
// Set this in a global env variable so we can use it in multiple stages
MY_ARTIFACT_TO_SCAN="artifact-filename.zip"
}

stages {
stage('# Build artifacts') {
// This is an example build stage producing an artifact to scan
steps {
echo "## Simulate building my artifact"
sh '''
curl -o "${MY_ARTIFACT_TO_SCAN}" -sS "https://example-artifact-repository.org/${MY_ARTIFACT_TO_SCAN}"
echo "OK: The artifact is $(ls $MY_ARTIFACT_TO_SCAN)"
'''
}
}

// <<<<<<<<<<<<<< START OF SCAN STAGE >>>>>>>>>>>>>>>>>>>>>>
stage('# Scan the artifact with rl-secure') {
environment {
// It's required to get the secrets from Jenkins because we will need them later in the container
RLSECURE_SITE_KEY = credentials('your-rl-secure-site-key')
RLSECURE_ENCODED_LICENSE = credentials('your-rl-secure-encoded-key')
}

steps {
sh '''
echo "Prepare input and output directories"
rm -rf output-directory # Must be empty
mkdir -p input-directory output-directory
cp "${MY_ARTIFACT_TO_SCAN}" ./output-directory/
ls -l ./output-directory
'''

// Run the `rl-secure` scan in a Docker container
sh '''
docker run --pull always --rm \
-u $(id -u):$(id -g) \
-v "$(pwd)/input-directory:/packages:ro" \
-v "$(pwd)/output-directory:/report" \
-e RLSECURE_ENCODED_LICENSE="${RLSECURE_ENCODED_LICENSE}" \
-e RLSECURE_SITE_KEY="${RLSECURE_SITE_KEY}" \
reversinglabs/rl-scanner \
rl-scan \
--package-path=/packages/"${MY_ARTIFACT_TO_SCAN}" \
--report-path=/report \
--report-format=all

'''
}
}
// <<<<<<<<<<<<<< END OF SCAN STAGE >>>>>>>>>>>>>>>>>>>>>>

// This is an example deploy stage which will not run if the scan stage fails
stage('Deploy the build artifact') {
steps {
echo "Deploying the artifact of the scan"
}
}
}

post {
always {
// This post block will also be executed on failure of the scan stage

echo "# Publish the rl-secure report"

// Publishing the HTML report only works if the report format is either rl-html or all
publishHTML (
target: [
allowMissing: true,
alwaysLinkToLastBuild: true,
keepAll: false,
reportDir: 'output-directory',
reportFiles: 'rl-html/sdlc.html',
reportName: 'rl-secure_SDLC_report'
]
)
}
}
}

Congratulations!

You have successfully configured your first rl-secure Jenkins integration. Next, you can try integrating rl-secure into more complex projects and pipelines.