How to do vulnerability scanning with Nix SBOMS?

Introduction

Hello,

My organization handles many docker images and I am trying to introduce Nix as an alternative method for building these images. One of the steps in introducing a new container to our registry is scanning for vulnerabilities using tools similar to Grype.

I figured the easiest way to insert a Nix based container into this pipeline would be to generate an SBOM and then use that SBOM with Grype to obtain an initial POC. My problem comes from the fact that I can’t seem to find a combination of SBOM generators and vulnerability scanners that will detect vulnerabilities in python packages included with a python environment. Let me illustrate with a simple example.

The package

I’ve arbitrarily chosen a package called python-multipart which is vulnerable to GHSA-59g5-xgcq-4qw3 and is on both GitHub’s Security Advisory and NVD. I picked a nixpkgs revision corresponding to version 0.0.9 using Nix package versions.

Bombon

My first approach was to see if I could use @nikstur 's bombon with the following snippet

        let
          python = pkgs.python3.withPackages (
            p: with p; [
              python-multipart
            ]
          );
        in
        {
          packages = {
            default = python;
            sbom = bombon.lib.${system}.buildBom python { };
          };
        }

Bombon successfully captures the package in the SBOM

    {
      "type": "application",
      "bom-ref": "dhmdrknyskqn7p4qci8w9l9rrpcysmr0-python3.12-python-multipart-0.0.9",
      "name": "python3.12-python-multipart-0.0.9",
      "version": "",
      "scope": "required",
      "purl": "pkg:nix/python3.12-python-multipart-0.0.9"
    }

but when I run the SBOM through Grype I don’t get any vulnerabilities.

$ grype  sbom:./result
 βœ” Vulnerability DB                [updated]  
 βœ” Scanned for vulnerabilities     [0 vulnerability matches]  
   β”œβ”€β”€ by severity: 0 critical, 0 high, 0 medium, 0 low, 0 negligible
   └── by status:   0 fixed, 0 not-fixed, 0 ignored 
No vulnerabilities found
A newer version of grype is available for download: 0.94.0 (installed version is 0.92.2)

I imagine this is because Nix is not yet defined as part of the PURL spec. I think the previous issue is most likely on hold due to @fricklerhandwerk 's Nixpkgs supply chain security project - #32 by fricklerhandwerk and @YorikSar 's work on adding CPEs (Common Platform Enumerations) to nix packages meta attribute as pointed out by @fricklerhandwerk at the bottom of the previously mentioned thread.

Syft

Syft also mentions that it has the capability to capture Nix packages in SBOMs so I tried the same approach as Bombon in first generating an SBOM and then sending it through Grype.

$ syft ./result
 βœ” Indexed file system                                                                                                        /nix/store/236s9a8wc3p378hbm80bjrd4ml61ihd8-python3-3.12.5-env 
 βœ” Cataloged contents                                                                                                       f6a214f7a5fcda0c2cee9660b7fc29f5649e3c68aad48e20e950137c98913a68 
   β”œβ”€β”€ βœ” Packages                        [0 packages]  
   └── βœ” Executables                     [14 executables]  
[0000]  WARN no explicit name and version provided for directory source, deriving artifact ID from the given path (which is not ideal)
No packages discovered

Syft doesn’t discover any packages :frowning_face:

Sbomnix

Sbomnix also has the ability to generate SBOMs from nix store paths.

$ grype  sbom:sbom.cdx.json
 βœ” Scanned for vulnerabilities     [6 vulnerability matches]  
   β”œβ”€β”€ by severity: 1 critical, 2 high, 3 medium, 0 low, 0 negligible
   └── by status:   6 fixed, 0 not-fixed, 0 ignored 
[0000]  WARN invalid CPE: cpe:2.3:a:python3-3.12.5-env:python3-3.12.5-env::*:*:*:*:*:*:*
NAME     INSTALLED  FIXED-IN                                     TYPE  VULNERABILITY   SEVERITY  EPSS%  RISK  
openssl  3.0.14     1.0.2zk, 1.1.1za, 3.0.15, 3.1.7, 3.2.3, ...  nix   CVE-2024-5535   Critical  89.39    4.7  
openssl  3.0.14     3.0.15, 3.1.7, 3.2.3, 3.3.2                  nix   CVE-2024-6119   High      62.89    0.3  
openssl  3.0.14     1.0.2zl, 1.1.1zb, 3.0.16, 3.1.8, 3.2.4, ...  nix   CVE-2024-9143   Medium    60.81    0.2  
sqlite   3.46.0     3.49.1                                       nix   CVE-2025-3277   Medium    26.56  < 0.1  
sqlite   3.46.0     3.49.1                                       nix   CVE-2025-29087  High      13.33  < 0.1  
openssl  3.0.14     1.0.2zl, 1.1.1zb, 3.0.16, 3.1.8, 3.2.4, ...  nix   CVE-2024-13176  Medium    13.19  < 0.1
A newer version of grype is available for download: 0.94.0 (installed version is 0.92.2)

