feat(k8s): add support for vulnerability detection (#5268)

Signed-off-by: knqyf263 <knqyf263@gmail.com>
Signed-off-by: chenk <hen.keinan@gmail.com>
Co-authored-by: DmitriyLewen <dmitriy.lewen@smartforce.io>
Co-authored-by: chenk <hen.keinan@gmail.com>
This commit is contained in:
Teppei Fukuda
2023-10-14 21:32:55 +09:00
committed by GitHub
parent 24a0d92145
commit cbbd1ce1f0
21 changed files with 1451 additions and 117 deletions

View File

@@ -5,6 +5,7 @@ The following packages are supported.
- [OS packages](#os-packages)
- [Language-specific packages](#language-specific-packages)
- [Kubernetes components (control plane, node and addons)](#kubernetes-components-control-plane-node-and-addons)
Trivy also detects known vulnerabilities in Kubernetes components using KBOM (Kubernetes bill of Material) scanning. To learn more, see the [documentation for Kubernetes scanning](../target/kubernetes.md#KBOM).
@@ -106,9 +107,9 @@ Trivy can detect vulnerabilities in Kubernetes clusters and components.
### Data Sources
| Vendor | Source |
| ------------- | ------------------------------------------------------------ |
| Kubernetes | [Kubernetes Official CVE feed][^1] |
| Vendor | Source |
| ------------- |---------------------------------------------|
| Kubernetes | [Kubernetes Official CVE feed][k8s-cve][^1] |
[^1]: Some manual triage and correction has been made.
@@ -195,4 +196,4 @@ Currently, specifying a username and password is not supported.
[nvd]: https://nvd.nist.gov/vuln
[Kubernetes Official CVE feed]: https://kubernetes.io/docs/reference/issues-security/official-cve-feed/
[k8s-cve]: https://kubernetes.io/docs/reference/issues-security/official-cve-feed/

4
go.mod
View File

@@ -23,9 +23,9 @@ require (
github.com/aquasecurity/table v1.8.0
github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da
github.com/aquasecurity/tml v0.6.1
github.com/aquasecurity/trivy-db v0.0.0-20230831170347-f732860d4917
github.com/aquasecurity/trivy-db v0.0.0-20231005141211-4fc651f7ac8d
github.com/aquasecurity/trivy-java-db v0.0.0-20230209231723-7cddb1406728
github.com/aquasecurity/trivy-kubernetes v0.5.7
github.com/aquasecurity/trivy-kubernetes v0.5.8-0.20230928134646-b414e546fe6d
github.com/aws/aws-sdk-go v1.45.19
github.com/aws/aws-sdk-go-v2 v1.21.0
github.com/aws/aws-sdk-go-v2/config v1.18.38

8
go.sum
View File

@@ -343,12 +343,12 @@ github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da h1:pj/adfN
github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da/go.mod h1:852lbQLpK2nCwlR4ZLYIccxYCfoQao6q9Nl6tjz54v8=
github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gwo=
github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY=
github.com/aquasecurity/trivy-db v0.0.0-20230831170347-f732860d4917 h1:MQd7h7yUyA8UlUzhjNMzpUX0NpD7jfxmRfSKwp/Ji3E=
github.com/aquasecurity/trivy-db v0.0.0-20230831170347-f732860d4917/go.mod h1:WJ5Qnk5ZNGWvks07GOZe2IOsuXrPfSC5c8hYGOGfrsU=
github.com/aquasecurity/trivy-db v0.0.0-20231005141211-4fc651f7ac8d h1:fjI9mkoTUAkbGqpzt9nJsO24RAdfG+ZSiLFj0G2jO8c=
github.com/aquasecurity/trivy-db v0.0.0-20231005141211-4fc651f7ac8d/go.mod h1:cj9/QmD9N3OZnKQMp+/DvdV+ym3HyIkd4e+F0ZM3ZGs=
github.com/aquasecurity/trivy-java-db v0.0.0-20230209231723-7cddb1406728 h1:0eS+V7SXHgqoT99tV1mtMW6HL4HdoB9qGLMCb1fZp8A=
github.com/aquasecurity/trivy-java-db v0.0.0-20230209231723-7cddb1406728/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8=
github.com/aquasecurity/trivy-kubernetes v0.5.7 h1:+tIrSnIkvweL+cuK0SSiYxF8EvKT3Xk1iuE9EWduV+c=
github.com/aquasecurity/trivy-kubernetes v0.5.7/go.mod h1:e1RaMcs2R/C+eP1Pi7JyhDB7Qn1PNRg5rTVwuJL7AiE=
github.com/aquasecurity/trivy-kubernetes v0.5.8-0.20230928134646-b414e546fe6d h1:5urHj0NMGflp/M9Ll5QlKfo0Kf6nJ01RED1HRgl0CeE=
github.com/aquasecurity/trivy-kubernetes v0.5.8-0.20230928134646-b414e546fe6d/go.mod h1:e1RaMcs2R/C+eP1Pi7JyhDB7Qn1PNRg5rTVwuJL7AiE=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=

View File

@@ -58,6 +58,15 @@ func TestSBOM(t *testing.T) {
},
golden: "testdata/fluentd-multiple-lockfiles.json.golden",
},
{
name: "minikube KBOM",
args: args{
input: "testdata/fixtures/sbom/minikube-kbom.json",
format: "json",
artifactType: "cyclonedx",
},
golden: "testdata/minikube-kbom.json.golden",
},
{
name: "centos7 in in-toto attestation",
args: args{

View File

@@ -144,3 +144,8 @@
ID: "cbl-mariner"
Name: "CBL-Mariner Vulnerability Data"
URL: "https://github.com/microsoft/CBL-MarinerVulnerabilityData"
- key: k8s::Official Kubernetes CVE Feed
value:
ID: "k8s"
Name: "Official Kubernetes CVE Feed"
URL: "https://kubernetes.io/docs/reference/issues-security/official-cve-feed/index.json"

View File

@@ -0,0 +1,16 @@
- bucket: "k8s::Official Kubernetes CVE Feed"
pairs:
- bucket: k8s.io/kubelet
pairs:
- key: CVE-2023-2431
value:
PatchedVersions:
- 1.24.14
- 1.25.9
- 1.26.4
- 1.27.1
VulnerableVersions:
- "< 1.24.14"
- ">= 1.25.0, < 1.25.9"
- ">= 1.26.0, < 1.26.4"
- ">= 1.27.0, < 1.27.1"

View File

@@ -1037,6 +1037,20 @@
ghsa: 3.0
nvd: 3.0
redhat: 3.0
- key: CVE-2023-2431
value:
Title: "Bypass of seccomp profile enforcement "
Description: "A security issue was discovered in Kubelet that allows pods to bypass the seccomp profile enforcement..."
Severity: LOW
VendorSeverity:
k8s: 1
CVSS:
k8s:
V3Vector: "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:N"
V3Score: 3.4
References:
- https://github.com/kubernetes/kubernetes/issues/118690
- https://www.cve.org/cverecord?id=CVE-2023-2431
- key: CVE-2021-3712
value:
CVSS:

View File

@@ -0,0 +1,434 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:e2daaea6-d96f-4b84-960c-0d72c348cd23",
"version": 1,
"metadata": {
"timestamp": "2023-09-29T06:25:00+00:00",
"tools": [
{
"vendor": "aquasecurity",
"name": "trivy",
"version": "0.45.1-15-g7bbd0d097"
}
],
"component": {
"bom-ref": "pkg:k8s/k8s.io%2Fkubernetes@1.27.0",
"type": "platform",
"name": "k8s.io/kubernetes",
"version": "1.27.0",
"purl": "pkg:k8s/k8s.io%2Fkubernetes@1.27.0",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "cluster"
}
]
}
},
"components": [
{
"bom-ref": "5262e708-f1a3-4fca-a1c3-0a8384f7f4a5",
"type": "operating-system",
"name": "ubuntu",
"version": "22.04.2",
"properties": [
{
"name": "aquasecurity:trivy:Class",
"value": "os-pkgs"
},
{
"name": "aquasecurity:trivy:Type",
"value": "ubuntu"
}
]
},
{
"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"
}
]
},
{
"bom-ref": "a6350ac3-52f6-4c5f-a3e3-184b9a634bef",
"type": "platform",
"name": "minikube",
"properties": [
{
"name": "aquasecurity:trivy:Architecture",
"value": "arm64"
},
{
"name": "aquasecurity:trivy:HostName",
"value": "minikube"
},
{
"name": "aquasecurity:trivy:KernelVersion",
"value": "5.15.49-linuxkit-pr"
},
{
"name": "aquasecurity:trivy:NodeRole",
"value": "master"
},
{
"name": "aquasecurity:trivy:OperatingSystem",
"value": "linux"
},
{
"name": "aquasecurity:trivy:resource:Name",
"value": "minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "node"
}
]
},
{
"bom-ref": "b19a88a3-017d-4e70-a73a-75f48696ec0f",
"type": "application",
"name": "kube-dns",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "coredns-5d78c9869d-nd92n"
}
]
},
{
"bom-ref": "b1c502c9-3c6e-43af-822b-1cb55c6c6ff3",
"type": "application",
"name": "go.etcd.io/etcd/v3",
"version": "3.5.7-0",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "etcd-minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "controlPlane"
}
]
},
{
"bom-ref": "pkg:golang/docker@24.0.4",
"type": "application",
"name": "docker",
"version": "24.0.4",
"purl": "pkg:golang/docker@24.0.4",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "docker"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "node"
}
]
},
{
"bom-ref": "pkg:k8s/k8s.io%2Fapiserver@1.27.0",
"type": "application",
"name": "k8s.io/apiserver",
"version": "1.27.0",
"purl": "pkg:k8s/k8s.io%2Fapiserver@1.27.0",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "kube-apiserver-minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "controlPlane"
}
]
},
{
"bom-ref": "pkg:k8s/k8s.io%2Fcontroller-manager@1.27.0",
"type": "application",
"name": "k8s.io/controller-manager",
"version": "1.27.0",
"purl": "pkg:k8s/k8s.io%2Fcontroller-manager@1.27.0",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "kube-controller-manager-minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "controlPlane"
}
]
},
{
"bom-ref": "pkg:k8s/k8s.io%2Fkube-proxy@1.27.0",
"type": "application",
"name": "k8s.io/kube-proxy",
"version": "1.27.0",
"purl": "pkg:k8s/k8s.io%2Fkube-proxy@1.27.0",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "kube-proxy-4wftc"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "node"
}
]
},
{
"bom-ref": "pkg:k8s/k8s.io%2Fkube-scheduler@1.27.0",
"type": "application",
"name": "k8s.io/kube-scheduler",
"version": "1.27.0",
"purl": "pkg:k8s/k8s.io%2Fkube-scheduler@1.27.0",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "kube-scheduler-minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "controlPlane"
}
]
},
{
"bom-ref": "pkg:k8s/k8s.io%2Fkubelet@1.27.0",
"type": "application",
"name": "k8s.io/kubelet",
"version": "1.27.0",
"purl": "pkg:k8s/k8s.io%2Fkubelet@1.27.0",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "k8s.io/kubelet"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "node"
}
]
},
{
"bom-ref": "pkg:oci/coredns@sha256%3Aa0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e?repository_url=registry.k8s.io%2Fcoredns%2Fcoredns",
"type": "container",
"name": "registry.k8s.io/coredns/coredns",
"version": "sha256:a0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e",
"purl": "pkg:oci/coredns@sha256%3Aa0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e?repository_url=registry.k8s.io%2Fcoredns%2Fcoredns",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/coredns/coredns:1.10.1"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
},
{
"bom-ref": "pkg:oci/etcd@sha256%3A51eae8381dcb1078289fa7b4f3df2630cdc18d09fb56f8e56b41c40e191d6c83?repository_url=registry.k8s.io%2Fetcd",
"type": "container",
"name": "registry.k8s.io/etcd",
"version": "sha256:51eae8381dcb1078289fa7b4f3df2630cdc18d09fb56f8e56b41c40e191d6c83",
"purl": "pkg:oci/etcd@sha256%3A51eae8381dcb1078289fa7b4f3df2630cdc18d09fb56f8e56b41c40e191d6c83?repository_url=registry.k8s.io%2Fetcd",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/etcd:3.5.7-0"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
},
{
"bom-ref": "pkg:oci/kube-apiserver@sha256%3A697cd88d94f7f2ef42144cb3072b016dcb2e9251f0e7d41a7fede557e555452d?repository_url=registry.k8s.io%2Fkube-apiserver",
"type": "container",
"name": "registry.k8s.io/kube-apiserver",
"version": "sha256:697cd88d94f7f2ef42144cb3072b016dcb2e9251f0e7d41a7fede557e555452d",
"purl": "pkg:oci/kube-apiserver@sha256%3A697cd88d94f7f2ef42144cb3072b016dcb2e9251f0e7d41a7fede557e555452d?repository_url=registry.k8s.io%2Fkube-apiserver",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/kube-apiserver:1.27.0"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
},
{
"bom-ref": "pkg:oci/kube-controller-manager@sha256%3A6286e500782ad6d0b37a1b8be57fc73f597dc931dfc73ff18ce534059803b265?repository_url=registry.k8s.io%2Fkube-controller-manager",
"type": "container",
"name": "registry.k8s.io/kube-controller-manager",
"version": "sha256:6286e500782ad6d0b37a1b8be57fc73f597dc931dfc73ff18ce534059803b265",
"purl": "pkg:oci/kube-controller-manager@sha256%3A6286e500782ad6d0b37a1b8be57fc73f597dc931dfc73ff18ce534059803b265?repository_url=registry.k8s.io%2Fkube-controller-manager",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/kube-controller-manager:1.27.0"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
},
{
"bom-ref": "pkg:oci/kube-proxy@sha256%3A4bcb707da9898d2625f5d4edc6d0c96519a24f16db914fc673aa8f97e41dbabf?repository_url=registry.k8s.io%2Fkube-proxy",
"type": "container",
"name": "registry.k8s.io/kube-proxy",
"version": "sha256:4bcb707da9898d2625f5d4edc6d0c96519a24f16db914fc673aa8f97e41dbabf",
"purl": "pkg:oci/kube-proxy@sha256%3A4bcb707da9898d2625f5d4edc6d0c96519a24f16db914fc673aa8f97e41dbabf?repository_url=registry.k8s.io%2Fkube-proxy",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/kube-proxy:1.27.0"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
},
{
"bom-ref": "pkg:oci/kube-scheduler@sha256%3A5897d7a97d23dce25cbf36fcd6e919180a8ef904bf5156583ffdb6a733ab04af?repository_url=registry.k8s.io%2Fkube-scheduler",
"type": "container",
"name": "registry.k8s.io/kube-scheduler",
"version": "sha256:5897d7a97d23dce25cbf36fcd6e919180a8ef904bf5156583ffdb6a733ab04af",
"purl": "pkg:oci/kube-scheduler@sha256%3A5897d7a97d23dce25cbf36fcd6e919180a8ef904bf5156583ffdb6a733ab04af?repository_url=registry.k8s.io%2Fkube-scheduler",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/kube-scheduler:1.27.0"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
}
],
"dependencies": [
{
"ref": "5262e708-f1a3-4fca-a1c3-0a8384f7f4a5",
"dependsOn": []
},
{
"ref": "a62abb1f-cb38-4fde-90f3-2bda3b87ddb2",
"dependsOn": [
"pkg:golang/docker@24.0.4",
"pkg:k8s/k8s.io%2Fkubelet@1.27.0"
]
},
{
"ref": "a6350ac3-52f6-4c5f-a3e3-184b9a634bef",
"dependsOn": [
"5262e708-f1a3-4fca-a1c3-0a8384f7f4a5",
"a62abb1f-cb38-4fde-90f3-2bda3b87ddb2"
]
},
{
"ref": "b19a88a3-017d-4e70-a73a-75f48696ec0f",
"dependsOn": [
"pkg:oci/coredns@sha256%3Aa0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e?repository_url=registry.k8s.io%2Fcoredns%2Fcoredns"
]
},
{
"ref": "b1c502c9-3c6e-43af-822b-1cb55c6c6ff3",
"dependsOn": [
"pkg:oci/etcd@sha256%3A51eae8381dcb1078289fa7b4f3df2630cdc18d09fb56f8e56b41c40e191d6c83?repository_url=registry.k8s.io%2Fetcd"
]
},
{
"ref": "pkg:golang/docker@24.0.4",
"dependsOn": []
},
{
"ref": "pkg:k8s/k8s.io%2Fapiserver@1.27.0",
"dependsOn": [
"pkg:oci/kube-apiserver@sha256%3A697cd88d94f7f2ef42144cb3072b016dcb2e9251f0e7d41a7fede557e555452d?repository_url=registry.k8s.io%2Fkube-apiserver"
]
},
{
"ref": "pkg:k8s/k8s.io%2Fcontroller-manager@1.27.0",
"dependsOn": [
"pkg:oci/kube-controller-manager@sha256%3A6286e500782ad6d0b37a1b8be57fc73f597dc931dfc73ff18ce534059803b265?repository_url=registry.k8s.io%2Fkube-controller-manager"
]
},
{
"ref": "pkg:k8s/k8s.io%2Fkube-proxy@1.27.0",
"dependsOn": [
"pkg:oci/kube-proxy@sha256%3A4bcb707da9898d2625f5d4edc6d0c96519a24f16db914fc673aa8f97e41dbabf?repository_url=registry.k8s.io%2Fkube-proxy"
]
},
{
"ref": "pkg:k8s/k8s.io%2Fkube-scheduler@1.27.0",
"dependsOn": [
"pkg:oci/kube-scheduler@sha256%3A5897d7a97d23dce25cbf36fcd6e919180a8ef904bf5156583ffdb6a733ab04af?repository_url=registry.k8s.io%2Fkube-scheduler"
]
},
{
"ref": "pkg:k8s/k8s.io%2Fkubelet@1.27.0",
"dependsOn": []
},
{
"ref": "pkg:k8s/k8s.io%2Fkubernetes@1.27.0",
"dependsOn": [
"a6350ac3-52f6-4c5f-a3e3-184b9a634bef",
"b19a88a3-017d-4e70-a73a-75f48696ec0f",
"b1c502c9-3c6e-43af-822b-1cb55c6c6ff3",
"pkg:k8s/k8s.io%2Fapiserver@1.27.0",
"pkg:k8s/k8s.io%2Fcontroller-manager@1.27.0",
"pkg:k8s/k8s.io%2Fkube-proxy@1.27.0",
"pkg:k8s/k8s.io%2Fkube-scheduler@1.27.0"
]
},
{
"ref": "pkg:oci/coredns@sha256%3Aa0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e?repository_url=registry.k8s.io%2Fcoredns%2Fcoredns",
"dependsOn": []
},
{
"ref": "pkg:oci/etcd@sha256%3A51eae8381dcb1078289fa7b4f3df2630cdc18d09fb56f8e56b41c40e191d6c83?repository_url=registry.k8s.io%2Fetcd",
"dependsOn": []
},
{
"ref": "pkg:oci/kube-apiserver@sha256%3A697cd88d94f7f2ef42144cb3072b016dcb2e9251f0e7d41a7fede557e555452d?repository_url=registry.k8s.io%2Fkube-apiserver",
"dependsOn": []
},
{
"ref": "pkg:oci/kube-controller-manager@sha256%3A6286e500782ad6d0b37a1b8be57fc73f597dc931dfc73ff18ce534059803b265?repository_url=registry.k8s.io%2Fkube-controller-manager",
"dependsOn": []
},
{
"ref": "pkg:oci/kube-proxy@sha256%3A4bcb707da9898d2625f5d4edc6d0c96519a24f16db914fc673aa8f97e41dbabf?repository_url=registry.k8s.io%2Fkube-proxy",
"dependsOn": []
},
{
"ref": "pkg:oci/kube-scheduler@sha256%3A5897d7a97d23dce25cbf36fcd6e919180a8ef904bf5156583ffdb6a733ab04af?repository_url=registry.k8s.io%2Fkube-scheduler",
"dependsOn": []
}
],
"vulnerabilities": []
}

