How to discover, map, and triage open source dependency vulnerabilities

by

As a security automation engineer, I spend most of my days working with developers to scale and enhance our Application Security Posture through automation. Like most companies today, JupiterOne writes and utilizes open source software. And with that, we have to be mindful of the vulnerabilities in third party code dependencies. So today we will be answering 3 questions:

  1. What dependencies do we have with vulnerabilities
  2. What is the best approach to patching these dependencies
  3. Who is responsible for fixing these issues? 

To answer these questions and provide solutions, we need 2 things - visibility and automation. JupiterOne provides the visibility and an engineer, such as myself, provides the automation. 

Gaining Visibility Across Source Control Systems and Tools 

Most people talk about using automation to keep a situation manageable. For example, companies will use automation to shift their security left, also known as DevSecOps. This process encourages continuous improvement by automating security processes in the initial part of the Software Development Lifecycle. Instead of waiting until software is ready for production to test the security of software, test it early, and test it often by scanning the code, dependencies, and for secrets when it’s committed to the code repository.

All source control systems support DevSecOps practices in some form or fashion. JupiterOne’s Data Model enables our queries to cover multiple source control systems and tools, as long as the integration or ingestion adheres to the same class model. 

For example, if I search for a class called “Findings” “THAT RELATES TO” “CodeRepo”, the “Finding” will be a code or dependency vulnerability and the “CodeRepo” type will be github_repo. If this was Azure DevOps, the class would still be CodeRepo, but with a type of azure_devops_repo. By using classes, we are able to use a single query to return data from multiple source control systems.

Efficiently Tracking Vulnerabilities Across Systems and Repos

So why is this important? Searching through one source control system is manageable. But, at some companies, I’ve supported multiple source control systems like Azure DevOps, Bitbucket, Github, GitLab, and more, simultaneously. This could be due to the size of the company, a company acquisition, or during a cut over from one source control system to another. Regardless, tracking vulnerabilities across multiple systems involved writing system-specific automation, aggregating data, and merging that data into a spreadsheet or report to provide visibility. This is a largely manual, time-consuming process. However, using JupiterOne simplifies this search. Again, to reiterate, the examples mentioned here can extend beyond GitHub, Javascript dependencies, etc - vulnerability tracking with JupiterOne can be applied to other languages, source control systems, or finding types.

JupiterOne, from its inception to present day, values open source. All JupiterOne integrations are open source, so anyone can submit features and enhancements. When I started at JupiterOne, I was writing automation to secure our applications and GitHub organization. I noticed that branch protection policies were missing from our GitHub ingestion. As a security engineer, it was important to track branch protection rules.

So, I created a feature branch for the JupiterOne GitHub integration project to ingest branch protection rules as a “Rule” class. Now I can search for “Rules” “THAT RELATE TO” “CodeRepos”. I ran into this same situation with our static scanning. Because CodeQL directly integrates into GitHub and the GitHub APIs, I deployed a CodeQL GitHub workflow to all of our code repos for static scans. Then, I wrote a new feature for the GitHub integration to pull in CodeQL static scan data as “Finding(s)”. The integration team is currently reviewing my feature and it should be released soon. 

Deploying CodeQL workflows to our (hundreds and hundreds of) repositories is a good segue into the second use case for automation in security: what happens when a massive overhaul needs to be performed to get us to a place where we can start implementing DevSecOps? This brings us to JupiterOne and the importance of establishing complete visibility as a prologue to your DevSecOps processes. 

Ask yourself: are we even ready to implement DevSecOps or do we need to perform some clean up first? You can’t implement security and start blocking builds headed to production without understanding what you have and how you should get to where you need to be. Everything would grind to a halt.

How to Identify All Dependency Vulnerabilities

Before we start patching vulnerabilities, we need to find out what vulnerabilities we have. To do this we will check both our code as well as the code dependencies - just because we don’t have critical vulnerabilities showing up in our static scans for the code we write, doesn’t mean they don’t exist somewhere else. For better results we combine static security scanning with dependency scanning. This ensures that all dependencies - including build and development dependencies are covered as well. Build and development dependencies might not show up in production, but a compromised developer’s laptop or CI/CD environment can be just as devastating as an exploit in production. So, let’s run a check against all findings that are related to our CodeRepo.

Find Finding as f  THAT RELATES to CodeRepo RETURN count(f)

That’s a huge number. But, that can include “Finding(s)” from critical all the way down to informational. So let’s narrow it down to just criticals and make sure we aren’t including archived repositories. 

Find Finding with severity = 'CRITICAL' as f  THAT RELATES TO CodeRepo with archived = false RETURN count(f)

You’ll likely see a lot of issues. Again, we are searching for all findings - even build and development dependencies. Also note, these dependencies might not be direct dependencies, but dependencies of dependencies, of dependencies. So, we need to figure out how to tackle these.