This time the system dependencies report vulnerabilities however the python vulnerabilities are still left out. Looking inside the generated sbom.cdx.json we can see why. Even though sbomnix is able to guess the CPEs, because Grype does not use CPEs, no vulnerabilities are found once again.

    {
      "type": "library",
      "bom-ref": "/nix/store/ibqys8vnd6n15s5abfp9rg81nv13871k-python3.12-python-multipart-0.0.9.drv",
      "name": "python3.12-python-multipart",
      "version": "0.0.9",
      "purl": "pkg:nix/python3.12-python-multipart@0.0.9",
      "cpe": "cpe:2.3:a:python3.12-python-multipart:python3.12-python-multipart:0.0.9:*:*:*:*:*:*:*",
      "description": "A streaming multipart parser for Python",
      "licenses": [
        {
          "license": {
            "id": "Apache-2.0"
          }
        }
      ],
      "properties": [
        {
          "name": "nix:output_path",
          "value": "/nix/store/dhmdrknyskqn7p4qci8w9l9rrpcysmr0-python3.12-python-multipart-0.0.9"
        },
        {
          "name": "nix:drv_path",
          "value": "/nix/store/ibqys8vnd6n15s5abfp9rg81nv13871k-python3.12-python-multipart-0.0.9.drv"
        },
        {
          "name": "homepage",
          "value": "https://github.com/andrew-d/python-multipart"
        }
      ]
    },

Vulnix

With the sbom options exhausted I tried to use the nix based vulnerability scanners as a last ditch effort even though I need to use SBOMs with my organization’s scanners.

In this case vulnix also does not identify any python dependencies

$ vulnix ./result
8 derivations with active advisories

------------------------------------------------------------------------
binutils-2.42

/nix/store/y5mx8497y389bwdwjl8sswn31zfci7zi-binutils-2.42.drv
CVE                                                CVSSv3
https://nvd.nist.gov/vuln/detail/CVE-2025-0840     7.5

------------------------------------------------------------------------
curl-8.9.1

/nix/store/qw7wyz0zjdhzk90ciy8yxb3d6zpjlpn4-curl-8.9.1.drv
CVE                                                CVSSv3
https://nvd.nist.gov/vuln/detail/CVE-2024-9681     6.5
https://nvd.nist.gov/vuln/detail/CVE-2025-0725
https://nvd.nist.gov/vuln/detail/CVE-2025-4947

------------------------------------------------------------------------
gcc-13.3.0

/nix/store/2paz2p74ndfi046qgrmii12fvrbpk4ax-gcc-13.3.0.drv
CVE                                                CVSSv3
https://nvd.nist.gov/vuln/detail/CVE-2023-4039     4.8

------------------------------------------------------------------------
glibc-2.39-52

/nix/store/0fj9hk6x5yprg4v9l2b0g4fp1bnchalz-glibc-2.39-52.drv
CVE                                                CVSSv3
https://nvd.nist.gov/vuln/detail/CVE-2024-33599
https://nvd.nist.gov/vuln/detail/CVE-2024-33600
https://nvd.nist.gov/vuln/detail/CVE-2024-33601
https://nvd.nist.gov/vuln/detail/CVE-2024-33602

------------------------------------------------------------------------
openssl-3.0.14

/nix/store/dk90k06afxlyvzrclkv6r57s2k6hb0q7-openssl-3.0.14.drv
CVE                                                CVSSv3
https://nvd.nist.gov/vuln/detail/CVE-2024-6119     7.5

------------------------------------------------------------------------
perl-5.38.2

/nix/store/qcvhn33qbvkfxnxnjnzvsr1rgc9yrzk7-perl-5.38.2.drv
CVE                                                CVSSv3
https://nvd.nist.gov/vuln/detail/CVE-2024-56406

------------------------------------------------------------------------
sqlite-3.46.0

/nix/store/53z81gf6p4v2q0cqagsk52sxf3f729aa-sqlite-3.46.0.drv
CVE                                                CVSSv3
https://nvd.nist.gov/vuln/detail/CVE-2025-29087    7.5

------------------------------------------------------------------------
zlib-1.3.1

/nix/store/2ksxw3mkxlxr7l00fqvfgf3v1jnzzm7m-zlib-1.3.1.drv
CVE                                                CVSSv3
https://nvd.nist.gov/vuln/detail/CVE-2023-6992     5.5

Vulnxscan

vulnxscan comes from the sbomnix repo and similarly does not yield any python dependencies.

