River
Y CombinatorBacked by Y Combinator

Software & TechnicalFree

Software Supply Chain Risk Assessment

River reads the lockfile rather than the scanner report, then asks one question of every package: if this needed a fix tomorrow, who ships it?

Start here

River starts from the lockfile, so every resolved package gets a row whether or not anything has ever been reported against it. Each row then carries what a scanner has no column for. The date of the last upstream release, whether the repository is archived, whether the publisher flagged the package deprecated, and how many separate accounts can publish a new version of it. Then the licence in the shape it was actually declared, and which of your own direct dependencies is the one pulling it in.

A scanner emits findings. A package with no finding emits nothing, which is why a clean report and a safe tree are two different claims. Kirkbourne, a payroll platform, had 1,284 resolved packages and a scanner report covering 34 of them. Of the 118 packages with no upstream release in two years, 112 appeared in no report anywhere, and nine of the archived ones were running in production. None of that is a finding, so none of it arrives on its own.

Built for the engineer who has to answer a customer's supply chain question honestly, and for the tech lead deciding what to swap out this quarter. Run it when you adopt something large, when you inherit a codebase you did not write, or once a year on the tree you already have. Findings a scanner already reported keep their own process in the vulnerability triage pack, and what comes back here is the evidence behind the next security questionnaire.

What a clean scan does not say

Unmaintained sounds like a judgement and is not. OpenSSF's Scorecard gives a project its lowest score when the repository is archived and its highest when there has been at least one commit a week across the previous ninety days. It rates that check high risk, on the grounds that an inactive project may not get patched. It is also honest about the other direction: a small utility can be finished rather than abandoned.

Some packages say it outright, in a field no vulnerability feed reads. The npm registry entry for request still carries the deprecation notice its maintainers set, and the last version was published in February 2020. Install it and you get a warning in your terminal and nothing at all in your scan. Every registry has some version of that flag, and no scanner treats it as a finding, because it is not one.

The licence is a string, and npm's own documentation gives it four legal shapes. It can be one SPDX identifier, or a compound expression such as MIT OR Apache-2.0. It can also be a pointer to a file inside the package, or one of the older object forms the same documentation now calls invalid metadata. Those last two cannot be resolved without a person opening the file and reading what it says. At Kirkbourne 42 packages sat in that state, and seven of them shipped inside the agent a customer downloads and runs.

How it works

  1. Send the lockfiles

    Manifests and lockfiles for every language in the repository, because the lockfile holds the real tree.

  2. Add the scan

    Whatever your scanner last produced, or nothing at all, in which case the review says so.

  3. Say how it ships

    Whether a customer ever runs this code themselves, since that is what makes a licence matter.

  4. Work the verdicts

    The ranked list, worst first, each row carrying the change to make and its call sites.

What you get

  • Every resolved package gets a row, not only the ones a scanner reported on
  • Last upstream release, archived and deprecated flags, and how many accounts can publish
  • The licence in the shape it was declared, including the ones no checker resolves
  • One forced answer per package: if this needed a fix tomorrow, who ships it
  • Each transitive package named with the direct dependency you would have to move
  • A verdict per row, replace, fork, pin or accept, with the call sites counted

Common questions

Our scanner report came back clean.

Then nothing in your tree has a published advisory against it, which is worth knowing and smaller than it sounds. Kirkbourne's report covered 34 packages out of 1,284. The other 1,250 were not assessed as safe, they were not assessed. Nine of them were archived upstream and running in production.

Isn't unmaintained just a matter of opinion?

It is a set of dates and flags: last release, last commit, whether the repository is archived, whether the publisher set a deprecation flag, and how many accounts can publish a new version. What you do about any of that stays a judgement, and it stays yours. The review supplies the facts and proposes a verdict.

Why name a direct parent for every transitive package?

Because you cannot remove a transitive package. You move the direct dependency that pulls it in, so that is the row which becomes work. The dependency relationship is one of the seven minimum data fields for an SBOM for the same reason, and most scanner exports drop it.

We already run Dependabot.

Good, and it answers a different question. Dependabot opens a pull request when a version you use has a newer one or an advisory against it. It has nothing to say about a package sitting on its final version forever because nobody is publishing another. That package generates no pull request and no finding, ever.

Does it tell us what to replace?

It proposes a verdict per row and shows the working: replace, fork and vendor, pin and watch, or accept, with the call sites counted so the effort is visible. Kirkbourne's top row was an archived SFTP client with no findings, 14 call sites and two drop-in alternatives. Its scanner's highest-severity row ranked eleventh.

What about licences? We are not lawyers.

Neither is this, and it gives no legal advice. It reports what each package declared, flags the ones nothing can resolve automatically, and separates them by whether that code ever reaches a customer's machine. How you ship changes which ones matter, which is why the form asks. What comes back is a shortlist for counsel.

What comes out of it?

A sheet with one row per package carrying depth, direct parent, last release, maintainer count, licence, findings and the verdict. Then a document on the ones worth replacing and why. Where a replacement changes how the system is put together, that belongs in an architecture decision record.

Software Supply Chain Risk Assessment

Fill in the form and your workspace opens with the work already underway.