From a3b48ad8f4a1082662c13510dadc25c5cd53e70e Mon Sep 17 00:00:00 2001 From: Francisco Javier Moreno Date: Wed, 20 Nov 2024 18:30:52 +0100 Subject: [PATCH 1/7] rhwa: Run FAR operator dast scan from pod --- images/rhwa/dast/Dockerfile | 7 --- images/rhwa/dast/config/rapidastConfig.yaml | 46 -------------- tests/rhwa/far-operator/far_suite_test.go | 23 ++++++- tests/rhwa/far-operator/tests/far.go | 66 +++++++++++++++++++++ tests/rhwa/internal/rhwaparams/const.go | 4 ++ 5 files changed, 92 insertions(+), 54 deletions(-) delete mode 100644 images/rhwa/dast/Dockerfile delete mode 100644 images/rhwa/dast/config/rapidastConfig.yaml diff --git a/images/rhwa/dast/Dockerfile b/images/rhwa/dast/Dockerfile deleted file mode 100644 index 8022b7663..000000000 --- a/images/rhwa/dast/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -# Use the official RAPIDast image as the base -FROM quay.io/redhatproductsecurity/rapidast:2.8.0 - -# Set working directory to the RAPIDast installation -WORKDIR /opt/rapidast - -COPY ./config/rapidastConfig.yaml ./config/ diff --git a/images/rhwa/dast/config/rapidastConfig.yaml b/images/rhwa/dast/config/rapidastConfig.yaml deleted file mode 100644 index a9e4a3639..000000000 --- a/images/rhwa/dast/config/rapidastConfig.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# This is a configuration template file to perform scans using user-defined container images or scripts -# -# Author: Red Hat Product Security -config: - # WARNING: `configVersion` indicates the schema version of the config file. - # This value tells RapiDAST what schema should be used to read this configuration. - # Therefore you should only change it if you update the configuration to a newer schema - # It is intended to keep backward compatibility (newer RapiDAST running an older config) - configVersion: 5 -# `application` contains data related to the application, not to the scans. -application: - shortName: "trivy" -# `general` is a section that will be applied to all scanners. -general: - container: - # This configures what technology is to be used for RapiDAST to run each scanner. - # Currently supported: `podman` and `none` - # none: Default. RapiDAST runs each scanner in the same host or inside the RapiDAST image container - # podman: RapiDAST orchestrates each scanner on its own using podman - # When undefined, relies on rapidast-defaults.yaml, or `none` if nothing is set - type: "none" -# `scanners' is a section that configures scanning options -scanners: - #generic_oobt: - # #results: "/opt/rapidast/results/oobtkube.sarif.json" # if None or "*stdout", the command's standard output is selected - # # toolDir: scanners/generic/tools - # inline: "python3 oobtkube.py -d 120 -p 12345 -i 10.74.16.40 -f /test/far_template.yaml -o oobtkube.sarif.json" - generic_trivy: - # results: - # An absolute path to file or directory where results are stored on the host. - # if it is "*stdout" or unspecified, the command's standard output will be selected - # When container.type is 'podman', this needs to be used along with the container.volumes configuration below - # If the result needs to be sent to DefectDojo, this must be a SARIF format file - #results: "/test/results/oobttest" - # Example: scan a k8s cluster for misconfiguration issue - # - kubeconfig file for the cluster is required - # - See https://aquasecurity.github.io/trivy/v0.49/docs/target/kubernetes/ for more information on 'trivy k8s' scan - # - scanners/generic/tools/convert_trivy_k8s_to_sarif.py converts the Trivy json result to the SARIF format - # 'inline' is used when container.type is not 'podman' - # 'toolDir' specifies the default directory where inline scripts are located - #toolDir: scanners/generic/tools - inline: "trivy k8s -n $NAMESPACE pod --severity=HIGH,CRITICAL --scanners=misconfig --report all --format json" - container: - parameters: - # Optional: list of expected return codes, anything else will be considered as an error. by default: [0] - validReturns: [ 0 ] diff --git a/tests/rhwa/far-operator/far_suite_test.go b/tests/rhwa/far-operator/far_suite_test.go index 7b2a0d443..6807040ed 100644 --- a/tests/rhwa/far-operator/far_suite_test.go +++ b/tests/rhwa/far-operator/far_suite_test.go @@ -6,14 +6,20 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/openshift-kni/eco-goinfra/pkg/namespace" "github.com/openshift-kni/eco-goinfra/pkg/reportxml" + "github.com/openshift-kni/eco-gotests/tests/internal/params" "github.com/openshift-kni/eco-gotests/tests/internal/reporter" "github.com/openshift-kni/eco-gotests/tests/rhwa/far-operator/internal/farparams" _ "github.com/openshift-kni/eco-gotests/tests/rhwa/far-operator/tests" . "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwainittools" + "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwaparams" ) -var _, currentFile, _, _ = runtime.Caller(0) +var ( + _, currentFile, _, _ = runtime.Caller(0) + testNS = namespace.NewBuilder(APIClient, rhwaparams.TestNamespaceName) +) func TestFAR(t *testing.T) { _, reporterConfig := GinkgoConfiguration() @@ -23,6 +29,15 @@ func TestFAR(t *testing.T) { RunSpecs(t, "FAR", Label(farparams.Labels...), reporterConfig) } +var _ = BeforeSuite(func() { + By("Creating test namespace with privileged labels") + for key, value := range params.PrivilegedNSLabels { + testNS.WithLabel(key, value) + } + _, err := testNS.Create() + Expect(err).ToNot(HaveOccurred(), "error to create test namespace") +}) + var _ = JustAfterEach(func() { reporter.ReportIfFailed( CurrentSpecReport(), currentFile, farparams.ReporterNamespacesToDump, farparams.ReporterCRDsToDump) @@ -32,3 +47,9 @@ var _ = ReportAfterSuite("", func(report Report) { reportxml.Create( report, RHWAConfig.GetReportPath(), RHWAConfig.TCPrefix) }) + +//var _ = AfterSuite(func() { +// By("Deleting test namespace") +// err := testNS.DeleteAndWait(rhwaparams.DefaultTimeout) +// Expect(err).ToNot(HaveOccurred(), "error to delete test namespace") +//}) diff --git a/tests/rhwa/far-operator/tests/far.go b/tests/rhwa/far-operator/tests/far.go index daddb1491..032e45939 100644 --- a/tests/rhwa/far-operator/tests/far.go +++ b/tests/rhwa/far-operator/tests/far.go @@ -2,18 +2,26 @@ package tests import ( "fmt" + "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/golang/glog" "github.com/openshift-kni/eco-goinfra/pkg/deployment" "github.com/openshift-kni/eco-goinfra/pkg/pod" + "github.com/openshift-kni/eco-goinfra/pkg/rbac" "github.com/openshift-kni/eco-goinfra/pkg/reportxml" + "github.com/openshift-kni/eco-goinfra/pkg/serviceaccount" + + "github.com/openshift-kni/eco-goinfra/pkg/nodes" + "github.com/openshift-kni/eco-gotests/tests/rhwa/far-operator/internal/farparams" . "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwainittools" "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwaparams" + v1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -44,4 +52,62 @@ var _ = Describe( ) Expect(err).ToNot(HaveOccurred(), "Pod is not ready") }) + + It("Verify FAR Operator passes trivy scan without vulnerabilities", reportxml.ID("76877"), func() { + + By("Retrieve list of nodes") + nodes, err := nodes.List(APIClient) + Expect(err).ToNot(HaveOccurred(), "Error getting nodes list") + + By("Create service account") + _, err = serviceaccount.NewBuilder(APIClient, "trivy-service-account", rhwaparams.TestNamespaceName).Create() + Expect(err).ToNot(HaveOccurred(), "Failed to create Service Account") + + _, err = rbac.NewClusterRoleBuilder(APIClient, "trivy-clusterrole", v1.PolicyRule{ + APIGroups: []string{ + "", + }, + Resources: []string{ + "pods", + }, + Verbs: []string{ + "get", + "list", + "watch", + }, + }).Create() + Expect(err).ToNot(HaveOccurred(), "Failed to create Cluster Role") + + _, err = rbac.NewClusterRoleBindingBuilder(APIClient, "trivy-clusterrole-binding", "trivy-clusterrole", v1.Subject{ + Kind: "ServiceAccount", + Name: "trivy-service-account", + Namespace: rhwaparams.TestNamespaceName, + }).Create() + Expect(err).ToNot(HaveOccurred(), "Failed to create Cluster Role Binding") + + dastTestPod := pod.NewBuilder( + APIClient, "rapidastclientpod", rhwaparams.TestNamespaceName, rhwaparams.TestContainerDast). + DefineOnNode(nodes[0].Object.Name). + WithTolerationToMaster(). + WithPrivilegedFlag() + + dastTestPod.Definition.Spec.ServiceAccountName = "trivy-service-account" + + By("Creating client test pod") + dastTestPod, err = pod.NewBuilder( + APIClient, "rapidastclientpod", rhwaparams.TestNamespaceName, rhwaparams.TestContainerDast). + DefineOnNode(nodes[0].Object.Name). + WithTolerationToMaster(). + WithPrivilegedFlag().CreateAndWaitUntilRunning(time.Minute) + Expect(err).ToNot(HaveOccurred(), "Failed to create client test pod") + + //TODO: check that the command can be actually executed by the pod. + command := []string{"bash", "-c", "export NAMESPACE=openshift-workload-availability rapidast.py --config ./config/rapidastConfig.yaml"} + output, err := dastTestPod.ExecCommand(command) + Expect(err).ToNot(HaveOccurred(), "Command failed") + + //TODO: The output of the rapidast command is a JSON that can be handled + glog.V(90).Infof("TRIVY command output: %s/n:", output.String()) + + }) }) diff --git a/tests/rhwa/internal/rhwaparams/const.go b/tests/rhwa/internal/rhwaparams/const.go index 6cd9a6abf..62495888d 100644 --- a/tests/rhwa/internal/rhwaparams/const.go +++ b/tests/rhwa/internal/rhwaparams/const.go @@ -11,4 +11,8 @@ const ( RhwaOperatorNs = "openshift-workload-availability" // DefaultTimeout represents the default timeout. DefaultTimeout = 300 * time.Second + // TestNamespaceName namespace where all dast test cases are performed + TestNamespaceName = "dast-tests" + + TestContainerDast = "quay.io/frmoreno/eco-dast:latest" ) From c9b7101b45450d8083d29d4bea3e80625e465789 Mon Sep 17 00:00:00 2001 From: Francisco Javier Moreno Date: Fri, 22 Nov 2024 13:00:59 +0100 Subject: [PATCH 2/7] Retrieve scan results --- images/rhwa/dast/Dockerfile | 8 +++ images/rhwa/dast/config/rapidastConfig.yaml | 46 ++++++++++++++ tests/rhwa/far-operator/far_suite_test.go | 10 ++-- tests/rhwa/far-operator/tests/far.go | 66 +++++++++++++++++---- 4 files changed, 113 insertions(+), 17 deletions(-) create mode 100644 images/rhwa/dast/Dockerfile create mode 100644 images/rhwa/dast/config/rapidastConfig.yaml diff --git a/images/rhwa/dast/Dockerfile b/images/rhwa/dast/Dockerfile new file mode 100644 index 000000000..0141ec353 --- /dev/null +++ b/images/rhwa/dast/Dockerfile @@ -0,0 +1,8 @@ +# Use the official RAPIDast image as the base +FROM quay.io/redhatproductsecurity/rapidast:2.8.0 + + +# Set working directory to the RAPIDast installation +WORKDIR /opt/rapidast + +COPY ./config/rapidastConfig.yaml ./config/ diff --git a/images/rhwa/dast/config/rapidastConfig.yaml b/images/rhwa/dast/config/rapidastConfig.yaml new file mode 100644 index 000000000..8ea4a5130 --- /dev/null +++ b/images/rhwa/dast/config/rapidastConfig.yaml @@ -0,0 +1,46 @@ +# This is a configuration template file to perform scans using user-defined container images or scripts +# +# Author: Red Hat Product Security +config: + # WARNING: `configVersion` indicates the schema version of the config file. + # This value tells RapiDAST what schema should be used to read this configuration. + # Therefore you should only change it if you update the configuration to a newer schema + # It is intended to keep backward compatibility (newer RapiDAST running an older config) + configVersion: 5 +# `application` contains data related to the application, not to the scans. +application: + shortName: "trivy" +# `general` is a section that will be applied to all scanners. +general: + container: + # This configures what technology is to be used for RapiDAST to run each scanner. + # Currently supported: `podman` and `none` + # none: Default. RapiDAST runs each scanner in the same host or inside the RapiDAST image container + # podman: RapiDAST orchestrates each scanner on its own using podman + # When undefined, relies on rapidast-defaults.yaml, or `none` if nothing is set + type: "none" +# `scanners' is a section that configures scanning options +scanners: + #generic_oobt: + # #results: "/opt/rapidast/results/oobtkube.sarif.json" # if None or "*stdout", the command's standard output is selected + # # toolDir: scanners/generic/tools + # inline: "python3 oobtkube.py -d 120 -p 12345 -i 10.74.16.40 -f /test/far_template.yaml -o oobtkube.sarif.json" + generic_trivy: + # results: + # An absolute path to file or directory where results are stored on the host. + # if it is "*stdout" or unspecified, the command's standard output will be selected + # When container.type is 'podman', this needs to be used along with the container.volumes configuration below + # If the result needs to be sent to DefectDojo, this must be a SARIF format file + #results: "/test/results/oobttest" + # Example: scan a k8s cluster for misconfiguration issue + # - kubeconfig file for the cluster is required + # - See https://aquasecurity.github.io/trivy/v0.49/docs/target/kubernetes/ for more information on 'trivy k8s' scan + # - scanners/generic/tools/convert_trivy_k8s_to_sarif.py converts the Trivy json result to the SARIF format + # 'inline' is used when container.type is not 'podman' + # 'toolDir' specifies the default directory where inline scripts are located + #toolDir: scanners/generic/tools + inline: "trivy k8s -n $NAMESPACE pod --severity=HIGH,CRITICAL --scanners=misconfig --report all --format json" + container: + parameters: + # Optional: list of expected return codes, anything else will be considered as an error. by default: [0] + validReturns: [ 0 ] \ No newline at end of file diff --git a/tests/rhwa/far-operator/far_suite_test.go b/tests/rhwa/far-operator/far_suite_test.go index 6807040ed..2b1c0db90 100644 --- a/tests/rhwa/far-operator/far_suite_test.go +++ b/tests/rhwa/far-operator/far_suite_test.go @@ -48,8 +48,8 @@ var _ = ReportAfterSuite("", func(report Report) { report, RHWAConfig.GetReportPath(), RHWAConfig.TCPrefix) }) -//var _ = AfterSuite(func() { -// By("Deleting test namespace") -// err := testNS.DeleteAndWait(rhwaparams.DefaultTimeout) -// Expect(err).ToNot(HaveOccurred(), "error to delete test namespace") -//}) +var _ = AfterSuite(func() { + By("Deleting test namespace") + err := testNS.DeleteAndWait(rhwaparams.DefaultTimeout) + Expect(err).ToNot(HaveOccurred(), "error to delete test namespace") +}) diff --git a/tests/rhwa/far-operator/tests/far.go b/tests/rhwa/far-operator/tests/far.go index 032e45939..e69d058a2 100644 --- a/tests/rhwa/far-operator/tests/far.go +++ b/tests/rhwa/far-operator/tests/far.go @@ -1,13 +1,13 @@ package tests import ( + "encoding/json" "fmt" "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/golang/glog" "github.com/openshift-kni/eco-goinfra/pkg/deployment" "github.com/openshift-kni/eco-goinfra/pkg/pod" "github.com/openshift-kni/eco-goinfra/pkg/rbac" @@ -25,6 +25,38 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +type DASTReport struct { + ClusterName string + Resources []struct { + Name string + Namespace string + Results []struct { + Target string + Class string + Type string + MisconfSummary struct { + Success int + Failures int + Exceptions int + } + Misconfigurations []struct { + Type string + ID string + AVDID string + Description string + Message string + Namespace string + Query string + Resolution string + Severity string + PrimaryURL string + References []string + Status string + } + } + } +} + var _ = Describe( "FAR Post Deployment tests", Ordered, @@ -83,31 +115,41 @@ var _ = Describe( Name: "trivy-service-account", Namespace: rhwaparams.TestNamespaceName, }).Create() - Expect(err).ToNot(HaveOccurred(), "Failed to create Cluster Role Binding") + Expect(err).ToNot(HaveOccurred(), "Failed to create ClusterRoleBinding") + By("Creating client test pod") dastTestPod := pod.NewBuilder( APIClient, "rapidastclientpod", rhwaparams.TestNamespaceName, rhwaparams.TestContainerDast). DefineOnNode(nodes[0].Object.Name). WithTolerationToMaster(). WithPrivilegedFlag() + Expect(err).ToNot(HaveOccurred(), "Failed to create client test pod") dastTestPod.Definition.Spec.ServiceAccountName = "trivy-service-account" By("Creating client test pod") - dastTestPod, err = pod.NewBuilder( - APIClient, "rapidastclientpod", rhwaparams.TestNamespaceName, rhwaparams.TestContainerDast). - DefineOnNode(nodes[0].Object.Name). - WithTolerationToMaster(). - WithPrivilegedFlag().CreateAndWaitUntilRunning(time.Minute) + _, err = dastTestPod.CreateAndWaitUntilRunning(time.Minute) Expect(err).ToNot(HaveOccurred(), "Failed to create client test pod") - //TODO: check that the command can be actually executed by the pod. - command := []string{"bash", "-c", "export NAMESPACE=openshift-workload-availability rapidast.py --config ./config/rapidastConfig.yaml"} + By("Running vulnerability scan") + command := []string{"bash", "-c", "NAMESPACE=openshift-workload-availability rapidast.py --config ./config/rapidastConfig.yaml 2> /dev/null"} output, err := dastTestPod.ExecCommand(command) Expect(err).ToNot(HaveOccurred(), "Command failed") - //TODO: The output of the rapidast command is a JSON that can be handled - glog.V(90).Infof("TRIVY command output: %s/n:", output.String()) - + By("Checking vulnerability scan results") + var parsableStruct DASTReport + err = json.Unmarshal(output.Bytes(), &parsableStruct) + Expect(err).ToNot(HaveOccurred()) + + var vulnerability_found bool = false + for _, resource := range parsableStruct.Resources { + for _, result := range resource.Results { + if result.MisconfSummary.Failures > 0 { + fmt.Printf("%d vulnerability(s) found in %s\n", result.MisconfSummary.Failures, resource.Name) + vulnerability_found = true + } + } + } + Expect(vulnerability_found).NotTo(BeTrue(), "Found vulnerability(s)") }) }) From 9b7e5797f6ac1a7e18f5c734da2373ed09794c8f Mon Sep 17 00:00:00 2001 From: Francisco Javier Moreno Date: Fri, 29 Nov 2024 15:16:56 +0100 Subject: [PATCH 3/7] parametrize namespace in rapidast command --- tests/rhwa/far-operator/tests/far.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/rhwa/far-operator/tests/far.go b/tests/rhwa/far-operator/tests/far.go index e69d058a2..6c246d594 100644 --- a/tests/rhwa/far-operator/tests/far.go +++ b/tests/rhwa/far-operator/tests/far.go @@ -132,7 +132,8 @@ var _ = Describe( Expect(err).ToNot(HaveOccurred(), "Failed to create client test pod") By("Running vulnerability scan") - command := []string{"bash", "-c", "NAMESPACE=openshift-workload-availability rapidast.py --config ./config/rapidastConfig.yaml 2> /dev/null"} + command := []string{"bash", "-c", + fmt.Sprintf("NAMESPACE=%s rapidast.py --config ./config/rapidastConfig.yaml 2> /dev/null", rhwaparams.RhwaOperatorNs)} output, err := dastTestPod.ExecCommand(command) Expect(err).ToNot(HaveOccurred(), "Command failed") From 7ab7ced3f5c50369490265cefd8b6044e36baf82 Mon Sep 17 00:00:00 2001 From: Francisco Javier Moreno Date: Fri, 29 Nov 2024 17:56:35 +0100 Subject: [PATCH 4/7] extract preparation of rapidast pod --- tests/rhwa/far-operator/tests/far.go | 52 +-------------- tests/rhwa/internal/rapidast/rapidast.go | 83 ++++++++++++++++++++++++ tests/rhwa/internal/rhwaparams/const.go | 2 + 3 files changed, 87 insertions(+), 50 deletions(-) create mode 100644 tests/rhwa/internal/rapidast/rapidast.go diff --git a/tests/rhwa/far-operator/tests/far.go b/tests/rhwa/far-operator/tests/far.go index 6c246d594..5de9106df 100644 --- a/tests/rhwa/far-operator/tests/far.go +++ b/tests/rhwa/far-operator/tests/far.go @@ -3,25 +3,19 @@ package tests import ( "encoding/json" "fmt" - "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/openshift-kni/eco-goinfra/pkg/deployment" "github.com/openshift-kni/eco-goinfra/pkg/pod" - "github.com/openshift-kni/eco-goinfra/pkg/rbac" "github.com/openshift-kni/eco-goinfra/pkg/reportxml" - "github.com/openshift-kni/eco-goinfra/pkg/serviceaccount" - - "github.com/openshift-kni/eco-goinfra/pkg/nodes" - "github.com/openshift-kni/eco-gotests/tests/rhwa/far-operator/internal/farparams" + . "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rapidast" . "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwainittools" "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwaparams" - v1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -87,49 +81,7 @@ var _ = Describe( It("Verify FAR Operator passes trivy scan without vulnerabilities", reportxml.ID("76877"), func() { - By("Retrieve list of nodes") - nodes, err := nodes.List(APIClient) - Expect(err).ToNot(HaveOccurred(), "Error getting nodes list") - - By("Create service account") - _, err = serviceaccount.NewBuilder(APIClient, "trivy-service-account", rhwaparams.TestNamespaceName).Create() - Expect(err).ToNot(HaveOccurred(), "Failed to create Service Account") - - _, err = rbac.NewClusterRoleBuilder(APIClient, "trivy-clusterrole", v1.PolicyRule{ - APIGroups: []string{ - "", - }, - Resources: []string{ - "pods", - }, - Verbs: []string{ - "get", - "list", - "watch", - }, - }).Create() - Expect(err).ToNot(HaveOccurred(), "Failed to create Cluster Role") - - _, err = rbac.NewClusterRoleBindingBuilder(APIClient, "trivy-clusterrole-binding", "trivy-clusterrole", v1.Subject{ - Kind: "ServiceAccount", - Name: "trivy-service-account", - Namespace: rhwaparams.TestNamespaceName, - }).Create() - Expect(err).ToNot(HaveOccurred(), "Failed to create ClusterRoleBinding") - - By("Creating client test pod") - dastTestPod := pod.NewBuilder( - APIClient, "rapidastclientpod", rhwaparams.TestNamespaceName, rhwaparams.TestContainerDast). - DefineOnNode(nodes[0].Object.Name). - WithTolerationToMaster(). - WithPrivilegedFlag() - Expect(err).ToNot(HaveOccurred(), "Failed to create client test pod") - - dastTestPod.Definition.Spec.ServiceAccountName = "trivy-service-account" - - By("Creating client test pod") - _, err = dastTestPod.CreateAndWaitUntilRunning(time.Minute) - Expect(err).ToNot(HaveOccurred(), "Failed to create client test pod") + dastTestPod := PrepareRapidastPod(APIClient) By("Running vulnerability scan") command := []string{"bash", "-c", diff --git a/tests/rhwa/internal/rapidast/rapidast.go b/tests/rhwa/internal/rapidast/rapidast.go new file mode 100644 index 000000000..147303b6a --- /dev/null +++ b/tests/rhwa/internal/rapidast/rapidast.go @@ -0,0 +1,83 @@ +package rapidast + +import ( + "time" + + "github.com/golang/glog" + "github.com/openshift-kni/eco-goinfra/pkg/clients" + "github.com/openshift-kni/eco-goinfra/pkg/nodes" + "github.com/openshift-kni/eco-goinfra/pkg/pod" + "github.com/openshift-kni/eco-goinfra/pkg/rbac" + "github.com/openshift-kni/eco-goinfra/pkg/serviceaccount" + . "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwainittools" + "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwaparams" + + v1 "k8s.io/api/rbac/v1" +) + +const ( + logLevel = rhwaparams.LogLevel +) + +func PrepareRapidastPod(apiClient *clients.Settings) *pod.Builder { + nodes, err := nodes.List(apiClient) + if err != nil { + glog.V(logLevel).Infof( + "Error in node list retrieval %s", err.Error()) + } + + _, err = serviceaccount.NewBuilder(APIClient, "trivy-service-account", rhwaparams.TestNamespaceName).Create() + if err != nil { + glog.V(logLevel).Infof( + "Error in service acount creation %s", err.Error()) + } + + _, err = rbac.NewClusterRoleBuilder(APIClient, "trivy-clusterrole", v1.PolicyRule{ + APIGroups: []string{ + "", + }, + Resources: []string{ + "pods", + }, + Verbs: []string{ + "get", + "list", + "watch", + }, + }).Create() + if err != nil { + glog.V(logLevel).Infof( + "Error in ClusterRoleBuilder creation %s", err.Error()) + } + + _, err = rbac.NewClusterRoleBindingBuilder(APIClient, "trivy-clusterrole-binding", "trivy-clusterrole", v1.Subject{ + Kind: "ServiceAccount", + Name: "trivy-service-account", + Namespace: rhwaparams.TestNamespaceName, + }).Create() + if err != nil { + glog.V(logLevel).Infof( + "Error in ClusterRoleBindingBuilder creation %s", err.Error()) + } + + dastTestPod := pod.NewBuilder( + APIClient, "rapidastclientpod", rhwaparams.TestNamespaceName, rhwaparams.TestContainerDast). + DefineOnNode(nodes[0].Object.Name). + WithTolerationToMaster(). + WithPrivilegedFlag() + if err != nil { + glog.V(logLevel).Infof( + "Error in rapidast client pod definition %s", err.Error()) + } + + dastTestPod.Definition.Spec.ServiceAccountName = "trivy-service-account" + + _, err = dastTestPod.CreateAndWaitUntilRunning(time.Minute) + if err != nil { + glog.V(logLevel).Infof( + "Error in rapidast client pod creation %s", err.Error()) + } + + return dastTestPod + +} diff --git a/tests/rhwa/internal/rhwaparams/const.go b/tests/rhwa/internal/rhwaparams/const.go index 62495888d..0cb3d324c 100644 --- a/tests/rhwa/internal/rhwaparams/const.go +++ b/tests/rhwa/internal/rhwaparams/const.go @@ -14,5 +14,7 @@ const ( // TestNamespaceName namespace where all dast test cases are performed TestNamespaceName = "dast-tests" + LogLevel = 90 + TestContainerDast = "quay.io/frmoreno/eco-dast:latest" ) From 98e6e72ba103cca643945f24dc845be7fb0203b0 Mon Sep 17 00:00:00 2001 From: Francisco Javier Moreno Date: Sat, 30 Nov 2024 23:22:57 +0100 Subject: [PATCH 5/7] move DastReport struct --- tests/rhwa/far-operator/tests/far.go | 33 +--------------------- tests/rhwa/internal/rapidast/dastreport.go | 33 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 32 deletions(-) create mode 100644 tests/rhwa/internal/rapidast/dastreport.go diff --git a/tests/rhwa/far-operator/tests/far.go b/tests/rhwa/far-operator/tests/far.go index 5de9106df..cf4246ea5 100644 --- a/tests/rhwa/far-operator/tests/far.go +++ b/tests/rhwa/far-operator/tests/far.go @@ -19,38 +19,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -type DASTReport struct { - ClusterName string - Resources []struct { - Name string - Namespace string - Results []struct { - Target string - Class string - Type string - MisconfSummary struct { - Success int - Failures int - Exceptions int - } - Misconfigurations []struct { - Type string - ID string - AVDID string - Description string - Message string - Namespace string - Query string - Resolution string - Severity string - PrimaryURL string - References []string - Status string - } - } - } -} - var _ = Describe( "FAR Post Deployment tests", Ordered, @@ -81,6 +49,7 @@ var _ = Describe( It("Verify FAR Operator passes trivy scan without vulnerabilities", reportxml.ID("76877"), func() { + By("Creating rapidast pod") dastTestPod := PrepareRapidastPod(APIClient) By("Running vulnerability scan") diff --git a/tests/rhwa/internal/rapidast/dastreport.go b/tests/rhwa/internal/rapidast/dastreport.go new file mode 100644 index 000000000..567f90695 --- /dev/null +++ b/tests/rhwa/internal/rapidast/dastreport.go @@ -0,0 +1,33 @@ +package rapidast + +type DASTReport struct { + ClusterName string + Resources []struct { + Name string + Namespace string + Results []struct { + Target string + Class string + Type string + MisconfSummary struct { + Success int + Failures int + Exceptions int + } + Misconfigurations []struct { + Type string + ID string + AVDID string + Description string + Message string + Namespace string + Query string + Resolution string + Severity string + PrimaryURL string + References []string + Status string + } + } + } +} From 47b220224688e0352a56377b8465f6d790fdc55e Mon Sep 17 00:00:00 2001 From: Francisco Javier Moreno Date: Tue, 3 Dec 2024 08:43:20 +0100 Subject: [PATCH 6/7] moving dast test to indepent file The dast test for far operator is moved to a separate file, and it is labeled with additional "dast" label, to be able to run it apart from the rest of the operator tests if needed. --- images/rhwa/dast/Dockerfile | 1 - tests/rhwa/far-operator/tests/dast.go | 61 ++++++++++++++++++++++ tests/rhwa/far-operator/tests/far.go | 30 ----------- tests/rhwa/internal/rapidast/dastreport.go | 1 + tests/rhwa/internal/rapidast/rapidast.go | 15 ++++-- tests/rhwa/internal/rhwaparams/const.go | 6 +-- 6 files changed, 75 insertions(+), 39 deletions(-) create mode 100644 tests/rhwa/far-operator/tests/dast.go diff --git a/images/rhwa/dast/Dockerfile b/images/rhwa/dast/Dockerfile index 0141ec353..8022b7663 100644 --- a/images/rhwa/dast/Dockerfile +++ b/images/rhwa/dast/Dockerfile @@ -1,7 +1,6 @@ # Use the official RAPIDast image as the base FROM quay.io/redhatproductsecurity/rapidast:2.8.0 - # Set working directory to the RAPIDast installation WORKDIR /opt/rapidast diff --git a/tests/rhwa/far-operator/tests/dast.go b/tests/rhwa/far-operator/tests/dast.go new file mode 100644 index 000000000..f37a6aaae --- /dev/null +++ b/tests/rhwa/far-operator/tests/dast.go @@ -0,0 +1,61 @@ +package tests + +import ( + "encoding/json" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/openshift-kni/eco-goinfra/pkg/deployment" + "github.com/openshift-kni/eco-goinfra/pkg/reportxml" + + "github.com/openshift-kni/eco-gotests/tests/rhwa/far-operator/internal/farparams" + rapidast "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rapidast" + . "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwainittools" + "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwaparams" +) + +var _ = Describe( + "FAR Post Deployment tests", + Ordered, + ContinueOnFailure, + Label(farparams.Label), Label("dast"), func() { + BeforeAll(func() { + By("Get FAR deployment object") + farDeployment, err := deployment.Pull( + APIClient, farparams.OperatorDeploymentName, rhwaparams.RhwaOperatorNs) + Expect(err).ToNot(HaveOccurred(), "Failed to get FAR deployment") + + By("Verify FAR deployment is Ready") + Expect(farDeployment.IsReady(rhwaparams.DefaultTimeout)).To(BeTrue(), "FAR deployment is not Ready") + }) + + It("Verify FAR Operator passes trivy scan without vulnerabilities", reportxml.ID("76877"), func() { + + By("Creating rapidast pod") + dastTestPod := rapidast.PrepareRapidastPod(APIClient) + + output, err := rapidast.RunRapidastScan(*dastTestPod, rhwaparams.RhwaOperatorNs) + Expect(err).ToNot(HaveOccurred()) + + By("Checking vulnerability scan results") + var parsableStruct rapidast.DASTReport + err = json.Unmarshal(output.Bytes(), &parsableStruct) + Expect(err).ToNot(HaveOccurred()) + + var vulnerabilityFound = false + for _, resource := range parsableStruct.Resources { + for _, result := range resource.Results { + if result.MisconfSummary.Failures > 0 { + fmt.Printf("%d vulnerability(s) found in %s\n", result.MisconfSummary.Failures, resource.Name) + for _, misconfiguration := range result.Misconfigurations { + fmt.Printf("- %+v\n", misconfiguration) + } + vulnerabilityFound = true + } + } + } + Expect(vulnerabilityFound).NotTo(BeTrue(), "Found vulnerability(s)") + }) + }) diff --git a/tests/rhwa/far-operator/tests/far.go b/tests/rhwa/far-operator/tests/far.go index cf4246ea5..daddb1491 100644 --- a/tests/rhwa/far-operator/tests/far.go +++ b/tests/rhwa/far-operator/tests/far.go @@ -1,7 +1,6 @@ package tests import ( - "encoding/json" "fmt" . "github.com/onsi/ginkgo/v2" @@ -12,7 +11,6 @@ import ( "github.com/openshift-kni/eco-goinfra/pkg/reportxml" "github.com/openshift-kni/eco-gotests/tests/rhwa/far-operator/internal/farparams" - . "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rapidast" . "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwainittools" "github.com/openshift-kni/eco-gotests/tests/rhwa/internal/rhwaparams" @@ -46,32 +44,4 @@ var _ = Describe( ) Expect(err).ToNot(HaveOccurred(), "Pod is not ready") }) - - It("Verify FAR Operator passes trivy scan without vulnerabilities", reportxml.ID("76877"), func() { - - By("Creating rapidast pod") - dastTestPod := PrepareRapidastPod(APIClient) - - By("Running vulnerability scan") - command := []string{"bash", "-c", - fmt.Sprintf("NAMESPACE=%s rapidast.py --config ./config/rapidastConfig.yaml 2> /dev/null", rhwaparams.RhwaOperatorNs)} - output, err := dastTestPod.ExecCommand(command) - Expect(err).ToNot(HaveOccurred(), "Command failed") - - By("Checking vulnerability scan results") - var parsableStruct DASTReport - err = json.Unmarshal(output.Bytes(), &parsableStruct) - Expect(err).ToNot(HaveOccurred()) - - var vulnerability_found bool = false - for _, resource := range parsableStruct.Resources { - for _, result := range resource.Results { - if result.MisconfSummary.Failures > 0 { - fmt.Printf("%d vulnerability(s) found in %s\n", result.MisconfSummary.Failures, resource.Name) - vulnerability_found = true - } - } - } - Expect(vulnerability_found).NotTo(BeTrue(), "Found vulnerability(s)") - }) }) diff --git a/tests/rhwa/internal/rapidast/dastreport.go b/tests/rhwa/internal/rapidast/dastreport.go index 567f90695..ffef63449 100644 --- a/tests/rhwa/internal/rapidast/dastreport.go +++ b/tests/rhwa/internal/rapidast/dastreport.go @@ -1,5 +1,6 @@ package rapidast +// DASTReport struct that receives the results of the rapidast scan. type DASTReport struct { ClusterName string Resources []struct { diff --git a/tests/rhwa/internal/rapidast/rapidast.go b/tests/rhwa/internal/rapidast/rapidast.go index 147303b6a..310702b37 100644 --- a/tests/rhwa/internal/rapidast/rapidast.go +++ b/tests/rhwa/internal/rapidast/rapidast.go @@ -1,6 +1,8 @@ package rapidast import ( + "bytes" + "fmt" "time" "github.com/golang/glog" @@ -19,6 +21,7 @@ const ( logLevel = rhwaparams.LogLevel ) +// PrepareRapidastPod initializes the pod in the cluster that allows to run rapidast. func PrepareRapidastPod(apiClient *clients.Settings) *pod.Builder { nodes, err := nodes.List(apiClient) if err != nil { @@ -65,11 +68,6 @@ func PrepareRapidastPod(apiClient *clients.Settings) *pod.Builder { DefineOnNode(nodes[0].Object.Name). WithTolerationToMaster(). WithPrivilegedFlag() - if err != nil { - glog.V(logLevel).Infof( - "Error in rapidast client pod definition %s", err.Error()) - } - dastTestPod.Definition.Spec.ServiceAccountName = "trivy-service-account" _, err = dastTestPod.CreateAndWaitUntilRunning(time.Minute) @@ -79,5 +77,12 @@ func PrepareRapidastPod(apiClient *clients.Settings) *pod.Builder { } return dastTestPod +} + +// RunRapidastScan executes the rapidast scan configured in the container. +func RunRapidastScan(dastTestPod pod.Builder, namespace string) (bytes.Buffer, error) { + command := []string{"bash", "-c", + fmt.Sprintf("NAMESPACE=%s rapidast.py --config ./config/rapidastConfig.yaml 2> /dev/null", namespace)} + return dastTestPod.ExecCommand(command) } diff --git a/tests/rhwa/internal/rhwaparams/const.go b/tests/rhwa/internal/rhwaparams/const.go index 0cb3d324c..122edc3b3 100644 --- a/tests/rhwa/internal/rhwaparams/const.go +++ b/tests/rhwa/internal/rhwaparams/const.go @@ -11,10 +11,10 @@ const ( RhwaOperatorNs = "openshift-workload-availability" // DefaultTimeout represents the default timeout. DefaultTimeout = 300 * time.Second - // TestNamespaceName namespace where all dast test cases are performed + // TestNamespaceName namespace where all dast test cases are performed. TestNamespaceName = "dast-tests" - + // LogLevel for the supporting functions. LogLevel = 90 - + // TestContainerDast specifies the container image to use for rapidast tests. TestContainerDast = "quay.io/frmoreno/eco-dast:latest" ) From 45c39b3fe08add4160cb22d809ba0ac2f4ff6efc Mon Sep 17 00:00:00 2001 From: Francisco Javier Moreno Date: Thu, 5 Dec 2024 18:25:15 +0100 Subject: [PATCH 7/7] remove dockerfile definition --- images/rhwa/dast/Dockerfile | 7 ---- images/rhwa/dast/config/rapidastConfig.yaml | 46 --------------------- tests/rhwa/internal/rhwaparams/const.go | 2 +- 3 files changed, 1 insertion(+), 54 deletions(-) delete mode 100644 images/rhwa/dast/Dockerfile delete mode 100644 images/rhwa/dast/config/rapidastConfig.yaml diff --git a/images/rhwa/dast/Dockerfile b/images/rhwa/dast/Dockerfile deleted file mode 100644 index 8022b7663..000000000 --- a/images/rhwa/dast/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -# Use the official RAPIDast image as the base -FROM quay.io/redhatproductsecurity/rapidast:2.8.0 - -# Set working directory to the RAPIDast installation -WORKDIR /opt/rapidast - -COPY ./config/rapidastConfig.yaml ./config/ diff --git a/images/rhwa/dast/config/rapidastConfig.yaml b/images/rhwa/dast/config/rapidastConfig.yaml deleted file mode 100644 index 8ea4a5130..000000000 --- a/images/rhwa/dast/config/rapidastConfig.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# This is a configuration template file to perform scans using user-defined container images or scripts -# -# Author: Red Hat Product Security -config: - # WARNING: `configVersion` indicates the schema version of the config file. - # This value tells RapiDAST what schema should be used to read this configuration. - # Therefore you should only change it if you update the configuration to a newer schema - # It is intended to keep backward compatibility (newer RapiDAST running an older config) - configVersion: 5 -# `application` contains data related to the application, not to the scans. -application: - shortName: "trivy" -# `general` is a section that will be applied to all scanners. -general: - container: - # This configures what technology is to be used for RapiDAST to run each scanner. - # Currently supported: `podman` and `none` - # none: Default. RapiDAST runs each scanner in the same host or inside the RapiDAST image container - # podman: RapiDAST orchestrates each scanner on its own using podman - # When undefined, relies on rapidast-defaults.yaml, or `none` if nothing is set - type: "none" -# `scanners' is a section that configures scanning options -scanners: - #generic_oobt: - # #results: "/opt/rapidast/results/oobtkube.sarif.json" # if None or "*stdout", the command's standard output is selected - # # toolDir: scanners/generic/tools - # inline: "python3 oobtkube.py -d 120 -p 12345 -i 10.74.16.40 -f /test/far_template.yaml -o oobtkube.sarif.json" - generic_trivy: - # results: - # An absolute path to file or directory where results are stored on the host. - # if it is "*stdout" or unspecified, the command's standard output will be selected - # When container.type is 'podman', this needs to be used along with the container.volumes configuration below - # If the result needs to be sent to DefectDojo, this must be a SARIF format file - #results: "/test/results/oobttest" - # Example: scan a k8s cluster for misconfiguration issue - # - kubeconfig file for the cluster is required - # - See https://aquasecurity.github.io/trivy/v0.49/docs/target/kubernetes/ for more information on 'trivy k8s' scan - # - scanners/generic/tools/convert_trivy_k8s_to_sarif.py converts the Trivy json result to the SARIF format - # 'inline' is used when container.type is not 'podman' - # 'toolDir' specifies the default directory where inline scripts are located - #toolDir: scanners/generic/tools - inline: "trivy k8s -n $NAMESPACE pod --severity=HIGH,CRITICAL --scanners=misconfig --report all --format json" - container: - parameters: - # Optional: list of expected return codes, anything else will be considered as an error. by default: [0] - validReturns: [ 0 ] \ No newline at end of file diff --git a/tests/rhwa/internal/rhwaparams/const.go b/tests/rhwa/internal/rhwaparams/const.go index 122edc3b3..78d4ee4b8 100644 --- a/tests/rhwa/internal/rhwaparams/const.go +++ b/tests/rhwa/internal/rhwaparams/const.go @@ -16,5 +16,5 @@ const ( // LogLevel for the supporting functions. LogLevel = 90 // TestContainerDast specifies the container image to use for rapidast tests. - TestContainerDast = "quay.io/frmoreno/eco-dast:latest" + TestContainerDast = "quay.io/ocp-edge-qe/eco-dast:latest" )