OSS Vulnerability Detection from SBoM with CI/CD for JS applications

Aman Agarwal
Geek Culture
Published in
6 min readSep 7, 2022
image source

Open Source Software (OSS) has ushered in a new era of modern application development and unleashed a whole new set of software products which was not possible earlier. Software composition for modern applications has become complex and more often than not involve a host of open source software. One can leverage the vast set of OSS packages and quickly compose new software applications with the requisite business logic. This increase in speed in software engineering comes with a major pitfall that one needs to be aware of and guard against: OSS vulnerabilities.

Cyber security is becoming a major concern for companies around the world. As more and more software gets built on OSS, any vulnerabilities in these OSS dependencies can lead to exploitation of these applications by malicious parties. Organisations around the world are consuming more software that ever before thanks to the growth of SaaS. This also means that the risks and threats for cyber security incidents also increase and need to be mitigated appropriately by people building these applications.

For any software application, a Software Bill of Materials (SBoM) listing all the dependencies can be generated and all the components listed in the SBoM can be analysed for known vulnerabilities in open source databases. This whole process of SBoM generation and vulnerability analysis can be automated into CI/CD pipelines. We can achieve this using dependency track application which can analyse CycloneDX Software Bill of Materials created during CI/CD or acquired from suppliers.

So let’s get cracking on setting things up.

Docker setup

Since dependency track is also an OSS package, we can self host the application on a 4 vCPU 16GB RAM VM using docker images for the backend server and frontend app.

Using docker-compose

# Downloads the latest Docker Compose file
curl -LO https://dependencytrack.org/docker-compose.yml

# Starts the stack using Docker Compose
docker-compose up -d

Using docker swarm

# Downloads the latest Docker Compose file
curl -LO https://dependencytrack.org/docker-compose.yml

# Initializes Docker Swarm (if not previously initialized)
docker swarm init

# Starts the stack using Docker Swarm
docker stack deploy -c docker-compose.yml dtrack

In the docker-compose.yml there will a variable API BASE URL that you will have to change depending on how you want to access the application

- API_BASE_URL=http://localhost:8081

If you want to access it directly using IP:Port then make sure port 8080 and 8081 are accessible for your VM and change the API_BASE_URL variable to API_BASE_URL=http://IP:8081 which will allow you to access the application http://IP:8080. Restart docker-compose or docker stack after making changes to the docker-compose.yml.

Since we want this to be part of our CI/CD pipelines we will not expose the public IP of the VM like this and use nginx as a reverse proxy and setup a DNS namespace eg http://dependency-tracker.mydomain.com

DNS setup

In your DNS manager for your domain (mydomain.com), add an A record that will map your subdomain (dependency-tracker) to the Public IP (below you can see the A record in cloudflare)

Nginx setup

Add a file called dependency-track in /etc/nginx/sites-available and /etc/nginx/sites-enabled with the following configs. Note that max_body_size is set to 100Mb to be able to ingest big json files depending on how many dependencies are there in your application.

server {
listen 80;
server_name dependency-tracker.mydomain.com;
client_max_body_size 100M;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /api/ {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8081;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

You can route this via port 443 in case you want access to be encrypted over https instead of http by generating and adding TLS certificates. We use our own custom cfbot service to auto generate TLS certificates from cloudflare. You can read more about automating TLS certificate management for cloudflare from my post here. Another option you can use is lets encrypt and certbot (point the ssl_certificate and ssl_certificate_key to the correct file paths).

listen 443 ssl;
server_name dependency-tracker.syookinsite.com;
ssl_certificate /etc/cfbot/live/certificate.pem;
ssl_certificate_key /etc/cfbot/live/key.pem;

Once done restart nginx and you will be able to see dependency tracker live on the url (http://dependency-tracker.mydomain.com or https://dependency-tracker.mydomain.com)

Login using the default credentials and add a new project (Project Name and Project UUID are needed for automating SBoM uploads in CI/CD pipelines). You can manually add an SBoM under the components sections of the project. We will use the APIs for uploading the sbom and updating the project versions.

dependency track dashboard
project list with risk score and vulnerabilities count
project component details
project vulnerabilities list
vulnerability details

SBoM Generation

Add the following library using npm that will generate the sbom

npm i --save @cyclonedx/bom

Create a new script file called bomGenerator.sh and add this command

cyclonedx-node -d -t application -o ./bom.json

The output of this command will be a bom.json which we will upload to the dependency track application. Make sure you add bom.json to .gitignore

In the package.json file add a stage called “sbom”

"sbom": "sh ./bomGenerator.sh"

Anytime you run npm run sbom the bom.json will get generated

SBoM Upload

We use gitlab for our CI/CD so we will add a new pipeline for dependency track

Under stages add the new stage

stages:- Build Staging- Build Production- Dependency Track

Add the stage details

dependency-track:stage: Dependency Trackimage:name: node:14.16entrypoint: ['']script:- npm install --silent- npm run sbom- |curl --location --request POST 'https://dependency-tracker.mydomain.com/api/v1/bom' -H 'Content-Type: multipart/form-data' -H 'X-Api-Key: '$DEPENDENCY_TRACKER_KEY'' -F 'project='$PROJECT_UUID'' -F 'bom=@./bom.json';- |curl --location --request POST 'https://dependency-tracker.mydomain.com/api/v1/project' \-H 'X-Api-Key: '$DEPENDENCY_TRACKER_KEY'' \-H 'Content-Type: application/json' \--data-raw '{"uuid": "'$PROJECT_UUID'","name": "project name","version": "'$VERSION'"}'only:- mastertags:- docker

This will first upload the sbom to the project using the projectUUID. Once the sbom has been uploaded you can update the project version using the version in the gitlab-ci.yml file (replace with any other variable you feel necessary).

Variables DEPENDENCY_TRACKER_KEY and PROJECT_UUID are added in the CI CD variables in gitlab group. To use the APIs you will have to create a new team under access management setting in the dependency track application and use the API key generated under your team.

And that’s it. Now every time you run the Dependency Track pipeline it will create a node container, install all the packages, generate a sbom for these dependencies and upload it to the dependency track application for vulnerability analysis of all the packages being used. Once the scan is done dependency track will list the vulnerabilities found in your components along with the links to the OSS Index. You can remedy the security issues and upload new sbom and keep track of the security of your code wrt to the listed vulnerabilities in the OSS databases.

Hope this post was helpful !!

--

--

Aman Agarwal
Geek Culture

Entrepreneur who likes solving problems using technology. Currently building the tech that powers Syook