Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve trivy operator slack template #597

Merged
merged 1 commit into from
Aug 28, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 142 additions & 18 deletions rego-templates/trivy-operator-slack.rego
Original file line number Diff line number Diff line change
@@ -1,20 +1,144 @@
package postee.trivyoperator.slack

title:=sprintf("Trivy Operator %s Report for - %s", [input.kind, input.metadata.name])

result:= res {
res:= [
{ "type":"section",
"text": {"type":"mrkdwn","text": sprintf("*CRITICAL:* %d", [input.report.summary.criticalCount])}},
{ "type":"section",
"text": {"type":"mrkdwn","text": sprintf("*HIGH:* %d", [input.report.summary.highCount])}},
{ "type":"section",
"text": {"type":"mrkdwn","text": sprintf("*MEDIUM:* %d", [input.report.summary.mediumCount])}},
{ "type":"section",
"text": {"type":"mrkdwn","text": sprintf("*LOW:* %d", [input.report.summary.lowCount])}},
{ "type":"section",
"text": {"type":"mrkdwn","text": sprintf("*UNKNOWN:* %d", [input.report.summary.unknownCount])}},
{ "type":"section",
"text": {"type":"mrkdwn","text": sprintf("*NONE:* %d", [input.report.summary.noneCount])}},
]
}
import data.postee.flat_array #converts [[{...},{...}], [{...},{...}]] to [{...},{...},{...},{...}]
import data.postee.with_default

############################################# Common functions ############################################

# render_sections split collection of cells provided to chunks of 5 rows each and wraps every chunk with section element
render_sections(rows, caption, headers) = result {
count(rows) > 0 # only if some vulnerabilities are found
rows_and_header := array.concat(headers, rows)
a := flat_array([s |
# code below converts 2 dimension array like [[row1, row2, ... row5], ....]
group_size := 10 #it's 5 but every row is represented by 2 items
num_chunks := ceil(count(rows_and_header) / group_size) - 1
indices := {b | b := numbers.range(0, num_chunks)[_] * group_size}
some k
fields := [array.slice(rows_and_header, i, i + group_size) | i := indices[_]][k]
# builds markdown section based on slice

s := with_caption(fields, caption, k)
])
result := array.concat(a, [{"type": "divider"}])
}

render_sections(rows, caption, headers) = [] { #do not render section if provided collection is empty
count(rows) == 0
}

with_caption(fields, caption, position) = s {
position == 0
s := [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": caption,
},
},
{
"type": "section",
"fields": fields,
},
]
}
with_caption(fields, caption, position) = s {
position > 0
s := [
{
"type": "section",
"fields": fields,
},
]
}


###########################################################################################################

vln_list(severity) = l {
# builds list of rows for section for the given severity
some i
vlnrb := [r |
item := input.report.vulnerabilities[i]
vlnname := item.vulnerabilityID

fxvrsn := with_default(item, "fixedVersion", "none")
resource_name = with_default(item, "resource", "none")
resource_version = with_default(item, "installedVersion", "none")
url = with_default(item, "primaryLink","")
item.severity == severity # only items with severity matched

r := [
{"type": "mrkdwn", "text": sprintf("<%s|%s>",[url,vlnname])},
{"type": "mrkdwn", "text": concat(" / ", [resource_name, resource_version, fxvrsn])},
]
]

caption := sprintf("*%s severity vulnerabilities*", [severity]) #TODO make first char uppercase

headers := [
{"type": "mrkdwn", "text": "*Vulnerability ID*"},
{"type": "mrkdwn", "text": "*Resource / Version / Fixed version*"},
]

# split rows and wrap slices with markdown section
l := render_sections(flat_array(vlnrb), caption, headers)
}

image_name := sprintf("%s:%s", [
with_default(input.report.artifact,"repository","unknown"),
with_default(input.report.artifact,"tag","unknown")
])
###########################################################################################################
postee := with_default(input, "postee", {})

title = sprintf("Vulnerability scan report %s", [image_name]) # title is

result = res {

header := [
{
"type": "header",
"text": {
"type": "plain_text",
"text": sprintf("Vulnerability issue with image:%s in namespace %s",[image_name, with_default(input.metadata,"namespace","unknown")]),
},
}
]

summary := [
{
"type": "divider"
},
{
"type": "context",
"elements": [
{"type": "mrkdwn", "text": "*Summary totals:*"},
],
},
{
"type": "context",
"elements": [
{"type": "mrkdwn", "text": sprintf("Critical: *%d*", [input.report.summary.criticalCount])},
{"type": "mrkdwn", "text": sprintf("High: *%d*", [input.report.summary.highCount])},
{"type": "mrkdwn", "text": sprintf("Medium: *%d*", [input.report.summary.mediumCount])},
{"type": "mrkdwn", "text": sprintf("Low: *%d*", [input.report.summary.lowCount])},
{"type": "mrkdwn", "text": sprintf("Unknown: *%d*", [input.report.summary.unknownCount])},
],
},
{
"type": "divider"
}
]

res := flat_array([
header,
summary,
vln_list("CRITICAL"),
vln_list("HIGH"),
vln_list("MEDIUM"),
vln_list("LOW"),
vln_list("UNKNOWN")
])
}