View File

@@ -0,0 +1,65 @@
{
"SchemaVersion": 2,
"ArtifactName": "testdata/fixtures/sbom/minikube-kbom.json",
"ArtifactType": "cyclonedx",
"Metadata": {
"OS": {
"Family": "ubuntu",
"Name": "22.04.2",
"EOSL": false
},
"ImageConfig": {
"architecture": "",
"created": "0001-01-01T00:00:00Z",
"os": "",
"rootfs": {
"type": "",
"diff_ids": null
},
"config": {}
}
},
"Results": [
{
"Target": "testdata/fixtures/sbom/minikube-kbom.json (ubuntu 22.04.2)",
"Class": "os-pkgs",
"Type": "ubuntu"
},
{
"Target": "Kubernetes",
"Class": "lang-pkgs",
"Type": "kubernetes",
"Vulnerabilities": [
{
"VulnerabilityID": "CVE-2023-2431",
"PkgName": "k8s.io/kubelet",
"InstalledVersion": "1.27.0",
"FixedVersion": "1.24.14, 1.25.9, 1.26.4, 1.27.1",
"Status": "fixed",
"Layer": {},
"SeveritySource": "k8s",
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2023-2431",
"PkgRef": "pkg:k8s/k8s.io%2Fkubelet@1.27.0",
"DataSource": {
"ID": "k8s",
"Name": "Official Kubernetes CVE Feed",
"URL": "https://kubernetes.io/docs/reference/issues-security/official-cve-feed/index.json"
},
"Title": "Bypass of seccomp profile enforcement ",
"Description": "A security issue was discovered in Kubelet that allows pods to bypass the seccomp profile enforcement...",
"Severity": "LOW",
"CVSS": {
"k8s": {
"V3Vector": "CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:L/I:L/A:N",
"V3Score": 3.4
}
},
"References": [
"https://github.com/kubernetes/kubernetes/issues/118690",
"https://www.cve.org/cverecord?id=CVE-2023-2431"
]
}
]
}
]
}