$ nix run github:tiiuae/sbomnix#vulnxscan -- ./result  
INFO     Evaluating './result'
INFO     Try force-realising store-path './result'
INFO     Generating SBOM for target '/nix/store/236s9a8wc3p378hbm80bjrd4ml61ihd8-python3-3.12.5-env'
INFO     Loading runtime dependencies referenced by '/nix/store/236s9a8wc3p378hbm80bjrd4ml61ihd8-python3-3.12.5-env'
INFO     Running vulnix scan
INFO     Running grype scan
INFO     Running OSV scan
INFO     Querying vulnerabilities
INFO     Filtering patched vulnerabilities
INFO     CVE-2024-5535 for 'openssl' is patched with: ['/nix/store/a2l99gf3kh2da9amplpvgxnqg7bn6ni7-CVE-2024-5535.patch']
INFO     Console report

Potential vulnerabilities impacting version_local: 

| vuln_id               | url                                             | package   | version_local   | severity   |  grype  |  osv  |  vulnix  |  sum  |
|-----------------------+-------------------------------------------------+-----------+-----------------+------------+---------+-------+----------+-------|
| BIT-sqlite-2025-29087 | https://osv.dev/BIT-sqlite-2025-29087           | sqlite    | 3.46.0          |            |    0    |   1   |    0     |   1   |
| CVE-2025-29087        | https://nvd.nist.gov/vuln/detail/CVE-2025-29087 | sqlite    | 3.46.0          | 7.5        |    1    |   0   |    1     |   2   |
| BIT-sqlite-2025-3277  | https://osv.dev/BIT-sqlite-2025-3277            | sqlite    | 3.46.0          |            |    0    |   1   |    0     |   1   |
| CVE-2025-3277         | https://nvd.nist.gov/vuln/detail/CVE-2025-3277  | sqlite    | 3.46.0          | 6.9        |    1    |   0   |    0     |   1   |
| CVE-2024-33602        | https://nvd.nist.gov/vuln/detail/CVE-2024-33602 | glibc     | 2.39-52         |            |    0    |   0   |    1     |   1   |
| CVE-2024-33601        | https://nvd.nist.gov/vuln/detail/CVE-2024-33601 | glibc     | 2.39-52         |            |    0    |   0   |    1     |   1   |
| CVE-2024-33600        | https://nvd.nist.gov/vuln/detail/CVE-2024-33600 | glibc     | 2.39-52         |            |    0    |   0   |    1     |   1   |
| CVE-2024-33599        | https://nvd.nist.gov/vuln/detail/CVE-2024-33599 | glibc     | 2.39-52         |            |    0    |   0   |    1     |   1   |
| CVE-2024-13176        | https://nvd.nist.gov/vuln/detail/CVE-2024-13176 | openssl   | 3.0.14          | 4.1        |    1    |   0   |    0     |   1   |
| CVE-2024-9143         | https://nvd.nist.gov/vuln/detail/CVE-2024-9143  | openssl   | 3.0.14          | 4.3        |    1    |   0   |    0     |   1   |
| CVE-2024-6119         | https://nvd.nist.gov/vuln/detail/CVE-2024-6119  | openssl   | 3.0.14          | 7.5        |    1    |   0   |    1     |   2   |
| BIT-sqlite-2024-0232  | https://osv.dev/BIT-sqlite-2024-0232            | sqlite    | 3.46.0          |            |    0    |   1   |    0     |   1   |
| CVE-2023-6992         | https://nvd.nist.gov/vuln/detail/CVE-2023-6992  | zlib      | 1.3.1           | 5.5        |    0    |   0   |    1     |   1   |
| CVE-2023-4039         | https://nvd.nist.gov/vuln/detail/CVE-2023-4039  | gcc       | 13.3.0          | 4.8        |    0    |   0   |    1     |   1   |
| MAL-2022-4301         | https://osv.dev/MAL-2022-4301                   | libidn2   | 2.3.7           |            |    0    |   1   |    0     |   1   |
| RUSTSEC-2019-0006     | https://osv.dev/RUSTSEC-2019-0006               | ncurses   | 6.4.20221231    |            |    0    |   1   |    0     |   1   |


INFO     Wrote: /home/collin/projects/minimal_nix_container/vulns.csv

Conclusion/Question

This post is not intended to be a slight on any of the tools being used. After doing a bunch of research on this topic, I can see that properly identifying packages is quite the difficult task.

I’m still left wondering if I’m doing something wrong or if there is some flag I need to add somewhere to include some metadata on these python dependencies to allow them to be picked up by vuln scanners. I’m not sure how other organizations keep track of there vulnerabilities with Nix so if someone could shed some light on this subject I would greatly appreciate it.

If anything I hope this can be a useful user story :smile:

Reference flake

