Automate JavaScript scanning with Retire.js in Azure DevOps

Every web developer uses a lot of JavaScript libraries. When installing packages from npm, it almost feels like you are downloading half of the libraries there. Like every other piece of code, JavaScript can include vulnerabilities that make hackers drool and eager to start exploiting your site. In this post, I'll show you how to detect JavaScript libraries with known vulnerabilities and how to automate the process on Azure DevOps.

Automate JavaScript scanning with Retire.js in Azure DevOps

There are different ways of figuring out if you include JavaScript libraries with vulnerabilities in your website. npm comes with npm audit which does exactly this, but it is limited to the packages referenced in a package.json file. In .NET, some may use this while other uses solutions like manually copying and pasting JavaScript libraries or installing them through libman. For this post, I'll introduce you to the tool I think work best for .NET developers: Retire.js.

Retire.js is an excellent "little" tool developed by Erlend Oftedal. It includes a large repository of known vulnerabilities, maintained by the community. It comes in different forms but is essentially a CLI that you run in your project directory.

To install Retire.js, you will need to have npm installed. This comes with Node, so go ahead and install the latest distribution if you haven't already. Then run the install script:

npm install -g retire

This will install Retire.js as a global tool and add it to your PATH. To demonstrate the features of Retire.js, I have created a new ASP.NET Core MVC project and targeted it against ASP.NET Core 3.1. You should never have to create new projects based on old .NET Core versions like this, but I know that doing so will install some vulnerable JavaScript libraries. Once created, we can run Retire.js from the command line in the root of the project:

C:\projects\WebsiteWithVulnerabilities\Website>retire
retire.js v5.2.5
Loading from cache: https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/jsrepository-v4.json
C:\projects\WebsiteWithVulnerabilities\Website\wwwroot\lib\jquery-validation\dist\additional-methods.js
 ↳ jquery-validation 1.17.0
jquery-validation 1.17.0 has known vulnerabilities: severity: high; summary: Regular Expression Denial of Service vulnerability, CVE: CVE-2021-21252, githubID: GHSA-jxwx-85vp-gvwm; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1193--2021-01-09 severity: low; summary: ReDoS vulnerability in URL2 validation, CVE: CVE-2021-43306, issue: 2428, githubID: GHSA-j9m2-h2pv-wvph; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1194--2022-05-19 severity: high; summary: ReDoS vulnerability in url and URL2 validation, CVE: CVE-2022-31147, githubID: GHSA-ffmh-x56j-9rc3; https://github.com/advisories/GHSA-ffmh-x56j-9rc3 https://github.com/jquery-validation/jquery-validation/commit/5bbd80d27fc6b607d2f7f106c89522051a9fb0dd severity: medium; summary: Potential XSS via showLabel, PR: 2462; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1200--2023-10-10
C:\projects\WebsiteWithVulnerabilities\Website\wwwroot\lib\jquery-validation\dist\additional-methods.min.js
 ↳ jquery-validation 1.17.0
jquery-validation 1.17.0 has known vulnerabilities: severity: high; summary: Regular Expression Denial of Service vulnerability, CVE: CVE-2021-21252, githubID: GHSA-jxwx-85vp-gvwm; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1193--2021-01-09 severity: low; summary: ReDoS vulnerability in URL2 validation, CVE: CVE-2021-43306, issue: 2428, githubID: GHSA-j9m2-h2pv-wvph; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1194--2022-05-19 severity: high; summary: ReDoS vulnerability in url and URL2 validation, CVE: CVE-2022-31147, githubID: GHSA-ffmh-x56j-9rc3; https://github.com/advisories/GHSA-ffmh-x56j-9rc3 https://github.com/jquery-validation/jquery-validation/commit/5bbd80d27fc6b607d2f7f106c89522051a9fb0dd severity: medium; summary: Potential XSS via showLabel, PR: 2462; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1200--2023-10-10
C:\projects\WebsiteWithVulnerabilities\Website\wwwroot\lib\jquery-validation\dist\jquery.validate.js
 ↳ jquery-validation 1.17.0
jquery-validation 1.17.0 has known vulnerabilities: severity: high; summary: Regular Expression Denial of Service vulnerability, CVE: CVE-2021-21252, githubID: GHSA-jxwx-85vp-gvwm; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1193--2021-01-09 severity: low; summary: ReDoS vulnerability in URL2 validation, CVE: CVE-2021-43306, issue: 2428, githubID: GHSA-j9m2-h2pv-wvph; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1194--2022-05-19 severity: high; summary: ReDoS vulnerability in url and URL2 validation, CVE: CVE-2022-31147, githubID: GHSA-ffmh-x56j-9rc3; https://github.com/advisories/GHSA-ffmh-x56j-9rc3 https://github.com/jquery-validation/jquery-validation/commit/5bbd80d27fc6b607d2f7f106c89522051a9fb0dd severity: medium; summary: Potential XSS via showLabel, PR: 2462; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1200--2023-10-10
C:\projects\WebsiteWithVulnerabilities\Website\wwwroot\lib\jquery-validation\dist\jquery.validate.min.js
 ↳ jquery-validation 1.17.0