View File

@@ -65,9 +65,6 @@ func NewDriver(libType ftypes.LangType) (Driver, bool) {
// https://www.swift.org/package-manager/#importing-dependencies
ecosystem = vulnerability.Swift
comparer = compare.GenericComparer{}
case ftypes.Bitnami:
ecosystem = vulnerability.Bitnami
comparer = compare.GenericComparer{}
case ftypes.Cocoapods:
// CocoaPods uses RubyGems version specifiers
// https://guides.cocoapods.org/making/making-a-cocoapod.html#cocoapods-versioning-specifics
@@ -76,6 +73,12 @@ func NewDriver(libType ftypes.LangType) (Driver, bool) {
case ftypes.CondaPkg:
log.Logger.Warn("Conda package is supported for SBOM, not for vulnerability scanning")
return Driver{}, false
case ftypes.Bitnami:
ecosystem = vulnerability.Bitnami
comparer = compare.GenericComparer{}
case ftypes.K8sUpstream:
ecosystem = vulnerability.Kubernetes
comparer = compare.GenericComparer{}
default:
log.Logger.Warnf("The %q library type is not supported for vulnerability scanning", libType)
return Driver{}, false

View File

@@ -71,6 +71,13 @@ const (
Pub LangType = "pub"
Hex LangType = "hex"
Bitnami LangType = "bitnami"
K8sUpstream LangType = "kubernetes"
EKS LangType = "eks" // Amazon Elastic Kubernetes Service
GKE LangType = "gke" // Google Kubernetes Engine
AKS LangType = "aks" // Azure Kubernetes Service
RKE LangType = "rke" // Rancher Kubernetes Engine
OCP LangType = "ocp" // Red Hat OpenShift Container Platform
)
// Config files

View File

@@ -208,6 +208,17 @@ const (
func clusterInfoToReportResources(allArtifact []*artifacts.Artifact) (*core.Component, error) {
var coreComponents []*core.Component
var cInfo *core.Component
// Find the first node name to identify AKS cluster
var nodeName string
for _, artifact := range allArtifact {
if artifact.Kind != nodeInfo {
continue
}
nodeName = artifact.Name
break
}
for _, artifact := range allArtifact {
switch artifact.Kind {
case pod:
@@ -257,6 +268,7 @@ func clusterInfoToReportResources(allArtifact []*artifacts.Artifact) (*core.Comp
Type: cdx.ComponentTypeApplication,
Properties: toProperties(comp.Properties, k8sCoreComponentNamespace),
Components: imageComponents,
PackageURL: generatePURL(comp.Name, comp.Version, nodeName),
}
coreComponents = append(coreComponents, rootComponent)
case nodeInfo:
@@ -287,6 +299,7 @@ func clusterInfoToReportResources(allArtifact []*artifacts.Artifact) (*core.Comp
Type: cdx.ComponentTypePlatform,
Properties: cInfo.Properties,
Components: coreComponents,
PackageURL: generatePURL(cInfo.Name, cInfo.Version, nodeName),
}
return rootComponent, nil
}
@@ -313,20 +326,20 @@ func osNameVersion(name string) (string, string) {
}
func runtimeNameVersion(name string) (string, string) {
parts := strings.Split(name, "://")
if len(parts) == 2 {
name := parts[0]
switch parts[0] {
case "cri-o":
name = "github.com/cri-o/cri-o"
case "containerd":
name = "github.com/containerd/containerd"
case "cri-dockerd":
name = "github.com/Mirantis/cri-dockerd"
}
return name, parts[1]
runtime, ver, ok := strings.Cut(name, "://")
if !ok {
return "", ""
}
return "", ""
switch runtime {
case "cri-o":
name = "github.com/cri-o/cri-o"
case "containerd":
name = "github.com/containerd/containerd"
case "cri-dockerd":
name = "github.com/Mirantis/cri-dockerd"
}
return name, ver
}
func nodeComponent(nf bom.NodeInfo) *core.Component {
@@ -388,9 +401,7 @@ func nodeComponent(nf bom.NodeInfo) *core.Component {
Namespace: k8sCoreComponentNamespace,
},
},
PackageURL: &purl.PackageURL{
PackageURL: *packageurl.NewPackageURL(golang, "", kubelet, kubeletVersion, packageurl.Qualifiers{}, ""),
},
PackageURL: generatePURL(kubelet, kubeletVersion, nf.NodeName),
},
{
Type: cdx.ComponentTypeApplication,
@@ -431,3 +442,27 @@ func toProperties(props map[string]string, namespace string) []core.Property {
})
return properties
}
func generatePURL(name, ver, nodeName string) *purl.PackageURL {
// Identify k8s distribution. An empty namespace means upstream.
var namespace string
switch {
case strings.Contains(ver, "eks"):
namespace = purl.NamespaceEKS
case strings.Contains(ver, "gke"):
namespace = purl.NamespaceGKE
case strings.Contains(ver, "rke2"):
namespace = purl.NamespaceRKE
case strings.Contains(ver, "hotfix"):
if !strings.Contains(nodeName, "aks") {
// Unknown k8s distribution
return nil
}
namespace = purl.NamespaceAKS
case strings.Contains(nodeName, "ocp"):
namespace = purl.NamespaceOCP
}
return &purl.PackageURL{
PackageURL: *packageurl.NewPackageURL(purl.TypeK8s, namespace, name, ver, nil, ""),
}
}

View File

@@ -17,7 +17,7 @@ import (
"github.com/aquasecurity/trivy/pkg/sbom/cyclonedx/core"
)
func TestK8sClusterInfoReport(t *testing.T) {
func TestScanner_Scan(t *testing.T) {
flagOpts := flag.Options{ReportOptions: flag.ReportOptions{Format: "cyclonedx"}}
tests := []struct {
name string
@@ -34,31 +34,31 @@ func TestK8sClusterInfoReport(t *testing.T) {
Kind: "ClusterInfo",
Name: "k8s.io/kubernetes",
RawResource: map[string]interface{}{
"name": "k8s.io/kubernetes",
"version": "1.21.1",
"type": "ClusterInfo",
"Properties": map[string]string{
"Name": "kube-cluster",
"Name": "kind-kind",
"Type": "cluster",
},
"Name": "kube-apiserver-kind-control-plane",
"Version": "1.21.1",
},
},
{
Namespace: "kube-system",
Kind: "PodInfo",
Name: "kube-apiserver-kind-control-plane",
Name: "k8s.io/apiserver",
RawResource: map[string]interface{}{
"Containers": []interface{}{map[string]interface{}{
"Digest": "18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f",
"ID": "kube-apiserver:v1.21.1",
"Registry": "k8s.gcr.io",
"Repository": "kube-apiserver",
"Version": "v1.21.1",
"Containers": []interface{}{
map[string]interface{}{
"Digest": "18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f",
"ID": "kube-apiserver:v1.21.1",
"Registry": "k8s.gcr.io",
"Repository": "kube-apiserver",
"Version": "v1.21.1",
},
},
},
"Properties": map[string]string{
"ControlPlaneComponents": "kube-apiserver",
},
"Name": "kube-apiserver-kind-control-plane",
"Namespace": "kube-system",
"Name": "k8s.io/apiserver",
"Version": "1.21.1",
},
},
{
@@ -83,18 +83,40 @@ func TestK8sClusterInfoReport(t *testing.T) {
},
want: &core.Component{
Type: cdx.ComponentTypePlatform,
Name: "kube-apiserver-kind-control-plane",
Name: "k8s.io/kubernetes",
Version: "1.21.1",
Properties: []core.Property{
{Name: "Name", Value: "kube-cluster", Namespace: k8sCoreComponentNamespace},
{
Name: "Name",
Value: "kind-kind",
Namespace: k8sCoreComponentNamespace,
},
{
Name: "Type",
Value: "cluster",
Namespace: k8sCoreComponentNamespace,
},
},
PackageURL: &purl.PackageURL{
PackageURL: packageurl.PackageURL{
Type: purl.TypeK8s,
Name: "k8s.io/kubernetes",
Version: "1.21.1",
},
},
Components: []*core.Component{
{
Type: cdx.ComponentTypeApplication,
Name: "kube-apiserver-kind-control-plane",
Properties: []core.Property{
{Name: "ControlPlaneComponents", Value: "kube-apiserver", Namespace: k8sCoreComponentNamespace},
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",
},
},
Properties: []core.Property{},
Components: []*core.Component{
{
Type: cdx.ComponentTypeContainer,
@@ -110,15 +132,18 @@ func TestK8sClusterInfoReport(t *testing.T) {
Key: "repository_url",
Value: "k8s.gcr.io/kube-apiserver",
},
{
Key: "arch",
},
},
},
},
Properties: []core.Property{
{Name: cyc.PropertyPkgID, Value: "k8s.gcr.io/kube-apiserver:1.21.1"},
{Name: cyc.PropertyPkgType, Value: "oci"},
{
Name: cyc.PropertyPkgID,
Value: "k8s.gcr.io/kube-apiserver:1.21.1",
},
{
Name: cyc.PropertyPkgType,
Value: "oci",
},
},
},
},
@@ -127,13 +152,36 @@ func TestK8sClusterInfoReport(t *testing.T) {
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},
{
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,
},
},
Components: []*core.Component{
{
@@ -141,16 +189,32 @@ func TestK8sClusterInfoReport(t *testing.T) {
Name: "ubuntu",
Version: "21.04",
Properties: []core.Property{
{Name: "Class", Value: "os-pkgs", Namespace: ""},
{Name: "Type", Value: "ubuntu", Namespace: ""},
{
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: ""},
{
Name: "Class",
Value: "lang-pkgs",
Namespace: "",
},
{
Name: "Type",
Value: "golang",
Namespace: "",
},
},
Components: []*core.Component{
{
@@ -158,15 +222,22 @@ func TestK8sClusterInfoReport(t *testing.T) {
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},
{
Name: k8sComponentType,
Value: "node",
Namespace: k8sCoreComponentNamespace,
},
{
Name: k8sComponentName,
Value: "k8s.io/kubelet",
Namespace: k8sCoreComponentNamespace,
},
},
PackageURL: &purl.PackageURL{
PackageURL: packageurl.PackageURL{
Type: "golang",
Name: "k8s.io/kubelet",
Version: "1.21.1",
Qualifiers: packageurl.Qualifiers{},
Type: "k8s",
Name: "k8s.io/kubelet",
Version: "1.21.1",
},
},
},
@@ -175,8 +246,16 @@ func TestK8sClusterInfoReport(t *testing.T) {
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},
{
Name: k8sComponentType,
Value: "node",
Namespace: k8sCoreComponentNamespace,
},
{
Name: k8sComponentName,
Value: "github.com/containerd/containerd",
Namespace: k8sCoreComponentNamespace,
},
},
PackageURL: &purl.PackageURL{
PackageURL: packageurl.PackageURL{
@@ -262,3 +341,63 @@ func TestTestOsNameVersion(t *testing.T) {
})
}
}
func TestGeneratePURL(t *testing.T) {
tests := []struct {
name string
compName string
compVersion string
nodeName string
want string
}{
{
name: "native k8s component",
compName: "k8s.io/kubelet",
compVersion: "1.24.10",
nodeName: "kind-kind",
want: "pkg:k8s/k8s.io%2Fkubelet@1.24.10",
},
{
name: "GKE",
compName: "k8s.io/kubelet",
compVersion: "1.24.10-gke.2300",
nodeName: "gke-gke1796-default-pool-768cb718-sk1d",
want: "pkg:k8s/gke/k8s.io%2Fkubelet@1.24.10-gke.2300",
},
{
name: "AKS",
compName: "k8s.io/kubelet",
compVersion: "1.24.10-hotfix.20221110",
nodeName: "aks-default-23814474-vmss000000",
want: "pkg:k8s/aks/k8s.io%2Fkubelet@1.24.10-hotfix.20221110",
},
{
name: "EKS",
compName: "k8s.io/kubelet",
compVersion: "1.23.17-eks-8ccc7ba",
nodeName: "eks-vmss000000",
want: "pkg:k8s/eks/k8s.io%2Fkubelet@1.23.17-eks-8ccc7ba",
},
{
name: "Rancher",
compName: "k8s.io/kubelet",
compVersion: "1.24.11+rke2r1",
nodeName: "ip-10-0-5-23",
want: "pkg:k8s/rke/k8s.io%2Fkubelet@1.24.11%2Brke2r1",
},
{
name: "OCP",
compName: "k8s.io/kubelet",
compVersion: "1.26.7+c7ee51f",
nodeName: "ocp413vpool14000-p8vnm-master-2",
want: "pkg:k8s/ocp/k8s.io%2Fkubelet@1.26.7%2Bc7ee51f",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := generatePURL(tt.compName, tt.compVersion, tt.nodeName)
assert.Equal(t, tt.want, got.String())
})
}
}

View File

@@ -18,6 +18,30 @@ import (
const (
TypeOCI = "oci"
TypeDart = "dart"
// TypeK8s is a custom type for Kubernetes components in PURL.
// - namespace: The service provider such as EKS or GKE. It is not case sensitive and must be lowercased.
// Known namespaces:
// - empty (upstream)
// - eks (AWS)
// - aks (GCP)
// - gke (Azure)
// - rke (Rancher)
// - name: The k8s component name and is case sensitive.
// - version: The combined version and release of a component.
//
// Examples:
// - pkg:k8s/upstream/k8s.io%2Fapiserver@1.24.1
// - pkg:k8s/eks/k8s.io%2Fkube-proxy@1.26.2-eksbuild.1
TypeK8s = "k8s"
NamespaceEKS = "eks"
NamespaceAKS = "aks"
NamespaceGKE = "gke"
NamespaceRKE = "rke"
NamespaceOCP = "ocp"
TypeUnknown = "unknown"
)
type PackageURL struct {
@@ -71,7 +95,7 @@ func (p *PackageURL) Package() *ftypes.Package {
// Return packages without namespace.
// OS packages are not supposed to have namespace.
if p.Namespace == "" || p.IsOSPkg() {
if p.Namespace == "" || p.Class() == types.ClassOSPkg {
return pkg
}
@@ -88,6 +112,7 @@ func (p *PackageURL) Package() *ftypes.Package {
}
// LangType returns an application type in Trivy
// nolint: gocyclo
func (p *PackageURL) LangType() ftypes.LangType {
switch p.Type {
case packageurl.TypeComposer:
@@ -120,12 +145,39 @@ func (p *PackageURL) LangType() ftypes.LangType {
return ftypes.Pub
case packageurl.TypeBitnami:
return ftypes.Bitnami
case TypeK8s:
switch p.Namespace {
case NamespaceEKS:
return ftypes.EKS
case NamespaceGKE:
return ftypes.GKE
case NamespaceAKS:
return ftypes.AKS
case NamespaceRKE:
return ftypes.RKE
case NamespaceOCP:
return ftypes.OCP
case "":
return ftypes.K8sUpstream
}
return TypeUnknown
default:
return TypeUnknown
}
return "unknown"
}
func (p *PackageURL) IsOSPkg() bool {
return p.Type == packageurl.TypeApk || p.Type == packageurl.TypeDebian || p.Type == packageurl.TypeRPM
func (p *PackageURL) Class() types.ResultClass {
switch p.Type {
case packageurl.TypeApk, packageurl.TypeDebian, packageurl.TypeRPM:
// OS packages
return types.ClassOSPkg
default:
if p.LangType() == TypeUnknown {
return types.ClassUnknown
}
// Language-specific packages
return types.ClassLangPkg
}
}
func (p *PackageURL) BOMRef() string {
@@ -221,15 +273,19 @@ func parseOCI(metadata types.Metadata) (packageurl.PackageURL, error) {
if index != -1 {
name = name[index+1:]
}
qualifiers := packageurl.Qualifiers{
packageurl.Qualifier{
var qualifiers packageurl.Qualifiers
if repoURL := digest.Repository.Name(); repoURL != "" {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "repository_url",
Value: digest.Repository.Name(),
},
packageurl.Qualifier{
Value: repoURL,
})
}
if arch := metadata.ImageConfig.Architecture; arch != "" {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "arch",
Value: metadata.ImageConfig.Architecture,
},
})
}
return *packageurl.NewPackageURL(packageurl.TypeOCI, "", name, digest.DigestStr(), qualifiers, ""), nil

View File

@@ -717,3 +717,47 @@ func TestPackage(t *testing.T) {
})
}
}
func TestPackageURL_LangType(t *testing.T) {
tests := []struct {
name string
purl packageurl.PackageURL
want ftypes.LangType
}{
{
name: "maven",
purl: packageurl.PackageURL{
Type: packageurl.TypeMaven,
Namespace: "org.springframework",
Name: "spring-core",
Version: "5.0.4.RELEASE",
},
want: ftypes.Jar,
},
{
name: "k8s",
purl: packageurl.PackageURL{
Type: purl.TypeK8s,
Name: "kubelet",
Version: "1.21.1",
},
want: ftypes.K8sUpstream,
},
{
name: "eks",
purl: packageurl.PackageURL{
Type: purl.TypeK8s,
Namespace: purl.NamespaceEKS,
Name: "kubelet",
Version: "1.21.1",
},
want: ftypes.EKS,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &purl.PackageURL{PackageURL: tt.purl}
assert.Equalf(t, tt.want, p.LangType(), "LangType()")
})
}
}

View File

@@ -0,0 +1,434 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:e2daaea6-d96f-4b84-960c-0d72c348cd23",
"version": 1,
"metadata": {
"timestamp": "2023-09-29T06:25:00+00:00",
"tools": [
{
"vendor": "aquasecurity",
"name": "trivy",
"version": "0.45.1-15-g7bbd0d097"
}
],
"component": {
"bom-ref": "pkg:k8s/k8s.io%2Fkubernetes@1.27.4",
"type": "platform",
"name": "k8s.io/kubernetes",
"version": "1.27.4",
"purl": "pkg:k8s/k8s.io%2Fkubernetes@1.27.4",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "cluster"
}
]
}
},
"components": [
{
"bom-ref": "5262e708-f1a3-4fca-a1c3-0a8384f7f4a5",
"type": "operating-system",
"name": "ubuntu",
"version": "22.04.2",
"properties": [
{
"name": "aquasecurity:trivy:Class",
"value": "os-pkgs"
},
{
"name": "aquasecurity:trivy:Type",
"value": "ubuntu"
}
]
},
{
"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"
}
]
},
{
"bom-ref": "a6350ac3-52f6-4c5f-a3e3-184b9a634bef",
"type": "platform",
"name": "minikube",
"properties": [
{
"name": "aquasecurity:trivy:Architecture",
"value": "arm64"
},
{
"name": "aquasecurity:trivy:HostName",
"value": "minikube"
},
{
"name": "aquasecurity:trivy:KernelVersion",
"value": "5.15.49-linuxkit-pr"
},
{
"name": "aquasecurity:trivy:NodeRole",
"value": "master"
},
{
"name": "aquasecurity:trivy:OperatingSystem",
"value": "linux"
},
{
"name": "aquasecurity:trivy:resource:Name",
"value": "minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "node"
}
]
},
{
"bom-ref": "b19a88a3-017d-4e70-a73a-75f48696ec0f",
"type": "application",
"name": "kube-dns",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "coredns-5d78c9869d-nd92n"
}
]
},
{
"bom-ref": "b1c502c9-3c6e-43af-822b-1cb55c6c6ff3",
"type": "application",
"name": "go.etcd.io/etcd/v3",
"version": "3.5.7-0",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "etcd-minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "controlPlane"
}
]
},
{
"bom-ref": "pkg:golang/docker@24.0.4",
"type": "application",
"name": "docker",
"version": "24.0.4",
"purl": "pkg:golang/docker@24.0.4",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "docker"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "node"
}
]
},
{
"bom-ref": "pkg:k8s/k8s.io%2Fapiserver@1.27.4",
"type": "application",
"name": "k8s.io/apiserver",
"version": "1.27.4",
"purl": "pkg:k8s/k8s.io%2Fapiserver@1.27.4",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "kube-apiserver-minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "controlPlane"
}
]
},
{
"bom-ref": "pkg:k8s/k8s.io%2Fcontroller-manager@1.27.4",
"type": "application",
"name": "k8s.io/controller-manager",
"version": "1.27.4",
"purl": "pkg:k8s/k8s.io%2Fcontroller-manager@1.27.4",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "kube-controller-manager-minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "controlPlane"
}
]
},
{
"bom-ref": "pkg:k8s/k8s.io%2Fkube-proxy@1.27.4",
"type": "application",
"name": "k8s.io/kube-proxy",
"version": "1.27.4",
"purl": "pkg:k8s/k8s.io%2Fkube-proxy@1.27.4",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "kube-proxy-4wftc"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "node"
}
]
},
{
"bom-ref": "pkg:k8s/k8s.io%2Fkube-scheduler@1.27.4",
"type": "application",
"name": "k8s.io/kube-scheduler",
"version": "1.27.4",
"purl": "pkg:k8s/k8s.io%2Fkube-scheduler@1.27.4",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "kube-scheduler-minikube"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "controlPlane"
}
]
},
{
"bom-ref": "pkg:k8s/k8s.io%2Fkubelet@1.27.4",
"type": "application",
"name": "k8s.io/kubelet",
"version": "1.27.4",
"purl": "pkg:k8s/k8s.io%2Fkubelet@1.27.4",
"properties": [
{
"name": "aquasecurity:trivy:resource:Name",
"value": "k8s.io/kubelet"
},
{
"name": "aquasecurity:trivy:resource:Type",
"value": "node"
}
]
},
{
"bom-ref": "pkg:oci/coredns@sha256%3Aa0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e?repository_url=registry.k8s.io%2Fcoredns%2Fcoredns",
"type": "container",
"name": "registry.k8s.io/coredns/coredns",
"version": "sha256:a0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e",
"purl": "pkg:oci/coredns@sha256%3Aa0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e?repository_url=registry.k8s.io%2Fcoredns%2Fcoredns",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/coredns/coredns:1.10.1"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
},
{
"bom-ref": "pkg:oci/etcd@sha256%3A51eae8381dcb1078289fa7b4f3df2630cdc18d09fb56f8e56b41c40e191d6c83?repository_url=registry.k8s.io%2Fetcd",
"type": "container",
"name": "registry.k8s.io/etcd",
"version": "sha256:51eae8381dcb1078289fa7b4f3df2630cdc18d09fb56f8e56b41c40e191d6c83",
"purl": "pkg:oci/etcd@sha256%3A51eae8381dcb1078289fa7b4f3df2630cdc18d09fb56f8e56b41c40e191d6c83?repository_url=registry.k8s.io%2Fetcd",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/etcd:3.5.7-0"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
},
{
"bom-ref": "pkg:oci/kube-apiserver@sha256%3A697cd88d94f7f2ef42144cb3072b016dcb2e9251f0e7d41a7fede557e555452d?repository_url=registry.k8s.io%2Fkube-apiserver",
"type": "container",
"name": "registry.k8s.io/kube-apiserver",
"version": "sha256:697cd88d94f7f2ef42144cb3072b016dcb2e9251f0e7d41a7fede557e555452d",
"purl": "pkg:oci/kube-apiserver@sha256%3A697cd88d94f7f2ef42144cb3072b016dcb2e9251f0e7d41a7fede557e555452d?repository_url=registry.k8s.io%2Fkube-apiserver",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/kube-apiserver:1.27.4"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
},
{
"bom-ref": "pkg:oci/kube-controller-manager@sha256%3A6286e500782ad6d0b37a1b8be57fc73f597dc931dfc73ff18ce534059803b265?repository_url=registry.k8s.io%2Fkube-controller-manager",
"type": "container",
"name": "registry.k8s.io/kube-controller-manager",
"version": "sha256:6286e500782ad6d0b37a1b8be57fc73f597dc931dfc73ff18ce534059803b265",
"purl": "pkg:oci/kube-controller-manager@sha256%3A6286e500782ad6d0b37a1b8be57fc73f597dc931dfc73ff18ce534059803b265?repository_url=registry.k8s.io%2Fkube-controller-manager",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/kube-controller-manager:1.27.4"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
},
{
"bom-ref": "pkg:oci/kube-proxy@sha256%3A4bcb707da9898d2625f5d4edc6d0c96519a24f16db914fc673aa8f97e41dbabf?repository_url=registry.k8s.io%2Fkube-proxy",
"type": "container",
"name": "registry.k8s.io/kube-proxy",
"version": "sha256:4bcb707da9898d2625f5d4edc6d0c96519a24f16db914fc673aa8f97e41dbabf",
"purl": "pkg:oci/kube-proxy@sha256%3A4bcb707da9898d2625f5d4edc6d0c96519a24f16db914fc673aa8f97e41dbabf?repository_url=registry.k8s.io%2Fkube-proxy",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/kube-proxy:1.27.4"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
},
{
"bom-ref": "pkg:oci/kube-scheduler@sha256%3A5897d7a97d23dce25cbf36fcd6e919180a8ef904bf5156583ffdb6a733ab04af?repository_url=registry.k8s.io%2Fkube-scheduler",
"type": "container",
"name": "registry.k8s.io/kube-scheduler",
"version": "sha256:5897d7a97d23dce25cbf36fcd6e919180a8ef904bf5156583ffdb6a733ab04af",
"purl": "pkg:oci/kube-scheduler@sha256%3A5897d7a97d23dce25cbf36fcd6e919180a8ef904bf5156583ffdb6a733ab04af?repository_url=registry.k8s.io%2Fkube-scheduler",
"properties": [
{
"name": "aquasecurity:trivy:PkgID",
"value": "registry.k8s.io/kube-scheduler:1.27.4"
},
{
"name": "aquasecurity:trivy:PkgType",
"value": "oci"
}
]
}
],
"dependencies": [
{
"ref": "5262e708-f1a3-4fca-a1c3-0a8384f7f4a5",
"dependsOn": []
},
{
"ref": "a62abb1f-cb38-4fde-90f3-2bda3b87ddb2",
"dependsOn": [
"pkg:golang/docker@24.0.4",
"pkg:k8s/k8s.io%2Fkubelet@1.27.4"
]
},
{
"ref": "a6350ac3-52f6-4c5f-a3e3-184b9a634bef",
"dependsOn": [
"5262e708-f1a3-4fca-a1c3-0a8384f7f4a5",
"a62abb1f-cb38-4fde-90f3-2bda3b87ddb2"
]
},
{
"ref": "b19a88a3-017d-4e70-a73a-75f48696ec0f",
"dependsOn": [
"pkg:oci/coredns@sha256%3Aa0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e?repository_url=registry.k8s.io%2Fcoredns%2Fcoredns"
]
},
{
"ref": "b1c502c9-3c6e-43af-822b-1cb55c6c6ff3",
"dependsOn": [
"pkg:oci/etcd@sha256%3A51eae8381dcb1078289fa7b4f3df2630cdc18d09fb56f8e56b41c40e191d6c83?repository_url=registry.k8s.io%2Fetcd"
]
},
{
"ref": "pkg:golang/docker@24.0.4",
"dependsOn": []
},
{
"ref": "pkg:k8s/k8s.io%2Fapiserver@1.27.4",
"dependsOn": [
"pkg:oci/kube-apiserver@sha256%3A697cd88d94f7f2ef42144cb3072b016dcb2e9251f0e7d41a7fede557e555452d?repository_url=registry.k8s.io%2Fkube-apiserver"
]
},
{
"ref": "pkg:k8s/k8s.io%2Fcontroller-manager@1.27.4",
"dependsOn": [
"pkg:oci/kube-controller-manager@sha256%3A6286e500782ad6d0b37a1b8be57fc73f597dc931dfc73ff18ce534059803b265?repository_url=registry.k8s.io%2Fkube-controller-manager"
]
},
{
"ref": "pkg:k8s/k8s.io%2Fkube-proxy@1.27.4",
"dependsOn": [
"pkg:oci/kube-proxy@sha256%3A4bcb707da9898d2625f5d4edc6d0c96519a24f16db914fc673aa8f97e41dbabf?repository_url=registry.k8s.io%2Fkube-proxy"
]
},
{
"ref": "pkg:k8s/k8s.io%2Fkube-scheduler@1.27.4",
"dependsOn": [
"pkg:oci/kube-scheduler@sha256%3A5897d7a97d23dce25cbf36fcd6e919180a8ef904bf5156583ffdb6a733ab04af?repository_url=registry.k8s.io%2Fkube-scheduler"
]
},
{
"ref": "pkg:k8s/k8s.io%2Fkubelet@1.27.4",
"dependsOn": []
},
{
"ref": "pkg:k8s/k8s.io%2Fkubernetes@1.27.4",
"dependsOn": [
"a6350ac3-52f6-4c5f-a3e3-184b9a634bef",
"b19a88a3-017d-4e70-a73a-75f48696ec0f",
"b1c502c9-3c6e-43af-822b-1cb55c6c6ff3",
"pkg:k8s/k8s.io%2Fapiserver@1.27.4",
"pkg:k8s/k8s.io%2Fcontroller-manager@1.27.4",
"pkg:k8s/k8s.io%2Fkube-proxy@1.27.4",
"pkg:k8s/k8s.io%2Fkube-scheduler@1.27.4"
]
},
{
"ref": "pkg:oci/coredns@sha256%3Aa0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e?repository_url=registry.k8s.io%2Fcoredns%2Fcoredns",
"dependsOn": []
},
{
"ref": "pkg:oci/etcd@sha256%3A51eae8381dcb1078289fa7b4f3df2630cdc18d09fb56f8e56b41c40e191d6c83?repository_url=registry.k8s.io%2Fetcd",
"dependsOn": []
},
{
"ref": "pkg:oci/kube-apiserver@sha256%3A697cd88d94f7f2ef42144cb3072b016dcb2e9251f0e7d41a7fede557e555452d?repository_url=registry.k8s.io%2Fkube-apiserver",
"dependsOn": []
},
{
"ref": "pkg:oci/kube-controller-manager@sha256%3A6286e500782ad6d0b37a1b8be57fc73f597dc931dfc73ff18ce534059803b265?repository_url=registry.k8s.io%2Fkube-controller-manager",
"dependsOn": []
},
{
"ref": "pkg:oci/kube-proxy@sha256%3A4bcb707da9898d2625f5d4edc6d0c96519a24f16db914fc673aa8f97e41dbabf?repository_url=registry.k8s.io%2Fkube-proxy",
"dependsOn": []
},
{
"ref": "pkg:oci/kube-scheduler@sha256%3A5897d7a97d23dce25cbf36fcd6e919180a8ef904bf5156583ffdb6a733ab04af?repository_url=registry.k8s.io%2Fkube-scheduler",
"dependsOn": []
}
],
"vulnerabilities": []
}

