From 4b3feaa6022e0010188623d640b78052f42e82ef Mon Sep 17 00:00:00 2001 From: Starttoaster Date: Wed, 1 May 2024 06:06:29 +0000 Subject: [PATCH 1/3] Add namespace and kind filters to roles and configaudits dashboards --- README.md | 8 -------- internal/web/server.go | 22 +++++++++++++++++++-- internal/web/views/configaudits/audit.go | 25 +++++++++++++++++++++--- internal/web/views/roles/roles.go | 16 +++++++++++++-- static/configaudits.html | 6 +++--- static/roles.html | 2 +- 6 files changed, 60 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 4352616..3aa75a8 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,6 @@ Example URL: `http://your.explorer.install/image?hasfix=true&severity=Critical` There are query parameters for image and digest as well, but it's not expected for them to be changed manually. These filters currently do not have a graphical toggle but there's a TODO item below to add some form elements to the page for them. -### ClusterRole/Role/Exposed Secrets filter - -| Parameters | Description | Example | -|--------------|-----------------------------------------------------------------------|---------------------------| -| severity | Filter by level of vulnerability severity. | severity=Critical | - -Example URL: `http://your.explorer.install/role?severity=Critical` - ## TODO - Make a home page that displays useful graphs for each of the report types. diff --git a/internal/web/server.go b/internal/web/server.go index 4a5a3f4..5584dd7 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -146,13 +146,21 @@ func rolesHandler(w http.ResponseWriter, r *http.Request) { return } + // Parse URL query params + q := r.URL.Query() + + // Check query params + namespace := q.Get("namespace") + // Get role reports reports, err := kube.GetRbacAssessmentReportList() if err != nil { log.Logger.Error("error getting VulnerabilityReports", "error", err.Error()) return } - roles := rolesview.GetView(reports) + roles := rolesview.GetView(reports, rolesview.Filters{ + Namespace: namespace, + }) err = tmpl.Execute(w, roles) if err != nil { @@ -293,13 +301,23 @@ func configauditsHandler(w http.ResponseWriter, r *http.Request) { return } + // Parse URL query params + q := r.URL.Query() + + // Check query params + namespace := q.Get("namespace") + kind := q.Get("kind") + // Get reports reports, err := kube.GetConfigAuditReportList() if err != nil { log.Logger.Error("error getting configauditreports", "error", err.Error()) return } - audits := configauditsview.GetView(reports) + audits := configauditsview.GetView(reports, configauditsview.Filters{ + Namespace: namespace, + Kind: kind, + }) err = tmpl.Execute(w, audits) if err != nil { diff --git a/internal/web/views/configaudits/audit.go b/internal/web/views/configaudits/audit.go index 9de60dd..cf09e23 100644 --- a/internal/web/views/configaudits/audit.go +++ b/internal/web/views/configaudits/audit.go @@ -7,8 +7,14 @@ import ( "github.com/aquasecurity/trivy-operator/pkg/apis/aquasecurity/v1alpha1" ) +// Filters represents the available optional filters to the config audit view +type Filters struct { + Namespace string + Kind string +} + // GetView converts some report data to the /roles view -func GetView(data *v1alpha1.ConfigAuditReportList) View { +func GetView(data *v1alpha1.ConfigAuditReportList, filters Filters) View { var view View for _, item := range data.Items { @@ -20,11 +26,24 @@ func GetView(data *v1alpha1.ConfigAuditReportList) View { } else { name = item.Name } + kind := item.ObjectMeta.Labels["trivy-operator.resource.kind"] + namespace := item.ObjectMeta.Labels["trivy-operator.resource.namespace"] + + if filters.Kind != "" { + if kind != filters.Kind { + continue + } + } + if filters.Namespace != "" { + if namespace != filters.Namespace { + continue + } + } audit := Data{ - Kind: item.ObjectMeta.Labels["trivy-operator.resource.kind"], + Kind: kind, Name: name, - Namespace: item.ObjectMeta.Labels["trivy-operator.resource.namespace"], + Namespace: namespace, } index, unique := view.isUnique(audit.Name, audit.Namespace, audit.Kind) diff --git a/internal/web/views/roles/roles.go b/internal/web/views/roles/roles.go index 0243665..85a8d33 100644 --- a/internal/web/views/roles/roles.go +++ b/internal/web/views/roles/roles.go @@ -7,8 +7,13 @@ import ( "github.com/aquasecurity/trivy-operator/pkg/apis/aquasecurity/v1alpha1" ) +// Filters represents the available optional filters to the roles audit view +type Filters struct { + Namespace string +} + // GetView converts some report data to the /roles view -func GetView(data *v1alpha1.RbacAssessmentReportList) View { +func GetView(data *v1alpha1.RbacAssessmentReportList, filters Filters) View { var r View for _, item := range data.Items { @@ -21,10 +26,17 @@ func GetView(data *v1alpha1.RbacAssessmentReportList) View { name = item.Name } + namespace := item.ObjectMeta.Labels["trivy-operator.resource.namespace"] + if filters.Namespace != "" { + if namespace != filters.Namespace { + continue + } + } + role := Data{ Kind: item.ObjectMeta.Labels["trivy-operator.resource.kind"], Name: name, - Namespace: item.ObjectMeta.Labels["trivy-operator.resource.namespace"], + Namespace: namespace, } index, unique := r.isUniqueRole(role.Name, role.Namespace, role.Kind) diff --git a/static/configaudits.html b/static/configaudits.html index 8c003d2..df9bc81 100644 --- a/static/configaudits.html +++ b/static/configaudits.html @@ -1,6 +1,6 @@ - Explorer: configaudits + Explorer: Config Audits @@ -38,7 +38,7 @@ - + {{ $data.Namespace }} @@ -48,7 +48,7 @@ - + {{ $data.Kind }} diff --git a/static/roles.html b/static/roles.html index 1615f3a..af21eec 100644 --- a/static/roles.html +++ b/static/roles.html @@ -38,7 +38,7 @@ - + {{ $data.Namespace }} From fc20ac430818d9febab1c0cec5fb61862f2d4f50 Mon Sep 17 00:00:00 2001 From: Starttoaster Date: Wed, 1 May 2024 06:36:34 +0000 Subject: [PATCH 2/3] Fix up readme --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3aa75a8..c6d9007 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ trivy-operator-explorer ## Filters -Some dashboards have filters available. These filters are only set-able from the URL in query parameters for now, as there are no forms on the explorer yet, though it is planned. +Some dashboards have filters that can be set from clicking elements on the page, but the following can only be set manually from the URL query parameters for now. UI elements might be added for these over time. ### Image filter @@ -39,10 +39,7 @@ Some dashboards have filters available. These filters are only set-able from the Example URL: `http://your.explorer.install/image?hasfix=true&severity=Critical` -There are query parameters for image and digest as well, but it's not expected for them to be changed manually. These filters currently do not have a graphical toggle but there's a TODO item below to add some form elements to the page for them. - ## TODO - Make a home page that displays useful graphs for each of the report types. -- Graphical elements for setting filters, currently they're just URL query parameters. -- Testing. Pretty sure by law no new product can have tests. +- Graphical elements for setting the filters on the /image page From 73c6fd37ea90598791708e8cbdad74b3fe877a79 Mon Sep 17 00:00:00 2001 From: Starttoaster Date: Wed, 1 May 2024 06:36:59 +0000 Subject: [PATCH 3/3] Add images filter for vulnerabilities with fixes --- internal/web/server.go | 18 +++++++++++++++++- internal/web/views/images/images.go | 14 +++++++++++++- static/css/output.css | 24 ++++++++++++++++++++++++ static/images.html | 3 +++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/internal/web/server.go b/internal/web/server.go index 5584dd7..26cec2c 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -51,13 +51,29 @@ func imagesHandler(w http.ResponseWriter, r *http.Request) { return } + // Parse URL query params + q := r.URL.Query() + + // Check query params + hasFix := q.Get("hasfix") + var hasFixBool bool + if hasFix != "" { + var err error + hasFixBool, err = strconv.ParseBool(hasFix) + if err != nil { + log.Logger.Warn("could not parse hasfix query parameter to bool type, ignoring filter", "raw", hasFix, "error", err.Error()) + } + } + // Get vulnerability reports data, err := kube.GetVulnerabilityReportList() if err != nil { log.Logger.Error("error getting VulnerabilityReports", "error", err.Error()) return } - imageData := imagesview.GetView(data) + imageData := imagesview.GetView(data, imagesview.Filters{ + HasFix: hasFixBool, + }) err = tmpl.Execute(w, imageData) if err != nil { diff --git a/internal/web/views/images/images.go b/internal/web/views/images/images.go index 0780ae5..2d13f9e 100644 --- a/internal/web/views/images/images.go +++ b/internal/web/views/images/images.go @@ -8,8 +8,13 @@ import ( "github.com/aquasecurity/trivy-operator/pkg/apis/aquasecurity/v1alpha1" ) +// Filters represents the available optional filters to the images view +type Filters struct { + HasFix bool +} + // GetView converts some report data to the /images view -func GetView(data *v1alpha1.VulnerabilityReportList) View { +func GetView(data *v1alpha1.VulnerabilityReportList, filters Filters) View { var i View for _, item := range data.Items { @@ -62,6 +67,13 @@ func GetView(data *v1alpha1.VulnerabilityReportList) View { FixedVersion: v.FixedVersion, } + // Filter by hasfix + if filters.HasFix { + if strings.TrimSpace(vuln.FixedVersion) == "" { + continue + } + } + // If the image is not unique, we need to check if the vulnerability is unique too // Seems rare, but Trivy Operator sometimes gives duplicate CVE data for an image uniqueVuln := i[imageIndex].isUniqueImageVulnerability(vuln.ID, vuln.Severity) diff --git a/static/css/output.css b/static/css/output.css index 603be9c..8fd37bc 100644 --- a/static/css/output.css +++ b/static/css/output.css @@ -767,6 +767,11 @@ video { background-color: rgb(249 250 251 / var(--tw-bg-opacity)); } +.bg-green-100 { + --tw-bg-opacity: 1; + background-color: rgb(220 252 231 / var(--tw-bg-opacity)); +} + .bg-orange-200 { --tw-bg-opacity: 1; background-color: rgb(254 215 170 / var(--tw-bg-opacity)); @@ -905,6 +910,10 @@ video { font-weight: 500; } +.font-normal { + font-weight: 400; +} + .font-semibold { font-weight: 600; } @@ -938,6 +947,11 @@ video { color: rgb(17 24 39 / var(--tw-text-opacity)); } +.text-green-800 { + --tw-text-opacity: 1; + color: rgb(22 101 52 / var(--tw-text-opacity)); +} + .text-orange-800 { --tw-text-opacity: 1; color: rgb(154 52 18 / var(--tw-text-opacity)); @@ -1107,6 +1121,11 @@ video { background-color: rgb(17 24 39 / var(--tw-bg-opacity)); } + .dark\:bg-green-900 { + --tw-bg-opacity: 1; + background-color: rgb(20 83 45 / var(--tw-bg-opacity)); + } + .dark\:bg-indigo-800 { --tw-bg-opacity: 1; background-color: rgb(55 48 163 / var(--tw-bg-opacity)); @@ -1152,6 +1171,11 @@ video { color: rgb(156 163 175 / var(--tw-text-opacity)); } + .dark\:text-green-300 { + --tw-text-opacity: 1; + color: rgb(134 239 172 / var(--tw-text-opacity)); + } + .dark\:text-orange-100 { --tw-text-opacity: 1; color: rgb(255 237 213 / var(--tw-text-opacity)); diff --git a/static/images.html b/static/images.html index d2e9000..bd9c0ad 100644 --- a/static/images.html +++ b/static/images.html @@ -29,6 +29,9 @@ Vulnerabilities + + Has fix? +