diff --git a/sbom/cnquery_bom_test.go b/sbom/cnquery_bom_test.go index 96712c6755..79ced70da9 100644 --- a/sbom/cnquery_bom_test.go +++ b/sbom/cnquery_bom_test.go @@ -29,8 +29,20 @@ func TestSimpleBom(t *testing.T) { require.NoError(t, err) data := output.String() + + // ensure os package is included assert.Contains(t, data, "alpine-baselayout") assert.Contains(t, data, "cpe:2.3:a:alpine-baselayout:alpine-baselayout:1695795276:aarch64:*:*:*:*:*:*") // check that package files are included assert.Contains(t, data, "etc/profile.d/color_prompt.sh.disabled") + + // ensure python package is included + assert.Contains(t, data, "pip") + assert.Contains(t, data, "cpe:2.3:a:pip_project:pip:21.2.4:*:*:*:*:*:*:*") + assert.Contains(t, data, "pkg:pypi/pip@21.2.4") + + // ensure npm package is included + assert.Contains(t, data, "npm") + assert.Contains(t, data, "cpe:2.3:a:npm:npm:10.2.4:*:*:*:*:*:*:*") + assert.Contains(t, data, "pkg:npm/npm@10.2.4") } diff --git a/sbom/cyclonedx_test.go b/sbom/cyclonedx_test.go index cf3a2f1ce6..384789643b 100644 --- a/sbom/cyclonedx_test.go +++ b/sbom/cyclonedx_test.go @@ -32,8 +32,20 @@ func TestCycloneDX(t *testing.T) { data := output.String() // os.WriteFile("./testdata/bom_cyclone.json", output.Bytes(), 0700) assert.Contains(t, data, "cyclonedx") + + // ensure os package is included assert.Contains(t, data, "alpine-baselayout") assert.Contains(t, data, "cpe:2.3:a:alpine-baselayout:alpine-baselayout:1695795276:aarch64:*:*:*:*:*:*") // check that package files are included assert.Contains(t, data, "etc/profile.d/color_prompt.sh.disabled") + + // ensure python package is included + assert.Contains(t, data, "pip") + assert.Contains(t, data, "cpe:2.3:a:pip_project:pip:21.2.4:*:*:*:*:*:*:*") + assert.Contains(t, data, "pkg:pypi/pip@21.2.4") + + // ensure npm package is included + assert.Contains(t, data, "npm") + assert.Contains(t, data, "cpe:2.3:a:npm:npm:10.2.4:*:*:*:*:*:*:*") + assert.Contains(t, data, "pkg:npm/npm@10.2.4") } diff --git a/sbom/report_collection.go b/sbom/report_collection.go index 42b82b969a..7c872d9823 100644 --- a/sbom/report_collection.go +++ b/sbom/report_collection.go @@ -55,6 +55,7 @@ type BomReport struct { Asset *BomAsset `json:"asset,omitempty"` Packages []BomPackage `json:"packages.list,omitempty"` PythonPackages []BomPackage `json:"python.packages,omitempty"` + NpmPackages []BomPackage `json:"npm.packages.list,omitempty"` } func (b *BomReport) ToJSON() ([]byte, error) { diff --git a/sbom/sbom.go b/sbom/sbom.go index e33937d9ca..095d328986 100644 --- a/sbom/sbom.go +++ b/sbom/sbom.go @@ -124,6 +124,26 @@ func GenerateBom(r *ReportCollectionJson) ([]Sbom, error) { }) } + bom.Packages = append(bom.Packages, bomPkg) + } + } + if rb.NpmPackages != nil { + for _, pkg := range rb.NpmPackages { + bomPkg := &Package{ + Name: pkg.Name, + Version: pkg.Version, + Purl: pkg.Purl, + Cpes: pkg.CPEs, + Type: "npm", + } + + for _, filepath := range pkg.FilePaths { + bomPkg.EvidenceList = append(bomPkg.EvidenceList, &Evidence{ + Type: EvidenceType_EVIDENCE_TYPE_FILE, + Value: filepath, + }) + } + bom.Packages = append(bom.Packages, bomPkg) } } diff --git a/sbom/sbom.mql.yaml b/sbom/sbom.mql.yaml index fb4419111d..d83891d41f 100644 --- a/sbom/sbom.mql.yaml +++ b/sbom/sbom.mql.yaml @@ -15,4 +15,7 @@ packs: mql: packages { name version purl cpes.map(uri) format files.map(path) } - uid: mondoo-sbom-python-packages title: Retrieve list of installed Python packages - mql: python.packages { name version purl cpes.map(uri) file.path } \ No newline at end of file + mql: python.packages { name version purl cpes.map(uri) file.path } + - uid: mondoo-sbom-npm-packages + title: Retrieve list of installed npm packages + mql: npm.packages { name version purl cpes.map(uri) files.map(path) } \ No newline at end of file diff --git a/sbom/sbom_test.go b/sbom/sbom_test.go index 60a8032dc5..d3840a2c8b 100644 --- a/sbom/sbom_test.go +++ b/sbom/sbom_test.go @@ -25,20 +25,38 @@ func TestSbomParsing(t *testing.T) { assert.Equal(t, "3.19.0", selectedBom.Asset.Platform.Version) assert.Equal(t, []string{"//platformid.api.mondoo.app/runtime/docker/images/1dc785547989b0db1c3cd9949c57574393e69bea98bfe044b0588e24721aa402"}, selectedBom.Asset.PlatformIds) - var pkg *Package - for i := range selectedBom.Packages { - if selectedBom.Packages[i].Name == "alpine-baselayout" { - pkg = selectedBom.Packages[i] - break - } - } - require.NotNil(t, pkg) + // search os package + pkg := findProtoPkg(selectedBom.Packages, "alpine-baselayout") assert.Equal(t, "alpine-baselayout", pkg.Name) assert.Contains(t, pkg.EvidenceList, &Evidence{ Type: EvidenceType_EVIDENCE_TYPE_FILE, Value: "etc/profile.d/color_prompt.sh.disabled", }) + // search python package + pkg = findProtoPkg(selectedBom.Packages, "pip") + assert.Equal(t, "pip", pkg.Name) + assert.Contains(t, pkg.EvidenceList, &Evidence{ + Type: EvidenceType_EVIDENCE_TYPE_FILE, + Value: "/opt/lib/python3.9/site-packages/pip-21.2.4.dist-info/METADATA", + }) + + // search npm package + pkg = findProtoPkg(selectedBom.Packages, "npm") + assert.Equal(t, "npm", pkg.Name) + assert.Contains(t, pkg.EvidenceList, &Evidence{ + Type: EvidenceType_EVIDENCE_TYPE_FILE, + Value: "/opt/lib/node_modules/npm/package.json", + }) +} + +func findProtoPkg(pkgs []*Package, name string) *Package { + for i := range pkgs { + if pkgs[i].Name == name { + return pkgs[i] + } + } + panic("package not found: " + name) } func TestArnGeneration(t *testing.T) { diff --git a/sbom/spdx_test.go b/sbom/spdx_test.go index 059d768b8d..1dbbe28dd4 100644 --- a/sbom/spdx_test.go +++ b/sbom/spdx_test.go @@ -29,8 +29,20 @@ func TestSpdx(t *testing.T) { err = exporter.Render(&output, &selectedBom) require.NoError(t, err) - res := output.String() - assert.Contains(t, res, "SPDX-2.3") - assert.Contains(t, res, "\"name\": \"alpine-baselayout\",") - assert.Contains(t, res, "\"cpe:2.3:a:alpine-baselayout:alpine-baselayout:1695795276:aarch64:*:*:*:*:*:*\"") + data := output.String() + assert.Contains(t, data, "SPDX-2.3") + + // ensure os package is included + assert.Contains(t, data, "alpine-baselayout") + assert.Contains(t, data, "cpe:2.3:a:alpine-baselayout:alpine-baselayout:1695795276:aarch64:*:*:*:*:*:*") + + // ensure python package is included + assert.Contains(t, data, "pip") + assert.Contains(t, data, "cpe:2.3:a:pip_project:pip:21.2.4:*:*:*:*:*:*:*") + assert.Contains(t, data, "pkg:pypi/pip@21.2.4") + + // ensure npm package is included + assert.Contains(t, data, "npm") + assert.Contains(t, data, "cpe:2.3:a:npm:npm:10.2.4:*:*:*:*:*:*:*") + assert.Contains(t, data, "pkg:npm/npm@10.2.4") } diff --git a/sbom/testdata/alpine.json b/sbom/testdata/alpine.json index 12a8125210..cb60b50a9e 100644 --- a/sbom/testdata/alpine.json +++ b/sbom/testdata/alpine.json @@ -304,7 +304,32 @@ ] }, "//local.cnquery.io/run/local-execution/queries/mondoo-sbom-python-packages": { - "python.packages": [] + "python.packages": [ + { + "name": "pip", + "version": "21.2.4", + "cpes.map": [ + "cpe:2.3:a:pip_project:pip:21.2.4:*:*:*:*:*:*:*" + ], + "purl": "pkg:pypi/pip@21.2.4", + "file.path": "/opt/lib/python3.9/site-packages/pip-21.2.4.dist-info/METADATA" + } + ] + }, + "//local.cnquery.io/run/local-execution/queries/mondoo-sbom-npm-packages": { + "npm.packages.list": [ + { + "name": "npm", + "files.map": [ + "/opt/lib/node_modules/npm/package.json" + ], + "cpes.map": [ + "cpe:2.3:a:npm:npm:10.2.4:*:*:*:*:*:*:*" + ], + "purl": "pkg:npm/npm@10.2.4", + "version": "10.2.4" + } + ] } } },