View File

@@ -128,7 +128,7 @@ func (c *BOM) parseSBOM(bom *cdx.BOM) error {
if _, ok := seen[ref]; ok {
continue
}
if component.Type == cdx.ComponentTypeLibrary {
if component.Type == cdx.ComponentTypeLibrary || component.PackageURL != "" {
libComponents = append(libComponents, component)
}
@@ -194,13 +194,17 @@ func parsePkgs(components []cdx.Component, seen map[string]struct{}) ([]ftypes.P
var pkgs []ftypes.Package
for _, com := range components {
seen[com.BOMRef] = struct{}{}
_, _, pkg, err := toPackage(com)
if err != nil {
if errors.Is(err, ErrPURLEmpty) {
continue
}
pkgURL, pkg, err := toPackage(com)
if errors.Is(err, ErrPURLEmpty) {
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 {
continue
}
pkgs = append(pkgs, *pkg)
}
return pkgs, nil
@@ -276,21 +280,22 @@ func dependencyMap(deps *[]cdx.Dependency) map[string][]string {
}
func aggregatePkgs(libs []cdx.Component) ([]ftypes.PackageInfo, []ftypes.Application, error) {
osPkgMap := make(map[ftypes.OSType]ftypes.Packages)
osPkgMap := make(map[string]ftypes.Packages)
langPkgMap := make(map[ftypes.LangType]ftypes.Packages)
for _, lib := range libs {
isOSPkg, pkgType, pkg, err := toPackage(lib)
if err != nil {
if errors.Is(err, ErrPURLEmpty) {
continue
}
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)
}
if isOSPkg {
osPkgMap[pkgType] = append(osPkgMap[pkgType], *pkg)
} else {
langPkgMap[pkgType] = append(langPkgMap[pkgType], *pkg)
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)
}
}
@@ -332,14 +337,14 @@ func toApplication(component cdx.Component) *ftypes.Application {
}
}
func toPackage(component cdx.Component) (bool, ftypes.TargetType, *ftypes.Package, error) {
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 false, "", nil, ErrPURLEmpty
return nil, nil, ErrPURLEmpty
}
p, err := purl.FromString(component.PackageURL)
if err != nil {
return false, "", nil, xerrors.Errorf("failed to parse purl: %w", err)
return nil, nil, xerrors.Errorf("failed to parse purl: %w", err)
}
pkg := p.Package()
@@ -365,7 +370,7 @@ func toPackage(component cdx.Component) (bool, ftypes.TargetType, *ftypes.Packag
case PropertySrcEpoch:
pkg.SrcEpoch, err = strconv.Atoi(value)
if err != nil {
return false, "", nil, xerrors.Errorf("failed to parse source epoch: %w", err)
return nil, nil, xerrors.Errorf("failed to parse source epoch: %w", err)
}
case PropertyModularitylabel:
pkg.Modularitylabel = value
@@ -376,8 +381,7 @@ func toPackage(component cdx.Component) (bool, ftypes.TargetType, *ftypes.Packag
}
}
isOSPkg := p.IsOSPkg()
if isOSPkg {
if p.Class() == types.ClassOSPkg {
// Fill source package information for components in third-party SBOMs .
if pkg.SrcName == "" {
pkg.SrcName = pkg.Name
@@ -393,7 +397,7 @@ func toPackage(component cdx.Component) (bool, ftypes.TargetType, *ftypes.Packag
}
}
return isOSPkg, p.LangType(), pkg, nil
return p, pkg, nil
}
func toTrivyCdxComponent(component cdx.Component) ftypes.Component {

View File

@@ -130,6 +130,72 @@ 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: "",
},
},
Applications: []ftypes.Application{
{
Type: ftypes.GoBinary,
Libraries: ftypes.Packages{
{
Name: "docker",
Version: "24.0.4",
Ref: "pkg:golang/docker@24.0.4",
},
},
},
{
Type: "golang",
FilePath: "node-core-components",
},
{
Type: ftypes.K8sUpstream,
Libraries: ftypes.Packages{
{
Name: "k8s.io/apiserver",
Version: "1.27.4",
Ref: "pkg:k8s/k8s.io%2Fapiserver@1.27.4",
},
{
Name: "k8s.io/controller-manager",
Version: "1.27.4",
Ref: "pkg:k8s/k8s.io%2Fcontroller-manager@1.27.4",
},
{
Name: "k8s.io/kube-proxy",
Version: "1.27.4",
Ref: "pkg:k8s/k8s.io%2Fkube-proxy@1.27.4",
},
{
Name: "k8s.io/kube-scheduler",
Version: "1.27.4",
Ref: "pkg:k8s/k8s.io%2Fkube-scheduler@1.27.4",
},
{
Name: "k8s.io/kubelet",
Version: "1.27.4",
Ref: "pkg:k8s/k8s.io%2Fkubelet@1.27.4",
},
{
Name: "k8s.io/kubernetes",
Version: "1.27.4",
Ref: "pkg:k8s/k8s.io%2Fkubernetes@1.27.4",
},
},
},
},
},
},
{
name: "happy path with infinity loop",
inputFile: "testdata/happy/infinite-loop-bom.json",

View File

@@ -177,10 +177,10 @@ func (s *SPDX) parsePackages(pkgs map[common.ElementID]*spdx.Package) error {
} else if err != nil {
return xerrors.Errorf("failed to parse package: %w", err)
}
switch pkgURL.Type {
case packageurl.TypeApk, packageurl.TypeDebian, packageurl.TypeRPM:
switch pkgURL.Class() {
case types.ClassOSPkg:
osPkgs = append(osPkgs, *pkg)
default:
case types.ClassLangPkg:
// Language-specific packages
pkgType := pkgURL.LangType()
app, ok := apps[pkgType]

View File

@@ -13,11 +13,12 @@ import (
var (
PkgTargets = map[ftypes.LangType]string{
ftypes.PythonPkg: "Python",
ftypes.CondaPkg: "Conda",
ftypes.GemSpec: "Ruby",
ftypes.NodePkg: "Node.js",
ftypes.Jar: "Java",
ftypes.PythonPkg: "Python",
ftypes.CondaPkg: "Conda",
ftypes.GemSpec: "Ruby",
ftypes.NodePkg: "Node.js",
ftypes.Jar: "Java",
ftypes.K8sUpstream: "Kubernetes",
}
)

View File

@@ -41,6 +41,7 @@ type Compliance = string
type Format string
const (
ClassUnknown ResultClass = "unknown"
ClassOSPkg ResultClass = "os-pkgs" // For detected packages and vulnerabilities in OS packages
ClassLangPkg ResultClass = "lang-pkgs" // For detected packages and vulnerabilities in language-specific packages
ClassConfig ResultClass = "config" // For detected misconfigurations