Files
trivy/pkg/k8s/scanner/scanner_test.go
Juan Ariza Toledano 1f0d6290c3 feat(vuln): include pkg identifier on detected vulnerabilities (#5439)
Signed-off-by: juan131 <jariza@vmware.com>
Signed-off-by: knqyf263 <knqyf263@gmail.com>
Co-authored-by: DmitriyLewen <dmitriy.lewen@smartforce.io>
Co-authored-by: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com>
Co-authored-by: Nikita Pivkin <nikita.pivkin@smartforce.io>
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2023-12-27 07:54:56 +00:00

555 lines
14 KiB
Go

package scanner
import (
"context"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"sort"
"testing"
cdx "github.com/CycloneDX/cyclonedx-go"
"github.com/package-url/packageurl-go"
"github.com/stretchr/testify/assert"
"github.com/aquasecurity/trivy-kubernetes/pkg/artifacts"
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: "test cluster info with resources",
clusterName: "test-cluster",
artifacts: []*artifacts.Artifact{
{
Namespace: "kube-system",
Kind: "Cluster",
Name: "k8s.io/kubernetes",
RawResource: map[string]interface{}{
"name": "k8s.io/kubernetes",
"version": "1.21.1",
"type": "ClusterInfo",
"Properties": map[string]string{
"Name": "kind-kind",
"Type": "cluster",
},
},
},
{
Namespace: "kube-system",
Kind: "ControlPlaneComponents",
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",
},
},
"Name": "k8s.io/apiserver",
"Version": "1.21.1",
},
},
{
Kind: "NodeComponents",
Name: "kind-control-plane",
RawResource: map[string]interface{}{
"ContainerRuntimeVersion": "containerd://1.5.2",
"Hostname": "kind-control-plane",
"KubeProxyVersion": "6.2.13-300.fc38.aarch64",
"KubeletVersion": "v1.21.1",
"NodeName": "kind-control-plane",
"OsImage": "Ubuntu 21.04",
"Properties": map[string]string{
"Architecture": "arm64",
"HostName": "kind-control-plane",
"KernelVersion": "6.2.15-300.fc38.aarch64",
"NodeRole": "master",
"OperatingSystem": "linux",
},
},
},
},
want: &core.Component{
Type: cdx.ComponentTypePlatform,
Name: "k8s.io/kubernetes",
Version: "1.21.1",
Properties: []core.Property{
{
Name: "Name",
Value: "kind-kind",
Namespace: k8sCoreComponentNamespace,
},
{
Name: "Type",
Value: "cluster",
Namespace: k8sCoreComponentNamespace,
},
},
PackageURL: &ftypes.PackageURL{
PackageURL: packageurl.PackageURL{
Type: purl.TypeK8s,
Name: "k8s.io/kubernetes",
Version: "1.21.1",
},
},
Components: []*core.Component{
{
Type: cdx.ComponentTypeApplication,
Name: "k8s.io/apiserver",
Version: "1.21.1",
PackageURL: &ftypes.PackageURL{
PackageURL: packageurl.PackageURL{
Type: purl.TypeK8s,
Name: "k8s.io/apiserver",
Version: "1.21.1",
},
},
Properties: []core.Property{},
Components: []*core.Component{
{
Type: cdx.ComponentTypeContainer,
Name: "k8s.gcr.io/kube-apiserver",
Version: "sha256:18e61c783b41758dd391ab901366ec3546b26fae00eef7e223d1f94da808e02f",
PackageURL: &ftypes.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",
},
},
},
},
},
{
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,
},
},
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: &ftypes.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: &ftypes.PackageURL{
PackageURL: packageurl.PackageURL{
Type: "golang",
Name: "github.com/containerd/containerd",
Version: "1.5.2",
Qualifiers: packageurl.Qualifiers{},
},
},
},
},
},
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.TODO()
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)
})
}
}
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
})
}
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)
}
return fComp
}
func TestTestOsNameVersion(t *testing.T) {
tests := []struct {
name string
nameVersion string
compName string
compVersion string
}{
{
name: "valid version",
nameVersion: "ubuntu 20.04",
compName: "ubuntu",
compVersion: "20.04",
},
{
name: "valid sem version",
nameVersion: "ubuntu 20.04.1",
compName: "ubuntu",
compVersion: "20.04.1",
},
{
name: "non valid version",
nameVersion: "ubuntu",
compName: "ubuntu",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
name, version := osNameVersion(tt.nameVersion)
assert.Equal(t, name, tt.compName)
assert.Equal(t, version, tt.compVersion)
})
}
}
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())
})
}
}
func TestK8sNamespace(t *testing.T) {
tests := []struct {
name string
compVersion string
nodeName string
want string
}{
{
name: "native k8s component",
compVersion: "1.24.10",
nodeName: "kind-kind",
want: "kubernetes",
},
{
name: "GKE",
compVersion: "1.24.10-gke.2300",
nodeName: "gke-gke1796-default-pool-768cb718-sk1d",
want: "gke",
},
{
name: "AKS",
compVersion: "1.24.10-hotfix.20221110",
nodeName: "aks-default-23814474-vmss000000",
want: "aks",
},
{
name: "EKS",
compVersion: "1.23.17-eks-8ccc7ba",
nodeName: "eks-vmss000000",
want: "eks",
},
{
name: "Rancher",
compVersion: "1.24.11+rke2r1",
nodeName: "ip-10-0-5-23",
want: "rke",
},
{
name: "OCP",
compVersion: "1.26.7+c7ee51f",
nodeName: "ocp413vpool14000-p8vnm-master-2",
want: "ocp",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := k8sNamespace(tt.compVersion, tt.nodeName)
assert.Equal(t, tt.want, got)
})
}
}
func TestRuntimeVersion(t *testing.T) {
tests := []struct {
name string
runtimeVersion string
wantName string
wantVersion string
}{
{
name: "containerd",
runtimeVersion: "containerd://1.5.2",
wantName: "github.com/containerd/containerd",
wantVersion: "1.5.2",
},
{
name: "cri-o",
runtimeVersion: "cri-o://1.5.2",
wantName: "github.com/cri-o/cri-o",
wantVersion: "1.5.2",
},
{
name: "cri-dockerd",
runtimeVersion: "cri-dockerd://1.5.2",
wantName: "github.com/Mirantis/cri-dockerd",
wantVersion: "1.5.2",
},
{
name: "na runtime",
runtimeVersion: "cri:1.5.2",
wantName: "",
wantVersion: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotName, gotVersion := runtimeNameVersion(tt.runtimeVersion)
assert.Equal(t, tt.wantName, gotName)
assert.Equal(t, tt.wantVersion, gotVersion)
})
}
}
func TestFindNodeName(t *testing.T) {
tests := []struct {
name string
artifacts []*artifacts.Artifact
want string
}{
{
name: "find node name",
artifacts: []*artifacts.Artifact{
{
Namespace: "kube-system",
Kind: "Cluster",
Name: "k8s.io/kubernetes",
RawResource: map[string]interface{}{},
},
{
Namespace: "kube-system",
Kind: "ControlPlaneComponents",
Name: "k8s.io/apiserver",
RawResource: map[string]interface{}{},
},
{
Kind: "NodeComponents",
Name: "kind-control-plane",
RawResource: map[string]interface{}{},
},
},
want: "kind-control-plane",
},
{
name: "didn't find node name",
artifacts: []*artifacts.Artifact{
{
Namespace: "kube-system",
Kind: "Cluster",
Name: "k8s.io/kubernetes",
RawResource: map[string]interface{}{},
},
{
Namespace: "kube-system",
Kind: "ControlPlaneComponents",
Name: "k8s.io/apiserver",
RawResource: map[string]interface{}{},
},
},
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := findNodeName(tt.artifacts)
assert.Equal(t, tt.want, got)
})
}
}