Grype reporting vulns for unknown versions

Hi! Since upgrading to the latest version of Grype, I’ve noticed that it’s reporting many vulnerabilities when the installed package version is “UNKNOWN.” This issue wasn’t present in version 0.81.0. Could this be a bug?

Thanks in advance!

Screenshot 2024-10-10 at 15.39.42

@aldomalerba are these new packages being discovered (new package names)? or are the same package names being discovered and now the version is unknown?

@wagoodman Sorry, how can I check which packages are being discovered? I see that this version is discovering more packages than the previous one.

The easiest way to do this is to generate an SBOM with syft (or look at the default table view). The two versions of syft you’d need are v1.13.0 and v1.14.0 based on the grype versions you noted.

curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- v1.13.0
./bin/syft <yourimagehere> -o table=with-1.13.txt 

curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- v1.14.0
./bin/syft <yourimagehere> -o table=with-1.14.txt

diff  with-1.13.txt  with-1.14.txt

If you can’t share the original artifact you’re scanning, this diff output would be enough to go on (would you mind sharing the results here?).

@wagoodman I compared the new version with the old one, and there are no “UNKNOWN” packages in the old version. However, in the new version, there are more packages, and some of them are marked as “UNKNOWN.”

with-1.13.txt

hosted-git-info                           7.0.2                  npm     e[38;5;102me[0m                 
http-cache-semantics                      4.1.1                  npm     e[38;5;102me[0m                 
http-proxy-agent                          7.0.2                  npm     e[38;5;102me[0m                 
https-proxy-agent                         7.0.5                  npm     e[38;5;102me[0m                 
iconv-lite                                0.6.3                  npm     e[38;5;102me[0m                 
ieee754                                   1.2.1                  npm     e[38;5;102me[0m  

with-1.14.txt

hosted-git-info                                  7.0.2                  npm     e[38;5;102me[0m                 
http-cache-semantics                             4.1.1                  npm     e[38;5;102me[0m                 
http-proxy                                       UNKNOWN                npm     e[38;5;102m(+1 duplicate)e[0m   
http-proxy-agent                                 7.0.2                  npm     e[38;5;102me[0m                 
http-proxy-agent                                 UNKNOWN                npm     e[38;5;102m(+1 duplicate)e[0m   
https-browserify                                 UNKNOWN                npm     e[38;5;102m(+1 duplicate)e[0m   
https-proxy-agent                                7.0.5                  npm     e[38;5;102me[0m                 
https-proxy-agent                                UNKNOWN                npm     e[38;5;102m(+1 duplicate)e[0m   
iconv-lite                                       0.6.3                  npm     e[38;5;102me[0m                 
icss-utils                                       UNKNOWN                npm     e[38;5;102m(+1 duplicate)e[0m   
ieee754                                          1.2.1                  npm     e[38;5;102me[0m```

These packages with UNKNOWN versions are definitely the result of this PR: feat: report unknowns in sbom by kzantow · Pull Request #2998 · anchore/syft · GitHub. Previously, the behavior was to drop all packages without a name OR version. The PR instead surfaces the packages and allows compliance rules to filter out packages without names and/or versions based on the Syft settings. The defaults include packages with a name but no version, which were previously getting dropped. Perhaps this is the wrong default behavior and/or we should revert the javascript cataloger behavior specifically?

@westonsteimel suggested that instead, we make it configurable in grype whether a version of UNKNOWN is considered to match all vulnerabilities for the package or none of them. I think that’s probably the right thing to do.

I also think it’s interesting that we have duplicate packages where one has an unknown version:

https-proxy-agent                                7.0.5                  npm     e[38;5;102me[0m                 
https-proxy-agent                                UNKNOWN                npm     e[38;5;102m(+1 duplicate)e[0m   

I haven’t investigated, but that strikes me as a bug in the known-unknowns functionality in Syft - maybe one strategy for parsing the package succeeded in finding a version, and another didn’t, but now the one that failed is being listed as a duplicated instead of being dropped. Note that Syft 1.13.0 found the same package, but only shows an entry with a known version.

Is there a public image that exhibits this behavior? I’m interested to see what files result in the UNKNOWN versions.

@kzantow at the moment I don’t have a public image, but if it helps, it’s a Docker image of a software built with Next.js version 14, and most of the packages are dependencies of Next.js.

Thanks @aldomalerba I was able to find public image that exhibits this behavior.

The image is here - I don’t know anything about it except that it’s useful for demonstrating this type of output.

Running Syft 1.14.0 has the following in the output:

$ syft --platform=linux/amd64 blueriver/nextjs:latest | grep strip-ansi
strip-ansi                                       6.0.1                   npm     (+1 duplicate)
strip-ansi                                       7.1.0                   npm     (+1 duplicate)
strip-ansi                                       UNKNOWN                 npm

while running Syft v1.13.0 (I just checked out the git tag and did go run ./cmd/syft) has this:

$ go run ./cmd/syft --platform=linux/amd64 blueriver/nextjs:latest | grep strip-ansi
strip-ansi                          6.0.1                   npm     (+1 duplicate)
strip-ansi                          7.1.0                   npm     (+1 duplicate)

So it looks like maybe we’re finding an extra strip-ansi package in latest Syft, and it was getting dropped previously. I guess the question is: is there a third installation of that package with no version information in the image, or did Syft make it up?

1 Like

Based on a conversation with @wagoodman and @westonsteimel:

  • Syft is right - there really is a 3rd, version-less strip-ansi in that image, for example. We believe this because the location is different. So previously this version-less package was dropped, but now its version is stubbed.
  • Grype should not attempt to match vulnerabilities against packages whose version is UNKNOWN by default, but maybe this should be configurable.

For the second item, there are 2 changes:

  1. When invoking Syft from Grype, we should set the MissingVersion behavior to “DROP” instead of “STUB”, see syft/syft/cataloging/compliance.go at 0c71bf23c51e3fc77ae115dc4a6148cb5b94bea3 · anchore/syft · GitHub
  2. When parsing an SBOM in Grype, we should drop packages with UNKNOWN version, since these cannot be sensibly compared to a vulnerability database. (Maybe some very risk averse users would with to change this, and instead treat packages with UNKNOWN versions as having every relevant vulnerability.)

The next step is to make a Grype issue and implement it.

@aldomalerba here’s a workaround for now:

SYFT_COMPLIANCE_MISSING_VERSION=drop syft --platform=linux/amd64 blueriver/nextjs:latest -o json | grype

for example. That will cause Syft to drop packages that have no version, since these are not very useful for matching against vulnerabilities.

1 Like

Thanks for the workaround, it works great. I’ll also keep an eye on the GitHub issue and the upcoming solution. Big thanks to everyone for the quick help and support!

1 Like

Just some notes for the investigation: I think this is something nextjs is doing.

If we make this Dockerfile:

FROM node:latest
RUN yes | npx create-next-app@latest app

And then build with docker build . -t test-unknowns:nextjs

It finds many of the same packages:

$ syft test-unknowns:nextjs | grep UNKNOWN
... snip ...
strip-ansi                                       UNKNOWN                         npm
... snip ...

I grabbed the location syft found at these and copied some of them out of the container. Basically, Syft is finding a package.json with a name, maybe some author information, and no version. However, these packages have version info in their package.json, see for example strip-ansi/package.json at main · chalk/strip-ansi · GitHub.

The package.json files usually have node_modules/dist/compiled in their path - I think some nextjs compilation / install process is stripping off some packaging data. I might try to open an issue with next js.

1 Like

Thanks again for the report @aldomalerba - the latest Grype release, v0.82.1, fixes this issue. Please let us know if you have any other questions or feedback!