{
  description = "Demonstration of vuln scanners with nix";
  inputs = {
    python-multipart-nixpkgs.url = "github:nixos/nixpkgs?rev=ab7b6889ae9d484eed2876868209e33eb262511d";
    nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
    flake-parts.url = "github:hercules-ci/flake-parts";
    bombon.url = "github:nikstur/bombon";
  };
  outputs =
    inputs@{
      self,
      nixpkgs,
      python-multipart-nixpkgs,
      flake-parts,
      bombon,
    }:
    flake-parts.lib.mkFlake { inherit inputs; } {
      systems = [ "x86_64-linux" ];
      perSystem =
        {
          pkgs,
          self',
          system,
          ...
        }:
        let
          python = python-multipart-nixpkgs.legacyPackages.${system}.python3.withPackages (
            p: with p; [
              python-multipart
            ]
          );
        in
        {
          packages = {
            default = python;
            sbom = bombon.lib.${system}.buildBom python { };
          };
          devShells.default = pkgs.mkShell {
            packages = with pkgs; [
              python
              syft
              grype
              vulnix
            ];
          };
        };
    };
}
7 Likes

To clarify, @RaitoBezarius proposal for adding Nix to the PURL spec is not competing with the currently ongoing proposal to encode CPE information in Nixpkgs expressions, this is different people working independently on various parts of the puzzle.

2 Likes

Ah, thank you for the clarification. The whole space for security seems like quite the dragon!

:wave: Hey @collinarnett!

Syft maintainer here coming to take a look at why you would be getting 0 packages when running syft against a container with nixos as the base image!

Here’s a minimal Dockerfile I used to demonstrate how syft does the cataloging:

FROM nixos/nix

RUN nix-channel --update

# Install Python + python-multipart
RUN nix-env -iA nixpkgs.python3 nixpkgs.python3Packages.python-multipart

Then I ran syft against the image(truncated the package list to show python-multipart appearing):

docker build nixos:test .
syft nixos:test
 βœ” Loaded image                                                                                                                    blah:blah
 βœ” Parsed image                                                      sha256:7063750e9af4c6ce7df75b40e91d515e4701be6a9700f03f4ee661e3139f41b3
 βœ” Cataloged contents                                                       376f7e5e8a52ae586bf7b2893571beb921f5ea8d47246756c982a2749605ca37
   β”œβ”€β”€ βœ” Packages                        [215 packages]
   β”œβ”€β”€ βœ” File metadata                   [89 locations]
   β”œβ”€β”€ βœ” File digests                    [89 files]
   └── βœ” Executables                     [1,366 executables]

...
pcre2                         10.44                nix
pcsclite                      2.3.0                nix
pgrp-pipe                     5                    nix
pomerium                      1.0.0                npm     (+1 duplicate)
publicsuffix-list-0           unstable-2025-03-12  nix
python                        3.13.5               binary
python-gdcm                   @GDCM_VER@           python  (+1 duplicate)
python-multipart              0.0.20               python
python3                       3.13.5               nix     (+1 duplicate)
python3.13-python-multipart   0.0.20               nix     (+1 duplicate)
react-static                  7.6.2                npm     (+1 duplicate)
readline                      8.2p13               nix     (+1 duplicate)
renderer                      1.0.0                npm     (+1 duplicate)
...