jquery-validation 1.17.0 has known vulnerabilities: severity: high; summary: Regular Expression Denial of Service vulnerability, CVE: CVE-2021-21252, githubID: GHSA-jxwx-85vp-gvwm; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1193--2021-01-09 severity: low; summary: ReDoS vulnerability in URL2 validation, CVE: CVE-2021-43306, issue: 2428, githubID: GHSA-j9m2-h2pv-wvph; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1194--2022-05-19 severity: high; summary: ReDoS vulnerability in url and URL2 validation, CVE: CVE-2022-31147, githubID: GHSA-ffmh-x56j-9rc3; https://github.com/advisories/GHSA-ffmh-x56j-9rc3 https://github.com/jquery-validation/jquery-validation/commit/5bbd80d27fc6b607d2f7f106c89522051a9fb0dd severity: medium; summary: Potential XSS via showLabel, PR: 2462; https://github.com/jquery-validation/jquery-validation/blob/master/changelog.md#1200--2023-10-10
C:\projects\WebsiteWithVulnerabilities\Website\wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.js
 ↳ bootstrap 4.3.1
bootstrap 4.3.1 has known vulnerabilities: severity: medium; summary: Bootstrap Cross-Site Scripting (XSS) vulnerability, CVE: CVE-2024-6531, githubID: GHSA-vc8w-jr9v-vj7f; https://github.com/advisories/GHSA-vc8w-jr9v-vj7f https://nvd.nist.gov/vuln/detail/CVE-2024-6531 https://github.com/rubysec/ruby-advisory-db/blob/master/gems/bootstrap/CVE-2024-6531.yml https://github.com/twbs/bootstrap https://www.herodevs.com/vulnerability-directory/cve-2024-6531
C:\projects\WebsiteWithVulnerabilities\Website\wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.min.js
 ↳ bootstrap 4.3.1
bootstrap 4.3.1 has known vulnerabilities: severity: medium; summary: Bootstrap Cross-Site Scripting (XSS) vulnerability, CVE: CVE-2024-6531, githubID: GHSA-vc8w-jr9v-vj7f; https://github.com/advisories/GHSA-vc8w-jr9v-vj7f https://nvd.nist.gov/vuln/detail/CVE-2024-6531 https://github.com/rubysec/ruby-advisory-db/blob/master/gems/bootstrap/CVE-2024-6531.yml https://github.com/twbs/bootstrap https://www.herodevs.com/vulnerability-directory/cve-2024-6531
C:\projects\WebsiteWithVulnerabilities\Website\wwwroot\lib\bootstrap\dist\js\bootstrap.js
 ↳ bootstrap 4.3.1
bootstrap 4.3.1 has known vulnerabilities: severity: medium; summary: Bootstrap Cross-Site Scripting (XSS) vulnerability, CVE: CVE-2024-6531, githubID: GHSA-vc8w-jr9v-vj7f; https://github.com/advisories/GHSA-vc8w-jr9v-vj7f https://nvd.nist.gov/vuln/detail/CVE-2024-6531 https://github.com/rubysec/ruby-advisory-db/blob/master/gems/bootstrap/CVE-2024-6531.yml https://github.com/twbs/bootstrap https://www.herodevs.com/vulnerability-directory/cve-2024-6531
C:\projects\WebsiteWithVulnerabilities\Website\wwwroot\lib\bootstrap\dist\js\bootstrap.min.js
 ↳ bootstrap 4.3.1
bootstrap 4.3.1 has known vulnerabilities: severity: medium; summary: Bootstrap Cross-Site Scripting (XSS) vulnerability, CVE: CVE-2024-6531, githubID: GHSA-vc8w-jr9v-vj7f; https://github.com/advisories/GHSA-vc8w-jr9v-vj7f https://nvd.nist.gov/vuln/detail/CVE-2024-6531 https://github.com/rubysec/ruby-advisory-db/blob/master/gems/bootstrap/CVE-2024-6531.yml https://github.com/twbs/bootstrap https://www.herodevs.com/vulnerability-directory/cve-2024-6531