Finding the Most Common Dependency Vulnerabilities

First, let’s see if the majority of these results are the same package that can be updated across the organization and put a big dent in that number.

Find Finding with severity = 'CRITICAL' as f  that has CodeRepo with archived = false as c RETURN f.vulnerablePackageName, f.vulnerableVersionRange, f.displayName,  COUNT(f) AS quantity ORDER by quantity DESC

Looks like there are a handful of packages that are the biggest contributor to the large vulnerability number. Almost 40% are caused by the top 2 packages.

Let’s take a look at which repos have json-schema.

Find Finding with vulnerablePackageName = 'json-schema' as f THAT has CodeRepo with archived = false as c Return c.displayName ORDER BY c.displayName

How to Identify Code Owners to Triage Vulnerabilities

Now let’s adjust that query to find out who has access to the repos, so we can start contacting teams to find out what this package is used for.

Find UserGroup as g THAT ALLOWS CodeRepo as c THAT HAS Finding with vulnerablePackageName = 'json-schema' as f RETURN g.name as "Group Name", c.displayName as "CodeRepo", c.webLink as "Repo URL" ORDER by g.name ASC

Now, we can download a CSV of the output and start contacting teams. 

If we aren’t sure who to contact, we can just check for the users:

Find UserGroup with name = 'security' as g THAT HAS User as u RETURN g.name, u.name, u.email

How to Validate Code Owners Have Closed Dependency Vulnerabilities

At this point, we’ve contacted all the teams, and no one has an issue with trying to patch it. I write automation to login to all the repos, create a new branch, patch the code, and create a pull request. As part of my automation, I use a standard naming convention for the pull request (PR) title so I can query on it in JupiterOne. I let the developers know that the code is there and should be merged into the main branch by next week. 

Next week comes around and it’s time to check to see who’s merged their code.

How many have merged:

FIND CodeRepo with archived = false as c THAT RELATES to PR with merged = false AND title = 'J1 FileManagement Automation v1.2.0' as p return count(c)

Now, we adjust that query to see if there are any pull requests that have not been merged.

FIND CodeRepo with archived = false as c  THAT RELATES to PR with merged = false  AND title = 'J1 FileManagement Automation v1.2.0' as preturn count(c)

Again, which engineering groups do we contact to ensure the merge happens?

FIND UserGroup as g THAT ALLOWS CodeRepo with archived = false as c  THAT RELATES to PR with merged = false      AND title = 'J1 FileManagement Automation v1.2.0' as preturn g.name, c.displayName, p.name, p.merged ORDER BY g.name ASC

Validating Dependencies are Checked Before Merging

Finally, we get the numbers down to a manageable count, so I start rolling out workflows to all the repos to validate that critical dependencies are addressed before merging. There is a field in GitHub called “requiredStatusChecks” in the branch protection rules. So I’m going to query against that.

Find CodeRepo as c  THAT HAS Rule with requiredStatusChecks ~= 'Validate' as r RETURN c.displayName, r.requiredStatusChecks


Here’s my list of repositories where the status check isn’t configured:

Find CodeRepo as c  THAT HAS Rule with requiredStatusChecks != 'Validate' as r RETURN c.displayName, r.requiredStatusChecks


And, that is a day in the life of a JupiterOne Security Automation Engineer!

To learn more about how JupiterOne security uses JupiterOne, we recommend Erich Smith’s blog on How We Generate a Software Bill of Materials (SBOM) with CycloneDX.  

Cameron Griffin
Cameron Griffin

Cameron is a Senior Security Automation Engineer at JupiterOne. He has spent decades working hard to be lazy - automating and documenting “all the things”. Cameron loves technology, security, and learning something new every day. When away from his keyboard, he enjoys surfing, skateboarding, yoga, reading, and music.

Keep Reading

‘Type and go’ - New JupiterOne search bar enhancements
October 30, 2023
Blog
‘Type and go’ - New JupiterOne search bar enhancements

JupiterOne aggregates and normalizes data from hundreds of different sources so you can identify and triage security risks easily.

Identify and eliminate endpoint device security gaps using the new JupiterOne Unified Device Matrix
October 6, 2023
Blog
Identify and eliminate endpoint device security gaps using the new JupiterOne Unified Device Matrix

It seems like a simple question. “Are any of our deployed user endpoint devices missing an endpoint detection and response agent?”

Why Better Asset Visibility Matters in Cybersecurity | JupiterOne
August 30, 2023
Blog
Back to basics: Why better asset visibility matters in your security program

At the most basic level of the Incident Response Hierarchy, security teams must know the assets they are defending.

15 Mar 2022
Blog
One line headline, one line headline

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud eiut.

15 Mar 2022
Blog
One line headline, one line headline

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud eiut.

15 Mar 2022
Blog
One line headline, one line headline

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud eiut.