From 8fcef352b37622c6ac1b9106a3869a8d2d1bc0ea Mon Sep 17 00:00:00 2001
From: Teppei Fukuda <knqyf263@gmail.com>
Date: Tue, 12 Mar 2024 10:56:10 +0400
Subject: [PATCH] refactor(sbom): add intermediate representation for BOM
 (#6240)

Signed-off-by: knqyf263 <knqyf263@gmail.com>
Co-authored-by: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com>
---
 go.mod                                        |    2 +-
 .../testdata/conda-cyclonedx.json.golden      |   18 +-
 .../testdata/fixtures/sbom/minikube-kbom.json |   12 +-
 ...fluentd-multiple-lockfiles.cdx.json.golden |  370 +-
 .../fluentd-multiple-lockfiles.json.golden    |    3 +
 .../testdata/minikube-kbom.json.golden        |    1 +
 .../testdata/pom-cyclonedx.json.golden        |   12 +-
 magefiles/magefile.go                         |   14 -
 pkg/dependency/id.go                          |   32 +
 pkg/dependency/id_test.go                     |   73 +
 pkg/dependency/parser/c/conan/parse.go        |    7 +-
 pkg/dependency/parser/c/conan/parse_test.go   |    2 +-
 pkg/dependency/parser/conda/meta/parse.go     |    2 +-
 .../parser/conda/meta/parse_test.go           |    2 +-
 pkg/dependency/parser/dart/pub/parse.go       |   12 +-
 pkg/dependency/parser/dart/pub/parse_test.go  |    2 +-
 .../parser/dotnet/core_deps/parse.go          |    2 +-
 .../parser/dotnet/core_deps/parse_test.go     |    2 +-
 .../parser/frameworks/wordpress/parse.go      |    2 +-
 .../parser/frameworks/wordpress/parse_test.go |    2 +-
 pkg/dependency/parser/golang/binary/parse.go  |    2 +-
 .../parser/golang/binary/parse_test.go        |    2 +-
 pkg/dependency/parser/golang/mod/parse.go     |   16 +-
 .../parser/golang/mod/parse_test.go           |   36 +-
 .../parser/golang/mod/parse_testcase.go       |    2 +-
 pkg/dependency/parser/golang/sum/parse.go     |    7 +-
 .../parser/golang/sum/parse_test.go           |    2 +-
 .../parser/golang/sum/parse_testcase.go       |    2 +-
 .../parser/gradle/lockfile/parse.go           |    7 +-
 .../parser/gradle/lockfile/parse_test.go      |    2 +-
 pkg/dependency/parser/hex/mix/parse.go        |   18 +-
 pkg/dependency/parser/hex/mix/parse_test.go   |    2 +-
 pkg/dependency/parser/java/jar/parse.go       |    2 +-
 pkg/dependency/parser/java/jar/parse_test.go  |    2 +-
 pkg/dependency/parser/java/jar/types.go       |    2 +-
 pkg/dependency/parser/java/pom/artifact.go    |    2 +-
 pkg/dependency/parser/java/pom/parse.go       |    8 +-
 pkg/dependency/parser/java/pom/parse_test.go  |    2 +-
 pkg/dependency/parser/java/pom/pom.go         |    2 +-
 pkg/dependency/parser/java/pom/utils.go       |    5 -
 pkg/dependency/parser/julia/manifest/parse.go |    2 +-
 .../parser/julia/manifest/parse_test.go       |    2 +-
 .../parser/julia/manifest/parse_testcase.go   |    2 +-
 pkg/dependency/parser/nodejs/npm/parse.go     |   42 +-
 .../parser/nodejs/npm/parse_test.go           |    2 +-
 .../parser/nodejs/npm/parse_testcase.go       | 1546 ++++++++-
 .../parser/nodejs/packagejson/parse.go        |    7 +-
 .../parser/nodejs/packagejson/parse_test.go   |    2 +-
 pkg/dependency/parser/nodejs/pnpm/parse.go    |   16 +-
 .../parser/nodejs/pnpm/parse_test.go          |    2 +-
 .../parser/nodejs/pnpm/parse_testcase.go      |    2 +-
 pkg/dependency/parser/nodejs/yarn/parse.go    |   15 +-
 .../parser/nodejs/yarn/parse_test.go          |    2 +-
 .../parser/nodejs/yarn/parse_testcase.go      |    2 +-
 pkg/dependency/parser/nuget/config/parse.go   |    2 +-
 .../parser/nuget/config/parse_test.go         |    2 +-
 pkg/dependency/parser/nuget/lock/parse.go     |   12 +-
 .../parser/nuget/lock/parse_test.go           |    2 +-
 .../parser/nuget/lock/parse_testcase.go       |    2 +-
 .../parser/nuget/packagesprops/parse.go       |    6 +-
 .../parser/nuget/packagesprops/parse_test.go  |    2 +-
 pkg/dependency/parser/php/composer/parse.go   |    7 +-
 .../parser/php/composer/parse_test.go         |    2 +-
 .../parser/python/packaging/parse.go          |    2 +-
 .../parser/python/packaging/parse_test.go     |    2 +-
 pkg/dependency/parser/python/pip/parse.go     |    2 +-
 .../parser/python/pip/parse_test.go           |    2 +-
 .../parser/python/pip/parse_testcase.go       |    2 +-
 pkg/dependency/parser/python/pipenv/parse.go  |   13 +-
 .../parser/python/pipenv/parse_test.go        |    2 +-
 .../parser/python/pipenv/parse_testcase.go    |    2 +-
 pkg/dependency/parser/python/poetry/parse.go  |   13 +-
 .../parser/python/poetry/parse_test.go        |    2 +-
 .../parser/python/poetry/parse_testcase.go    |    2 +-
 pkg/dependency/parser/ruby/bundler/parse.go   |   28 +-
 .../parser/ruby/bundler/parse_test.go         |    2 +-
 pkg/dependency/parser/ruby/gemspec/parse.go   |    5 +-
 .../parser/ruby/gemspec/parse_test.go         |    2 +-
 pkg/dependency/parser/rust/binary/parse.go    |   13 +-
 .../parser/rust/binary/parse_test.go          |    2 +-
 .../parser/rust/cargo/naive_pkg_parser.go     |    4 +-
 pkg/dependency/parser/rust/cargo/parse.go     |   22 +-
 .../parser/rust/cargo/parse_test.go           |    2 +-
 .../parser/swift/cocoapods/parse.go           |   12 +-
 .../parser/swift/cocoapods/parse_test.go      |    2 +-
 pkg/dependency/parser/swift/swift/parse.go    |    7 +-
 .../parser/swift/swift/parse_test.go          |    2 +-
 pkg/dependency/parser/utils/utils.go          |    6 +-
 pkg/dependency/parser/utils/utils_test.go     |    2 +-
 pkg/dependency/{parser => }/types/types.go    |    0
 pkg/fanal/analyzer/language/analyze.go        |    2 +-
 pkg/fanal/analyzer/language/analyze_test.go   |    4 +-
 .../analyzer/language/dart/pub/pubspec.go     |    6 +-
 .../analyzer/language/dotnet/nuget/nuget.go   |    2 +-
 pkg/fanal/analyzer/language/golang/mod/mod.go |    2 +-
 pkg/fanal/analyzer/language/nodejs/npm/npm.go |    2 +-
 pkg/fanal/analyzer/language/nodejs/pkg/pkg.go |    2 +-
 .../analyzer/language/nodejs/yarn/yarn.go     |    2 +-
 .../language/php/composer/composer.go         |    2 +-
 .../language/python/packaging/packaging.go    |    2 +-
 .../analyzer/language/python/poetry/poetry.go |    2 +-
 .../analyzer/language/rust/cargo/cargo.go     |    2 +-
 pkg/fanal/analyzer/sbom/sbom_test.go          |    2 +
 pkg/fanal/artifact/image/remote_sbom_test.go  |   19 +-
 pkg/fanal/artifact/sbom/sbom.go               |    5 +-
 pkg/fanal/artifact/sbom/sbom_test.go          |   37 +-
 .../handler/unpackaged/unpackaged_test.go     |    1 +
 pkg/fanal/types/artifact.go                   |    3 +-
 pkg/fanal/types/sbom.go                       |   43 -
 pkg/k8s/report/cyclonedx.go                   |   15 +-
 pkg/k8s/report/report.go                      |   13 +-
 pkg/k8s/scanner/scanner.go                    |  278 +-
 pkg/k8s/scanner/scanner_test.go               |  408 ++-
 pkg/k8s/writer.go                             |    2 +-
 pkg/module/module.go                          |    5 +-
 pkg/module/serialize/types.go                 |   10 -
 pkg/module/serialize/types_easyjson.go        | 3024 -----------------
 pkg/module/wasm/sdk.go                        |   23 +-
 pkg/purl/purl.go                              |  123 +-
 pkg/purl/purl_test.go                         |  432 +--
 pkg/rekortest/server.go                       |    2 +-
 pkg/report/cyclonedx/cyclonedx.go             |    4 +-
 pkg/report/github/github.go                   |    2 +-
 pkg/sbom/core/bom.go                          |  290 ++
 pkg/sbom/cyclonedx/core/cyclonedx.go          |  513 ---
 pkg/sbom/cyclonedx/core/cyclonedx_test.go     |  380 ---
 pkg/sbom/cyclonedx/marshal.go                 |  726 ++--
 pkg/sbom/cyclonedx/marshal_test.go            |  256 +-
 .../testdata/{sad => happy}/invalid-purl.json |    0
 pkg/sbom/cyclonedx/testdata/happy/kbom.json   |   12 +-
 .../testdata/sad/invalid-serial.json          |    6 +
 pkg/sbom/cyclonedx/unmarshal.go               |  529 +--
 pkg/sbom/cyclonedx/unmarshal_test.go          |  179 +-
 pkg/sbom/io/decode.go                         |  336 ++
 pkg/sbom/io/encode.go                         |  343 ++
 pkg/sbom/io/encode_test.go                    |  339 ++
 pkg/sbom/sbom.go                              |   29 +-
 pkg/sbom/spdx/marshal.go                      |    2 +-
 pkg/sbom/spdx/unmarshal.go                    |    8 +-
 pkg/sbom/spdx/unmarshal_test.go               |   18 +-
 pkg/scanner/scan.go                           |    4 +-
 pkg/types/report.go                           |    5 +-
 pkg/types/sbom.go                             |   19 +-
 pkg/uuid/uuid.go                              |   12 +-
 pkg/uuid/uuid_tinygo.go                       |   13 +
 pkg/vex/cyclonedx.go                          |    8 +-
 pkg/vex/vex.go                                |    5 +-
 pkg/vex/vex_test.go                           |    7 +-
 148 files changed, 4910 insertions(+), 6141 deletions(-)
 create mode 100644 pkg/dependency/id.go
 create mode 100644 pkg/dependency/id_test.go
 rename pkg/dependency/{parser => }/types/types.go (100%)
 delete mode 100644 pkg/fanal/types/sbom.go
 delete mode 100644 pkg/module/serialize/types_easyjson.go
 create mode 100644 pkg/sbom/core/bom.go
 delete mode 100644 pkg/sbom/cyclonedx/core/cyclonedx.go
 delete mode 100644 pkg/sbom/cyclonedx/core/cyclonedx_test.go
 rename pkg/sbom/cyclonedx/testdata/{sad => happy}/invalid-purl.json (100%)
 create mode 100644 pkg/sbom/cyclonedx/testdata/sad/invalid-serial.json
 create mode 100644 pkg/sbom/io/decode.go
 create mode 100644 pkg/sbom/io/encode.go
 create mode 100644 pkg/sbom/io/encode_test.go
 create mode 100644 pkg/uuid/uuid_tinygo.go

diff --git a/go.mod b/go.mod
index aeea13fa7d2d..ce787a0c2565 100644
--- a/go.mod
+++ b/go.mod
@@ -66,7 +66,7 @@ require (
 	github.com/kylelemons/godebug v1.1.0
 	github.com/liamg/jfather v0.0.7
 	github.com/magefile/mage v1.15.0
-	github.com/mailru/easyjson v0.7.7
+	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/masahiro331/go-disk v0.0.0-20220919035250-c8da316f91ac
 	github.com/masahiro331/go-ebs-file v0.0.0-20240112135404-d5fbb1d46323
 	github.com/masahiro331/go-ext4-filesystem v0.0.0-20231208112839-4339555a0cd4
diff --git a/integration/testdata/conda-cyclonedx.json.golden b/integration/testdata/conda-cyclonedx.json.golden
index 30e0321b52a5..9640112cce12 100644
--- a/integration/testdata/conda-cyclonedx.json.golden
+++ b/integration/testdata/conda-cyclonedx.json.golden
@@ -2,7 +2,7 @@
   "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
   "bomFormat": "CycloneDX",
   "specVersion": "1.5",
-  "serialNumber": "urn:uuid:3ff14136-e09f-4df9-80ea-000000000001",
+  "serialNumber": "urn:uuid:3ff14136-e09f-4df9-80ea-000000000004",
   "version": 1,
   "metadata": {
     "timestamp": "2021-08-25T12:20:30+00:00",
@@ -17,7 +17,7 @@
       ]
     },
     "component": {
-      "bom-ref": "3ff14136-e09f-4df9-80ea-000000000002",
+      "bom-ref": "3ff14136-e09f-4df9-80ea-000000000001",
       "type": "application",
       "name": "testdata/fixtures/repo/conda",
       "properties": [
@@ -30,7 +30,7 @@
   },
   "components": [
     {
-      "bom-ref": "pkg:conda/openssl@1.1.1q?file_path=miniconda3%2Fenvs%2Ftestenv%2Fconda-meta%2Fopenssl-1.1.1q-h7f8727e_0.json",
+      "bom-ref": "pkg:conda/openssl@1.1.1q",
       "type": "library",
       "name": "openssl",
       "version": "1.1.1q",
@@ -54,7 +54,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:conda/pip@22.2.2?file_path=miniconda3%2Fenvs%2Ftestenv%2Fconda-meta%2Fpip-22.2.2-py38h06a4308_0.json",
+      "bom-ref": "pkg:conda/pip@22.2.2",
       "type": "library",
       "name": "pip",
       "version": "22.2.2",
@@ -80,18 +80,18 @@
   ],
   "dependencies": [
     {
-      "ref": "3ff14136-e09f-4df9-80ea-000000000002",
+      "ref": "3ff14136-e09f-4df9-80ea-000000000001",
       "dependsOn": [
-        "pkg:conda/openssl@1.1.1q?file_path=miniconda3%2Fenvs%2Ftestenv%2Fconda-meta%2Fopenssl-1.1.1q-h7f8727e_0.json",
-        "pkg:conda/pip@22.2.2?file_path=miniconda3%2Fenvs%2Ftestenv%2Fconda-meta%2Fpip-22.2.2-py38h06a4308_0.json"
+        "pkg:conda/openssl@1.1.1q",
+        "pkg:conda/pip@22.2.2"
       ]
     },
     {
-      "ref": "pkg:conda/openssl@1.1.1q?file_path=miniconda3%2Fenvs%2Ftestenv%2Fconda-meta%2Fopenssl-1.1.1q-h7f8727e_0.json",
+      "ref": "pkg:conda/openssl@1.1.1q",
       "dependsOn": []
     },
     {
-      "ref": "pkg:conda/pip@22.2.2?file_path=miniconda3%2Fenvs%2Ftestenv%2Fconda-meta%2Fpip-22.2.2-py38h06a4308_0.json",
+      "ref": "pkg:conda/pip@22.2.2",
       "dependsOn": []
     }
   ],
diff --git a/integration/testdata/fixtures/sbom/minikube-kbom.json b/integration/testdata/fixtures/sbom/minikube-kbom.json
index e63c5733a816..c1ee53d6c9c8 100644
--- a/integration/testdata/fixtures/sbom/minikube-kbom.json
+++ b/integration/testdata/fixtures/sbom/minikube-kbom.json
@@ -51,17 +51,7 @@
     {
       "bom-ref": "a62abb1f-cb38-4fde-90f3-2bda3b87ddb2",
       "type": "application",
-      "name": "node-core-components",
-      "properties": [
-        {
-          "name": "aquasecurity:trivy:Class",
-          "value": "lang-pkgs"
-        },
-        {
-          "name": "aquasecurity:trivy:Type",
-          "value": "golang"
-        }
-      ]
+      "name": "node-core-components"
     },
     {
       "bom-ref": "a6350ac3-52f6-4c5f-a3e3-184b9a634bef",
diff --git a/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden b/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden
index aab59a6cf47f..40fdceb532c5 100644
--- a/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden
+++ b/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden
@@ -2,7 +2,7 @@
   "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
   "bomFormat": "CycloneDX",
   "specVersion": "1.5",
-  "serialNumber": "urn:uuid:3ff14136-e09f-4df9-80ea-000000000001",
+  "serialNumber": "urn:uuid:3ff14136-e09f-4df9-80ea-000000000163",
   "version": 1,
   "metadata": {
     "timestamp": "2021-08-25T12:20:30+00:00",
@@ -17,13 +17,33 @@
       ]
     },
     "component": {
-      "bom-ref": "3ff14136-e09f-4df9-80ea-000000000002",
+      "bom-ref": "3ff14136-e09f-4df9-80ea-000000000001",
       "type": "container",
       "name": "testdata/fixtures/images/fluentd-multiple-lockfiles.tar.gz",
       "properties": [
         {
           "name": "aquasecurity:trivy:DiffID",
-          "value": "sha256:831c5620387fb9efec59fc82a42b948546c6be601e3ab34a87108ecf852aa15f,sha256:02874b2b269dea8dde0f7edb4c9906904dfe38a09de1a214f20c650cfb15c60e,sha256:3752e1f6fd759c795c13aff2c93c081529366e27635ba6621e849b0f9cfc77f0,sha256:75e43d55939745950bc3f8fad56c5834617c4339f0f54755e69a0dd5372624e9,sha256:788c00e2cfc8f2a018ae4344ccf0b2c226ebd756d7effd1ce50eea1a4252cd89,sha256:25165eb51d15842f870f97873e0a58409d5e860e6108e3dd829bd10e484c0065"
+          "value": "sha256:02874b2b269dea8dde0f7edb4c9906904dfe38a09de1a214f20c650cfb15c60e"
+        },
+        {
+          "name": "aquasecurity:trivy:DiffID",
+          "value": "sha256:25165eb51d15842f870f97873e0a58409d5e860e6108e3dd829bd10e484c0065"
+        },
+        {
+          "name": "aquasecurity:trivy:DiffID",
+          "value": "sha256:3752e1f6fd759c795c13aff2c93c081529366e27635ba6621e849b0f9cfc77f0"
+        },
+        {
+          "name": "aquasecurity:trivy:DiffID",
+          "value": "sha256:75e43d55939745950bc3f8fad56c5834617c4339f0f54755e69a0dd5372624e9"
+        },
+        {
+          "name": "aquasecurity:trivy:DiffID",
+          "value": "sha256:788c00e2cfc8f2a018ae4344ccf0b2c226ebd756d7effd1ce50eea1a4252cd89"
+        },
+        {
+          "name": "aquasecurity:trivy:DiffID",
+          "value": "sha256:831c5620387fb9efec59fc82a42b948546c6be601e3ab34a87108ecf852aa15f"
         },
         {
           "name": "aquasecurity:trivy:ImageID",
@@ -38,7 +58,7 @@
   },
   "components": [
     {
-      "bom-ref": "3ff14136-e09f-4df9-80ea-000000000003",
+      "bom-ref": "3ff14136-e09f-4df9-80ea-000000000002",
       "type": "operating-system",
       "name": "debian",
       "version": "10.2",
@@ -5715,7 +5735,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/activesupport@6.0.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Factivesupport-6.0.2.1.gemspec",
+      "bom-ref": "pkg:gem/activesupport@6.0.2.1",
       "type": "library",
       "name": "activesupport",
       "version": "6.0.2.1",
@@ -5747,7 +5767,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/addressable@2.7.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Faddressable-2.7.0.gemspec",
+      "bom-ref": "pkg:gem/addressable@2.7.0",
       "type": "library",
       "name": "addressable",
       "version": "2.7.0",
@@ -5779,7 +5799,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/concurrent-ruby@1.1.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fconcurrent-ruby-1.1.6.gemspec",
+      "bom-ref": "pkg:gem/concurrent-ruby@1.1.6",
       "type": "library",
       "name": "concurrent-ruby",
       "version": "1.1.6",
@@ -5811,7 +5831,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/cool.io@1.6.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fcool.io-1.6.0.gemspec",
+      "bom-ref": "pkg:gem/cool.io@1.6.0",
       "type": "library",
       "name": "cool.io",
       "version": "1.6.0",
@@ -5836,7 +5856,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/dig_rb@1.0.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fdig_rb-1.0.1.gemspec",
+      "bom-ref": "pkg:gem/dig_rb@1.0.1",
       "type": "library",
       "name": "dig_rb",
       "version": "1.0.1",
@@ -5868,7 +5888,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/domain_name@0.5.20190701?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fdomain_name-0.5.20190701.gemspec",
+      "bom-ref": "pkg:gem/domain_name@0.5.20190701",
       "type": "library",
       "name": "domain_name",
       "version": "0.5.20190701",
@@ -5910,7 +5930,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/elasticsearch-api@7.5.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Felasticsearch-api-7.5.0.gemspec",
+      "bom-ref": "pkg:gem/elasticsearch-api@7.5.0",
       "type": "library",
       "name": "elasticsearch-api",
       "version": "7.5.0",
@@ -5942,7 +5962,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/elasticsearch-transport@7.5.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Felasticsearch-transport-7.5.0.gemspec",
+      "bom-ref": "pkg:gem/elasticsearch-transport@7.5.0",
       "type": "library",
       "name": "elasticsearch-transport",
       "version": "7.5.0",
@@ -5974,7 +5994,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/elasticsearch@7.5.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Felasticsearch-7.5.0.gemspec",
+      "bom-ref": "pkg:gem/elasticsearch@7.5.0",
       "type": "library",
       "name": "elasticsearch",
       "version": "7.5.0",
@@ -6006,7 +6026,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/excon@0.72.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fexcon-0.72.0.gemspec",
+      "bom-ref": "pkg:gem/excon@0.72.0",
       "type": "library",
       "name": "excon",
       "version": "0.72.0",
@@ -6038,7 +6058,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/faraday@0.17.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffaraday-0.17.3.gemspec",
+      "bom-ref": "pkg:gem/faraday@0.17.3",
       "type": "library",
       "name": "faraday",
       "version": "0.17.3",
@@ -6070,7 +6090,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/ffi-compiler@1.0.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fffi-compiler-1.0.1.gemspec",
+      "bom-ref": "pkg:gem/ffi-compiler@1.0.1",
       "type": "library",
       "name": "ffi-compiler",
       "version": "1.0.1",
@@ -6102,7 +6122,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/ffi@1.12.2?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fffi-1.12.2.gemspec",
+      "bom-ref": "pkg:gem/ffi@1.12.2",
       "type": "library",
       "name": "ffi",
       "version": "1.12.2",
@@ -6134,7 +6154,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/fluent-plugin-concat@2.4.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-concat-2.4.0.gemspec",
+      "bom-ref": "pkg:gem/fluent-plugin-concat@2.4.0",
       "type": "library",
       "name": "fluent-plugin-concat",
       "version": "2.4.0",
@@ -6166,7 +6186,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/fluent-plugin-detect-exceptions@0.0.13?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-detect-exceptions-0.0.13.gemspec",
+      "bom-ref": "pkg:gem/fluent-plugin-detect-exceptions@0.0.13",
       "type": "library",
       "name": "fluent-plugin-detect-exceptions",
       "version": "0.0.13",
@@ -6198,7 +6218,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/fluent-plugin-elasticsearch@3.8.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-elasticsearch-3.8.0.gemspec",
+      "bom-ref": "pkg:gem/fluent-plugin-elasticsearch@3.8.0",
       "type": "library",
       "name": "fluent-plugin-elasticsearch",
       "version": "3.8.0",
@@ -6230,7 +6250,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/fluent-plugin-kubernetes_metadata_filter@2.4.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-kubernetes_metadata_filter-2.4.1.gemspec",
+      "bom-ref": "pkg:gem/fluent-plugin-kubernetes_metadata_filter@2.4.1",
       "type": "library",
       "name": "fluent-plugin-kubernetes_metadata_filter",
       "version": "2.4.1",
@@ -6262,7 +6282,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/fluent-plugin-multi-format-parser@1.0.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-multi-format-parser-1.0.0.gemspec",
+      "bom-ref": "pkg:gem/fluent-plugin-multi-format-parser@1.0.0",
       "type": "library",
       "name": "fluent-plugin-multi-format-parser",
       "version": "1.0.0",
@@ -6294,7 +6314,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/fluent-plugin-prometheus@1.7.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-prometheus-1.7.0.gemspec",
+      "bom-ref": "pkg:gem/fluent-plugin-prometheus@1.7.0",
       "type": "library",
       "name": "fluent-plugin-prometheus",
       "version": "1.7.0",
@@ -6326,7 +6346,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/fluent-plugin-systemd@1.0.2?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-systemd-1.0.2.gemspec",
+      "bom-ref": "pkg:gem/fluent-plugin-systemd@1.0.2",
       "type": "library",
       "name": "fluent-plugin-systemd",
       "version": "1.0.2",
@@ -6358,7 +6378,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/fluentd@1.8.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluentd-1.8.0.gemspec",
+      "bom-ref": "pkg:gem/fluentd@1.8.0",
       "type": "library",
       "name": "fluentd",
       "version": "1.8.0",
@@ -6390,7 +6410,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/http-accept@1.7.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-accept-1.7.0.gemspec",
+      "bom-ref": "pkg:gem/http-accept@1.7.0",
       "type": "library",
       "name": "http-accept",
       "version": "1.7.0",
@@ -6415,7 +6435,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/http-cookie@1.0.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-cookie-1.0.3.gemspec",
+      "bom-ref": "pkg:gem/http-cookie@1.0.3",
       "type": "library",
       "name": "http-cookie",
       "version": "1.0.3",
@@ -6447,7 +6467,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/http-form_data@2.2.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-form_data-2.2.0.gemspec",
+      "bom-ref": "pkg:gem/http-form_data@2.2.0",
       "type": "library",
       "name": "http-form_data",
       "version": "2.2.0",
@@ -6479,7 +6499,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/http-parser@1.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-parser-1.2.1.gemspec",
+      "bom-ref": "pkg:gem/http-parser@1.2.1",
       "type": "library",
       "name": "http-parser",
       "version": "1.2.1",
@@ -6511,7 +6531,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/http@4.3.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-4.3.0.gemspec",
+      "bom-ref": "pkg:gem/http@4.3.0",
       "type": "library",
       "name": "http",
       "version": "4.3.0",
@@ -6543,7 +6563,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/http_parser.rb@0.6.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp_parser.rb-0.6.0.gemspec",
+      "bom-ref": "pkg:gem/http_parser.rb@0.6.0",
       "type": "library",
       "name": "http_parser.rb",
       "version": "0.6.0",
@@ -6575,7 +6595,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/i18n@1.8.2?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fi18n-1.8.2.gemspec",
+      "bom-ref": "pkg:gem/i18n@1.8.2",
       "type": "library",
       "name": "i18n",
       "version": "1.8.2",
@@ -6607,7 +6627,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/kubeclient@4.6.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fkubeclient-4.6.0.gemspec",
+      "bom-ref": "pkg:gem/kubeclient@4.6.0",
       "type": "library",
       "name": "kubeclient",
       "version": "4.6.0",
@@ -6639,7 +6659,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/lru_redux@1.1.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Flru_redux-1.1.0.gemspec",
+      "bom-ref": "pkg:gem/lru_redux@1.1.0",
       "type": "library",
       "name": "lru_redux",
       "version": "1.1.0",
@@ -6671,7 +6691,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/mime-types-data@3.2019.1009?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmime-types-data-3.2019.1009.gemspec",
+      "bom-ref": "pkg:gem/mime-types-data@3.2019.1009",
       "type": "library",
       "name": "mime-types-data",
       "version": "3.2019.1009",
@@ -6703,7 +6723,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/mime-types@3.3.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmime-types-3.3.1.gemspec",
+      "bom-ref": "pkg:gem/mime-types@3.3.1",
       "type": "library",
       "name": "mime-types",
       "version": "3.3.1",
@@ -6735,7 +6755,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/minitest@5.14.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fminitest-5.14.0.gemspec",
+      "bom-ref": "pkg:gem/minitest@5.14.0",
       "type": "library",
       "name": "minitest",
       "version": "5.14.0",
@@ -6767,7 +6787,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/msgpack@1.3.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmsgpack-1.3.3.gemspec",
+      "bom-ref": "pkg:gem/msgpack@1.3.3",
       "type": "library",
       "name": "msgpack",
       "version": "1.3.3",
@@ -6799,7 +6819,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/multi_json@1.14.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmulti_json-1.14.1.gemspec",
+      "bom-ref": "pkg:gem/multi_json@1.14.1",
       "type": "library",
       "name": "multi_json",
       "version": "1.14.1",
@@ -6831,7 +6851,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/multipart-post@2.1.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmultipart-post-2.1.1.gemspec",
+      "bom-ref": "pkg:gem/multipart-post@2.1.1",
       "type": "library",
       "name": "multipart-post",
       "version": "2.1.1",
@@ -6863,7 +6883,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/netrc@0.11.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fnetrc-0.11.0.gemspec",
+      "bom-ref": "pkg:gem/netrc@0.11.0",
       "type": "library",
       "name": "netrc",
       "version": "0.11.0",
@@ -6895,7 +6915,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/oj@3.10.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Foj-3.10.0.gemspec",
+      "bom-ref": "pkg:gem/oj@3.10.0",
       "type": "library",
       "name": "oj",
       "version": "3.10.0",
@@ -6927,7 +6947,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/prometheus-client@0.9.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fprometheus-client-0.9.0.gemspec",
+      "bom-ref": "pkg:gem/prometheus-client@0.9.0",
       "type": "library",
       "name": "prometheus-client",
       "version": "0.9.0",
@@ -6959,7 +6979,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/public_suffix@4.0.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fpublic_suffix-4.0.3.gemspec",
+      "bom-ref": "pkg:gem/public_suffix@4.0.3",
       "type": "library",
       "name": "public_suffix",
       "version": "4.0.3",
@@ -6991,7 +7011,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/quantile@0.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fquantile-0.2.1.gemspec",
+      "bom-ref": "pkg:gem/quantile@0.2.1",
       "type": "library",
       "name": "quantile",
       "version": "0.2.1",
@@ -7023,7 +7043,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/rake@13.0.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Frake-13.0.1.gemspec",
+      "bom-ref": "pkg:gem/rake@13.0.1",
       "type": "library",
       "name": "rake",
       "version": "13.0.1",
@@ -7055,7 +7075,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/recursive-open-struct@1.1.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Frecursive-open-struct-1.1.0.gemspec",
+      "bom-ref": "pkg:gem/recursive-open-struct@1.1.0",
       "type": "library",
       "name": "recursive-open-struct",
       "version": "1.1.0",
@@ -7087,7 +7107,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/rest-client@2.1.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Frest-client-2.1.0.gemspec",
+      "bom-ref": "pkg:gem/rest-client@2.1.0",
       "type": "library",
       "name": "rest-client",
       "version": "2.1.0",
@@ -7119,7 +7139,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/serverengine@2.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fserverengine-2.2.1.gemspec",
+      "bom-ref": "pkg:gem/serverengine@2.2.1",
       "type": "library",
       "name": "serverengine",
       "version": "2.2.1",
@@ -7151,7 +7171,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/sigdump@0.2.4?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fsigdump-0.2.4.gemspec",
+      "bom-ref": "pkg:gem/sigdump@0.2.4",
       "type": "library",
       "name": "sigdump",
       "version": "0.2.4",
@@ -7183,7 +7203,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/strptime@0.2.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fstrptime-0.2.3.gemspec",
+      "bom-ref": "pkg:gem/strptime@0.2.3",
       "type": "library",
       "name": "strptime",
       "version": "0.2.3",
@@ -7215,7 +7235,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/systemd-journal@1.3.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fsystemd-journal-1.3.3.gemspec",
+      "bom-ref": "pkg:gem/systemd-journal@1.3.3",
       "type": "library",
       "name": "systemd-journal",
       "version": "1.3.3",
@@ -7247,7 +7267,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/thread_safe@0.3.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fthread_safe-0.3.6.gemspec",
+      "bom-ref": "pkg:gem/thread_safe@0.3.6",
       "type": "library",
       "name": "thread_safe",
       "version": "0.3.6",
@@ -7279,7 +7299,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/tzinfo-data@1.2019.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ftzinfo-data-1.2019.3.gemspec",
+      "bom-ref": "pkg:gem/tzinfo-data@1.2019.3",
       "type": "library",
       "name": "tzinfo-data",
       "version": "1.2019.3",
@@ -7311,7 +7331,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/tzinfo@1.2.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ftzinfo-1.2.6.gemspec",
+      "bom-ref": "pkg:gem/tzinfo@1.2.6",
       "type": "library",
       "name": "tzinfo",
       "version": "1.2.6",
@@ -7343,7 +7363,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/unf@0.1.4?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Funf-0.1.4.gemspec",
+      "bom-ref": "pkg:gem/unf@0.1.4",
       "type": "library",
       "name": "unf",
       "version": "0.1.4",
@@ -7375,7 +7395,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/unf_ext@0.0.7.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Funf_ext-0.0.7.6.gemspec",
+      "bom-ref": "pkg:gem/unf_ext@0.0.7.6",
       "type": "library",
       "name": "unf_ext",
       "version": "0.0.7.6",
@@ -7407,7 +7427,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/yajl-ruby@1.4.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fyajl-ruby-1.4.1.gemspec",
+      "bom-ref": "pkg:gem/yajl-ruby@1.4.1",
       "type": "library",
       "name": "yajl-ruby",
       "version": "1.4.1",
@@ -7439,7 +7459,7 @@
       ]
     },
     {
-      "bom-ref": "pkg:gem/zeitwerk@2.3.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fzeitwerk-2.3.0.gemspec",
+      "bom-ref": "pkg:gem/zeitwerk@2.3.0",
       "type": "library",
       "name": "zeitwerk",
       "version": "2.3.0",
@@ -7473,68 +7493,68 @@
   ],
   "dependencies": [
     {
-      "ref": "3ff14136-e09f-4df9-80ea-000000000002",
+      "ref": "3ff14136-e09f-4df9-80ea-000000000001",
       "dependsOn": [
-        "3ff14136-e09f-4df9-80ea-000000000003",
-        "pkg:gem/activesupport@6.0.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Factivesupport-6.0.2.1.gemspec",
-        "pkg:gem/addressable@2.7.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Faddressable-2.7.0.gemspec",
-        "pkg:gem/concurrent-ruby@1.1.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fconcurrent-ruby-1.1.6.gemspec",
-        "pkg:gem/cool.io@1.6.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fcool.io-1.6.0.gemspec",
-        "pkg:gem/dig_rb@1.0.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fdig_rb-1.0.1.gemspec",
-        "pkg:gem/domain_name@0.5.20190701?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fdomain_name-0.5.20190701.gemspec",
-        "pkg:gem/elasticsearch-api@7.5.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Felasticsearch-api-7.5.0.gemspec",
-        "pkg:gem/elasticsearch-transport@7.5.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Felasticsearch-transport-7.5.0.gemspec",
-        "pkg:gem/elasticsearch@7.5.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Felasticsearch-7.5.0.gemspec",
-        "pkg:gem/excon@0.72.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fexcon-0.72.0.gemspec",
-        "pkg:gem/faraday@0.17.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffaraday-0.17.3.gemspec",
-        "pkg:gem/ffi-compiler@1.0.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fffi-compiler-1.0.1.gemspec",
-        "pkg:gem/ffi@1.12.2?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fffi-1.12.2.gemspec",
-        "pkg:gem/fluent-plugin-concat@2.4.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-concat-2.4.0.gemspec",
-        "pkg:gem/fluent-plugin-detect-exceptions@0.0.13?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-detect-exceptions-0.0.13.gemspec",
-        "pkg:gem/fluent-plugin-elasticsearch@3.8.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-elasticsearch-3.8.0.gemspec",
-        "pkg:gem/fluent-plugin-kubernetes_metadata_filter@2.4.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-kubernetes_metadata_filter-2.4.1.gemspec",
-        "pkg:gem/fluent-plugin-multi-format-parser@1.0.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-multi-format-parser-1.0.0.gemspec",
-        "pkg:gem/fluent-plugin-prometheus@1.7.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-prometheus-1.7.0.gemspec",
-        "pkg:gem/fluent-plugin-systemd@1.0.2?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-systemd-1.0.2.gemspec",
-        "pkg:gem/fluentd@1.8.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluentd-1.8.0.gemspec",
-        "pkg:gem/http-accept@1.7.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-accept-1.7.0.gemspec",
-        "pkg:gem/http-cookie@1.0.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-cookie-1.0.3.gemspec",
-        "pkg:gem/http-form_data@2.2.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-form_data-2.2.0.gemspec",
-        "pkg:gem/http-parser@1.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-parser-1.2.1.gemspec",
-        "pkg:gem/http@4.3.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-4.3.0.gemspec",
-        "pkg:gem/http_parser.rb@0.6.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp_parser.rb-0.6.0.gemspec",
-        "pkg:gem/i18n@1.8.2?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fi18n-1.8.2.gemspec",
-        "pkg:gem/kubeclient@4.6.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fkubeclient-4.6.0.gemspec",
-        "pkg:gem/lru_redux@1.1.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Flru_redux-1.1.0.gemspec",
-        "pkg:gem/mime-types-data@3.2019.1009?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmime-types-data-3.2019.1009.gemspec",
-        "pkg:gem/mime-types@3.3.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmime-types-3.3.1.gemspec",
-        "pkg:gem/minitest@5.14.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fminitest-5.14.0.gemspec",
-        "pkg:gem/msgpack@1.3.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmsgpack-1.3.3.gemspec",
-        "pkg:gem/multi_json@1.14.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmulti_json-1.14.1.gemspec",
-        "pkg:gem/multipart-post@2.1.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmultipart-post-2.1.1.gemspec",
-        "pkg:gem/netrc@0.11.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fnetrc-0.11.0.gemspec",
-        "pkg:gem/oj@3.10.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Foj-3.10.0.gemspec",
-        "pkg:gem/prometheus-client@0.9.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fprometheus-client-0.9.0.gemspec",
-        "pkg:gem/public_suffix@4.0.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fpublic_suffix-4.0.3.gemspec",
-        "pkg:gem/quantile@0.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fquantile-0.2.1.gemspec",
-        "pkg:gem/rake@13.0.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Frake-13.0.1.gemspec",
-        "pkg:gem/recursive-open-struct@1.1.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Frecursive-open-struct-1.1.0.gemspec",
-        "pkg:gem/rest-client@2.1.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Frest-client-2.1.0.gemspec",
-        "pkg:gem/serverengine@2.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fserverengine-2.2.1.gemspec",
-        "pkg:gem/sigdump@0.2.4?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fsigdump-0.2.4.gemspec",
-        "pkg:gem/strptime@0.2.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fstrptime-0.2.3.gemspec",
-        "pkg:gem/systemd-journal@1.3.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fsystemd-journal-1.3.3.gemspec",
-        "pkg:gem/thread_safe@0.3.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fthread_safe-0.3.6.gemspec",
-        "pkg:gem/tzinfo-data@1.2019.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ftzinfo-data-1.2019.3.gemspec",
-        "pkg:gem/tzinfo@1.2.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ftzinfo-1.2.6.gemspec",
-        "pkg:gem/unf@0.1.4?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Funf-0.1.4.gemspec",
-        "pkg:gem/unf_ext@0.0.7.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Funf_ext-0.0.7.6.gemspec",
-        "pkg:gem/yajl-ruby@1.4.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fyajl-ruby-1.4.1.gemspec",
-        "pkg:gem/zeitwerk@2.3.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fzeitwerk-2.3.0.gemspec"
-      ]
-    },
-    {
-      "ref": "3ff14136-e09f-4df9-80ea-000000000003",
+        "3ff14136-e09f-4df9-80ea-000000000002",
+        "pkg:gem/activesupport@6.0.2.1",
+        "pkg:gem/addressable@2.7.0",
+        "pkg:gem/concurrent-ruby@1.1.6",
+        "pkg:gem/cool.io@1.6.0",
+        "pkg:gem/dig_rb@1.0.1",
+        "pkg:gem/domain_name@0.5.20190701",
+        "pkg:gem/elasticsearch-api@7.5.0",
+        "pkg:gem/elasticsearch-transport@7.5.0",
+        "pkg:gem/elasticsearch@7.5.0",
+        "pkg:gem/excon@0.72.0",
+        "pkg:gem/faraday@0.17.3",
+        "pkg:gem/ffi-compiler@1.0.1",
+        "pkg:gem/ffi@1.12.2",
+        "pkg:gem/fluent-plugin-concat@2.4.0",
+        "pkg:gem/fluent-plugin-detect-exceptions@0.0.13",
+        "pkg:gem/fluent-plugin-elasticsearch@3.8.0",
+        "pkg:gem/fluent-plugin-kubernetes_metadata_filter@2.4.1",
+        "pkg:gem/fluent-plugin-multi-format-parser@1.0.0",
+        "pkg:gem/fluent-plugin-prometheus@1.7.0",
+        "pkg:gem/fluent-plugin-systemd@1.0.2",
+        "pkg:gem/fluentd@1.8.0",
+        "pkg:gem/http-accept@1.7.0",
+        "pkg:gem/http-cookie@1.0.3",
+        "pkg:gem/http-form_data@2.2.0",
+        "pkg:gem/http-parser@1.2.1",
+        "pkg:gem/http@4.3.0",
+        "pkg:gem/http_parser.rb@0.6.0",
+        "pkg:gem/i18n@1.8.2",
+        "pkg:gem/kubeclient@4.6.0",
+        "pkg:gem/lru_redux@1.1.0",
+        "pkg:gem/mime-types-data@3.2019.1009",
+        "pkg:gem/mime-types@3.3.1",
+        "pkg:gem/minitest@5.14.0",
+        "pkg:gem/msgpack@1.3.3",
+        "pkg:gem/multi_json@1.14.1",
+        "pkg:gem/multipart-post@2.1.1",
+        "pkg:gem/netrc@0.11.0",
+        "pkg:gem/oj@3.10.0",
+        "pkg:gem/prometheus-client@0.9.0",
+        "pkg:gem/public_suffix@4.0.3",
+        "pkg:gem/quantile@0.2.1",
+        "pkg:gem/rake@13.0.1",
+        "pkg:gem/recursive-open-struct@1.1.0",
+        "pkg:gem/rest-client@2.1.0",
+        "pkg:gem/serverengine@2.2.1",
+        "pkg:gem/sigdump@0.2.4",
+        "pkg:gem/strptime@0.2.3",
+        "pkg:gem/systemd-journal@1.3.3",
+        "pkg:gem/thread_safe@0.3.6",
+        "pkg:gem/tzinfo-data@1.2019.3",
+        "pkg:gem/tzinfo@1.2.6",
+        "pkg:gem/unf@0.1.4",
+        "pkg:gem/unf_ext@0.0.7.6",
+        "pkg:gem/yajl-ruby@1.4.1",
+        "pkg:gem/zeitwerk@2.3.0"
+      ]
+    },
+    {
+      "ref": "3ff14136-e09f-4df9-80ea-000000000002",
       "dependsOn": [
         "pkg:deb/debian/adduser@3.118?arch=all&distro=debian-10.2",
         "pkg:deb/debian/apt@1.8.2?arch=amd64&distro=debian-10.2",
@@ -8324,223 +8344,223 @@
       ]
     },
     {
-      "ref": "pkg:gem/activesupport@6.0.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Factivesupport-6.0.2.1.gemspec",
+      "ref": "pkg:gem/activesupport@6.0.2.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/addressable@2.7.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Faddressable-2.7.0.gemspec",
+      "ref": "pkg:gem/addressable@2.7.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/concurrent-ruby@1.1.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fconcurrent-ruby-1.1.6.gemspec",
+      "ref": "pkg:gem/concurrent-ruby@1.1.6",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/cool.io@1.6.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fcool.io-1.6.0.gemspec",
+      "ref": "pkg:gem/cool.io@1.6.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/dig_rb@1.0.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fdig_rb-1.0.1.gemspec",
+      "ref": "pkg:gem/dig_rb@1.0.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/domain_name@0.5.20190701?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fdomain_name-0.5.20190701.gemspec",
+      "ref": "pkg:gem/domain_name@0.5.20190701",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/elasticsearch-api@7.5.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Felasticsearch-api-7.5.0.gemspec",
+      "ref": "pkg:gem/elasticsearch-api@7.5.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/elasticsearch-transport@7.5.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Felasticsearch-transport-7.5.0.gemspec",
+      "ref": "pkg:gem/elasticsearch-transport@7.5.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/elasticsearch@7.5.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Felasticsearch-7.5.0.gemspec",
+      "ref": "pkg:gem/elasticsearch@7.5.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/excon@0.72.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fexcon-0.72.0.gemspec",
+      "ref": "pkg:gem/excon@0.72.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/faraday@0.17.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffaraday-0.17.3.gemspec",
+      "ref": "pkg:gem/faraday@0.17.3",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/ffi-compiler@1.0.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fffi-compiler-1.0.1.gemspec",
+      "ref": "pkg:gem/ffi-compiler@1.0.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/ffi@1.12.2?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fffi-1.12.2.gemspec",
+      "ref": "pkg:gem/ffi@1.12.2",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/fluent-plugin-concat@2.4.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-concat-2.4.0.gemspec",
+      "ref": "pkg:gem/fluent-plugin-concat@2.4.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/fluent-plugin-detect-exceptions@0.0.13?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-detect-exceptions-0.0.13.gemspec",
+      "ref": "pkg:gem/fluent-plugin-detect-exceptions@0.0.13",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/fluent-plugin-elasticsearch@3.8.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-elasticsearch-3.8.0.gemspec",
+      "ref": "pkg:gem/fluent-plugin-elasticsearch@3.8.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/fluent-plugin-kubernetes_metadata_filter@2.4.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-kubernetes_metadata_filter-2.4.1.gemspec",
+      "ref": "pkg:gem/fluent-plugin-kubernetes_metadata_filter@2.4.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/fluent-plugin-multi-format-parser@1.0.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-multi-format-parser-1.0.0.gemspec",
+      "ref": "pkg:gem/fluent-plugin-multi-format-parser@1.0.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/fluent-plugin-prometheus@1.7.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-prometheus-1.7.0.gemspec",
+      "ref": "pkg:gem/fluent-plugin-prometheus@1.7.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/fluent-plugin-systemd@1.0.2?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluent-plugin-systemd-1.0.2.gemspec",
+      "ref": "pkg:gem/fluent-plugin-systemd@1.0.2",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/fluentd@1.8.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ffluentd-1.8.0.gemspec",
+      "ref": "pkg:gem/fluentd@1.8.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/http-accept@1.7.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-accept-1.7.0.gemspec",
+      "ref": "pkg:gem/http-accept@1.7.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/http-cookie@1.0.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-cookie-1.0.3.gemspec",
+      "ref": "pkg:gem/http-cookie@1.0.3",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/http-form_data@2.2.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-form_data-2.2.0.gemspec",
+      "ref": "pkg:gem/http-form_data@2.2.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/http-parser@1.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-parser-1.2.1.gemspec",
+      "ref": "pkg:gem/http-parser@1.2.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/http@4.3.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp-4.3.0.gemspec",
+      "ref": "pkg:gem/http@4.3.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/http_parser.rb@0.6.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fhttp_parser.rb-0.6.0.gemspec",
+      "ref": "pkg:gem/http_parser.rb@0.6.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/i18n@1.8.2?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fi18n-1.8.2.gemspec",
+      "ref": "pkg:gem/i18n@1.8.2",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/kubeclient@4.6.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fkubeclient-4.6.0.gemspec",
+      "ref": "pkg:gem/kubeclient@4.6.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/lru_redux@1.1.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Flru_redux-1.1.0.gemspec",
+      "ref": "pkg:gem/lru_redux@1.1.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/mime-types-data@3.2019.1009?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmime-types-data-3.2019.1009.gemspec",
+      "ref": "pkg:gem/mime-types-data@3.2019.1009",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/mime-types@3.3.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmime-types-3.3.1.gemspec",
+      "ref": "pkg:gem/mime-types@3.3.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/minitest@5.14.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fminitest-5.14.0.gemspec",
+      "ref": "pkg:gem/minitest@5.14.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/msgpack@1.3.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmsgpack-1.3.3.gemspec",
+      "ref": "pkg:gem/msgpack@1.3.3",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/multi_json@1.14.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmulti_json-1.14.1.gemspec",
+      "ref": "pkg:gem/multi_json@1.14.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/multipart-post@2.1.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fmultipart-post-2.1.1.gemspec",
+      "ref": "pkg:gem/multipart-post@2.1.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/netrc@0.11.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fnetrc-0.11.0.gemspec",
+      "ref": "pkg:gem/netrc@0.11.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/oj@3.10.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Foj-3.10.0.gemspec",
+      "ref": "pkg:gem/oj@3.10.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/prometheus-client@0.9.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fprometheus-client-0.9.0.gemspec",
+      "ref": "pkg:gem/prometheus-client@0.9.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/public_suffix@4.0.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fpublic_suffix-4.0.3.gemspec",
+      "ref": "pkg:gem/public_suffix@4.0.3",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/quantile@0.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fquantile-0.2.1.gemspec",
+      "ref": "pkg:gem/quantile@0.2.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/rake@13.0.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Frake-13.0.1.gemspec",
+      "ref": "pkg:gem/rake@13.0.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/recursive-open-struct@1.1.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Frecursive-open-struct-1.1.0.gemspec",
+      "ref": "pkg:gem/recursive-open-struct@1.1.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/rest-client@2.1.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Frest-client-2.1.0.gemspec",
+      "ref": "pkg:gem/rest-client@2.1.0",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/serverengine@2.2.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fserverengine-2.2.1.gemspec",
+      "ref": "pkg:gem/serverengine@2.2.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/sigdump@0.2.4?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fsigdump-0.2.4.gemspec",
+      "ref": "pkg:gem/sigdump@0.2.4",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/strptime@0.2.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fstrptime-0.2.3.gemspec",
+      "ref": "pkg:gem/strptime@0.2.3",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/systemd-journal@1.3.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fsystemd-journal-1.3.3.gemspec",
+      "ref": "pkg:gem/systemd-journal@1.3.3",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/thread_safe@0.3.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fthread_safe-0.3.6.gemspec",
+      "ref": "pkg:gem/thread_safe@0.3.6",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/tzinfo-data@1.2019.3?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ftzinfo-data-1.2019.3.gemspec",
+      "ref": "pkg:gem/tzinfo-data@1.2019.3",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/tzinfo@1.2.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Ftzinfo-1.2.6.gemspec",
+      "ref": "pkg:gem/tzinfo@1.2.6",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/unf@0.1.4?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Funf-0.1.4.gemspec",
+      "ref": "pkg:gem/unf@0.1.4",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/unf_ext@0.0.7.6?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Funf_ext-0.0.7.6.gemspec",
+      "ref": "pkg:gem/unf_ext@0.0.7.6",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/yajl-ruby@1.4.1?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fyajl-ruby-1.4.1.gemspec",
+      "ref": "pkg:gem/yajl-ruby@1.4.1",
       "dependsOn": []
     },
     {
-      "ref": "pkg:gem/zeitwerk@2.3.0?file_path=var%2Flib%2Fgems%2F2.5.0%2Fspecifications%2Fzeitwerk-2.3.0.gemspec",
+      "ref": "pkg:gem/zeitwerk@2.3.0",
       "dependsOn": []
     }
   ],
diff --git a/integration/testdata/fluentd-multiple-lockfiles.json.golden b/integration/testdata/fluentd-multiple-lockfiles.json.golden
index 23a1ae2a8af3..701c0262753d 100644
--- a/integration/testdata/fluentd-multiple-lockfiles.json.golden
+++ b/integration/testdata/fluentd-multiple-lockfiles.json.golden
@@ -27,6 +27,7 @@
       "Vulnerabilities": [
         {
           "VulnerabilityID": "CVE-2019-18276",
+          "PkgID": "bash@5.0-4",
           "PkgName": "bash",
           "PkgIdentifier": {
             "PURL": "pkg:deb/debian/bash@5.0-4?distro=debian-10.2",
@@ -90,6 +91,7 @@
           "VendorIDs": [
             "DSA-4613-1"
           ],
+          "PkgID": "libidn2-0@2.0.5-1",
           "PkgName": "libidn2-0",
           "PkgIdentifier": {
             "PURL": "pkg:deb/debian/libidn2-0@2.0.5-1?distro=debian-10.2",
@@ -158,6 +160,7 @@
       "Vulnerabilities": [
         {
           "VulnerabilityID": "CVE-2020-8165",
+          "PkgID": "activesupport@6.0.2.1",
           "PkgName": "activesupport",
           "PkgPath": "var/lib/gems/2.5.0/specifications/activesupport-6.0.2.1.gemspec",
           "PkgIdentifier": {
diff --git a/integration/testdata/minikube-kbom.json.golden b/integration/testdata/minikube-kbom.json.golden
index 080e061142e8..fd5b6b24718f 100644
--- a/integration/testdata/minikube-kbom.json.golden
+++ b/integration/testdata/minikube-kbom.json.golden
@@ -32,6 +32,7 @@
       "Vulnerabilities": [
         {
           "VulnerabilityID": "CVE-2023-2431",
+          "PkgID": "k8s.io/kubelet@1.27.0",
           "PkgName": "k8s.io/kubelet",
           "PkgIdentifier": {
             "PURL": "pkg:k8s/k8s.io%2Fkubelet@1.27.0",
diff --git a/integration/testdata/pom-cyclonedx.json.golden b/integration/testdata/pom-cyclonedx.json.golden
index 5487c239e2da..27f54a71848b 100644
--- a/integration/testdata/pom-cyclonedx.json.golden
+++ b/integration/testdata/pom-cyclonedx.json.golden
@@ -2,7 +2,7 @@
   "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
   "bomFormat": "CycloneDX",
   "specVersion": "1.5",
-  "serialNumber": "urn:uuid:3ff14136-e09f-4df9-80ea-000000000001",
+  "serialNumber": "urn:uuid:3ff14136-e09f-4df9-80ea-000000000005",
   "version": 1,
   "metadata": {
     "timestamp": "2021-08-25T12:20:30+00:00",
@@ -17,7 +17,7 @@
       ]
     },
     "component": {
-      "bom-ref": "3ff14136-e09f-4df9-80ea-000000000002",
+      "bom-ref": "3ff14136-e09f-4df9-80ea-000000000001",
       "type": "application",
       "name": "testdata/fixtures/repo/pom",
       "properties": [
@@ -30,7 +30,7 @@
   },
   "components": [
     {
-      "bom-ref": "3ff14136-e09f-4df9-80ea-000000000003",
+      "bom-ref": "3ff14136-e09f-4df9-80ea-000000000002",
       "type": "application",
       "name": "pom.xml",
       "properties": [
@@ -83,13 +83,13 @@
   ],
   "dependencies": [
     {
-      "ref": "3ff14136-e09f-4df9-80ea-000000000002",
+      "ref": "3ff14136-e09f-4df9-80ea-000000000001",
       "dependsOn": [
-        "3ff14136-e09f-4df9-80ea-000000000003"
+        "3ff14136-e09f-4df9-80ea-000000000002"
       ]
     },
     {
-      "ref": "3ff14136-e09f-4df9-80ea-000000000003",
+      "ref": "3ff14136-e09f-4df9-80ea-000000000002",
       "dependsOn": [
         "pkg:maven/com.example/log4shell@1.0-SNAPSHOT",
         "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.9.1"
diff --git a/magefiles/magefile.go b/magefiles/magefile.go
index f3024c99d7c4..fbb612b9d855 100644
--- a/magefiles/magefile.go
+++ b/magefiles/magefile.go
@@ -77,14 +77,6 @@ func (Tool) Labeler() error {
 	return sh.Run("go", "install", "github.com/knqyf263/labeler@latest")
 }
 
-// EasyJSON installs easyjson
-func (Tool) EasyJSON() error {
-	if exists(filepath.Join(GOBIN, "easyjson")) {
-		return nil
-	}
-	return sh.Run("go", "install", "github.com/mailru/easyjson/...@v0.7.7")
-}
-
 // Kind installs kind cluster
 func (Tool) Kind() error {
 	return sh.RunWithV(ENV, "go", "install", "sigs.k8s.io/kind@v0.19.0")
@@ -164,12 +156,6 @@ func Yacc() error {
 	return sh.Run("go", "generate", "./pkg/licensing/expression/...")
 }
 
-// Easyjson generates JSON marshaler/unmarshaler for TinyGo/WebAssembly as TinyGo doesn't support encoding/json.
-func Easyjson() error {
-	mg.Deps(Tool{}.EasyJSON)
-	return sh.Run("easyjson", "./pkg/module/serialize/types.go")
-}
-
 type Test mg.Namespace
 
 // FixtureContainerImages downloads and extracts required images
diff --git a/pkg/dependency/id.go b/pkg/dependency/id.go
new file mode 100644
index 000000000000..d40289cedc6a
--- /dev/null
+++ b/pkg/dependency/id.go
@@ -0,0 +1,32 @@
+package dependency
+
+import (
+	"strings"
+
+	"github.com/aquasecurity/trivy/pkg/fanal/types"
+)
+
+// ID returns a unique ID for the given library.
+// The package ID is used to construct the dependency graph.
+// The separator is different for each language type.
+func ID(ltype types.LangType, name, version string) string {
+	if version == "" {
+		return name
+	}
+
+	sep := "@"
+	switch ltype {
+	case types.Conan:
+		sep = "/"
+	case types.GoModule, types.GoBinary:
+		// Return a module ID according the Go way.
+		// Format: <module_name>@v<module_version>
+		// e.g. github.com/aquasecurity/go-dep-parser@v0.0.0-20230130190635-5e31092b0621
+		if !strings.HasPrefix(version, "v") {
+			version = "v" + version
+		}
+	case types.Jar, types.Pom, types.Gradle:
+		sep = ":"
+	}
+	return name + sep + version
+}
diff --git a/pkg/dependency/id_test.go b/pkg/dependency/id_test.go
new file mode 100644
index 000000000000..173d71c3d726
--- /dev/null
+++ b/pkg/dependency/id_test.go
@@ -0,0 +1,73 @@
+package dependency_test
+
+import (
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/fanal/types"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestID(t *testing.T) {
+	type args struct {
+		ltype   types.LangType
+		name    string
+		version string
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{
+			name: "conan",
+			args: args{
+				ltype:   types.Conan,
+				name:    "test",
+				version: "1.0.0",
+			},
+			want: "test/1.0.0",
+		},
+		{
+			name: "go module",
+			args: args{
+				ltype:   types.GoModule,
+				name:    "test",
+				version: "1.0.0",
+			},
+			want: "test@v1.0.0",
+		},
+		{
+			name: "gradle",
+			args: args{
+				ltype:   types.Gradle,
+				name:    "test",
+				version: "1.0.0",
+			},
+			want: "test:1.0.0",
+		},
+		{
+			name: "pip",
+			args: args{
+				ltype:   types.Pip,
+				name:    "test",
+				version: "1.0.0",
+			},
+			want: "test@1.0.0",
+		},
+		{
+			name: "no version",
+			args: args{
+				ltype:   types.Pom,
+				name:    "test",
+				version: "",
+			},
+			want: "test",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := dependency.ID(tt.args.ltype, tt.args.name, tt.args.version)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
diff --git a/pkg/dependency/parser/c/conan/parse.go b/pkg/dependency/parser/c/conan/parse.go
index 88e98e0b91dc..78e3bdc09636 100644
--- a/pkg/dependency/parser/c/conan/parse.go
+++ b/pkg/dependency/parser/c/conan/parse.go
@@ -1,7 +1,6 @@
 package conan
 
 import (
-	"fmt"
 	"io"
 	"strings"
 
@@ -9,7 +8,9 @@ import (
 	"golang.org/x/exp/slices"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
@@ -109,7 +110,7 @@ func parseRef(node Node) (types.Library, error) {
 		return types.Library{}, xerrors.Errorf("Unable to determine conan dependency: %q", node.Ref)
 	}
 	return types.Library{
-		ID:      fmt.Sprintf("%s/%s", ss[0], ss[1]),
+		ID:      dependency.ID(ftypes.Conan, ss[0], ss[1]),
 		Name:    ss[0],
 		Version: ss[1],
 		Locations: []types.Location{
diff --git a/pkg/dependency/parser/c/conan/parse_test.go b/pkg/dependency/parser/c/conan/parse_test.go
index 862036bec738..a22ec90afc1b 100644
--- a/pkg/dependency/parser/c/conan/parse_test.go
+++ b/pkg/dependency/parser/c/conan/parse_test.go
@@ -10,7 +10,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/c/conan"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/conda/meta/parse.go b/pkg/dependency/parser/conda/meta/parse.go
index 4acdcf6c94c3..30d344bfdfa3 100644
--- a/pkg/dependency/parser/conda/meta/parse.go
+++ b/pkg/dependency/parser/conda/meta/parse.go
@@ -5,7 +5,7 @@ import (
 
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
diff --git a/pkg/dependency/parser/conda/meta/parse_test.go b/pkg/dependency/parser/conda/meta/parse_test.go
index 656eb9b70d98..8bde5e184f0e 100644
--- a/pkg/dependency/parser/conda/meta/parse_test.go
+++ b/pkg/dependency/parser/conda/meta/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/conda/meta"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/dart/pub/parse.go b/pkg/dependency/parser/dart/pub/parse.go
index 3b6ef40280e7..efd8d9aa5d41 100644
--- a/pkg/dependency/parser/dart/pub/parse.go
+++ b/pkg/dependency/parser/dart/pub/parse.go
@@ -1,12 +1,12 @@
 package pub
 
 import (
-	"fmt"
-
 	"golang.org/x/xerrors"
 	"gopkg.in/yaml.v3"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
@@ -44,7 +44,7 @@ func (Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, er
 		// It will be confusing if we exclude direct dev dependencies and include transitive dev dependencies.
 		// We decided to keep all dev dependencies until Pub will add support for "transitive main" and "transitive dev".
 		lib := types.Library{
-			ID:       pkgID(name, dep.Version),
+			ID:       dependency.ID(ftypes.Pub, name, dep.Version),
 			Name:     name,
 			Version:  dep.Version,
 			Indirect: dep.Dependency == transitiveDep,
@@ -54,7 +54,3 @@ func (Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, er
 
 	return libs, nil, nil
 }
-
-func pkgID(name, version string) string {
-	return fmt.Sprintf(idFormat, name, version)
-}
diff --git a/pkg/dependency/parser/dart/pub/parse_test.go b/pkg/dependency/parser/dart/pub/parse_test.go
index 00bed49158c5..ef62ab971658 100644
--- a/pkg/dependency/parser/dart/pub/parse_test.go
+++ b/pkg/dependency/parser/dart/pub/parse_test.go
@@ -10,7 +10,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/dart/pub"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParser_Parse(t *testing.T) {
diff --git a/pkg/dependency/parser/dotnet/core_deps/parse.go b/pkg/dependency/parser/dotnet/core_deps/parse.go
index a8d44b8939e3..c4bf533a87df 100644
--- a/pkg/dependency/parser/dotnet/core_deps/parse.go
+++ b/pkg/dependency/parser/dotnet/core_deps/parse.go
@@ -7,7 +7,7 @@ import (
 	"github.com/liamg/jfather"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
diff --git a/pkg/dependency/parser/dotnet/core_deps/parse_test.go b/pkg/dependency/parser/dotnet/core_deps/parse_test.go
index 4e6e80955b75..839cf9ed97ba 100644
--- a/pkg/dependency/parser/dotnet/core_deps/parse_test.go
+++ b/pkg/dependency/parser/dotnet/core_deps/parse_test.go
@@ -10,7 +10,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/frameworks/wordpress/parse.go b/pkg/dependency/parser/frameworks/wordpress/parse.go
index 1eb481e73481..61e00ded81cc 100644
--- a/pkg/dependency/parser/frameworks/wordpress/parse.go
+++ b/pkg/dependency/parser/frameworks/wordpress/parse.go
@@ -7,7 +7,7 @@ import (
 
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func Parse(r io.Reader) (lib types.Library, err error) {
diff --git a/pkg/dependency/parser/frameworks/wordpress/parse_test.go b/pkg/dependency/parser/frameworks/wordpress/parse_test.go
index aafbefe513be..623ae06b87c7 100644
--- a/pkg/dependency/parser/frameworks/wordpress/parse_test.go
+++ b/pkg/dependency/parser/frameworks/wordpress/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParseWordPress(t *testing.T) {
diff --git a/pkg/dependency/parser/golang/binary/parse.go b/pkg/dependency/parser/golang/binary/parse.go
index bd31cec9ad1c..8bf9bf4dab3e 100644
--- a/pkg/dependency/parser/golang/binary/parse.go
+++ b/pkg/dependency/parser/golang/binary/parse.go
@@ -6,7 +6,7 @@ import (
 
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
diff --git a/pkg/dependency/parser/golang/binary/parse_test.go b/pkg/dependency/parser/golang/binary/parse_test.go
index 1d9345fa11d3..cee58377ef25 100644
--- a/pkg/dependency/parser/golang/binary/parse_test.go
+++ b/pkg/dependency/parser/golang/binary/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/golang/binary"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/golang/mod/parse.go b/pkg/dependency/parser/golang/mod/parse.go
index 716eed249534..dded3d6527d3 100644
--- a/pkg/dependency/parser/golang/mod/parse.go
+++ b/pkg/dependency/parser/golang/mod/parse.go
@@ -1,7 +1,6 @@
 package mod
 
 import (
-	"fmt"
 	"io"
 	"regexp"
 	"strconv"
@@ -11,7 +10,9 @@ import (
 	"golang.org/x/mod/modfile"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
@@ -89,7 +90,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 			continue
 		}
 		libs[require.Mod.Path] = types.Library{
-			ID:                 ModuleID(require.Mod.Path, require.Mod.Version[1:]),
+			ID:                 packageID(require.Mod.Path, require.Mod.Version[1:]),
 			Name:               require.Mod.Path,
 			Version:            require.Mod.Version[1:],
 			Indirect:           require.Indirect,
@@ -124,7 +125,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 
 			// Add replaced library to library register.
 			libs[rep.New.Path] = types.Library{
-				ID:                 ModuleID(rep.New.Path, rep.New.Version[1:]),
+				ID:                 packageID(rep.New.Path, rep.New.Version[1:]),
 				Name:               rep.New.Path,
 				Version:            rep.New.Version[1:],
 				Indirect:           old.Indirect,
@@ -154,9 +155,6 @@ func lessThan117(ver string) bool {
 	return major <= 1 && minor < 17
 }
 
-// ModuleID returns a module ID according the Go way.
-// Format: <module_name>@v<module_version>
-// e.g. github.com/aquasecurity/go-dep-parser@v0.0.0-20230130190635-5e31092b0621
-func ModuleID(name, version string) string {
-	return fmt.Sprintf("%s@v%s", name, version)
+func packageID(name, version string) string {
+	return dependency.ID(ftypes.GoModule, name, version)
 }
diff --git a/pkg/dependency/parser/golang/mod/parse_test.go b/pkg/dependency/parser/golang/mod/parse_test.go
index d3a6ee65845b..6372785df058 100644
--- a/pkg/dependency/parser/golang/mod/parse_test.go
+++ b/pkg/dependency/parser/golang/mod/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
@@ -99,37 +99,3 @@ func TestParse(t *testing.T) {
 		})
 	}
 }
-
-func TestModuleID(t *testing.T) {
-	type args struct {
-		name    string
-		version string
-	}
-	tests := []struct {
-		name string
-		args args
-		want string
-	}{
-		{
-			name: "normal",
-			args: args{
-				name:    "github.com/aquasecurity/trivy",
-				version: "0.38.0",
-			},
-			want: "github.com/aquasecurity/trivy@v0.38.0",
-		},
-		{
-			name: "pseudo version",
-			args: args{
-				name:    "github.com/aquasecurity/go-dep-parser",
-				version: "0.0.0-20230130190635-5e31092b0621",
-			},
-			want: "github.com/aquasecurity/go-dep-parser@v0.0.0-20230130190635-5e31092b0621",
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			assert.Equalf(t, tt.want, ModuleID(tt.args.name, tt.args.version), "ModuleID(%v, %v)", tt.args.name, tt.args.version)
-		})
-	}
-}
diff --git a/pkg/dependency/parser/golang/mod/parse_testcase.go b/pkg/dependency/parser/golang/mod/parse_testcase.go
index c0e683185e7a..35a396e53c1c 100644
--- a/pkg/dependency/parser/golang/mod/parse_testcase.go
+++ b/pkg/dependency/parser/golang/mod/parse_testcase.go
@@ -1,6 +1,6 @@
 package mod
 
-import "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+import "github.com/aquasecurity/trivy/pkg/dependency/types"
 
 var (
 	// execute go mod tidy in normal folder
diff --git a/pkg/dependency/parser/golang/sum/parse.go b/pkg/dependency/parser/golang/sum/parse.go
index 7a5eee5c6f0c..e06b474c0a97 100644
--- a/pkg/dependency/parser/golang/sum/parse.go
+++ b/pkg/dependency/parser/golang/sum/parse.go
@@ -6,8 +6,9 @@ import (
 
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/golang/mod"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
@@ -40,7 +41,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 
 	for k, v := range uniqueLibs {
 		libs = append(libs, types.Library{
-			ID:      mod.ModuleID(k, v),
+			ID:      dependency.ID(ftypes.GoModule, k, v),
 			Name:    k,
 			Version: v,
 		})
diff --git a/pkg/dependency/parser/golang/sum/parse_test.go b/pkg/dependency/parser/golang/sum/parse_test.go
index 39709bb5008d..3888743cdf85 100644
--- a/pkg/dependency/parser/golang/sum/parse_test.go
+++ b/pkg/dependency/parser/golang/sum/parse_test.go
@@ -9,7 +9,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/golang/sum/parse_testcase.go b/pkg/dependency/parser/golang/sum/parse_testcase.go
index ea72984159f0..70d4972c6f76 100644
--- a/pkg/dependency/parser/golang/sum/parse_testcase.go
+++ b/pkg/dependency/parser/golang/sum/parse_testcase.go
@@ -1,6 +1,6 @@
 package sum
 
-import "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+import "github.com/aquasecurity/trivy/pkg/dependency/types"
 
 var (
 	// docker run --name gomod --rm -it golang:1.15 bash
diff --git a/pkg/dependency/parser/gradle/lockfile/parse.go b/pkg/dependency/parser/gradle/lockfile/parse.go
index d88aab6bf461..3a60f3f58872 100644
--- a/pkg/dependency/parser/gradle/lockfile/parse.go
+++ b/pkg/dependency/parser/gradle/lockfile/parse.go
@@ -2,11 +2,12 @@ package lockfile
 
 import (
 	"bufio"
-	"fmt"
 	"strings"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
@@ -36,7 +37,7 @@ func (Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, er
 		name := strings.Join(dep[:2], ":")
 		version := strings.Split(dep[2], "=")[0] // remove classPaths
 		libs = append(libs, types.Library{
-			ID:      fmt.Sprintf("%s:%s", name, version),
+			ID:      dependency.ID(ftypes.Gradle, name, version),
 			Name:    name,
 			Version: version,
 			Locations: []types.Location{
diff --git a/pkg/dependency/parser/gradle/lockfile/parse_test.go b/pkg/dependency/parser/gradle/lockfile/parse_test.go
index be198774081a..e9f76883e4e5 100644
--- a/pkg/dependency/parser/gradle/lockfile/parse_test.go
+++ b/pkg/dependency/parser/gradle/lockfile/parse_test.go
@@ -6,7 +6,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/stretchr/testify/assert"
 )
 
diff --git a/pkg/dependency/parser/hex/mix/parse.go b/pkg/dependency/parser/hex/mix/parse.go
index 6430d406e0af..edc43fd284c6 100644
--- a/pkg/dependency/parser/hex/mix/parse.go
+++ b/pkg/dependency/parser/hex/mix/parse.go
@@ -2,12 +2,13 @@ package mix
 
 import (
 	"bufio"
-	"fmt"
 	"strings"
 	"unicode"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
@@ -50,10 +51,15 @@ func (Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, er
 		}
 		version := strings.Trim(ss[2], `"`)
 		libs = append(libs, types.Library{
-			ID:        fmt.Sprintf("%s@%s", name, version),
-			Name:      name,
-			Version:   version,
-			Locations: []types.Location{{StartLine: lineNumber, EndLine: lineNumber}},
+			ID:      dependency.ID(ftypes.Hex, name, version),
+			Name:    name,
+			Version: version,
+			Locations: []types.Location{
+				{
+					StartLine: lineNumber,
+					EndLine:   lineNumber,
+				},
+			},
 		})
 
 	}
diff --git a/pkg/dependency/parser/hex/mix/parse_test.go b/pkg/dependency/parser/hex/mix/parse_test.go
index 3392b39468c5..ab3d929dd96f 100644
--- a/pkg/dependency/parser/hex/mix/parse_test.go
+++ b/pkg/dependency/parser/hex/mix/parse_test.go
@@ -6,7 +6,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/stretchr/testify/assert"
 )
 
diff --git a/pkg/dependency/parser/java/jar/parse.go b/pkg/dependency/parser/java/jar/parse.go
index 800fc648c14e..d5f1f6df0a4d 100644
--- a/pkg/dependency/parser/java/jar/parse.go
+++ b/pkg/dependency/parser/java/jar/parse.go
@@ -18,7 +18,7 @@ import (
 	"go.uber.org/zap"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
diff --git a/pkg/dependency/parser/java/jar/parse_test.go b/pkg/dependency/parser/java/jar/parse_test.go
index cf706063335f..88125a5a34b1 100644
--- a/pkg/dependency/parser/java/jar/parse_test.go
+++ b/pkg/dependency/parser/java/jar/parse_test.go
@@ -14,7 +14,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/java/jar"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 var (
diff --git a/pkg/dependency/parser/java/jar/types.go b/pkg/dependency/parser/java/jar/types.go
index c01ba6ce4866..ddd378b778b7 100644
--- a/pkg/dependency/parser/java/jar/types.go
+++ b/pkg/dependency/parser/java/jar/types.go
@@ -5,7 +5,7 @@ import (
 
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 var ArtifactNotFoundErr = xerrors.New("no artifact found")
diff --git a/pkg/dependency/parser/java/pom/artifact.go b/pkg/dependency/parser/java/pom/artifact.go
index 86448170f910..7cbab3b5b651 100644
--- a/pkg/dependency/parser/java/pom/artifact.go
+++ b/pkg/dependency/parser/java/pom/artifact.go
@@ -9,7 +9,7 @@ import (
 	"github.com/samber/lo"
 	"golang.org/x/exp/slices"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 )
 
diff --git a/pkg/dependency/parser/java/pom/parse.go b/pkg/dependency/parser/java/pom/parse.go
index fa70995ea5aa..8abecc5df36c 100644
--- a/pkg/dependency/parser/java/pom/parse.go
+++ b/pkg/dependency/parser/java/pom/parse.go
@@ -18,8 +18,10 @@ import (
 	"golang.org/x/net/html/charset"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
@@ -688,3 +690,7 @@ func parsePom(r io.Reader) (*pomXML, error) {
 	}
 	return parsed, nil
 }
+
+func packageID(name, version string) string {
+	return dependency.ID(ftypes.Pom, name, version)
+}
diff --git a/pkg/dependency/parser/java/pom/parse_test.go b/pkg/dependency/parser/java/pom/parse_test.go
index f3c8c7a41c89..4123d1dde960 100644
--- a/pkg/dependency/parser/java/pom/parse_test.go
+++ b/pkg/dependency/parser/java/pom/parse_test.go
@@ -11,7 +11,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/java/pom"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestPom_Parse(t *testing.T) {
diff --git a/pkg/dependency/parser/java/pom/pom.go b/pkg/dependency/parser/java/pom/pom.go
index eb3e9ff7cc86..8b610cc5925b 100644
--- a/pkg/dependency/parser/java/pom/pom.go
+++ b/pkg/dependency/parser/java/pom/pom.go
@@ -12,8 +12,8 @@ import (
 	"github.com/samber/lo"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 )
 
diff --git a/pkg/dependency/parser/java/pom/utils.go b/pkg/dependency/parser/java/pom/utils.go
index 73be21fcc12c..fb42673515f2 100644
--- a/pkg/dependency/parser/java/pom/utils.go
+++ b/pkg/dependency/parser/java/pom/utils.go
@@ -1,7 +1,6 @@
 package pom
 
 import (
-	"fmt"
 	"os"
 	"strings"
 )
@@ -20,7 +19,3 @@ func isProperty(version string) bool {
 	}
 	return false
 }
-
-func packageID(name, version string) string {
-	return fmt.Sprintf("%s:%s", name, version)
-}
diff --git a/pkg/dependency/parser/julia/manifest/parse.go b/pkg/dependency/parser/julia/manifest/parse.go
index 3fc99d5ab0b3..685a54074629 100644
--- a/pkg/dependency/parser/julia/manifest/parse.go
+++ b/pkg/dependency/parser/julia/manifest/parse.go
@@ -8,7 +8,7 @@ import (
 	"golang.org/x/exp/maps"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
diff --git a/pkg/dependency/parser/julia/manifest/parse_test.go b/pkg/dependency/parser/julia/manifest/parse_test.go
index 6a3081e99164..6eacf5a76133 100644
--- a/pkg/dependency/parser/julia/manifest/parse_test.go
+++ b/pkg/dependency/parser/julia/manifest/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/julia/manifest/parse_testcase.go b/pkg/dependency/parser/julia/manifest/parse_testcase.go
index c5b7a00c720c..3055cc8d15a8 100644
--- a/pkg/dependency/parser/julia/manifest/parse_testcase.go
+++ b/pkg/dependency/parser/julia/manifest/parse_testcase.go
@@ -1,6 +1,6 @@
 package julia
 
-import "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+import "github.com/aquasecurity/trivy/pkg/dependency/types"
 
 var (
 	juliaV1_6Libs = []types.Library{
diff --git a/pkg/dependency/parser/nodejs/npm/parse.go b/pkg/dependency/parser/nodejs/npm/parse.go
index 0dbc1b1b2bac..e289720b89a2 100644
--- a/pkg/dependency/parser/nodejs/npm/parse.go
+++ b/pkg/dependency/parser/nodejs/npm/parse.go
@@ -12,8 +12,10 @@ import (
 	"golang.org/x/exp/maps"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
@@ -107,7 +109,7 @@ func (p *Parser) parseV2(packages map[string]Package) ([]types.Library, []types.
 			pkgName = pkgNameFromPath(pkgPath)
 		}
 
-		pkgID := utils.PackageID(pkgName, pkg.Version)
+		pkgID := packageID(pkgName, pkg.Version)
 		location := types.Location{
 			StartLine: pkg.StartLine,
 			EndLine:   pkg.EndLine,
@@ -237,7 +239,7 @@ func findDependsOn(pkgPath, depName string, packages map[string]Package) (string
 		modulePath = joinPaths(modulePath, depName)
 
 		if dep, ok := packages[modulePath]; ok {
-			return utils.PackageID(depName, dep.Version), nil
+			return packageID(depName, dep.Version), nil
 		}
 	}
 
@@ -254,40 +256,40 @@ func (p *Parser) parseV1(dependencies map[string]Dependency, versions map[string
 
 	var libs []types.Library
 	var deps []types.Dependency
-	for pkgName, dependency := range dependencies {
+	for pkgName, dep := range dependencies {
 		lib := types.Library{
-			ID:       utils.PackageID(pkgName, dependency.Version),
+			ID:       packageID(pkgName, dep.Version),
 			Name:     pkgName,
-			Version:  dependency.Version,
-			Dev:      dependency.Dev,
+			Version:  dep.Version,
+			Dev:      dep.Dev,
 			Indirect: true, // lockfile v1 schema doesn't have information about Direct dependencies
 			ExternalReferences: []types.ExternalRef{
 				{
 					Type: types.RefOther,
-					URL:  dependency.Resolved,
+					URL:  dep.Resolved,
 				},
 			},
 			Locations: []types.Location{
 				{
-					StartLine: dependency.StartLine,
-					EndLine:   dependency.EndLine,
+					StartLine: dep.StartLine,
+					EndLine:   dep.EndLine,
 				},
 			},
 		}
 		libs = append(libs, lib)
 
-		dependsOn := make([]string, 0, len(dependency.Requires))
-		for libName, requiredVer := range dependency.Requires {
+		dependsOn := make([]string, 0, len(dep.Requires))
+		for libName, requiredVer := range dep.Requires {
 			// Try to resolve the version with nested dependencies first
-			if resolvedDep, ok := dependency.Dependencies[libName]; ok {
-				libID := utils.PackageID(libName, resolvedDep.Version)
+			if resolvedDep, ok := dep.Dependencies[libName]; ok {
+				libID := packageID(libName, resolvedDep.Version)
 				dependsOn = append(dependsOn, libID)
 				continue
 			}
 
 			// Try to resolve the version with the higher level dependencies
 			if ver, ok := versions[libName]; ok {
-				dependsOn = append(dependsOn, utils.PackageID(libName, ver))
+				dependsOn = append(dependsOn, packageID(libName, ver))
 				continue
 			}
 
@@ -297,14 +299,14 @@ func (p *Parser) parseV1(dependencies map[string]Dependency, versions map[string
 
 		if len(dependsOn) > 0 {
 			deps = append(deps, types.Dependency{
-				ID:        utils.PackageID(lib.Name, lib.Version),
+				ID:        packageID(lib.Name, lib.Version),
 				DependsOn: dependsOn,
 			})
 		}
 
-		if dependency.Dependencies != nil {
+		if dep.Dependencies != nil {
 			// Recursion
-			childLibs, childDeps := p.parseV1(dependency.Dependencies, maps.Clone(versions))
+			childLibs, childDeps := p.parseV1(dep.Dependencies, maps.Clone(versions))
 			libs = append(libs, childLibs...)
 			deps = append(deps, childDeps...)
 		}
@@ -379,3 +381,7 @@ func (t *Package) UnmarshalJSONWithMetadata(node jfather.Node) error {
 	t.EndLine = node.Range().End.Line
 	return nil
 }
+
+func packageID(name, version string) string {
+	return dependency.ID(ftypes.Npm, name, version)
+}
diff --git a/pkg/dependency/parser/nodejs/npm/parse_test.go b/pkg/dependency/parser/nodejs/npm/parse_test.go
index 9ccb37f165d0..c67055a71628 100644
--- a/pkg/dependency/parser/nodejs/npm/parse_test.go
+++ b/pkg/dependency/parser/nodejs/npm/parse_test.go
@@ -7,7 +7,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/nodejs/npm/parse_testcase.go b/pkg/dependency/parser/nodejs/npm/parse_testcase.go
index 6a7237e2bdca..e68addd15219 100644
--- a/pkg/dependency/parser/nodejs/npm/parse_testcase.go
+++ b/pkg/dependency/parser/nodejs/npm/parse_testcase.go
@@ -1,6 +1,6 @@
 package npm
 
-import "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+import "github.com/aquasecurity/trivy/pkg/dependency/types"
 
 var (
 	// docker run --name node --rm -it node@sha256:51dd437f31812df71108b81385e2945071ec813d5815fa3403855669c8f3432b sh
@@ -13,52 +13,683 @@ var (
 	// libraries are filled manually
 
 	npmV1Libs = []types.Library{
-		{ID: "@babel/helper-string-parser@7.19.4", Name: "@babel/helper-string-parser", Version: "7.19.4", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz"}}, Locations: []types.Location{{StartLine: 7, EndLine: 11}}},
-		{ID: "asap@2.0.6", Name: "asap", Version: "2.0.6", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz"}}, Locations: []types.Location{{StartLine: 12, EndLine: 17}}},
-		{ID: "body-parser@1.18.3", Name: "body-parser", Version: "1.18.3", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz"}}, Locations: []types.Location{{StartLine: 18, EndLine: 49}}},
-		{ID: "bytes@3.0.0", Name: "bytes", Version: "3.0.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz"}}, Locations: []types.Location{{StartLine: 50, EndLine: 54}}},
-		{ID: "content-type@1.0.5", Name: "content-type", Version: "1.0.5", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz"}}, Locations: []types.Location{{StartLine: 55, EndLine: 59}}},
-		{ID: "debug@2.5.2", Name: "debug", Version: "2.5.2", Dev: true, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/debug/-/debug-2.5.2.tgz"}}, Locations: []types.Location{{StartLine: 60, EndLine: 76}}},
-		{ID: "debug@2.6.9", Name: "debug", Version: "2.6.9", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"}}, Locations: []types.Location{{StartLine: 35, EndLine: 42}, {StartLine: 111, EndLine: 118}}},
-		{ID: "depd@1.1.2", Name: "depd", Version: "1.1.2", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz"}}, Locations: []types.Location{{StartLine: 77, EndLine: 81}}},
-		{ID: "ee-first@1.1.1", Name: "ee-first", Version: "1.1.1", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"}}, Locations: []types.Location{{StartLine: 82, EndLine: 86}}},
-		{ID: "encodeurl@1.0.2", Name: "encodeurl", Version: "1.0.2", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz"}}, Locations: []types.Location{{StartLine: 87, EndLine: 91}}},
-		{ID: "escape-html@1.0.3", Name: "escape-html", Version: "1.0.3", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz"}}, Locations: []types.Location{{StartLine: 92, EndLine: 96}}},
-		{ID: "finalhandler@1.1.1", Name: "finalhandler", Version: "1.1.1", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz"}}, Locations: []types.Location{{StartLine: 97, EndLine: 125}}},
-		{ID: "http-errors@1.6.3", Name: "http-errors", Version: "1.6.3", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz"}}, Locations: []types.Location{{StartLine: 126, EndLine: 136}}},
-		{ID: "iconv-lite@0.4.23", Name: "iconv-lite", Version: "0.4.23", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz"}}, Locations: []types.Location{{StartLine: 137, EndLine: 144}}},
-		{ID: "inherits@2.0.3", Name: "inherits", Version: "2.0.3", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"}}, Locations: []types.Location{{StartLine: 145, EndLine: 149}}},
-		{ID: "media-typer@0.3.0", Name: "media-typer", Version: "0.3.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"}}, Locations: []types.Location{{StartLine: 150, EndLine: 154}}},
-		{ID: "mime-db@1.52.0", Name: "mime-db", Version: "1.52.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"}}, Locations: []types.Location{{StartLine: 155, EndLine: 159}}},
-		{ID: "mime-types@2.1.35", Name: "mime-types", Version: "2.1.35", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"}}, Locations: []types.Location{{StartLine: 160, EndLine: 167}}},
-		{ID: "ms@0.7.2", Name: "ms", Version: "0.7.2", Dev: true, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz"}}, Locations: []types.Location{{StartLine: 69, EndLine: 74}}},
-		{ID: "ms@1.0.0", Name: "ms", Version: "1.0.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/ms/-/ms-1.0.0.tgz"}}, Locations: []types.Location{{StartLine: 168, EndLine: 172}}},
-		{ID: "ms@2.0.0", Name: "ms", Version: "2.0.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"}}, Locations: []types.Location{{StartLine: 43, EndLine: 47}, {StartLine: 119, EndLine: 123}}},
-		{ID: "on-finished@2.3.0", Name: "on-finished", Version: "2.3.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz"}}, Locations: []types.Location{{StartLine: 173, EndLine: 180}}},
-		{ID: "parseurl@1.3.3", Name: "parseurl", Version: "1.3.3", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"}}, Locations: []types.Location{{StartLine: 181, EndLine: 185}}},
-		{ID: "promise@8.3.0", Name: "promise", Version: "8.3.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz"}}, Locations: []types.Location{{StartLine: 186, EndLine: 194}}},
-		{ID: "qs@6.5.2", Name: "qs", Version: "6.5.2", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz"}}, Locations: []types.Location{{StartLine: 195, EndLine: 199}}},
-		{ID: "raw-body@2.3.3", Name: "raw-body", Version: "2.3.3", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz"}}, Locations: []types.Location{{StartLine: 200, EndLine: 210}}},
-		{ID: "safer-buffer@2.1.2", Name: "safer-buffer", Version: "2.1.2", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"}}, Locations: []types.Location{{StartLine: 211, EndLine: 215}}},
-		{ID: "setprototypeof@1.1.0", Name: "setprototypeof", Version: "1.1.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz"}}, Locations: []types.Location{{StartLine: 216, EndLine: 220}}},
-		{ID: "statuses@1.4.0", Name: "statuses", Version: "1.4.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz"}}, Locations: []types.Location{{StartLine: 221, EndLine: 225}}},
-		{ID: "type-is@1.6.18", Name: "type-is", Version: "1.6.18", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz"}}, Locations: []types.Location{{StartLine: 226, EndLine: 234}}},
-		{ID: "unpipe@1.0.0", Name: "unpipe", Version: "1.0.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"}}, Locations: []types.Location{{StartLine: 235, EndLine: 239}}},
+		{
+			ID:       "@babel/helper-string-parser@7.19.4",
+			Name:     "@babel/helper-string-parser",
+			Version:  "7.19.4",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 7,
+					EndLine:   11,
+				},
+			},
+		},
+		{
+			ID:       "asap@2.0.6",
+			Name:     "asap",
+			Version:  "2.0.6",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 12,
+					EndLine:   17,
+				},
+			},
+		},
+		{
+			ID:       "body-parser@1.18.3",
+			Name:     "body-parser",
+			Version:  "1.18.3",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 18,
+					EndLine:   49,
+				},
+			},
+		},
+		{
+			ID:       "bytes@3.0.0",
+			Name:     "bytes",
+			Version:  "3.0.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 50,
+					EndLine:   54,
+				},
+			},
+		},
+		{
+			ID:       "content-type@1.0.5",
+			Name:     "content-type",
+			Version:  "1.0.5",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 55,
+					EndLine:   59,
+				},
+			},
+		},
+		{
+			ID:       "debug@2.5.2",
+			Name:     "debug",
+			Version:  "2.5.2",
+			Dev:      true,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/debug/-/debug-2.5.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 60,
+					EndLine:   76,
+				},
+			},
+		},
+		{
+			ID:       "debug@2.6.9",
+			Name:     "debug",
+			Version:  "2.6.9",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 35,
+					EndLine:   42,
+				},
+				{
+					StartLine: 111,
+					EndLine:   118,
+				},
+			},
+		},
+		{
+			ID:       "depd@1.1.2",
+			Name:     "depd",
+			Version:  "1.1.2",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 77,
+					EndLine:   81,
+				},
+			},
+		},
+		{
+			ID:       "ee-first@1.1.1",
+			Name:     "ee-first",
+			Version:  "1.1.1",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 82,
+					EndLine:   86,
+				},
+			},
+		},
+		{
+			ID:       "encodeurl@1.0.2",
+			Name:     "encodeurl",
+			Version:  "1.0.2",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 87,
+					EndLine:   91,
+				},
+			},
+		},
+		{
+			ID:       "escape-html@1.0.3",
+			Name:     "escape-html",
+			Version:  "1.0.3",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 92,
+					EndLine:   96,
+				},
+			},
+		},
+		{
+			ID:       "finalhandler@1.1.1",
+			Name:     "finalhandler",
+			Version:  "1.1.1",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 97,
+					EndLine:   125,
+				},
+			},
+		},
+		{
+			ID:       "http-errors@1.6.3",
+			Name:     "http-errors",
+			Version:  "1.6.3",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 126,
+					EndLine:   136,
+				},
+			},
+		},
+		{
+			ID:       "iconv-lite@0.4.23",
+			Name:     "iconv-lite",
+			Version:  "0.4.23",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 137,
+					EndLine:   144,
+				},
+			},
+		},
+		{
+			ID:       "inherits@2.0.3",
+			Name:     "inherits",
+			Version:  "2.0.3",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 145,
+					EndLine:   149,
+				},
+			},
+		},
+		{
+			ID:       "media-typer@0.3.0",
+			Name:     "media-typer",
+			Version:  "0.3.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 150,
+					EndLine:   154,
+				},
+			},
+		},
+		{
+			ID:       "mime-db@1.52.0",
+			Name:     "mime-db",
+			Version:  "1.52.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 155,
+					EndLine:   159,
+				},
+			},
+		},
+		{
+			ID:       "mime-types@2.1.35",
+			Name:     "mime-types",
+			Version:  "2.1.35",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 160,
+					EndLine:   167,
+				},
+			},
+		},
+		{
+			ID:       "ms@0.7.2",
+			Name:     "ms",
+			Version:  "0.7.2",
+			Dev:      true,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 69,
+					EndLine:   74,
+				},
+			},
+		},
+		{
+			ID:       "ms@1.0.0",
+			Name:     "ms",
+			Version:  "1.0.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/ms/-/ms-1.0.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 168,
+					EndLine:   172,
+				},
+			},
+		},
+		{
+			ID:       "ms@2.0.0",
+			Name:     "ms",
+			Version:  "2.0.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 43,
+					EndLine:   47,
+				},
+				{
+					StartLine: 119,
+					EndLine:   123,
+				},
+			},
+		},
+		{
+			ID:       "on-finished@2.3.0",
+			Name:     "on-finished",
+			Version:  "2.3.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 173,
+					EndLine:   180,
+				},
+			},
+		},
+		{
+			ID:       "parseurl@1.3.3",
+			Name:     "parseurl",
+			Version:  "1.3.3",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 181,
+					EndLine:   185,
+				},
+			},
+		},
+		{
+			ID:       "promise@8.3.0",
+			Name:     "promise",
+			Version:  "8.3.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 186,
+					EndLine:   194,
+				},
+			},
+		},
+		{
+			ID:       "qs@6.5.2",
+			Name:     "qs",
+			Version:  "6.5.2",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 195,
+					EndLine:   199,
+				},
+			},
+		},
+		{
+			ID:       "raw-body@2.3.3",
+			Name:     "raw-body",
+			Version:  "2.3.3",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 200,
+					EndLine:   210,
+				},
+			},
+		},
+		{
+			ID:       "safer-buffer@2.1.2",
+			Name:     "safer-buffer",
+			Version:  "2.1.2",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 211,
+					EndLine:   215,
+				},
+			},
+		},
+		{
+			ID:       "setprototypeof@1.1.0",
+			Name:     "setprototypeof",
+			Version:  "1.1.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 216,
+					EndLine:   220,
+				},
+			},
+		},
+		{
+			ID:       "statuses@1.4.0",
+			Name:     "statuses",
+			Version:  "1.4.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 221,
+					EndLine:   225,
+				},
+			},
+		},
+		{
+			ID:       "type-is@1.6.18",
+			Name:     "type-is",
+			Version:  "1.6.18",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 226,
+					EndLine:   234,
+				},
+			},
+		},
+		{
+			ID:       "unpipe@1.0.0",
+			Name:     "unpipe",
+			Version:  "1.0.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 235,
+					EndLine:   239,
+				},
+			},
+		},
 	}
 
 	// dependencies are filled manually
 	npmDeps = []types.Dependency{
-		{ID: "body-parser@1.18.3", DependsOn: []string{"bytes@3.0.0", "content-type@1.0.5", "debug@2.6.9", "depd@1.1.2", "http-errors@1.6.3", "iconv-lite@0.4.23", "on-finished@2.3.0", "qs@6.5.2", "raw-body@2.3.3", "type-is@1.6.18"}},
-		{ID: "debug@2.5.2", DependsOn: []string{"ms@0.7.2"}},
-		{ID: "debug@2.6.9", DependsOn: []string{"ms@2.0.0"}},
-		{ID: "finalhandler@1.1.1", DependsOn: []string{"debug@2.6.9", "encodeurl@1.0.2", "escape-html@1.0.3", "on-finished@2.3.0", "parseurl@1.3.3", "statuses@1.4.0", "unpipe@1.0.0"}},
-		{ID: "http-errors@1.6.3", DependsOn: []string{"depd@1.1.2", "inherits@2.0.3", "setprototypeof@1.1.0", "statuses@1.4.0"}},
-		{ID: "iconv-lite@0.4.23", DependsOn: []string{"safer-buffer@2.1.2"}},
-		{ID: "mime-types@2.1.35", DependsOn: []string{"mime-db@1.52.0"}},
-		{ID: "on-finished@2.3.0", DependsOn: []string{"ee-first@1.1.1"}},
-		{ID: "promise@8.3.0", DependsOn: []string{"asap@2.0.6"}},
-		{ID: "raw-body@2.3.3", DependsOn: []string{"bytes@3.0.0", "http-errors@1.6.3", "iconv-lite@0.4.23", "unpipe@1.0.0"}},
-		{ID: "type-is@1.6.18", DependsOn: []string{"media-typer@0.3.0", "mime-types@2.1.35"}},
+		{
+			ID: "body-parser@1.18.3",
+			DependsOn: []string{
+				"bytes@3.0.0",
+				"content-type@1.0.5",
+				"debug@2.6.9",
+				"depd@1.1.2",
+				"http-errors@1.6.3",
+				"iconv-lite@0.4.23",
+				"on-finished@2.3.0",
+				"qs@6.5.2",
+				"raw-body@2.3.3",
+				"type-is@1.6.18",
+			},
+		},
+		{
+			ID:        "debug@2.5.2",
+			DependsOn: []string{"ms@0.7.2"},
+		},
+		{
+			ID:        "debug@2.6.9",
+			DependsOn: []string{"ms@2.0.0"},
+		},
+		{
+			ID: "finalhandler@1.1.1",
+			DependsOn: []string{
+				"debug@2.6.9",
+				"encodeurl@1.0.2",
+				"escape-html@1.0.3",
+				"on-finished@2.3.0",
+				"parseurl@1.3.3",
+				"statuses@1.4.0",
+				"unpipe@1.0.0",
+			},
+		},
+		{
+			ID: "http-errors@1.6.3",
+			DependsOn: []string{
+				"depd@1.1.2",
+				"inherits@2.0.3",
+				"setprototypeof@1.1.0",
+				"statuses@1.4.0",
+			},
+		},
+		{
+			ID:        "iconv-lite@0.4.23",
+			DependsOn: []string{"safer-buffer@2.1.2"},
+		},
+		{
+			ID:        "mime-types@2.1.35",
+			DependsOn: []string{"mime-db@1.52.0"},
+		},
+		{
+			ID:        "on-finished@2.3.0",
+			DependsOn: []string{"ee-first@1.1.1"},
+		},
+		{
+			ID:        "promise@8.3.0",
+			DependsOn: []string{"asap@2.0.6"},
+		},
+		{
+			ID: "raw-body@2.3.3",
+			DependsOn: []string{
+				"bytes@3.0.0",
+				"http-errors@1.6.3",
+				"iconv-lite@0.4.23",
+				"unpipe@1.0.0",
+			},
+		},
+		{
+			ID: "type-is@1.6.18",
+			DependsOn: []string{
+				"media-typer@0.3.0",
+				"mime-types@2.1.35",
+			},
+		},
 	}
 
 	// ... and
@@ -69,37 +700,603 @@ var (
 	// npm i --lockfile-version 3
 	// same as npmV2Libs.
 	npmV2Libs = []types.Library{
-		{ID: "@babel/helper-string-parser@7.19.4", Name: "@babel/helper-string-parser", Version: "7.19.4", Dev: false, Indirect: false, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz"}}, Locations: []types.Location{{StartLine: 24, EndLine: 31}}},
-		{ID: "asap@2.0.6", Name: "asap", Version: "2.0.6", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz"}}, Locations: []types.Location{{StartLine: 32, EndLine: 37}}},
-		{ID: "body-parser@1.18.3", Name: "body-parser", Version: "1.18.3", Dev: false, Indirect: false, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz"}}, Locations: []types.Location{{StartLine: 38, EndLine: 57}}},
-		{ID: "bytes@3.0.0", Name: "bytes", Version: "3.0.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz"}}, Locations: []types.Location{{StartLine: 71, EndLine: 78}}},
-		{ID: "content-type@1.0.5", Name: "content-type", Version: "1.0.5", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz"}}, Locations: []types.Location{{StartLine: 79, EndLine: 86}}},
-		{ID: "debug@2.5.2", Name: "debug", Version: "2.5.2", Dev: true, Indirect: false, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/debug/-/debug-2.5.2.tgz"}}, Locations: []types.Location{{StartLine: 87, EndLine: 95}}},
-		{ID: "debug@2.6.9", Name: "debug", Version: "2.6.9", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"}}, Locations: []types.Location{{StartLine: 58, EndLine: 65}, {StartLine: 145, EndLine: 152}}},
-		{ID: "depd@1.1.2", Name: "depd", Version: "1.1.2", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz"}}, Locations: []types.Location{{StartLine: 102, EndLine: 109}}},
-		{ID: "ee-first@1.1.1", Name: "ee-first", Version: "1.1.1", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"}}, Locations: []types.Location{{StartLine: 110, EndLine: 114}}},
-		{ID: "encodeurl@1.0.2", Name: "encodeurl", Version: "1.0.2", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz"}}, Locations: []types.Location{{StartLine: 115, EndLine: 122}}},
-		{ID: "escape-html@1.0.3", Name: "escape-html", Version: "1.0.3", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz"}}, Locations: []types.Location{{StartLine: 123, EndLine: 127}}},
-		{ID: "finalhandler@1.1.1", Name: "finalhandler", Version: "1.1.1", Dev: false, Indirect: false, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz"}}, Locations: []types.Location{{StartLine: 128, EndLine: 144}}},
-		{ID: "http-errors@1.6.3", Name: "http-errors", Version: "1.6.3", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz"}}, Locations: []types.Location{{StartLine: 158, EndLine: 171}}},
-		{ID: "iconv-lite@0.4.23", Name: "iconv-lite", Version: "0.4.23", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz"}}, Locations: []types.Location{{StartLine: 172, EndLine: 182}}},
-		{ID: "inherits@2.0.3", Name: "inherits", Version: "2.0.3", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"}}, Locations: []types.Location{{StartLine: 183, EndLine: 187}}},
-		{ID: "media-typer@0.3.0", Name: "media-typer", Version: "0.3.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"}}, Locations: []types.Location{{StartLine: 188, EndLine: 195}}},
-		{ID: "mime-db@1.52.0", Name: "mime-db", Version: "1.52.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"}}, Locations: []types.Location{{StartLine: 196, EndLine: 203}}},
-		{ID: "mime-types@2.1.35", Name: "mime-types", Version: "2.1.35", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"}}, Locations: []types.Location{{StartLine: 204, EndLine: 214}}},
-		{ID: "ms@0.7.2", Name: "ms", Version: "0.7.2", Dev: true, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz"}}, Locations: []types.Location{{StartLine: 96, EndLine: 101}}},
-		{ID: "ms@1.0.0", Name: "ms", Version: "1.0.0", Dev: false, Indirect: false, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/ms/-/ms-1.0.0.tgz"}}, Locations: []types.Location{{StartLine: 215, EndLine: 219}}},
-		{ID: "ms@2.0.0", Name: "ms", Version: "2.0.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"}}, Locations: []types.Location{{StartLine: 66, EndLine: 70}, {StartLine: 153, EndLine: 157}}},
-		{ID: "on-finished@2.3.0", Name: "on-finished", Version: "2.3.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz"}}, Locations: []types.Location{{StartLine: 220, EndLine: 230}}},
-		{ID: "parseurl@1.3.3", Name: "parseurl", Version: "1.3.3", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"}}, Locations: []types.Location{{StartLine: 231, EndLine: 238}}},
-		{ID: "promise@8.3.0", Name: "promise", Version: "8.3.0", Dev: false, Indirect: false, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz"}}, Locations: []types.Location{{StartLine: 239, EndLine: 247}}},
-		{ID: "qs@6.5.2", Name: "qs", Version: "6.5.2", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz"}}, Locations: []types.Location{{StartLine: 248, EndLine: 255}}},
-		{ID: "raw-body@2.3.3", Name: "raw-body", Version: "2.3.3", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz"}}, Locations: []types.Location{{StartLine: 256, EndLine: 269}}},
-		{ID: "safer-buffer@2.1.2", Name: "safer-buffer", Version: "2.1.2", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"}}, Locations: []types.Location{{StartLine: 270, EndLine: 274}}},
-		{ID: "setprototypeof@1.1.0", Name: "setprototypeof", Version: "1.1.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz"}}, Locations: []types.Location{{StartLine: 275, EndLine: 279}}},
-		{ID: "statuses@1.4.0", Name: "statuses", Version: "1.4.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz"}}, Locations: []types.Location{{StartLine: 280, EndLine: 287}}},
-		{ID: "type-is@1.6.18", Name: "type-is", Version: "1.6.18", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz"}}, Locations: []types.Location{{StartLine: 288, EndLine: 299}}},
-		{ID: "unpipe@1.0.0", Name: "unpipe", Version: "1.0.0", Dev: false, Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"}}, Locations: []types.Location{{StartLine: 300, EndLine: 307}}},
+		{
+			ID:       "@babel/helper-string-parser@7.19.4",
+			Name:     "@babel/helper-string-parser",
+			Version:  "7.19.4",
+			Dev:      false,
+			Indirect: false,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 24,
+					EndLine:   31,
+				},
+			},
+		},
+		{
+			ID:       "asap@2.0.6",
+			Name:     "asap",
+			Version:  "2.0.6",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 32,
+					EndLine:   37,
+				},
+			},
+		},
+		{
+			ID:       "body-parser@1.18.3",
+			Name:     "body-parser",
+			Version:  "1.18.3",
+			Dev:      false,
+			Indirect: false,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 38,
+					EndLine:   57,
+				},
+			},
+		},
+		{
+			ID:       "bytes@3.0.0",
+			Name:     "bytes",
+			Version:  "3.0.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 71,
+					EndLine:   78,
+				},
+			},
+		},
+		{
+			ID:       "content-type@1.0.5",
+			Name:     "content-type",
+			Version:  "1.0.5",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 79,
+					EndLine:   86,
+				},
+			},
+		},
+		{
+			ID:       "debug@2.5.2",
+			Name:     "debug",
+			Version:  "2.5.2",
+			Dev:      true,
+			Indirect: false,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/debug/-/debug-2.5.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 87,
+					EndLine:   95,
+				},
+			},
+		},
+		{
+			ID:       "debug@2.6.9",
+			Name:     "debug",
+			Version:  "2.6.9",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 58,
+					EndLine:   65,
+				},
+				{
+					StartLine: 145,
+					EndLine:   152,
+				},
+			},
+		},
+		{
+			ID:       "depd@1.1.2",
+			Name:     "depd",
+			Version:  "1.1.2",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 102,
+					EndLine:   109,
+				},
+			},
+		},
+		{
+			ID:       "ee-first@1.1.1",
+			Name:     "ee-first",
+			Version:  "1.1.1",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 110,
+					EndLine:   114,
+				},
+			},
+		},
+		{
+			ID:       "encodeurl@1.0.2",
+			Name:     "encodeurl",
+			Version:  "1.0.2",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 115,
+					EndLine:   122,
+				},
+			},
+		},
+		{
+			ID:       "escape-html@1.0.3",
+			Name:     "escape-html",
+			Version:  "1.0.3",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 123,
+					EndLine:   127,
+				},
+			},
+		},
+		{
+			ID:       "finalhandler@1.1.1",
+			Name:     "finalhandler",
+			Version:  "1.1.1",
+			Dev:      false,
+			Indirect: false,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 128,
+					EndLine:   144,
+				},
+			},
+		},
+		{
+			ID:       "http-errors@1.6.3",
+			Name:     "http-errors",
+			Version:  "1.6.3",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 158,
+					EndLine:   171,
+				},
+			},
+		},
+		{
+			ID:       "iconv-lite@0.4.23",
+			Name:     "iconv-lite",
+			Version:  "0.4.23",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 172,
+					EndLine:   182,
+				},
+			},
+		},
+		{
+			ID:       "inherits@2.0.3",
+			Name:     "inherits",
+			Version:  "2.0.3",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 183,
+					EndLine:   187,
+				},
+			},
+		},
+		{
+			ID:       "media-typer@0.3.0",
+			Name:     "media-typer",
+			Version:  "0.3.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 188,
+					EndLine:   195,
+				},
+			},
+		},
+		{
+			ID:       "mime-db@1.52.0",
+			Name:     "mime-db",
+			Version:  "1.52.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 196,
+					EndLine:   203,
+				},
+			},
+		},
+		{
+			ID:       "mime-types@2.1.35",
+			Name:     "mime-types",
+			Version:  "2.1.35",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 204,
+					EndLine:   214,
+				},
+			},
+		},
+		{
+			ID:       "ms@0.7.2",
+			Name:     "ms",
+			Version:  "0.7.2",
+			Dev:      true,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 96,
+					EndLine:   101,
+				},
+			},
+		},
+		{
+			ID:       "ms@1.0.0",
+			Name:     "ms",
+			Version:  "1.0.0",
+			Dev:      false,
+			Indirect: false,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/ms/-/ms-1.0.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 215,
+					EndLine:   219,
+				},
+			},
+		},
+		{
+			ID:       "ms@2.0.0",
+			Name:     "ms",
+			Version:  "2.0.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 66,
+					EndLine:   70,
+				},
+				{
+					StartLine: 153,
+					EndLine:   157,
+				},
+			},
+		},
+		{
+			ID:       "on-finished@2.3.0",
+			Name:     "on-finished",
+			Version:  "2.3.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 220,
+					EndLine:   230,
+				},
+			},
+		},
+		{
+			ID:       "parseurl@1.3.3",
+			Name:     "parseurl",
+			Version:  "1.3.3",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 231,
+					EndLine:   238,
+				},
+			},
+		},
+		{
+			ID:       "promise@8.3.0",
+			Name:     "promise",
+			Version:  "8.3.0",
+			Dev:      false,
+			Indirect: false,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 239,
+					EndLine:   247,
+				},
+			},
+		},
+		{
+			ID:       "qs@6.5.2",
+			Name:     "qs",
+			Version:  "6.5.2",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 248,
+					EndLine:   255,
+				},
+			},
+		},
+		{
+			ID:       "raw-body@2.3.3",
+			Name:     "raw-body",
+			Version:  "2.3.3",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 256,
+					EndLine:   269,
+				},
+			},
+		},
+		{
+			ID:       "safer-buffer@2.1.2",
+			Name:     "safer-buffer",
+			Version:  "2.1.2",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 270,
+					EndLine:   274,
+				},
+			},
+		},
+		{
+			ID:       "setprototypeof@1.1.0",
+			Name:     "setprototypeof",
+			Version:  "1.1.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 275,
+					EndLine:   279,
+				},
+			},
+		},
+		{
+			ID:       "statuses@1.4.0",
+			Name:     "statuses",
+			Version:  "1.4.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 280,
+					EndLine:   287,
+				},
+			},
+		},
+		{
+			ID:       "type-is@1.6.18",
+			Name:     "type-is",
+			Version:  "1.6.18",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 288,
+					EndLine:   299,
+				},
+			},
+		},
+		{
+			ID:       "unpipe@1.0.0",
+			Name:     "unpipe",
+			Version:  "1.0.0",
+			Dev:      false,
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 300,
+					EndLine:   307,
+				},
+			},
+		},
 	}
 
 	// docker run --name node --rm -it node@sha256:51dd437f31812df71108b81385e2945071ec813d5815fa3403855669c8f3432b sh
@@ -117,19 +1314,133 @@ var (
 	// npm update
 	// libraries are filled manually
 	npmV3WithWorkspaceLibs = []types.Library{
-		{ID: "debug@2.5.2", Name: "debug", Version: "2.5.2", Indirect: false, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/debug/-/debug-2.5.2.tgz"}}, Locations: []types.Location{{StartLine: 39, EndLine: 46}}},
-		{ID: "debug@2.6.9", Name: "debug", Version: "2.6.9", Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"}}, Locations: []types.Location{{StartLine: 31, EndLine: 38}}},
-		{ID: "function1@", Name: "function1", Version: "", Indirect: false, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "functions/func1"}}, Locations: []types.Location{{StartLine: 18, EndLine: 23}}},
-		{ID: "ms@0.7.2", Name: "ms", Version: "0.7.2", Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz"}}, Locations: []types.Location{{StartLine: 47, EndLine: 51}}},
-		{ID: "ms@2.0.0", Name: "ms", Version: "2.0.0", Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"}}, Locations: []types.Location{{StartLine: 56, EndLine: 60}}},
-		{ID: "nested_func@1.0.0", Name: "nested_func", Version: "1.0.0", Indirect: false, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "functions/nested_func"}}, Locations: []types.Location{{StartLine: 24, EndLine: 30}}},
+		{
+			ID:       "debug@2.5.2",
+			Name:     "debug",
+			Version:  "2.5.2",
+			Indirect: false,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/debug/-/debug-2.5.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 39,
+					EndLine:   46,
+				},
+			},
+		},
+		{
+			ID:       "debug@2.6.9",
+			Name:     "debug",
+			Version:  "2.6.9",
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 31,
+					EndLine:   38,
+				},
+			},
+		},
+		{
+			ID:       "function1",
+			Name:     "function1",
+			Version:  "",
+			Indirect: false,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "functions/func1",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 18,
+					EndLine:   23,
+				},
+			},
+		},
+		{
+			ID:       "ms@0.7.2",
+			Name:     "ms",
+			Version:  "0.7.2",
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 47,
+					EndLine:   51,
+				},
+			},
+		},
+		{
+			ID:       "ms@2.0.0",
+			Name:     "ms",
+			Version:  "2.0.0",
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 56,
+					EndLine:   60,
+				},
+			},
+		},
+		{
+			ID:       "nested_func@1.0.0",
+			Name:     "nested_func",
+			Version:  "1.0.0",
+			Indirect: false,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "functions/nested_func",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 24,
+					EndLine:   30,
+				},
+			},
+		},
 	}
 
 	npmV3WithWorkspaceDeps = []types.Dependency{
-		{ID: "debug@2.5.2", DependsOn: []string{"ms@0.7.2"}},
-		{ID: "debug@2.6.9", DependsOn: []string{"ms@2.0.0"}},
-		{ID: "function1@", DependsOn: []string{"nested_func@1.0.0"}},
-		{ID: "nested_func@1.0.0", DependsOn: []string{"debug@2.6.9"}},
+		{
+			ID:        "debug@2.5.2",
+			DependsOn: []string{"ms@0.7.2"},
+		},
+		{
+			ID:        "debug@2.6.9",
+			DependsOn: []string{"ms@2.0.0"},
+		},
+		{
+			ID:        "function1",
+			DependsOn: []string{"nested_func@1.0.0"},
+		},
+		{
+			ID:        "nested_func@1.0.0",
+			DependsOn: []string{"debug@2.6.9"},
+		},
 	}
 
 	// docker run --name node --rm -it node@sha256:51dd437f31812df71108b81385e2945071ec813d5815fa3403855669c8f3432b sh
@@ -139,13 +1450,70 @@ var (
 	// npm install --save debug@2.6.9 -w func1
 	// libraries are filled manually
 	npmV3WithoutRootDepsField = []types.Library{
-		{ID: "debug@2.6.9", Name: "debug", Version: "2.6.9", Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"}}, Locations: []types.Location{{StartLine: 22, EndLine: 29}}},
-		{ID: "func1@1.0.0", Name: "func1", Version: "1.0.0", Indirect: false, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "functions/func1"}}, Locations: []types.Location{{StartLine: 15, EndLine: 21}}},
-		{ID: "ms@2.0.0", Name: "ms", Version: "2.0.0", Indirect: true, ExternalReferences: []types.ExternalRef{{Type: types.RefOther, URL: "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"}}, Locations: []types.Location{{StartLine: 34, EndLine: 38}}},
+		{
+			ID:       "debug@2.6.9",
+			Name:     "debug",
+			Version:  "2.6.9",
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 22,
+					EndLine:   29,
+				},
+			},
+		},
+		{
+			ID:       "func1@1.0.0",
+			Name:     "func1",
+			Version:  "1.0.0",
+			Indirect: false,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "functions/func1",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 15,
+					EndLine:   21,
+				},
+			},
+		},
+		{
+			ID:       "ms@2.0.0",
+			Name:     "ms",
+			Version:  "2.0.0",
+			Indirect: true,
+			ExternalReferences: []types.ExternalRef{
+				{
+					Type: types.RefOther,
+					URL:  "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+				},
+			},
+			Locations: []types.Location{
+				{
+					StartLine: 34,
+					EndLine:   38,
+				},
+			},
+		},
 	}
 
 	npmV3WithoutRootDepsFieldDeps = []types.Dependency{
-		{ID: "debug@2.6.9", DependsOn: []string{"ms@2.0.0"}},
-		{ID: "func1@1.0.0", DependsOn: []string{"debug@2.6.9"}},
+		{
+			ID:        "debug@2.6.9",
+			DependsOn: []string{"ms@2.0.0"},
+		},
+		{
+			ID:        "func1@1.0.0",
+			DependsOn: []string{"debug@2.6.9"},
+		},
 	}
 )
diff --git a/pkg/dependency/parser/nodejs/packagejson/parse.go b/pkg/dependency/parser/nodejs/packagejson/parse.go
index f2558750c323..f4bf258f7aae 100644
--- a/pkg/dependency/parser/nodejs/packagejson/parse.go
+++ b/pkg/dependency/parser/nodejs/packagejson/parse.go
@@ -7,8 +7,9 @@ import (
 
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 )
 
 var nameRegexp = regexp.MustCompile(`^(@[A-Za-z0-9-._]+/)?[A-Za-z0-9-._]+$`)
@@ -51,7 +52,7 @@ func (p *Parser) Parse(r io.Reader) (Package, error) {
 	// Name and version fields are optional
 	// https://docs.npmjs.com/cli/v9/configuring-npm/package-json#name
 	if pkgJSON.Name != "" && pkgJSON.Version != "" {
-		id = utils.PackageID(pkgJSON.Name, pkgJSON.Version)
+		id = dependency.ID(ftypes.NodePkg, pkgJSON.Name, pkgJSON.Version)
 	}
 
 	return Package{
diff --git a/pkg/dependency/parser/nodejs/packagejson/parse_test.go b/pkg/dependency/parser/nodejs/packagejson/parse_test.go
index 9b925a525be3..4f04cebcc1ee 100644
--- a/pkg/dependency/parser/nodejs/packagejson/parse_test.go
+++ b/pkg/dependency/parser/nodejs/packagejson/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/nodejs/packagejson"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/nodejs/pnpm/parse.go b/pkg/dependency/parser/nodejs/pnpm/parse.go
index e36166f84782..9e93be6a89c1 100644
--- a/pkg/dependency/parser/nodejs/pnpm/parse.go
+++ b/pkg/dependency/parser/nodejs/pnpm/parse.go
@@ -9,7 +9,9 @@ import (
 	"gopkg.in/yaml.v3"
 
 	"github.com/aquasecurity/go-version/pkg/semver"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
@@ -40,10 +42,6 @@ func NewParser() types.Parser {
 	return &Parser{}
 }
 
-func (p *Parser) ID(name, version string) string {
-	return fmt.Sprintf("%s@%s", name, version)
-}
-
 func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) {
 	var lockFile LockFile
 	if err := yaml.NewDecoder(r).Decode(&lockFile); err != nil {
@@ -80,11 +78,11 @@ func (p *Parser) parse(lockVer float64, lockFile LockFile) ([]types.Library, []t
 		if name == "" {
 			name, version = parsePackage(depPath, lockVer)
 		}
-		pkgID := p.ID(name, version)
+		pkgID := packageID(name, version)
 
 		dependencies := make([]string, 0, len(info.Dependencies))
 		for depName, depVer := range info.Dependencies {
-			dependencies = append(dependencies, p.ID(depName, depVer))
+			dependencies = append(dependencies, packageID(depName, depVer))
 		}
 
 		libs = append(libs, types.Library{
@@ -178,3 +176,7 @@ func parseDepPath(depPath, versionSep string) (string, string) {
 	}
 	return name, version
 }
+
+func packageID(name, version string) string {
+	return dependency.ID(ftypes.Pnpm, name, version)
+}
diff --git a/pkg/dependency/parser/nodejs/pnpm/parse_test.go b/pkg/dependency/parser/nodejs/pnpm/parse_test.go
index 183c015a12d9..19851a2c21c0 100644
--- a/pkg/dependency/parser/nodejs/pnpm/parse_test.go
+++ b/pkg/dependency/parser/nodejs/pnpm/parse_test.go
@@ -9,7 +9,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/nodejs/pnpm/parse_testcase.go b/pkg/dependency/parser/nodejs/pnpm/parse_testcase.go
index 83ea044db638..2b776cd0a9c8 100644
--- a/pkg/dependency/parser/nodejs/pnpm/parse_testcase.go
+++ b/pkg/dependency/parser/nodejs/pnpm/parse_testcase.go
@@ -1,6 +1,6 @@
 package pnpm
 
-import "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+import "github.com/aquasecurity/trivy/pkg/dependency/types"
 
 var (
 	// docker run --name node --rm -it node:16-alpine sh
diff --git a/pkg/dependency/parser/nodejs/yarn/parse.go b/pkg/dependency/parser/nodejs/yarn/parse.go
index 0264afc7cefa..9b8394eb57c8 100644
--- a/pkg/dependency/parser/nodejs/yarn/parse.go
+++ b/pkg/dependency/parser/nodejs/yarn/parse.go
@@ -10,8 +10,9 @@ import (
 	"github.com/samber/lo"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
@@ -86,7 +87,7 @@ func parsePackagePatterns(target string) (packagename, protocol string, patterns
 	}
 	patterns = lo.Map(patternsSplit, func(pattern string, _ int) string {
 		_, _, version, _ := parsePattern(pattern)
-		return utils.PackageID(packagename, version)
+		return packageID(packagename, version)
 	})
 	return
 }
@@ -261,7 +262,7 @@ func parseDependency(line string) (string, error) {
 	if name, version, err := getDependency(line); err != nil {
 		return "", err
 	} else {
-		return utils.PackageID(name, version), nil
+		return packageID(name, version), nil
 	}
 }
 
@@ -286,7 +287,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 			continue
 		}
 
-		libID := utils.PackageID(lib.Name, lib.Version)
+		libID := packageID(lib.Name, lib.Version)
 		libs = append(libs, types.Library{
 			ID:        libID,
 			Name:      lib.Name,
@@ -314,3 +315,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 	deps := parseResults(patternIDs, dependsOn)
 	return libs, deps, nil
 }
+
+func packageID(name, version string) string {
+	return dependency.ID(ftypes.Yarn, name, version)
+}
diff --git a/pkg/dependency/parser/nodejs/yarn/parse_test.go b/pkg/dependency/parser/nodejs/yarn/parse_test.go
index 07a85a718fc0..90ce497ed9d8 100644
--- a/pkg/dependency/parser/nodejs/yarn/parse_test.go
+++ b/pkg/dependency/parser/nodejs/yarn/parse_test.go
@@ -9,7 +9,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParsePattern(t *testing.T) {
diff --git a/pkg/dependency/parser/nodejs/yarn/parse_testcase.go b/pkg/dependency/parser/nodejs/yarn/parse_testcase.go
index 4325b0e53ef6..e9d48c246f29 100644
--- a/pkg/dependency/parser/nodejs/yarn/parse_testcase.go
+++ b/pkg/dependency/parser/nodejs/yarn/parse_testcase.go
@@ -1,6 +1,6 @@
 package yarn
 
-import "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+import "github.com/aquasecurity/trivy/pkg/dependency/types"
 
 var (
 	yarnHappy = []types.Library{
diff --git a/pkg/dependency/parser/nuget/config/parse.go b/pkg/dependency/parser/nuget/config/parse.go
index ff6bafd31a03..6a43d30ddf94 100644
--- a/pkg/dependency/parser/nuget/config/parse.go
+++ b/pkg/dependency/parser/nuget/config/parse.go
@@ -5,8 +5,8 @@ import (
 
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
diff --git a/pkg/dependency/parser/nuget/config/parse_test.go b/pkg/dependency/parser/nuget/config/parse_test.go
index 229e09f50217..864246bc9c44 100644
--- a/pkg/dependency/parser/nuget/config/parse_test.go
+++ b/pkg/dependency/parser/nuget/config/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/nuget/config"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/nuget/lock/parse.go b/pkg/dependency/parser/nuget/lock/parse.go
index 8b3a238178d0..30205362db8a 100644
--- a/pkg/dependency/parser/nuget/lock/parse.go
+++ b/pkg/dependency/parser/nuget/lock/parse.go
@@ -6,8 +6,10 @@ import (
 	"github.com/liamg/jfather"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
@@ -51,7 +53,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 				continue
 			}
 
-			depId := utils.PackageID(packageName, packageContent.Resolved)
+			depId := packageID(packageName, packageContent.Resolved)
 
 			lib := types.Library{
 				ID:       depId,
@@ -70,7 +72,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 			var dependsOn []string
 
 			for depName := range packageContent.Dependencies {
-				dependsOn = append(dependsOn, utils.PackageID(depName, targetContent[depName].Resolved))
+				dependsOn = append(dependsOn, packageID(depName, targetContent[depName].Resolved))
 			}
 
 			if savedDependsOn, ok := depsMap[depId]; ok {
@@ -105,3 +107,7 @@ func (t *Dependency) UnmarshalJSONWithMetadata(node jfather.Node) error {
 	t.EndLine = node.Range().End.Line
 	return nil
 }
+
+func packageID(name, version string) string {
+	return dependency.ID(ftypes.NuGet, name, version)
+}
diff --git a/pkg/dependency/parser/nuget/lock/parse_test.go b/pkg/dependency/parser/nuget/lock/parse_test.go
index 18be1118c0a0..04ddb22244df 100644
--- a/pkg/dependency/parser/nuget/lock/parse_test.go
+++ b/pkg/dependency/parser/nuget/lock/parse_test.go
@@ -10,7 +10,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/nuget/lock/parse_testcase.go b/pkg/dependency/parser/nuget/lock/parse_testcase.go
index d6676e0bf6c5..699db9f8c037 100644
--- a/pkg/dependency/parser/nuget/lock/parse_testcase.go
+++ b/pkg/dependency/parser/nuget/lock/parse_testcase.go
@@ -1,6 +1,6 @@
 package lock
 
-import "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+import "github.com/aquasecurity/trivy/pkg/dependency/types"
 
 var (
 	// docker run --rm -i -t mcr.microsoft.com/dotnet/sdk:latest
diff --git a/pkg/dependency/parser/nuget/packagesprops/parse.go b/pkg/dependency/parser/nuget/packagesprops/parse.go
index 2471d6faf7db..5e4c6831d1a1 100644
--- a/pkg/dependency/parser/nuget/packagesprops/parse.go
+++ b/pkg/dependency/parser/nuget/packagesprops/parse.go
@@ -6,8 +6,10 @@ import (
 
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
@@ -44,7 +46,7 @@ func (p pkg) library() types.Library {
 	name = strings.TrimSpace(name)
 	version := strings.TrimSpace(p.Version)
 	return types.Library{
-		ID:      utils.PackageID(name, version),
+		ID:      dependency.ID(ftypes.NuGet, name, version),
 		Name:    name,
 		Version: version,
 	}
diff --git a/pkg/dependency/parser/nuget/packagesprops/parse_test.go b/pkg/dependency/parser/nuget/packagesprops/parse_test.go
index 8fcd6b7ae6ea..96a50716d7ef 100644
--- a/pkg/dependency/parser/nuget/packagesprops/parse_test.go
+++ b/pkg/dependency/parser/nuget/packagesprops/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	config "github.com/aquasecurity/trivy/pkg/dependency/parser/nuget/packagesprops"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/php/composer/parse.go b/pkg/dependency/parser/php/composer/parse.go
index 849a67941d34..1fbf4316db9a 100644
--- a/pkg/dependency/parser/php/composer/parse.go
+++ b/pkg/dependency/parser/php/composer/parse.go
@@ -9,8 +9,9 @@ import (
 	"golang.org/x/exp/maps"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
@@ -47,7 +48,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 	foundDeps := make(map[string][]string)
 	for _, pkg := range lockFile.Packages {
 		lib := types.Library{
-			ID:       utils.PackageID(pkg.Name, pkg.Version),
+			ID:       dependency.ID(ftypes.Composer, pkg.Name, pkg.Version),
 			Name:     pkg.Name,
 			Version:  pkg.Version,
 			Indirect: false, // composer.lock file doesn't have info about Direct/Indirect deps. Will think that all dependencies are Direct
diff --git a/pkg/dependency/parser/php/composer/parse_test.go b/pkg/dependency/parser/php/composer/parse_test.go
index c56c4d3aa8b6..8c80899bc4ba 100644
--- a/pkg/dependency/parser/php/composer/parse_test.go
+++ b/pkg/dependency/parser/php/composer/parse_test.go
@@ -1,7 +1,7 @@
 package composer
 
 import (
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	"os"
diff --git a/pkg/dependency/parser/python/packaging/parse.go b/pkg/dependency/parser/python/packaging/parse.go
index 50ef1d3ba5cd..41514872fbb7 100644
--- a/pkg/dependency/parser/python/packaging/parse.go
+++ b/pkg/dependency/parser/python/packaging/parse.go
@@ -9,7 +9,7 @@ import (
 
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
diff --git a/pkg/dependency/parser/python/packaging/parse_test.go b/pkg/dependency/parser/python/packaging/parse_test.go
index 7bbc890cf70b..ee08c5bdca82 100644
--- a/pkg/dependency/parser/python/packaging/parse_test.go
+++ b/pkg/dependency/parser/python/packaging/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/python/packaging"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/python/pip/parse.go b/pkg/dependency/parser/python/pip/parse.go
index ca7b412f3817..4d4f893d63c0 100644
--- a/pkg/dependency/parser/python/pip/parse.go
+++ b/pkg/dependency/parser/python/pip/parse.go
@@ -10,7 +10,7 @@ import (
 	"golang.org/x/text/transform"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
diff --git a/pkg/dependency/parser/python/pip/parse_test.go b/pkg/dependency/parser/python/pip/parse_test.go
index aea0c7d6e7a6..a3a183f94a8e 100644
--- a/pkg/dependency/parser/python/pip/parse_test.go
+++ b/pkg/dependency/parser/python/pip/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/python/pip/parse_testcase.go b/pkg/dependency/parser/python/pip/parse_testcase.go
index b33f8f54e398..45642d47f2fa 100644
--- a/pkg/dependency/parser/python/pip/parse_testcase.go
+++ b/pkg/dependency/parser/python/pip/parse_testcase.go
@@ -1,6 +1,6 @@
 package pip
 
-import "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+import "github.com/aquasecurity/trivy/pkg/dependency/types"
 
 var (
 	requirementsFlask = []types.Library{
diff --git a/pkg/dependency/parser/python/pipenv/parse.go b/pkg/dependency/parser/python/pipenv/parse.go
index 7b773fb1dbf6..70332764195e 100644
--- a/pkg/dependency/parser/python/pipenv/parse.go
+++ b/pkg/dependency/parser/python/pipenv/parse.go
@@ -7,7 +7,7 @@ import (
 	"github.com/liamg/jfather"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
@@ -39,9 +39,14 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 	var libs []types.Library
 	for pkgName, dependency := range lockFile.Default {
 		libs = append(libs, types.Library{
-			Name:      pkgName,
-			Version:   strings.TrimLeft(dependency.Version, "="),
-			Locations: []types.Location{{StartLine: dependency.StartLine, EndLine: dependency.EndLine}},
+			Name:    pkgName,
+			Version: strings.TrimLeft(dependency.Version, "="),
+			Locations: []types.Location{
+				{
+					StartLine: dependency.StartLine,
+					EndLine:   dependency.EndLine,
+				},
+			},
 		})
 	}
 	return libs, nil, nil
diff --git a/pkg/dependency/parser/python/pipenv/parse_test.go b/pkg/dependency/parser/python/pipenv/parse_test.go
index 9390680f65d7..03fbe573ee7b 100644
--- a/pkg/dependency/parser/python/pipenv/parse_test.go
+++ b/pkg/dependency/parser/python/pipenv/parse_test.go
@@ -10,7 +10,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/python/pipenv/parse_testcase.go b/pkg/dependency/parser/python/pipenv/parse_testcase.go
index 718301bfc21e..6a611944d3eb 100644
--- a/pkg/dependency/parser/python/pipenv/parse_testcase.go
+++ b/pkg/dependency/parser/python/pipenv/parse_testcase.go
@@ -1,6 +1,6 @@
 package pipenv
 
-import "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+import "github.com/aquasecurity/trivy/pkg/dependency/types"
 
 var (
 	// docker run --name pipenv --rm -it python:3.9-alpine sh
diff --git a/pkg/dependency/parser/python/poetry/parse.go b/pkg/dependency/parser/python/poetry/parse.go
index 38dcac2e1870..e476b8c18d93 100644
--- a/pkg/dependency/parser/python/poetry/parse.go
+++ b/pkg/dependency/parser/python/poetry/parse.go
@@ -8,8 +8,9 @@ import (
 	"golang.org/x/xerrors"
 
 	version "github.com/aquasecurity/go-pep440-version"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
@@ -50,7 +51,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 			continue
 		}
 
-		pkgID := utils.PackageID(pkg.Name, pkg.Version)
+		pkgID := packageID(pkg.Name, pkg.Version)
 		libs = append(libs, types.Library{
 			ID:      pkgID,
 			Name:    pkg.Name,
@@ -124,7 +125,7 @@ func parseDependency(name string, versRange any, libVersions map[string][]string
 		if matched, err := matchVersion(ver, vRange); err != nil {
 			return "", xerrors.Errorf("failed to match version for %s: %w", name, err)
 		} else if matched {
-			return utils.PackageID(name, ver), nil
+			return packageID(name, ver), nil
 		}
 	}
 	return "", xerrors.Errorf("no matched version found for %q", name)
@@ -153,3 +154,7 @@ func normalizePkgName(name string) string {
 	name = strings.ReplaceAll(name, ".", "-") // e.g. https://github.com/python-poetry/poetry/blob/c8945eb110aeda611cc6721565d7ad0c657d453a/poetry.lock#L816
 	return name
 }
+
+func packageID(name, ver string) string {
+	return dependency.ID(ftypes.Poetry, name, ver)
+}
diff --git a/pkg/dependency/parser/python/poetry/parse_test.go b/pkg/dependency/parser/python/poetry/parse_test.go
index 91518be8aa1f..c02999a8eff8 100644
--- a/pkg/dependency/parser/python/poetry/parse_test.go
+++ b/pkg/dependency/parser/python/poetry/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParser_Parse(t *testing.T) {
diff --git a/pkg/dependency/parser/python/poetry/parse_testcase.go b/pkg/dependency/parser/python/poetry/parse_testcase.go
index 440131b6fd3f..c6511c0bd089 100644
--- a/pkg/dependency/parser/python/poetry/parse_testcase.go
+++ b/pkg/dependency/parser/python/poetry/parse_testcase.go
@@ -1,6 +1,6 @@
 package poetry
 
-import "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+import "github.com/aquasecurity/trivy/pkg/dependency/types"
 
 var (
 	// docker run --name pipenv --rm -it python@sha256:e1141f10176d74d1a0e87a7c0a0a5a98dd98ec5ac12ce867768f40c6feae2fd9 sh
diff --git a/pkg/dependency/parser/ruby/bundler/parse.go b/pkg/dependency/parser/ruby/bundler/parse.go
index 24b05d4aca38..e8cd538e0da6 100644
--- a/pkg/dependency/parser/ruby/bundler/parse.go
+++ b/pkg/dependency/parser/ruby/bundler/parse.go
@@ -8,8 +8,9 @@ import (
 	"golang.org/x/exp/maps"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
@@ -47,13 +48,18 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 			version := strings.Trim(s[1], "()")          // drop parentheses
 			version = strings.SplitN(version, "-", 2)[0] // drop platform (e.g. 1.13.6-x86_64-linux => 1.13.6)
 			name := s[0]
-			pkgID = utils.PackageID(name, version)
+			pkgID = packageID(name, version)
 			libs[name] = types.Library{
-				ID:        pkgID,
-				Name:      name,
-				Version:   version,
-				Indirect:  true,
-				Locations: []types.Location{{StartLine: lineNum, EndLine: lineNum}},
+				ID:       pkgID,
+				Name:     name,
+				Version:  version,
+				Indirect: true,
+				Locations: []types.Location{
+					{
+						StartLine: lineNum,
+						EndLine:   lineNum,
+					},
+				},
 			}
 		}
 		// Parse dependency graph
@@ -89,7 +95,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 		dependsOn = make([]string, 0)
 		for _, pkgName := range dep.DependsOn {
 			if lib, ok := libs[pkgName]; ok {
-				dependsOn = append(dependsOn, utils.PackageID(pkgName, lib.Version))
+				dependsOn = append(dependsOn, packageID(pkgName, lib.Version))
 			}
 		}
 		deps[i].DependsOn = dependsOn
@@ -134,3 +140,7 @@ func parseDirectDeps(scanner *bufio.Scanner) []string {
 	}
 	return deps
 }
+
+func packageID(name, version string) string {
+	return dependency.ID(ftypes.Bundler, name, version)
+}
diff --git a/pkg/dependency/parser/ruby/bundler/parse_test.go b/pkg/dependency/parser/ruby/bundler/parse_test.go
index 3732b97f0f35..6b2b27dd5e3a 100644
--- a/pkg/dependency/parser/ruby/bundler/parse_test.go
+++ b/pkg/dependency/parser/ruby/bundler/parse_test.go
@@ -9,7 +9,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/ruby/bundler"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 var (
diff --git a/pkg/dependency/parser/ruby/gemspec/parse.go b/pkg/dependency/parser/ruby/gemspec/parse.go
index 65c94c6d2cef..e458f6bacd0e 100644
--- a/pkg/dependency/parser/ruby/gemspec/parse.go
+++ b/pkg/dependency/parser/ruby/gemspec/parse.go
@@ -8,7 +8,7 @@ import (
 
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
@@ -99,7 +99,8 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) (libs []types.Library, deps []types.D
 			Name:    name,
 			Version: version,
 			License: license,
-		}}, nil, nil
+		},
+	}, nil, nil
 }
 
 func findSubString(re *regexp.Regexp, line, name string) string {
diff --git a/pkg/dependency/parser/ruby/gemspec/parse_test.go b/pkg/dependency/parser/ruby/gemspec/parse_test.go
index 41610a68f45d..586c0ee6b941 100644
--- a/pkg/dependency/parser/ruby/gemspec/parse_test.go
+++ b/pkg/dependency/parser/ruby/gemspec/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/ruby/gemspec"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func TestParse(t *testing.T) {
diff --git a/pkg/dependency/parser/rust/binary/parse.go b/pkg/dependency/parser/rust/binary/parse.go
index 71acbb2d5dce..5ddb2cf20be6 100644
--- a/pkg/dependency/parser/rust/binary/parse.go
+++ b/pkg/dependency/parser/rust/binary/parse.go
@@ -5,8 +5,9 @@ import (
 	rustaudit "github.com/microsoft/go-rustaudit"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
@@ -48,7 +49,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 		if pkg.Kind != rustaudit.Runtime {
 			continue
 		}
-		pkgID := utils.PackageID(pkg.Name, pkg.Version)
+		pkgID := packageID(pkg.Name, pkg.Version)
 		libs = append(libs, types.Library{
 			ID:       pkgID,
 			Name:     pkg.Name,
@@ -60,7 +61,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 		for _, dep_idx := range pkg.Dependencies {
 			dep := info.Packages[dep_idx]
 			if dep.Kind == rustaudit.Runtime {
-				childDeps = append(childDeps, utils.PackageID(dep.Name, dep.Version))
+				childDeps = append(childDeps, packageID(dep.Name, dep.Version))
 			}
 		}
 		if len(childDeps) > 0 {
@@ -73,3 +74,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 
 	return libs, deps, nil
 }
+
+func packageID(name, version string) string {
+	return dependency.ID(ftypes.RustBinary, name, version)
+}
diff --git a/pkg/dependency/parser/rust/binary/parse_test.go b/pkg/dependency/parser/rust/binary/parse_test.go
index a3e2b381cc2d..63b0a3a705d1 100644
--- a/pkg/dependency/parser/rust/binary/parse_test.go
+++ b/pkg/dependency/parser/rust/binary/parse_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/rust/binary"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 // Test binaries generated from cargo-auditable test fixture
diff --git a/pkg/dependency/parser/rust/cargo/naive_pkg_parser.go b/pkg/dependency/parser/rust/cargo/naive_pkg_parser.go
index f12134a7891f..775dae13477f 100644
--- a/pkg/dependency/parser/rust/cargo/naive_pkg_parser.go
+++ b/pkg/dependency/parser/rust/cargo/naive_pkg_parser.go
@@ -5,8 +5,6 @@ import (
 	"fmt"
 	"io"
 	"strings"
-
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
 )
 
 type pkgPosition struct {
@@ -40,7 +38,7 @@ func (parser *naivePkgParser) parse() map[string]pkgPosition {
 		switch {
 		case strings.HasPrefix(strings.TrimSpace(line), "["):
 			if currentPkg.name != "" {
-				pkgId := utils.PackageID(currentPkg.name, currentPkg.version)
+				pkgId := packageID(currentPkg.name, currentPkg.version)
 				currentPkg.setEndPositionIfEmpty(lineNum - 1)
 				idx[pkgId] = currentPkg.position
 			}
diff --git a/pkg/dependency/parser/rust/cargo/parse.go b/pkg/dependency/parser/rust/cargo/parse.go
index e9426f5069d2..282e25152d04 100644
--- a/pkg/dependency/parser/rust/cargo/parse.go
+++ b/pkg/dependency/parser/rust/cargo/parse.go
@@ -9,8 +9,9 @@ import (
 	"github.com/samber/lo"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
@@ -54,14 +55,19 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency,
 	var libs []types.Library
 	var deps []types.Dependency
 	for _, pkg := range lockfile.Packages {
-		pkgID := utils.PackageID(pkg.Name, pkg.Version)
+		pkgID := packageID(pkg.Name, pkg.Version)
 		lib := types.Library{
 			ID:      pkgID,
 			Name:    pkg.Name,
 			Version: pkg.Version,
 		}
 		if pos, ok := lineNumIdx[pkgID]; ok {
-			lib.Locations = []types.Location{{StartLine: pos.start, EndLine: pos.end}}
+			lib.Locations = []types.Location{
+				{
+					StartLine: pos.start,
+					EndLine:   pos.end,
+				},
+			}
 		}
 
 		libs = append(libs, lib)
@@ -96,11 +102,11 @@ func parseDependencies(pkgId string, pkg cargoPkg, pkgs map[string]cargoPkg) *ty
 				log.Logger.Debugf("can't find version for %s", name)
 				continue
 			}
-			dependOn = append(dependOn, utils.PackageID(name, version.Version))
+			dependOn = append(dependOn, packageID(name, version.Version))
 		// 2: non-unique dependency in new lock file
 		// 3: old lock file
 		case 2, 3:
-			dependOn = append(dependOn, utils.PackageID(fields[0], fields[1]))
+			dependOn = append(dependOn, packageID(fields[0], fields[1]))
 		default:
 			log.Logger.Debugf("wrong dependency format for %s", pkgDep)
 			continue
@@ -116,3 +122,7 @@ func parseDependencies(pkgId string, pkg cargoPkg, pkgs map[string]cargoPkg) *ty
 		return nil
 	}
 }
+
+func packageID(name, version string) string {
+	return dependency.ID(ftypes.Cargo, name, version)
+}
diff --git a/pkg/dependency/parser/rust/cargo/parse_test.go b/pkg/dependency/parser/rust/cargo/parse_test.go
index 0bf494702696..903cf0190a7d 100644
--- a/pkg/dependency/parser/rust/cargo/parse_test.go
+++ b/pkg/dependency/parser/rust/cargo/parse_test.go
@@ -11,7 +11,7 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 var (
diff --git a/pkg/dependency/parser/swift/cocoapods/parse.go b/pkg/dependency/parser/swift/cocoapods/parse.go
index 3caeeec04366..7b2a580fd74c 100644
--- a/pkg/dependency/parser/swift/cocoapods/parse.go
+++ b/pkg/dependency/parser/swift/cocoapods/parse.go
@@ -8,8 +8,10 @@ import (
 	"golang.org/x/xerrors"
 	"gopkg.in/yaml.v3"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
@@ -72,7 +74,7 @@ func (Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, er
 		var dependsOn []string
 		// find versions for child dependencies
 		for _, childDep := range childDeps {
-			dependsOn = append(dependsOn, utils.PackageID(childDep, parsedDeps[childDep].Version))
+			dependsOn = append(dependsOn, packageID(childDep, parsedDeps[childDep].Version))
 		}
 		deps = append(deps, types.Dependency{
 			ID:        parsedDeps[dep].ID,
@@ -99,10 +101,14 @@ func parseDep(dep string) (types.Library, error) {
 	name := ss[0]
 	version := strings.Trim(strings.TrimSpace(ss[1]), "()")
 	lib := types.Library{
-		ID:      utils.PackageID(name, version),
+		ID:      packageID(name, version),
 		Name:    name,
 		Version: version,
 	}
 
 	return lib, nil
 }
+
+func packageID(name, version string) string {
+	return dependency.ID(ftypes.Cocoapods, name, version)
+}
diff --git a/pkg/dependency/parser/swift/cocoapods/parse_test.go b/pkg/dependency/parser/swift/cocoapods/parse_test.go
index c1628e566af2..3a0823338713 100644
--- a/pkg/dependency/parser/swift/cocoapods/parse_test.go
+++ b/pkg/dependency/parser/swift/cocoapods/parse_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/swift/cocoapods"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
diff --git a/pkg/dependency/parser/swift/swift/parse.go b/pkg/dependency/parser/swift/swift/parse.go
index 19f5943b6b78..daeb8d3ef243 100644
--- a/pkg/dependency/parser/swift/swift/parse.go
+++ b/pkg/dependency/parser/swift/swift/parse.go
@@ -9,8 +9,9 @@ import (
 	"github.com/samber/lo"
 	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	"github.com/aquasecurity/trivy/pkg/dependency"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
 	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
@@ -51,7 +52,7 @@ func (Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, er
 		version := lo.Ternary(pin.State.Version != "", pin.State.Version, pin.State.Branch)
 
 		libs = append(libs, types.Library{
-			ID:      utils.PackageID(name, version),
+			ID:      dependency.ID(ftypes.Swift, name, version),
 			Name:    name,
 			Version: version,
 			Locations: []types.Location{
diff --git a/pkg/dependency/parser/swift/swift/parse_test.go b/pkg/dependency/parser/swift/swift/parse_test.go
index 530186c419d0..b1d3d127a85a 100644
--- a/pkg/dependency/parser/swift/swift/parse_test.go
+++ b/pkg/dependency/parser/swift/swift/parse_test.go
@@ -1,7 +1,7 @@
 package swift
 
 import (
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/stretchr/testify/assert"
 	"os"
 	"testing"
diff --git a/pkg/dependency/parser/utils/utils.go b/pkg/dependency/parser/utils/utils.go
index 8c128e0cfb4b..e89bc4ca3b65 100644
--- a/pkg/dependency/parser/utils/utils.go
+++ b/pkg/dependency/parser/utils/utils.go
@@ -6,7 +6,7 @@ import (
 
 	"golang.org/x/exp/maps"
 
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 )
 
 func UniqueStrings(ss []string) []string {
@@ -65,7 +65,3 @@ func MergeMaps(parent, child map[string]string) map[string]string {
 	}
 	return newParent
 }
-
-func PackageID(name, version string) string {
-	return fmt.Sprintf("%s@%s", name, version)
-}
diff --git a/pkg/dependency/parser/utils/utils_test.go b/pkg/dependency/parser/utils/utils_test.go
index 6292aad9f07c..ca8c8ab67568 100644
--- a/pkg/dependency/parser/utils/utils_test.go
+++ b/pkg/dependency/parser/utils/utils_test.go
@@ -1,7 +1,7 @@
 package utils
 
 import (
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	"github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/stretchr/testify/require"
 	"testing"
 )
diff --git a/pkg/dependency/parser/types/types.go b/pkg/dependency/types/types.go
similarity index 100%
rename from pkg/dependency/parser/types/types.go
rename to pkg/dependency/types/types.go
diff --git a/pkg/fanal/analyzer/language/analyze.go b/pkg/fanal/analyzer/language/analyze.go
index b60eebafc5d0..d31cbb2dfb0f 100644
--- a/pkg/fanal/analyzer/language/analyze.go
+++ b/pkg/fanal/analyzer/language/analyze.go
@@ -6,7 +6,7 @@ import (
 
 	"golang.org/x/xerrors"
 
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/digest"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/types"
diff --git a/pkg/fanal/analyzer/language/analyze_test.go b/pkg/fanal/analyzer/language/analyze_test.go
index b1dc6654db86..260c86b59ae8 100644
--- a/pkg/fanal/analyzer/language/analyze_test.go
+++ b/pkg/fanal/analyzer/language/analyze_test.go
@@ -9,11 +9,11 @@ import (
 	"github.com/stretchr/testify/require"
 	"golang.org/x/xerrors"
 
-	xio "github.com/aquasecurity/trivy/pkg/x/io"
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
 	"github.com/aquasecurity/trivy/pkg/fanal/types"
+	xio "github.com/aquasecurity/trivy/pkg/x/io"
 )
 
 type mockParser struct {
diff --git a/pkg/fanal/analyzer/language/dart/pub/pubspec.go b/pkg/fanal/analyzer/language/dart/pub/pubspec.go
index 285dbc74f963..ab924cafd191 100644
--- a/pkg/fanal/analyzer/language/dart/pub/pubspec.go
+++ b/pkg/fanal/analyzer/language/dart/pub/pubspec.go
@@ -14,9 +14,9 @@ import (
 	"golang.org/x/xerrors"
 	"gopkg.in/yaml.v3"
 
+	"github.com/aquasecurity/trivy/pkg/dependency"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/dart/pub"
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
-	"github.com/aquasecurity/trivy/pkg/dependency/parser/utils"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
 	"github.com/aquasecurity/trivy/pkg/fanal/types"
@@ -166,7 +166,7 @@ func parsePubSpecYaml(r io.Reader) (string, []string, error) {
 	// save only dependencies names
 	dependsOn := maps.Keys(spec.Dependencies)
 
-	return utils.PackageID(spec.Name, spec.Version), dependsOn, nil
+	return dependency.ID(types.Pub, spec.Name, spec.Version), dependsOn, nil
 }
 
 func (a pubSpecLockAnalyzer) Required(filePath string, _ os.FileInfo) bool {
diff --git a/pkg/fanal/analyzer/language/dotnet/nuget/nuget.go b/pkg/fanal/analyzer/language/dotnet/nuget/nuget.go
index 5374d8789d90..6411f4d1bc8d 100644
--- a/pkg/fanal/analyzer/language/dotnet/nuget/nuget.go
+++ b/pkg/fanal/analyzer/language/dotnet/nuget/nuget.go
@@ -14,7 +14,7 @@ import (
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/nuget/config"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/nuget/lock"
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
 	"github.com/aquasecurity/trivy/pkg/fanal/types"
diff --git a/pkg/fanal/analyzer/language/golang/mod/mod.go b/pkg/fanal/analyzer/language/golang/mod/mod.go
index beb2f9f1213e..5aa0ae2293fe 100644
--- a/pkg/fanal/analyzer/language/golang/mod/mod.go
+++ b/pkg/fanal/analyzer/language/golang/mod/mod.go
@@ -19,7 +19,7 @@ import (
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/golang/mod"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/golang/sum"
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
 	"github.com/aquasecurity/trivy/pkg/fanal/types"
diff --git a/pkg/fanal/analyzer/language/nodejs/npm/npm.go b/pkg/fanal/analyzer/language/nodejs/npm/npm.go
index ecca16aa1996..c5dd5d26eed0 100644
--- a/pkg/fanal/analyzer/language/nodejs/npm/npm.go
+++ b/pkg/fanal/analyzer/language/nodejs/npm/npm.go
@@ -13,7 +13,7 @@ import (
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/nodejs/npm"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/nodejs/packagejson"
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
 	"github.com/aquasecurity/trivy/pkg/fanal/types"
diff --git a/pkg/fanal/analyzer/language/nodejs/pkg/pkg.go b/pkg/fanal/analyzer/language/nodejs/pkg/pkg.go
index d8c87b9b60ad..aabc4da6b6a2 100644
--- a/pkg/fanal/analyzer/language/nodejs/pkg/pkg.go
+++ b/pkg/fanal/analyzer/language/nodejs/pkg/pkg.go
@@ -6,7 +6,7 @@ import (
 	"path/filepath"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/nodejs/packagejson"
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
 	"github.com/aquasecurity/trivy/pkg/fanal/types"
diff --git a/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go b/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go
index 86e185d95360..1cbd6b4896aa 100644
--- a/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go
+++ b/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go
@@ -20,7 +20,7 @@ import (
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/nodejs/packagejson"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/nodejs/yarn"
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/detector/library/compare/npm"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
diff --git a/pkg/fanal/analyzer/language/php/composer/composer.go b/pkg/fanal/analyzer/language/php/composer/composer.go
index 8e795468b88f..d0244514a0e7 100644
--- a/pkg/fanal/analyzer/language/php/composer/composer.go
+++ b/pkg/fanal/analyzer/language/php/composer/composer.go
@@ -15,7 +15,7 @@ import (
 	"golang.org/x/xerrors"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/php/composer"
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
 	"github.com/aquasecurity/trivy/pkg/fanal/types"
diff --git a/pkg/fanal/analyzer/language/python/packaging/packaging.go b/pkg/fanal/analyzer/language/python/packaging/packaging.go
index b5e505140fd0..c45a4f5aef0f 100644
--- a/pkg/fanal/analyzer/language/python/packaging/packaging.go
+++ b/pkg/fanal/analyzer/language/python/packaging/packaging.go
@@ -16,7 +16,7 @@ import (
 	"golang.org/x/xerrors"
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/python/packaging"
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
 	"github.com/aquasecurity/trivy/pkg/fanal/types"
diff --git a/pkg/fanal/analyzer/language/python/poetry/poetry.go b/pkg/fanal/analyzer/language/python/poetry/poetry.go
index 0dc95b428ca8..90897e8f12ba 100644
--- a/pkg/fanal/analyzer/language/python/poetry/poetry.go
+++ b/pkg/fanal/analyzer/language/python/poetry/poetry.go
@@ -12,7 +12,7 @@ import (
 
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/python/poetry"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/python/pyproject"
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
 	"github.com/aquasecurity/trivy/pkg/fanal/types"
diff --git a/pkg/fanal/analyzer/language/rust/cargo/cargo.go b/pkg/fanal/analyzer/language/rust/cargo/cargo.go
index c9baf7f4f12b..f487ba0c46e8 100644
--- a/pkg/fanal/analyzer/language/rust/cargo/cargo.go
+++ b/pkg/fanal/analyzer/language/rust/cargo/cargo.go
@@ -20,7 +20,7 @@ import (
 	"github.com/aquasecurity/go-version/pkg/semver"
 	goversion "github.com/aquasecurity/go-version/pkg/version"
 	"github.com/aquasecurity/trivy/pkg/dependency/parser/rust/cargo"
-	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/parser/types"
+	godeptypes "github.com/aquasecurity/trivy/pkg/dependency/types"
 	"github.com/aquasecurity/trivy/pkg/detector/library/compare"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer/language"
diff --git a/pkg/fanal/analyzer/sbom/sbom_test.go b/pkg/fanal/analyzer/sbom/sbom_test.go
index 096d0ececec5..c6f5b4b33701 100644
--- a/pkg/fanal/analyzer/sbom/sbom_test.go
+++ b/pkg/fanal/analyzer/sbom/sbom_test.go
@@ -124,6 +124,7 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) {
 						Libraries: types.Packages{
 							{
 								FilePath: "opt/bitnami/elasticsearch/modules/apm/elastic-apm-agent-1.36.0.jar",
+								ID:       "co.elastic.apm:apm-agent:1.36.0",
 								Name:     "co.elastic.apm:apm-agent",
 								Version:  "1.36.0",
 								Identifier: types.PkgIdentifier{
@@ -138,6 +139,7 @@ func Test_sbomAnalyzer_Analyze(t *testing.T) {
 							},
 							{
 								FilePath: "opt/bitnami/elasticsearch/modules/apm/elastic-apm-agent-1.36.0.jar",
+								ID:       "co.elastic.apm:apm-agent-cached-lookup-key:1.36.0",
 								Name:     "co.elastic.apm:apm-agent-cached-lookup-key",
 								Version:  "1.36.0",
 								Identifier: types.PkgIdentifier{
diff --git a/pkg/fanal/artifact/image/remote_sbom_test.go b/pkg/fanal/artifact/image/remote_sbom_test.go
index 3e445fc0b9ee..c9255c2057bd 100644
--- a/pkg/fanal/artifact/image/remote_sbom_test.go
+++ b/pkg/fanal/artifact/image/remote_sbom_test.go
@@ -70,7 +70,7 @@ func TestArtifact_InspectRekorAttestation(t *testing.T) {
 			putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{
 				{
 					Args: cache.ArtifactCachePutBlobArgs{
-						BlobID: "sha256:5c9fad635c53ddafd1b5248fcd989b6c0f311c91a2fe2a206c7d67a715335fa1",
+						BlobID: "sha256:066b9998617ffb7dfe0a3219ac5c3efc1008a6223606fcf474e7d5c965e4e8da",
 						BlobInfo: types.BlobInfo{
 							SchemaVersion: types.BlobJSONSchemaVersion,
 							OS: types.OS{
@@ -81,6 +81,7 @@ func TestArtifact_InspectRekorAttestation(t *testing.T) {
 								{
 									Packages: types.Packages{
 										{
+											ID:      "musl@1.2.3-r0",
 											Name:    "musl",
 											Version: "1.2.3-r0",
 											Identifier: types.PkgIdentifier{
@@ -119,9 +120,9 @@ func TestArtifact_InspectRekorAttestation(t *testing.T) {
 			want: types.ArtifactReference{
 				Name: "test/image:10",
 				Type: types.ArtifactCycloneDX,
-				ID:   "sha256:5c9fad635c53ddafd1b5248fcd989b6c0f311c91a2fe2a206c7d67a715335fa1",
+				ID:   "sha256:066b9998617ffb7dfe0a3219ac5c3efc1008a6223606fcf474e7d5c965e4e8da",
 				BlobIDs: []string{
-					"sha256:5c9fad635c53ddafd1b5248fcd989b6c0f311c91a2fe2a206c7d67a715335fa1",
+					"sha256:066b9998617ffb7dfe0a3219ac5c3efc1008a6223606fcf474e7d5c965e4e8da",
 				},
 			},
 		},
@@ -169,7 +170,7 @@ func TestArtifact_InspectRekorAttestation(t *testing.T) {
 				return
 			}
 			require.NoError(t, err, tt.name)
-			got.CycloneDX = nil
+			got.BOM = nil
 			assert.Equal(t, tt.want, got)
 		})
 	}
@@ -222,7 +223,7 @@ func TestArtifact_inspectOCIReferrerSBOM(t *testing.T) {
 			putBlobExpectations: []cache.ArtifactCachePutBlobExpectation{
 				{
 					Args: cache.ArtifactCachePutBlobArgs{
-						BlobID: "sha256:fb9379cfc2aeff911515f04ca04300a8c0609c8a2a19b4a3b05a984802fa44eb",
+						BlobID: "sha256:7e0b5476a5ff5a10594ad1ed7566220fcc43ecff29b831236cb2e98e574a1d05",
 						BlobInfo: types.BlobInfo{
 							SchemaVersion: types.BlobJSONSchemaVersion,
 							Applications: []types.Application{
@@ -230,6 +231,7 @@ func TestArtifact_inspectOCIReferrerSBOM(t *testing.T) {
 									Type: types.GoBinary,
 									Libraries: types.Packages{
 										{
+											ID:      "github.com/opencontainers/go-digest@v1.0.0",
 											Name:    "github.com/opencontainers/go-digest",
 											Version: "v1.0.0",
 											Identifier: types.PkgIdentifier{
@@ -243,6 +245,7 @@ func TestArtifact_inspectOCIReferrerSBOM(t *testing.T) {
 											},
 										},
 										{
+											ID:      "golang.org/x/sync@v0.1.0",
 											Name:    "golang.org/x/sync",
 											Version: "v0.1.0",
 											Identifier: types.PkgIdentifier{
@@ -265,9 +268,9 @@ func TestArtifact_inspectOCIReferrerSBOM(t *testing.T) {
 			want: types.ArtifactReference{
 				Name: registry + "/test/image:10",
 				Type: types.ArtifactCycloneDX,
-				ID:   "sha256:fb9379cfc2aeff911515f04ca04300a8c0609c8a2a19b4a3b05a984802fa44eb",
+				ID:   "sha256:7e0b5476a5ff5a10594ad1ed7566220fcc43ecff29b831236cb2e98e574a1d05",
 				BlobIDs: []string{
-					"sha256:fb9379cfc2aeff911515f04ca04300a8c0609c8a2a19b4a3b05a984802fa44eb",
+					"sha256:7e0b5476a5ff5a10594ad1ed7566220fcc43ecff29b831236cb2e98e574a1d05",
 				},
 			},
 		},
@@ -309,7 +312,7 @@ func TestArtifact_inspectOCIReferrerSBOM(t *testing.T) {
 			}
 
 			require.NoError(t, err, tt.name)
-			got.CycloneDX = nil
+			got.BOM = nil
 			assert.Equal(t, tt.want, got)
 		})
 	}
diff --git a/pkg/fanal/artifact/sbom/sbom.go b/pkg/fanal/artifact/sbom/sbom.go
index e788e14b48db..2be0ce31e6a4 100644
--- a/pkg/fanal/artifact/sbom/sbom.go
+++ b/pkg/fanal/artifact/sbom/sbom.go
@@ -8,6 +8,7 @@ import (
 	"path/filepath"
 
 	"github.com/opencontainers/go-digest"
+	"github.com/samber/lo"
 	"golang.org/x/xerrors"
 
 	"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
@@ -57,7 +58,7 @@ func (a Artifact) Inspect(_ context.Context) (types.ArtifactReference, error) {
 
 	blobInfo := types.BlobInfo{
 		SchemaVersion: types.BlobJSONSchemaVersion,
-		OS:            bom.OS,
+		OS:            lo.FromPtr(bom.Metadata.OS),
 		PackageInfos:  bom.Packages,
 		Applications:  bom.Applications,
 	}
@@ -87,7 +88,7 @@ func (a Artifact) Inspect(_ context.Context) (types.ArtifactReference, error) {
 		BlobIDs: []string{cacheKey},
 
 		// Keep an original report
-		CycloneDX: bom.CycloneDX,
+		BOM: bom.BOM,
 	}, nil
 }
 
diff --git a/pkg/fanal/artifact/sbom/sbom_test.go b/pkg/fanal/artifact/sbom/sbom_test.go
index f0286d4c3198..3b26194cfa55 100644
--- a/pkg/fanal/artifact/sbom/sbom_test.go
+++ b/pkg/fanal/artifact/sbom/sbom_test.go
@@ -30,7 +30,7 @@ func TestArtifact_Inspect(t *testing.T) {
 			filePath: filepath.Join("testdata", "bom.json"),
 			putBlobExpectation: cache.ArtifactCachePutBlobExpectation{
 				Args: cache.ArtifactCachePutBlobArgs{
-					BlobID: "sha256:5d7b14f463a56006dd4060d04295afbda468350f2e5a786ae48d18fd02fb9a89",
+					BlobID: "sha256:f6d4bf4edf2818010ef009b6cd0f837c94dac3464d99e665470c8d05648478e3",
 					BlobInfo: types.BlobInfo{
 						SchemaVersion: types.BlobJSONSchemaVersion,
 						OS: types.OS{
@@ -41,6 +41,7 @@ func TestArtifact_Inspect(t *testing.T) {
 							{
 								Packages: types.Packages{
 									{
+										ID:         "musl@1.2.3-r0",
 										Name:       "musl",
 										Version:    "1.2.3-r0",
 										SrcName:    "musl",
@@ -74,6 +75,7 @@ func TestArtifact_Inspect(t *testing.T) {
 								FilePath: "app/composer/composer.lock",
 								Libraries: types.Packages{
 									{
+										ID:      "pear/log@1.13.1",
 										Name:    "pear/log",
 										Version: "1.13.1",
 										Layer: types.Layer{
@@ -90,7 +92,7 @@ func TestArtifact_Inspect(t *testing.T) {
 										},
 									},
 									{
-
+										ID:      "pear/pear_exception@v1.0.0",
 										Name:    "pear/pear_exception",
 										Version: "v1.0.0",
 										Layer: types.Layer{
@@ -113,6 +115,7 @@ func TestArtifact_Inspect(t *testing.T) {
 								FilePath: "app/gobinary/gobinary",
 								Libraries: types.Packages{
 									{
+										ID:      "github.com/package-url/packageurl-go@v0.1.1-0.20220203205134-d70459300c8a",
 										Name:    "github.com/package-url/packageurl-go",
 										Version: "v0.1.1-0.20220203205134-d70459300c8a",
 										Layer: types.Layer{
@@ -135,6 +138,7 @@ func TestArtifact_Inspect(t *testing.T) {
 								FilePath: "",
 								Libraries: types.Packages{
 									{
+										ID:      "org.codehaus.mojo:child-project:1.0",
 										Name:    "org.codehaus.mojo:child-project",
 										Version: "1.0",
 										Layer: types.Layer{
@@ -148,6 +152,7 @@ func TestArtifact_Inspect(t *testing.T) {
 												Name:      "child-project",
 												Version:   "1.0",
 											},
+											// Keep the original value
 											BOMRef: "pkg:maven/org.codehaus.mojo/child-project@1.0?file_path=app%2Fmaven%2Ftarget%2Fchild-project-1.0.jar",
 										},
 									},
@@ -158,6 +163,7 @@ func TestArtifact_Inspect(t *testing.T) {
 								FilePath: "",
 								Libraries: types.Packages{
 									{
+										ID:       "bootstrap@5.0.2",
 										Name:     "bootstrap",
 										Version:  "5.0.2",
 										Licenses: []string{"MIT"},
@@ -171,6 +177,7 @@ func TestArtifact_Inspect(t *testing.T) {
 												Name:    "bootstrap",
 												Version: "5.0.2",
 											},
+											// Keep the original value
 											BOMRef: "pkg:npm/bootstrap@5.0.2?file_path=app%2Fapp%2Fpackage.json",
 										},
 									},
@@ -184,9 +191,9 @@ func TestArtifact_Inspect(t *testing.T) {
 			want: types.ArtifactReference{
 				Name: filepath.Join("testdata", "bom.json"),
 				Type: types.ArtifactCycloneDX,
-				ID:   "sha256:5d7b14f463a56006dd4060d04295afbda468350f2e5a786ae48d18fd02fb9a89",
+				ID:   "sha256:f6d4bf4edf2818010ef009b6cd0f837c94dac3464d99e665470c8d05648478e3",
 				BlobIDs: []string{
-					"sha256:5d7b14f463a56006dd4060d04295afbda468350f2e5a786ae48d18fd02fb9a89",
+					"sha256:f6d4bf4edf2818010ef009b6cd0f837c94dac3464d99e665470c8d05648478e3",
 				},
 			},
 		},
@@ -195,7 +202,7 @@ func TestArtifact_Inspect(t *testing.T) {
 			filePath: filepath.Join("testdata", "sbom.cdx.intoto.jsonl"),
 			putBlobExpectation: cache.ArtifactCachePutBlobExpectation{
 				Args: cache.ArtifactCachePutBlobArgs{
-					BlobID: "sha256:5d7b14f463a56006dd4060d04295afbda468350f2e5a786ae48d18fd02fb9a89",
+					BlobID: "sha256:f6d4bf4edf2818010ef009b6cd0f837c94dac3464d99e665470c8d05648478e3",
 					BlobInfo: types.BlobInfo{
 						SchemaVersion: types.BlobJSONSchemaVersion,
 						OS: types.OS{
@@ -206,6 +213,7 @@ func TestArtifact_Inspect(t *testing.T) {
 							{
 								Packages: types.Packages{
 									{
+										ID:         "musl@1.2.3-r0",
 										Name:       "musl",
 										Version:    "1.2.3-r0",
 										SrcName:    "musl",
@@ -239,6 +247,7 @@ func TestArtifact_Inspect(t *testing.T) {
 								FilePath: "app/composer/composer.lock",
 								Libraries: types.Packages{
 									{
+										ID:      "pear/log@1.13.1",
 										Name:    "pear/log",
 										Version: "1.13.1",
 										Identifier: types.PkgIdentifier{
@@ -255,7 +264,7 @@ func TestArtifact_Inspect(t *testing.T) {
 										},
 									},
 									{
-
+										ID:      "pear/pear_exception@v1.0.0",
 										Name:    "pear/pear_exception",
 										Version: "v1.0.0",
 										Identifier: types.PkgIdentifier{
@@ -278,6 +287,7 @@ func TestArtifact_Inspect(t *testing.T) {
 								FilePath: "app/gobinary/gobinary",
 								Libraries: types.Packages{
 									{
+										ID:      "github.com/package-url/packageurl-go@v0.1.1-0.20220203205134-d70459300c8a",
 										Name:    "github.com/package-url/packageurl-go",
 										Version: "v0.1.1-0.20220203205134-d70459300c8a",
 										Identifier: types.PkgIdentifier{
@@ -300,6 +310,7 @@ func TestArtifact_Inspect(t *testing.T) {
 								FilePath: "",
 								Libraries: types.Packages{
 									{
+										ID:      "org.codehaus.mojo:child-project:1.0",
 										Name:    "org.codehaus.mojo:child-project",
 										Version: "1.0",
 										Identifier: types.PkgIdentifier{
@@ -309,6 +320,7 @@ func TestArtifact_Inspect(t *testing.T) {
 												Name:      "child-project",
 												Version:   "1.0",
 											},
+											// Keep the original value
 											BOMRef: "pkg:maven/org.codehaus.mojo/child-project@1.0?file_path=app%2Fmaven%2Ftarget%2Fchild-project-1.0.jar",
 										},
 										Layer: types.Layer{
@@ -323,6 +335,7 @@ func TestArtifact_Inspect(t *testing.T) {
 								FilePath: "",
 								Libraries: types.Packages{
 									{
+										ID:      "bootstrap@5.0.2",
 										Name:    "bootstrap",
 										Version: "5.0.2",
 										Identifier: types.PkgIdentifier{
@@ -331,6 +344,7 @@ func TestArtifact_Inspect(t *testing.T) {
 												Name:    "bootstrap",
 												Version: "5.0.2",
 											},
+											// Keep the original value
 											BOMRef: "pkg:npm/bootstrap@5.0.2?file_path=app%2Fapp%2Fpackage.json",
 										},
 										Licenses: []string{"MIT"},
@@ -349,9 +363,9 @@ func TestArtifact_Inspect(t *testing.T) {
 			want: types.ArtifactReference{
 				Name: filepath.Join("testdata", "sbom.cdx.intoto.jsonl"),
 				Type: types.ArtifactCycloneDX,
-				ID:   "sha256:5d7b14f463a56006dd4060d04295afbda468350f2e5a786ae48d18fd02fb9a89",
+				ID:   "sha256:f6d4bf4edf2818010ef009b6cd0f837c94dac3464d99e665470c8d05648478e3",
 				BlobIDs: []string{
-					"sha256:5d7b14f463a56006dd4060d04295afbda468350f2e5a786ae48d18fd02fb9a89",
+					"sha256:f6d4bf4edf2818010ef009b6cd0f837c94dac3464d99e665470c8d05648478e3",
 				},
 			},
 		},
@@ -368,16 +382,13 @@ func TestArtifact_Inspect(t *testing.T) {
 			filePath: filepath.Join("testdata", "os-only-bom.json"),
 			putBlobExpectation: cache.ArtifactCachePutBlobExpectation{
 				Args: cache.ArtifactCachePutBlobArgs{
-					BlobID: "sha256:033dc76e6daf7d8ba439d678dc7e33400687098f3e9f563f6975adf4eb440eee",
+					BlobID: "sha256:911a6c875617315c51971dddf19fa2d47d6132cd14e9c6a87deb074afaf07818",
 					BlobInfo: types.BlobInfo{
 						SchemaVersion: types.BlobJSONSchemaVersion,
 						OS: types.OS{
 							Family: "alpine",
 							Name:   "3.16.0",
 						},
-						PackageInfos: []types.PackageInfo{
-							{},
-						},
 					},
 				},
 				Returns: cache.ArtifactCachePutBlobReturns{
@@ -410,7 +421,7 @@ func TestArtifact_Inspect(t *testing.T) {
 			}
 
 			// Not compare the original CycloneDX report
-			got.CycloneDX = nil
+			got.BOM = nil
 
 			require.NoError(t, err)
 			assert.Equal(t, tt.want, got)
diff --git a/pkg/fanal/handler/unpackaged/unpackaged_test.go b/pkg/fanal/handler/unpackaged/unpackaged_test.go
index a37748101144..b466aa83282a 100644
--- a/pkg/fanal/handler/unpackaged/unpackaged_test.go
+++ b/pkg/fanal/handler/unpackaged/unpackaged_test.go
@@ -43,6 +43,7 @@ func Test_unpackagedHook_Handle(t *testing.T) {
 						FilePath: "go.mod",
 						Libraries: types.Packages{
 							{
+								ID:      "github.com/spf13/cobra@v1.5.0",
 								Name:    "github.com/spf13/cobra",
 								Version: "1.5.0",
 								Identifier: types.PkgIdentifier{
diff --git a/pkg/fanal/types/artifact.go b/pkg/fanal/types/artifact.go
index cd005c6582b1..86870d52ee40 100644
--- a/pkg/fanal/types/artifact.go
+++ b/pkg/fanal/types/artifact.go
@@ -10,6 +10,7 @@ import (
 	"github.com/samber/lo"
 
 	"github.com/aquasecurity/trivy/pkg/digest"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
 )
 
 type OS struct {
@@ -277,7 +278,7 @@ type ArtifactReference struct {
 	ImageMetadata ImageMetadata
 
 	// SBOM
-	CycloneDX *CycloneDX
+	BOM *core.BOM
 }
 
 type ImageMetadata struct {
diff --git a/pkg/fanal/types/sbom.go b/pkg/fanal/types/sbom.go
deleted file mode 100644
index 6a7298bc2605..000000000000
--- a/pkg/fanal/types/sbom.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package types
-
-// CycloneDX re-defines only necessary fields from cyclondx/cyclonedx-go
-// cf. https://github.com/CycloneDX/cyclonedx-go/blob/de6bc07025d148badc8f6699ccb556744a5f4070/cyclonedx.go#L58-L77
-//
-// The encoding/xml package that cyclondx-go depends on cannot be imported due to some limitations in TinyGo.
-// cf. https://tinygo.org/docs/reference/lang-support/stdlib/
-type CycloneDX struct {
-	// JSON specific fields
-	BOMFormat   string      `json:"bomFormat" xml:"-"`
-	SpecVersion SpecVersion `json:"specVersion" xml:"-"`
-
-	SerialNumber string      `json:"serialNumber,omitempty" xml:"serialNumber,attr,omitempty"`
-	Version      int         `json:"version" xml:"version,attr"`
-	Metadata     Metadata    `json:"metadata,omitempty" xml:"metadata,omitempty"`
-	Components   []Component `json:"components,omitempty" xml:"components>component,omitempty"`
-}
-
-type Metadata struct {
-	Timestamp string    `json:"timestamp,omitempty" xml:"timestamp,omitempty"`
-	Component Component `json:"component,omitempty" xml:"component,omitempty"`
-}
-
-type Component struct {
-	BOMRef     string        `json:"bom-ref,omitempty" xml:"bom-ref,attr,omitempty"`
-	MIMEType   string        `json:"mime-type,omitempty" xml:"mime-type,attr,omitempty"`
-	Type       ComponentType `json:"type" xml:"type,attr"`
-	Name       string        `json:"name" xml:"name"`
-	Group      string        `json:"group" xml:"group"`
-	Version    string        `json:"version,omitempty" xml:"version,omitempty"`
-	PackageURL string        `json:"purl,omitempty" xml:"purl,omitempty"`
-	Properties []Property    `json:"properties,omitempty" xml:"properties>property,omitempty"`
-}
-
-type Property struct {
-	Name  string `json:"name" xml:"name,attr"`
-	Value string `json:"value" xml:",chardata"`
-}
-
-type (
-	ComponentType string
-	SpecVersion   int
-)
diff --git a/pkg/k8s/report/cyclonedx.go b/pkg/k8s/report/cyclonedx.go
index 6e10d34e7e89..1d87769c29b2 100644
--- a/pkg/k8s/report/cyclonedx.go
+++ b/pkg/k8s/report/cyclonedx.go
@@ -5,14 +5,16 @@ import (
 	"io"
 
 	cdx "github.com/CycloneDX/cyclonedx-go"
+	"golang.org/x/xerrors"
 
-	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx/core"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
+	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx"
 )
 
 // CycloneDXWriter implements types.Writer
 type CycloneDXWriter struct {
 	encoder   cdx.BOMEncoder
-	marshaler *core.CycloneDX
+	marshaler cyclonedx.Marshaler
 }
 
 // NewCycloneDXWriter constract new CycloneDXWriter
@@ -22,11 +24,14 @@ func NewCycloneDXWriter(output io.Writer, format cdx.BOMFileFormat, appVersion s
 	encoder.SetEscapeHTML(false)
 	return CycloneDXWriter{
 		encoder:   encoder,
-		marshaler: core.NewCycloneDX(appVersion),
+		marshaler: cyclonedx.NewMarshaler(appVersion),
 	}
 }
 
-func (w CycloneDXWriter) Write(ctx context.Context, component *core.Component) error {
-	bom := w.marshaler.Marshal(ctx, component)
+func (w CycloneDXWriter) Write(ctx context.Context, component *core.BOM) error {
+	bom, err := w.marshaler.Marshal(ctx, component)
+	if err != nil {
+		return xerrors.Errorf("CycloneDX marshal error: %w", err)
+	}
 	return w.encoder.Encode(bom)
 }
diff --git a/pkg/k8s/report/report.go b/pkg/k8s/report/report.go
index dc0b44c7ab73..5de332a703bc 100644
--- a/pkg/k8s/report/report.go
+++ b/pkg/k8s/report/report.go
@@ -12,7 +12,7 @@ import (
 	"github.com/aquasecurity/trivy-kubernetes/pkg/artifacts"
 	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
-	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx/core"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
 	"github.com/aquasecurity/trivy/pkg/types"
 )
 
@@ -40,8 +40,8 @@ type Option struct {
 type Report struct {
 	SchemaVersion int `json:",omitempty"`
 	ClusterName   string
-	Resources     []Resource      `json:",omitempty"`
-	RootComponent *core.Component `json:"-"`
+	Resources     []Resource `json:",omitempty"`
+	BOM           *core.BOM  `json:"-"`
 	name          string
 }
 
@@ -201,7 +201,12 @@ func SeparateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
 }
 
 func rbacResource(misConfig Resource) bool {
-	return slices.Contains([]string{"Role", "RoleBinding", "ClusterRole", "ClusterRoleBinding"}, misConfig.Kind)
+	return slices.Contains([]string{
+		"Role",
+		"RoleBinding",
+		"ClusterRole",
+		"ClusterRoleBinding",
+	}, misConfig.Kind)
 }
 
 func infraResource(misConfig Resource) bool {
diff --git a/pkg/k8s/scanner/scanner.go b/pkg/k8s/scanner/scanner.go
index ff5018406b66..16ac301c9fa4 100644
--- a/pkg/k8s/scanner/scanner.go
+++ b/pkg/k8s/scanner/scanner.go
@@ -7,7 +7,6 @@ import (
 	"sort"
 	"strings"
 
-	cdx "github.com/CycloneDX/cyclonedx-go"
 	ms "github.com/mitchellh/mapstructure"
 	"github.com/package-url/packageurl-go"
 	"github.com/samber/lo"
@@ -25,14 +24,14 @@ import (
 	"github.com/aquasecurity/trivy/pkg/log"
 	"github.com/aquasecurity/trivy/pkg/parallel"
 	"github.com/aquasecurity/trivy/pkg/purl"
-	cyc "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx"
-	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx/core"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
+	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx"
 	"github.com/aquasecurity/trivy/pkg/scanner/local"
 	"github.com/aquasecurity/trivy/pkg/types"
 )
 
 const (
-	k8sCoreComponentNamespace = core.Namespace + "resource:"
+	k8sCoreComponentNamespace = cyclonedx.Namespace + "resource:"
 	k8sComponentType          = "Type"
 	k8sComponentName          = "Name"
 	k8sComponentNode          = "node"
@@ -71,13 +70,13 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact)
 	}()
 
 	if s.opts.Format == types.FormatCycloneDX {
-		rootComponent, err := clusterInfoToReportResources(artifactsData)
+		kbom, err := s.clusterInfoToReportResources(artifactsData)
 		if err != nil {
 			return report.Report{}, err
 		}
 		return report.Report{
 			SchemaVersion: 0,
-			RootComponent: rootComponent,
+			BOM:           kbom,
 		}, nil
 	}
 	var resourceArtifacts []*artifacts.Artifact
@@ -213,7 +212,6 @@ func (s *Scanner) filter(ctx context.Context, r types.Report, artifact *artifact
 }
 
 const (
-	golang                 = "golang"
 	oci                    = "oci"
 	kubelet                = "k8s.io/kubelet"
 	controlPlaneComponents = "ControlPlaneComponents"
@@ -225,7 +223,7 @@ const (
 func (s *Scanner) scanK8sVulns(ctx context.Context, artifactsData []*artifacts.Artifact) ([]report.Resource, error) {
 	var resources []report.Resource
 	var nodeName string
-	if nodeName = findNodeName(artifactsData); nodeName == "" {
+	if nodeName = s.findNodeName(artifactsData); nodeName == "" {
 		return resources, nil
 	}
 
@@ -357,7 +355,7 @@ func (s *Scanner) scanK8sVulns(ctx context.Context, artifactsData []*artifacts.A
 	return resources, nil
 }
 
-func findNodeName(allArtifact []*artifacts.Artifact) string {
+func (*Scanner) findNodeName(allArtifact []*artifacts.Artifact) string {
 	for _, artifact := range allArtifact {
 		if artifact.Kind != nodeComponents {
 			continue
@@ -367,25 +365,36 @@ func findNodeName(allArtifact []*artifacts.Artifact) string {
 	return ""
 }
 
-func clusterInfoToReportResources(allArtifact []*artifacts.Artifact) (*core.Component, error) {
+func (s *Scanner) clusterInfoToReportResources(allArtifact []*artifacts.Artifact) (*core.BOM, error) {
+	var rootComponent *core.Component
 	var coreComponents []*core.Component
-	var cInfo *core.Component
 
 	// Find the first node name to identify AKS cluster
 	var nodeName string
-	if nodeName = findNodeName(allArtifact); nodeName == "" {
+	if nodeName = s.findNodeName(allArtifact); nodeName == "" {
 		return nil, fmt.Errorf("failed to find node name")
 	}
 
+	kbom := core.NewBOM()
 	for _, artifact := range allArtifact {
 		switch artifact.Kind {
 		case controlPlaneComponents:
 			var comp bom.Component
-			err := ms.Decode(artifact.RawResource, &comp)
-			if err != nil {
+			if err := ms.Decode(artifact.RawResource, &comp); err != nil {
 				return nil, err
 			}
-			var imageComponents []*core.Component
+
+			controlPlane := &core.Component{
+				Name:       comp.Name,
+				Version:    comp.Version,
+				Type:       core.TypeApplication,
+				Properties: toProperties(comp.Properties, k8sCoreComponentNamespace),
+				PkgID: core.PkgID{
+					PURL: generatePURL(comp.Name, comp.Version, nodeName),
+				},
+			}
+			coreComponents = append(coreComponents, controlPlane)
+
 			for _, c := range comp.Containers {
 				name := fmt.Sprintf("%s/%s", c.Registry, c.Repository)
 				cDigest := c.Digest
@@ -399,67 +408,149 @@ func clusterInfoToReportResources(allArtifact []*artifacts.Artifact) (*core.Comp
 						fmt.Sprintf("%s@%s", name, cDigest),
 					},
 				}, ftypes.Package{})
-
 				if err != nil {
 					return nil, xerrors.Errorf("failed to create PURL: %w", err)
 				}
-				imageComponents = append(imageComponents, &core.Component{
-					PackageURL: imagePURL,
-					Type:       cdx.ComponentTypeContainer,
-					Name:       name,
-					Version:    cDigest,
+
+				imageComponent := &core.Component{
+					Type:    core.TypeContainer,
+					Name:    name,
+					Version: cDigest,
+					PkgID: core.PkgID{
+						PURL: imagePURL.Unwrap(),
+					},
 					Properties: []core.Property{
 						{
-							Name:  cyc.PropertyPkgID,
+							Name:  core.PropertyPkgID,
 							Value: fmt.Sprintf("%s:%s", name, ver),
 						},
 						{
-							Name:  cyc.PropertyPkgType,
+							Name:  core.PropertyPkgType,
 							Value: oci,
 						},
 					},
-				})
-			}
-			rootComponent := &core.Component{
-				Name:       comp.Name,
-				Version:    comp.Version,
-				Type:       cdx.ComponentTypeApplication,
-				Properties: toProperties(comp.Properties, k8sCoreComponentNamespace),
-				Components: imageComponents,
-				PackageURL: generatePURL(comp.Name, comp.Version, nodeName),
+				}
+				kbom.AddRelationship(controlPlane, imageComponent, core.RelationshipDependsOn)
 			}
-			coreComponents = append(coreComponents, rootComponent)
 		case nodeComponents:
 			var nf bom.NodeInfo
 			err := ms.Decode(artifact.RawResource, &nf)
 			if err != nil {
 				return nil, err
 			}
-			coreComponents = append(coreComponents, nodeComponent(nf))
+			coreComponents = append(coreComponents, s.nodeComponent(kbom, nf))
 		case clusterInfo:
 			var cf bom.ClusterInfo
-			err := ms.Decode(artifact.RawResource, &cf)
-			if err != nil {
+			if err := ms.Decode(artifact.RawResource, &cf); err != nil {
 				return nil, err
 			}
-			cInfo = &core.Component{
+			rootComponent = &core.Component{
+				Type:       core.TypePlatform,
 				Name:       cf.Name,
 				Version:    cf.Version,
 				Properties: toProperties(cf.Properties, k8sCoreComponentNamespace),
+				PkgID: core.PkgID{
+					PURL: generatePURL(cf.Name, cf.Version, nodeName),
+				},
+				Root: true,
 			}
+			kbom.AddComponent(rootComponent)
 		default:
 			return nil, fmt.Errorf("resource kind %s is not supported", artifact.Kind)
 		}
 	}
-	rootComponent := &core.Component{
-		Name:       cInfo.Name,
-		Version:    cInfo.Version,
-		Type:       cdx.ComponentTypePlatform,
-		Properties: cInfo.Properties,
-		Components: coreComponents,
-		PackageURL: generatePURL(cInfo.Name, cInfo.Version, nodeName),
+
+	for _, c := range coreComponents {
+		kbom.AddRelationship(rootComponent, c, core.RelationshipContains)
+	}
+
+	return kbom, nil
+}
+
+func (s *Scanner) nodeComponent(b *core.BOM, nf bom.NodeInfo) *core.Component {
+	osName, osVersion := osNameVersion(nf.OsImage)
+	runtimeName, runtimeVersion := runtimeNameVersion(nf.ContainerRuntimeVersion)
+	kubeletVersion := sanitizedVersion(nf.KubeletVersion)
+	properties := toProperties(nf.Properties, "")
+	properties = append(properties, toProperties(map[string]string{
+		k8sComponentType: k8sComponentNode,
+		k8sComponentName: nf.NodeName,
+	}, k8sCoreComponentNamespace)...)
+
+	nodeComponent := &core.Component{
+		Type:       core.TypePlatform,
+		Name:       nf.NodeName,
+		Properties: properties,
+	}
+
+	osComponent := &core.Component{
+		Type:    core.TypeOS,
+		Name:    osName,
+		Version: osVersion,
+		Properties: []core.Property{
+			{
+				Name:  "Class",
+				Value: string(types.ClassOSPkg),
+			},
+			{
+				Name:  "Type",
+				Value: osName,
+			},
+		},
+	}
+	b.AddRelationship(nodeComponent, osComponent, core.RelationshipContains)
+
+	appComponent := &core.Component{
+		Type: core.TypeApplication,
+		Name: nodeCoreComponents,
+	}
+	b.AddRelationship(nodeComponent, appComponent, core.RelationshipContains)
+
+	kubeletComponent := &core.Component{
+		Type:    core.TypeApplication,
+		Name:    kubelet,
+		Version: kubeletVersion,
+		Properties: []core.Property{
+			{
+				Name:      k8sComponentType,
+				Value:     k8sComponentNode,
+				Namespace: k8sCoreComponentNamespace,
+			},
+			{
+				Name:      k8sComponentName,
+				Value:     kubelet,
+				Namespace: k8sCoreComponentNamespace,
+			},
+		},
+		PkgID: core.PkgID{
+			PURL: generatePURL(kubelet, kubeletVersion, nf.NodeName),
+		},
+	}
+	b.AddRelationship(appComponent, kubeletComponent, core.RelationshipContains)
+
+	runtimeComponent := &core.Component{
+		Type:    core.TypeApplication,
+		Name:    runtimeName,
+		Version: runtimeVersion,
+		Properties: []core.Property{
+			{
+				Name:      k8sComponentType,
+				Value:     k8sComponentNode,
+				Namespace: k8sCoreComponentNamespace,
+			},
+			{
+				Name:      k8sComponentName,
+				Value:     runtimeName,
+				Namespace: k8sCoreComponentNamespace,
+			},
+		},
+		PkgID: core.PkgID{
+			PURL: packageurl.NewPackageURL(packageurl.TypeGolang, "", runtimeName, runtimeVersion, packageurl.Qualifiers{}, ""),
+		},
 	}
-	return rootComponent, nil
+	b.AddRelationship(appComponent, runtimeComponent, core.RelationshipContains)
+
+	return nodeComponent
 }
 
 func sanitizedVersion(ver string) string {
@@ -500,93 +591,6 @@ func runtimeNameVersion(name string) (string, string) {
 	return name, ver
 }
 
-func nodeComponent(nf bom.NodeInfo) *core.Component {
-	osName, osVersion := osNameVersion(nf.OsImage)
-	runtimeName, runtimeVersion := runtimeNameVersion(nf.ContainerRuntimeVersion)
-	kubeletVersion := sanitizedVersion(nf.KubeletVersion)
-	properties := toProperties(nf.Properties, "")
-	properties = append(properties, toProperties(map[string]string{
-		k8sComponentType: k8sComponentNode,
-		k8sComponentName: nf.NodeName,
-	}, k8sCoreComponentNamespace)...)
-	return &core.Component{
-		Type:       cdx.ComponentTypePlatform,
-		Name:       nf.NodeName,
-		Properties: properties,
-		Components: []*core.Component{
-			{
-				Type:    cdx.ComponentTypeOS,
-				Name:    osName,
-				Version: osVersion,
-				Properties: []core.Property{
-					{
-						Name:  "Class",
-						Value: string(types.ClassOSPkg),
-					},
-					{
-						Name:  "Type",
-						Value: osName,
-					},
-				},
-			},
-			{
-				Type: cdx.ComponentTypeApplication,
-				Name: nodeCoreComponents,
-				Properties: []core.Property{
-					{
-						Name:  "Class",
-						Value: string(types.ClassLangPkg),
-					},
-					{
-						Name:  "Type",
-						Value: golang,
-					},
-				},
-				Components: []*core.Component{
-					{
-						Type:    cdx.ComponentTypeApplication,
-						Name:    kubelet,
-						Version: kubeletVersion,
-						Properties: []core.Property{
-							{
-								Name:      k8sComponentType,
-								Value:     k8sComponentNode,
-								Namespace: k8sCoreComponentNamespace,
-							},
-							{
-								Name:      k8sComponentName,
-								Value:     kubelet,
-								Namespace: k8sCoreComponentNamespace,
-							},
-						},
-						PackageURL: generatePURL(kubelet, kubeletVersion, nf.NodeName),
-					},
-					{
-						Type:    cdx.ComponentTypeApplication,
-						Name:    runtimeName,
-						Version: runtimeVersion,
-						Properties: []core.Property{
-							{
-								Name:      k8sComponentType,
-								Value:     k8sComponentNode,
-								Namespace: k8sCoreComponentNamespace,
-							},
-							{
-								Name:      k8sComponentName,
-								Value:     runtimeName,
-								Namespace: k8sCoreComponentNamespace,
-							},
-						},
-						PackageURL: &purl.PackageURL{
-							PackageURL: *packageurl.NewPackageURL(golang, "", runtimeName, runtimeVersion, packageurl.Qualifiers{}, ""),
-						},
-					},
-				},
-			},
-		},
-	}
-}
-
 func toProperties(props map[string]string, namespace string) []core.Property {
 	properties := lo.MapToSlice(props, func(k, v string) core.Property {
 		return core.Property{
@@ -595,14 +599,16 @@ func toProperties(props map[string]string, namespace string) []core.Property {
 			Namespace: namespace,
 		}
 	})
+	if len(properties) == 0 {
+		return nil
+	}
 	sort.Slice(properties, func(i, j int) bool {
 		return properties[i].Name < properties[j].Name
 	})
 	return properties
 }
 
-func generatePURL(name, ver, nodeName string) *purl.PackageURL {
-
+func generatePURL(name, ver, nodeName string) *packageurl.PackageURL {
 	var namespace string
 	// Identify k8s distribution. An empty namespace means upstream.
 	if namespace = k8sNamespace(ver, nodeName); namespace == "" {
@@ -611,9 +617,7 @@ func generatePURL(name, ver, nodeName string) *purl.PackageURL {
 		namespace = ""
 	}
 
-	return &purl.PackageURL{
-		PackageURL: *packageurl.NewPackageURL(purl.TypeK8s, namespace, name, ver, nil, ""),
-	}
+	return packageurl.NewPackageURL(purl.TypeK8s, namespace, name, ver, nil, "")
 }
 
 func k8sNamespace(ver, nodeName string) string {
diff --git a/pkg/k8s/scanner/scanner_test.go b/pkg/k8s/scanner/scanner_test.go
index ff879d2ac14c..8c9850c12b76 100644
--- a/pkg/k8s/scanner/scanner_test.go
+++ b/pkg/k8s/scanner/scanner_test.go
@@ -2,10 +2,13 @@ package scanner
 
 import (
 	"context"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
+	"github.com/aquasecurity/trivy/pkg/uuid"
+	"github.com/stretchr/testify/require"
+	"golang.org/x/exp/maps"
 	"sort"
 	"testing"
 
-	cdx "github.com/CycloneDX/cyclonedx-go"
 	"github.com/package-url/packageurl-go"
 	"github.com/stretchr/testify/assert"
 
@@ -13,17 +16,15 @@ import (
 	cmd "github.com/aquasecurity/trivy/pkg/commands/artifact"
 	"github.com/aquasecurity/trivy/pkg/flag"
 	"github.com/aquasecurity/trivy/pkg/purl"
-	cyc "github.com/aquasecurity/trivy/pkg/sbom/cyclonedx"
-	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx/core"
 )
 
 func TestScanner_Scan(t *testing.T) {
 	flagOpts := flag.Options{ReportOptions: flag.ReportOptions{Format: "cyclonedx"}}
 	tests := []struct {
-		name        string
-		clusterName string
-		artifacts   []*artifacts.Artifact
-		want        *core.Component
+		name           string
+		clusterName    string
+		artifacts      []*artifacts.Artifact
+		wantComponents []*core.Component
 	}{
 		{
 			name:        "test cluster info with resources",
@@ -81,194 +82,190 @@ func TestScanner_Scan(t *testing.T) {
 					},
 				},
 			},
-			want: &core.Component{
-				Type:    cdx.ComponentTypePlatform,
-				Name:    "k8s.io/kubernetes",
-				Version: "1.21.1",
-				Properties: []core.Property{
-					{
-						Name:      "Name",
-						Value:     "kind-kind",
-						Namespace: k8sCoreComponentNamespace,
+			wantComponents: []*core.Component{
+				{
+					Type:    core.TypeApplication,
+					Name:    "github.com/containerd/containerd",
+					Version: "1.5.2",
+					Properties: []core.Property{
+						{
+							Name:      k8sComponentName,
+							Value:     "github.com/containerd/containerd",
+							Namespace: k8sCoreComponentNamespace,
+						},
+						{
+							Name:      k8sComponentType,
+							Value:     "node",
+							Namespace: k8sCoreComponentNamespace,
+						},
 					},
-					{
-						Name:      "Type",
-						Value:     "cluster",
-						Namespace: k8sCoreComponentNamespace,
+					PkgID: core.PkgID{
+						PURL: &packageurl.PackageURL{
+							Type:       "golang",
+							Name:       "github.com/containerd/containerd",
+							Version:    "1.5.2",
+							Qualifiers: packageurl.Qualifiers{},
+						},
+						BOMRef: "pkg:golang/github.com%2Fcontainerd%2Fcontainerd@1.5.2",
 					},
 				},
-				PackageURL: &purl.PackageURL{
-					PackageURL: packageurl.PackageURL{
-						Type:    purl.TypeK8s,
-						Name:    "k8s.io/kubernetes",
-						Version: "1.21.1",
+				{
+					Type:    core.TypeApplication,
+					Name:    "k8s.io/apiserver",
+					Version: "1.21.1",
+					PkgID: core.PkgID{
+						PURL: &packageurl.PackageURL{
+							Type:    purl.TypeK8s,
+							Name:    "k8s.io/apiserver",
+							Version: "1.21.1",
+						},
+						BOMRef: "pkg:k8s/k8s.io%2Fapiserver@1.21.1",
 					},
 				},
-				Components: []*core.Component{
-					{
-						Type:    cdx.ComponentTypeApplication,
-						Name:    "k8s.io/apiserver",
-						Version: "1.21.1",
-						PackageURL: &purl.PackageURL{
-							PackageURL: packageurl.PackageURL{
-								Type:    purl.TypeK8s,
-								Name:    "k8s.io/apiserver",
-								Version: "1.21.1",
-							},
+				{
+					Type:    core.TypeApplication,
+					Name:    "k8s.io/kubelet",
+					Version: "1.21.1",
+					Properties: []core.Property{
+						{
+							Name:      k8sComponentName,
+							Value:     "k8s.io/kubelet",
+							Namespace: k8sCoreComponentNamespace,
 						},
-						Properties: []core.Property{},
-						Components: []*core.Component{
-							{
-								Type:    cdx.ComponentTypeContainer,
-								Name:    "k8s.gcr.io/kube-apiserver",
-								Version: "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f",
-								PackageURL: &purl.PackageURL{
-									PackageURL: packageurl.PackageURL{
-										Type:    "oci",
-										Name:    "kube-apiserver",
-										Version: "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f",
-										Qualifiers: packageurl.Qualifiers{
-											{
-												Key:   "repository_url",
-												Value: "k8s.gcr.io/kube-apiserver",
-											},
-										},
-									},
-								},
-								Properties: []core.Property{
-									{
-										Name:  cyc.PropertyPkgID,
-										Value: "k8s.gcr.io/kube-apiserver:1.21.1",
-									},
-									{
-										Name:  cyc.PropertyPkgType,
-										Value: "oci",
-									},
-								},
-							},
+						{
+							Name:      k8sComponentType,
+							Value:     "node",
+							Namespace: k8sCoreComponentNamespace,
 						},
 					},
-					{
-						Type: cdx.ComponentTypePlatform,
-						Name: "kind-control-plane",
-						Properties: []core.Property{
-							{
-								Name:  "Architecture",
-								Value: "arm64",
-							},
-							{
-								Name:  "HostName",
-								Value: "kind-control-plane",
-							},
-							{
-								Name:  "KernelVersion",
-								Value: "6.2.15-300.fc38.aarch64",
-							},
-							{
-								Name:  "NodeRole",
-								Value: "master",
-							},
-							{
-								Name:  "OperatingSystem",
-								Value: "linux",
-							},
-							{
-								Name:      k8sComponentName,
-								Value:     "kind-control-plane",
-								Namespace: k8sCoreComponentNamespace,
-							},
-							{
-								Name:      k8sComponentType,
-								Value:     "node",
-								Namespace: k8sCoreComponentNamespace,
-							},
+					PkgID: core.PkgID{
+						PURL: &packageurl.PackageURL{
+							Type:    "k8s",
+							Name:    "k8s.io/kubelet",
+							Version: "1.21.1",
 						},
-						Components: []*core.Component{
-							{
-								Type:    cdx.ComponentTypeOS,
-								Name:    "ubuntu",
-								Version: "21.04",
-								Properties: []core.Property{
-									{
-										Name:      "Class",
-										Value:     "os-pkgs",
-										Namespace: "",
-									},
-									{
-										Name:      "Type",
-										Value:     "ubuntu",
-										Namespace: "",
-									},
-								},
-							},
-							{
-								Type: cdx.ComponentTypeApplication,
-								Name: "node-core-components",
-								Properties: []core.Property{
-									{
-										Name:      "Class",
-										Value:     "lang-pkgs",
-										Namespace: "",
-									},
-									{
-										Name:      "Type",
-										Value:     "golang",
-										Namespace: "",
-									},
-								},
-								Components: []*core.Component{
-									{
-										Type:    cdx.ComponentTypeApplication,
-										Name:    "k8s.io/kubelet",
-										Version: "1.21.1",
-										Properties: []core.Property{
-											{
-												Name:      k8sComponentType,
-												Value:     "node",
-												Namespace: k8sCoreComponentNamespace,
-											},
-											{
-												Name:      k8sComponentName,
-												Value:     "k8s.io/kubelet",
-												Namespace: k8sCoreComponentNamespace,
-											},
-										},
-										PackageURL: &purl.PackageURL{
-											PackageURL: packageurl.PackageURL{
-												Type:    "k8s",
-												Name:    "k8s.io/kubelet",
-												Version: "1.21.1",
-											},
-										},
-									},
-									{
-										Type:    cdx.ComponentTypeApplication,
-										Name:    "github.com/containerd/containerd",
-										Version: "1.5.2",
-										Properties: []core.Property{
-											{
-												Name:      k8sComponentType,
-												Value:     "node",
-												Namespace: k8sCoreComponentNamespace,
-											},
-											{
-												Name:      k8sComponentName,
-												Value:     "github.com/containerd/containerd",
-												Namespace: k8sCoreComponentNamespace,
-											},
-										},
-										PackageURL: &purl.PackageURL{
-											PackageURL: packageurl.PackageURL{
-												Type:       "golang",
-												Name:       "github.com/containerd/containerd",
-												Version:    "1.5.2",
-												Qualifiers: packageurl.Qualifiers{},
-											},
-										},
-									},
+						BOMRef: "pkg:k8s/k8s.io%2Fkubelet@1.21.1",
+					},
+				},
+				{
+					Type: core.TypeApplication,
+					Name: "node-core-components",
+					PkgID: core.PkgID{
+						BOMRef: "3ff14136-e09f-4df9-80ea-000000000006",
+					},
+				},
+				{
+					Type:    core.TypeContainer,
+					Name:    "k8s.gcr.io/kube-apiserver",
+					Version: "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f",
+					PkgID: core.PkgID{
+						PURL: &packageurl.PackageURL{
+							Type:    "oci",
+							Name:    "kube-apiserver",
+							Version: "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f",
+							Qualifiers: packageurl.Qualifiers{
+								{
+									Key:   "repository_url",
+									Value: "k8s.gcr.io/kube-apiserver",
 								},
 							},
 						},
+						BOMRef: "pkg:oci/kube-apiserver@sha256%3A18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f?repository_url=k8s.gcr.io%2Fkube-apiserver",
+					},
+					Properties: []core.Property{
+						{
+							Name:  core.PropertyPkgID,
+							Value: "k8s.gcr.io/kube-apiserver:1.21.1",
+						},
+						{
+							Name:  core.PropertyPkgType,
+							Value: "oci",
+						},
+					},
+				},
+				{
+					Type:    core.TypeOS,
+					Name:    "ubuntu",
+					Version: "21.04",
+					Properties: []core.Property{
+						{
+							Name:      "Class",
+							Value:     "os-pkgs",
+							Namespace: "",
+						},
+						{
+							Name:      "Type",
+							Value:     "ubuntu",
+							Namespace: "",
+						},
+					},
+					PkgID: core.PkgID{
+						BOMRef: "3ff14136-e09f-4df9-80ea-000000000005",
+					},
+				},
+				{
+					Type:    core.TypePlatform,
+					Root:    true,
+					Name:    "k8s.io/kubernetes",
+					Version: "1.21.1",
+					Properties: []core.Property{
+						{
+							Name:      "Name",
+							Value:     "kind-kind",
+							Namespace: k8sCoreComponentNamespace,
+						},
+						{
+							Name:      "Type",
+							Value:     "cluster",
+							Namespace: k8sCoreComponentNamespace,
+						},
+					},
+					PkgID: core.PkgID{
+						PURL: &packageurl.PackageURL{
+							Type:    purl.TypeK8s,
+							Name:    "k8s.io/kubernetes",
+							Version: "1.21.1",
+						},
+						BOMRef: "pkg:k8s/k8s.io%2Fkubernetes@1.21.1",
+					},
+				},
+				{
+					Type: core.TypePlatform,
+					Name: "kind-control-plane",
+					Properties: []core.Property{
+						{
+							Name:  "Architecture",
+							Value: "arm64",
+						},
+						{
+							Name:  "HostName",
+							Value: "kind-control-plane",
+						},
+						{
+							Name:  "KernelVersion",
+							Value: "6.2.15-300.fc38.aarch64",
+						},
+						{
+							Name:      k8sComponentName,
+							Value:     "kind-control-plane",
+							Namespace: k8sCoreComponentNamespace,
+						},
+						{
+							Name:  "NodeRole",
+							Value: "master",
+						},
+						{
+							Name:  "OperatingSystem",
+							Value: "linux",
+						},
+						{
+							Name:      k8sComponentType,
+							Value:     "node",
+							Namespace: k8sCoreComponentNamespace,
+						},
+					},
+					PkgID: core.PkgID{
+						BOMRef: "3ff14136-e09f-4df9-80ea-000000000004",
 					},
 				},
 			},
@@ -276,34 +273,34 @@ func TestScanner_Scan(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			ctx := context.TODO()
+			ctx := context.Background()
+			uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d")
+
 			runner, err := cmd.NewRunner(ctx, flagOpts)
 			assert.NoError(t, err)
+
 			scanner := NewScanner(tt.clusterName, runner, flagOpts)
 			got, err := scanner.Scan(ctx, tt.artifacts)
-			sortNodeComponents(got.RootComponent)
-			sortNodeComponents(tt.want)
-			assert.Equal(t, tt.want, got.RootComponent)
-		})
-	}
-}
+			require.NoError(t, err)
 
-func sortNodeComponents(component *core.Component) {
-	nodeComp := findComponentByName(component, "node-core-components")
-	sort.Slice(nodeComp.Components, func(i, j int) bool {
-		return nodeComp.Components[i].Name < nodeComp.Components[j].Name
-	})
-}
+			gotComponents := maps.Values(got.BOM.Components())
+			require.Equal(t, len(tt.wantComponents), len(gotComponents))
 
-func findComponentByName(component *core.Component, compName string) *core.Component {
-	if component.Name == compName {
-		return component
-	}
-	var fComp *core.Component
-	for _, comp := range component.Components {
-		fComp = findComponentByName(comp, compName)
+			sort.Slice(gotComponents, func(i, j int) bool {
+				switch {
+				case gotComponents[i].Type != gotComponents[j].Type:
+					return gotComponents[i].Type < gotComponents[j].Type
+				case gotComponents[i].Name != gotComponents[j].Name:
+					return gotComponents[i].Name < gotComponents[j].Name
+				default:
+					return gotComponents[i].Version < gotComponents[j].Version
+				}
+			})
+			for i, want := range tt.wantComponents {
+				assert.EqualExportedValues(t, *want, *gotComponents[i], want.Name)
+			}
+		})
 	}
-	return fComp
 }
 
 func TestTestOsNameVersion(t *testing.T) {
@@ -546,7 +543,8 @@ func TestFindNodeName(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got := findNodeName(tt.artifacts)
+			s := Scanner{}
+			got := s.findNodeName(tt.artifacts)
 			assert.Equal(t, tt.want, got)
 		})
 	}
diff --git a/pkg/k8s/writer.go b/pkg/k8s/writer.go
index 0e218b38f30f..13204501fb59 100644
--- a/pkg/k8s/writer.go
+++ b/pkg/k8s/writer.go
@@ -46,7 +46,7 @@ func Write(ctx context.Context, k8sreport report.Report, option report.Option) e
 		return nil
 	case types.FormatCycloneDX:
 		w := report.NewCycloneDXWriter(option.Output, cdx.BOMFileFormatJSON, option.APIVersion)
-		return w.Write(ctx, k8sreport.RootComponent)
+		return w.Write(ctx, k8sreport.BOM)
 	}
 	return nil
 }
diff --git a/pkg/module/module.go b/pkg/module/module.go
index 4395feeddeb3..3d670999e1b9 100644
--- a/pkg/module/module.go
+++ b/pkg/module/module.go
@@ -9,7 +9,6 @@ import (
 	"regexp"
 	"sync"
 
-	"github.com/mailru/easyjson"
 	"github.com/samber/lo"
 	"github.com/tetratelabs/wazero"
 	"github.com/tetratelabs/wazero/api"
@@ -235,8 +234,8 @@ func unmarshal(mem api.Memory, ptrSize uint64, v any) error {
 	return nil
 }
 
-func marshal(ctx context.Context, m api.Module, malloc api.Function, v easyjson.Marshaler) (uint64, uint64, error) {
-	b, err := easyjson.Marshal(v)
+func marshal(ctx context.Context, m api.Module, malloc api.Function, v any) (uint64, uint64, error) {
+	b, err := json.Marshal(v)
 	if err != nil {
 		return 0, 0, xerrors.Errorf("marshal error: %w", err)
 	}
diff --git a/pkg/module/serialize/types.go b/pkg/module/serialize/types.go
index 21733f736082..df72a953eee3 100644
--- a/pkg/module/serialize/types.go
+++ b/pkg/module/serialize/types.go
@@ -4,15 +4,8 @@ import (
 	"github.com/aquasecurity/trivy/pkg/types"
 )
 
-// TinyGo doesn't support encoding/json, but github.com/mailru/easyjson for now.
-// We need to generate JSON-related methods like MarshalEasyJSON implementing easyjson.Marshaler.
-//
-// $ make easyjson
-
-//easyjson:json
 type StringSlice []string
 
-//easyjson:json
 type AnalysisResult struct {
 	// TODO: support other fields as well
 	// OS                   *types.OS
@@ -34,7 +27,6 @@ type CustomResource struct {
 
 type PostScanAction string
 
-//easyjson:json
 type PostScanSpec struct {
 	// What action the module will do in post scanning.
 	// value: INSERT, UPDATE and DELETE
@@ -45,8 +37,6 @@ type PostScanSpec struct {
 	IDs []string
 }
 
-//easyjson:json
 type Results []Result
 
-//easyjson:json
 type Result types.Result
diff --git a/pkg/module/serialize/types_easyjson.go b/pkg/module/serialize/types_easyjson.go
deleted file mode 100644
index 988347841c28..000000000000
--- a/pkg/module/serialize/types_easyjson.go
+++ /dev/null
@@ -1,3024 +0,0 @@
-// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
-
-package serialize
-
-import (
-	json "encoding/json"
-	types2 "github.com/aquasecurity/trivy-db/pkg/types"
-	digest "github.com/aquasecurity/trivy/pkg/digest"
-	types1 "github.com/aquasecurity/trivy/pkg/fanal/types"
-	types "github.com/aquasecurity/trivy/pkg/types"
-	easyjson "github.com/mailru/easyjson"
-	jlexer "github.com/mailru/easyjson/jlexer"
-	jwriter "github.com/mailru/easyjson/jwriter"
-	time "time"
-)
-
-// suppress unused package warning
-var (
-	_ *json.RawMessage
-	_ *jlexer.Lexer
-	_ *jwriter.Writer
-	_ easyjson.Marshaler
-)
-
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize(in *jlexer.Lexer, out *StringSlice) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		in.Skip()
-		*out = nil
-	} else {
-		in.Delim('[')
-		if *out == nil {
-			if !in.IsDelim(']') {
-				*out = make(StringSlice, 0, 4)
-			} else {
-				*out = StringSlice{}
-			}
-		} else {
-			*out = (*out)[:0]
-		}
-		for !in.IsDelim(']') {
-			var v1 string
-			v1 = string(in.String())
-			*out = append(*out, v1)
-			in.WantComma()
-		}
-		in.Delim(']')
-	}
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize(out *jwriter.Writer, in StringSlice) {
-	if in == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
-		out.RawString("null")
-	} else {
-		out.RawByte('[')
-		for v2, v3 := range in {
-			if v2 > 0 {
-				out.RawByte(',')
-			}
-			out.String(string(v3))
-		}
-		out.RawByte(']')
-	}
-}
-
-// MarshalJSON supports json.Marshaler interface
-func (v StringSlice) MarshalJSON() ([]byte, error) {
-	w := jwriter.Writer{}
-	easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize(&w, v)
-	return w.Buffer.BuildBytes(), w.Error
-}
-
-// MarshalEasyJSON supports easyjson.Marshaler interface
-func (v StringSlice) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize(w, v)
-}
-
-// UnmarshalJSON supports json.Unmarshaler interface
-func (v *StringSlice) UnmarshalJSON(data []byte) error {
-	r := jlexer.Lexer{Data: data}
-	easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize(&r, v)
-	return r.Error()
-}
-
-// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
-func (v *StringSlice) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize(l, v)
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize1(in *jlexer.Lexer, out *Results) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		in.Skip()
-		*out = nil
-	} else {
-		in.Delim('[')
-		if *out == nil {
-			if !in.IsDelim(']') {
-				*out = make(Results, 0, 0)
-			} else {
-				*out = Results{}
-			}
-		} else {
-			*out = (*out)[:0]
-		}
-		for !in.IsDelim(']') {
-			var v4 Result
-			(v4).UnmarshalEasyJSON(in)
-			*out = append(*out, v4)
-			in.WantComma()
-		}
-		in.Delim(']')
-	}
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize1(out *jwriter.Writer, in Results) {
-	if in == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
-		out.RawString("null")
-	} else {
-		out.RawByte('[')
-		for v5, v6 := range in {
-			if v5 > 0 {
-				out.RawByte(',')
-			}
-			(v6).MarshalEasyJSON(out)
-		}
-		out.RawByte(']')
-	}
-}
-
-// MarshalJSON supports json.Marshaler interface
-func (v Results) MarshalJSON() ([]byte, error) {
-	w := jwriter.Writer{}
-	easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize1(&w, v)
-	return w.Buffer.BuildBytes(), w.Error
-}
-
-// MarshalEasyJSON supports easyjson.Marshaler interface
-func (v Results) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize1(w, v)
-}
-
-// UnmarshalJSON supports json.Unmarshaler interface
-func (v *Results) UnmarshalJSON(data []byte) error {
-	r := jlexer.Lexer{Data: data}
-	easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize1(&r, v)
-	return r.Error()
-}
-
-// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
-func (v *Results) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize1(l, v)
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize2(in *jlexer.Lexer, out *Result) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Target":
-			out.Target = string(in.String())
-		case "Class":
-			out.Class = types.ResultClass(in.String())
-		case "Type":
-			out.Type = types1.TargetType(in.String())
-		case "Packages":
-			if in.IsNull() {
-				in.Skip()
-				out.Packages = nil
-			} else {
-				in.Delim('[')
-				if out.Packages == nil {
-					if !in.IsDelim(']') {
-						out.Packages = make([]types1.Package, 0, 0)
-					} else {
-						out.Packages = []types1.Package{}
-					}
-				} else {
-					out.Packages = (out.Packages)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v7 types1.Package
-					easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes(in, &v7)
-					out.Packages = append(out.Packages, v7)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "Vulnerabilities":
-			if in.IsNull() {
-				in.Skip()
-				out.Vulnerabilities = nil
-			} else {
-				in.Delim('[')
-				if out.Vulnerabilities == nil {
-					if !in.IsDelim(']') {
-						out.Vulnerabilities = make([]types.DetectedVulnerability, 0, 0)
-					} else {
-						out.Vulnerabilities = []types.DetectedVulnerability{}
-					}
-				} else {
-					out.Vulnerabilities = (out.Vulnerabilities)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v8 types.DetectedVulnerability
-					easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes(in, &v8)
-					out.Vulnerabilities = append(out.Vulnerabilities, v8)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "MisconfSummary":
-			if in.IsNull() {
-				in.Skip()
-				out.MisconfSummary = nil
-			} else {
-				if out.MisconfSummary == nil {
-					out.MisconfSummary = new(types.MisconfSummary)
-				}
-				easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes1(in, out.MisconfSummary)
-			}
-		case "Misconfigurations":
-			if in.IsNull() {
-				in.Skip()
-				out.Misconfigurations = nil
-			} else {
-				in.Delim('[')
-				if out.Misconfigurations == nil {
-					if !in.IsDelim(']') {
-						out.Misconfigurations = make([]types.DetectedMisconfiguration, 0, 0)
-					} else {
-						out.Misconfigurations = []types.DetectedMisconfiguration{}
-					}
-				} else {
-					out.Misconfigurations = (out.Misconfigurations)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v9 types.DetectedMisconfiguration
-					easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes2(in, &v9)
-					out.Misconfigurations = append(out.Misconfigurations, v9)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "Secrets":
-			if in.IsNull() {
-				in.Skip()
-				out.Secrets = nil
-			} else {
-				in.Delim('[')
-				if out.Secrets == nil {
-					if !in.IsDelim(']') {
-						out.Secrets = make([]types.DetectedSecret, 0, 0)
-					} else {
-						out.Secrets = []types.DetectedSecret{}
-					}
-				} else {
-					out.Secrets = (out.Secrets)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v10 types.DetectedSecret
-					easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes3(in, &v10)
-					out.Secrets = append(out.Secrets, v10)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "Licenses":
-			if in.IsNull() {
-				in.Skip()
-				out.Licenses = nil
-			} else {
-				in.Delim('[')
-				if out.Licenses == nil {
-					if !in.IsDelim(']') {
-						out.Licenses = make([]types.DetectedLicense, 0, 0)
-					} else {
-						out.Licenses = []types.DetectedLicense{}
-					}
-				} else {
-					out.Licenses = (out.Licenses)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v11 types.DetectedLicense
-					easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes4(in, &v11)
-					out.Licenses = append(out.Licenses, v11)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "CustomResources":
-			if in.IsNull() {
-				in.Skip()
-				out.CustomResources = nil
-			} else {
-				in.Delim('[')
-				if out.CustomResources == nil {
-					if !in.IsDelim(']') {
-						out.CustomResources = make([]types1.CustomResource, 0, 0)
-					} else {
-						out.CustomResources = []types1.CustomResource{}
-					}
-				} else {
-					out.CustomResources = (out.CustomResources)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v12 types1.CustomResource
-					easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes1(in, &v12)
-					out.CustomResources = append(out.CustomResources, v12)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize2(out *jwriter.Writer, in Result) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"Target\":"
-		out.RawString(prefix[1:])
-		out.String(string(in.Target))
-	}
-	if in.Class != "" {
-		const prefix string = ",\"Class\":"
-		out.RawString(prefix)
-		out.String(string(in.Class))
-	}
-	if in.Type != "" {
-		const prefix string = ",\"Type\":"
-		out.RawString(prefix)
-		out.String(string(in.Type))
-	}
-	if len(in.Packages) != 0 {
-		const prefix string = ",\"Packages\":"
-		out.RawString(prefix)
-		{
-			out.RawByte('[')
-			for v13, v14 := range in.Packages {
-				if v13 > 0 {
-					out.RawByte(',')
-				}
-				easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes(out, v14)
-			}
-			out.RawByte(']')
-		}
-	}
-	if len(in.Vulnerabilities) != 0 {
-		const prefix string = ",\"Vulnerabilities\":"
-		out.RawString(prefix)
-		{
-			out.RawByte('[')
-			for v15, v16 := range in.Vulnerabilities {
-				if v15 > 0 {
-					out.RawByte(',')
-				}
-				easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes(out, v16)
-			}
-			out.RawByte(']')
-		}
-	}
-	if in.MisconfSummary != nil {
-		const prefix string = ",\"MisconfSummary\":"
-		out.RawString(prefix)
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes1(out, *in.MisconfSummary)
-	}
-	if len(in.Misconfigurations) != 0 {
-		const prefix string = ",\"Misconfigurations\":"
-		out.RawString(prefix)
-		{
-			out.RawByte('[')
-			for v17, v18 := range in.Misconfigurations {
-				if v17 > 0 {
-					out.RawByte(',')
-				}
-				easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes2(out, v18)
-			}
-			out.RawByte(']')
-		}
-	}
-	if len(in.Secrets) != 0 {
-		const prefix string = ",\"Secrets\":"
-		out.RawString(prefix)
-		{
-			out.RawByte('[')
-			for v19, v20 := range in.Secrets {
-				if v19 > 0 {
-					out.RawByte(',')
-				}
-				easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes3(out, v20)
-			}
-			out.RawByte(']')
-		}
-	}
-	if len(in.Licenses) != 0 {
-		const prefix string = ",\"Licenses\":"
-		out.RawString(prefix)
-		{
-			out.RawByte('[')
-			for v21, v22 := range in.Licenses {
-				if v21 > 0 {
-					out.RawByte(',')
-				}
-				easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes4(out, v22)
-			}
-			out.RawByte(']')
-		}
-	}
-	if len(in.CustomResources) != 0 {
-		const prefix string = ",\"CustomResources\":"
-		out.RawString(prefix)
-		{
-			out.RawByte('[')
-			for v23, v24 := range in.CustomResources {
-				if v23 > 0 {
-					out.RawByte(',')
-				}
-				easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes1(out, v24)
-			}
-			out.RawByte(']')
-		}
-	}
-	out.RawByte('}')
-}
-
-// MarshalJSON supports json.Marshaler interface
-func (v Result) MarshalJSON() ([]byte, error) {
-	w := jwriter.Writer{}
-	easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize2(&w, v)
-	return w.Buffer.BuildBytes(), w.Error
-}
-
-// MarshalEasyJSON supports easyjson.Marshaler interface
-func (v Result) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize2(w, v)
-}
-
-// UnmarshalJSON supports json.Unmarshaler interface
-func (v *Result) UnmarshalJSON(data []byte) error {
-	r := jlexer.Lexer{Data: data}
-	easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize2(&r, v)
-	return r.Error()
-}
-
-// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
-func (v *Result) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize2(l, v)
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes1(in *jlexer.Lexer, out *types1.CustomResource) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Type":
-			out.Type = string(in.String())
-		case "FilePath":
-			out.FilePath = string(in.String())
-		case "Layer":
-			easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in, &out.Layer)
-		case "Data":
-			if m, ok := out.Data.(easyjson.Unmarshaler); ok {
-				m.UnmarshalEasyJSON(in)
-			} else if m, ok := out.Data.(json.Unmarshaler); ok {
-				_ = m.UnmarshalJSON(in.Raw())
-			} else {
-				out.Data = in.Interface()
-			}
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes1(out *jwriter.Writer, in types1.CustomResource) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"Type\":"
-		out.RawString(prefix[1:])
-		out.String(string(in.Type))
-	}
-	{
-		const prefix string = ",\"FilePath\":"
-		out.RawString(prefix)
-		out.String(string(in.FilePath))
-	}
-	{
-		const prefix string = ",\"Layer\":"
-		out.RawString(prefix)
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out, in.Layer)
-	}
-	{
-		const prefix string = ",\"Data\":"
-		out.RawString(prefix)
-		if m, ok := in.Data.(easyjson.Marshaler); ok {
-			m.MarshalEasyJSON(out)
-		} else if m, ok := in.Data.(json.Marshaler); ok {
-			out.Raw(m.MarshalJSON())
-		} else {
-			out.Raw(json.Marshal(in.Data))
-		}
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in *jlexer.Lexer, out *types1.Layer) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Digest":
-			out.Digest = string(in.String())
-		case "DiffID":
-			out.DiffID = string(in.String())
-		case "CreatedBy":
-			out.CreatedBy = string(in.String())
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out *jwriter.Writer, in types1.Layer) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	if in.Digest != "" {
-		const prefix string = ",\"Digest\":"
-		first = false
-		out.RawString(prefix[1:])
-		out.String(string(in.Digest))
-	}
-	if in.DiffID != "" {
-		const prefix string = ",\"DiffID\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.DiffID))
-	}
-	if in.CreatedBy != "" {
-		const prefix string = ",\"CreatedBy\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.CreatedBy))
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes4(in *jlexer.Lexer, out *types.DetectedLicense) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Severity":
-			out.Severity = string(in.String())
-		case "Category":
-			out.Category = types1.LicenseCategory(in.String())
-		case "PkgName":
-			out.PkgName = string(in.String())
-		case "FilePath":
-			out.FilePath = string(in.String())
-		case "Name":
-			out.Name = string(in.String())
-		case "Confidence":
-			out.Confidence = float64(in.Float64())
-		case "Link":
-			out.Link = string(in.String())
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes4(out *jwriter.Writer, in types.DetectedLicense) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"Severity\":"
-		out.RawString(prefix[1:])
-		out.String(string(in.Severity))
-	}
-	{
-		const prefix string = ",\"Category\":"
-		out.RawString(prefix)
-		out.String(string(in.Category))
-	}
-	{
-		const prefix string = ",\"PkgName\":"
-		out.RawString(prefix)
-		out.String(string(in.PkgName))
-	}
-	{
-		const prefix string = ",\"FilePath\":"
-		out.RawString(prefix)
-		out.String(string(in.FilePath))
-	}
-	{
-		const prefix string = ",\"Name\":"
-		out.RawString(prefix)
-		out.String(string(in.Name))
-	}
-	{
-		const prefix string = ",\"Confidence\":"
-		out.RawString(prefix)
-		out.Float64(float64(in.Confidence))
-	}
-	{
-		const prefix string = ",\"Link\":"
-		out.RawString(prefix)
-		out.String(string(in.Link))
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes3(in *jlexer.Lexer, out *types.DetectedSecret) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "RuleID":
-			out.RuleID = string(in.String())
-		case "Category":
-			out.Category = types1.SecretRuleCategory(in.String())
-		case "Severity":
-			out.Severity = string(in.String())
-		case "Title":
-			out.Title = string(in.String())
-		case "StartLine":
-			out.StartLine = int(in.Int())
-		case "EndLine":
-			out.EndLine = int(in.Int())
-		case "Code":
-			easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in, &out.Code)
-		case "Match":
-			out.Match = string(in.String())
-		case "Layer":
-			easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in, &out.Layer)
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes3(out *jwriter.Writer, in types.DetectedSecret) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"RuleID\":"
-		out.RawString(prefix[1:])
-		out.String(string(in.RuleID))
-	}
-	{
-		const prefix string = ",\"Category\":"
-		out.RawString(prefix)
-		out.String(string(in.Category))
-	}
-	{
-		const prefix string = ",\"Severity\":"
-		out.RawString(prefix)
-		out.String(string(in.Severity))
-	}
-	{
-		const prefix string = ",\"Title\":"
-		out.RawString(prefix)
-		out.String(string(in.Title))
-	}
-	{
-		const prefix string = ",\"StartLine\":"
-		out.RawString(prefix)
-		out.Int(int(in.StartLine))
-	}
-	{
-		const prefix string = ",\"EndLine\":"
-		out.RawString(prefix)
-		out.Int(int(in.EndLine))
-	}
-	{
-		const prefix string = ",\"Code\":"
-		out.RawString(prefix)
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out, in.Code)
-	}
-	{
-		const prefix string = ",\"Match\":"
-		out.RawString(prefix)
-		out.String(string(in.Match))
-	}
-	if true {
-		const prefix string = ",\"Layer\":"
-		out.RawString(prefix)
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out, in.Layer)
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in *jlexer.Lexer, out *types1.Code) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Lines":
-			if in.IsNull() {
-				in.Skip()
-				out.Lines = nil
-			} else {
-				in.Delim('[')
-				if out.Lines == nil {
-					if !in.IsDelim(']') {
-						out.Lines = make([]types1.Line, 0, 0)
-					} else {
-						out.Lines = []types1.Line{}
-					}
-				} else {
-					out.Lines = (out.Lines)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v25 types1.Line
-					easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes4(in, &v25)
-					out.Lines = append(out.Lines, v25)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out *jwriter.Writer, in types1.Code) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"Lines\":"
-		out.RawString(prefix[1:])
-		if in.Lines == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
-			out.RawString("null")
-		} else {
-			out.RawByte('[')
-			for v26, v27 := range in.Lines {
-				if v26 > 0 {
-					out.RawByte(',')
-				}
-				easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes4(out, v27)
-			}
-			out.RawByte(']')
-		}
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes4(in *jlexer.Lexer, out *types1.Line) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Number":
-			out.Number = int(in.Int())
-		case "Content":
-			out.Content = string(in.String())
-		case "IsCause":
-			out.IsCause = bool(in.Bool())
-		case "Annotation":
-			out.Annotation = string(in.String())
-		case "Truncated":
-			out.Truncated = bool(in.Bool())
-		case "Highlighted":
-			out.Highlighted = string(in.String())
-		case "FirstCause":
-			out.FirstCause = bool(in.Bool())
-		case "LastCause":
-			out.LastCause = bool(in.Bool())
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes4(out *jwriter.Writer, in types1.Line) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"Number\":"
-		out.RawString(prefix[1:])
-		out.Int(int(in.Number))
-	}
-	{
-		const prefix string = ",\"Content\":"
-		out.RawString(prefix)
-		out.String(string(in.Content))
-	}
-	{
-		const prefix string = ",\"IsCause\":"
-		out.RawString(prefix)
-		out.Bool(bool(in.IsCause))
-	}
-	{
-		const prefix string = ",\"Annotation\":"
-		out.RawString(prefix)
-		out.String(string(in.Annotation))
-	}
-	{
-		const prefix string = ",\"Truncated\":"
-		out.RawString(prefix)
-		out.Bool(bool(in.Truncated))
-	}
-	if in.Highlighted != "" {
-		const prefix string = ",\"Highlighted\":"
-		out.RawString(prefix)
-		out.String(string(in.Highlighted))
-	}
-	{
-		const prefix string = ",\"FirstCause\":"
-		out.RawString(prefix)
-		out.Bool(bool(in.FirstCause))
-	}
-	{
-		const prefix string = ",\"LastCause\":"
-		out.RawString(prefix)
-		out.Bool(bool(in.LastCause))
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes2(in *jlexer.Lexer, out *types.DetectedMisconfiguration) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Type":
-			out.Type = string(in.String())
-		case "ID":
-			out.ID = string(in.String())
-		case "AVDID":
-			out.AVDID = string(in.String())
-		case "Title":
-			out.Title = string(in.String())
-		case "Description":
-			out.Description = string(in.String())
-		case "Message":
-			out.Message = string(in.String())
-		case "Namespace":
-			out.Namespace = string(in.String())
-		case "Query":
-			out.Query = string(in.String())
-		case "Resolution":
-			out.Resolution = string(in.String())
-		case "Severity":
-			out.Severity = string(in.String())
-		case "PrimaryURL":
-			out.PrimaryURL = string(in.String())
-		case "References":
-			if in.IsNull() {
-				in.Skip()
-				out.References = nil
-			} else {
-				in.Delim('[')
-				if out.References == nil {
-					if !in.IsDelim(']') {
-						out.References = make([]string, 0, 4)
-					} else {
-						out.References = []string{}
-					}
-				} else {
-					out.References = (out.References)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v28 string
-					v28 = string(in.String())
-					out.References = append(out.References, v28)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "Status":
-			out.Status = types.MisconfStatus(in.String())
-		case "Layer":
-			easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in, &out.Layer)
-		case "CauseMetadata":
-			easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes5(in, &out.CauseMetadata)
-		case "Traces":
-			if in.IsNull() {
-				in.Skip()
-				out.Traces = nil
-			} else {
-				in.Delim('[')
-				if out.Traces == nil {
-					if !in.IsDelim(']') {
-						out.Traces = make([]string, 0, 4)
-					} else {
-						out.Traces = []string{}
-					}
-				} else {
-					out.Traces = (out.Traces)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v29 string
-					v29 = string(in.String())
-					out.Traces = append(out.Traces, v29)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes2(out *jwriter.Writer, in types.DetectedMisconfiguration) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	if in.Type != "" {
-		const prefix string = ",\"Type\":"
-		first = false
-		out.RawString(prefix[1:])
-		out.String(string(in.Type))
-	}
-	if in.ID != "" {
-		const prefix string = ",\"ID\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.ID))
-	}
-	if in.AVDID != "" {
-		const prefix string = ",\"AVDID\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.AVDID))
-	}
-	if in.Title != "" {
-		const prefix string = ",\"Title\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Title))
-	}
-	if in.Description != "" {
-		const prefix string = ",\"Description\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Description))
-	}
-	if in.Message != "" {
-		const prefix string = ",\"Message\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Message))
-	}
-	if in.Namespace != "" {
-		const prefix string = ",\"Namespace\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Namespace))
-	}
-	if in.Query != "" {
-		const prefix string = ",\"Query\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Query))
-	}
-	if in.Resolution != "" {
-		const prefix string = ",\"Resolution\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Resolution))
-	}
-	if in.Severity != "" {
-		const prefix string = ",\"Severity\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Severity))
-	}
-	if in.PrimaryURL != "" {
-		const prefix string = ",\"PrimaryURL\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.PrimaryURL))
-	}
-	if len(in.References) != 0 {
-		const prefix string = ",\"References\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('[')
-			for v30, v31 := range in.References {
-				if v30 > 0 {
-					out.RawByte(',')
-				}
-				out.String(string(v31))
-			}
-			out.RawByte(']')
-		}
-	}
-	if in.Status != "" {
-		const prefix string = ",\"Status\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Status))
-	}
-	if true {
-		const prefix string = ",\"Layer\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out, in.Layer)
-	}
-	if true {
-		const prefix string = ",\"CauseMetadata\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes5(out, in.CauseMetadata)
-	}
-	if len(in.Traces) != 0 {
-		const prefix string = ",\"Traces\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('[')
-			for v32, v33 := range in.Traces {
-				if v32 > 0 {
-					out.RawByte(',')
-				}
-				out.String(string(v33))
-			}
-			out.RawByte(']')
-		}
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes5(in *jlexer.Lexer, out *types1.CauseMetadata) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Resource":
-			out.Resource = string(in.String())
-		case "Provider":
-			out.Provider = string(in.String())
-		case "Service":
-			out.Service = string(in.String())
-		case "StartLine":
-			out.StartLine = int(in.Int())
-		case "EndLine":
-			out.EndLine = int(in.Int())
-		case "Code":
-			easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes3(in, &out.Code)
-		case "Occurrences":
-			if in.IsNull() {
-				in.Skip()
-				out.Occurrences = nil
-			} else {
-				in.Delim('[')
-				if out.Occurrences == nil {
-					if !in.IsDelim(']') {
-						out.Occurrences = make([]types1.Occurrence, 0, 1)
-					} else {
-						out.Occurrences = []types1.Occurrence{}
-					}
-				} else {
-					out.Occurrences = (out.Occurrences)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v34 types1.Occurrence
-					easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes6(in, &v34)
-					out.Occurrences = append(out.Occurrences, v34)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes5(out *jwriter.Writer, in types1.CauseMetadata) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	if in.Resource != "" {
-		const prefix string = ",\"Resource\":"
-		first = false
-		out.RawString(prefix[1:])
-		out.String(string(in.Resource))
-	}
-	if in.Provider != "" {
-		const prefix string = ",\"Provider\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Provider))
-	}
-	if in.Service != "" {
-		const prefix string = ",\"Service\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Service))
-	}
-	if in.StartLine != 0 {
-		const prefix string = ",\"StartLine\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Int(int(in.StartLine))
-	}
-	if in.EndLine != 0 {
-		const prefix string = ",\"EndLine\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Int(int(in.EndLine))
-	}
-	if true {
-		const prefix string = ",\"Code\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes3(out, in.Code)
-	}
-	if len(in.Occurrences) != 0 {
-		const prefix string = ",\"Occurrences\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('[')
-			for v35, v36 := range in.Occurrences {
-				if v35 > 0 {
-					out.RawByte(',')
-				}
-				easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes6(out, v36)
-			}
-			out.RawByte(']')
-		}
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes6(in *jlexer.Lexer, out *types1.Occurrence) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Resource":
-			out.Resource = string(in.String())
-		case "Filename":
-			out.Filename = string(in.String())
-		case "Location":
-			easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes7(in, &out.Location)
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes6(out *jwriter.Writer, in types1.Occurrence) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	if in.Resource != "" {
-		const prefix string = ",\"Resource\":"
-		first = false
-		out.RawString(prefix[1:])
-		out.String(string(in.Resource))
-	}
-	if in.Filename != "" {
-		const prefix string = ",\"Filename\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Filename))
-	}
-	{
-		const prefix string = ",\"Location\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes7(out, in.Location)
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes7(in *jlexer.Lexer, out *types1.Location) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "StartLine":
-			out.StartLine = int(in.Int())
-		case "EndLine":
-			out.EndLine = int(in.Int())
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes7(out *jwriter.Writer, in types1.Location) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	if in.StartLine != 0 {
-		const prefix string = ",\"StartLine\":"
-		first = false
-		out.RawString(prefix[1:])
-		out.Int(int(in.StartLine))
-	}
-	if in.EndLine != 0 {
-		const prefix string = ",\"EndLine\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Int(int(in.EndLine))
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes1(in *jlexer.Lexer, out *types.MisconfSummary) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Successes":
-			out.Successes = int(in.Int())
-		case "Failures":
-			out.Failures = int(in.Int())
-		case "Exceptions":
-			out.Exceptions = int(in.Int())
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes1(out *jwriter.Writer, in types.MisconfSummary) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"Successes\":"
-		out.RawString(prefix[1:])
-		out.Int(int(in.Successes))
-	}
-	{
-		const prefix string = ",\"Failures\":"
-		out.RawString(prefix)
-		out.Int(int(in.Failures))
-	}
-	{
-		const prefix string = ",\"Exceptions\":"
-		out.RawString(prefix)
-		out.Int(int(in.Exceptions))
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgTypes(in *jlexer.Lexer, out *types.DetectedVulnerability) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "VulnerabilityID":
-			out.VulnerabilityID = string(in.String())
-		case "VendorIDs":
-			if in.IsNull() {
-				in.Skip()
-				out.VendorIDs = nil
-			} else {
-				in.Delim('[')
-				if out.VendorIDs == nil {
-					if !in.IsDelim(']') {
-						out.VendorIDs = make([]string, 0, 4)
-					} else {
-						out.VendorIDs = []string{}
-					}
-				} else {
-					out.VendorIDs = (out.VendorIDs)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v37 string
-					v37 = string(in.String())
-					out.VendorIDs = append(out.VendorIDs, v37)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "PkgID":
-			out.PkgID = string(in.String())
-		case "PkgName":
-			out.PkgName = string(in.String())
-		case "PkgPath":
-			out.PkgPath = string(in.String())
-		case "PkgIdentifier":
-			if data := in.Raw(); in.Ok() {
-				in.AddError((out.PkgIdentifier).UnmarshalJSON(data))
-			}
-		case "InstalledVersion":
-			out.InstalledVersion = string(in.String())
-		case "FixedVersion":
-			out.FixedVersion = string(in.String())
-		case "Status":
-			if data := in.Raw(); in.Ok() {
-				in.AddError((out.Status).UnmarshalJSON(data))
-			}
-		case "Layer":
-			easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in, &out.Layer)
-		case "SeveritySource":
-			out.SeveritySource = types2.SourceID(in.String())
-		case "PrimaryURL":
-			out.PrimaryURL = string(in.String())
-		case "DataSource":
-			if in.IsNull() {
-				in.Skip()
-				out.DataSource = nil
-			} else {
-				if out.DataSource == nil {
-					out.DataSource = new(types2.DataSource)
-				}
-				easyjson6601e8cdDecodeGithubComAquasecurityTrivyDbPkgTypes(in, out.DataSource)
-			}
-		case "Custom":
-			if m, ok := out.Custom.(easyjson.Unmarshaler); ok {
-				m.UnmarshalEasyJSON(in)
-			} else if m, ok := out.Custom.(json.Unmarshaler); ok {
-				_ = m.UnmarshalJSON(in.Raw())
-			} else {
-				out.Custom = in.Interface()
-			}
-		case "Title":
-			out.Title = string(in.String())
-		case "Description":
-			out.Description = string(in.String())
-		case "Severity":
-			out.Severity = string(in.String())
-		case "CweIDs":
-			if in.IsNull() {
-				in.Skip()
-				out.CweIDs = nil
-			} else {
-				in.Delim('[')
-				if out.CweIDs == nil {
-					if !in.IsDelim(']') {
-						out.CweIDs = make([]string, 0, 4)
-					} else {
-						out.CweIDs = []string{}
-					}
-				} else {
-					out.CweIDs = (out.CweIDs)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v38 string
-					v38 = string(in.String())
-					out.CweIDs = append(out.CweIDs, v38)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "VendorSeverity":
-			if in.IsNull() {
-				in.Skip()
-			} else {
-				in.Delim('{')
-				if !in.IsDelim('}') {
-					out.VendorSeverity = make(types2.VendorSeverity)
-				} else {
-					out.VendorSeverity = nil
-				}
-				for !in.IsDelim('}') {
-					key := types2.SourceID(in.String())
-					in.WantColon()
-					var v39 types2.Severity
-					v39 = types2.Severity(in.Int())
-					(out.VendorSeverity)[key] = v39
-					in.WantComma()
-				}
-				in.Delim('}')
-			}
-		case "CVSS":
-			if in.IsNull() {
-				in.Skip()
-			} else {
-				in.Delim('{')
-				if !in.IsDelim('}') {
-					out.CVSS = make(types2.VendorCVSS)
-				} else {
-					out.CVSS = nil
-				}
-				for !in.IsDelim('}') {
-					key := types2.SourceID(in.String())
-					in.WantColon()
-					var v40 types2.CVSS
-					easyjson6601e8cdDecodeGithubComAquasecurityTrivyDbPkgTypes1(in, &v40)
-					(out.CVSS)[key] = v40
-					in.WantComma()
-				}
-				in.Delim('}')
-			}
-		case "References":
-			if in.IsNull() {
-				in.Skip()
-				out.References = nil
-			} else {
-				in.Delim('[')
-				if out.References == nil {
-					if !in.IsDelim(']') {
-						out.References = make([]string, 0, 4)
-					} else {
-						out.References = []string{}
-					}
-				} else {
-					out.References = (out.References)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v41 string
-					v41 = string(in.String())
-					out.References = append(out.References, v41)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "PublishedDate":
-			if in.IsNull() {
-				in.Skip()
-				out.PublishedDate = nil
-			} else {
-				if out.PublishedDate == nil {
-					out.PublishedDate = new(time.Time)
-				}
-				if data := in.Raw(); in.Ok() {
-					in.AddError((*out.PublishedDate).UnmarshalJSON(data))
-				}
-			}
-		case "LastModifiedDate":
-			if in.IsNull() {
-				in.Skip()
-				out.LastModifiedDate = nil
-			} else {
-				if out.LastModifiedDate == nil {
-					out.LastModifiedDate = new(time.Time)
-				}
-				if data := in.Raw(); in.Ok() {
-					in.AddError((*out.LastModifiedDate).UnmarshalJSON(data))
-				}
-			}
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgTypes(out *jwriter.Writer, in types.DetectedVulnerability) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	if in.VulnerabilityID != "" {
-		const prefix string = ",\"VulnerabilityID\":"
-		first = false
-		out.RawString(prefix[1:])
-		out.String(string(in.VulnerabilityID))
-	}
-	if len(in.VendorIDs) != 0 {
-		const prefix string = ",\"VendorIDs\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('[')
-			for v42, v43 := range in.VendorIDs {
-				if v42 > 0 {
-					out.RawByte(',')
-				}
-				out.String(string(v43))
-			}
-			out.RawByte(']')
-		}
-	}
-	if in.PkgID != "" {
-		const prefix string = ",\"PkgID\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.PkgID))
-	}
-	if in.PkgName != "" {
-		const prefix string = ",\"PkgName\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.PkgName))
-	}
-	if in.PkgPath != "" {
-		const prefix string = ",\"PkgPath\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.PkgPath))
-	}
-	if true {
-		const prefix string = ",\"PkgIdentifier\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Raw((in.PkgIdentifier).MarshalJSON())
-	}
-	if in.InstalledVersion != "" {
-		const prefix string = ",\"InstalledVersion\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.InstalledVersion))
-	}
-	if in.FixedVersion != "" {
-		const prefix string = ",\"FixedVersion\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.FixedVersion))
-	}
-	if in.Status != 0 {
-		const prefix string = ",\"Status\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Raw((in.Status).MarshalJSON())
-	}
-	if true {
-		const prefix string = ",\"Layer\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out, in.Layer)
-	}
-	if in.SeveritySource != "" {
-		const prefix string = ",\"SeveritySource\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.SeveritySource))
-	}
-	if in.PrimaryURL != "" {
-		const prefix string = ",\"PrimaryURL\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.PrimaryURL))
-	}
-	if in.DataSource != nil {
-		const prefix string = ",\"DataSource\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyDbPkgTypes(out, *in.DataSource)
-	}
-	if in.Custom != nil {
-		const prefix string = ",\"Custom\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		if m, ok := in.Custom.(easyjson.Marshaler); ok {
-			m.MarshalEasyJSON(out)
-		} else if m, ok := in.Custom.(json.Marshaler); ok {
-			out.Raw(m.MarshalJSON())
-		} else {
-			out.Raw(json.Marshal(in.Custom))
-		}
-	}
-	if in.Title != "" {
-		const prefix string = ",\"Title\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Title))
-	}
-	if in.Description != "" {
-		const prefix string = ",\"Description\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Description))
-	}
-	if in.Severity != "" {
-		const prefix string = ",\"Severity\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Severity))
-	}
-	if len(in.CweIDs) != 0 {
-		const prefix string = ",\"CweIDs\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('[')
-			for v44, v45 := range in.CweIDs {
-				if v44 > 0 {
-					out.RawByte(',')
-				}
-				out.String(string(v45))
-			}
-			out.RawByte(']')
-		}
-	}
-	if len(in.VendorSeverity) != 0 {
-		const prefix string = ",\"VendorSeverity\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('{')
-			v46First := true
-			for v46Name, v46Value := range in.VendorSeverity {
-				if v46First {
-					v46First = false
-				} else {
-					out.RawByte(',')
-				}
-				out.String(string(v46Name))
-				out.RawByte(':')
-				out.Int(int(v46Value))
-			}
-			out.RawByte('}')
-		}
-	}
-	if len(in.CVSS) != 0 {
-		const prefix string = ",\"CVSS\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('{')
-			v47First := true
-			for v47Name, v47Value := range in.CVSS {
-				if v47First {
-					v47First = false
-				} else {
-					out.RawByte(',')
-				}
-				out.String(string(v47Name))
-				out.RawByte(':')
-				easyjson6601e8cdEncodeGithubComAquasecurityTrivyDbPkgTypes1(out, v47Value)
-			}
-			out.RawByte('}')
-		}
-	}
-	if len(in.References) != 0 {
-		const prefix string = ",\"References\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('[')
-			for v48, v49 := range in.References {
-				if v48 > 0 {
-					out.RawByte(',')
-				}
-				out.String(string(v49))
-			}
-			out.RawByte(']')
-		}
-	}
-	if in.PublishedDate != nil {
-		const prefix string = ",\"PublishedDate\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Raw((*in.PublishedDate).MarshalJSON())
-	}
-	if in.LastModifiedDate != nil {
-		const prefix string = ",\"LastModifiedDate\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Raw((*in.LastModifiedDate).MarshalJSON())
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyDbPkgTypes1(in *jlexer.Lexer, out *types2.CVSS) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "V2Vector":
-			out.V2Vector = string(in.String())
-		case "V3Vector":
-			out.V3Vector = string(in.String())
-		case "V2Score":
-			out.V2Score = float64(in.Float64())
-		case "V3Score":
-			out.V3Score = float64(in.Float64())
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyDbPkgTypes1(out *jwriter.Writer, in types2.CVSS) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	if in.V2Vector != "" {
-		const prefix string = ",\"V2Vector\":"
-		first = false
-		out.RawString(prefix[1:])
-		out.String(string(in.V2Vector))
-	}
-	if in.V3Vector != "" {
-		const prefix string = ",\"V3Vector\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.V3Vector))
-	}
-	if in.V2Score != 0 {
-		const prefix string = ",\"V2Score\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Float64(float64(in.V2Score))
-	}
-	if in.V3Score != 0 {
-		const prefix string = ",\"V3Score\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Float64(float64(in.V3Score))
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyDbPkgTypes(in *jlexer.Lexer, out *types2.DataSource) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "ID":
-			out.ID = types2.SourceID(in.String())
-		case "Name":
-			out.Name = string(in.String())
-		case "URL":
-			out.URL = string(in.String())
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyDbPkgTypes(out *jwriter.Writer, in types2.DataSource) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	if in.ID != "" {
-		const prefix string = ",\"ID\":"
-		first = false
-		out.RawString(prefix[1:])
-		out.String(string(in.ID))
-	}
-	if in.Name != "" {
-		const prefix string = ",\"Name\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Name))
-	}
-	if in.URL != "" {
-		const prefix string = ",\"URL\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.URL))
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes(in *jlexer.Lexer, out *types1.Package) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "ID":
-			out.ID = string(in.String())
-		case "Name":
-			out.Name = string(in.String())
-		case "Identifier":
-			if data := in.Raw(); in.Ok() {
-				in.AddError((out.Identifier).UnmarshalJSON(data))
-			}
-		case "Version":
-			out.Version = string(in.String())
-		case "Release":
-			out.Release = string(in.String())
-		case "Epoch":
-			out.Epoch = int(in.Int())
-		case "Arch":
-			out.Arch = string(in.String())
-		case "Dev":
-			out.Dev = bool(in.Bool())
-		case "SrcName":
-			out.SrcName = string(in.String())
-		case "SrcVersion":
-			out.SrcVersion = string(in.String())
-		case "SrcRelease":
-			out.SrcRelease = string(in.String())
-		case "SrcEpoch":
-			out.SrcEpoch = int(in.Int())
-		case "Licenses":
-			if in.IsNull() {
-				in.Skip()
-				out.Licenses = nil
-			} else {
-				in.Delim('[')
-				if out.Licenses == nil {
-					if !in.IsDelim(']') {
-						out.Licenses = make([]string, 0, 4)
-					} else {
-						out.Licenses = []string{}
-					}
-				} else {
-					out.Licenses = (out.Licenses)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v50 string
-					v50 = string(in.String())
-					out.Licenses = append(out.Licenses, v50)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "Maintainer":
-			out.Maintainer = string(in.String())
-		case "Modularitylabel":
-			out.Modularitylabel = string(in.String())
-		case "BuildInfo":
-			if in.IsNull() {
-				in.Skip()
-				out.BuildInfo = nil
-			} else {
-				if out.BuildInfo == nil {
-					out.BuildInfo = new(types1.BuildInfo)
-				}
-				easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes8(in, out.BuildInfo)
-			}
-		case "Indirect":
-			out.Indirect = bool(in.Bool())
-		case "DependsOn":
-			if in.IsNull() {
-				in.Skip()
-				out.DependsOn = nil
-			} else {
-				in.Delim('[')
-				if out.DependsOn == nil {
-					if !in.IsDelim(']') {
-						out.DependsOn = make([]string, 0, 4)
-					} else {
-						out.DependsOn = []string{}
-					}
-				} else {
-					out.DependsOn = (out.DependsOn)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v51 string
-					v51 = string(in.String())
-					out.DependsOn = append(out.DependsOn, v51)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "Layer":
-			easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes2(in, &out.Layer)
-		case "FilePath":
-			out.FilePath = string(in.String())
-		case "Digest":
-			out.Digest = digest.Digest(in.String())
-		case "Locations":
-			if in.IsNull() {
-				in.Skip()
-				out.Locations = nil
-			} else {
-				in.Delim('[')
-				if out.Locations == nil {
-					if !in.IsDelim(']') {
-						out.Locations = make([]types1.Location, 0, 4)
-					} else {
-						out.Locations = []types1.Location{}
-					}
-				} else {
-					out.Locations = (out.Locations)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v52 types1.Location
-					easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes7(in, &v52)
-					out.Locations = append(out.Locations, v52)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "InstalledFiles":
-			if in.IsNull() {
-				in.Skip()
-				out.InstalledFiles = nil
-			} else {
-				in.Delim('[')
-				if out.InstalledFiles == nil {
-					if !in.IsDelim(']') {
-						out.InstalledFiles = make([]string, 0, 4)
-					} else {
-						out.InstalledFiles = []string{}
-					}
-				} else {
-					out.InstalledFiles = (out.InstalledFiles)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v53 string
-					v53 = string(in.String())
-					out.InstalledFiles = append(out.InstalledFiles, v53)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes(out *jwriter.Writer, in types1.Package) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	if in.ID != "" {
-		const prefix string = ",\"ID\":"
-		first = false
-		out.RawString(prefix[1:])
-		out.String(string(in.ID))
-	}
-	if in.Name != "" {
-		const prefix string = ",\"Name\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Name))
-	}
-	if true {
-		const prefix string = ",\"Identifier\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Raw((in.Identifier).MarshalJSON())
-	}
-	if in.Version != "" {
-		const prefix string = ",\"Version\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Version))
-	}
-	if in.Release != "" {
-		const prefix string = ",\"Release\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Release))
-	}
-	if in.Epoch != 0 {
-		const prefix string = ",\"Epoch\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Int(int(in.Epoch))
-	}
-	if in.Arch != "" {
-		const prefix string = ",\"Arch\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Arch))
-	}
-	if in.Dev {
-		const prefix string = ",\"Dev\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Bool(bool(in.Dev))
-	}
-	if in.SrcName != "" {
-		const prefix string = ",\"SrcName\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.SrcName))
-	}
-	if in.SrcVersion != "" {
-		const prefix string = ",\"SrcVersion\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.SrcVersion))
-	}
-	if in.SrcRelease != "" {
-		const prefix string = ",\"SrcRelease\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.SrcRelease))
-	}
-	if in.SrcEpoch != 0 {
-		const prefix string = ",\"SrcEpoch\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Int(int(in.SrcEpoch))
-	}
-	if len(in.Licenses) != 0 {
-		const prefix string = ",\"Licenses\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('[')
-			for v54, v55 := range in.Licenses {
-				if v54 > 0 {
-					out.RawByte(',')
-				}
-				out.String(string(v55))
-			}
-			out.RawByte(']')
-		}
-	}
-	if in.Maintainer != "" {
-		const prefix string = ",\"Maintainer\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Maintainer))
-	}
-	if in.Modularitylabel != "" {
-		const prefix string = ",\"Modularitylabel\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Modularitylabel))
-	}
-	if in.BuildInfo != nil {
-		const prefix string = ",\"BuildInfo\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes8(out, *in.BuildInfo)
-	}
-	if in.Indirect {
-		const prefix string = ",\"Indirect\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Bool(bool(in.Indirect))
-	}
-	if len(in.DependsOn) != 0 {
-		const prefix string = ",\"DependsOn\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('[')
-			for v56, v57 := range in.DependsOn {
-				if v56 > 0 {
-					out.RawByte(',')
-				}
-				out.String(string(v57))
-			}
-			out.RawByte(']')
-		}
-	}
-	if true {
-		const prefix string = ",\"Layer\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes2(out, in.Layer)
-	}
-	if in.FilePath != "" {
-		const prefix string = ",\"FilePath\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.FilePath))
-	}
-	if in.Digest != "" {
-		const prefix string = ",\"Digest\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Digest))
-	}
-	if len(in.Locations) != 0 {
-		const prefix string = ",\"Locations\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('[')
-			for v58, v59 := range in.Locations {
-				if v58 > 0 {
-					out.RawByte(',')
-				}
-				easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes7(out, v59)
-			}
-			out.RawByte(']')
-		}
-	}
-	if len(in.InstalledFiles) != 0 {
-		const prefix string = ",\"InstalledFiles\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		{
-			out.RawByte('[')
-			for v60, v61 := range in.InstalledFiles {
-				if v60 > 0 {
-					out.RawByte(',')
-				}
-				out.String(string(v61))
-			}
-			out.RawByte(']')
-		}
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgFanalTypes8(in *jlexer.Lexer, out *types1.BuildInfo) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "ContentSets":
-			if in.IsNull() {
-				in.Skip()
-				out.ContentSets = nil
-			} else {
-				in.Delim('[')
-				if out.ContentSets == nil {
-					if !in.IsDelim(']') {
-						out.ContentSets = make([]string, 0, 4)
-					} else {
-						out.ContentSets = []string{}
-					}
-				} else {
-					out.ContentSets = (out.ContentSets)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v62 string
-					v62 = string(in.String())
-					out.ContentSets = append(out.ContentSets, v62)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		case "Nvr":
-			out.Nvr = string(in.String())
-		case "Arch":
-			out.Arch = string(in.String())
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgFanalTypes8(out *jwriter.Writer, in types1.BuildInfo) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	if len(in.ContentSets) != 0 {
-		const prefix string = ",\"ContentSets\":"
-		first = false
-		out.RawString(prefix[1:])
-		{
-			out.RawByte('[')
-			for v63, v64 := range in.ContentSets {
-				if v63 > 0 {
-					out.RawByte(',')
-				}
-				out.String(string(v64))
-			}
-			out.RawByte(']')
-		}
-	}
-	if in.Nvr != "" {
-		const prefix string = ",\"Nvr\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Nvr))
-	}
-	if in.Arch != "" {
-		const prefix string = ",\"Arch\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(in.Arch))
-	}
-	out.RawByte('}')
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize3(in *jlexer.Lexer, out *PostScanSpec) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Action":
-			out.Action = PostScanAction(in.String())
-		case "IDs":
-			if in.IsNull() {
-				in.Skip()
-				out.IDs = nil
-			} else {
-				in.Delim('[')
-				if out.IDs == nil {
-					if !in.IsDelim(']') {
-						out.IDs = make([]string, 0, 4)
-					} else {
-						out.IDs = []string{}
-					}
-				} else {
-					out.IDs = (out.IDs)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v65 string
-					v65 = string(in.String())
-					out.IDs = append(out.IDs, v65)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize3(out *jwriter.Writer, in PostScanSpec) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"Action\":"
-		out.RawString(prefix[1:])
-		out.String(string(in.Action))
-	}
-	{
-		const prefix string = ",\"IDs\":"
-		out.RawString(prefix)
-		if in.IDs == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
-			out.RawString("null")
-		} else {
-			out.RawByte('[')
-			for v66, v67 := range in.IDs {
-				if v66 > 0 {
-					out.RawByte(',')
-				}
-				out.String(string(v67))
-			}
-			out.RawByte(']')
-		}
-	}
-	out.RawByte('}')
-}
-
-// MarshalJSON supports json.Marshaler interface
-func (v PostScanSpec) MarshalJSON() ([]byte, error) {
-	w := jwriter.Writer{}
-	easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize3(&w, v)
-	return w.Buffer.BuildBytes(), w.Error
-}
-
-// MarshalEasyJSON supports easyjson.Marshaler interface
-func (v PostScanSpec) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize3(w, v)
-}
-
-// UnmarshalJSON supports json.Unmarshaler interface
-func (v *PostScanSpec) UnmarshalJSON(data []byte) error {
-	r := jlexer.Lexer{Data: data}
-	easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize3(&r, v)
-	return r.Error()
-}
-
-// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
-func (v *PostScanSpec) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize3(l, v)
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize4(in *jlexer.Lexer, out *AnalysisResult) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "CustomResources":
-			if in.IsNull() {
-				in.Skip()
-				out.CustomResources = nil
-			} else {
-				in.Delim('[')
-				if out.CustomResources == nil {
-					if !in.IsDelim(']') {
-						out.CustomResources = make([]CustomResource, 0, 1)
-					} else {
-						out.CustomResources = []CustomResource{}
-					}
-				} else {
-					out.CustomResources = (out.CustomResources)[:0]
-				}
-				for !in.IsDelim(']') {
-					var v68 CustomResource
-					easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize5(in, &v68)
-					out.CustomResources = append(out.CustomResources, v68)
-					in.WantComma()
-				}
-				in.Delim(']')
-			}
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize4(out *jwriter.Writer, in AnalysisResult) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"CustomResources\":"
-		out.RawString(prefix[1:])
-		if in.CustomResources == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
-			out.RawString("null")
-		} else {
-			out.RawByte('[')
-			for v69, v70 := range in.CustomResources {
-				if v69 > 0 {
-					out.RawByte(',')
-				}
-				easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize5(out, v70)
-			}
-			out.RawByte(']')
-		}
-	}
-	out.RawByte('}')
-}
-
-// MarshalJSON supports json.Marshaler interface
-func (v AnalysisResult) MarshalJSON() ([]byte, error) {
-	w := jwriter.Writer{}
-	easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize4(&w, v)
-	return w.Buffer.BuildBytes(), w.Error
-}
-
-// MarshalEasyJSON supports easyjson.Marshaler interface
-func (v AnalysisResult) MarshalEasyJSON(w *jwriter.Writer) {
-	easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize4(w, v)
-}
-
-// UnmarshalJSON supports json.Unmarshaler interface
-func (v *AnalysisResult) UnmarshalJSON(data []byte) error {
-	r := jlexer.Lexer{Data: data}
-	easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize4(&r, v)
-	return r.Error()
-}
-
-// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
-func (v *AnalysisResult) UnmarshalEasyJSON(l *jlexer.Lexer) {
-	easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize4(l, v)
-}
-func easyjson6601e8cdDecodeGithubComAquasecurityTrivyPkgModuleSerialize5(in *jlexer.Lexer, out *CustomResource) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeFieldName(false)
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "Type":
-			out.Type = string(in.String())
-		case "FilePath":
-			out.FilePath = string(in.String())
-		case "Data":
-			if m, ok := out.Data.(easyjson.Unmarshaler); ok {
-				m.UnmarshalEasyJSON(in)
-			} else if m, ok := out.Data.(json.Unmarshaler); ok {
-				_ = m.UnmarshalJSON(in.Raw())
-			} else {
-				out.Data = in.Interface()
-			}
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-func easyjson6601e8cdEncodeGithubComAquasecurityTrivyPkgModuleSerialize5(out *jwriter.Writer, in CustomResource) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"Type\":"
-		out.RawString(prefix[1:])
-		out.String(string(in.Type))
-	}
-	{
-		const prefix string = ",\"FilePath\":"
-		out.RawString(prefix)
-		out.String(string(in.FilePath))
-	}
-	{
-		const prefix string = ",\"Data\":"
-		out.RawString(prefix)
-		if m, ok := in.Data.(easyjson.Marshaler); ok {
-			m.MarshalEasyJSON(out)
-		} else if m, ok := in.Data.(json.Marshaler); ok {
-			out.Raw(m.MarshalJSON())
-		} else {
-			out.Raw(json.Marshal(in.Data))
-		}
-	}
-	out.RawByte('}')
-}
diff --git a/pkg/module/wasm/sdk.go b/pkg/module/wasm/sdk.go
index dab28a88497c..25b9f1e777c3 100644
--- a/pkg/module/wasm/sdk.go
+++ b/pkg/module/wasm/sdk.go
@@ -6,12 +6,11 @@ package wasm
 // TinyGo can build this package, but Go cannot.
 
 import (
+	"encoding/json"
 	"fmt"
 	"reflect"
 	"unsafe"
 
-	"github.com/mailru/easyjson"
-
 	"github.com/aquasecurity/trivy/pkg/module/api"
 	"github.com/aquasecurity/trivy/pkg/module/serialize"
 )
@@ -40,20 +39,16 @@ func Error(message string) {
 	_error(ptr, size)
 }
 
-//go:wasm-module env
-//export debug
+//go:wasmimport env debug
 func _debug(ptr uint32, size uint32)
 
-//go:wasm-module env
-//export info
+//go:wasmimport env info
 func _info(ptr uint32, size uint32)
 
-//go:wasm-module env
-//export warn
+//go:wasmimport env warn
 func _warn(ptr uint32, size uint32)
 
-//go:wasm-module env
-//export error
+//go:wasmimport env error
 func _error(ptr uint32, size uint32)
 
 var module api.Module
@@ -134,8 +129,8 @@ func _post_scan(ptr, size uint32) uint64 {
 	return marshal(results)
 }
 
-func marshal(v easyjson.Marshaler) uint64 {
-	b, err := easyjson.Marshal(v)
+func marshal(v any) uint64 {
+	b, err := json.Marshal(v)
 	if err != nil {
 		Error(fmt.Sprintf("marshal error: %s", err))
 		return 0
@@ -145,14 +140,14 @@ func marshal(v easyjson.Marshaler) uint64 {
 	return (uint64(p) << uint64(32)) | uint64(len(b))
 }
 
-func unmarshal(ptr, size uint32, v easyjson.Unmarshaler) error {
+func unmarshal(ptr, size uint32, v any) error {
 	var b []byte
 	s := (*reflect.SliceHeader)(unsafe.Pointer(&b))
 	s.Len = uintptr(size)
 	s.Cap = uintptr(size)
 	s.Data = uintptr(ptr)
 
-	if err := easyjson.Unmarshal(b, v); err != nil {
+	if err := json.Unmarshal(b, v); err != nil {
 		return fmt.Errorf("unmarshal error: %s", err)
 	}
 
diff --git a/pkg/purl/purl.go b/pkg/purl/purl.go
index de02b309667a..59a4b99df30c 100644
--- a/pkg/purl/purl.go
+++ b/pkg/purl/purl.go
@@ -8,8 +8,10 @@ import (
 	cn "github.com/google/go-containerregistry/pkg/name"
 	version "github.com/knqyf263/go-rpm-version"
 	packageurl "github.com/package-url/packageurl-go"
+	"github.com/samber/lo"
 	"golang.org/x/xerrors"
 
+	"github.com/aquasecurity/trivy/pkg/dependency"
 	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/scanner/utils"
 	"github.com/aquasecurity/trivy/pkg/types"
@@ -42,10 +44,7 @@ const (
 	TypeUnknown = "unknown"
 )
 
-type PackageURL struct {
-	packageurl.PackageURL
-	FilePath string
-}
+type PackageURL packageurl.PackageURL
 
 func FromString(s string) (*PackageURL, error) {
 	p, err := packageurl.FromString(s)
@@ -53,25 +52,11 @@ func FromString(s string) (*PackageURL, error) {
 		return nil, xerrors.Errorf("failed to parse purl(%s): %w", s, err)
 	}
 
-	// Take out and delete the file path from qualifiers
-	var filePath string
-	for i, q := range p.Qualifiers {
-		if q.Key != "file_path" {
-			continue
-		}
-		filePath = q.Value
-		p.Qualifiers = append(p.Qualifiers[:i], p.Qualifiers[i+1:]...)
-		break
-	}
-
 	if len(p.Qualifiers) == 0 {
 		p.Qualifiers = nil
 	}
 
-	return &PackageURL{
-		PackageURL: p,
-		FilePath:   filePath,
-	}, nil
+	return lo.ToPtr(PackageURL(p)), nil
 }
 
 // nolint: gocyclo
@@ -120,52 +105,24 @@ func New(t ftypes.TargetType, metadata types.Metadata, pkg ftypes.Package) (*Pac
 		purl, err := parseOCI(metadata)
 		if err != nil {
 			return nil, err
-		} else if purl.Type == "" {
+		} else if purl == nil {
 			return nil, nil
 		}
-		return &PackageURL{PackageURL: purl}, nil
+		return (*PackageURL)(purl), nil
 	}
 
-	return &PackageURL{
-		PackageURL: *packageurl.NewPackageURL(ptype, namespace, name, ver, qualifiers, subpath),
-		FilePath:   pkg.FilePath,
-	}, nil
-}
-
-// WithPath wraps packageurl.PackageURL with the given file path
-func WithPath(purl *packageurl.PackageURL, filePath string) *PackageURL {
-	if purl == nil {
-		return nil
-	}
-	return &PackageURL{
-		PackageURL: *purl,
-		FilePath:   filePath,
-	}
-}
-
-func (p *PackageURL) BOMRef() string {
-	// 'bom-ref' must be unique within BOM, but PURLs may conflict
-	// when the same packages are installed in an artifact.
-	// In that case, we prefer to make PURLs unique by adding file paths,
-	// rather than using UUIDs, even if it is not PURL technically.
-	// ref. https://cyclonedx.org/use-cases/#dependency-graph
-	purl := p.PackageURL // so that it will not override the qualifiers below
-	if p.FilePath != "" {
-		purl.Qualifiers = append(purl.Qualifiers,
-			packageurl.Qualifier{
-				Key:   "file_path",
-				Value: p.FilePath,
-			},
-		)
-	}
-	return purl.String()
+	return (*PackageURL)(packageurl.NewPackageURL(ptype, namespace, name, ver, qualifiers, subpath)), nil
 }
 
 func (p *PackageURL) Unwrap() *packageurl.PackageURL {
 	if p == nil {
 		return nil
 	}
-	return &p.PackageURL
+	purl := (*packageurl.PackageURL)(p)
+	if len(purl.Qualifiers) == 0 {
+		purl.Qualifiers = nil
+	}
+	return purl
 }
 
 // LangType returns an application type in Trivy
@@ -236,8 +193,28 @@ func (p *PackageURL) Class() types.ResultClass {
 }
 
 func (p *PackageURL) Package() *ftypes.Package {
+	pkgName := p.Name
+	if p.Namespace != "" && p.Class() != types.ClassOSPkg {
+		if p.Type == packageurl.TypeMaven || p.Type == packageurl.TypeGradle {
+			// Maven and Gradle packages separate ":"
+			// e.g. org.springframework:spring-core
+			pkgName = p.Namespace + ":" + p.Name
+		} else {
+			pkgName = p.Namespace + "/" + p.Name
+		}
+	}
+
+	// CocoaPods purl has no namespace, but has subpath
+	// https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#cocoapods
+	if p.Subpath != "" && p.Type == packageurl.TypeCocoapods {
+		// CocoaPods uses <moduleName>/<submoduleName> format for package name
+		// e.g. `pkg:cocoapods/GoogleUtilities@7.5.2#NSData+zlib` => `GoogleUtilities/NSData+zlib`
+		pkgName = p.Name + "/" + p.Subpath
+	}
+
 	pkg := &ftypes.Package{
-		Name:    p.Name,
+		ID:      dependency.ID(p.LangType(), pkgName, p.Version),
+		Name:    pkgName,
 		Version: p.Version,
 		Identifier: ftypes.PkgIdentifier{
 			PURL: p.Unwrap(),
@@ -257,34 +234,12 @@ func (p *PackageURL) Package() *ftypes.Package {
 		}
 	}
 
-	// CocoaPods purl has no namespace, but has subpath
-	// https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#cocoapods
-	if p.Type == packageurl.TypeCocoapods && p.Subpath != "" {
-		// CocoaPods uses <moduleName>/<submoduleName> format for package name
-		// e.g. `pkg:cocoapods/GoogleUtilities@7.5.2#NSData+zlib` => `GoogleUtilities/NSData+zlib`
-		pkg.Name = p.Name + "/" + p.Subpath
-	}
-
 	if p.Type == packageurl.TypeRPM {
 		rpmVer := version.NewVersion(p.Version)
 		pkg.Release = rpmVer.Release()
 		pkg.Version = rpmVer.Version()
 	}
 
-	// Return packages without namespace.
-	// OS packages are not supposed to have namespace.
-	if p.Namespace == "" || p.Class() == types.ClassOSPkg {
-		return pkg
-	}
-
-	if p.Type == packageurl.TypeMaven || p.Type == packageurl.TypeGradle {
-		// Maven and Gradle packages separate ":"
-		// e.g. org.springframework:spring-core
-		pkg.Name = p.Namespace + ":" + p.Name
-	} else {
-		pkg.Name = p.Namespace + "/" + p.Name
-	}
-
 	return pkg
 }
 
@@ -318,15 +273,19 @@ func (p *PackageURL) Match(target *packageurl.PackageURL) bool {
 	return true
 }
 
+func (p *PackageURL) String() string {
+	return p.Unwrap().String()
+}
+
 // ref. https://github.com/package-url/purl-spec/blob/a748c36ad415c8aeffe2b8a4a5d8a50d16d6d85f/PURL-TYPES.rst#oci
-func parseOCI(metadata types.Metadata) (packageurl.PackageURL, error) {
+func parseOCI(metadata types.Metadata) (*packageurl.PackageURL, error) {
 	if len(metadata.RepoDigests) == 0 {
-		return *packageurl.NewPackageURL("", "", "", "", nil, ""), nil
+		return nil, nil
 	}
 
 	digest, err := cn.NewDigest(metadata.RepoDigests[0])
 	if err != nil {
-		return packageurl.PackageURL{}, xerrors.Errorf("failed to parse digest: %w", err)
+		return nil, xerrors.Errorf("failed to parse digest: %w", err)
 	}
 
 	name := strings.ToLower(digest.RepositoryStr())
@@ -349,7 +308,7 @@ func parseOCI(metadata types.Metadata) (packageurl.PackageURL, error) {
 		})
 	}
 
-	return *packageurl.NewPackageURL(packageurl.TypeOCI, "", name, digest.DigestStr(), qualifiers, ""), nil
+	return packageurl.NewPackageURL(packageurl.TypeOCI, "", name, digest.DigestStr(), qualifiers, ""), nil
 }
 
 // ref. https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#apk
diff --git a/pkg/purl/purl_test.go b/pkg/purl/purl_test.go
index 876911ca521a..79b2647cff4d 100644
--- a/pkg/purl/purl_test.go
+++ b/pkg/purl/purl_test.go
@@ -31,12 +31,10 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "5.3.14",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeMaven,
-					Namespace: "org.springframework",
-					Name:      "spring-core",
-					Version:   "5.3.14",
-				},
+				Type:      packageurl.TypeMaven,
+				Namespace: "org.springframework",
+				Name:      "spring-core",
+				Version:   "5.3.14",
 			},
 		},
 		{
@@ -47,12 +45,10 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "5.3.14",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeMaven,
-					Namespace: "org.springframework",
-					Name:      "spring-core",
-					Version:   "5.3.14",
-				},
+				Type:      packageurl.TypeMaven,
+				Namespace: "org.springframework",
+				Name:      "spring-core",
+				Version:   "5.3.14",
 			},
 		},
 		{
@@ -63,12 +59,10 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "1.2.0",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeNPM,
-					Namespace: "@xtuc",
-					Name:      "ieee754",
-					Version:   "1.2.0",
-				},
+				Type:      packageurl.TypeNPM,
+				Namespace: "@xtuc",
+				Name:      "ieee754",
+				Version:   "1.2.0",
 			},
 		},
 		{
@@ -79,11 +73,9 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "4.17.21",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeNPM,
-					Name:    "lodash",
-					Version: "4.17.21",
-				},
+				Type:    packageurl.TypeNPM,
+				Name:    "lodash",
+				Version: "4.17.21",
 			},
 		},
 		{
@@ -94,12 +86,10 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "1.2.0",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeNPM,
-					Namespace: "@xtuc",
-					Name:      "ieee754",
-					Version:   "1.2.0",
-				},
+				Type:      packageurl.TypeNPM,
+				Namespace: "@xtuc",
+				Name:      "ieee754",
+				Version:   "1.2.0",
 			},
 		},
 		{
@@ -110,11 +100,9 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "4.17.21",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeNPM,
-					Name:    "lodash",
-					Version: "4.17.21",
-				},
+				Type:    packageurl.TypeNPM,
+				Name:    "lodash",
+				Version: "4.17.21",
 			},
 		},
 		{
@@ -125,11 +113,9 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "1.2.0",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypePyPi,
-					Name:    "django-test",
-					Version: "1.2.0",
-				},
+				Type:    packageurl.TypePyPi,
+				Name:    "django-test",
+				Version: "1.2.0",
 			},
 		},
 		{
@@ -140,11 +126,9 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "0.4.1",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeConda,
-					Name:    "absl-py",
-					Version: "0.4.1",
-				},
+				Type:    packageurl.TypeConda,
+				Name:    "absl-py",
+				Version: "0.4.1",
 			},
 		},
 		{
@@ -155,12 +139,10 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "v1.0.2",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeComposer,
-					Namespace: "symfony",
-					Name:      "contracts",
-					Version:   "v1.0.2",
-				},
+				Type:      packageurl.TypeComposer,
+				Namespace: "symfony",
+				Name:      "contracts",
+				Version:   "v1.0.2",
 			},
 		},
 		{
@@ -171,12 +153,10 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "v1.5.0",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeGolang,
-					Namespace: "github.com/go-sql-driver",
-					Name:      "mysql",
-					Version:   "v1.5.0",
-				},
+				Type:      packageurl.TypeGolang,
+				Namespace: "github.com/go-sql-driver",
+				Name:      "mysql",
+				Version:   "v1.5.0",
 			},
 		},
 		{
@@ -203,11 +183,9 @@ func TestNewPackageURL(t *testing.T) {
 				},
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeHex,
-					Name:    "bunt",
-					Version: "0.2.0",
-				},
+				Type:    packageurl.TypeHex,
+				Name:    "bunt",
+				Version: "0.2.0",
 			},
 		},
 		{
@@ -218,11 +196,9 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "0.13.2",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypePub,
-					Name:    "http",
-					Version: "0.13.2",
-				},
+				Type:    packageurl.TypePub,
+				Name:    "http",
+				Version: "0.13.2",
 			},
 		},
 		{
@@ -234,12 +210,10 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "1.1.0",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeSwift,
-					Namespace: "github.com/apple",
-					Name:      "swift-atomics",
-					Version:   "1.1.0",
-				},
+				Type:      packageurl.TypeSwift,
+				Namespace: "github.com/apple",
+				Name:      "swift-atomics",
+				Version:   "1.1.0",
 			},
 		},
 		{
@@ -251,12 +225,10 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "7.5.2",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeCocoapods,
-					Name:    "GoogleUtilities",
-					Version: "7.5.2",
-					Subpath: "NSData+zlib",
-				},
+				Type:    packageurl.TypeCocoapods,
+				Name:    "GoogleUtilities",
+				Version: "7.5.2",
+				Subpath: "NSData+zlib",
 			},
 		},
 		{
@@ -268,11 +240,9 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "0.7.3",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeCargo,
-					Name:    "abomination",
-					Version: "0.7.3",
-				},
+				Type:    packageurl.TypeCargo,
+				Name:    "abomination",
+				Version: "0.7.3",
 			},
 		},
 		{
@@ -284,11 +254,9 @@ func TestNewPackageURL(t *testing.T) {
 				Version: "9.0.1",
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeNuget,
-					Name:    "Newtonsoft.Json",
-					Version: "9.0.1",
-				},
+				Type:    packageurl.TypeNuget,
+				Name:    "Newtonsoft.Json",
+				Version: "9.0.1",
 			},
 		},
 		{
@@ -314,24 +282,22 @@ func TestNewPackageURL(t *testing.T) {
 				},
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeRPM,
-					Namespace: "redhat",
-					Name:      "acl",
-					Version:   "2.2.53-1.el8",
-					Qualifiers: packageurl.Qualifiers{
-						{
-							Key:   "arch",
-							Value: "aarch64",
-						},
-						{
-							Key:   "epoch",
-							Value: "1",
-						},
-						{
-							Key:   "distro",
-							Value: "redhat-8",
-						},
+				Type:      packageurl.TypeRPM,
+				Namespace: "redhat",
+				Name:      "acl",
+				Version:   "2.2.53-1.el8",
+				Qualifiers: packageurl.Qualifiers{
+					{
+						Key:   "arch",
+						Value: "aarch64",
+					},
+					{
+						Key:   "epoch",
+						Value: "1",
+					},
+					{
+						Key:   "distro",
+						Value: "redhat-8",
 					},
 				},
 			},
@@ -352,20 +318,18 @@ func TestNewPackageURL(t *testing.T) {
 				},
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeOCI,
-					Namespace: "",
-					Name:      "core",
-					Version:   "sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260",
-					Qualifiers: packageurl.Qualifiers{
-						{
-							Key:   "repository_url",
-							Value: "cblmariner2preview.azurecr.io/base/core",
-						},
-						{
-							Key:   "arch",
-							Value: "amd64",
-						},
+				Type:      packageurl.TypeOCI,
+				Namespace: "",
+				Name:      "core",
+				Version:   "sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260",
+				Qualifiers: packageurl.Qualifiers{
+					{
+						Key:   "repository_url",
+						Value: "cblmariner2preview.azurecr.io/base/core",
+					},
+					{
+						Key:   "arch",
+						Value: "amd64",
 					},
 				},
 			},
@@ -400,20 +364,18 @@ func TestNewPackageURL(t *testing.T) {
 				},
 			},
 			want: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeOCI,
-					Namespace: "",
-					Name:      "alpine",
-					Version:   "sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260",
-					Qualifiers: packageurl.Qualifiers{
-						{
-							Key:   "repository_url",
-							Value: "index.docker.io/library/alpine",
-						},
-						{
-							Key:   "arch",
-							Value: "amd64",
-						},
+				Type:      packageurl.TypeOCI,
+				Namespace: "",
+				Name:      "alpine",
+				Version:   "sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260",
+				Qualifiers: packageurl.Qualifiers{
+					{
+						Key:   "repository_url",
+						Value: "index.docker.io/library/alpine",
+					},
+					{
+						Key:   "arch",
+						Value: "amd64",
 					},
 				},
 			},
@@ -458,79 +420,65 @@ func TestFromString(t *testing.T) {
 			name: "happy path for maven",
 			purl: "pkg:maven/org.springframework/spring-core@5.0.4.RELEASE",
 			want: purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeMaven,
-					Namespace: "org.springframework",
-					Version:   "5.0.4.RELEASE",
-					Name:      "spring-core",
-				},
-				FilePath: "",
+				Type:      packageurl.TypeMaven,
+				Namespace: "org.springframework",
+				Version:   "5.0.4.RELEASE",
+				Name:      "spring-core",
 			},
 		},
 		{
 			name: "happy path for npm",
-			purl: "pkg:npm/bootstrap@5.0.2?file_path=app%2Fapp%2Fpackage.json",
+			purl: "pkg:npm/bootstrap@5.0.2",
 			want: purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeNPM,
-					Name:    "bootstrap",
-					Version: "5.0.2",
-				},
-				FilePath: "app/app/package.json",
+				Type:    packageurl.TypeNPM,
+				Name:    "bootstrap",
+				Version: "5.0.2",
 			},
 		},
 		{
 			name: "happy path for coocapods",
 			purl: "pkg:cocoapods/GoogleUtilities@7.5.2#NSData+zlib",
 			want: purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeCocoapods,
-					Name:    "GoogleUtilities",
-					Version: "7.5.2",
-					Subpath: "NSData+zlib",
-				},
+				Type:    packageurl.TypeCocoapods,
+				Name:    "GoogleUtilities",
+				Version: "7.5.2",
+				Subpath: "NSData+zlib",
 			},
 		},
 		{
 			name: "happy path for hex",
 			purl: "pkg:hex/plug@1.14.0",
 			want: purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeHex,
-					Name:    "plug",
-					Version: "1.14.0",
-				},
+				Type:    packageurl.TypeHex,
+				Name:    "plug",
+				Version: "1.14.0",
 			},
 		},
 		{
 			name: "happy path for dart",
 			purl: "pkg:pub/http@0.13.2",
 			want: purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypePub,
-					Name:    "http",
-					Version: "0.13.2",
-				},
+				Type:    packageurl.TypePub,
+				Name:    "http",
+				Version: "0.13.2",
 			},
 		},
 		{
 			name: "happy path for apk",
 			purl: "pkg:apk/alpine/alpine-baselayout@3.2.0-r16?distro=3.14.2&epoch=1",
 			want: purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      string(analyzer.TypeApk),
-					Namespace: "alpine",
-					Name:      "alpine-baselayout",
-					Version:   "3.2.0-r16",
-					Qualifiers: packageurl.Qualifiers{
-						{
-							Key:   "distro",
-							Value: "3.14.2",
-						},
-						{
-							Key:   "epoch",
-							Value: "1",
-						},
+				Type:      string(analyzer.TypeApk),
+				Namespace: "alpine",
+				Name:      "alpine-baselayout",
+				Version:   "3.2.0-r16",
+				Qualifiers: packageurl.Qualifiers{
+					{
+						Key:   "distro",
+						Value: "3.14.2",
+					},
+					{
+						Key:   "epoch",
+						Value: "1",
 					},
 				},
 			},
@@ -539,35 +487,29 @@ func TestFromString(t *testing.T) {
 			name: "happy path for rpm",
 			purl: "pkg:rpm/redhat/containers-common@0.1.14",
 			want: purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeRPM,
-					Namespace: "redhat",
-					Name:      "containers-common",
-					Version:   "0.1.14",
-				},
+				Type:      packageurl.TypeRPM,
+				Namespace: "redhat",
+				Name:      "containers-common",
+				Version:   "0.1.14",
 			},
 		},
 		{
 			name: "happy path for conda",
 			purl: "pkg:conda/absl-py@0.4.1",
 			want: purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeConda,
-					Name:    "absl-py",
-					Version: "0.4.1",
-				},
+				Type:    packageurl.TypeConda,
+				Name:    "absl-py",
+				Version: "0.4.1",
 			},
 		},
 		{
 			name: "bad rpm",
 			purl: "pkg:rpm/redhat/a--@1.0.0",
 			want: purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeRPM,
-					Namespace: "redhat",
-					Name:      "a--",
-					Version:   "1.0.0",
-				},
+				Type:      packageurl.TypeRPM,
+				Namespace: "redhat",
+				Name:      "a--",
+				Version:   "1.0.0",
 			},
 		},
 	}
@@ -594,32 +536,31 @@ func TestPackageURL_Package(t *testing.T) {
 		{
 			name: "rpm + Qualifiers",
 			pkgURL: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeRPM,
-					Namespace: "redhat",
-					Name:      "nodejs-full-i18n",
-					Version:   "10.21.0-3.module_el8.2.0+391+8da3adc6",
-					Qualifiers: packageurl.Qualifiers{
-						{
-							Key:   "arch",
-							Value: "x86_64",
-						},
-						{
-							Key:   "epoch",
-							Value: "1",
-						},
-						{
-							Key:   "modularitylabel",
-							Value: "nodejs:10:8020020200707141642:6a468ee4",
-						},
-						{
-							Key:   "distro",
-							Value: "redhat-8",
-						},
+				Type:      packageurl.TypeRPM,
+				Namespace: "redhat",
+				Name:      "nodejs-full-i18n",
+				Version:   "10.21.0-3.module_el8.2.0+391+8da3adc6",
+				Qualifiers: packageurl.Qualifiers{
+					{
+						Key:   "arch",
+						Value: "x86_64",
+					},
+					{
+						Key:   "epoch",
+						Value: "1",
+					},
+					{
+						Key:   "modularitylabel",
+						Value: "nodejs:10:8020020200707141642:6a468ee4",
+					},
+					{
+						Key:   "distro",
+						Value: "redhat-8",
 					},
 				},
 			},
 			wantPkg: &ftypes.Package{
+				ID:              "nodejs-full-i18n@10.21.0-3.module_el8.2.0+391+8da3adc6",
 				Name:            "nodejs-full-i18n",
 				Version:         "10.21.0",
 				Release:         "3.module_el8.2.0+391+8da3adc6",
@@ -657,14 +598,14 @@ func TestPackageURL_Package(t *testing.T) {
 		{
 			name: "composer with namespace",
 			pkgURL: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeComposer,
-					Namespace: "symfony",
-					Name:      "contracts",
-					Version:   "1.0.2",
-				},
+				Type:      packageurl.TypeComposer,
+				Namespace: "symfony",
+				Name:      "contracts",
+				Version:   "1.0.2",
 			},
+
 			wantPkg: &ftypes.Package{
+				ID:      "symfony/contracts@1.0.2",
 				Name:    "symfony/contracts",
 				Version: "1.0.2",
 				Identifier: ftypes.PkgIdentifier{
@@ -680,14 +621,14 @@ func TestPackageURL_Package(t *testing.T) {
 		{
 			name: "maven with namespace",
 			pkgURL: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeMaven,
-					Namespace: "org.springframework",
-					Name:      "spring-core",
-					Version:   "5.0.4.RELEASE",
-				},
+				Type:      packageurl.TypeMaven,
+				Namespace: "org.springframework",
+				Name:      "spring-core",
+				Version:   "5.0.4.RELEASE",
 			},
+
 			wantPkg: &ftypes.Package{
+				ID:      "org.springframework:spring-core:5.0.4.RELEASE",
 				Name:    "org.springframework:spring-core",
 				Version: "5.0.4.RELEASE",
 				Identifier: ftypes.PkgIdentifier{
@@ -703,14 +644,14 @@ func TestPackageURL_Package(t *testing.T) {
 		{
 			name: "cocoapods with subpath",
 			pkgURL: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:    packageurl.TypeCocoapods,
-					Version: "4.2.0",
-					Name:    "AppCenter",
-					Subpath: "Analytics",
-				},
+				Type:    packageurl.TypeCocoapods,
+				Version: "4.2.0",
+				Name:    "AppCenter",
+				Subpath: "Analytics",
 			},
+
 			wantPkg: &ftypes.Package{
+				ID:      "AppCenter/Analytics@4.2.0",
 				Name:    "AppCenter/Analytics",
 				Version: "4.2.0",
 				Identifier: ftypes.PkgIdentifier{
@@ -726,20 +667,19 @@ func TestPackageURL_Package(t *testing.T) {
 		{
 			name: "wrong epoch",
 			pkgURL: &purl.PackageURL{
-				PackageURL: packageurl.PackageURL{
-					Type:      packageurl.TypeRPM,
-					Namespace: "redhat",
-					Name:      "acl",
-					Version:   "2.2.53-1.el8",
-					Qualifiers: packageurl.Qualifiers{
-						{
-							Key:   "epoch",
-							Value: "wrong",
-						},
+				Type:      packageurl.TypeRPM,
+				Namespace: "redhat",
+				Name:      "acl",
+				Version:   "2.2.53-1.el8",
+				Qualifiers: packageurl.Qualifiers{
+					{
+						Key:   "epoch",
+						Value: "wrong",
 					},
 				},
 			},
 			wantPkg: &ftypes.Package{
+				ID:      "acl@2.2.53-1.el8",
 				Name:    "acl",
 				Version: "2.2.53",
 				Release: "1.el8",
@@ -807,7 +747,7 @@ func TestPackageURL_LangType(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			p := &purl.PackageURL{PackageURL: tt.purl}
+			p := (purl.PackageURL)(tt.purl)
 			assert.Equalf(t, tt.want, p.LangType(), "LangType()")
 		})
 	}
diff --git a/pkg/rekortest/server.go b/pkg/rekortest/server.go
index e5eb7dbd7858..0e9207507492 100644
--- a/pkg/rekortest/server.go
+++ b/pkg/rekortest/server.go
@@ -141,7 +141,7 @@ var (
 				},
 				Dependencies: &[]cyclonedx.Dependency{
 					{
-						Ref: "pkg:oci/alpine@sha256:bc41182d7ef5ffc53a40b044e725193bc10142a1243f395ee852a8d9730fc2ad?repository_url=index.docker.io%2Flibrary%2Falpine&6arch=amd64",
+						Ref: "pkg:oci/alpine@sha256:bc41182d7ef5ffc53a40b044e725193bc10142a1243f395ee852a8d9730fc2ad?repository_url=index.docker.io%2Flibrary%2Falpine&arch=amd64",
 						Dependencies: &[]string{
 							"fad4eb97-3d2a-4499-ace7-2c94444148a7",
 						},
diff --git a/pkg/report/cyclonedx/cyclonedx.go b/pkg/report/cyclonedx/cyclonedx.go
index f624c02e83b2..76cd91d67752 100644
--- a/pkg/report/cyclonedx/cyclonedx.go
+++ b/pkg/report/cyclonedx/cyclonedx.go
@@ -15,7 +15,7 @@ import (
 type Writer struct {
 	output    io.Writer
 	format    cdx.BOMFileFormat
-	marshaler *cyclonedx.Marshaler
+	marshaler cyclonedx.Marshaler
 }
 
 func NewWriter(output io.Writer, appVersion string) Writer {
@@ -28,7 +28,7 @@ func NewWriter(output io.Writer, appVersion string) Writer {
 
 // Write writes the results in CycloneDX format
 func (w Writer) Write(ctx context.Context, report types.Report) error {
-	bom, err := w.marshaler.Marshal(ctx, report)
+	bom, err := w.marshaler.MarshalReport(ctx, report)
 	if err != nil {
 		return xerrors.Errorf("CycloneDX marshal error: %w", err)
 	}
diff --git a/pkg/report/github/github.go b/pkg/report/github/github.go
index e92d9921a2e6..5de466ad6beb 100644
--- a/pkg/report/github/github.go
+++ b/pkg/report/github/github.go
@@ -190,5 +190,5 @@ func buildPurl(t ftypes.TargetType, metadata types.Metadata, pkg ftypes.Package)
 	if packageUrl == nil {
 		return "", nil
 	}
-	return packageUrl.ToString(), nil
+	return packageUrl.String(), nil
 }
diff --git a/pkg/sbom/core/bom.go b/pkg/sbom/core/bom.go
new file mode 100644
index 000000000000..5f55f673306b
--- /dev/null
+++ b/pkg/sbom/core/bom.go
@@ -0,0 +1,290 @@
+package core
+
+import (
+	"sort"
+
+	"github.com/package-url/packageurl-go"
+
+	dtypes "github.com/aquasecurity/trivy-db/pkg/types"
+	"github.com/aquasecurity/trivy/pkg/digest"
+	"github.com/aquasecurity/trivy/pkg/uuid"
+)
+
+const (
+	TypeApplication ComponentType = "application"
+	TypeContainer   ComponentType = "container"
+	TypeLibrary     ComponentType = "library"
+	TypeOS          ComponentType = "os"
+	TypePlatform    ComponentType = "platform"
+
+	// Metadata properties
+	PropertySchemaVersion = "SchemaVersion"
+	PropertyType          = "Type"
+	PropertyClass         = "Class"
+
+	// Image properties
+	PropertySize       = "Size"
+	PropertyImageID    = "ImageID"
+	PropertyRepoDigest = "RepoDigest"
+	PropertyDiffID     = "DiffID"
+	PropertyRepoTag    = "RepoTag"
+
+	// Package properties
+	PropertyPkgID           = "PkgID"
+	PropertyPkgType         = "PkgType"
+	PropertySrcName         = "SrcName"
+	PropertySrcVersion      = "SrcVersion"
+	PropertySrcRelease      = "SrcRelease"
+	PropertySrcEpoch        = "SrcEpoch"
+	PropertyModularitylabel = "Modularitylabel"
+	PropertyFilePath        = "FilePath"
+	PropertyLayerDigest     = "LayerDigest"
+	PropertyLayerDiffID     = "LayerDiffID"
+
+	// Relationships
+	RelationshipDescribes RelationshipType = "describes"
+	RelationshipContains  RelationshipType = "contains"
+	RelationshipDependsOn RelationshipType = "depends_on"
+)
+
+type ComponentType string
+type RelationshipType string
+
+// BOM represents an intermediate representation of a component for SBOM.
+type BOM struct {
+	SerialNumber string
+	Version      int
+
+	rootID        uuid.UUID
+	components    map[uuid.UUID]*Component
+	relationships map[uuid.UUID][]Relationship
+
+	// Vulnerabilities is a list of vulnerabilities that affect the component
+	// CycloneDX: vulnerabilities
+	// SPDX: N/A
+	vulnerabilities map[uuid.UUID][]Vulnerability
+
+	// purls is a map of package URLs to UUIDs
+	// This is used to ensure that each package URL is only represented once in the BOM.
+	purls map[string][]uuid.UUID
+}
+
+type Component struct {
+	// id is the unique identifier of the component for internal use.
+	// It's transparently generated by UUIDv4
+	id uuid.UUID
+
+	// Type is the type of the component
+	// CycloneDX: component.type
+	Type ComponentType
+
+	// Root represents the root of the BOM
+	// Only one root is allowed in a BOM.
+	// CycloneDX: metadata.component
+	Root bool
+
+	// Name is the name of the component
+	// CycloneDX: component.name
+	// SPDX: package.name
+	Name string
+
+	// Group is the group of the component
+	// CycloneDX: component.group
+	// SPDX: N/A
+	Group string
+
+	// Version is the version of the component
+	// CycloneDX: component.version
+	// SPDX: package.versionInfo
+	Version string
+
+	// Licenses is a list of licenses that apply to the component
+	// CycloneDX: component.licenses
+	// SPDX: package.licenseConcluded, package.licenseDeclared
+	Licenses []string
+
+	// PkgID has PURL and BOMRef for the component
+	// PURL:
+	//   CycloneDX: component.purl
+	//   SPDX: package.externalRefs.referenceLocator
+	// BOMRef:
+	//   CycloneDX: component.bom-ref
+	//   SPDX: N/A
+	PkgID PkgID
+
+	// Supplier is the name of the supplier of the component
+	// CycloneDX: component.supplier
+	// SPDX: package.supplier
+	Supplier string
+
+	// Files is a list of files that are part of the component.
+	// CycloneDX: component.properties
+	// SPDX: files
+	Files []File
+
+	// Properties is a list of key-value pairs that provide additional information about the component
+	// CycloneDX: component.properties
+	// SPDX: package.attributionTexts
+	Properties Properties `hash:"set"`
+}
+
+func (c *Component) ID() uuid.UUID {
+	return c.id
+}
+
+type File struct {
+	// Path is a path of the file.
+	// CycloneDX: N/A
+	// SPDX: package.files[].fileName
+	Path string
+
+	// Hash is a hash that uniquely identify the component.
+	// CycloneDX: component.hashes
+	// SPDX: package.files[].checksum
+	Hash digest.Digest
+}
+
+type Property struct {
+	Name      string
+	Value     string
+	Namespace string
+}
+
+type Properties []Property
+
+func (p Properties) Len() int { return len(p) }
+func (p Properties) Less(i, j int) bool {
+	if p[i].Name != p[j].Name {
+		return p[i].Name < p[j].Name
+	}
+	return p[i].Value < p[j].Value
+}
+func (p Properties) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+type Relationship struct {
+	Dependency uuid.UUID
+	Type       RelationshipType
+}
+
+type PkgID struct {
+	PURL   *packageurl.PackageURL
+	BOMRef string
+}
+
+type Vulnerability struct {
+	dtypes.Vulnerability
+	ID               string
+	PkgID            string
+	PkgName          string
+	InstalledVersion string
+	FixedVersion     string
+	PrimaryURL       string
+	DataSource       *dtypes.DataSource
+}
+
+func NewBOM() *BOM {
+	return &BOM{
+		components:      make(map[uuid.UUID]*Component),
+		relationships:   make(map[uuid.UUID][]Relationship),
+		vulnerabilities: make(map[uuid.UUID][]Vulnerability),
+		purls:           make(map[string][]uuid.UUID),
+	}
+}
+
+func (b *BOM) setupComponent(c *Component) {
+	if c.id == uuid.Nil {
+		c.id = uuid.New()
+	}
+	if c.PkgID.PURL != nil {
+		p := c.PkgID.PURL.String()
+		b.purls[p] = append(b.purls[p], c.id)
+	}
+	sort.Sort(c.Properties)
+}
+
+func (b *BOM) AddComponent(c *Component) {
+	b.setupComponent(c)
+	if c.Root {
+		b.rootID = c.id
+	}
+	b.components[c.id] = c
+}
+
+func (b *BOM) AddRelationship(parent, child *Component, relationshipType RelationshipType) {
+	if parent.id == uuid.Nil {
+		b.AddComponent(parent)
+	}
+
+	if child == nil {
+		b.relationships[parent.id] = nil // Meaning no dependencies
+		return
+	}
+
+	if child.id == uuid.Nil {
+		b.AddComponent(child)
+	}
+
+	b.relationships[parent.id] = append(b.relationships[parent.id], Relationship{
+		Type:       relationshipType,
+		Dependency: child.id,
+	})
+}
+
+func (b *BOM) AddVulnerabilities(c *Component, vulns []Vulnerability) {
+	if c.id == uuid.Nil {
+		b.AddComponent(c)
+	}
+	if _, ok := b.vulnerabilities[c.id]; ok {
+		return
+	}
+	b.vulnerabilities[c.id] = vulns
+}
+
+func (b *BOM) Root() *Component {
+	root, ok := b.components[b.rootID]
+	if !ok {
+		return nil
+	}
+	root.PkgID.BOMRef = b.bomRef(root)
+	return root
+}
+
+func (b *BOM) Components() map[uuid.UUID]*Component {
+	// Fill in BOMRefs for components
+	for id, c := range b.components {
+		b.components[id].PkgID.BOMRef = b.bomRef(c)
+	}
+	return b.components
+}
+
+func (b *BOM) Relationships() map[uuid.UUID][]Relationship {
+	return b.relationships
+}
+
+func (b *BOM) Vulnerabilities() map[uuid.UUID][]Vulnerability {
+	return b.vulnerabilities
+}
+
+func (b *BOM) NumComponents() int {
+	return len(b.components) + 1 // +1 for the root component
+}
+
+// bomRef returns BOMRef for CycloneDX
+// When multiple lock files have the same dependency with the same name and version, PURL in the BOM can conflict.
+// In that case, PURL cannot be used as a unique identifier, and UUIDv4 be used for BOMRef.
+func (b *BOM) bomRef(c *Component) string {
+	if c.PkgID.BOMRef != "" {
+		return c.PkgID.BOMRef
+	}
+	// Return the UUID of the component if the PURL is not present.
+	if c.PkgID.PURL == nil {
+		return c.id.String()
+	}
+	p := c.PkgID.PURL.String()
+
+	// Return the UUID of the component if the PURL is not unique in the BOM.
+	if len(b.purls[p]) > 1 {
+		return c.id.String()
+	}
+	return p
+}
diff --git a/pkg/sbom/cyclonedx/core/cyclonedx.go b/pkg/sbom/cyclonedx/core/cyclonedx.go
deleted file mode 100644
index fc326145d5c6..000000000000
--- a/pkg/sbom/cyclonedx/core/cyclonedx.go
+++ /dev/null
@@ -1,513 +0,0 @@
-package core
-
-import (
-	"context"
-	"fmt"
-	"sort"
-	"strconv"
-	"strings"
-
-	cdx "github.com/CycloneDX/cyclonedx-go"
-	"github.com/samber/lo"
-	"golang.org/x/exp/slices"
-
-	dtypes "github.com/aquasecurity/trivy-db/pkg/types"
-	"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
-	"github.com/aquasecurity/trivy/pkg/clock"
-	"github.com/aquasecurity/trivy/pkg/digest"
-	"github.com/aquasecurity/trivy/pkg/log"
-	"github.com/aquasecurity/trivy/pkg/purl"
-	"github.com/aquasecurity/trivy/pkg/types"
-	"github.com/aquasecurity/trivy/pkg/uuid"
-)
-
-const (
-	ToolVendor = "aquasecurity"
-	ToolName   = "trivy"
-	Namespace  = ToolVendor + ":" + ToolName + ":"
-
-	// https://json-schema.org/understanding-json-schema/reference/string.html#dates-and-times
-	timeLayout = "2006-01-02T15:04:05+00:00"
-)
-
-type CycloneDX struct {
-	appVersion string
-}
-
-type Component struct {
-	Type       cdx.ComponentType
-	Name       string
-	Group      string
-	Version    string
-	PackageURL *purl.PackageURL
-	Licenses   []string
-	Hashes     []digest.Digest
-	Supplier   string
-	Properties []Property
-
-	Components      []*Component
-	Vulnerabilities []types.DetectedVulnerability
-}
-
-type Property struct {
-	Name      string
-	Value     string
-	Namespace string
-}
-
-func NewCycloneDX(version string) *CycloneDX {
-	return &CycloneDX{
-		appVersion: version,
-	}
-}
-
-func (c *CycloneDX) Marshal(ctx context.Context, root *Component) *cdx.BOM {
-	bom := cdx.NewBOM()
-	bom.SerialNumber = uuid.New().URN()
-	bom.Metadata = c.Metadata(ctx)
-
-	components := make(map[string]*cdx.Component)
-	dependencies := make(map[string]*[]string)
-	vulnerabilities := make(map[string]*cdx.Vulnerability)
-	bom.Metadata.Component = c.MarshalComponent(root, components, dependencies, vulnerabilities)
-
-	// Remove metadata component
-	delete(components, bom.Metadata.Component.BOMRef)
-
-	bom.Components = c.Components(components)
-	bom.Dependencies = c.Dependencies(dependencies)
-	bom.Vulnerabilities = c.Vulnerabilities(vulnerabilities)
-
-	return bom
-}
-
-func (c *CycloneDX) MarshalComponent(component *Component, components map[string]*cdx.Component,
-	deps map[string]*[]string, vulns map[string]*cdx.Vulnerability) *cdx.Component {
-	bomRef := c.BOMRef(component)
-
-	// When multiple lock files have the same dependency with the same name and version,
-	// "BOM-Ref" (PURL technically) of "Library" components may conflict.
-	// In that case, only one "Library" component will be added and
-	// some "Application" components will refer to the same component.
-	// e.g.
-	//    Application component (/app1/package-lock.json)
-	//    |
-	//    |    Application component (/app2/package-lock.json)
-	//    |    |
-	//    â””----â”´----> Library component (npm package, express-4.17.3)
-	//
-	if v, ok := components[bomRef]; ok {
-		return v
-	}
-
-	cdxComponent := &cdx.Component{
-		BOMRef:     bomRef,
-		Type:       component.Type,
-		Name:       component.Name,
-		Group:      component.Group,
-		Version:    component.Version,
-		PackageURL: c.PackageURL(component.PackageURL),
-		Supplier:   c.Supplier(component.Supplier),
-		Hashes:     c.Hashes(component.Hashes),
-		Licenses:   c.Licenses(component.Licenses),
-		Properties: lo.ToPtr(c.Properties(component.Properties)),
-	}
-	components[cdxComponent.BOMRef] = cdxComponent
-
-	for _, v := range component.Vulnerabilities {
-		// If the same vulnerability affects multiple packages, those packages will be aggregated into one vulnerability.
-		//   Vulnerability component (CVE-2020-26247)
-		//     -> Library component (nokogiri /srv/app1/vendor/bundle/ruby/3.0.0/specifications/nokogiri-1.10.0.gemspec)
-		//     -> Library component (nokogiri /srv/app2/vendor/bundle/ruby/3.0.0/specifications/nokogiri-1.10.0.gemspec)
-		if vuln, ok := vulns[v.VulnerabilityID]; ok {
-			*vuln.Affects = append(*vuln.Affects, cdxAffects(bomRef, v.InstalledVersion))
-			if v.FixedVersion != "" {
-				// new recommendation
-				rec := fmt.Sprintf("Upgrade %s to version %s", v.PkgName, v.FixedVersion)
-				// previous recommendations
-				recs := strings.Split(vuln.Recommendation, "; ")
-				if !slices.Contains(recs, rec) {
-					recs = append(recs, rec)
-					slices.Sort(recs)
-					vuln.Recommendation = strings.Join(recs, "; ")
-				}
-			}
-		} else {
-			vulns[v.VulnerabilityID] = c.marshalVulnerability(cdxComponent.BOMRef, v)
-		}
-	}
-
-	dependencies := make([]string, 0) // nolint:gocritic // Components that do not have their own dependencies must be declared as empty elements
-	for _, child := range component.Components {
-		childComponent := c.MarshalComponent(child, components, deps, vulns)
-		dependencies = append(dependencies, childComponent.BOMRef)
-	}
-	sort.Strings(dependencies)
-
-	deps[cdxComponent.BOMRef] = &dependencies
-
-	return cdxComponent
-}
-
-func (c *CycloneDX) marshalVulnerability(bomRef string, vuln types.DetectedVulnerability) *cdx.Vulnerability {
-	v := &cdx.Vulnerability{
-		ID:          vuln.VulnerabilityID,
-		Source:      cdxSource(vuln.DataSource),
-		Ratings:     cdxRatings(vuln),
-		CWEs:        cwes(vuln.CweIDs),
-		Description: vuln.Description,
-		Advisories:  cdxAdvisories(append([]string{vuln.PrimaryURL}, vuln.References...)),
-	}
-	if vuln.FixedVersion != "" {
-		v.Recommendation = fmt.Sprintf("Upgrade %s to version %s", vuln.PkgName, vuln.FixedVersion)
-	}
-	if vuln.PublishedDate != nil {
-		v.Published = vuln.PublishedDate.Format(timeLayout)
-	}
-	if vuln.LastModifiedDate != nil {
-		v.Updated = vuln.LastModifiedDate.Format(timeLayout)
-	}
-
-	v.Affects = &[]cdx.Affects{cdxAffects(bomRef, vuln.InstalledVersion)}
-
-	return v
-}
-
-func (c *CycloneDX) BOMRef(component *Component) string {
-	// PURL takes precedence over UUID
-	if component.PackageURL == nil {
-		return uuid.New().String()
-	}
-	return component.PackageURL.BOMRef()
-}
-
-func (c *CycloneDX) Metadata(ctx context.Context) *cdx.Metadata {
-	return &cdx.Metadata{
-		Timestamp: clock.Now(ctx).UTC().Format(timeLayout),
-		Tools: &cdx.ToolsChoice{
-			Components: &[]cdx.Component{
-				{
-					Type:    cdx.ComponentTypeApplication,
-					Group:   ToolVendor,
-					Name:    ToolName,
-					Version: c.appVersion,
-				},
-			},
-		},
-	}
-}
-
-func (c *CycloneDX) Components(uniq map[string]*cdx.Component) *[]cdx.Component {
-	// Convert components from map to slice and sort by BOM-Ref
-	components := lo.MapToSlice(uniq, func(_ string, value *cdx.Component) cdx.Component {
-		return *value
-	})
-	sort.Slice(components, func(i, j int) bool {
-		return components[i].BOMRef < components[j].BOMRef
-	})
-	return &components
-}
-
-func (c *CycloneDX) Dependencies(uniq map[string]*[]string) *[]cdx.Dependency {
-	// Convert dependencies from map to slice and sort by BOM-Ref
-	dependencies := lo.MapToSlice(uniq, func(bomRef string, value *[]string) cdx.Dependency {
-		return cdx.Dependency{
-			Ref:          bomRef,
-			Dependencies: value,
-		}
-	})
-	sort.Slice(dependencies, func(i, j int) bool {
-		return dependencies[i].Ref < dependencies[j].Ref
-	})
-	return &dependencies
-}
-
-func (c *CycloneDX) Vulnerabilities(uniq map[string]*cdx.Vulnerability) *[]cdx.Vulnerability {
-	vulns := lo.MapToSlice(uniq, func(bomRef string, value *cdx.Vulnerability) cdx.Vulnerability {
-		sort.Slice(*value.Affects, func(i, j int) bool {
-			return (*value.Affects)[i].Ref < (*value.Affects)[j].Ref
-		})
-		return *value
-	})
-	sort.Slice(vulns, func(i, j int) bool {
-		return vulns[i].ID < vulns[j].ID
-	})
-	return &vulns
-}
-
-func (c *CycloneDX) PackageURL(p *purl.PackageURL) string {
-	if p == nil {
-		return ""
-	}
-	return p.String()
-}
-
-func (c *CycloneDX) Supplier(supplier string) *cdx.OrganizationalEntity {
-	if supplier == "" {
-		return nil
-	}
-	return &cdx.OrganizationalEntity{
-		Name: supplier,
-	}
-}
-
-func (c *CycloneDX) Hashes(hashes []digest.Digest) *[]cdx.Hash {
-	if len(hashes) == 0 {
-		return nil
-	}
-	var cdxHashes []cdx.Hash
-	for _, hash := range hashes {
-		var alg cdx.HashAlgorithm
-		switch hash.Algorithm() {
-		case digest.SHA1:
-			alg = cdx.HashAlgoSHA1
-		case digest.SHA256:
-			alg = cdx.HashAlgoSHA256
-		case digest.MD5:
-			alg = cdx.HashAlgoMD5
-		default:
-			log.Logger.Debugf("Unable to convert %q algorithm to CycloneDX format", hash.Algorithm())
-			continue
-		}
-
-		cdxHashes = append(cdxHashes, cdx.Hash{
-			Algorithm: alg,
-			Value:     hash.Encoded(),
-		})
-	}
-	return &cdxHashes
-}
-
-func (c *CycloneDX) Licenses(licenses []string) *cdx.Licenses {
-	if len(licenses) == 0 {
-		return nil
-	}
-	choices := lo.Map(licenses, func(license string, i int) cdx.LicenseChoice {
-		return cdx.LicenseChoice{
-			License: &cdx.License{
-				Name: license,
-			},
-		}
-	})
-	return lo.ToPtr(cdx.Licenses(choices))
-}
-
-func (c *CycloneDX) Properties(properties []Property) []cdx.Property {
-	cdxProps := make([]cdx.Property, 0, len(properties))
-	for _, property := range properties {
-		namespace := Namespace
-		if len(property.Namespace) > 0 {
-			namespace = property.Namespace
-		}
-		cdxProps = append(cdxProps,
-			cdx.Property{
-				Name:  namespace + property.Name,
-				Value: property.Value,
-			})
-	}
-	sort.Slice(cdxProps, func(i, j int) bool {
-		return cdxProps[i].Name < cdxProps[j].Name
-	})
-	return cdxProps
-}
-
-func IsTrivySBOM(c *cdx.BOM) bool {
-	if c == nil || c.Metadata == nil || c.Metadata.Tools == nil {
-		return false
-	}
-
-	for _, component := range lo.FromPtr(c.Metadata.Tools.Components) {
-		if component.Group == ToolVendor && component.Name == ToolName {
-			return true
-		}
-	}
-
-	// Metadata.Tools array is deprecated (as of CycloneDX v1.5). We check this field for backward compatibility.
-	// cf. https://github.com/CycloneDX/cyclonedx-go/blob/b9654ae9b4705645152d20eb9872b5f3d73eac49/cyclonedx.go#L988
-	for _, tool := range lo.FromPtr(c.Metadata.Tools.Tools) {
-		if tool.Vendor == ToolVendor && tool.Name == ToolName {
-			return true
-		}
-	}
-
-	return false
-}
-
-func LookupProperty(properties *[]cdx.Property, key string) string {
-	for _, p := range lo.FromPtr(properties) {
-		if p.Name == Namespace+key {
-			return p.Value
-		}
-	}
-	return ""
-}
-
-func UnmarshalProperties(properties *[]cdx.Property) map[string]string {
-	props := make(map[string]string)
-	for _, prop := range lo.FromPtr(properties) {
-		if !strings.HasPrefix(prop.Name, Namespace) {
-			continue
-		}
-		props[strings.TrimPrefix(prop.Name, Namespace)] = prop.Value
-	}
-	return props
-}
-
-func cdxAdvisories(refs []string) *[]cdx.Advisory {
-	refs = lo.Uniq(refs)
-	advs := lo.FilterMap(refs, func(ref string, _ int) (cdx.Advisory, bool) {
-		return cdx.Advisory{URL: ref}, ref != ""
-	})
-
-	// cyclonedx converts link to empty `[]cdx.Advisory` to `null`
-	// `bom-1.5.schema.json` doesn't support this - `Invalid type. Expected: array, given: null`
-	// we need to explicitly set `nil` for empty `refs` slice
-	if len(advs) == 0 {
-		return nil
-	}
-
-	return &advs
-}
-
-func cwes(cweIDs []string) *[]int {
-	// to skip cdx.Vulnerability.CWEs when generating json
-	// we should return 'clear' nil without 'type' interface part
-	if cweIDs == nil {
-		return nil
-	}
-	var ret []int
-	for _, cweID := range cweIDs {
-		number, err := strconv.Atoi(strings.TrimPrefix(strings.ToLower(cweID), "cwe-"))
-		if err != nil {
-			log.Logger.Debugf("cwe id parse error: %s", err)
-			continue
-		}
-		ret = append(ret, number)
-	}
-	return &ret
-}
-
-func cdxRatings(vuln types.DetectedVulnerability) *[]cdx.VulnerabilityRating {
-	rates := make([]cdx.VulnerabilityRating, 0) // nolint:gocritic // To export an empty array in JSON
-	for sourceID, severity := range vuln.VendorSeverity {
-		// When the vendor also provides CVSS score/vector
-		if cvss, ok := vuln.CVSS[sourceID]; ok {
-			if cvss.V2Score != 0 || cvss.V2Vector != "" {
-				rates = append(rates, cdxRatingV2(sourceID, severity, cvss))
-			}
-			if cvss.V3Score != 0 || cvss.V3Vector != "" {
-				rates = append(rates, cdxRatingV3(sourceID, severity, cvss))
-			}
-		} else { // When the vendor provides only severity
-			rate := cdx.VulnerabilityRating{
-				Source: &cdx.Source{
-					Name: string(sourceID),
-				},
-				Severity: toCDXSeverity(severity),
-			}
-			rates = append(rates, rate)
-		}
-	}
-
-	// For consistency
-	sort.Slice(rates, func(i, j int) bool {
-		if rates[i].Source.Name != rates[j].Source.Name {
-			return rates[i].Source.Name < rates[j].Source.Name
-		}
-		if rates[i].Method != rates[j].Method {
-			return rates[i].Method < rates[j].Method
-		}
-		if rates[i].Score != nil && rates[j].Score != nil {
-			return *rates[i].Score < *rates[j].Score
-		}
-		return rates[i].Vector < rates[j].Vector
-	})
-	return &rates
-}
-
-func cdxRatingV2(sourceID dtypes.SourceID, severity dtypes.Severity, cvss dtypes.CVSS) cdx.VulnerabilityRating {
-	cdxSeverity := toCDXSeverity(severity)
-
-	// Trivy keeps only CVSSv3 severity for NVD.
-	// The CVSSv2 severity must be calculated according to CVSSv2 score.
-	if sourceID == vulnerability.NVD {
-		cdxSeverity = nvdSeverityV2(cvss.V2Score)
-	}
-	return cdx.VulnerabilityRating{
-		Source: &cdx.Source{
-			Name: string(sourceID),
-		},
-		Score:    &cvss.V2Score,
-		Method:   cdx.ScoringMethodCVSSv2,
-		Severity: cdxSeverity,
-		Vector:   cvss.V2Vector,
-	}
-}
-
-func cdxRatingV3(sourceID dtypes.SourceID, severity dtypes.Severity, cvss dtypes.CVSS) cdx.VulnerabilityRating {
-	rate := cdx.VulnerabilityRating{
-		Source: &cdx.Source{
-			Name: string(sourceID),
-		},
-		Score:    &cvss.V3Score,
-		Method:   cdx.ScoringMethodCVSSv3,
-		Severity: toCDXSeverity(severity),
-		Vector:   cvss.V3Vector,
-	}
-	if strings.HasPrefix(cvss.V3Vector, "CVSS:3.1") {
-		rate.Method = cdx.ScoringMethodCVSSv31
-	}
-	return rate
-}
-
-func nvdSeverityV2(score float64) cdx.Severity {
-	// cf. https://nvd.nist.gov/vuln-metrics/cvss
-	switch {
-	case score < 4.0:
-		return cdx.SeverityInfo
-	case 4.0 <= score && score < 7.0:
-		return cdx.SeverityMedium
-	case 7.0 <= score:
-		return cdx.SeverityHigh
-	}
-	return cdx.SeverityUnknown
-}
-
-func toCDXSeverity(s dtypes.Severity) cdx.Severity {
-	switch s {
-	case dtypes.SeverityLow:
-		return cdx.SeverityLow
-	case dtypes.SeverityMedium:
-		return cdx.SeverityMedium
-	case dtypes.SeverityHigh:
-		return cdx.SeverityHigh
-	case dtypes.SeverityCritical:
-		return cdx.SeverityCritical
-	default:
-		return cdx.SeverityUnknown
-	}
-}
-
-func cdxSource(source *dtypes.DataSource) *cdx.Source {
-	if source == nil {
-		return nil
-	}
-
-	return &cdx.Source{
-		Name: string(source.ID),
-		URL:  source.URL,
-	}
-}
-
-func cdxAffects(ref, version string) cdx.Affects {
-	return cdx.Affects{
-		Ref: ref,
-		Range: &[]cdx.AffectedVersions{
-			{
-				Version: version,
-				Status:  cdx.VulnerabilityStatusAffected,
-				// "AffectedVersions.Range" is not included, because it does not exist in DetectedVulnerability.
-			},
-		},
-	}
-}
diff --git a/pkg/sbom/cyclonedx/core/cyclonedx_test.go b/pkg/sbom/cyclonedx/core/cyclonedx_test.go
deleted file mode 100644
index 87d5a589f9bf..000000000000
--- a/pkg/sbom/cyclonedx/core/cyclonedx_test.go
+++ /dev/null
@@ -1,380 +0,0 @@
-package core_test
-
-import (
-	"context"
-	"github.com/aquasecurity/trivy/pkg/purl"
-	"testing"
-	"time"
-
-	cdx "github.com/CycloneDX/cyclonedx-go"
-	"github.com/package-url/packageurl-go"
-	"github.com/stretchr/testify/assert"
-
-	"github.com/aquasecurity/trivy/pkg/clock"
-	"github.com/aquasecurity/trivy/pkg/digest"
-	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx/core"
-	"github.com/aquasecurity/trivy/pkg/uuid"
-)
-
-func TestMarshaler_CoreComponent(t *testing.T) {
-	noDepRefs := []string{}
-	tests := []struct {
-		name          string
-		rootComponent *core.Component
-		want          *cdx.BOM
-	}{
-		{
-			name: "marshal CoreComponent",
-			rootComponent: &core.Component{
-				Type: cdx.ComponentTypeContainer,
-				Name: "test-cluster",
-				Components: []*core.Component{
-					{
-						Type: cdx.ComponentTypeApplication,
-						Name: "kube-apiserver-kind-control-plane",
-						Properties: []core.Property{
-							{
-								Name:  "control_plane_components",
-								Value: "kube-apiserver",
-							},
-						},
-						Components: []*core.Component{
-							{
-								Type:    cdx.ComponentTypeContainer,
-								Name:    "k8s.gcr.io/kube-apiserver",
-								Version: "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f",
-								PackageURL: &purl.PackageURL{
-									PackageURL: packageurl.PackageURL{
-										Type:    "oci",
-										Name:    "kube-apiserver",
-										Version: "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f",
-										Qualifiers: packageurl.Qualifiers{
-											{
-												Key:   "repository_url",
-												Value: "k8s.gcr.io/kube-apiserver",
-											},
-											{
-												Key: "arch",
-											},
-										},
-									},
-								},
-								Hashes: []digest.Digest{"sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f"},
-								Properties: []core.Property{
-									{
-										Name:  "PkgID",
-										Value: "k8s.gcr.io/kube-apiserver:1.21.1",
-									},
-									{
-										Name:  "PkgType",
-										Value: "oci",
-									},
-								},
-							},
-						},
-					},
-					{
-						Type: cdx.ComponentTypeContainer,
-						Name: "kind-control-plane",
-						Properties: []core.Property{
-							{
-								Name:  "architecture",
-								Value: "arm64",
-							},
-							{
-								Name:  "host_name",
-								Value: "kind-control-plane",
-							},
-							{
-								Name:  "kernel_version",
-								Value: "6.2.13-300.fc38.aarch64",
-							},
-							{
-								Name:  "node_role",
-								Value: "master",
-							},
-							{
-								Name:  "operating_system",
-								Value: "linux",
-							},
-						},
-						Components: []*core.Component{
-							{
-								Type:    cdx.ComponentTypeOS,
-								Name:    "ubuntu",
-								Version: "21.04",
-								Properties: []core.Property{
-									{
-										Name:  "Class",
-										Value: "os-pkgs",
-									},
-									{
-										Name:  "Type",
-										Value: "ubuntu",
-									},
-								},
-							},
-							{
-								Type: cdx.ComponentTypeApplication,
-								Name: "node-core-components",
-								Properties: []core.Property{
-									{
-										Name:  "Class",
-										Value: "lang-pkgs",
-									},
-									{
-										Name:  "Type",
-										Value: "golang",
-									},
-								},
-								Components: []*core.Component{
-									{
-										Type:    cdx.ComponentTypeLibrary,
-										Name:    "kubelet",
-										Version: "1.21.1",
-										Properties: []core.Property{
-											{
-												Name:  "PkgType",
-												Value: "golang",
-											},
-										},
-										PackageURL: &purl.PackageURL{
-											PackageURL: packageurl.PackageURL{
-												Type:       "golang",
-												Name:       "kubelet",
-												Version:    "1.21.1",
-												Qualifiers: packageurl.Qualifiers{},
-											},
-										},
-									},
-									{
-										Type:    cdx.ComponentTypeLibrary,
-										Name:    "containerd",
-										Version: "1.5.2",
-										Properties: []core.Property{
-											{
-												Name:  "PkgType",
-												Value: "golang",
-											},
-										},
-										PackageURL: &purl.PackageURL{
-											PackageURL: packageurl.PackageURL{
-												Type:       "golang",
-												Name:       "containerd",
-												Version:    "1.5.2",
-												Qualifiers: packageurl.Qualifiers{},
-											},
-										},
-									},
-								},
-							},
-						},
-					},
-				},
-			},
-
-			want: &cdx.BOM{
-				XMLNS:        "http://cyclonedx.org/schema/bom/1.5",
-				BOMFormat:    "CycloneDX",
-				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000001",
-				JSONSchema:   "http://cyclonedx.org/schema/bom-1.5.schema.json",
-				SpecVersion:  cdx.SpecVersion1_5,
-				Version:      1,
-				Metadata: &cdx.Metadata{
-					Timestamp: "2021-08-25T12:20:30+00:00",
-					Tools: &cdx.ToolsChoice{
-						Components: &[]cdx.Component{
-							{
-								Type:    cdx.ComponentTypeApplication,
-								Name:    "trivy",
-								Group:   "aquasecurity",
-								Version: "dev",
-							},
-						},
-					},
-					Component: &cdx.Component{
-						BOMRef:     "3ff14136-e09f-4df9-80ea-000000000002",
-						Name:       "test-cluster",
-						Properties: &[]cdx.Property{},
-						Type:       cdx.ComponentTypeContainer,
-					},
-				},
-				Vulnerabilities: &[]cdx.Vulnerability{},
-				Components: &[]cdx.Component{
-					{
-						BOMRef: "3ff14136-e09f-4df9-80ea-000000000003",
-						Type:   "application",
-						Name:   "kube-apiserver-kind-control-plane",
-						Properties: &[]cdx.Property{
-							{
-								Name:  "aquasecurity:trivy:control_plane_components",
-								Value: "kube-apiserver",
-							},
-						},
-					},
-					{
-						BOMRef: "3ff14136-e09f-4df9-80ea-000000000004",
-						Type:   "container",
-						Name:   "kind-control-plane",
-						Properties: &[]cdx.Property{
-							{
-								Name:  "aquasecurity:trivy:architecture",
-								Value: "arm64",
-							},
-							{
-								Name:  "aquasecurity:trivy:host_name",
-								Value: "kind-control-plane",
-							},
-							{
-								Name:  "aquasecurity:trivy:kernel_version",
-								Value: "6.2.13-300.fc38.aarch64",
-							},
-							{
-								Name:  "aquasecurity:trivy:node_role",
-								Value: "master",
-							},
-							{
-								Name:  "aquasecurity:trivy:operating_system",
-								Value: "linux",
-							},
-						},
-					},
-					{
-						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000005",
-						Type:    "operating-system",
-						Name:    "ubuntu",
-						Version: "21.04",
-						Properties: &[]cdx.Property{
-							{
-								Name:  "aquasecurity:trivy:Class",
-								Value: "os-pkgs",
-							},
-							{
-								Name:  "aquasecurity:trivy:Type",
-								Value: "ubuntu",
-							},
-						},
-					},
-					{
-						BOMRef: "3ff14136-e09f-4df9-80ea-000000000006",
-						Type:   "application",
-						Name:   "node-core-components",
-						Properties: &[]cdx.Property{
-							{
-								Name:  "aquasecurity:trivy:Class",
-								Value: "lang-pkgs",
-							},
-							{
-								Name:  "aquasecurity:trivy:Type",
-								Value: "golang",
-							},
-						},
-					},
-					{
-						BOMRef:     "pkg:golang/containerd@1.5.2",
-						Type:       "library",
-						Name:       "containerd",
-						Version:    "1.5.2",
-						PackageURL: "pkg:golang/containerd@1.5.2",
-						Properties: &[]cdx.Property{
-							{
-								Name:  "aquasecurity:trivy:PkgType",
-								Value: "golang",
-							},
-						},
-					},
-					{
-						BOMRef:     "pkg:golang/kubelet@1.21.1",
-						Type:       "library",
-						Name:       "kubelet",
-						Version:    "1.21.1",
-						PackageURL: "pkg:golang/kubelet@1.21.1",
-						Properties: &[]cdx.Property{
-							{
-								Name:  "aquasecurity:trivy:PkgType",
-								Value: "golang",
-							},
-						},
-					},
-					{
-						BOMRef: "pkg:oci/kube-apiserver@sha256%3A18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f?arch=&repository_url=k8s.gcr.io%2Fkube-apiserver",
-						Hashes: &[]cdx.Hash{
-							{
-								Algorithm: "SHA-256",
-								Value:     "18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f",
-							},
-						},
-						Type:       "container",
-						Name:       "k8s.gcr.io/kube-apiserver",
-						Version:    "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f",
-						PackageURL: "pkg:oci/kube-apiserver@sha256%3A18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f?arch=&repository_url=k8s.gcr.io%2Fkube-apiserver",
-						Properties: &[]cdx.Property{
-							{
-								Name:  "aquasecurity:trivy:PkgID",
-								Value: "k8s.gcr.io/kube-apiserver:1.21.1",
-							},
-							{
-								Name:  "aquasecurity:trivy:PkgType",
-								Value: "oci",
-							},
-						},
-					},
-				},
-				Dependencies: &[]cdx.Dependency{
-					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000002",
-						Dependencies: &[]string{
-							"3ff14136-e09f-4df9-80ea-000000000003",
-							"3ff14136-e09f-4df9-80ea-000000000004",
-						},
-					},
-					{
-						Ref:          "3ff14136-e09f-4df9-80ea-000000000003",
-						Dependencies: &[]string{"pkg:oci/kube-apiserver@sha256%3A18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f?arch=&repository_url=k8s.gcr.io%2Fkube-apiserver"},
-					},
-					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000004",
-						Dependencies: &[]string{
-							"3ff14136-e09f-4df9-80ea-000000000005",
-							"3ff14136-e09f-4df9-80ea-000000000006",
-						},
-					},
-					{
-						Ref:          "3ff14136-e09f-4df9-80ea-000000000005",
-						Dependencies: &noDepRefs,
-					},
-					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000006",
-						Dependencies: &[]string{
-							"pkg:golang/containerd@1.5.2",
-							"pkg:golang/kubelet@1.21.1",
-						},
-					},
-					{
-						Ref:          "pkg:golang/containerd@1.5.2",
-						Dependencies: &noDepRefs,
-					},
-					{
-						Ref:          "pkg:golang/kubelet@1.21.1",
-						Dependencies: &noDepRefs,
-					},
-					{
-						Ref:          "pkg:oci/kube-apiserver@sha256%3A18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f?arch=&repository_url=k8s.gcr.io%2Fkube-apiserver",
-						Dependencies: &noDepRefs,
-					},
-				},
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			ctx := clock.With(context.Background(), time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC))
-			uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d")
-
-			marshaler := core.NewCycloneDX("dev")
-			got := marshaler.Marshal(ctx, tt.rootComponent)
-			assert.Equal(t, tt.want, got)
-		})
-	}
-}
diff --git a/pkg/sbom/cyclonedx/marshal.go b/pkg/sbom/cyclonedx/marshal.go
index 97bdb6092b42..be9fc23372b7 100644
--- a/pkg/sbom/cyclonedx/marshal.go
+++ b/pkg/sbom/cyclonedx/marshal.go
@@ -3,6 +3,8 @@ package cyclonedx
 import (
 	"context"
 	"fmt"
+	"slices"
+	"sort"
 	"strconv"
 	"strings"
 
@@ -11,424 +13,488 @@ import (
 	"github.com/samber/lo"
 	"golang.org/x/xerrors"
 
+	dtypes "github.com/aquasecurity/trivy-db/pkg/types"
+	"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
+	"github.com/aquasecurity/trivy/pkg/clock"
 	"github.com/aquasecurity/trivy/pkg/digest"
-	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
-	"github.com/aquasecurity/trivy/pkg/purl"
-	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx/core"
-	"github.com/aquasecurity/trivy/pkg/scanner/utils"
+	"github.com/aquasecurity/trivy/pkg/log"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
+	sbomio "github.com/aquasecurity/trivy/pkg/sbom/io"
 	"github.com/aquasecurity/trivy/pkg/types"
+	"github.com/aquasecurity/trivy/pkg/uuid"
 )
 
 const (
-	PropertySchemaVersion = "SchemaVersion"
-	PropertyType          = "Type"
-	PropertyClass         = "Class"
-
-	// Image properties
-	PropertySize       = "Size"
-	PropertyImageID    = "ImageID"
-	PropertyRepoDigest = "RepoDigest"
-	PropertyDiffID     = "DiffID"
-	PropertyRepoTag    = "RepoTag"
-
-	// Package properties
-	PropertyPkgID           = "PkgID"
-	PropertyPkgType         = "PkgType"
-	PropertySrcName         = "SrcName"
-	PropertySrcVersion      = "SrcVersion"
-	PropertySrcRelease      = "SrcRelease"
-	PropertySrcEpoch        = "SrcEpoch"
-	PropertyModularitylabel = "Modularitylabel"
-	PropertyFilePath        = "FilePath"
-	PropertyLayerDigest     = "LayerDigest"
-	PropertyLayerDiffID     = "LayerDiffID"
-)
+	ToolVendor = "aquasecurity"
+	ToolName   = "trivy"
+	Namespace  = ToolVendor + ":" + ToolName + ":"
 
-var (
-	ErrInvalidBOMLink = xerrors.New("invalid bomLink format error")
+	// https://json-schema.org/understanding-json-schema/reference/string.html#dates-and-times
+	timeLayout = "2006-01-02T15:04:05+00:00"
 )
 
 type Marshaler struct {
-	core *core.CycloneDX
+	appVersion   string // Trivy version
+	bom          *core.BOM
+	componentIDs map[uuid.UUID]string
 }
 
-func NewMarshaler(version string) *Marshaler {
-	return &Marshaler{
-		core: core.NewCycloneDX(version),
+func NewMarshaler(version string) Marshaler {
+	return Marshaler{
+		appVersion: version,
 	}
 }
 
-// Marshal converts the Trivy report to the CycloneDX format
-func (e *Marshaler) Marshal(ctx context.Context, report types.Report) (*cdx.BOM, error) {
-	// Convert
-	root, err := e.MarshalReport(report)
+// MarshalReport converts the Trivy report to the CycloneDX format
+func (m *Marshaler) MarshalReport(ctx context.Context, report types.Report) (*cdx.BOM, error) {
+	// Convert into an intermediate representation
+	bom, err := sbomio.NewEncoder().Encode(report)
 	if err != nil {
 		return nil, xerrors.Errorf("failed to marshal report: %w", err)
 	}
 
-	return e.core.Marshal(ctx, root), nil
+	return m.Marshal(ctx, bom)
 }
 
-func (e *Marshaler) MarshalReport(r types.Report) (*core.Component, error) {
-	// Metadata component
-	root, err := e.rootComponent(r)
-	if err != nil {
-		return nil, err
+// Marshal converts the Trivy component to the CycloneDX format
+func (m *Marshaler) Marshal(ctx context.Context, bom *core.BOM) (*cdx.BOM, error) {
+	m.bom = bom
+	m.componentIDs = make(map[uuid.UUID]string, m.bom.NumComponents())
+
+	cdxBOM := cdx.NewBOM()
+	cdxBOM.SerialNumber = uuid.New().URN()
+	cdxBOM.Metadata = m.Metadata(ctx)
+
+	var err error
+	if cdxBOM.Metadata.Component, err = m.MarshalRoot(); err != nil {
+		return nil, xerrors.Errorf("failed to marshal component: %w", err)
 	}
 
-	for _, result := range r.Results {
-		components, err := e.marshalResult(r.Metadata, result)
-		if err != nil {
-			return nil, err
-		}
-		root.Components = append(root.Components, components...)
+	if cdxBOM.Components, err = m.marshalComponents(); err != nil {
+		return nil, xerrors.Errorf("failed to marshal components: %w", err)
 	}
-	return root, nil
-}
 
-func (e *Marshaler) marshalResult(metadata types.Metadata, result types.Result) ([]*core.Component, error) {
-	if result.Type == ftypes.NodePkg || result.Type == ftypes.PythonPkg ||
-		result.Type == ftypes.GemSpec || result.Type == ftypes.Jar || result.Type == ftypes.CondaPkg {
-		// If a package is language-specific package that isn't associated with a lock file,
-		// it will be a dependency of a component under "metadata".
-		// e.g.
-		//   Container component (alpine:3.15) ----------------------- #1
-		//     -> Library component (npm package, express-4.17.3) ---- #2
-		//     -> Library component (python package, django-4.0.2) --- #2
-		//     -> etc.
-		// ref. https://cyclonedx.org/use-cases/#inventory
-
-		// Dependency graph from #1 to #2
-		components, err := e.marshalPackages(metadata, result)
-		if err != nil {
-			return nil, err
-		}
-		return components, nil
-	} else if result.Class == types.ClassOSPkg || result.Class == types.ClassLangPkg {
-		// If a package is OS package, it will be a dependency of "Operating System" component.
-		// e.g.
-		//   Container component (alpine:3.15) --------------------- #1
-		//     -> Operating System Component (Alpine Linux 3.15) --- #2
-		//       -> Library component (bash-4.12) ------------------ #3
-		//       -> Library component (vim-8.2)   ------------------ #3
-		//       -> etc.
-		//
-		// Else if a package is language-specific package associated with a lock file,
-		// it will be a dependency of "Application" component.
-		// e.g.
-		//   Container component (alpine:3.15) ------------------------ #1
-		//     -> Application component (/app/package-lock.json) ------ #2
-		//       -> Library component (npm package, express-4.17.3) --- #3
-		//       -> Library component (npm package, lodash-4.17.21) --- #3
-		//       -> etc.
-
-		// #2
-		appComponent := e.resultComponent(result, metadata.OS)
-
-		// #3
-		components, err := e.marshalPackages(metadata, result)
-		if err != nil {
-			return nil, err
-		}
+	cdxBOM.Dependencies = m.marshalDependencies()
+	cdxBOM.Vulnerabilities = m.marshalVulnerabilities()
 
-		// Dependency graph from #2 to #3
-		appComponent.Components = components
+	return cdxBOM, nil
+}
 
-		// Dependency graph from #1 to #2
-		return []*core.Component{appComponent}, nil
+func (m *Marshaler) Metadata(ctx context.Context) *cdx.Metadata {
+	return &cdx.Metadata{
+		Timestamp: clock.Now(ctx).UTC().Format(timeLayout),
+		Tools: &cdx.ToolsChoice{
+			Components: &[]cdx.Component{
+				{
+					Type:    cdx.ComponentTypeApplication,
+					Group:   ToolVendor,
+					Name:    ToolName,
+					Version: m.appVersion,
+				},
+			},
+		},
 	}
-	return nil, nil
 }
 
-func (e *Marshaler) marshalPackages(metadata types.Metadata, result types.Result) ([]*core.Component, error) {
-	// Get dependency parents first
-	parents := ftypes.Packages(result.Packages).ParentDeps()
+func (m *Marshaler) MarshalRoot() (*cdx.Component, error) {
+	return m.MarshalComponent(m.bom.Root())
+}
+
+func (m *Marshaler) MarshalComponent(component *core.Component) (*cdx.Component, error) {
+	componentType, err := m.componentType(component.Type)
+	if err != nil {
+		return nil, xerrors.Errorf("failed to get cdx component type: %w", err)
+	}
 
-	// Group vulnerabilities by package ID
-	vulns := lo.GroupBy(result.Vulnerabilities, func(v types.DetectedVulnerability) string {
-		return lo.Ternary(v.PkgID == "", fmt.Sprintf("%s@%s", v.PkgName, v.InstalledVersion), v.PkgID)
-	})
+	cdxComponent := &cdx.Component{
+		BOMRef:     component.PkgID.BOMRef,
+		Type:       componentType,
+		Name:       component.Name,
+		Group:      component.Group,
+		Version:    component.Version,
+		PackageURL: m.PackageURL(component.PkgID.PURL),
+		Supplier:   m.Supplier(component.Supplier),
+		Hashes:     m.Hashes(component.Files),
+		Licenses:   m.Licenses(component.Licenses),
+		Properties: m.Properties(component.Properties),
+	}
+	m.componentIDs[component.ID()] = cdxComponent.BOMRef
+
+	return cdxComponent, nil
+}
 
-	// Create package map
-	pkgs := lo.SliceToMap(result.Packages, func(pkg ftypes.Package) (string, Package) {
-		pkgID := lo.Ternary(pkg.ID == "", fmt.Sprintf("%s@%s", pkg.Name, utils.FormatVersion(pkg)), pkg.ID)
-		return pkgID, Package{
-			Type:            result.Type,
-			Metadata:        metadata,
-			Package:         pkg,
-			Vulnerabilities: vulns[pkgID],
+func (m *Marshaler) marshalComponents() (*[]cdx.Component, error) {
+	var cdxComponents []cdx.Component
+	for _, component := range m.bom.Components() {
+		if component.Root {
+			continue
 		}
+		c, err := m.MarshalComponent(component)
+		if err != nil {
+			return nil, xerrors.Errorf("failed to marshal component: %w", err)
+		}
+		cdxComponents = append(cdxComponents, *c)
+	}
+
+	// CycloneDX requires an empty slice rather than a nil slice
+	if len(cdxComponents) == 0 {
+		return &[]cdx.Component{}, nil
+	}
+
+	// Sort components by BOM-Ref
+	sort.Slice(cdxComponents, func(i, j int) bool {
+		return cdxComponents[i].BOMRef < cdxComponents[j].BOMRef
 	})
+	return &cdxComponents, nil
+}
 
-	var directComponents []*core.Component
-	for _, pkg := range pkgs {
-		// Skip indirect dependencies
-		if pkg.Indirect && len(parents[pkg.ID]) != 0 {
+func (m *Marshaler) marshalDependencies() *[]cdx.Dependency {
+	var dependencies []cdx.Dependency
+	for key, rels := range m.bom.Relationships() {
+		ref, ok := m.componentIDs[key]
+		if !ok {
 			continue
 		}
 
-		// Recursive packages from direct dependencies
-		if component, err := e.marshalPackage(pkg, pkgs, make(map[string]*core.Component)); err != nil {
-			return nil, nil
-		} else if component != nil {
-			directComponents = append(directComponents, component)
+		deps := lo.FilterMap(rels, func(rel core.Relationship, _ int) (string, bool) {
+			d, ok := m.componentIDs[rel.Dependency]
+			return d, ok
+		})
+		sort.Strings(deps)
+
+		dependencies = append(dependencies, cdx.Dependency{
+			Ref:          ref,
+			Dependencies: &deps,
+		})
+	}
+
+	// Sort dependencies by BOM-Ref
+	sort.Slice(dependencies, func(i, j int) bool {
+		return dependencies[i].Ref < dependencies[j].Ref
+	})
+	return &dependencies
+}
+
+func (m *Marshaler) marshalVulnerabilities() *[]cdx.Vulnerability {
+	vulns := make(map[string]*cdx.Vulnerability)
+	for id, vv := range m.bom.Vulnerabilities() {
+		bomRef := m.componentIDs[id]
+		for _, v := range vv {
+			// If the same vulnerability affects multiple packages, those packages will be aggregated into one vulnerability.
+			//   Vulnerability component (CVE-2020-26247)
+			//     -> Library component (nokogiri /srv/app1/vendor/bundle/ruby/3.0.0/specifications/nokogiri-1.10.0.gemspec)
+			//     -> Library component (nokogiri /srv/app2/vendor/bundle/ruby/3.0.0/specifications/nokogiri-1.10.0.gemspec)
+			if vuln, ok := vulns[v.ID]; ok {
+				*vuln.Affects = append(*vuln.Affects, m.affects(bomRef, v.InstalledVersion))
+				if v.FixedVersion != "" {
+					// new recommendation
+					rec := fmt.Sprintf("Upgrade %s to version %s", v.PkgName, v.FixedVersion)
+					// previous recommendations
+					recs := strings.Split(vuln.Recommendation, "; ")
+					if !slices.Contains(recs, rec) {
+						recs = append(recs, rec)
+						slices.Sort(recs)
+						vuln.Recommendation = strings.Join(recs, "; ")
+					}
+				}
+			} else {
+				vulns[v.ID] = m.marshalVulnerability(bomRef, v)
+			}
 		}
 	}
 
-	return directComponents, nil
+	vulnList := lo.MapToSlice(vulns, func(_ string, value *cdx.Vulnerability) cdx.Vulnerability {
+		sort.Slice(*value.Affects, func(i, j int) bool {
+			return (*value.Affects)[i].Ref < (*value.Affects)[j].Ref
+		})
+		return *value
+	})
+	sort.Slice(vulnList, func(i, j int) bool {
+		return vulnList[i].ID < vulnList[j].ID
+	})
+	return &vulnList
 }
 
-type Package struct {
-	ftypes.Package
-	Type            ftypes.TargetType
-	Metadata        types.Metadata
-	Vulnerabilities []types.DetectedVulnerability
+// componentType converts the Trivy component type to the CycloneDX component type
+func (*Marshaler) componentType(t core.ComponentType) (cdx.ComponentType, error) {
+	switch t {
+	case core.TypeContainer:
+		return cdx.ComponentTypeContainer, nil
+	case core.TypeApplication:
+		return cdx.ComponentTypeApplication, nil
+	case core.TypeLibrary:
+		return cdx.ComponentTypeLibrary, nil
+	case core.TypeOS:
+		return cdx.ComponentTypeOS, nil
+	case core.TypePlatform:
+		return cdx.ComponentTypePlatform, nil
+	}
+	return "", xerrors.Errorf("unknown component type: %s", t)
 }
 
-func (e *Marshaler) marshalPackage(pkg Package, pkgs map[string]Package, components map[string]*core.Component,
-) (*core.Component, error) {
-	if c, ok := components[pkg.ID]; ok {
-		return c, nil
+func (*Marshaler) PackageURL(p *packageurl.PackageURL) string {
+	if p == nil {
+		return ""
 	}
+	return p.String()
+}
 
-	component, err := pkgComponent(pkg)
-	if err != nil {
-		return nil, xerrors.Errorf("failed to parse pkg: %w", err)
+func (*Marshaler) Supplier(supplier string) *cdx.OrganizationalEntity {
+	if supplier == "" {
+		return nil
 	}
+	return &cdx.OrganizationalEntity{
+		Name: supplier,
+	}
+}
 
-	// Skip component that can't be converted from `Package`
-	if component == nil {
-		return nil, nil
+func (*Marshaler) Hashes(files []core.File) *[]cdx.Hash {
+	hashes := lo.FilterMap(files, func(f core.File, index int) (digest.Digest, bool) {
+		return f.Hash, f.Hash != ""
+	})
+	if len(hashes) == 0 {
+		return nil
 	}
-	components[pkg.ID] = component
 
-	// Iterate dependencies
-	for _, dep := range pkg.DependsOn {
-		childPkg, ok := pkgs[dep]
-		if !ok {
+	var cdxHashes []cdx.Hash
+	for _, h := range hashes {
+		var alg cdx.HashAlgorithm
+		switch h.Algorithm() {
+		case digest.SHA1:
+			alg = cdx.HashAlgoSHA1
+		case digest.SHA256:
+			alg = cdx.HashAlgoSHA256
+		case digest.MD5:
+			alg = cdx.HashAlgoMD5
+		default:
+			log.Logger.Debugf("Unable to convert %q algorithm to CycloneDX format", h.Algorithm())
 			continue
 		}
 
-		child, err := e.marshalPackage(childPkg, pkgs, components)
-		if err != nil {
-			return nil, xerrors.Errorf("failed to parse pkg: %w", err)
-		}
-		component.Components = append(component.Components, child)
+		cdxHashes = append(cdxHashes, cdx.Hash{
+			Algorithm: alg,
+			Value:     h.Encoded(),
+		})
 	}
-	return component, nil
+	return &cdxHashes
 }
 
-func (e *Marshaler) rootComponent(r types.Report) (*core.Component, error) {
-	root := &core.Component{
-		Name: r.ArtifactName,
+func (*Marshaler) Licenses(licenses []string) *cdx.Licenses {
+	if len(licenses) == 0 {
+		return nil
 	}
+	choices := lo.Map(licenses, func(license string, i int) cdx.LicenseChoice {
+		return cdx.LicenseChoice{
+			License: &cdx.License{
+				Name: license,
+			},
+		}
+	})
+	return lo.ToPtr(cdx.Licenses(choices))
+}
 
-	props := []core.Property{
-		{
-			Name:  PropertySchemaVersion,
-			Value: strconv.Itoa(r.SchemaVersion),
-		},
-	}
+func (*Marshaler) Properties(properties []core.Property) *[]cdx.Property {
+	cdxProps := make([]cdx.Property, 0, len(properties))
+	for _, property := range properties {
+		namespace := Namespace
+		if len(property.Namespace) > 0 {
+			namespace = property.Namespace
+		}
 
-	switch r.ArtifactType {
-	case ftypes.ArtifactContainerImage:
-		root.Type = cdx.ComponentTypeContainer
-		props = append(props, core.Property{
-			Name:  PropertyImageID,
-			Value: r.Metadata.ImageID,
+		cdxProps = append(cdxProps, cdx.Property{
+			Name:  namespace + property.Name,
+			Value: property.Value,
 		})
-
-		p, err := purl.New(purl.TypeOCI, r.Metadata, ftypes.Package{})
-		if err != nil {
-			return nil, xerrors.Errorf("failed to new package url for oci: %w", err)
-		}
-		if p != nil {
-			root.PackageURL = p
+	}
+	sort.Slice(cdxProps, func(i, j int) bool {
+		if cdxProps[i].Name != cdxProps[j].Name {
+			return cdxProps[i].Name < cdxProps[j].Name
 		}
+		return cdxProps[i].Value < cdxProps[j].Value
+	})
+	return &cdxProps
+}
 
-	case ftypes.ArtifactVM:
-		root.Type = cdx.ComponentTypeContainer
-	case ftypes.ArtifactFilesystem, ftypes.ArtifactRepository:
-		root.Type = cdx.ComponentTypeApplication
-	case ftypes.ArtifactCycloneDX:
-		return toCoreComponent(r.CycloneDX.Metadata.Component)
+func (*Marshaler) affects(ref, version string) cdx.Affects {
+	return cdx.Affects{
+		Ref: ref,
+		Range: &[]cdx.AffectedVersions{
+			{
+				Version: version,
+				Status:  cdx.VulnerabilityStatusAffected,
+				// "AffectedVersions.Range" is not included, because it does not exist in DetectedVulnerability.
+			},
+		},
 	}
+}
 
-	if r.Metadata.Size != 0 {
-		props = append(props, core.Property{
-			Name:  PropertySize,
-			Value: strconv.FormatInt(r.Metadata.Size, 10),
-		})
+func (*Marshaler) advisories(refs []string) *[]cdx.Advisory {
+	refs = lo.Uniq(refs)
+	advs := lo.FilterMap(refs, func(ref string, _ int) (cdx.Advisory, bool) {
+		return cdx.Advisory{URL: ref}, ref != ""
+	})
+
+	// cyclonedx converts link to empty `[]cdx.Advisory` to `null`
+	// `bom-1.5.schema.json` doesn't support this - `Invalid type. Expected: array, given: null`
+	// we need to explicitly set `nil` for empty `refs` slice
+	if len(advs) == 0 {
+		return nil
 	}
 
-	if len(r.Metadata.RepoDigests) > 0 {
-		props = append(props, core.Property{
-			Name:  PropertyRepoDigest,
-			Value: strings.Join(r.Metadata.RepoDigests, ","),
-		})
+	return &advs
+}
+
+func (m *Marshaler) marshalVulnerability(bomRef string, vuln core.Vulnerability) *cdx.Vulnerability {
+	v := &cdx.Vulnerability{
+		ID:          vuln.ID,
+		Source:      m.source(vuln.DataSource),
+		Ratings:     m.ratings(vuln),
+		CWEs:        m.cwes(vuln.CweIDs),
+		Description: vuln.Description,
+		Advisories:  m.advisories(append([]string{vuln.PrimaryURL}, vuln.References...)),
 	}
-	if len(r.Metadata.DiffIDs) > 0 {
-		props = append(props, core.Property{
-			Name:  PropertyDiffID,
-			Value: strings.Join(r.Metadata.DiffIDs, ","),
-		})
+	if vuln.FixedVersion != "" {
+		v.Recommendation = fmt.Sprintf("Upgrade %s to version %s", vuln.PkgName, vuln.FixedVersion)
 	}
-	if len(r.Metadata.RepoTags) > 0 {
-		props = append(props, core.Property{
-			Name:  PropertyRepoTag,
-			Value: strings.Join(r.Metadata.RepoTags, ","),
-		})
+	if vuln.PublishedDate != nil {
+		v.Published = vuln.PublishedDate.Format(timeLayout)
+	}
+	if vuln.LastModifiedDate != nil {
+		v.Updated = vuln.LastModifiedDate.Format(timeLayout)
 	}
 
-	root.Properties = filterProperties(props)
+	v.Affects = &[]cdx.Affects{m.affects(bomRef, vuln.InstalledVersion)}
 
-	return root, nil
+	return v
 }
 
-func (e *Marshaler) resultComponent(r types.Result, osFound *ftypes.OS) *core.Component {
-	component := &core.Component{
-		Name: r.Target,
-		Properties: []core.Property{
-			{
-				Name:  PropertyType,
-				Value: string(r.Type),
-			},
-			{
-				Name:  PropertyClass,
-				Value: string(r.Class),
-			},
-		},
+func (*Marshaler) source(source *dtypes.DataSource) *cdx.Source {
+	if source == nil {
+		return nil
 	}
 
-	switch r.Class {
-	case types.ClassOSPkg:
-		// UUID needs to be generated since Operating System Component cannot generate PURL.
-		// https://cyclonedx.org/use-cases/#known-vulnerabilities
-		if osFound != nil {
-			component.Name = string(osFound.Family)
-			component.Version = osFound.Name
-		}
-		component.Type = cdx.ComponentTypeOS
-	case types.ClassLangPkg:
-		// UUID needs to be generated since Application Component cannot generate PURL.
-		// https://cyclonedx.org/use-cases/#known-vulnerabilities
-		component.Type = cdx.ComponentTypeApplication
+	return &cdx.Source{
+		Name: string(source.ID),
+		URL:  source.URL,
 	}
-
-	return component
 }
 
-func pkgComponent(pkg Package) (*core.Component, error) {
-	name := pkg.Name
-	version := pkg.Version
-	var group string
-	// there are cases when we can't build purl
-	// e.g. local Go packages
-	if pu := pkg.Identifier.PURL; pu != nil {
-		version = pu.Version
-		// Use `group` field for GroupID and `name` for ArtifactID for java files
-		// https://github.com/aquasecurity/trivy/issues/4675
-		// Use `group` field for npm scopes
-		// https://github.com/aquasecurity/trivy/issues/5908
-		if pu.Type == packageurl.TypeMaven || pu.Type == packageurl.TypeNPM {
-			name = pu.Name
-			group = pu.Namespace
+func (*Marshaler) cwes(cweIDs []string) *[]int {
+	// to skip cdx.Vulnerability.CWEs when generating json
+	// we should return 'clear' nil without 'type' interface part
+	if cweIDs == nil {
+		return nil
+	}
+	var ret []int
+	for _, cweID := range cweIDs {
+		number, err := strconv.Atoi(strings.TrimPrefix(strings.ToLower(cweID), "cwe-"))
+		if err != nil {
+			log.Logger.Debugf("cwe id parse error: %s", err)
+			continue
 		}
+		ret = append(ret, number)
 	}
+	return &ret
+}
 
-	properties := []core.Property{
-		{
-			Name:  PropertyPkgID,
-			Value: pkg.ID,
-		},
-		{
-			Name:  PropertyPkgType,
-			Value: string(pkg.Type),
-		},
-		{
-			Name:  PropertyFilePath,
-			Value: pkg.FilePath,
-		},
-		{
-			Name:  PropertySrcName,
-			Value: pkg.SrcName,
-		},
-		{
-			Name:  PropertySrcVersion,
-			Value: pkg.SrcVersion,
-		},
-		{
-			Name:  PropertySrcRelease,
-			Value: pkg.SrcRelease,
-		},
-		{
-			Name:  PropertySrcEpoch,
-			Value: strconv.Itoa(pkg.SrcEpoch),
-		},
-		{
-			Name:  PropertyModularitylabel,
-			Value: pkg.Modularitylabel,
-		},
-		{
-			Name:  PropertyLayerDigest,
-			Value: pkg.Layer.Digest,
-		},
-		{
-			Name:  PropertyLayerDiffID,
-			Value: pkg.Layer.DiffID,
-		},
+func (m *Marshaler) ratings(vuln core.Vulnerability) *[]cdx.VulnerabilityRating {
+	rates := make([]cdx.VulnerabilityRating, 0) // nolint:gocritic // To export an empty array in JSON
+	for sourceID, severity := range vuln.VendorSeverity {
+		// When the vendor also provides CVSS score/vector
+		if cvss, ok := vuln.CVSS[sourceID]; ok {
+			if cvss.V2Score != 0 || cvss.V2Vector != "" {
+				rates = append(rates, m.ratingV2(sourceID, severity, cvss))
+			}
+			if cvss.V3Score != 0 || cvss.V3Vector != "" {
+				rates = append(rates, m.ratingV3(sourceID, severity, cvss))
+			}
+		} else { // When the vendor provides only severity
+			rate := cdx.VulnerabilityRating{
+				Source: &cdx.Source{
+					Name: string(sourceID),
+				},
+				Severity: m.severity(severity),
+			}
+			rates = append(rates, rate)
+		}
 	}
 
-	return &core.Component{
-		Type:            cdx.ComponentTypeLibrary,
-		Name:            name,
-		Group:           group,
-		Version:         version,
-		PackageURL:      purl.WithPath(pkg.Identifier.PURL, pkg.FilePath),
-		Supplier:        pkg.Maintainer,
-		Licenses:        pkg.Licenses,
-		Hashes:          lo.Ternary(pkg.Digest == "", nil, []digest.Digest{pkg.Digest}),
-		Properties:      filterProperties(properties),
-		Vulnerabilities: pkg.Vulnerabilities,
-	}, nil
+	// For consistency
+	sort.Slice(rates, func(i, j int) bool {
+		if rates[i].Source.Name != rates[j].Source.Name {
+			return rates[i].Source.Name < rates[j].Source.Name
+		}
+		if rates[i].Method != rates[j].Method {
+			return rates[i].Method < rates[j].Method
+		}
+		if rates[i].Score != nil && rates[j].Score != nil {
+			return *rates[i].Score < *rates[j].Score
+		}
+		return rates[i].Vector < rates[j].Vector
+	})
+	return &rates
 }
 
-func filterProperties(props []core.Property) []core.Property {
-	return lo.Filter(props, func(property core.Property, index int) bool {
-		return !(property.Value == "" || (property.Name == PropertySrcEpoch && property.Value == "0"))
-	})
+func (m *Marshaler) ratingV2(sourceID dtypes.SourceID, severity dtypes.Severity, cvss dtypes.CVSS) cdx.VulnerabilityRating {
+	cdxSeverity := m.severity(severity)
+
+	// Trivy keeps only CVSSv3 severity for NVD.
+	// The CVSSv2 severity must be calculated according to CVSSv2 score.
+	if sourceID == vulnerability.NVD {
+		cdxSeverity = m.nvdSeverityV2(cvss.V2Score)
+	}
+	return cdx.VulnerabilityRating{
+		Source: &cdx.Source{
+			Name: string(sourceID),
+		},
+		Score:    &cvss.V2Score,
+		Method:   cdx.ScoringMethodCVSSv2,
+		Severity: cdxSeverity,
+		Vector:   cvss.V2Vector,
+	}
 }
 
-func toCoreComponent(c ftypes.Component) (*core.Component, error) {
-	var props []core.Property
-	for _, prop := range c.Properties {
-		var namespace string
-		name := prop.Name
-		// Separate the Trivy namespace to avoid double spelling of namespaces.
-		if strings.HasPrefix(name, core.Namespace) {
-			name = strings.TrimPrefix(name, core.Namespace)
-			namespace = core.Namespace
-		}
-		props = append(props, core.Property{
-			Namespace: namespace,
-			Name:      name,
-			Value:     prop.Value,
-		})
+func (m *Marshaler) ratingV3(sourceID dtypes.SourceID, severity dtypes.Severity, cvss dtypes.CVSS) cdx.VulnerabilityRating {
+	rate := cdx.VulnerabilityRating{
+		Source: &cdx.Source{
+			Name: string(sourceID),
+		},
+		Score:    &cvss.V3Score,
+		Method:   cdx.ScoringMethodCVSSv3,
+		Severity: m.severity(severity),
+		Vector:   cvss.V3Vector,
 	}
-	var p *purl.PackageURL
-	if c.PackageURL != "" {
-		var err error
-		p, err = purl.FromString(c.PackageURL)
-		if err != nil {
-			return nil, xerrors.Errorf("failed to parse purl: %w", err)
-		}
+	if strings.HasPrefix(cvss.V3Vector, "CVSS:3.1") {
+		rate.Method = cdx.ScoringMethodCVSSv31
 	}
+	return rate
+}
 
-	return &core.Component{
-		Name:       c.Name,
-		Group:      c.Group,
-		PackageURL: p,
-		Type:       cdx.ComponentType(c.Type),
-		Properties: props,
-	}, nil
+// severity converts the Trivy severity to the CycloneDX severity
+func (*Marshaler) severity(s dtypes.Severity) cdx.Severity {
+	switch s {
+	case dtypes.SeverityLow:
+		return cdx.SeverityLow
+	case dtypes.SeverityMedium:
+		return cdx.SeverityMedium
+	case dtypes.SeverityHigh:
+		return cdx.SeverityHigh
+	case dtypes.SeverityCritical:
+		return cdx.SeverityCritical
+	default:
+		return cdx.SeverityUnknown
+	}
+}
+
+func (*Marshaler) nvdSeverityV2(score float64) cdx.Severity {
+	// cf. https://nvd.nist.gov/vuln-metrics/cvss
+	switch {
+	case score < 4.0:
+		return cdx.SeverityInfo
+	case 4.0 <= score && score < 7.0:
+		return cdx.SeverityMedium
+	case 7.0 <= score:
+		return cdx.SeverityHigh
+	}
+	return cdx.SeverityUnknown
 }
diff --git a/pkg/sbom/cyclonedx/marshal_test.go b/pkg/sbom/cyclonedx/marshal_test.go
index 1819daa3a92b..7999ea2eae70 100644
--- a/pkg/sbom/cyclonedx/marshal_test.go
+++ b/pkg/sbom/cyclonedx/marshal_test.go
@@ -2,6 +2,7 @@ package cyclonedx_test
 
 import (
 	"context"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
 	"github.com/package-url/packageurl-go"
 	"testing"
 	"time"
@@ -22,7 +23,23 @@ import (
 	"github.com/aquasecurity/trivy/pkg/uuid"
 )
 
-func TestMarshaler_Marshal(t *testing.T) {
+func TestMarshaler_MarshalReport(t *testing.T) {
+	testSBOM := core.NewBOM()
+	testSBOM.AddComponent(&core.Component{
+		Root: true,
+		Type: core.TypeApplication,
+		Name: "jackson-databind-2.13.4.1.jar",
+		PkgID: core.PkgID{
+			BOMRef: "aff65b54-6009-4c32-968d-748949ef46e8",
+		},
+		Properties: []core.Property{
+			{
+				Name:  "SchemaVersion",
+				Value: "2",
+			},
+		},
+	})
+
 	tests := []struct {
 		name        string
 		inputReport types.Report
@@ -139,6 +156,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						Type:   ftypes.Bundler,
 						Packages: []ftypes.Package{
 							{
+								// This package conflicts
 								ID:      "actionpack@7.0.0",
 								Name:    "actionpack",
 								Version: "7.0.0",
@@ -175,6 +193,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						Type:   ftypes.Bundler,
 						Packages: []ftypes.Package{
 							{
+								// This package conflicts
 								ID:      "actionpack@7.0.0",
 								Name:    "actionpack",
 								Version: "7.0.0",
@@ -238,7 +257,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 				BOMFormat:    "CycloneDX",
 				SpecVersion:  cdx.SpecVersion1_5,
 				JSONSchema:   "http://cyclonedx.org/schema/bom-1.5.schema.json",
-				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000001",
+				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000014",
 				Version:      1,
 				Metadata: &cdx.Metadata{
 					Timestamp: "2021-08-25T12:20:30+00:00",
@@ -303,7 +322,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					{
-						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000003",
+						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000004",
 						Type:    cdx.ComponentTypeApplication,
 						Name:    "app/subproject/Gemfile.lock",
 						Version: "",
@@ -319,7 +338,24 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					{
-						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000004",
+						BOMRef:     "3ff14136-e09f-4df9-80ea-000000000005",
+						Type:       cdx.ComponentTypeLibrary,
+						Name:       "actionpack",
+						Version:    "7.0.0",
+						PackageURL: "pkg:gem/actionpack@7.0.0",
+						Properties: &[]cdx.Property{
+							{
+								Name:  "aquasecurity:trivy:PkgID",
+								Value: "actionpack@7.0.0",
+							},
+							{
+								Name:  "aquasecurity:trivy:PkgType",
+								Value: "bundler",
+							},
+						},
+					},
+					{
+						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000007",
 						Type:    cdx.ComponentTypeApplication,
 						Name:    "app/Gemfile.lock",
 						Version: "",
@@ -335,7 +371,24 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					{
-						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000005",
+						BOMRef:     "3ff14136-e09f-4df9-80ea-000000000008",
+						Type:       cdx.ComponentTypeLibrary,
+						Name:       "actionpack",
+						Version:    "7.0.0",
+						PackageURL: "pkg:gem/actionpack@7.0.0",
+						Properties: &[]cdx.Property{
+							{
+								Name:  "aquasecurity:trivy:PkgID",
+								Value: "actionpack@7.0.0",
+							},
+							{
+								Name:  "aquasecurity:trivy:PkgType",
+								Value: "bundler",
+							},
+						},
+					},
+					{
+						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000009",
 						Type:    cdx.ComponentTypeApplication,
 						Name:    "app/datacollector.deps.json",
 						Version: "",
@@ -351,10 +404,9 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					{
-						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000006",
-						Type:    cdx.ComponentTypeApplication,
-						Name:    "usr/local/bin/tfsec",
-						Version: "",
+						BOMRef: "3ff14136-e09f-4df9-80ea-000000000011",
+						Type:   cdx.ComponentTypeApplication,
+						Name:   "usr/local/bin/tfsec",
 						Properties: &[]cdx.Property{
 							{
 								Name:  "aquasecurity:trivy:Class",
@@ -368,7 +420,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 					},
 					{
 						// Use UUID for local Go packages
-						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000007",
+						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000013",
 						Type:    cdx.ComponentTypeLibrary,
 						Name:    "./api",
 						Version: "(devel)",
@@ -396,23 +448,6 @@ func TestMarshaler_Marshal(t *testing.T) {
 							},
 						},
 					},
-					{
-						BOMRef:     "pkg:gem/actionpack@7.0.0",
-						Type:       cdx.ComponentTypeLibrary,
-						Name:       "actionpack",
-						Version:    "7.0.0",
-						PackageURL: "pkg:gem/actionpack@7.0.0",
-						Properties: &[]cdx.Property{
-							{
-								Name:  "aquasecurity:trivy:PkgID",
-								Value: "actionpack@7.0.0",
-							},
-							{
-								Name:  "aquasecurity:trivy:PkgType",
-								Value: "bundler",
-							},
-						},
-					},
 					{
 						BOMRef:     "pkg:golang/golang.org/x/crypto@v0.0.0-20210421170649-83a5a9bb288b",
 						Type:       cdx.ComponentTypeLibrary,
@@ -497,45 +532,49 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000003",
+						Ref: "3ff14136-e09f-4df9-80ea-000000000004",
 						Dependencies: &[]string{
+							"3ff14136-e09f-4df9-80ea-000000000005",
 							"pkg:gem/actioncontroller@7.0.0",
-							"pkg:gem/actionpack@7.0.0",
 						},
 					},
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000004",
+						Ref:          "3ff14136-e09f-4df9-80ea-000000000005",
+						Dependencies: &[]string{},
+					},
+					{
+						Ref: "3ff14136-e09f-4df9-80ea-000000000007",
 						Dependencies: &[]string{
-							"pkg:gem/actionpack@7.0.0",
+							"3ff14136-e09f-4df9-80ea-000000000008",
 						},
 					},
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000005",
+						Ref:          "3ff14136-e09f-4df9-80ea-000000000008",
+						Dependencies: &[]string{},
+					},
+					{
+						Ref: "3ff14136-e09f-4df9-80ea-000000000009",
 						Dependencies: &[]string{
 							"pkg:nuget/Newtonsoft.Json@9.0.1",
 						},
 					},
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000006",
+						Ref: "3ff14136-e09f-4df9-80ea-000000000011",
 						Dependencies: &[]string{
-							"3ff14136-e09f-4df9-80ea-000000000007",
+							"3ff14136-e09f-4df9-80ea-000000000013",
 							"pkg:golang/golang.org/x/crypto@v0.0.0-20210421170649-83a5a9bb288b",
 						},
 					},
 					{
-						Ref:          "3ff14136-e09f-4df9-80ea-000000000007",
+						Ref:          "3ff14136-e09f-4df9-80ea-000000000013",
 						Dependencies: lo.ToPtr([]string{}),
 					},
 					{
 						Ref: "pkg:gem/actioncontroller@7.0.0",
 						Dependencies: &[]string{
-							"pkg:gem/actionpack@7.0.0",
+							"3ff14136-e09f-4df9-80ea-000000000005",
 						},
 					},
-					{
-						Ref:          "pkg:gem/actionpack@7.0.0",
-						Dependencies: lo.ToPtr([]string{}),
-					},
 					{
 						Ref:          "pkg:golang/golang.org/x/crypto@v0.0.0-20210421170649-83a5a9bb288b",
 						Dependencies: lo.ToPtr([]string{}),
@@ -548,10 +587,10 @@ func TestMarshaler_Marshal(t *testing.T) {
 						Ref: "pkg:oci/rails@sha256%3Aa27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177?arch=arm64&repository_url=index.docker.io%2Flibrary%2Frails",
 						Dependencies: &[]string{
 							"3ff14136-e09f-4df9-80ea-000000000002",
-							"3ff14136-e09f-4df9-80ea-000000000003",
 							"3ff14136-e09f-4df9-80ea-000000000004",
-							"3ff14136-e09f-4df9-80ea-000000000005",
-							"3ff14136-e09f-4df9-80ea-000000000006",
+							"3ff14136-e09f-4df9-80ea-000000000007",
+							"3ff14136-e09f-4df9-80ea-000000000009",
+							"3ff14136-e09f-4df9-80ea-000000000011",
 						},
 					},
 					{
@@ -873,7 +912,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 				BOMFormat:    "CycloneDX",
 				SpecVersion:  cdx.SpecVersion1_5,
 				JSONSchema:   "http://cyclonedx.org/schema/bom-1.5.schema.json",
-				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000001",
+				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000007",
 				Version:      1,
 				Metadata: &cdx.Metadata{
 					Timestamp: "2021-08-25T12:20:30+00:00",
@@ -889,7 +928,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 					},
 					Component: &cdx.Component{
 						Type:       cdx.ComponentTypeContainer,
-						BOMRef:     "3ff14136-e09f-4df9-80ea-000000000002",
+						BOMRef:     "3ff14136-e09f-4df9-80ea-000000000001",
 						PackageURL: "",
 						Name:       "centos:latest",
 						Properties: &[]cdx.Property{
@@ -914,7 +953,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 				},
 				Components: &[]cdx.Component{
 					{
-						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000003",
+						BOMRef:  "3ff14136-e09f-4df9-80ea-000000000002",
 						Type:    cdx.ComponentTypeOS,
 						Name:    string(ftypes.CentOS),
 						Version: "8.3.2011",
@@ -930,7 +969,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					{
-						BOMRef:     "pkg:gem/actionpack@7.0.0?file_path=tools%2Fproject-john%2Fspecifications%2Factionpack.gemspec",
+						BOMRef:     "pkg:gem/actionpack@7.0.0",
 						Type:       cdx.ComponentTypeLibrary,
 						Name:       "actionpack",
 						Version:    "7.0.0",
@@ -955,7 +994,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					{
-						BOMRef:     "pkg:gem/actionpack@7.0.1?file_path=tools%2Fproject-doe%2Fspecifications%2Factionpack.gemspec",
+						BOMRef:     "pkg:gem/actionpack@7.0.1",
 						Type:       cdx.ComponentTypeLibrary,
 						Name:       "actionpack",
 						Version:    "7.0.1",
@@ -1070,15 +1109,15 @@ func TestMarshaler_Marshal(t *testing.T) {
 				},
 				Dependencies: &[]cdx.Dependency{
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000002",
+						Ref: "3ff14136-e09f-4df9-80ea-000000000001",
 						Dependencies: &[]string{
-							"3ff14136-e09f-4df9-80ea-000000000003",
-							"pkg:gem/actionpack@7.0.0?file_path=tools%2Fproject-john%2Fspecifications%2Factionpack.gemspec",
-							"pkg:gem/actionpack@7.0.1?file_path=tools%2Fproject-doe%2Fspecifications%2Factionpack.gemspec",
+							"3ff14136-e09f-4df9-80ea-000000000002",
+							"pkg:gem/actionpack@7.0.0",
+							"pkg:gem/actionpack@7.0.1",
 						},
 					},
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000003",
+						Ref: "3ff14136-e09f-4df9-80ea-000000000002",
 						Dependencies: &[]string{
 							"pkg:rpm/centos/acl@2.2.53-1.el8?arch=aarch64&distro=centos-8.3.2011&epoch=1",
 							// Trivy is unable to identify the direct OS packages as of today.
@@ -1086,11 +1125,11 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					{
-						Ref:          "pkg:gem/actionpack@7.0.0?file_path=tools%2Fproject-john%2Fspecifications%2Factionpack.gemspec",
+						Ref:          "pkg:gem/actionpack@7.0.0",
 						Dependencies: lo.ToPtr([]string{}),
 					},
 					{
-						Ref:          "pkg:gem/actionpack@7.0.1?file_path=tools%2Fproject-doe%2Fspecifications%2Factionpack.gemspec",
+						Ref:          "pkg:gem/actionpack@7.0.1",
 						Dependencies: lo.ToPtr([]string{}),
 					},
 					{
@@ -1163,7 +1202,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						Updated:   "2022-02-22T21:47:00+00:00",
 						Affects: &[]cdx.Affects{
 							{
-								Ref: "pkg:gem/actionpack@7.0.0?file_path=tools%2Fproject-john%2Fspecifications%2Factionpack.gemspec",
+								Ref: "pkg:gem/actionpack@7.0.0",
 								Range: &[]cdx.AffectedVersions{
 									{
 										Version: "7.0.0",
@@ -1172,7 +1211,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 								},
 							},
 							{
-								Ref: "pkg:gem/actionpack@7.0.1?file_path=tools%2Fproject-doe%2Fspecifications%2Factionpack.gemspec",
+								Ref: "pkg:gem/actionpack@7.0.1",
 								Range: &[]cdx.AffectedVersions{
 									{
 										Version: "7.0.1",
@@ -1257,7 +1296,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 				BOMFormat:    "CycloneDX",
 				SpecVersion:  cdx.SpecVersion1_5,
 				JSONSchema:   "http://cyclonedx.org/schema/bom-1.5.schema.json",
-				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000001",
+				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000007",
 				Version:      1,
 				Metadata: &cdx.Metadata{
 					Timestamp: "2021-08-25T12:20:30+00:00",
@@ -1272,7 +1311,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					Component: &cdx.Component{
-						BOMRef: "3ff14136-e09f-4df9-80ea-000000000002",
+						BOMRef: "3ff14136-e09f-4df9-80ea-000000000001",
 						Type:   cdx.ComponentTypeApplication,
 						Name:   "masahiro331/CVE-2021-41098",
 						Properties: &[]cdx.Property{
@@ -1285,7 +1324,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 				},
 				Components: &[]cdx.Component{
 					{
-						BOMRef: "3ff14136-e09f-4df9-80ea-000000000003",
+						BOMRef: "3ff14136-e09f-4df9-80ea-000000000002",
 						Type:   cdx.ComponentTypeApplication,
 						Name:   "Gemfile.lock",
 						Properties: &[]cdx.Property{
@@ -1300,7 +1339,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					{
-						BOMRef: "3ff14136-e09f-4df9-80ea-000000000004",
+						BOMRef: "3ff14136-e09f-4df9-80ea-000000000005",
 						Type:   cdx.ComponentTypeApplication,
 						Name:   "yarn.lock",
 						Properties: &[]cdx.Property{
@@ -1328,7 +1367,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					{
-						BOMRef:     "pkg:maven/org.springframework/spring-web@5.3.22?file_path=spring-web-5.3.22.jar",
+						BOMRef:     "pkg:maven/org.springframework/spring-web@5.3.22",
 						Type:       "library",
 						Name:       "spring-web",
 						Group:      "org.springframework",
@@ -1367,21 +1406,21 @@ func TestMarshaler_Marshal(t *testing.T) {
 				Vulnerabilities: &[]cdx.Vulnerability{},
 				Dependencies: &[]cdx.Dependency{
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000002",
+						Ref: "3ff14136-e09f-4df9-80ea-000000000001",
 						Dependencies: &[]string{
-							"3ff14136-e09f-4df9-80ea-000000000003",
-							"3ff14136-e09f-4df9-80ea-000000000004",
-							"pkg:maven/org.springframework/spring-web@5.3.22?file_path=spring-web-5.3.22.jar",
+							"3ff14136-e09f-4df9-80ea-000000000002",
+							"3ff14136-e09f-4df9-80ea-000000000005",
+							"pkg:maven/org.springframework/spring-web@5.3.22",
 						},
 					},
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000003",
+						Ref: "3ff14136-e09f-4df9-80ea-000000000002",
 						Dependencies: &[]string{
 							"pkg:gem/actioncable@6.1.4.1",
 						},
 					},
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000004",
+						Ref: "3ff14136-e09f-4df9-80ea-000000000005",
 						Dependencies: &[]string{
 							"pkg:npm/%40babel/helper-string-parser@7.23.4",
 						},
@@ -1391,7 +1430,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						Dependencies: lo.ToPtr([]string{}),
 					},
 					{
-						Ref:          "pkg:maven/org.springframework/spring-web@5.3.22?file_path=spring-web-5.3.22.jar",
+						Ref:          "pkg:maven/org.springframework/spring-web@5.3.22",
 						Dependencies: lo.ToPtr([]string{}),
 					},
 					{
@@ -1407,26 +1446,6 @@ func TestMarshaler_Marshal(t *testing.T) {
 				SchemaVersion: report.SchemaVersion,
 				ArtifactName:  "./report.cdx.json",
 				ArtifactType:  ftypes.ArtifactCycloneDX,
-				CycloneDX: &ftypes.CycloneDX{
-					BOMFormat:    "CycloneDX",
-					SpecVersion:  6,
-					SerialNumber: "urn:uuid:ea7360be-19a5-4f61-98dd-d4e170eb6737",
-					Version:      1,
-					Metadata: ftypes.Metadata{
-						Timestamp: "2024-02-16T06:05:53+00:00",
-						Component: ftypes.Component{
-							BOMRef: "aff65b54-6009-4c32-968d-748949ef46e8",
-							Type:   "application",
-							Name:   "jackson-databind-2.13.4.1.jar",
-							Properties: []ftypes.Property{
-								{
-									Name:  "aquasecurity:trivy:SchemaVersion",
-									Value: "2",
-								},
-							},
-						},
-					},
-				},
 				Results: types.Results{
 					{
 						Target: "Java",
@@ -1495,13 +1514,14 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 				},
+				BOM: testSBOM,
 			},
 			want: &cdx.BOM{
 				XMLNS:        "http://cyclonedx.org/schema/bom/1.5",
 				BOMFormat:    "CycloneDX",
 				SpecVersion:  cdx.SpecVersion1_5,
 				JSONSchema:   "http://cyclonedx.org/schema/bom-1.5.schema.json",
-				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000001",
+				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000002",
 				Version:      1,
 				Metadata: &cdx.Metadata{
 					Timestamp: "2021-08-25T12:20:30+00:00",
@@ -1516,7 +1536,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					Component: &cdx.Component{
-						BOMRef: "3ff14136-e09f-4df9-80ea-000000000002",
+						BOMRef: "aff65b54-6009-4c32-968d-748949ef46e8", // The original bom-ref is used
 						Type:   cdx.ComponentTypeApplication,
 						Name:   "jackson-databind-2.13.4.1.jar",
 						Properties: &[]cdx.Property{
@@ -1529,7 +1549,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 				},
 				Components: &[]cdx.Component{
 					{
-						BOMRef:     "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.4.1?file_path=jackson-databind-2.13.4.1.jar",
+						BOMRef:     "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.4.1",
 						Type:       cdx.ComponentTypeLibrary,
 						Group:      "com.fasterxml.jackson.core",
 						Name:       "jackson-databind",
@@ -1579,7 +1599,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						Updated:   "2022-12-20T10:15:00+00:00",
 						Affects: &[]cdx.Affects{
 							{
-								Ref: "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.4.1?file_path=jackson-databind-2.13.4.1.jar",
+								Ref: "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.4.1",
 								Range: &[]cdx.AffectedVersions{
 									{
 										Version: "2.13.4.1",
@@ -1592,13 +1612,13 @@ func TestMarshaler_Marshal(t *testing.T) {
 				},
 				Dependencies: &[]cdx.Dependency{
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000002",
+						Ref: "aff65b54-6009-4c32-968d-748949ef46e8",
 						Dependencies: &[]string{
-							"pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.4.1?file_path=jackson-databind-2.13.4.1.jar",
+							"pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.4.1",
 						},
 					},
 					{
-						Ref:          "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.4.1?file_path=jackson-databind-2.13.4.1.jar",
+						Ref:          "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.4.1",
 						Dependencies: lo.ToPtr([]string{}),
 					},
 				},
@@ -1753,7 +1773,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 				BOMFormat:    "CycloneDX",
 				SpecVersion:  cdx.SpecVersion1_5,
 				JSONSchema:   "http://cyclonedx.org/schema/bom-1.5.schema.json",
-				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000001",
+				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000004",
 				Version:      1,
 				Metadata: &cdx.Metadata{
 					Timestamp: "2021-08-25T12:20:30+00:00",
@@ -1768,7 +1788,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					Component: &cdx.Component{
-						BOMRef: "3ff14136-e09f-4df9-80ea-000000000002",
+						BOMRef: "3ff14136-e09f-4df9-80ea-000000000001",
 						Type:   cdx.ComponentTypeApplication,
 						Name:   "CVE-2023-34468",
 						Properties: &[]cdx.Property{
@@ -1781,7 +1801,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 				},
 				Components: &[]cdx.Component{
 					{
-						BOMRef:     "pkg:maven/org.apache.nifi/nifi-dbcp-base@1.20.0?file_path=nifi-dbcp-base-1.20.0.jar",
+						BOMRef:     "pkg:maven/org.apache.nifi/nifi-dbcp-base@1.20.0",
 						Type:       "library",
 						Name:       "nifi-dbcp-base",
 						Group:      "org.apache.nifi",
@@ -1799,7 +1819,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 					{
-						BOMRef:     "pkg:maven/org.apache.nifi/nifi-hikari-dbcp-service@1.20.0?file_path=nifi-hikari-dbcp-service-1.20.0.jar",
+						BOMRef:     "pkg:maven/org.apache.nifi/nifi-hikari-dbcp-service@1.20.0",
 						Type:       "library",
 						Name:       "nifi-hikari-dbcp-service",
 						Group:      "org.apache.nifi",
@@ -1819,18 +1839,18 @@ func TestMarshaler_Marshal(t *testing.T) {
 				},
 				Dependencies: &[]cdx.Dependency{
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000002",
+						Ref: "3ff14136-e09f-4df9-80ea-000000000001",
 						Dependencies: &[]string{
-							"pkg:maven/org.apache.nifi/nifi-dbcp-base@1.20.0?file_path=nifi-dbcp-base-1.20.0.jar",
-							"pkg:maven/org.apache.nifi/nifi-hikari-dbcp-service@1.20.0?file_path=nifi-hikari-dbcp-service-1.20.0.jar",
+							"pkg:maven/org.apache.nifi/nifi-dbcp-base@1.20.0",
+							"pkg:maven/org.apache.nifi/nifi-hikari-dbcp-service@1.20.0",
 						},
 					},
 					{
-						Ref:          "pkg:maven/org.apache.nifi/nifi-dbcp-base@1.20.0?file_path=nifi-dbcp-base-1.20.0.jar",
+						Ref:          "pkg:maven/org.apache.nifi/nifi-dbcp-base@1.20.0",
 						Dependencies: lo.ToPtr([]string{}),
 					},
 					{
-						Ref:          "pkg:maven/org.apache.nifi/nifi-hikari-dbcp-service@1.20.0?file_path=nifi-hikari-dbcp-service-1.20.0.jar",
+						Ref:          "pkg:maven/org.apache.nifi/nifi-hikari-dbcp-service@1.20.0",
 						Dependencies: lo.ToPtr([]string{}),
 					},
 				},
@@ -1879,7 +1899,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 						Updated:   "2023-06-21T02:20:00+00:00",
 						Affects: &[]cdx.Affects{
 							{
-								Ref: "pkg:maven/org.apache.nifi/nifi-dbcp-base@1.20.0?file_path=nifi-dbcp-base-1.20.0.jar",
+								Ref: "pkg:maven/org.apache.nifi/nifi-dbcp-base@1.20.0",
 								Range: &[]cdx.AffectedVersions{
 									{
 										Version: "1.20.0",
@@ -1888,7 +1908,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 								},
 							},
 							{
-								Ref: "pkg:maven/org.apache.nifi/nifi-hikari-dbcp-service@1.20.0?file_path=nifi-hikari-dbcp-service-1.20.0.jar",
+								Ref: "pkg:maven/org.apache.nifi/nifi-hikari-dbcp-service@1.20.0",
 								Range: &[]cdx.AffectedVersions{
 									{
 										Version: "1.20.0",
@@ -1939,7 +1959,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 				BOMFormat:    "CycloneDX",
 				SpecVersion:  cdx.SpecVersion1_5,
 				JSONSchema:   "http://cyclonedx.org/schema/bom-1.5.schema.json",
-				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000001",
+				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000003",
 				Version:      1,
 				Metadata: &cdx.Metadata{
 					Timestamp: "2021-08-25T12:20:30+00:00",
@@ -1956,7 +1976,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 					Component: &cdx.Component{
 						Type:   cdx.ComponentTypeApplication,
 						Name:   "test-aggregate",
-						BOMRef: "3ff14136-e09f-4df9-80ea-000000000002",
+						BOMRef: "3ff14136-e09f-4df9-80ea-000000000001",
 						Properties: &[]cdx.Property{
 							{
 								Name:  "aquasecurity:trivy:SchemaVersion",
@@ -1967,7 +1987,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 				},
 				Components: &[]cdx.Component{
 					{
-						BOMRef:     "pkg:npm/ruby-typeprof@0.20.1?file_path=usr%2Flocal%2Flib%2Fruby%2Fgems%2F3.1.0%2Fgems%2Ftypeprof-0.21.1%2Fvscode%2Fpackage.json",
+						BOMRef:     "pkg:npm/ruby-typeprof@0.20.1",
 						Type:       "library",
 						Name:       "ruby-typeprof",
 						Version:    "0.20.1",
@@ -2002,13 +2022,13 @@ func TestMarshaler_Marshal(t *testing.T) {
 				Vulnerabilities: &[]cdx.Vulnerability{},
 				Dependencies: &[]cdx.Dependency{
 					{
-						Ref: "3ff14136-e09f-4df9-80ea-000000000002",
+						Ref: "3ff14136-e09f-4df9-80ea-000000000001",
 						Dependencies: &[]string{
-							"pkg:npm/ruby-typeprof@0.20.1?file_path=usr%2Flocal%2Flib%2Fruby%2Fgems%2F3.1.0%2Fgems%2Ftypeprof-0.21.1%2Fvscode%2Fpackage.json",
+							"pkg:npm/ruby-typeprof@0.20.1",
 						},
 					},
 					{
-						Ref:          "pkg:npm/ruby-typeprof@0.20.1?file_path=usr%2Flocal%2Flib%2Fruby%2Fgems%2F3.1.0%2Fgems%2Ftypeprof-0.21.1%2Fvscode%2Fpackage.json",
+						Ref:          "pkg:npm/ruby-typeprof@0.20.1",
 						Dependencies: lo.ToPtr([]string{}),
 					},
 				},
@@ -2027,7 +2047,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 				BOMFormat:    "CycloneDX",
 				SpecVersion:  cdx.SpecVersion1_5,
 				JSONSchema:   "http://cyclonedx.org/schema/bom-1.5.schema.json",
-				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000001",
+				SerialNumber: "urn:uuid:3ff14136-e09f-4df9-80ea-000000000002",
 				Version:      1,
 				Metadata: &cdx.Metadata{
 					Timestamp: "2021-08-25T12:20:30+00:00",
@@ -2044,7 +2064,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 					Component: &cdx.Component{
 						Type:   cdx.ComponentTypeApplication,
 						Name:   "empty/path",
-						BOMRef: "3ff14136-e09f-4df9-80ea-000000000002",
+						BOMRef: "3ff14136-e09f-4df9-80ea-000000000001",
 						Properties: &[]cdx.Property{
 							{
 								Name:  "aquasecurity:trivy:SchemaVersion",
@@ -2053,11 +2073,11 @@ func TestMarshaler_Marshal(t *testing.T) {
 						},
 					},
 				},
-				Components:      lo.ToPtr([]cdx.Component{}),
+				Components:      &[]cdx.Component{},
 				Vulnerabilities: &[]cdx.Vulnerability{},
 				Dependencies: &[]cdx.Dependency{
 					{
-						Ref:          "3ff14136-e09f-4df9-80ea-000000000002",
+						Ref:          "3ff14136-e09f-4df9-80ea-000000000001",
 						Dependencies: lo.ToPtr([]string{}),
 					},
 				},
@@ -2071,7 +2091,7 @@ func TestMarshaler_Marshal(t *testing.T) {
 			uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d")
 
 			marshaler := cyclonedx.NewMarshaler("dev")
-			got, err := marshaler.Marshal(ctx, tt.inputReport)
+			got, err := marshaler.MarshalReport(ctx, tt.inputReport)
 			require.NoError(t, err)
 			assert.Equal(t, tt.want, got)
 		})
diff --git a/pkg/sbom/cyclonedx/testdata/sad/invalid-purl.json b/pkg/sbom/cyclonedx/testdata/happy/invalid-purl.json
similarity index 100%
rename from pkg/sbom/cyclonedx/testdata/sad/invalid-purl.json
rename to pkg/sbom/cyclonedx/testdata/happy/invalid-purl.json
diff --git a/pkg/sbom/cyclonedx/testdata/happy/kbom.json b/pkg/sbom/cyclonedx/testdata/happy/kbom.json
index 3219cf7efaf6..7d4a5b82b65e 100644
--- a/pkg/sbom/cyclonedx/testdata/happy/kbom.json
+++ b/pkg/sbom/cyclonedx/testdata/happy/kbom.json
@@ -54,17 +54,7 @@
     {
       "bom-ref": "a62abb1f-cb38-4fde-90f3-2bda3b87ddb2",
       "type": "application",
-      "name": "node-core-components",
-      "properties": [
-        {
-          "name": "aquasecurity:trivy:Class",
-          "value": "lang-pkgs"
-        },
-        {
-          "name": "aquasecurity:trivy:Type",
-          "value": "golang"
-        }
-      ]
+      "name": "node-core-components"
     },
     {
       "bom-ref": "a6350ac3-52f6-4c5f-a3e3-184b9a634bef",
diff --git a/pkg/sbom/cyclonedx/testdata/sad/invalid-serial.json b/pkg/sbom/cyclonedx/testdata/sad/invalid-serial.json
new file mode 100644
index 000000000000..4edeb3f3ec13
--- /dev/null
+++ b/pkg/sbom/cyclonedx/testdata/sad/invalid-serial.json
@@ -0,0 +1,6 @@
+{
+  "bomFormat": "CycloneDX",
+  "specVersion": "1.5",
+  "serialNumber": 1000000,
+  "version": 1
+}
\ No newline at end of file
diff --git a/pkg/sbom/cyclonedx/unmarshal.go b/pkg/sbom/cyclonedx/unmarshal.go
index 59c36bde5226..b234b85fd637 100644
--- a/pkg/sbom/cyclonedx/unmarshal.go
+++ b/pkg/sbom/cyclonedx/unmarshal.go
@@ -3,33 +3,26 @@ package cyclonedx
 import (
 	"bytes"
 	"errors"
-	"fmt"
 	"io"
-	"sort"
-	"strconv"
+	"strings"
 
 	cdx "github.com/CycloneDX/cyclonedx-go"
 	"github.com/package-url/packageurl-go"
 	"github.com/samber/lo"
-	"golang.org/x/exp/maps"
+	"go.uber.org/zap"
 	"golang.org/x/xerrors"
 
-	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
+	"github.com/aquasecurity/trivy/pkg/digest"
 	"github.com/aquasecurity/trivy/pkg/log"
-	"github.com/aquasecurity/trivy/pkg/purl"
-	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx/core"
-	"github.com/aquasecurity/trivy/pkg/types"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
 )
 
 var (
-	ErrPURLEmpty = errors.New("purl empty error")
+	ErrUnsupportedType = errors.New("unsupported type")
 )
 
 type BOM struct {
-	*types.SBOM
-
-	dependencies map[string][]string
-	components   map[string]cdx.Component
+	*core.BOM
 }
 
 func DecodeJSON(r io.Reader) (*cdx.BOM, error) {
@@ -41,417 +34,151 @@ func DecodeJSON(r io.Reader) (*cdx.BOM, error) {
 	return bom, nil
 }
 
-func (c *BOM) UnmarshalJSON(b []byte) error {
-	log.Logger.Debug("Unmarshaling CycloneDX JSON...")
-	if c.SBOM == nil {
-		c.SBOM = &types.SBOM{}
+func (b *BOM) UnmarshalJSON(data []byte) error {
+	log.Logger.Debug("Unmarshalling CycloneDX JSON...")
+	if b.BOM == nil {
+		b.BOM = core.NewBOM()
 	}
-	bom, err := DecodeJSON(bytes.NewReader(b))
+
+	cdxBOM, err := DecodeJSON(bytes.NewReader(data))
 	if err != nil {
 		return xerrors.Errorf("CycloneDX decode error: %w", err)
 	}
 
-	if !core.IsTrivySBOM(bom) {
+	if !IsTrivySBOM(cdxBOM) {
 		log.Logger.Warnf("Third-party SBOM may lead to inaccurate vulnerability detection")
 		log.Logger.Warnf("Recommend using Trivy to generate SBOMs")
 	}
 
-	if err = c.parseSBOM(bom); err != nil {
+	if err = b.parseBOM(cdxBOM); err != nil {
 		return xerrors.Errorf("failed to parse sbom: %w", err)
 	}
 
-	sort.Slice(c.Applications, func(i, j int) bool {
-		if c.Applications[i].Type != c.Applications[j].Type {
-			return c.Applications[i].Type < c.Applications[j].Type
-		}
-		return c.Applications[i].FilePath < c.Applications[j].FilePath
-	})
-
-	var metadata ftypes.Metadata
-	if bom.Metadata != nil {
-		metadata.Timestamp = bom.Metadata.Timestamp
-		if bom.Metadata.Component != nil {
-			metadata.Component = toTrivyCdxComponent(lo.FromPtr(bom.Metadata.Component))
-		}
-	}
-
-	var components []ftypes.Component
-	for _, component := range lo.FromPtr(bom.Components) {
-		components = append(components, toTrivyCdxComponent(component))
-	}
+	// Store the original metadata
+	b.BOM.SerialNumber = cdxBOM.SerialNumber
+	b.BOM.Version = cdxBOM.Version
 
-	// Keep the original SBOM
-	c.CycloneDX = &ftypes.CycloneDX{
-		BOMFormat:    bom.BOMFormat,
-		SpecVersion:  ftypes.SpecVersion(bom.SpecVersion),
-		SerialNumber: bom.SerialNumber,
-		Version:      bom.Version,
-		Metadata:     metadata,
-		Components:   components,
-	}
 	return nil
 }
 
-func (c *BOM) parseSBOM(bom *cdx.BOM) error {
-	c.dependencies = dependencyMap(bom.Dependencies)
-	c.components = componentMap(bom.Metadata, bom.Components)
-	var seen = make(map[string]struct{})
-	for bomRef := range c.dependencies {
-		component := c.components[bomRef]
-		switch component.Type {
-		case cdx.ComponentTypeOS: // OS info and OS packages
-			seen[component.BOMRef] = struct{}{}
-			c.OS = toOS(component)
-			pkgInfo, err := c.parseOSPkgs(component, seen)
-			if err != nil {
-				return xerrors.Errorf("failed to parse os packages: %w", err)
-			}
-			c.Packages = append(c.Packages, pkgInfo)
-		case cdx.ComponentTypeApplication: // It would be a lock file in a CycloneDX report generated by Trivy
-			if core.LookupProperty(component.Properties, PropertyType) == "" {
-				continue
-			}
-			app, err := c.parseLangPkgs(component, seen)
-			if err != nil {
-				return xerrors.Errorf("failed to parse language packages: %w", err)
-			}
-			c.Applications = append(c.Applications, *app)
-		case cdx.ComponentTypeLibrary:
-			// It is an individual package not associated with any lock files and should be processed later.
-			// e.g. .gemspec, .egg and .wheel
-			continue
-		}
+func (b *BOM) parseBOM(bom *cdx.BOM) error {
+	// Convert all CycloneDX components into Trivy components
+	components := b.parseComponents(bom.Components)
+
+	// Convert the metadata component into Trivy component
+	mComponent, err := b.parseMetadataComponent(bom)
+	if err != nil {
+		return xerrors.Errorf("failed to parse root component: %w", err)
+	} else if mComponent != nil {
+		components[mComponent.PkgID.BOMRef] = mComponent
 	}
 
-	var libComponents []cdx.Component
-	for ref, component := range c.components {
-		if _, ok := seen[ref]; ok {
+	// Parse dependencies and build relationships
+	for _, dep := range lo.FromPtr(bom.Dependencies) {
+		ref, ok := components[dep.Ref]
+		if !ok {
 			continue
 		}
-		if component.Type == cdx.ComponentTypeLibrary || component.PackageURL != "" {
-			libComponents = append(libComponents, component)
-		}
-
-		// For third-party SBOMs.
-		// If there are no operating-system dependent libraries, make them implicitly dependent.
-		if component.Type == cdx.ComponentTypeOS {
-			if lo.IsNotEmpty(c.OS) {
-				return xerrors.New("multiple OSes are not supported")
+		for _, depRef := range lo.FromPtr(dep.Dependencies) {
+			dependency, ok := components[depRef]
+			if !ok {
+				continue
 			}
-			c.OS = toOS(component)
+			b.BOM.AddRelationship(ref, dependency, core.RelationshipDependsOn)
 		}
 	}
-
-	pkgInfos, aggregatedApps, err := aggregatePkgs(libComponents)
-	if err != nil {
-		return xerrors.Errorf("failed to aggregate packages: %w", err)
-	}
-
-	// For third party SBOMs.
-	// If a package that depends on the operating-system did not exist,
-	// but an os package is found during aggregate, it is used.
-	if len(c.Packages) == 0 && len(pkgInfos) != 0 {
-		if !c.OS.Detected() {
-			log.Logger.Warnf("Ignore the OS package as no OS information is found.")
-		} else {
-			c.Packages = pkgInfos
-		}
-	}
-	c.Applications = append(c.Applications, aggregatedApps...)
-
 	return nil
 }
 
-func (c *BOM) parseOSPkgs(component cdx.Component, seen map[string]struct{}) (ftypes.PackageInfo, error) {
-	components := c.walkDependencies(component.BOMRef, make(map[string]struct{}))
-	pkgs, err := parsePkgs(components, seen)
-	if err != nil {
-		return ftypes.PackageInfo{}, xerrors.Errorf("failed to parse os package: %w", err)
+func (b *BOM) parseMetadataComponent(bom *cdx.BOM) (*core.Component, error) {
+	if bom.Metadata == nil || bom.Metadata.Component == nil {
+		return nil, nil
 	}
-
-	return ftypes.PackageInfo{
-		Packages: pkgs,
-	}, nil
-}
-
-func (c *BOM) parseLangPkgs(component cdx.Component, seen map[string]struct{}) (*ftypes.Application, error) {
-	components := c.walkDependencies(component.BOMRef, make(map[string]struct{}))
-	components = lo.UniqBy(components, func(c cdx.Component) string {
-		return c.BOMRef
-	})
-
-	app := toApplication(component)
-	pkgs, err := parsePkgs(components, seen)
+	root, err := b.parseComponent(*bom.Metadata.Component)
 	if err != nil {
-		return nil, xerrors.Errorf("failed to parse language-specific packages: %w", err)
+		return nil, xerrors.Errorf("failed to parse metadata component: %w", err)
 	}
-	app.Libraries = pkgs
-
-	return app, nil
+	root.Root = true
+	b.BOM.AddComponent(root)
+	return root, nil
 }
 
-func parsePkgs(components []cdx.Component, seen map[string]struct{}) ([]ftypes.Package, error) {
-	var pkgs []ftypes.Package
-	for _, com := range components {
-		seen[com.BOMRef] = struct{}{}
-		pkgURL, pkg, err := toPackage(com)
-		if errors.Is(err, ErrPURLEmpty) {
+func (b *BOM) parseComponents(cdxComponents *[]cdx.Component) map[string]*core.Component {
+	components := make(map[string]*core.Component)
+	for _, component := range lo.FromPtr(cdxComponents) {
+		c, err := b.parseComponent(component)
+		if errors.Is(err, ErrUnsupportedType) {
+			log.Logger.Infow("Skipping the component with the unsupported type",
+				zap.String("bom-ref", component.BOMRef), zap.String("type", string(component.Type)))
 			continue
 		} else if err != nil {
-			return nil, xerrors.Errorf("failed to parse language package: %w", err)
-		}
-
-		// Skip unsupported package types
-		if pkgURL.Class() == types.ClassUnknown {
+			log.Logger.Warnw("Failed to parse component: %s", zap.Error(err))
 			continue
 		}
-		pkgs = append(pkgs, *pkg)
-	}
-	return pkgs, nil
-}
-
-// walkDependencies takes all nested dependencies of the root component.
-func (c *BOM) walkDependencies(rootRef string, uniqComponents map[string]struct{}) []cdx.Component {
-	// e.g. Library A, B, C, D and E will be returned as dependencies of Application 1.
-	// type: Application 1
-	//   - type: Library A
-	//     - type: Library B
-	//   - type: Application 2
-	//     - type: Library C
-	//     - type: Application 3
-	//       - type: Library D
-	//       - type: Library E
-	var components []cdx.Component
-	for _, dep := range c.dependencies[rootRef] {
-		component, ok := c.components[dep]
-		if !ok {
-			continue
-		}
-
-		// there are cases of looped components:
-		// type: Application 1
-		//  - type: Library A
-		//    - type: Library B
-		// 	    - type: Library A
-		// ...
-		// use uniqComponents to fix infinite loop
-		if _, ok = uniqComponents[dep]; ok {
-			continue
-		}
-		uniqComponents[dep] = struct{}{}
-
-		// Take only 'Libraries'
-		if component.Type == cdx.ComponentTypeLibrary {
-			components = append(components, component)
-		}
 
-		components = append(components, c.walkDependencies(dep, uniqComponents)...)
+		b.BOM.AddComponent(c)
+		components[component.BOMRef] = c
 	}
 	return components
 }
 
-func componentMap(metadata *cdx.Metadata, components *[]cdx.Component) map[string]cdx.Component {
-	cmap := make(map[string]cdx.Component)
-
-	for _, component := range lo.FromPtr(components) {
-		cmap[component.BOMRef] = component
-	}
-	if metadata != nil && metadata.Component != nil {
-		cmap[metadata.Component.BOMRef] = *metadata.Component
-	}
-	return cmap
-}
-
-func dependencyMap(deps *[]cdx.Dependency) map[string][]string {
-	depMap := make(map[string][]string)
-
-	for _, dep := range lo.FromPtr(deps) {
-		if _, ok := depMap[dep.Ref]; ok {
-			continue
-		}
-		var refs []string
-		if dep.Dependencies != nil {
-			refs = append(refs, *dep.Dependencies...)
-		}
-
-		depMap[dep.Ref] = refs
-	}
-	return depMap
-}
-
-func aggregatePkgs(libs []cdx.Component) ([]ftypes.PackageInfo, []ftypes.Application, error) {
-	osPkgMap := make(map[string]ftypes.Packages)
-	langPkgMap := make(map[ftypes.LangType]ftypes.Packages)
-	for _, lib := range libs {
-		pkgURL, pkg, err := toPackage(lib)
-		if errors.Is(err, ErrPURLEmpty) {
-			continue
-		} else if err != nil {
-			return nil, nil, xerrors.Errorf("failed to parse the component: %w", err)
-		}
-
-		switch pkgURL.Class() {
-		case types.ClassOSPkg:
-			osPkgMap[pkgURL.Type] = append(osPkgMap[pkgURL.Type], *pkg)
-		case types.ClassLangPkg:
-			langType := pkgURL.LangType()
-			langPkgMap[langType] = append(langPkgMap[langType], *pkg)
-		}
-	}
-
-	if len(osPkgMap) > 1 {
-		return nil, nil, xerrors.Errorf("multiple types of OS packages in SBOM are not supported (%q)",
-			maps.Keys(osPkgMap))
-	}
-
-	var osPkgs ftypes.PackageInfo
-	for _, pkgs := range osPkgMap {
-		// Just take the first element
-		sort.Sort(pkgs)
-		osPkgs = ftypes.PackageInfo{Packages: pkgs}
-		break
-	}
-
-	var apps []ftypes.Application
-	for pkgType, pkgs := range langPkgMap {
-		sort.Sort(pkgs)
-		apps = append(apps, ftypes.Application{
-			Type:      pkgType,
-			Libraries: pkgs,
-		})
-	}
-	return []ftypes.PackageInfo{osPkgs}, apps, nil
-}
-
-func toOS(component cdx.Component) ftypes.OS {
-	return ftypes.OS{
-		Family: ftypes.OSType(component.Name),
-		Name:   component.Version,
-	}
-}
-
-func toApplication(component cdx.Component) *ftypes.Application {
-	return &ftypes.Application{
-		Type:     ftypes.LangType(core.LookupProperty(component.Properties, PropertyType)),
-		FilePath: component.Name,
-	}
-}
-
-func toPackage(component cdx.Component) (*purl.PackageURL, *ftypes.Package, error) {
-	if component.PackageURL == "" {
-		log.Logger.Warnf("Skip the component (BOM-Ref: %s) as the PURL is empty", component.BOMRef)
-		return nil, nil, ErrPURLEmpty
-	}
-	p, err := purl.FromString(component.PackageURL)
+func (b *BOM) parseComponent(c cdx.Component) (*core.Component, error) {
+	componentType, err := b.unmarshalType(c.Type)
 	if err != nil {
-		return nil, nil, xerrors.Errorf("failed to parse purl: %w", err)
+		return nil, xerrors.Errorf("failed to unmarshal component type: %w", err)
 	}
 
-	pkg := p.Package()
-	// Trivy's marshall loses case-sensitivity in PURL used in SBOM for packages (Go, Npm, PyPI),
-	// so we have to use an original package name
-	pkg.Name = packageName(p.Type, pkg.Name, component)
-	pkg.Licenses = parsePackageLicenses(component.Licenses)
-
-	for key, value := range core.UnmarshalProperties(component.Properties) {
-		switch key {
-		case PropertyPkgID:
-			pkg.ID = value
-		case PropertySrcName:
-			pkg.SrcName = value
-		case PropertySrcVersion:
-			pkg.SrcVersion = value
-		case PropertySrcRelease:
-			pkg.SrcRelease = value
-		case PropertySrcEpoch:
-			pkg.SrcEpoch, err = strconv.Atoi(value)
-			if err != nil {
-				return nil, nil, xerrors.Errorf("failed to parse source epoch: %w", err)
-			}
-		case PropertyModularitylabel:
-			pkg.Modularitylabel = value
-		case PropertyLayerDiffID:
-			pkg.Layer.DiffID = value
-		case PropertyLayerDigest:
-			pkg.Layer.Digest = value
-		case PropertyFilePath:
-			pkg.FilePath = value
+	// Parse PURL
+	var purl packageurl.PackageURL
+	if c.PackageURL != "" {
+		purl, err = packageurl.FromString(c.PackageURL)
+		if err != nil {
+			return nil, xerrors.Errorf("failed to parse PURL: %w", err)
 		}
 	}
 
-	if pkg.FilePath != "" {
-		p.FilePath = pkg.FilePath
-	}
-	pkg.Identifier.BOMRef = component.BOMRef
-
-	if p.Class() == types.ClassOSPkg {
-		fillSrcPkg(pkg)
-	}
-
-	return p, pkg, nil
+	component := &core.Component{
+		Type:     componentType,
+		Name:     c.Name,
+		Group:    c.Group,
+		Version:  c.Version,
+		Licenses: b.unmarshalLicenses(c.Licenses),
+		Files: lo.Map(b.unmarshalHashes(c.Hashes), func(d digest.Digest, _ int) core.File {
+			return core.File{Hash: d} // CycloneDX doesn't have a file path for the hash
+		}),
+		PkgID: core.PkgID{
+			PURL:   &purl,
+			BOMRef: c.BOMRef,
+		},
+		Supplier:   b.unmarshalSupplier(c.Supplier),
+		Properties: b.unmarshalProperties(c.Properties),
+	}
+
+	return component, nil
 }
 
-func fillSrcPkg(pkg *ftypes.Package) {
-	// Fill source package information for components in third-party SBOMs .
-	if pkg.SrcName == "" {
-		pkg.SrcName = pkg.Name
-	}
-	if pkg.SrcVersion == "" {
-		pkg.SrcVersion = pkg.Version
-	}
-	if pkg.SrcRelease == "" {
-		pkg.SrcRelease = pkg.Release
-	}
-	if pkg.SrcEpoch == 0 {
-		pkg.SrcEpoch = pkg.Epoch
-	}
-}
-
-func toTrivyCdxComponent(component cdx.Component) ftypes.Component {
-	var props []ftypes.Property
-	for _, prop := range lo.FromPtr(component.Properties) {
-		props = append(props, ftypes.Property{
-			Name:  prop.Name,
-			Value: prop.Value,
-		})
-	}
-	return ftypes.Component{
-		BOMRef:     component.BOMRef,
-		MIMEType:   component.MIMEType,
-		Type:       ftypes.ComponentType(component.Type),
-		Name:       component.Name,
-		Group:      component.Group,
-		Version:    component.Version,
-		PackageURL: component.PackageURL,
-		Properties: props,
-	}
-}
-
-func packageName(typ, pkgNameFromPurl string, component cdx.Component) string {
-	if typ == packageurl.TypeMaven || typ == packageurl.TypeNPM {
-		// Maven uses `Group` field for `GroupID`
-		// Npm uses `Group` field for `Scope`
-		if component.Group != "" {
-			return fmt.Sprintf("%s%s%s", component.Group, packageNameSeparator(typ), component.Name)
-		} else {
-			// use name derived from purl if `Group` doesn't exist
-			return pkgNameFromPurl
-		}
-	}
-	return component.Name
-}
-
-// packageNameSeparator selects separator to join `group` and `name` fields of the component
-func packageNameSeparator(typ string) string {
-	if typ == packageurl.TypeMaven {
-		return ":"
-	}
-	return "/"
+func (b *BOM) unmarshalType(t cdx.ComponentType) (core.ComponentType, error) {
+	var ctype core.ComponentType
+	switch t {
+	case cdx.ComponentTypeContainer:
+		ctype = core.TypeContainer
+	case cdx.ComponentTypeApplication:
+		ctype = core.TypeApplication
+	case cdx.ComponentTypeLibrary:
+		ctype = core.TypeLibrary
+	case cdx.ComponentTypeOS:
+		ctype = core.TypeOS
+	case cdx.ComponentTypePlatform:
+		ctype = core.TypePlatform
+	default:
+		return "", ErrUnsupportedType
+	}
+	return ctype, nil
 }
 
 // parsePackageLicenses checks all supported license fields and returns a list of licenses.
 // https://cyclonedx.org/docs/1.5/json/#components_items_licenses
-func parsePackageLicenses(l *cdx.Licenses) []string {
+func (b *BOM) unmarshalLicenses(l *cdx.Licenses) []string {
 	var licenses []string
 	for _, license := range lo.FromPtr(l) {
 		if license.License != nil {
@@ -475,3 +202,61 @@ func parsePackageLicenses(l *cdx.Licenses) []string {
 	}
 	return licenses
 }
+
+func (b *BOM) unmarshalHashes(hashes *[]cdx.Hash) []digest.Digest {
+	var digests []digest.Digest
+	for _, h := range lo.FromPtr(hashes) {
+		var alg digest.Algorithm
+		switch h.Algorithm {
+		case cdx.HashAlgoSHA1:
+			alg = digest.SHA1
+		case cdx.HashAlgoSHA256:
+			alg = digest.SHA256
+		case cdx.HashAlgoMD5:
+			alg = digest.MD5
+		default:
+			log.Logger.Warnf("Unsupported hash algorithm: %s", h.Algorithm)
+		}
+		digests = append(digests, digest.NewDigestFromString(alg, h.Value))
+	}
+	return digests
+}
+
+func (b *BOM) unmarshalSupplier(supplier *cdx.OrganizationalEntity) string {
+	if supplier == nil {
+		return ""
+	}
+	return supplier.Name
+}
+
+func (b *BOM) unmarshalProperties(properties *[]cdx.Property) []core.Property {
+	var props []core.Property
+	for _, p := range lo.FromPtr(properties) {
+		props = append(props, core.Property{
+			Name:  strings.TrimPrefix(p.Name, Namespace),
+			Value: p.Value,
+		})
+	}
+	return props
+}
+
+func IsTrivySBOM(c *cdx.BOM) bool {
+	if c == nil || c.Metadata == nil || c.Metadata.Tools == nil {
+		return false
+	}
+
+	for _, component := range lo.FromPtr(c.Metadata.Tools.Components) {
+		if component.Group == ToolVendor && component.Name == ToolName {
+			return true
+		}
+	}
+
+	// Metadata.Tools array is deprecated (as of CycloneDX v1.5). We check this field for backward compatibility.
+	// cf. https://github.com/CycloneDX/cyclonedx-go/blob/b9654ae9b4705645152d20eb9872b5f3d73eac49/cyclonedx.go#L988
+	for _, tool := range lo.FromPtr(c.Metadata.Tools.Tools) {
+		if tool.Vendor == ToolVendor && tool.Name == ToolName {
+			return true
+		}
+	}
+	return false
+}
diff --git a/pkg/sbom/cyclonedx/unmarshal_test.go b/pkg/sbom/cyclonedx/unmarshal_test.go
index 61b24082343e..6ffd9ce89ad9 100644
--- a/pkg/sbom/cyclonedx/unmarshal_test.go
+++ b/pkg/sbom/cyclonedx/unmarshal_test.go
@@ -3,6 +3,8 @@ package cyclonedx_test
 import (
 	"encoding/json"
 	"github.com/aquasecurity/trivy/pkg/purl"
+	sbomio "github.com/aquasecurity/trivy/pkg/sbom/io"
+	"github.com/aquasecurity/trivy/pkg/types"
 	"github.com/package-url/packageurl-go"
 	"os"
 	"testing"
@@ -12,7 +14,6 @@ import (
 
 	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx"
-	"github.com/aquasecurity/trivy/pkg/types"
 )
 
 func TestUnmarshaler_Unmarshal(t *testing.T) {
@@ -26,14 +27,25 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 			name:      "happy path",
 			inputFile: "testdata/happy/bom.json",
 			want: types.SBOM{
-				OS: ftypes.OS{
-					Family: "alpine",
-					Name:   "3.16.0",
+				Metadata: types.Metadata{
+					ImageID: "sha256:49193a2310dbad4c02382da87ac624a80a92387a4f7536235f9ba590e5bcd7b5",
+					DiffIDs: []string{
+						"sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
+						"sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3",
+					},
+					RepoTags: []string{
+						"maven-test-project:latest",
+					},
+					OS: &ftypes.OS{
+						Family: "alpine",
+						Name:   "3.16.0",
+					},
 				},
 				Packages: []ftypes.PackageInfo{
 					{
 						Packages: ftypes.Packages{
 							{
+								ID:         "musl@1.2.3-r0",
 								Name:       "musl",
 								Version:    "1.2.3-r0",
 								SrcName:    "musl",
@@ -67,6 +79,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 						FilePath: "app/composer/composer.lock",
 						Libraries: ftypes.Packages{
 							{
+								ID:      "pear/log@1.13.1",
 								Name:    "pear/log",
 								Version: "1.13.1",
 								Identifier: ftypes.PkgIdentifier{
@@ -83,7 +96,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 								},
 							},
 							{
-
+								ID:      "pear/pear_exception@v1.0.0",
 								Name:    "pear/pear_exception",
 								Version: "v1.0.0",
 								Identifier: ftypes.PkgIdentifier{
@@ -106,6 +119,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 						FilePath: "app/gobinary/gobinary",
 						Libraries: ftypes.Packages{
 							{
+								ID:      "github.com/package-url/packageurl-go@v0.1.1-0.20220203205134-d70459300c8a",
 								Name:    "github.com/package-url/packageurl-go",
 								Version: "v0.1.1-0.20220203205134-d70459300c8a",
 								Identifier: ftypes.PkgIdentifier{
@@ -128,6 +142,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 						FilePath: "app/gradle/target/gradle.lockfile",
 						Libraries: ftypes.Packages{
 							{
+								ID:   "com.example:example:0.0.1",
 								Name: "com.example:example",
 								Identifier: ftypes.PkgIdentifier{
 									PURL: &packageurl.PackageURL{
@@ -149,6 +164,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 						Type: ftypes.Jar,
 						Libraries: ftypes.Packages{
 							{
+								ID:   "org.codehaus.mojo:child-project:1.0",
 								Name: "org.codehaus.mojo:child-project",
 								Identifier: ftypes.PkgIdentifier{
 									PURL: &packageurl.PackageURL{
@@ -172,6 +188,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 						FilePath: "",
 						Libraries: ftypes.Packages{
 							{
+								ID:      "@example/bootstrap@5.0.2",
 								Name:    "@example/bootstrap",
 								Version: "5.0.2",
 								Identifier: ftypes.PkgIdentifier{
@@ -198,13 +215,10 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 			name:      "happy path KBOM",
 			inputFile: "testdata/happy/kbom.json",
 			want: types.SBOM{
-				OS: ftypes.OS{
-					Family: "ubuntu",
-					Name:   "22.04.2",
-				},
-				Packages: []ftypes.PackageInfo{
-					{
-						FilePath: "",
+				Metadata: types.Metadata{
+					OS: &ftypes.OS{
+						Family: "ubuntu",
+						Name:   "22.04.2",
 					},
 				},
 				Applications: []ftypes.Application{
@@ -212,6 +226,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 						Type: ftypes.GoBinary,
 						Libraries: ftypes.Packages{
 							{
+								ID:      "docker@v24.0.4",
 								Name:    "docker",
 								Version: "24.0.4",
 								Identifier: ftypes.PkgIdentifier{
@@ -225,14 +240,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 							},
 						},
 					},
-					{
-						Type:     "golang",
-						FilePath: "node-core-components",
-					},
 					{
 						Type: ftypes.K8sUpstream,
 						Libraries: ftypes.Packages{
 							{
+								ID:      "k8s.io/apiserver@1.27.4",
 								Name:    "k8s.io/apiserver",
 								Version: "1.27.4",
 								Identifier: ftypes.PkgIdentifier{
@@ -245,6 +257,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 								},
 							},
 							{
+								ID:      "k8s.io/controller-manager@1.27.4",
 								Name:    "k8s.io/controller-manager",
 								Version: "1.27.4",
 								Identifier: ftypes.PkgIdentifier{
@@ -257,6 +270,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 								},
 							},
 							{
+								ID:      "k8s.io/kube-proxy@1.27.4",
 								Name:    "k8s.io/kube-proxy",
 								Version: "1.27.4",
 								Identifier: ftypes.PkgIdentifier{
@@ -269,6 +283,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 								},
 							},
 							{
+								ID:      "k8s.io/kube-scheduler@1.27.4",
 								Name:    "k8s.io/kube-scheduler",
 								Version: "1.27.4",
 								Identifier: ftypes.PkgIdentifier{
@@ -281,6 +296,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 								},
 							},
 							{
+								ID:      "k8s.io/kubelet@1.27.4",
 								Name:    "k8s.io/kubelet",
 								Version: "1.27.4",
 								Identifier: ftypes.PkgIdentifier{
@@ -293,6 +309,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 								},
 							},
 							{
+								ID:      "k8s.io/kubernetes@1.27.4",
 								Name:    "k8s.io/kubernetes",
 								Version: "1.27.4",
 								Identifier: ftypes.PkgIdentifier{
@@ -303,6 +320,12 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 									},
 									BOMRef: "pkg:k8s/k8s.io%2Fkubernetes@1.27.4",
 								},
+								DependsOn: []string{
+									"k8s.io/apiserver@1.27.4",
+									"k8s.io/controller-manager@1.27.4",
+									"k8s.io/kube-proxy@1.27.4",
+									"k8s.io/kube-scheduler@1.27.4",
+								},
 							},
 						},
 					},
@@ -313,9 +336,21 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 			name:      "happy path with infinity loop",
 			inputFile: "testdata/happy/infinite-loop-bom.json",
 			want: types.SBOM{
-				OS: ftypes.OS{
-					Family: "ubuntu",
-					Name:   "22.04",
+				Metadata: types.Metadata{
+					ImageID: "sha256:08d22c0ceb150ddeb2237c5fa3129c0183f3cc6f5eeb2e7aa4016da3ad02140a",
+					DiffIDs: []string{
+						"sha256:b93c1bd012ab8fda60f5b4f5906bf244586e0e3292d84571d3abb56472248466",
+					},
+					RepoTags: []string{
+						"ubuntu:latest",
+					},
+					RepoDigests: []string{
+						"ubuntu@sha256:67211c14fa74f070d27cc59d69a7fa9aeff8e28ea118ef3babc295a0428a6d21",
+					},
+					OS: &ftypes.OS{
+						Family: "ubuntu",
+						Name:   "22.04",
+					},
 				},
 				Packages: []ftypes.PackageInfo{
 					{
@@ -347,6 +382,9 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 									},
 									BOMRef: "pkg:deb/ubuntu/libc6@2.35-0ubuntu3.1?distro=ubuntu-22.04",
 								},
+								DependsOn: []string{
+									"libcrypt1@1:4.4.27-1",
+								},
 								Layer: ftypes.Layer{
 									Digest: "sha256:74ac377868f863e123f24c409f79709f7563fa464557c36a09cf6f85c8b92b7f",
 									DiffID: "sha256:b93c1bd012ab8fda60f5b4f5906bf244586e0e3292d84571d3abb56472248466",
@@ -380,6 +418,9 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 									},
 									BOMRef: "pkg:deb/ubuntu/libcrypt1@4.4.27-1?epoch=1&distro=ubuntu-22.04",
 								},
+								DependsOn: []string{
+									"libc6@2.35-0ubuntu3.1",
+								},
 								Layer: ftypes.Layer{
 									Digest: "sha256:74ac377868f863e123f24c409f79709f7563fa464557c36a09cf6f85c8b92b7f",
 									DiffID: "sha256:b93c1bd012ab8fda60f5b4f5906bf244586e0e3292d84571d3abb56472248466",
@@ -394,14 +435,17 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 			name:      "happy path for third party sbom",
 			inputFile: "testdata/happy/third-party-bom.json",
 			want: types.SBOM{
-				OS: ftypes.OS{
-					Family: "alpine",
-					Name:   "3.16.0",
+				Metadata: types.Metadata{
+					OS: &ftypes.OS{
+						Family: "alpine",
+						Name:   "3.16.0",
+					},
 				},
 				Packages: []ftypes.PackageInfo{
 					{
 						Packages: ftypes.Packages{
 							{
+								ID:         "musl@1.2.3-r0",
 								Name:       "musl",
 								Version:    "1.2.3-r0",
 								SrcName:    "musl",
@@ -432,6 +476,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 						FilePath: "",
 						Libraries: ftypes.Packages{
 							{
+								ID:      "pear/log@1.13.1",
 								Name:    "pear/log",
 								Version: "1.13.1",
 								Identifier: ftypes.PkgIdentifier{
@@ -446,7 +491,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 								Licenses: []string{"MIT"},
 							},
 							{
-
+								ID:      "pear/pear_exception@v1.0.0",
 								Name:    "pear/pear_exception",
 								Version: "v1.0.0",
 								Identifier: ftypes.PkgIdentifier{
@@ -474,6 +519,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 						FilePath: "",
 						Libraries: ftypes.Packages{
 							{
+								ID:      "pear/log@1.13.1",
 								Name:    "pear/log",
 								Version: "1.13.1",
 								Identifier: ftypes.PkgIdentifier{
@@ -501,6 +547,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 						FilePath: "",
 						Libraries: ftypes.Packages{
 							{
+								ID:      "pear/log@1.13.1",
 								Name:    "pear/log",
 								Version: "1.13.1",
 								Identifier: ftypes.PkgIdentifier{
@@ -514,7 +561,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 								},
 							},
 							{
-
+								ID:      "pear/pear_exception@v1.0.0",
 								Name:    "pear/pear_exception",
 								Version: "v1.0.0",
 								Identifier: ftypes.PkgIdentifier{
@@ -542,6 +589,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 						FilePath: "",
 						Libraries: ftypes.Packages{
 							{
+								ID:      "pear/core@1.13.1",
 								Name:    "pear/core",
 								Version: "1.13.1",
 								Identifier: ftypes.PkgIdentifier{
@@ -553,8 +601,13 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 									},
 									BOMRef: "pkg:composer/pear/core@1.13.1",
 								},
+								DependsOn: []string{
+									"pear/log@1.13.1",
+									"pear/pear_exception@v1.0.0",
+								},
 							},
 							{
+								ID:      "pear/log@1.13.1",
 								Name:    "pear/log",
 								Version: "1.13.1",
 								Identifier: ftypes.PkgIdentifier{
@@ -568,7 +621,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 								},
 							},
 							{
-
+								ID:      "pear/pear_exception@v1.0.0",
 								Name:    "pear/pear_exception",
 								Version: "v1.0.0",
 								Identifier: ftypes.PkgIdentifier{
@@ -595,6 +648,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 						Type: "jar",
 						Libraries: ftypes.Packages{
 							{
+								ID:      "org.springframework:spring-web:5.3.22",
 								Name:    "org.springframework:spring-web",
 								Version: "5.3.22",
 								Identifier: ftypes.PkgIdentifier{
@@ -617,19 +671,37 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 			name:      "happy path only os component",
 			inputFile: "testdata/happy/os-only-bom.json",
 			want: types.SBOM{
-				OS: ftypes.OS{
-					Family: "alpine",
-					Name:   "3.16.0",
-				},
-				Packages: []ftypes.PackageInfo{
-					{},
+				Metadata: types.Metadata{
+					ImageID: "sha256:49193a2310dbad4c02382da87ac624a80a92387a4f7536235f9ba590e5bcd7b5",
+					DiffIDs: []string{
+						"sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
+						"sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3",
+					},
+					RepoTags: []string{
+						"maven-test-project:latest",
+					},
+					OS: &ftypes.OS{
+						Family: "alpine",
+						Name:   "3.16.0",
+					},
 				},
 			},
 		},
 		{
 			name:      "happy path empty component",
 			inputFile: "testdata/happy/empty-bom.json",
-			want:      types.SBOM{},
+			want: types.SBOM{
+				Metadata: types.Metadata{
+					ImageID: "sha256:49193a2310dbad4c02382da87ac624a80a92387a4f7536235f9ba590e5bcd7b5",
+					DiffIDs: []string{
+						"sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1",
+						"sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3",
+					},
+					RepoTags: []string{
+						"maven-test-project:latest",
+					},
+				},
+			},
 		},
 		{
 			name:      "happy path empty metadata component",
@@ -637,9 +709,36 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 			want:      types.SBOM{},
 		},
 		{
-			name:      "sad path invalid purl",
-			inputFile: "testdata/sad/invalid-purl.json",
-			wantErr:   "failed to parse purl",
+			name:      "invalid purl",
+			inputFile: "testdata/happy/invalid-purl.json",
+			want: types.SBOM{
+				Applications: []ftypes.Application{
+					{
+						Type: ftypes.Composer,
+						Libraries: ftypes.Packages{
+							{
+								ID:      "pear/core@1.13.1",
+								Name:    "pear/core",
+								Version: "1.13.1",
+								Identifier: ftypes.PkgIdentifier{
+									PURL: &packageurl.PackageURL{
+										Type:      packageurl.TypeComposer,
+										Namespace: "pear",
+										Name:      "core",
+										Version:   "1.13.1",
+									},
+									BOMRef: "pkg:composer/pear/core@1.13.1",
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			name:      "invalid serial",
+			inputFile: "testdata/sad/invalid-serial.json",
+			wantErr:   "CycloneDX decode error",
 		},
 	}
 
@@ -652,16 +751,16 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 			var cdx cyclonedx.BOM
 			err = json.NewDecoder(f).Decode(&cdx)
 			if tt.wantErr != "" {
-				require.Error(t, err)
-				assert.Contains(t, err.Error(), tt.wantErr)
+				require.ErrorContains(t, err, tt.wantErr)
 				return
 			}
 			require.NoError(t, err)
 
-			// Not compare the CycloneDX field
-			got := *cdx.SBOM
-			got.CycloneDX = nil
+			var got types.SBOM
+			err = sbomio.NewDecoder(cdx.BOM).Decode(&got)
+			require.NoError(t, err)
 
+			got.BOM = nil
 			assert.Equal(t, tt.want, got)
 		})
 	}
diff --git a/pkg/sbom/io/decode.go b/pkg/sbom/io/decode.go
new file mode 100644
index 000000000000..f0385ddedc26
--- /dev/null
+++ b/pkg/sbom/io/decode.go
@@ -0,0 +1,336 @@
+package io
+
+import (
+	"errors"
+	"sort"
+	"strconv"
+
+	"github.com/package-url/packageurl-go"
+	"go.uber.org/zap"
+	"golang.org/x/exp/maps"
+	"golang.org/x/xerrors"
+
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
+	"github.com/aquasecurity/trivy/pkg/log"
+	"github.com/aquasecurity/trivy/pkg/purl"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
+	"github.com/aquasecurity/trivy/pkg/types"
+	"github.com/aquasecurity/trivy/pkg/uuid"
+)
+
+var (
+	ErrPURLEmpty       = errors.New("purl empty error")
+	ErrUnsupportedType = errors.New("unsupported type")
+)
+
+type Decoder struct {
+	bom *core.BOM
+
+	osID uuid.UUID
+	pkgs map[uuid.UUID]*ftypes.Package
+	apps map[uuid.UUID]*ftypes.Application
+}
+
+func NewDecoder(bom *core.BOM) *Decoder {
+	return &Decoder{
+		bom:  bom,
+		pkgs: make(map[uuid.UUID]*ftypes.Package),
+		apps: make(map[uuid.UUID]*ftypes.Application),
+	}
+}
+
+func (m *Decoder) Decode(sbom *types.SBOM) error {
+	// Parse the root component
+	if err := m.decodeRoot(sbom); err != nil {
+		return xerrors.Errorf("failed to decode root component: %w", err)
+	}
+
+	// Parse all components
+	if err := m.decodeComponents(sbom); err != nil {
+		return xerrors.Errorf("failed to decode components: %w", err)
+	}
+
+	// Build dependency graph between packages
+	m.buildDependencyGraph()
+
+	// Add OS packages
+	m.addOSPkgs(sbom)
+
+	// Add language-specific packages
+	m.addLangPkgs(sbom)
+
+	// Add remaining packages
+	if err := m.addOrphanPkgs(sbom); err != nil {
+		return xerrors.Errorf("failed to aggregate packages: %w", err)
+	}
+
+	sort.Slice(sbom.Applications, func(i, j int) bool {
+		if sbom.Applications[i].Type != sbom.Applications[j].Type {
+			return sbom.Applications[i].Type < sbom.Applications[j].Type
+		}
+		return sbom.Applications[i].FilePath < sbom.Applications[j].FilePath
+	})
+
+	sbom.BOM = m.bom
+
+	return nil
+}
+
+func (m *Decoder) decodeRoot(s *types.SBOM) error {
+	root := m.bom.Root()
+	if root == nil {
+		return nil // No root found
+	}
+
+	var err error
+	for _, prop := range root.Properties {
+		switch prop.Name {
+		case core.PropertyImageID:
+			s.Metadata.ImageID = prop.Value
+		case core.PropertySize:
+			if s.Metadata.Size, err = strconv.ParseInt(prop.Value, 10, 64); err != nil {
+				return xerrors.Errorf("failed to convert size: %w", err)
+			}
+		case core.PropertyRepoDigest:
+			s.Metadata.RepoDigests = append(s.Metadata.RepoDigests, prop.Value)
+		case core.PropertyDiffID:
+			s.Metadata.DiffIDs = append(s.Metadata.DiffIDs, prop.Value)
+		case core.PropertyRepoTag:
+			s.Metadata.RepoTags = append(s.Metadata.RepoTags, prop.Value)
+		}
+	}
+	return nil
+}
+
+func (m *Decoder) decodeComponents(sbom *types.SBOM) error {
+	for id, c := range m.bom.Components() {
+		switch c.Type {
+		case core.TypeOS:
+			if m.osID != uuid.Nil {
+				return xerrors.New("multiple OS components are not supported")
+			}
+			m.osID = id
+			sbom.Metadata.OS = &ftypes.OS{
+				Family: ftypes.OSType(c.Name),
+				Name:   c.Version,
+			}
+			continue
+		case core.TypeApplication:
+			if app := m.decodeApplication(c); app.Type != "" {
+				m.apps[id] = app
+				continue
+			}
+		}
+
+		// Third-party SBOMs may contain packages in types other than "Library"
+		if c.Type == core.TypeLibrary || c.PkgID.PURL != nil {
+			pkg, err := m.decodeLibrary(c)
+			if errors.Is(err, ErrUnsupportedType) {
+				continue
+			} else if err != nil {
+				return xerrors.Errorf("failed to decode library: %w", err)
+			}
+			m.pkgs[id] = pkg
+		}
+	}
+
+	return nil
+}
+
+// buildDependencyGraph builds a dependency graph between packages
+func (m *Decoder) buildDependencyGraph() {
+	for id, rels := range m.bom.Relationships() {
+		pkg, ok := m.pkgs[id]
+		if !ok {
+			continue
+		}
+		for _, rel := range rels {
+			dep, ok := m.pkgs[rel.Dependency]
+			if !ok {
+				continue
+			}
+			pkg.DependsOn = append(pkg.DependsOn, dep.ID)
+		}
+		continue
+	}
+}
+
+func (m *Decoder) decodeApplication(c *core.Component) *ftypes.Application {
+	app := &ftypes.Application{
+		FilePath: c.Name,
+	}
+	for _, prop := range c.Properties {
+		if prop.Name == core.PropertyType {
+			app.Type = ftypes.LangType(prop.Value)
+		}
+	}
+	return app
+}
+
+func (m *Decoder) decodeLibrary(c *core.Component) (*ftypes.Package, error) {
+	p := (*purl.PackageURL)(c.PkgID.PURL)
+	if p == nil {
+		log.Logger.Debugw("Skipping a component without PURL",
+			zap.String("name", c.Name), zap.String("version", c.Version))
+		return nil, ErrPURLEmpty
+	}
+
+	pkg := p.Package()
+	if p.Class() == types.ClassUnknown {
+		log.Logger.Debugw("Skipping a component with an unsupported type",
+			zap.String("name", c.Name), zap.String("version", c.Version), zap.String("type", p.Type))
+		return nil, ErrUnsupportedType
+	}
+	pkg.Name = m.pkgName(pkg, c)
+
+	var err error
+	for _, prop := range c.Properties {
+		switch prop.Name {
+		case core.PropertyPkgID:
+			pkg.ID = prop.Value
+		case core.PropertyFilePath:
+			pkg.FilePath = prop.Value
+		case core.PropertySrcName:
+			pkg.SrcName = prop.Value
+		case core.PropertySrcVersion:
+			pkg.SrcVersion = prop.Value
+		case core.PropertySrcRelease:
+			pkg.SrcRelease = prop.Value
+		case core.PropertySrcEpoch:
+			if pkg.SrcEpoch, err = strconv.Atoi(prop.Value); err != nil {
+				return nil, xerrors.Errorf("invalid src epoch: %w", err)
+			}
+		case core.PropertyModularitylabel:
+			pkg.Modularitylabel = prop.Value
+		case core.PropertyLayerDigest:
+			pkg.Layer.Digest = prop.Value
+		case core.PropertyLayerDiffID:
+			pkg.Layer.DiffID = prop.Value
+		}
+	}
+
+	pkg.Identifier.BOMRef = c.PkgID.BOMRef
+	pkg.Licenses = c.Licenses
+	if len(c.Files) > 0 {
+		pkg.Digest = c.Files[0].Hash
+	}
+
+	if p.Class() == types.ClassOSPkg {
+		m.fillSrcPkg(pkg)
+	}
+
+	return pkg, nil
+}
+
+// pkgName returns the package name.
+// PURL loses case-sensitivity (e.g. Go, Npm, PyPI), so we have to use an original package name.
+func (m *Decoder) pkgName(pkg *ftypes.Package, c *core.Component) string {
+	p := c.PkgID.PURL
+
+	// A name from PURL takes precedence for CocoaPods since it has subpath.
+	if c.PkgID.PURL.Type == packageurl.TypeCocoapods {
+		return pkg.Name
+	}
+
+	if c.Group != "" {
+		if p.Type == packageurl.TypeMaven || p.Type == packageurl.TypeGradle {
+			return c.Group + ":" + c.Name
+		}
+		return c.Group + "/" + c.Name
+	}
+	return c.Name
+}
+
+func (m *Decoder) fillSrcPkg(pkg *ftypes.Package) {
+	// Fill source package information for components in third-party SBOMs .
+	if pkg.SrcName == "" {
+		pkg.SrcName = pkg.Name
+	}
+	if pkg.SrcVersion == "" {
+		pkg.SrcVersion = pkg.Version
+	}
+	if pkg.SrcRelease == "" {
+		pkg.SrcRelease = pkg.Release
+	}
+	if pkg.SrcEpoch == 0 {
+		pkg.SrcEpoch = pkg.Epoch
+	}
+}
+
+// addOSPkgs traverses relationships and adds OS packages
+func (m *Decoder) addOSPkgs(sbom *types.SBOM) {
+	var pkgs []ftypes.Package
+	for _, rel := range m.bom.Relationships()[m.osID] {
+		pkg, ok := m.pkgs[rel.Dependency]
+		if !ok {
+			continue
+		}
+		pkgs = append(pkgs, *pkg)
+		delete(m.pkgs, rel.Dependency) // Delete the added package
+	}
+	if len(pkgs) == 0 {
+		return
+	}
+	sbom.Packages = []ftypes.PackageInfo{{Packages: pkgs}}
+}
+
+// addLangPkgs traverses relationships and adds language-specific packages
+func (m *Decoder) addLangPkgs(sbom *types.SBOM) {
+	for id, app := range m.apps {
+		for _, rel := range m.bom.Relationships()[id] {
+			pkg, ok := m.pkgs[rel.Dependency]
+			if !ok {
+				continue
+			}
+			app.Libraries = append(app.Libraries, *pkg)
+			delete(m.pkgs, rel.Dependency) // Delete the added package
+		}
+		sbom.Applications = append(sbom.Applications, *app)
+	}
+}
+
+// addOrphanPkgs adds orphan packages.
+// Orphan packages are packages that are not related to any components.
+func (m *Decoder) addOrphanPkgs(sbom *types.SBOM) error {
+	osPkgMap := make(map[string]ftypes.Packages)
+	langPkgMap := make(map[ftypes.LangType]ftypes.Packages)
+	for _, pkg := range m.pkgs {
+		p := (*purl.PackageURL)(pkg.Identifier.PURL)
+		switch p.Class() {
+		case types.ClassOSPkg:
+			osPkgMap[p.Type] = append(osPkgMap[p.Type], *pkg)
+		case types.ClassLangPkg:
+			langType := p.LangType()
+			langPkgMap[langType] = append(langPkgMap[langType], *pkg)
+		}
+	}
+
+	if len(osPkgMap) > 1 {
+		return xerrors.Errorf("multiple types of OS packages in SBOM are not supported (%q)", maps.Keys(osPkgMap))
+	}
+
+	// Add OS packages only when OS is detected.
+	for _, pkgs := range osPkgMap {
+		if sbom.Metadata.OS == nil || !sbom.Metadata.OS.Detected() {
+			log.Logger.Warn("Ignore the OS package as no OS is detected.")
+			break
+		}
+
+		// TODO: mismatch between the OS and the packages should be rejected.
+		// e.g. OS: debian, Packages: rpm
+		sort.Sort(pkgs)
+		sbom.Packages = append(sbom.Packages, ftypes.PackageInfo{Packages: pkgs})
+
+		break // Just take the first element
+	}
+
+	// Add language-specific packages
+	for pkgType, pkgs := range langPkgMap {
+		sort.Sort(pkgs)
+		sbom.Applications = append(sbom.Applications, ftypes.Application{
+			Type:      pkgType,
+			Libraries: pkgs,
+		})
+	}
+	return nil
+}
diff --git a/pkg/sbom/io/encode.go b/pkg/sbom/io/encode.go
new file mode 100644
index 000000000000..73c0d4fef3dc
--- /dev/null
+++ b/pkg/sbom/io/encode.go
@@ -0,0 +1,343 @@
+package io
+
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/package-url/packageurl-go"
+	"github.com/samber/lo"
+	"golang.org/x/xerrors"
+
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
+	"github.com/aquasecurity/trivy/pkg/purl"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
+	"github.com/aquasecurity/trivy/pkg/types"
+)
+
+type Encoder struct {
+	bom *core.BOM
+}
+
+func NewEncoder() *Encoder {
+	return &Encoder{}
+}
+
+func (m *Encoder) Encode(report types.Report) (*core.BOM, error) {
+	// Metadata component
+	root, err := m.rootComponent(report)
+	if err != nil {
+		return nil, xerrors.Errorf("failed to create root component: %w", err)
+	}
+
+	m.bom = core.NewBOM()
+	m.bom.AddComponent(root)
+
+	for _, result := range report.Results {
+		m.encodeResult(root, report.Metadata, result)
+	}
+
+	// Components that do not have their own dependencies MUST be declared as empty elements within the graph.
+	if _, ok := m.bom.Relationships()[root.ID()]; !ok {
+		m.bom.AddRelationship(root, nil, "")
+	}
+	return m.bom, nil
+}
+
+func (m *Encoder) rootComponent(r types.Report) (*core.Component, error) {
+	root := &core.Component{
+		Root: true,
+		Name: r.ArtifactName,
+	}
+
+	props := []core.Property{
+		{
+			Name:  core.PropertySchemaVersion,
+			Value: strconv.Itoa(r.SchemaVersion),
+		},
+	}
+
+	switch r.ArtifactType {
+	case ftypes.ArtifactContainerImage:
+		root.Type = core.TypeContainer
+		props = append(props, core.Property{
+			Name:  core.PropertyImageID,
+			Value: r.Metadata.ImageID,
+		})
+
+		p, err := purl.New(purl.TypeOCI, r.Metadata, ftypes.Package{})
+		if err != nil {
+			return nil, xerrors.Errorf("failed to new package url for oci: %w", err)
+		}
+		if p != nil {
+			root.PkgID.PURL = p.Unwrap()
+		}
+
+	case ftypes.ArtifactVM:
+		root.Type = core.TypeContainer
+	case ftypes.ArtifactFilesystem, ftypes.ArtifactRepository:
+		root.Type = core.TypeApplication
+	case ftypes.ArtifactCycloneDX:
+		return r.BOM.Root(), nil
+	}
+
+	if r.Metadata.Size != 0 {
+		props = append(props, core.Property{
+			Name:  core.PropertySize,
+			Value: strconv.FormatInt(r.Metadata.Size, 10),
+		})
+	}
+
+	for _, d := range r.Metadata.RepoDigests {
+		props = append(props, core.Property{
+			Name:  core.PropertyRepoDigest,
+			Value: d,
+		})
+	}
+
+	for _, id := range r.Metadata.DiffIDs {
+		props = append(props, core.Property{
+			Name:  core.PropertyDiffID,
+			Value: id,
+		})
+	}
+
+	for _, tag := range r.Metadata.RepoTags {
+		props = append(props, core.Property{
+			Name:  core.PropertyRepoTag,
+			Value: tag,
+		})
+	}
+
+	root.Properties = filterProperties(props)
+
+	return root, nil
+}
+
+func (m *Encoder) encodeResult(root *core.Component, metadata types.Metadata, result types.Result) {
+	if result.Type == ftypes.NodePkg || result.Type == ftypes.PythonPkg ||
+		result.Type == ftypes.GemSpec || result.Type == ftypes.Jar || result.Type == ftypes.CondaPkg {
+		// If a package is language-specific package that isn't associated with a lock file,
+		// it will be a dependency of a component under "metadata".
+		// e.g.
+		//   Container component (alpine:3.15) ----------------------- #1
+		//     -> Library component (npm package, express-4.17.3) ---- #2
+		//     -> Library component (python package, django-4.0.2) --- #2
+		//     -> etc.
+		// ref. https://cyclonedx.org/use-cases/#inventory
+
+		// Dependency graph from #1 to #2
+		m.encodePackages(root, result)
+	} else if result.Class == types.ClassOSPkg || result.Class == types.ClassLangPkg {
+		// If a package is OS package, it will be a dependency of "Operating System" component.
+		// e.g.
+		//   Container component (alpine:3.15) --------------------- #1
+		//     -> Operating System Component (Alpine Linux 3.15) --- #2
+		//       -> Library component (bash-4.12) ------------------ #3
+		//       -> Library component (vim-8.2)   ------------------ #3
+		//       -> etc.
+		//
+		// Else if a package is language-specific package associated with a lock file,
+		// it will be a dependency of "Application" component.
+		// e.g.
+		//   Container component (alpine:3.15) ------------------------ #1
+		//     -> Application component (/app/package-lock.json) ------ #2
+		//       -> Library component (npm package, express-4.17.3) --- #3
+		//       -> Library component (npm package, lodash-4.17.21) --- #3
+		//       -> etc.
+
+		// #2
+		appComponent := m.resultComponent(root, result, metadata.OS)
+
+		// #3
+		m.encodePackages(appComponent, result)
+	}
+}
+
+func (m *Encoder) encodePackages(parent *core.Component, result types.Result) {
+	// Get dependency parents first
+	parents := ftypes.Packages(result.Packages).ParentDeps()
+
+	// Group vulnerabilities by package ID
+	vulns := make(map[string][]core.Vulnerability)
+	for _, vuln := range result.Vulnerabilities {
+		v := m.vulnerability(vuln)
+		vulns[v.PkgID] = append(vulns[v.PkgID], v)
+	}
+
+	// Convert packages into components and add them to the BOM
+	components := make(map[string]*core.Component, len(result.Packages))
+	for i, pkg := range result.Packages {
+		pkgID := lo.Ternary(pkg.ID == "", fmt.Sprintf("%s@%s", pkg.Name, pkg.Version), pkg.ID)
+		result.Packages[i].ID = pkgID
+
+		// Convert packages to components
+		c := m.component(result.Type, pkg)
+		components[pkgID] = c
+
+		// Add a component
+		m.bom.AddComponent(c)
+
+		// Add vulnerabilities
+		if vv := vulns[pkgID]; vv != nil {
+			m.bom.AddVulnerabilities(c, vv)
+		}
+	}
+
+	// Build a dependency graph
+	for _, pkg := range result.Packages {
+		// Skip indirect dependencies
+		if pkg.Indirect && len(parents[pkg.ID]) != 0 {
+			continue
+		}
+
+		directPkg := components[pkg.ID]
+		m.bom.AddRelationship(parent, directPkg, core.RelationshipContains)
+
+		for _, dep := range pkg.DependsOn {
+			indirectPkg, ok := components[dep]
+			if !ok {
+				continue
+			}
+			m.bom.AddRelationship(directPkg, indirectPkg, core.RelationshipDependsOn)
+		}
+
+		// Components that do not have their own dependencies MUST be declared as empty elements within the graph.
+		// TODO: Should check if the component has actually no dependencies or the dependency graph is not supported.
+		if len(pkg.DependsOn) == 0 {
+			m.bom.AddRelationship(directPkg, nil, "")
+		}
+	}
+}
+
+func (m *Encoder) resultComponent(root *core.Component, r types.Result, osFound *ftypes.OS) *core.Component {
+	component := &core.Component{
+		Name: r.Target,
+		Properties: []core.Property{
+			{
+				Name:  core.PropertyType,
+				Value: string(r.Type),
+			},
+			{
+				Name:  core.PropertyClass,
+				Value: string(r.Class),
+			},
+		},
+	}
+
+	switch r.Class {
+	case types.ClassOSPkg:
+		if osFound != nil {
+			component.Name = string(osFound.Family)
+			component.Version = osFound.Name
+		}
+		component.Type = core.TypeOS
+	case types.ClassLangPkg:
+		component.Type = core.TypeApplication
+	}
+
+	m.bom.AddRelationship(root, component, core.RelationshipContains)
+	return component
+}
+
+func (*Encoder) component(pkgType ftypes.TargetType, pkg ftypes.Package) *core.Component {
+	name := pkg.Name
+	version := pkg.Version
+	var group string
+	// there are cases when we can't build purl
+	// e.g. local Go packages
+	if pu := pkg.Identifier.PURL; pu != nil {
+		version = pu.Version
+		// Use `group` field for GroupID and `name` for ArtifactID for java files
+		// https://github.com/aquasecurity/trivy/issues/4675
+		// Use `group` field for npm scopes
+		// https://github.com/aquasecurity/trivy/issues/5908
+		if pu.Type == packageurl.TypeMaven || pu.Type == packageurl.TypeNPM {
+			name = pu.Name
+			group = pu.Namespace
+		}
+	}
+
+	properties := []core.Property{
+		{
+			Name:  core.PropertyPkgID,
+			Value: pkg.ID,
+		},
+		{
+			Name:  core.PropertyPkgType,
+			Value: string(pkgType),
+		},
+		{
+			Name:  core.PropertyFilePath,
+			Value: pkg.FilePath,
+		},
+		{
+			Name:  core.PropertySrcName,
+			Value: pkg.SrcName,
+		},
+		{
+			Name:  core.PropertySrcVersion,
+			Value: pkg.SrcVersion,
+		},
+		{
+			Name:  core.PropertySrcRelease,
+			Value: pkg.SrcRelease,
+		},
+		{
+			Name:  core.PropertySrcEpoch,
+			Value: strconv.Itoa(pkg.SrcEpoch),
+		},
+		{
+			Name:  core.PropertyModularitylabel,
+			Value: pkg.Modularitylabel,
+		},
+		{
+			Name:  core.PropertyLayerDigest,
+			Value: pkg.Layer.Digest,
+		},
+		{
+			Name:  core.PropertyLayerDiffID,
+			Value: pkg.Layer.DiffID,
+		},
+	}
+
+	var files []core.File
+	if pkg.FilePath != "" || pkg.Digest != "" {
+		files = append(files, core.File{
+			Path: pkg.FilePath,
+			Hash: pkg.Digest,
+		})
+	}
+
+	return &core.Component{
+		Type:    core.TypeLibrary,
+		Name:    name,
+		Group:   group,
+		Version: version,
+		PkgID: core.PkgID{
+			PURL: pkg.Identifier.PURL,
+		},
+		Supplier:   pkg.Maintainer,
+		Licenses:   pkg.Licenses,
+		Files:      files,
+		Properties: filterProperties(properties),
+	}
+}
+
+func (*Encoder) vulnerability(vuln types.DetectedVulnerability) core.Vulnerability {
+	return core.Vulnerability{
+		Vulnerability:    vuln.Vulnerability,
+		ID:               vuln.VulnerabilityID,
+		PkgID:            lo.Ternary(vuln.PkgID == "", fmt.Sprintf("%s@%s", vuln.PkgName, vuln.InstalledVersion), vuln.PkgID),
+		PkgName:          vuln.PkgName,
+		InstalledVersion: vuln.InstalledVersion,
+		FixedVersion:     vuln.FixedVersion,
+		PrimaryURL:       vuln.PrimaryURL,
+		DataSource:       vuln.DataSource,
+	}
+}
+
+func filterProperties(props []core.Property) []core.Property {
+	return lo.Filter(props, func(property core.Property, _ int) bool {
+		return !(property.Value == "" || (property.Name == core.PropertySrcEpoch && property.Value == "0"))
+	})
+}
diff --git a/pkg/sbom/io/encode_test.go b/pkg/sbom/io/encode_test.go
new file mode 100644
index 000000000000..5c2af5b54d9f
--- /dev/null
+++ b/pkg/sbom/io/encode_test.go
@@ -0,0 +1,339 @@
+package io_test
+
+import (
+	dtypes "github.com/aquasecurity/trivy-db/pkg/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
+	sbomio "github.com/aquasecurity/trivy/pkg/sbom/io"
+	"github.com/aquasecurity/trivy/pkg/types"
+	"github.com/aquasecurity/trivy/pkg/uuid"
+	"github.com/package-url/packageurl-go"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"testing"
+)
+
+func TestEncoder_Encode(t *testing.T) {
+	tests := []struct {
+		name           string
+		report         types.Report
+		wantComponents map[uuid.UUID]*core.Component
+		wantRels       map[uuid.UUID][]core.Relationship
+		wantVulns      map[uuid.UUID][]core.Vulnerability
+		wantErr        string
+	}{
+		{
+			name: "container image",
+			report: types.Report{
+				SchemaVersion: 2,
+				ArtifactName:  "debian:12",
+				ArtifactType:  ftypes.ArtifactContainerImage,
+				Metadata: types.Metadata{
+					OS: &ftypes.OS{
+						Family: ftypes.Debian,
+						Name:   "12",
+					},
+					RepoTags: []string{
+						"debian:latest",
+						"debian:12",
+					},
+					RepoDigests: []string{
+						"debian@sha256:4482958b4461ff7d9fabc24b3a9ab1e9a2c85ece07b2db1840c7cbc01d053e90",
+					},
+				},
+				Results: []types.Result{
+					{
+						Target: "debian:12",
+						Type:   ftypes.Debian,
+						Class:  types.ClassOSPkg,
+						Packages: []ftypes.Package{
+							{
+								ID:      "libc6@2.37-15.1",
+								Name:    "libc6",
+								Version: "2.37-15.1",
+								Identifier: ftypes.PkgIdentifier{
+									PURL: &packageurl.PackageURL{
+										Type:    packageurl.TypeDebian,
+										Name:    "libc6",
+										Version: "2.37-15.1",
+									},
+								},
+							},
+							{
+								ID:      "curl@7.50.3-1",
+								Name:    "curl",
+								Version: "7.50.3-1",
+								Identifier: ftypes.PkgIdentifier{
+									PURL: &packageurl.PackageURL{
+										Type:    packageurl.TypeDebian,
+										Name:    "curl",
+										Version: "7.50.3-1",
+									},
+								},
+								DependsOn: []string{
+									"libc6@2.37-15.1",
+								},
+							},
+						},
+						Vulnerabilities: []types.DetectedVulnerability{
+							{
+								PkgName:          "curl",
+								PkgID:            "curl@7.50.3-1",
+								VulnerabilityID:  "CVE-2021-22876",
+								InstalledVersion: "7.50.3-1",
+								FixedVersion:     "7.50.3-1+deb9u1",
+								Vulnerability: dtypes.Vulnerability{
+									Severity: "HIGH",
+								},
+							},
+						},
+					},
+					{
+						Target: "Java",
+						Type:   ftypes.Jar,
+						Class:  types.ClassLangPkg,
+						Packages: []ftypes.Package{
+							{
+								ID:       "org.apache.xmlgraphics/batik-anim:1.9.1",
+								Name:     "org.apache.xmlgraphics/batik-anim",
+								Version:  "1.9.1",
+								FilePath: "/app/batik-anim-1.9.1.jar",
+								Identifier: ftypes.PkgIdentifier{
+									PURL: &packageurl.PackageURL{
+										Type:      packageurl.TypeMaven,
+										Namespace: "org.apache.xmlgraphics",
+										Name:      "batik-anim",
+										Version:   "1.9.1",
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantComponents: map[uuid.UUID]*core.Component{
+				uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000001"): {
+					Type: core.TypeContainer,
+					Name: "debian:12",
+					Root: true,
+					PkgID: core.PkgID{
+						PURL: &packageurl.PackageURL{
+							Type:    packageurl.TypeOCI,
+							Name:    "debian",
+							Version: "sha256:4482958b4461ff7d9fabc24b3a9ab1e9a2c85ece07b2db1840c7cbc01d053e90",
+							Qualifiers: packageurl.Qualifiers{
+								{
+									Key:   "repository_url",
+									Value: "index.docker.io/library/debian",
+								},
+							},
+						},
+						BOMRef: "pkg:oci/debian@sha256%3A4482958b4461ff7d9fabc24b3a9ab1e9a2c85ece07b2db1840c7cbc01d053e90?repository_url=index.docker.io%2Flibrary%2Fdebian",
+					},
+					Properties: []core.Property{
+						{
+							Name:  core.PropertyRepoDigest,
+							Value: "debian@sha256:4482958b4461ff7d9fabc24b3a9ab1e9a2c85ece07b2db1840c7cbc01d053e90",
+						},
+						{
+							Name:  core.PropertyRepoTag,
+							Value: "debian:12",
+						},
+						{
+							Name:  core.PropertyRepoTag,
+							Value: "debian:latest",
+						},
+						{
+							Name:  core.PropertySchemaVersion,
+							Value: "2",
+						},
+					},
+				},
+				uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000002"): {
+					Type:    core.TypeOS,
+					Name:    "debian",
+					Version: "12",
+					Properties: []core.Property{
+						{
+							Name:  core.PropertyClass,
+							Value: "os-pkgs",
+						},
+						{
+							Name:  core.PropertyType,
+							Value: "debian",
+						},
+					},
+					PkgID: core.PkgID{
+						BOMRef: "3ff14136-e09f-4df9-80ea-000000000002",
+					},
+				},
+				uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000003"): {
+					Type:    core.TypeLibrary,
+					Name:    "libc6",
+					Version: "2.37-15.1",
+					Properties: []core.Property{
+						{
+							Name:  core.PropertyPkgID,
+							Value: "libc6@2.37-15.1",
+						},
+						{
+							Name:  core.PropertyPkgType,
+							Value: "debian",
+						},
+					},
+					PkgID: core.PkgID{
+						PURL: &packageurl.PackageURL{
+							Type:    packageurl.TypeDebian,
+							Name:    "libc6",
+							Version: "2.37-15.1",
+						},
+						BOMRef: "pkg:deb/libc6@2.37-15.1",
+					},
+				},
+				uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000004"): {
+					Type:    core.TypeLibrary,
+					Name:    "curl",
+					Version: "7.50.3-1",
+					Properties: []core.Property{
+						{
+							Name:  core.PropertyPkgID,
+							Value: "curl@7.50.3-1",
+						},
+						{
+							Name:  core.PropertyPkgType,
+							Value: "debian",
+						},
+					},
+					PkgID: core.PkgID{
+						PURL: &packageurl.PackageURL{
+							Type:    packageurl.TypeDebian,
+							Name:    "curl",
+							Version: "7.50.3-1",
+						},
+						BOMRef: "pkg:deb/curl@7.50.3-1",
+					},
+				},
+				uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000005"): {
+					Type:    core.TypeLibrary,
+					Group:   "org.apache.xmlgraphics",
+					Name:    "batik-anim",
+					Version: "1.9.1",
+					Files: []core.File{
+						{
+							Path: "/app/batik-anim-1.9.1.jar",
+						},
+					},
+					Properties: []core.Property{
+						{
+							Name:  core.PropertyFilePath,
+							Value: "/app/batik-anim-1.9.1.jar",
+						},
+						{
+							Name:  core.PropertyPkgID,
+							Value: "org.apache.xmlgraphics/batik-anim:1.9.1",
+						},
+						{
+							Name:  core.PropertyPkgType,
+							Value: "jar",
+						},
+					},
+					PkgID: core.PkgID{
+						PURL: &packageurl.PackageURL{
+							Type:      packageurl.TypeMaven,
+							Namespace: "org.apache.xmlgraphics",
+							Name:      "batik-anim",
+							Version:   "1.9.1",
+						},
+						BOMRef: "pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1",
+					},
+				},
+			},
+			wantRels: map[uuid.UUID][]core.Relationship{
+				uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000001"): {
+					{
+						Dependency: uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000002"),
+						Type:       core.RelationshipContains,
+					},
+					{
+						Dependency: uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000005"),
+						Type:       core.RelationshipContains,
+					},
+				},
+				uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000002"): {
+					{
+						Dependency: uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000003"),
+						Type:       core.RelationshipContains,
+					},
+					{
+						Dependency: uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000004"),
+						Type:       core.RelationshipContains,
+					},
+				},
+				uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000003"): nil,
+				uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000004"): {
+					{
+						Dependency: uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000003"),
+						Type:       core.RelationshipDependsOn,
+					},
+				},
+				uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000005"): nil,
+			},
+			wantVulns: map[uuid.UUID][]core.Vulnerability{
+				uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000004"): {
+					{
+						ID:               "CVE-2021-22876",
+						PkgID:            "curl@7.50.3-1",
+						PkgName:          "curl",
+						InstalledVersion: "7.50.3-1",
+						FixedVersion:     "7.50.3-1+deb9u1",
+						Vulnerability: dtypes.Vulnerability{
+							Severity: "HIGH",
+						},
+					},
+				},
+			},
+		},
+		{
+			name: "invalid digest",
+			report: types.Report{
+				SchemaVersion: 2,
+				ArtifactName:  "debian:12",
+				ArtifactType:  ftypes.ArtifactContainerImage,
+				Metadata: types.Metadata{
+					OS: &ftypes.OS{
+						Family: ftypes.Debian,
+						Name:   "12",
+					},
+					RepoTags: []string{
+						"debian:12",
+					},
+					RepoDigests: []string{
+						"debian@sha256:123",
+					},
+				},
+			},
+			wantErr: "failed to parse digest",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d")
+
+			got, err := sbomio.NewEncoder().Encode(tt.report)
+			if tt.wantErr != "" {
+				require.ErrorContains(t, err, tt.wantErr)
+				return
+			}
+			require.NoError(t, err)
+
+			require.Len(t, got.Components(), len(tt.wantComponents))
+			for id, want := range tt.wantComponents {
+				assert.EqualExportedValues(t, *want, *got.Components()[id])
+			}
+
+			assert.Equal(t, tt.wantRels, got.Relationships())
+			assert.Equal(t, tt.wantVulns, got.Vulnerabilities())
+		})
+	}
+}
diff --git a/pkg/sbom/sbom.go b/pkg/sbom/sbom.go
index 481f18da3639..2d8d74b267a0 100644
--- a/pkg/sbom/sbom.go
+++ b/pkg/sbom/sbom.go
@@ -11,7 +11,9 @@ import (
 	"golang.org/x/xerrors"
 
 	"github.com/aquasecurity/trivy/pkg/attestation"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
 	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx"
+	sbomio "github.com/aquasecurity/trivy/pkg/sbom/io"
 	"github.com/aquasecurity/trivy/pkg/sbom/spdx"
 	"github.com/aquasecurity/trivy/pkg/types"
 )
@@ -181,20 +183,21 @@ func decodeAttestCycloneDXJSONFormat(r io.ReadSeeker) (Format, bool) {
 func Decode(f io.Reader, format Format) (types.SBOM, error) {
 	var (
 		v       interface{}
-		bom     types.SBOM
+		bom     = core.NewBOM()
+		sbom    types.SBOM
 		decoder interface{ Decode(any) error }
 	)
 
 	switch format {
 	case FormatCycloneDXJSON:
-		v = &cyclonedx.BOM{SBOM: &bom}
+		v = &cyclonedx.BOM{BOM: bom}
 		decoder = json.NewDecoder(f)
 	case FormatAttestCycloneDXJSON:
 		// dsse envelope
 		//   => in-toto attestation
 		//     => CycloneDX JSON
 		v = &attestation.Statement{
-			Predicate: &cyclonedx.BOM{SBOM: &bom},
+			Predicate: &cyclonedx.BOM{BOM: bom},
 		}
 		decoder = json.NewDecoder(f)
 	case FormatLegacyCosignAttestCycloneDXJSON:
@@ -204,26 +207,34 @@ func Decode(f io.Reader, format Format) (types.SBOM, error) {
 		//       => CycloneDX JSON
 		v = &attestation.Statement{
 			Predicate: &attestation.CosignPredicate{
-				Data: &cyclonedx.BOM{SBOM: &bom},
+				Data: &cyclonedx.BOM{BOM: bom},
 			},
 		}
 		decoder = json.NewDecoder(f)
 	case FormatSPDXJSON:
-		v = &spdx.SPDX{SBOM: &bom}
+		v = &spdx.SPDX{SBOM: &sbom}
 		decoder = json.NewDecoder(f)
 	case FormatSPDXTV:
-		v = &spdx.SPDX{SBOM: &bom}
+		v = &spdx.SPDX{SBOM: &sbom}
 		decoder = spdx.NewTVDecoder(f)
-
 	default:
 		return types.SBOM{}, xerrors.Errorf("%s scanning is not yet supported", format)
 
 	}
 
-	// Decode a file content into sbom.SBOM
+	// Decode a file content into core.BOM
 	if err := decoder.Decode(v); err != nil {
 		return types.SBOM{}, xerrors.Errorf("failed to decode: %w", err)
 	}
 
-	return bom, nil
+	// TODO: use BOM in SPDX
+	if format == FormatSPDXJSON || format == FormatSPDXTV {
+		return sbom, nil
+	}
+
+	if err := sbomio.NewDecoder(bom).Decode(&sbom); err != nil {
+		return types.SBOM{}, xerrors.Errorf("failed to decode: %w", err)
+	}
+
+	return sbom, nil
 }
diff --git a/pkg/sbom/spdx/marshal.go b/pkg/sbom/spdx/marshal.go
index b88f92c1c23f..ceb9a1ae24ce 100644
--- a/pkg/sbom/spdx/marshal.go
+++ b/pkg/sbom/spdx/marshal.go
@@ -247,7 +247,7 @@ func (m *Marshaler) rootPackage(r types.Report, pkgDownloadLocation string) (*sp
 	if p, err := purl.New(purl.TypeOCI, r.Metadata, ftypes.Package{}); err != nil {
 		return nil, xerrors.Errorf("failed to new package url for oci: %w", err)
 	} else if p != nil {
-		externalReferences = append(externalReferences, purlExternalReference(p.ToString()))
+		externalReferences = append(externalReferences, purlExternalReference(p.String()))
 	}
 
 	if r.Metadata.ImageID != "" {
diff --git a/pkg/sbom/spdx/unmarshal.go b/pkg/sbom/spdx/unmarshal.go
index 1723476a323d..718bdd608886 100644
--- a/pkg/sbom/spdx/unmarshal.go
+++ b/pkg/sbom/spdx/unmarshal.go
@@ -105,7 +105,7 @@ func (s *SPDX) unmarshal(spdxDocument *spdx.Document) error {
 		switch {
 		// Relationship: root package => OS
 		case isOperatingSystem(pkgB.PackageSPDXIdentifier):
-			s.SBOM.OS = parseOS(*pkgB)
+			s.SBOM.Metadata.OS = parseOS(*pkgB)
 			delete(orphanPkgs, pkgB.PackageSPDXIdentifier)
 		// Relationship: OS => OS package
 		case isOperatingSystem(pkgA.PackageSPDXIdentifier):
@@ -157,8 +157,6 @@ func (s *SPDX) unmarshal(spdxDocument *spdx.Document) error {
 		return err
 	}
 
-	// Keep the original document
-	s.SPDX = spdxDocument
 	return nil
 }
 
@@ -239,8 +237,8 @@ func initApplication(pkg spdx.Package) *ftypes.Application {
 	return app
 }
 
-func parseOS(pkg spdx.Package) ftypes.OS {
-	return ftypes.OS{
+func parseOS(pkg spdx.Package) *ftypes.OS {
+	return &ftypes.OS{
 		Family: ftypes.OSType(pkg.PackageName),
 		Name:   pkg.PackageVersion,
 	}
diff --git a/pkg/sbom/spdx/unmarshal_test.go b/pkg/sbom/spdx/unmarshal_test.go
index 2d6ee258c378..cee50461508e 100644
--- a/pkg/sbom/spdx/unmarshal_test.go
+++ b/pkg/sbom/spdx/unmarshal_test.go
@@ -26,9 +26,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 			name:      "happy path",
 			inputFile: "testdata/happy/bom.json",
 			want: types.SBOM{
-				OS: ftypes.OS{
-					Family: "alpine",
-					Name:   "3.16.0",
+				Metadata: types.Metadata{
+					OS: &ftypes.OS{
+						Family: "alpine",
+						Name:   "3.16.0",
+					},
 				},
 				Packages: []ftypes.PackageInfo{
 					{
@@ -298,9 +300,11 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 			name:      "happy path only os component",
 			inputFile: "testdata/happy/os-only-bom.json",
 			want: types.SBOM{
-				OS: ftypes.OS{
-					Family: "alpine",
-					Name:   "3.16.0",
+				Metadata: types.Metadata{
+					OS: &ftypes.OS{
+						Family: "alpine",
+						Name:   "3.16.0",
+					},
 				},
 			},
 		},
@@ -331,7 +335,7 @@ func TestUnmarshaler_Unmarshal(t *testing.T) {
 			}
 
 			// Not compare the SPDX field
-			v.SPDX = nil
+			v.BOM = nil
 
 			sort.Slice(v.Applications, func(i, j int) bool {
 				return v.Applications[i].Type < v.Applications[j].Type
diff --git a/pkg/scanner/scan.go b/pkg/scanner/scan.go
index 82e900024acb..4cf647b66d13 100644
--- a/pkg/scanner/scan.go
+++ b/pkg/scanner/scan.go
@@ -186,8 +186,8 @@ func (s Scanner) ScanArtifact(ctx context.Context, options types.ScanOptions) (t
 			RepoDigests: artifactInfo.ImageMetadata.RepoDigests,
 			ImageConfig: artifactInfo.ImageMetadata.ConfigFile,
 		},
-		CycloneDX: artifactInfo.CycloneDX,
-		Results:   results,
+		Results: results,
+		BOM:     artifactInfo.BOM,
 	}, nil
 }
 
diff --git a/pkg/types/report.go b/pkg/types/report.go
index 6b8aae6940b6..e6c96d9564eb 100644
--- a/pkg/types/report.go
+++ b/pkg/types/report.go
@@ -6,6 +6,7 @@ import (
 	v1 "github.com/google/go-containerregistry/pkg/v1" // nolint: goimports
 
 	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
 )
 
 // Report represents a scan result
@@ -17,8 +18,8 @@ type Report struct {
 	Metadata      Metadata            `json:",omitempty"`
 	Results       Results             `json:",omitempty"`
 
-	// SBOM
-	CycloneDX *ftypes.CycloneDX `json:"-"` // Just for internal usage, not exported in JSON
+	// parsed SBOM
+	BOM *core.BOM `json:"-"` // Just for internal usage, not exported in JSON
 }
 
 // Metadata represents a metadata of artifact
diff --git a/pkg/types/sbom.go b/pkg/types/sbom.go
index 61d21b7d7867..82f6139e48f7 100644
--- a/pkg/types/sbom.go
+++ b/pkg/types/sbom.go
@@ -1,21 +1,20 @@
 package types
 
 import (
-	stypes "github.com/spdx/tools-golang/spdx"
-
-	"github.com/aquasecurity/trivy/pkg/fanal/types"
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
 )
 
+type SBOMSource = string
+
 type SBOM struct {
-	OS           types.OS
-	Packages     []types.PackageInfo
-	Applications []types.Application
+	Metadata Metadata
 
-	CycloneDX *types.CycloneDX
-	SPDX      *stypes.Document
-}
+	Packages     []ftypes.PackageInfo
+	Applications []ftypes.Application
 
-type SBOMSource = string
+	BOM *core.BOM
+}
 
 const (
 	SBOMSourceOCI   = SBOMSource("oci")
diff --git a/pkg/uuid/uuid.go b/pkg/uuid/uuid.go
index 2841b7f099bc..3e3ad456a805 100644
--- a/pkg/uuid/uuid.go
+++ b/pkg/uuid/uuid.go
@@ -1,3 +1,5 @@
+//go:build !tinygo.wasm
+
 package uuid
 
 import (
@@ -7,7 +9,13 @@ import (
 	"github.com/google/uuid"
 )
 
-var newUUID func() uuid.UUID = uuid.New
+type UUID = uuid.UUID
+
+var (
+	newUUID   func() uuid.UUID = uuid.New
+	Nil                        = uuid.Nil
+	MustParse                  = uuid.MustParse
+)
 
 // SetFakeUUID sets a fake UUID for testing.
 // The 'format' is used to generate a fake UUID and
@@ -23,6 +31,6 @@ func SetFakeUUID(t *testing.T, format string) {
 	})
 }
 
-func New() uuid.UUID {
+func New() UUID {
 	return newUUID()
 }
diff --git a/pkg/uuid/uuid_tinygo.go b/pkg/uuid/uuid_tinygo.go
new file mode 100644
index 000000000000..fe328cea9282
--- /dev/null
+++ b/pkg/uuid/uuid_tinygo.go
@@ -0,0 +1,13 @@
+//go:build tinygo.wasm
+
+package uuid
+
+// TinyGo doesn't work with github.com/google/uuid
+
+type UUID string
+
+func (UUID) String() string { return "" }
+
+const Nil = ""
+
+func New() UUID { return "" }
diff --git a/pkg/vex/cyclonedx.go b/pkg/vex/cyclonedx.go
index f1a860fe7ae7..a956703da3ae 100644
--- a/pkg/vex/cyclonedx.go
+++ b/pkg/vex/cyclonedx.go
@@ -5,13 +5,13 @@ import (
 	"github.com/samber/lo"
 	"go.uber.org/zap"
 
-	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/log"
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
 	"github.com/aquasecurity/trivy/pkg/types"
 )
 
 type CycloneDX struct {
-	sbom       *ftypes.CycloneDX
+	sbom       *core.BOM
 	statements []Statement
 	logger     *zap.SugaredLogger
 }
@@ -23,7 +23,7 @@ type Statement struct {
 	Justification   string
 }
 
-func newCycloneDX(cdxSBOM *ftypes.CycloneDX, vex *cdx.BOM) *CycloneDX {
+func newCycloneDX(sbom *core.BOM, vex *cdx.BOM) *CycloneDX {
 	var stmts []Statement
 	for _, vuln := range lo.FromPtr(vex.Vulnerabilities) {
 		affects := lo.Map(lo.FromPtr(vuln.Affects), func(item cdx.Affects, index int) string {
@@ -39,7 +39,7 @@ func newCycloneDX(cdxSBOM *ftypes.CycloneDX, vex *cdx.BOM) *CycloneDX {
 		})
 	}
 	return &CycloneDX{
-		sbom:       cdxSBOM,
+		sbom:       sbom,
 		statements: stmts,
 		logger:     log.Logger.With(zap.String("VEX format", "CycloneDX")),
 	}
diff --git a/pkg/vex/vex.go b/pkg/vex/vex.go
index 61a41a960246..dc2c118b56bc 100644
--- a/pkg/vex/vex.go
+++ b/pkg/vex/vex.go
@@ -11,6 +11,7 @@ import (
 	"github.com/sirupsen/logrus"
 	"golang.org/x/xerrors"
 
+	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
 	"github.com/aquasecurity/trivy/pkg/sbom"
 	"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx"
 	"github.com/aquasecurity/trivy/pkg/types"
@@ -66,10 +67,10 @@ func decodeCycloneDXJSON(r io.ReadSeeker, report types.Report) (VEX, error) {
 	if err != nil {
 		return nil, xerrors.Errorf("json decode error: %w", err)
 	}
-	if report.CycloneDX == nil {
+	if report.ArtifactType != ftypes.ArtifactCycloneDX {
 		return nil, xerrors.New("CycloneDX VEX can be used with CycloneDX SBOM")
 	}
-	return newCycloneDX(report.CycloneDX, vex), nil
+	return newCycloneDX(report.BOM, vex), nil
 }
 
 func decodeOpenVEX(r io.ReadSeeker) (VEX, error) {
diff --git a/pkg/vex/vex_test.go b/pkg/vex/vex_test.go
index 08724884074d..d591ccfdc6c6 100644
--- a/pkg/vex/vex_test.go
+++ b/pkg/vex/vex_test.go
@@ -1,6 +1,7 @@
 package vex_test
 
 import (
+	"github.com/aquasecurity/trivy/pkg/sbom/core"
 	"os"
 	"testing"
 
@@ -115,7 +116,8 @@ func TestVEX_Filter(t *testing.T) {
 			fields: fields{
 				filePath: "testdata/cyclonedx.json",
 				report: types.Report{
-					CycloneDX: &ftypes.CycloneDX{
+					ArtifactType: ftypes.ArtifactCycloneDX,
+					BOM: &core.BOM{
 						SerialNumber: "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79",
 						Version:      1,
 					},
@@ -197,7 +199,8 @@ func TestVEX_Filter(t *testing.T) {
 			fields: fields{
 				filePath: "testdata/cyclonedx.json",
 				report: types.Report{
-					CycloneDX: &ftypes.CycloneDX{
+					ArtifactType: ftypes.ArtifactCycloneDX,
+					BOM: &core.BOM{
 						SerialNumber: "urn:uuid:wrong",
 						Version:      1,
 					},