Here is the full syft json for python-multipart found by syft in the above container:

    {
      "id": "e9d60daae6fe89c3",
      "name": "python-multipart",
      "version": "0.0.20",
      "type": "python",
      "foundBy": "python-installed-package-cataloger",
      "locations": [
        {
          "path": "/nix/store/f9wf70ivpjdq6ibbw259lfg9wr15lmsi-python3.13-python-multipart-0.0.20/lib/python3.13/site-packages/python_multipart-0.0.20.dist-info/METADATA",
          "layerID": "sha256:e494d8833830cbca631125bddd033f52a514c2ecdb40639e17fc66e7730ddebc",
          "accessPath": "/nix/store/f9wf70ivpjdq6ibbw259lfg9wr15lmsi-python3.13-python-multipart-0.0.20/lib/python3.13/site-packages/python_multipart-0.0.20.dist-info/METADATA",
          "annotations": {
            "evidence": "primary"
          }
        },
        {
          "path": "/nix/store/f9wf70ivpjdq6ibbw259lfg9wr15lmsi-python3.13-python-multipart-0.0.20/lib/python3.13/site-packages/python_multipart-0.0.20.dist-info/RECORD",
          "layerID": "sha256:e494d8833830cbca631125bddd033f52a514c2ecdb40639e17fc66e7730ddebc",
          "accessPath": "/nix/store/f9wf70ivpjdq6ibbw259lfg9wr15lmsi-python3.13-python-multipart-0.0.20/lib/python3.13/site-packages/python_multipart-0.0.20.dist-info/RECORD",
          "annotations": {
            "evidence": "supporting"
          }
        }
      ],
      "licenses": [
        {
          "value": "Apache-2.0",
          "spdxExpression": "Apache-2.0",
          "type": "declared",
          "urls": [],
          "locations": [
            {
              "path": "/nix/store/f9wf70ivpjdq6ibbw259lfg9wr15lmsi-python3.13-python-multipart-0.0.20/lib/python3.13/site-packages/python_multipart-0.0.20.dist-info/METADATA",
              "layerID": "sha256:e494d8833830cbca631125bddd033f52a514c2ecdb40639e17fc66e7730ddebc",
              "accessPath": "/nix/store/f9wf70ivpjdq6ibbw259lfg9wr15lmsi-python3.13-python-multipart-0.0.20/lib/python3.13/site-packages/python_multipart-0.0.20.dist-info/METADATA"
            }
          ]
        }
      ],
      "language": "python",
      "cpes": [
        {
          "cpe": "cpe:2.3:a:fastapiexpert:python-multipart:0.0.20:*:*:*:*:python:*:*",
          "source": "nvd-cpe-dictionary"
        }
      ],
      "purl": "pkg:pypi/python-multipart@0.0.20",
      "metadataType": "python-package",
      "metadata": {
        "name": "python-multipart",
        "version": "0.0.20",
        "author": "",
        "authorEmail": "Andrew Dunham <andrew@du.nham.ca>, Marcelo Trylesinski <marcelotryle@gmail.com>",
        "platform": "",
        "files": [
          {
            "path": "multipart/__init__.py",
            "digest": {
              "algorithm": "sha256",
              "value": "_ttxOAFnTN4jeac-_8NeXpaXYYo0PPEIp8Ogo4YFNHE"
            },
            "size": "935"
          },
          {
            "path": "multipart/decoders.py",
            "digest": {
              "algorithm": "sha256",
              "value": "XvkAwTU9UFPiXkc0hkvovHf0W6H3vK-2ieWlhav02hQ"
            },
            "size": "40"
          },
          {
            "path": "multipart/exceptions.py",
            "digest": {
              "algorithm": "sha256",
              "value": "6D_X-seiOmMAlIeiGlPGUs8-vpcvIGJeQycFMDb1f7A"
            },
            "size": "42"
          },
          {
            "path": "multipart/multipart.py",
            "digest": {
              "algorithm": "sha256",
              "value": "8fDH14j_VMbrch_58wlzi63XNARGv80kOZAyN72aG7A"
            },
            "size": "41"
          },
          {
            "path": "python_multipart/__init__.py",
            "digest": {
              "algorithm": "sha256",
              "value": "Nlw6Yrc__qXnCZLo17OzbJR2w2mwiSFk69IG4Wl35EU"
            },
            "size": "512"
          },
          {
            "path": "python_multipart/decoders.py",
            "digest": {
              "algorithm": "sha256",
              "value": "JM43FMNn_EKP0MI2ZkuZHhNa0MOASoIR0U5TvdG585k"
            },
            "size": "6669"
          },
          {
            "path": "python_multipart/exceptions.py",
            "digest": {
              "algorithm": "sha256",
              "value": "a9buSOv_eiHZoukEJhdWX9LJYSJ6t7XOK3ZEaWoQZlk"
            },
            "size": "992"
          },
          {
            "path": "python_multipart/multipart.py",
            "digest": {
              "algorithm": "sha256",
              "value": "pk3o3eB3KXbNxzOBxbEjCdz-1ESEZIMXVIfl12grG-o"
            },
            "size": "76427"
          },
          {
            "path": "python_multipart/py.typed",
            "digest": {
              "algorithm": "sha256",
              "value": "47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU"
            },
            "size": "0"
          },
          {
            "path": "python_multipart-0.0.20.dist-info/METADATA",
            "digest": {
              "algorithm": "sha256",
              "value": "h2GtPOVShbVkpBUrjp5KE3t6eiJJhd0_WCaCXrb5TgU"
            },
            "size": "1817"
          },
          {
            "path": "python_multipart-0.0.20.dist-info/WHEEL",
            "digest": {
              "algorithm": "sha256",
              "value": "qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ"
            },
            "size": "87"
          },
          {
            "path": "python_multipart-0.0.20.dist-info/licenses/LICENSE.txt",
            "digest": {
              "algorithm": "sha256",
              "value": "qOgzF2zWF9rwC51tOfoVyo7evG0WQwec0vSJPAwom-I"
            },
            "size": "556"
          },
          {
            "path": "python_multipart-0.0.20.dist-info/RECORD"
          }
        ],
        "sitePackagesRootPath": "/nix/store/f9wf70ivpjdq6ibbw259lfg9wr15lmsi-python3.13-python-multipart-0.0.20/lib/python3.13/site-packages",
        "requiresPython": ">=3.8"
      }
    },

