diff --git a/pkg/conthandler/container_main_handler.go b/pkg/conthandler/container_main_handler.go index 43b7a858eb..522ab321d3 100644 --- a/pkg/conthandler/container_main_handler.go +++ b/pkg/conthandler/container_main_handler.go @@ -86,7 +86,7 @@ func (ch *ContainerHandler) afterTimerActions() error { fileList := containerData.containerAggregator.GetContainerRealtimeFileList() if err = <-containerData.syncChannel[StepGetSBOM]; err != nil { - logger.L().Ctx(context.GetBackgroundContext()).Warning("failed to get SBOM", []helpers.IDetails{helpers.String("container ID", afterTimerActionsData.containerID), helpers.String("container name", containerData.event.GetContainerName()), helpers.String("k8s resource ", containerData.event.GetK8SWorkloadID()), helpers.Error(err)}...) + logger.L().Ctx(context.GetBackgroundContext()).Debug("failed to get SBOM", []helpers.IDetails{helpers.String("container ID", afterTimerActionsData.containerID), helpers.String("container name", containerData.event.GetContainerName()), helpers.String("k8s resource ", containerData.event.GetK8SWorkloadID()), helpers.Error(err)}...) continue } if err = containerData.sbomClient.FilterSBOM(fileList); err != nil { @@ -133,6 +133,11 @@ func createTicker() *time.Ticker { return time.NewTicker(config.GetConfigurationConfigContext().GetUpdateDataPeriod()) } +func (ch *ContainerHandler) deleteResources(watchedContainer watchedContainerData, contEvent v1.ContainerEventData) { + watchedContainer.sbomClient.CleanResources() + ch.watchedContainers.Delete(contEvent.GetContainerID()) +} + func (ch *ContainerHandler) startRelevancyProcess(contEvent v1.ContainerEventData) { containerDataInterface, exist := ch.watchedContainers.Load(contEvent.GetContainerID()) if !exist { @@ -158,10 +163,10 @@ func (ch *ContainerHandler) startRelevancyProcess(contEvent v1.ContainerEventDat if err != nil { logger.L().Ctx(context.GetBackgroundContext()).Warning("we have failed to stop to aggregate data", helpers.String("container ID", contEvent.GetContainerID()), helpers.String("container name", contEvent.GetContainerName()), helpers.String("k8s resources", contEvent.GetK8SWorkloadID())) } - ch.watchedContainers.Delete(contEvent.GetContainerID()) break } } + ch.deleteResources(watchedContainer, contEvent) } func getShortContainerID(containerID string) string { diff --git a/pkg/sbom/sbom.go b/pkg/sbom/sbom.go index 3cd0574db9..a0dff22d6e 100644 --- a/pkg/sbom/sbom.go +++ b/pkg/sbom/sbom.go @@ -14,7 +14,7 @@ const ( type SBOMStructure struct { storageClient SBOMStorageClient - SBOMData SBOMFormat + SBOMData v1.SBOMFormat firstReport bool imageID string wlid string @@ -37,7 +37,7 @@ func CreateSBOMStorageClient(sc storageclient.StorageClient, wlid, imageID strin storageClient: SBOMStorageClient{ client: sc, }, - SBOMData: v1.CreateSBOMDataSPDXVersionV040(), + SBOMData: v1.CreateSBOMDataSPDXVersionV040(instanceID), firstReport: true, instanceID: instanceID, wlid: wlid, @@ -89,6 +89,10 @@ func (sc *SBOMStructure) StoreFilterSBOM(instanceID string) error { return errorsOfSBOM[DataAlreadyExist] } +func (sc *SBOMStructure) CleanResources() { + sc.SBOMData.CleanResources() +} + func IsAlreadyExist() error { return errorsOfSBOM[DataAlreadyExist] } diff --git a/pkg/sbom/sbom_interface.go b/pkg/sbom/sbom_interface.go index 3b47098ad7..f7e242b847 100644 --- a/pkg/sbom/sbom_interface.go +++ b/pkg/sbom/sbom_interface.go @@ -4,4 +4,5 @@ type SBOMClient interface { GetSBOM(imageID string) error FilterSBOM(sbomFileRelevantMap map[string]bool) error StoreFilterSBOM(instanceID string) error + CleanResources() } diff --git a/pkg/sbom/sbom_test.go b/pkg/sbom/sbom_test.go index ab5e1c5b09..4fb8b8dcb1 100644 --- a/pkg/sbom/sbom_test.go +++ b/pkg/sbom/sbom_test.go @@ -15,7 +15,7 @@ func TestGetSBOM(t *testing.T) { SBOMClient := CreateSBOMStorageClient(storageclient.CreateSBOMStorageHttpClientMock(), "", "", &instanceidhandler.InstanceID{}) err := SBOMClient.GetSBOM(storageclient.NGINX) if err != nil { - t.Fatalf("fail to get sbom") + t.Fatalf("fail to get sbom, %v", err) } } @@ -24,13 +24,13 @@ func TestFilterSBOM(t *testing.T) { SBOMClient := CreateSBOMStorageClient(storageclient.CreateSBOMStorageHttpClientMock(), "", "", &instanceidhandler.InstanceID{}) err := SBOMClient.GetSBOM(storageclient.NGINX) if err != nil { - t.Fatalf("fail to get sbom") + t.Fatalf("fail to get sbom, %v", err) } err = SBOMClient.FilterSBOM(map[string]bool{ "/usr/share/adduser/adduser.conf": true, }) if err != nil { - t.Fatalf("fail to filter sbom") + t.Fatalf("fail to filter sbom, %v", err) } } diff --git a/pkg/sbom/testdata/nginx-spdx-format-mock.json b/pkg/sbom/testdata/nginx-spdx-format-mock.json index 501e8c3928..deda3f62b4 100644 --- a/pkg/sbom/testdata/nginx-spdx-format-mock.json +++ b/pkg/sbom/testdata/nginx-spdx-format-mock.json @@ -160,6 +160,11 @@ "spdxElementId": "SPDXRef-Package-deb-adduser-3e9282034226b93f", "relatedSpdxElement": "SPDXRef-e97fef92a7d904e0", "relationshipType": "CONTAINS" + }, + { + "relatedSpdxElement": "SPDXRef-DOCUMENT", + "relationshipType": "DESCRIBES", + "spdxElementId": "SPDXRef-DOCUMENT" } ] } diff --git a/pkg/sbom/testdata/not-spdx-format.json b/pkg/sbom/testdata/not-spdx-format.json new file mode 100644 index 0000000000..41dbe55f6d --- /dev/null +++ b/pkg/sbom/testdata/not-spdx-format.json @@ -0,0 +1,3 @@ +{ + "aaa": 12345 +} \ No newline at end of file diff --git a/pkg/sbom/sbom_format_interface.go b/pkg/sbom/v1/sbom_format_interface.go similarity index 96% rename from pkg/sbom/sbom_format_interface.go rename to pkg/sbom/v1/sbom_format_interface.go index 0c43becf11..f31981da99 100644 --- a/pkg/sbom/sbom_format_interface.go +++ b/pkg/sbom/v1/sbom_format_interface.go @@ -11,4 +11,5 @@ type SBOMFormat interface { AddResourceVersionIfNeeded(string) StoreFilteredSBOMName(string) StoreMetadata(wlidData, imageID string, instanceID instanceidhandler.IInstanceID) + CleanResources() } diff --git a/pkg/sbom/v1/sbom_spdx_storage_format.go b/pkg/sbom/v1/sbom_spdx_storage_format.go index 24b070155c..3131043cc1 100644 --- a/pkg/sbom/v1/sbom_spdx_storage_format.go +++ b/pkg/sbom/v1/sbom_spdx_storage_format.go @@ -1,7 +1,11 @@ package sbom import ( + "encoding/json" "fmt" + "io/ioutil" + "os" + "sniffer/pkg/context" "sync" "github.com/armosec/utils-k8s-go/wlid" @@ -22,39 +26,84 @@ const ( KubescapeOrganizationName = "Kubescape" KubescapeNodeAgentName = "KubescapeNodeAgent" RelationshipContainType = "CONTAINS" + directorySBOM = "SBOM" ) +var spdxDataDirPath string + type SBOMData struct { - spdxData spdxv1beta1.SBOMSPDXv2p3 + spdxDataPath string filteredSpdxData spdxv1beta1.SBOMSPDXv2p3Filtered relevantRealtimeFilesBySPDXIdentifier sync.Map newRelevantData bool alreadyExistSBOM bool + instanceID instanceidhandler.IInstanceID +} + +func createSBOMDir() { + wd, err := os.Getwd() + if err != nil { + logger.L().Ctx(context.GetBackgroundContext()).Fatal("failed to get working directory", helpers.Error(err)) + } + spdxDataDirPath = fmt.Sprintf("%s/%s", wd, directorySBOM) + err = os.MkdirAll(spdxDataDirPath, os.ModeDir|os.ModePerm) + if err != nil { + logger.L().Ctx(context.GetBackgroundContext()).Fatal("failed to create directory for SBOM resources", helpers.String("directory path", spdxDataDirPath), helpers.Error(err)) + } +} + +func init() { + createSBOMDir() } -func CreateSBOMDataSPDXVersionV040() *SBOMData { +func CreateSBOMDataSPDXVersionV040(instanceID instanceidhandler.IInstanceID) SBOMFormat { + return &SBOMData{ + spdxDataPath: fmt.Sprintf("%s/%s", spdxDataDirPath, instanceID.GetHashed()), filteredSpdxData: spdxv1beta1.SBOMSPDXv2p3Filtered{}, relevantRealtimeFilesBySPDXIdentifier: sync.Map{}, newRelevantData: false, alreadyExistSBOM: false, + instanceID: instanceID, } } +func (sbom *SBOMData) saveSBOM(spdxData *spdxv1beta1.SBOMSPDXv2p3) error { + f, err := os.Create(sbom.spdxDataPath) + if err != nil { + return err + } + defer f.Close() + + data, err := json.Marshal(spdxData) + if err != nil { + return err + } + _, err = f.Write(data) + if err != nil { + return err + } + return nil +} + func (sbom *SBOMData) StoreSBOM(sbomData any) error { spdxData, ok := sbomData.(*spdxv1beta1.SBOMSPDXv2p3) if !ok { return fmt.Errorf("storage format: StoreSBOM: SBOM data format is not supported") } - sbom.spdxData = *spdxData - for i := range sbom.spdxData.Spec.SPDX.Files { - sbom.relevantRealtimeFilesBySPDXIdentifier.Store(spdxv1beta1.ElementID(sbom.spdxData.Spec.SPDX.Files[i].FileSPDXIdentifier), false) + err := sbom.saveSBOM(spdxData) + if err != nil { + return err } - sbom.filteredSpdxData.Spec = sbom.spdxData.Spec - sbom.filteredSpdxData.Status = sbom.spdxData.Status - sbom.spdxData.Spec.SPDX.CreationInfo.Creators = append(sbom.spdxData.Spec.SPDX.CreationInfo.Creators, []spdxv1beta1.Creator{ + for i := range spdxData.Spec.SPDX.Files { + sbom.relevantRealtimeFilesBySPDXIdentifier.Store(spdxv1beta1.ElementID(spdxData.Spec.SPDX.Files[i].FileSPDXIdentifier), false) + } + + sbom.filteredSpdxData.Spec = spdxData.Spec + sbom.filteredSpdxData.Status = spdxData.Status + sbom.filteredSpdxData.Spec.SPDX.CreationInfo.Creators = append(sbom.filteredSpdxData.Spec.SPDX.CreationInfo.Creators, []spdxv1beta1.Creator{ { CreatorType: Organization, Creator: KubescapeOrganizationName, @@ -74,40 +123,67 @@ func (sbom *SBOMData) StoreSBOM(sbomData any) error { return nil } +func (sbom *SBOMData) getSBOMDataSPDXFormat() (*spdxv1beta1.SBOMSPDXv2p3, error) { + file, err := os.Open(sbom.spdxDataPath) + if err != nil { + return nil, err + } + defer file.Close() + + bytes, err := ioutil.ReadAll(file) + if err != nil { + return nil, err + } + + spdxData := spdxv1beta1.SBOMSPDXv2p3{} + err = json.Unmarshal(bytes, &spdxData) + if err != nil { + return nil, err + } + + return &spdxData, nil +} + func (sbom *SBOMData) FilterSBOM(sbomFileRelevantMap map[string]bool) error { sbom.newRelevantData = false + + spdxData, err := sbom.getSBOMDataSPDXFormat() + if err != nil { + return err + } + //filter relevant file list - for i := range sbom.spdxData.Spec.SPDX.Files { - if exist := sbomFileRelevantMap[sbom.spdxData.Spec.SPDX.Files[i].FileName]; exist { - if data, _ := sbom.relevantRealtimeFilesBySPDXIdentifier.Load(spdxv1beta1.ElementID(sbom.spdxData.Spec.SPDX.Files[i].FileSPDXIdentifier)); data != nil && !data.(bool) { - sbom.filteredSpdxData.Spec.SPDX.Files = append(sbom.filteredSpdxData.Spec.SPDX.Files, sbom.spdxData.Spec.SPDX.Files[i]) - sbom.relevantRealtimeFilesBySPDXIdentifier.Store(spdxv1beta1.ElementID(sbom.spdxData.Spec.SPDX.Files[i].FileSPDXIdentifier), true) + for i := range spdxData.Spec.SPDX.Files { + if exist := sbomFileRelevantMap[spdxData.Spec.SPDX.Files[i].FileName]; exist { + if data, _ := sbom.relevantRealtimeFilesBySPDXIdentifier.Load(spdxv1beta1.ElementID(spdxData.Spec.SPDX.Files[i].FileSPDXIdentifier)); data != nil && !data.(bool) { + sbom.filteredSpdxData.Spec.SPDX.Files = append(sbom.filteredSpdxData.Spec.SPDX.Files, spdxData.Spec.SPDX.Files[i]) + sbom.relevantRealtimeFilesBySPDXIdentifier.Store(spdxv1beta1.ElementID(spdxData.Spec.SPDX.Files[i].FileSPDXIdentifier), true) sbom.newRelevantData = true } } } //filter relationship list - for i := range sbom.spdxData.Spec.SPDX.Relationships { - switch sbom.spdxData.Spec.SPDX.Relationships[i].Relationship { + for i := range spdxData.Spec.SPDX.Relationships { + switch spdxData.Spec.SPDX.Relationships[i].Relationship { case RelationshipContainType: - if data, _ := sbom.relevantRealtimeFilesBySPDXIdentifier.Load(spdxv1beta1.ElementID(sbom.spdxData.Spec.SPDX.Relationships[i].RefB.ElementRefID)); data != nil && data.(bool) { - sbom.filteredSpdxData.Spec.SPDX.Relationships = append(sbom.filteredSpdxData.Spec.SPDX.Relationships, sbom.spdxData.Spec.SPDX.Relationships[i]) + if data, _ := sbom.relevantRealtimeFilesBySPDXIdentifier.Load(spdxv1beta1.ElementID(spdxData.Spec.SPDX.Relationships[i].RefB.ElementRefID)); data != nil && data.(bool) { + sbom.filteredSpdxData.Spec.SPDX.Relationships = append(sbom.filteredSpdxData.Spec.SPDX.Relationships, spdxData.Spec.SPDX.Relationships[i]) } default: - sbom.filteredSpdxData.Spec.SPDX.Relationships = append(sbom.filteredSpdxData.Spec.SPDX.Relationships, sbom.spdxData.Spec.SPDX.Relationships[i]) + sbom.filteredSpdxData.Spec.SPDX.Relationships = append(sbom.filteredSpdxData.Spec.SPDX.Relationships, spdxData.Spec.SPDX.Relationships[i]) } } //filter relevant package list - for i := range sbom.spdxData.Spec.SPDX.Packages { + for i := range spdxData.Spec.SPDX.Packages { relevantPackageMap := make(map[spdxv1beta1.DocElementID]bool) for j := range sbom.filteredSpdxData.Spec.SPDX.Relationships { switch sbom.filteredSpdxData.Spec.SPDX.Relationships[j].Relationship { case RelationshipContainType: if alreadyExist := relevantPackageMap[sbom.filteredSpdxData.Spec.SPDX.Relationships[j].RefA]; !alreadyExist { - if spdxv1beta1.ElementID(sbom.filteredSpdxData.Spec.SPDX.Relationships[j].RefA.ElementRefID) == sbom.spdxData.Spec.SPDX.Packages[i].PackageSPDXIdentifier { - sbom.filteredSpdxData.Spec.SPDX.Packages = append(sbom.filteredSpdxData.Spec.SPDX.Packages, sbom.spdxData.Spec.SPDX.Packages[i]) + if spdxv1beta1.ElementID(sbom.filteredSpdxData.Spec.SPDX.Relationships[j].RefA.ElementRefID) == spdxData.Spec.SPDX.Packages[i].PackageSPDXIdentifier { + sbom.filteredSpdxData.Spec.SPDX.Packages = append(sbom.filteredSpdxData.Spec.SPDX.Packages, spdxData.Spec.SPDX.Packages[i]) } } } @@ -176,3 +252,10 @@ func (sc *SBOMData) AddResourceVersionIfNeeded(resourceVersion string) { sc.filteredSpdxData.SetResourceVersion(resourceVersion) } } + +func (sc *SBOMData) CleanResources() { + err := os.Remove(sc.spdxDataPath) + if err != nil { + logger.L().Debug("fail to remove file", helpers.String("file name", sc.spdxDataPath), helpers.Error(err)) + } +} diff --git a/pkg/sbom/v1/sbom_spdx_storage_format_test.go b/pkg/sbom/v1/sbom_spdx_storage_format_test.go new file mode 100644 index 0000000000..fbb5d6ba7d --- /dev/null +++ b/pkg/sbom/v1/sbom_spdx_storage_format_test.go @@ -0,0 +1,469 @@ +package sbom + +import ( + "encoding/json" + "os" + "path" + "sniffer/pkg/utils" + "testing" + + instanceidhandlerV1 "github.com/kubescape/k8s-interface/instanceidhandler/v1" + spdxv1beta1 "github.com/kubescape/storage/pkg/apis/softwarecomposition/v1beta1" +) + +const ( + instnaceIDMock = "apiVersion-v1/namespace-aaa/kind-deployment/name-redis/containerName-redis" +) + +type notSPDXFormatSBOMData struct { + Data int `json:"aaa"` +} + +func TestStoreLabels(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to store SBOM file, err: %v", err) + } + err = SBOMData.FilterSBOM((map[string]bool{ + "/usr/share/adduser/adduser.conf": true, + })) + if err != nil { + t.Fatalf("fail to filter SBOM, err: %v", err) + } + SBOMData.StoreMetadata("wlid://cluster-test/namespace-aaa/deplo#*?yment/redis", "e41ced4a64bd065a1a8b79dbc5832b744a3ad82e7fcbe9fb2ebdd1267f972775", instanceID) + for i := range SBOMData.filteredSpdxData.Labels { + switch i { + case instanceidhandlerV1.NamespaceMetadataKey: + if SBOMData.filteredSpdxData.Labels[i] != "aaa" { + t.Fatalf("label key %s should be v1 not %s", i, SBOMData.filteredSpdxData.Labels[i]) + } + case instanceidhandlerV1.NameMetadataKey: + if SBOMData.filteredSpdxData.Labels[i] != "redis" { + t.Fatalf("label key %s should be v1 not %s", i, SBOMData.filteredSpdxData.Labels[i]) + } + case instanceidhandlerV1.ContainerNameMetadataKey: + if SBOMData.filteredSpdxData.Labels[i] != "redis" { + t.Fatalf("label key %s should be v1 not %s", i, SBOMData.filteredSpdxData.Labels[i]) + } + } + } +} + +func TestGetSBOMData(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to store SBOM file, err: %v", err) + } + _, err = SBOMData.getSBOMDataSPDXFormat() + if err != nil { + t.Fatalf("fail to get SBOM, err: %v", err) + } + SBOMData.spdxDataPath = "123" + err = SBOMData.FilterSBOM(map[string]bool{ + "/usr/share/adduser/adduser.conf": true, + }) + if err == nil { + t.Fatalf("FilterSBOM should failed") + } + +} + +func TestSaveSBOM(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.saveSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to save SBOM file, err: %v", err) + } + SBOMData.spdxDataPath = "/proc/1/blabla" + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err == nil { + t.Fatalf("StoreSBOM should fail") + } + +} + +func TestCreateSBOMDataSPDXVersionV040(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + expectedPath := utils.CurrentDir() + "/" + directorySBOM + "/" + instanceID.GetHashed() + if SBOMData.spdxDataPath != expectedPath { + t.Fatalf("fail to create SBOMData, expected path %s get path %s", expectedPath, SBOMData.spdxDataPath) + } + +} + +func TestCreateSBOMDir(t *testing.T) { + createSBOMDir() + _, err := os.Stat(utils.CurrentDir() + "/" + directorySBOM) + if os.IsNotExist(err) { + t.Fatalf("fail to create SBOM directory with err %v", err) + } +} + +func TestGetFilterSBOMData(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to store SBOM file, err: %v", err) + } + if SBOMData.GetFilterSBOMData() != &SBOMData.filteredSpdxData { + t.Fatalf("fail to get SBOM filtered data") + } +} + +func TestStoreSBOM(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to store SBOM file, err: %v", err) + } + + var notSPDXFormatSBOMDataMock notSPDXFormatSBOMData + nginxSBOMPath = path.Join(utils.CurrentDir(), "..", "testdata", "not-spdx-format.json") + bytes, err = os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, ¬SPDXFormatSBOMDataMock) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(¬SPDXFormatSBOMDataMock) + if err == nil { + t.Fatalf("StoreSBOM should fail") + } +} + +func TestFilterSBOM(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to store SBOM file, err: %v", err) + } + err = SBOMData.FilterSBOM((map[string]bool{ + "/usr/share/adduser/adduser.conf": true, + })) + if err != nil { + t.Fatalf("fail to filter SBOM, err: %v", err) + } + +} + +func TestIsNewRelevantSBOMDataExist(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to store SBOM file, err: %v", err) + } + err = SBOMData.FilterSBOM((map[string]bool{ + "/usr/share/adduser/adduser.conf": true, + })) + if err != nil { + t.Fatalf("fail to filter SBOM, err: %v", err) + } + if SBOMData.IsNewRelevantSBOMDataExist() != true { + t.Fatalf("IsNewRelevantSBOMDataExist should return true, not false") + } +} + +func TestIsSBOMAlreadyExist(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to store SBOM file, err: %v", err) + } + err = SBOMData.FilterSBOM((map[string]bool{ + "/usr/share/adduser/adduser.conf": true, + })) + if err != nil { + t.Fatalf("fail to filter SBOM, err: %v", err) + } + if SBOMData.IsSBOMAlreadyExist() != true { + t.Fatalf("IsSBOMAlreadyExist should return true, not false") + } +} + +func TestAddResourceVersionIfNeeded(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to store SBOM file, err: %v", err) + } + err = SBOMData.FilterSBOM((map[string]bool{ + "/usr/share/adduser/adduser.conf": true, + })) + if err != nil { + t.Fatalf("fail to filter SBOM, err: %v", err) + } + SBOMData.AddResourceVersionIfNeeded("123") + if SBOMData.filteredSpdxData.ObjectMeta.ResourceVersion != "123" { + t.Fatalf("ResourceVersion should be 123 not %s", SBOMData.filteredSpdxData.ObjectMeta.ResourceVersion) + } +} + +func TestStoreFilteredSBOMName(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to store SBOM file, err: %v", err) + } + err = SBOMData.FilterSBOM((map[string]bool{ + "/usr/share/adduser/adduser.conf": true, + })) + if err != nil { + t.Fatalf("fail to filter SBOM, err: %v", err) + } + SBOMData.StoreFilteredSBOMName(instanceID.GetHashed()) + if SBOMData.filteredSpdxData.GetName() != instanceID.GetHashed() { + t.Fatalf("filteredSpdxData name should be %s not %s", instanceID.GetHashed(), SBOMData.filteredSpdxData.GetName()) + } + +} + +func TestStoreMetadata(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to store SBOM file, err: %v", err) + } + err = SBOMData.FilterSBOM((map[string]bool{ + "/usr/share/adduser/adduser.conf": true, + })) + if err != nil { + t.Fatalf("fail to filter SBOM, err: %v", err) + } + SBOMData.StoreMetadata("wlid://cluster-test/namespace-aaa/deployment/redis", "e41ced4a64bd065a1a8b79dbc5832b744a3ad82e7fcbe9fb2ebdd1267f972775", instanceID) + for i := range SBOMData.filteredSpdxData.Labels { + switch i { + case instanceidhandlerV1.NamespaceMetadataKey: + if SBOMData.filteredSpdxData.Labels[i] != "aaa" { + t.Fatalf("label key %s should be v1 not %s", i, SBOMData.filteredSpdxData.Labels[i]) + } + case instanceidhandlerV1.KindMetadataKey: + if SBOMData.filteredSpdxData.Labels[i] != "Deployment" { + t.Fatalf("label key %s should be Deployment not %s", i, SBOMData.filteredSpdxData.Labels[i]) + } + case instanceidhandlerV1.NameMetadataKey: + if SBOMData.filteredSpdxData.Labels[i] != "redis" { + t.Fatalf("label key %s should be v1 not %s", i, SBOMData.filteredSpdxData.Labels[i]) + } + case instanceidhandlerV1.ContainerNameMetadataKey: + if SBOMData.filteredSpdxData.Labels[i] != "redis" { + t.Fatalf("label key %s should be v1 not %s", i, SBOMData.filteredSpdxData.Labels[i]) + } + } + } +} + +func TestCleanResources(t *testing.T) { + instanceID, err := instanceidhandlerV1.GenerateInstanceIDFromString(instnaceIDMock) + if err != nil { + t.Fatalf("fail to create instance ID, err: %v", err) + } + data := CreateSBOMDataSPDXVersionV040(instanceID) + SBOMData := data.(*SBOMData) + + var SBOMDataMock spdxv1beta1.SBOMSPDXv2p3 + nginxSBOMPath := path.Join(utils.CurrentDir(), "..", "testdata", "nginx-spdx-format-mock.json") + bytes, err := os.ReadFile(nginxSBOMPath) + if err != nil { + t.Fatalf("fail to read SBOM file, err: %v", err) + } + err = json.Unmarshal(bytes, &SBOMDataMock.Spec.SPDX) + if err != nil { + t.Fatalf("fail to unmarshal SBOM file, err: %v", err) + } + err = SBOMData.StoreSBOM(&SBOMDataMock) + if err != nil { + t.Fatalf("fail to store SBOM file, err: %v", err) + } + SBOMData.CleanResources() + _, err = os.Stat(utils.CurrentDir() + "/" + directorySBOM + "/" + instanceID.GetHashed()) + if !os.IsNotExist(err) { + t.Fatalf("SBOM file of %s should be deleted", instanceID.GetHashed()) + } + SBOMData.CleanResources() +}