As shown from the output, our project includes old versions of jQuery and Bootstrap with known vulnerabilities. Every listing contains a message as well as useful links to pages where you can get additional details, like this one: https://github.com/advisories/GHSA-vc8w-jr9v-vj7f. Running this will give you a good overview of the state of your current JavaScript dependencies and whether anything needs an upgrade.

There are many possibilities with the CLI that I don't want to go further into in this post. To see the possibilities, you can run Retire.js with an --help option:

C:\projects\WebsiteWithVulnerabilities\Website>retire --help
Usage: cli [options]

Options:
  -V, --version            output the version number
  -v, --verbose            Show identified files (by default only vulnerable files are shown)
  -c, --nocache            Don't use local cache
  --jspath <path>          Folder to scan for javascript files (deprecated)
  --path <path>            Folder to scan for javascript files
  --jsrepo <path|url>      Local or internal version of repo. Can be multiple comma separated. Default: 'central')
  --cachedir <path>        Path to use for local cache instead of /tmp/.retire-cache
  --proxy <url>            Proxy url (http://some.host:8080)
  --outputformat <format>  Valid formats: text, json, jsonsimple, depcheck (experimental), cyclonedx, cyclonedxJSON and
                           cyclonedxJSON1_6
  --outputpath <path>      File to which output should be written
  --ignore <paths>         Comma delimited list of paths to ignore
  --ignorefile <path>      Custom ignore file, defaults to .retireignore / .retireignore.json
  --severity <level>       Specify the bug severity level from which the process fails. Allowed levels none, low,
                           medium, high, critical. Default: none
  --exitwith <code>        Custom exit code (default: 13) when vulnerabilities are found
  --colors                 Enable color output (console output only)
  --insecure               Enable fetching remote jsrepo/noderepo files from hosts using an insecure or self-signed SSL
                           (TLS) certificate
  --ext <extensions>       Comma separated list of file extensions for JavaScript files. The default is "js"
  --cacert <path>          Use the specified certificate file to verify the peer used for fetching remote
                           jsrepo/noderepo files
  --includeOsv             Include OSV advisories in the output
  --deep                   Deep scan (slower and experimental)
  -h, --help               display help for command

One parameter that I want to mention is --colors which includes severity-based colors on the output. This is a nice addition when running the tool locally.

Having to run Retire.js manually is doomed to be forgotten over time. Automation is the way forward, which is what we have done on elmah.io too. I wanted us to run Retire.js as part of our builds on Azure DevOps. This can easily be done by running the CLI as part of the build pipeline, but I wanted an even easier approach. That's why I set out to develop a small extension for Azure DevOps that will run Retire.js with the right parameters and output parsing. The extension is made available for free in the Azure DevOps Marketplace here: https://marketplace.visualstudio.com/items?itemName=elmahio.retire-extension.

Let's set up a build pipeline for our vulnerable website:

trigger:
- master

pool:
  vmImage: windows-latest

steps:
- task: UseDotNet@2
  inputs:
    packageType: 'sdk'
    version: '9.0.x'
    installationPath: $(Agent.ToolsDirectory)/dotnet

- task: DotNetCoreCLI@2
  displayName: Build
  inputs:
    command: 'build'

Next, click the Get it free button on the Azure Marketplace listing:

Retire.js on the Azure DevOps Marketplace

This will redirect you to the install page, where you can pick where to install the extension:

Select organization

After clicking the Install button, the Retire.js extension is available for you to use in your build pipeline. The extension works in both the old UI-based builds as well as in YAML. To run Retire.js as part of our build, we can extend the pipeline with the following code:

- task: Retire@1
  inputs:
    verbose: false
    failOnVulnerabilities: false

I have included two out of three available inputs for the task. The third is a custom path parameter which I haven't included in this example. verbose translates directly to the parameter of the same name in Retire.js. As suggested by the name, it outputs some additional information about each vulnerability. failOnVulnerabilities is unique to the extension and will cause the Azure DevOps build to fail if any vulnerabilities are found.

When running the pipeline, you will see output like this:

Build output on Azure DevOps

Notice how each vulnerability is listed as a warning in the build output, alongside a formatted output of each vulnerability. This is one of the advantages of using the extension over running the retire command manually using a script task or similar.

With this pipeline in place, we can start automating the process of detecting vulnerabilities in our JavaScript dependencies. I hope this post has inspired some of you to start looking at your dependencies too.

elmah.io: Error logging and Uptime Monitoring for your web apps

This blog post is brought to you by elmah.io. elmah.io is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With elmah.io, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.

See how we can help you monitor your website for crashes Monitor your website