Do you mind sharing any details about your scan target of result and how it’s being built? I’d love to track down where we might be falling short here and update our cataloger accordingly :smile:

Also! Just to finish the loop here I updated the version in the above multipart entry to be < 18.

After doing that and running the SBOM syft generated into grype the vulnerability you’re looking for GHSA-59g5-xgcq-4qw3 appears :tada:

cat nixos-test-syft.json | grype
NAME                INSTALLED  FIXED IN  TYPE    VULNERABILITY        SEVERITY  EPSS           RISK
python-multipart    0.0.17     0.0.18    python  GHSA-59g5-xgcq-4qw3  High      0.5% (62nd)    0.3
sqlite              3.48.0     3.49.1    nix     CVE-2025-3277        Medium    < 0.1% (26th)  < 0.1
...
1 Like

Hey thanks so much for taking a look into this.

Do you mind sharing any details about your scan target of result and how it’s being built?

If you take the nix flake file I provided along with this lockfile you should be able to run nix build . in the same directory as the flake.nix and flake.lock provided you have flakes and nix-command added to the experimental section of your nix config. More details can be found here (Flakes - NixOS Wiki).

flake.lock
{
  "nodes": {
    "bombon": {
      "inputs": {
        "flake-parts": "flake-parts",
        "nixpkgs": "nixpkgs",
        "pre-commit-hooks-nix": "pre-commit-hooks-nix",
        "systems": "systems"
      },
      "locked": {
        "lastModified": 1748818846,
        "narHash": "sha256-xM03NFKW63HUKgGkugoX4dqwLAzYnTergbugUvu1Q5g=",
        "owner": "nikstur",
        "repo": "bombon",
        "rev": "9696201440b1e2ffa41ccd104ddffa1be60ee70b",
        "type": "github"
      },
      "original": {
        "owner": "nikstur",
        "repo": "bombon",
        "type": "github"
      }
    },
    "flake-compat": {
      "flake": false,
      "locked": {
        "lastModified": 1696426674,
        "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
        "owner": "edolstra",
        "repo": "flake-compat",
        "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
        "type": "github"
      },
      "original": {
        "owner": "edolstra",
        "repo": "flake-compat",
        "type": "github"
      }
    },
    "flake-parts": {
      "inputs": {
        "nixpkgs-lib": [
          "bombon",
          "nixpkgs"
        ]
      },
      "locked": {
        "lastModified": 1743550720,
        "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
        "owner": "hercules-ci",
        "repo": "flake-parts",
        "rev": "c621e8422220273271f52058f618c94e405bb0f5",
        "type": "github"
      },
      "original": {
        "owner": "hercules-ci",
        "repo": "flake-parts",
        "type": "github"
      }
    },
    "flake-parts_2": {
      "inputs": {
        "nixpkgs-lib": "nixpkgs-lib"
      },
      "locked": {
        "lastModified": 1749398372,
        "narHash": "sha256-tYBdgS56eXYaWVW3fsnPQ/nFlgWi/Z2Ymhyu21zVM98=",
        "owner": "hercules-ci",
        "repo": "flake-parts",
        "rev": "9305fe4e5c2a6fcf5ba6a3ff155720fbe4076569",
        "type": "github"
      },
      "original": {
        "owner": "hercules-ci",
        "repo": "flake-parts",
        "type": "github"
      }
    },
    "gitignore": {
      "inputs": {
        "nixpkgs": [
          "bombon",
          "pre-commit-hooks-nix",
          "nixpkgs"
        ]
      },
      "locked": {
        "lastModified": 1709087332,
        "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
        "owner": "hercules-ci",
        "repo": "gitignore.nix",
        "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
        "type": "github"
      },
      "original": {
        "owner": "hercules-ci",
        "repo": "gitignore.nix",
        "type": "github"
      }
    },
    "nixpkgs": {
      "locked": {
        "lastModified": 1748693115,
        "narHash": "sha256-StSrWhklmDuXT93yc3GrTlb0cKSS0agTAxMGjLKAsY8=",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "910796cabe436259a29a72e8d3f5e180fc6dfacc",
        "type": "github"
      },
      "original": {
        "owner": "NixOS",
        "ref": "nixos-unstable",
        "repo": "nixpkgs",
        "type": "github"
      }
    },
    "nixpkgs-lib": {
      "locked": {
        "lastModified": 1748740939,
        "narHash": "sha256-rQaysilft1aVMwF14xIdGS3sj1yHlI6oKQNBRTF40cc=",
        "owner": "nix-community",
        "repo": "nixpkgs.lib",
        "rev": "656a64127e9d791a334452c6b6606d17539476e2",
        "type": "github"
      },
      "original": {
        "owner": "nix-community",
        "repo": "nixpkgs.lib",
        "type": "github"
      }
    },
    "nixpkgs_2": {
      "locked": {
        "lastModified": 1750898778,
        "narHash": "sha256-DXI7+SKDlTyA+C4zp0LoIywQ+BfdH5m4nkuxbWgV4UU=",
        "owner": "nixos",
        "repo": "nixpkgs",
        "rev": "322d8a3c6940039f7cff179a8b09c5d7ca06359d",
        "type": "github"
      },
      "original": {
        "owner": "nixos",
        "ref": "nixpkgs-unstable",
        "repo": "nixpkgs",
        "type": "github"
      }
    },
    "pre-commit-hooks-nix": {
      "inputs": {
        "flake-compat": "flake-compat",
        "gitignore": "gitignore",
        "nixpkgs": [
          "bombon",
          "nixpkgs"
        ]
      },
      "locked": {
        "lastModified": 1747372754,
        "narHash": "sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo+bnXU9D9k=",
        "owner": "cachix",
        "repo": "pre-commit-hooks.nix",
        "rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46",
        "type": "github"
      },
      "original": {
        "owner": "cachix",
        "repo": "pre-commit-hooks.nix",
        "type": "github"
      }
    },
    "python-multipart-nixpkgs": {
      "locked": {
        "lastModified": 1727390236,
        "narHash": "sha256-X2LaWM0WwoxUfu7cQmKyVbkYn0xwdRvBA+vkLg/OgnI=",
        "owner": "nixos",
        "repo": "nixpkgs",
        "rev": "ab7b6889ae9d484eed2876868209e33eb262511d",
        "type": "github"
      },
      "original": {
        "owner": "nixos",
        "repo": "nixpkgs",
        "rev": "ab7b6889ae9d484eed2876868209e33eb262511d",
        "type": "github"
      }
    },
    "root": {
      "inputs": {
        "bombon": "bombon",
        "flake-parts": "flake-parts_2",
        "nixpkgs": "nixpkgs_2",
        "python-multipart-nixpkgs": "python-multipart-nixpkgs"
      }
    },
    "systems": {
      "locked": {
        "lastModified": 1681028828,
        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
        "owner": "nix-systems",
        "repo": "default",
        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
        "type": "github"
      },
      "original": {
        "owner": "nix-systems",
        "repo": "default",
        "type": "github"
      }
    }
  },
  "root": "root",
  "version": 7
}

