Why Pastoralist: Dependency Overrides Need an Audit Trail

A few years ago, I had to fix CVE security issues in a project. One way to address CVEs was with overrides. Overrides let you tell your package manager which dependency versions of package to use, but they don't explain why those dependency override versions were chosen.

On the project I was working on, there were lots of overrides. I didn't know why they were there, whether they could be removed, or what might happen if I added more. It felt like a problem that, if unsolved, would lead me to the same situation again.

This was the start of Pastoralist[1]. I initially built a little logic for the dependency overrides in the project I was working on. The logic allowed me to add and lint an object to a package.json which explained why each override was there. This was a win. It way better than what we had but still a bit unfulfilling and error prone. I thought I could make providing context to overrides easier and this spawned Pastoralist[1]. Recording why an dependency override was there is helpful, but I also wanted to know whether it was still needed.

Below, I will go over Pastoralist and how it answers the questions above for you. Maybe, it can do even more!


The Problem with Overrides

Dependency overrides are a simple, powerful, but blunt tool. The more you add, the harder they are to maintain. Without git and dependency tree information, you have no idea why they are there and neither does the rest of your team.

Lets see how this confusion looks in the example package.json overrides object below:


Quick review of overrides in five OSS projects

Now that hopefully you have some understand of how overrides can get confusing, let's look at five popular projects that use overrides to see if they have issues and see what Pastoralist can do to to help.

The table shows how many overrides each project has.


n8n had 58 overrides: some for security patches (ws, semver, axios), some compatibility pins (date-fns, chokidar, esbuild), some forks ([email protected] remapped to npm:[email protected]). Prisma had 5 overrides that all appeared security-related.

Are all of those overrides still needed?

Now lets see if Pastoralist can help.

In this sample, 140 of 171 tested overrides no longer matched anything in the current dependency tree, or 82%!

Pastoralist found unused overrides in all five projects! And, that's just the start. For example, in Prisma, all 5 overrides appearing security-related. None still matched anything in the dependency tree. Pastoralist can removed them.

Pastoralist is not just for one time use either. Pastoralist can also help with ongoing maintenance by checking for CVEs and adding overrides to address them.


How Pastoralist Works, A Breakdown

Let's look into how Pastoralist works.

Pastoralist[2] helps you with overrides in 3 key ways.

1. Tracking: Understand Each Override

Pastoralist trackes each override. It investigates it to understand the dependency and records that context in its appendix:

The appendix records why an override is there, what depends on it, and when Pastoralist first recorded it.

2. Scan: Check Security and Usage

Pastoralist checks your dependencies for known security issues. It also checks whether your overrides still match the dependency tree. Security scanning supports six providers: OSV[9] (free, no auth required), GitHub Advisory Database, npm audit, Snyk (experimental), Socket (experimental), and Spektion (experimental). Pastoralist can add overrides for CVEs automatically with --checkSecurity --forceSecurityRefactor. It also supports patches you add yourself. Interactive mode lets you choose which fixes to apply.

3. Clean: Update the Appendix and Overrides

Pastoralist updates its appendix and overrides object based on dependency context, security findings, and usage. Use --remove-unused to remove verified unused entries from both the overrides object and the pastoralist object, then run your normal CI checks.


Before and After

This example records the existing override, its dependents, and when Pastoralist first recorded it. It does not add a reason. Reasons can come from security findings or are supplied by the maintainer.


Conclusion

There are a few key things I don't want you to miss in this article!

  1. Overrides are a good tool for pinning dependency versions.
  2. Pastoralist checks for security issues and can add dependency overrides to fix them.
  3. Pastoralist documents why your dependency overrides exist and cleans them up if they are no longer needed.
  4. Pastoralist works with one cli command: npx pastoralist --checkSecurity --forceSecurityRefactor --remove-unused.

Pastoralist has been saving me a lot of time. I am still improving it but it's already giving me joy. Please give Pastoralist a star and try it out. Maybe it could save you a lot of time too?!

If you want more detail on overrides, reach out and I will be happy to write more articles or assist.


Sources

  1. Pastoralist Documentation

    This is the Pastoralist Docs Site. It provides the API, examples, and more things for you and your agents to work quickly.

  2. Pastoralist

    Source code for the override tracking, security scans, and cleanup discussed here.

  3. Jeff Wainwright

    My GitHub profile, where I maintain Pastoralist.

  1. n8n

    n8n is a project I use to automate tool usage and was used in this article to demo override improvements.

  2. TanStack Router

    The TanStack Router repository was used in the override cleanup sample along with n8n and others.

  3. Cal.com

    The Cal.com repository used to check whether its resolutions were still needed.

  4. Next.js

    The Next.js repository included in the override cleanup sample.

  5. Prisma

    The project where all five tested overrides no longer matched the dependency tree.

  6. OSV

    The default vulnerability database used by Pastoralist for its security checks; created and owned by Google.