After you run nix build . you should find a result symlink to a python installation. Here’s the tree for that.

tree ./result -L 2
./result
β”œβ”€β”€ bin
β”‚   β”œβ”€β”€ 2to3
β”‚   β”œβ”€β”€ 2to3-3.12
β”‚   β”œβ”€β”€ idle
β”‚   β”œβ”€β”€ idle3
β”‚   β”œβ”€β”€ idle3.12
β”‚   β”œβ”€β”€ pydoc
β”‚   β”œβ”€β”€ pydoc3
β”‚   β”œβ”€β”€ pydoc3.12
β”‚   β”œβ”€β”€ python
β”‚   β”œβ”€β”€ python3
β”‚   β”œβ”€β”€ python3.12
β”‚   β”œβ”€β”€ python3.12-config
β”‚   β”œβ”€β”€ python3-config
β”‚   └── python-config
β”œβ”€β”€ include -> /nix/store/h3i0acpmr8mrjx07519xxmidv8mpax4y-python3-3.12.5/include
β”œβ”€β”€ lib
β”‚   β”œβ”€β”€ libpython3.12.so -> /nix/store/h3i0acpmr8mrjx07519xxmidv8mpax4y-python3-3.12.5/lib/libpython3.12.so
β”‚   β”œβ”€β”€ libpython3.12.so.1.0 -> /nix/store/h3i0acpmr8mrjx07519xxmidv8mpax4y-python3-3.12.5/lib/libpython3.12.so.1.0
β”‚   β”œβ”€β”€ libpython3.so -> /nix/store/h3i0acpmr8mrjx07519xxmidv8mpax4y-python3-3.12.5/lib/libpython3.so
β”‚   β”œβ”€β”€ pkgconfig -> /nix/store/h3i0acpmr8mrjx07519xxmidv8mpax4y-python3-3.12.5/lib/pkgconfig
β”‚   └── python3.12
└── share -> /nix/store/h3i0acpmr8mrjx07519xxmidv8mpax4y-python3-3.12.5/share

In my actual use-case I embed the python installation in a docker container using the following nix code in that same flake.nix file.
             ...
             default = python;                                                                                                              
             container = pkgs.dockerTools.buildLayeredImage {                                                                               
               name = "python-multipart-container";                                                                                         
               contents = [python];                                                                                                         
             };                                                                                                                             
             sbom = bombon.lib.${system}.buildBom python { }; 
             ...

If I run syft on that container, it finds the python packages.

$ nix build .#container && docker load < ./result                                                 
Loaded image: python-multipart-container:v14yr0d4jax2sfb109yz0fbjj018mdfz
$ syft python-multipart-container:v14yr0d4jax2sfb109yz0fbjj018mdfz -o json > python-multipart.json
 βœ” Loaded image                                                                  python-multipart-container:v14yr0d4jax2sfb109yz0fbjj018mdfz 
 βœ” Parsed image                                                      sha256:ee5ebcf0e0dc698de123e94c9a02907dded8b167e6792841ecb05ef8be9d16f3 
 βœ” Cataloged contents                                                       44981cff51843fa437d39075b7714a76046294ce321887b3f16382006c8453db 
   β”œβ”€β”€ βœ” Packages                        [29 packages]  
   β”œβ”€β”€ βœ” File metadata                   [8 locations]  
   β”œβ”€β”€ βœ” File digests                    [8 files]  
   └── βœ” Executables                     [452 executables]  
$ cat python-multipart.json | grype
NAME              INSTALLED  FIXED-IN                                       TYPE    VULNERABILITY        SEVERITY  EPSS%  RISK  
openssl           3.0.14     1.0.2zk, 1.1.1za, 3.0.15, 3.1.7, 3.2.3, ...    nix     CVE-2024-5535        Critical  87.80    3.5  
python            3.12.5     3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.6, ...  binary  CVE-2024-6232        High      76.66    0.8  
openssl           3.0.14     3.0.15, 3.1.7, 3.2.3, 3.3.2                    nix     CVE-2024-6119        High      70.42    0.5  
python            3.12.5     3.9.22, 3.10.17, 3.11.12, 3.12.9, ...          binary  CVE-2025-0938        Medium    69.20    0.4  
python-multipart  0.0.9      0.0.18                                         python  GHSA-59g5-xgcq-4qw3  High      62.95    0.3  
openssl           3.0.14     1.0.2zl, 1.1.1zb, 3.0.16, 3.1.8, 3.2.4, ...    nix     CVE-2024-9143        Medium    69.92    0.3  
python            3.12.5     3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.6, ...  binary  CVE-2024-7592        High      54.84    0.2  
python            3.12.5     3.12.9, 3.13.2, 3.14.0a3                       binary  CVE-2024-12254       High      40.71    0.1  
python            3.12.5     3.9.23, 3.10.18, 3.11.13, 3.12.11, ...         binary  CVE-2025-4517        Critical  34.76    0.1  
python            3.12.5     3.9.23, 3.10.18, 3.11.13, 3.12.11, ...         binary  CVE-2025-4138        High      27.68  < 0.1  
python            3.12.5     3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.6, ...  binary  CVE-2024-8088        High      26.01  < 0.1  
python            3.12.5     3.9.23, 3.10.18, 3.11.13, 3.12.11, ...         binary  CVE-2025-4330        High      25.64  < 0.1  
python            3.12.5     3.8.20, 3.9.20, 3.10.15, 3.11.10, 3.12.6, ...  binary  CVE-2023-27043       Medium    30.96  < 0.1  
sqlite            3.46.0     3.49.1                                         nix     CVE-2025-3277        Medium    26.46  < 0.1  
python            3.12.5     3.9.23, 3.10.18, 3.11.13, 3.12.11, ...         binary  CVE-2025-4435        High      20.34  < 0.1  
sqlite            3.46.0     3.50.2                                         nix     CVE-2025-6965        Critical  14.52  < 0.1  
python            3.12.5     3.14.0b3                                       binary  CVE-2025-6069        Medium    23.89  < 0.1  
python            3.12.5     3.9.23, 3.10.18, 3.11.13, 3.12.11, ...         binary  CVE-2024-12718       Medium    21.54  < 0.1  
python            3.12.5     3.9.21, 3.10.16, 3.11.11, 3.12.8, ...          binary  CVE-2024-9287        High      13.85  < 0.1  
openssl           3.0.14     1.0.2zl, 1.1.1zb, 3.0.16, 3.1.8, 3.2.4, ...    nix     CVE-2024-13176       Medium    22.43  < 0.1  
python            3.12.5     3.9.21, 3.10.16, 3.11.11, 3.12.8, ...          binary  CVE-2024-50602       Medium    16.83  < 0.1  
sqlite            3.46.0     3.49.1                                         nix     CVE-2025-29087       High      13.22  < 0.1  
python            3.12.5     3.9.23, 3.10.18, 3.11.13, 3.12.11, ...         binary  CVE-2025-4516        Medium     3.85  < 0.1

Running grype directly on the container also finds the vulnerabilities which solves my original problem of getting vulnerabilities based on an image built with Nix :smile:

1 Like

I use this at work, perhaps it might help you and give you some ideas : Pol Dellaiera / ec-lib Β· GitLab

2 Likes