Simplify CVSS code in a more object-oriented manner

This commit is contained in:
maride 2026-01-28 13:35:45 +01:00
parent de6dcf2b82
commit 98c0f72e0d
2 changed files with 31 additions and 49 deletions

View File

@ -4,50 +4,32 @@
#let isUsed = state("isUsed", false) #let isUsed = state("isUsed", false)
// Statistics, used e.g. for the management summary // Statistics, used e.g. for the management summary
#let riskCategoryStats = ( #let riskCategories = (
Critical: state("riskCriticalStat", 0), "CRITICAL": (color: red, title: "Critical", state: state("riskCriticalStat", 0)),
High: state("riskHighStat", 0), "HIGH": (color: orange, title: "High", state: state("riskHighStat", 0)),
Medium: state("riskMediumStat", 0), "MEDIUM": (color: yellow, title: "Medium", state: state("riskMediumStat", 0)),
Low: state("riskLowStat", 0), "LOW": (color: lime, title: "Low", state: state("riskLowStat", 0)),
None: state("riskInformativeStat", 0), "NONE": (color: white, title: "None", state: state("riskInformativeStat", 0)),
Other: state("riskOtherStat", 0) "OTHER": (color: gray, title: "Other", state: state("riskOtherStat", 0))
) )
// Function to update the statistics // Function to update the statistics
#let updateRiskCategoryStats(status) = { #let updateRiskCategoryStats(status) = {
status = upper(status)
// check argument
panicOnInvalid(status, riskCategories.keys())
// Update status // Update status
if status == "Critical" { context(riskCategories.at(status).state.update(v => v + 1))
context(riskCategoryStats.Critical.update(v => v + 1))
} else if status == "High" {
context(riskCategoryStats.High.update(v => v + 1))
} else if status == "Medium" {
context(riskCategoryStats.Medium.update(v => v + 1))
} else if status == "Low" {
context(riskCategoryStats.Low.update(v => v + 1))
} else if status == "None" {
context(riskCategoryStats.None.update(v => v + 1))
} else if status == "Other" {
context(riskCategoryStats.Other.update(v => v + 1))
} else {
panic("Unknown state: " + status)
}
} }
// Return the table cell formatted according to its content - for the CVSS result // Return the table cell formatted according to its content - for the CVSS result
#let colorize(str) = { #let coloredCell(status) = {
if str == "Critical" { status = upper(status)
table.cell(str, fill: red, align: center) // check argument
} else if str == "High" { panicOnInvalid(status, riskCategories.keys())
table.cell(str, fill: orange, align: center)
} else if str == "Medium" { table.cell(riskCategories.at(status).title, fill: riskCategories.at(status).color, align: center)
table.cell(str, fill: yellow, align: center)
} else if str == "Low" {
table.cell(str, fill: lime, align: center)
} else if str == "None" {
table.cell(str, fill: white, align: center)
} else {
panic("Unknown CVSS state: " + str)
}
} }
// Create a small CIA table to be included for every finding // Create a small CIA table to be included for every finding
@ -75,15 +57,15 @@
let status = "?" let status = "?"
if baseScore >= 9.0 { if baseScore >= 9.0 {
status = "Critical" status = "CRITICAL"
} else if baseScore >= 7.0 { } else if baseScore >= 7.0 {
status = "High" status = "HIGH"
} else if baseScore >= 4.0 { } else if baseScore >= 4.0 {
status = "Medium" status = "MEDIUM"
} else if baseScore >= 0.1 { } else if baseScore >= 0.1 {
status = "Low" status = "LOW"
} else { } else {
status = "None" status = "NONE"
} }
block( block(
@ -98,7 +80,7 @@
table.cell(colspan: 3)[*Impact Metrics*], table.cell(colspan: 3)[*Impact Metrics*],
table.cell(rowspan: 2, align: bottom)[*#sym.sum*], table.cell(rowspan: 2, align: bottom)[*#sym.sum*],
[*AV*], [*AC*], [*PR*], [*UI*], [*S*], [*C*], [*I*], [*A*], [*AV*], [*AC*], [*PR*], [*UI*], [*S*], [*C*], [*I*], [*A*],
attackVector, attackComplexity, privilegesRequired, userInteraction, scope, cia.colorize(confidentiality), cia.colorize(integrity), cia.colorize(availability), colorize(status), attackVector, attackComplexity, privilegesRequired, userInteraction, scope, cia.colorize(confidentiality), cia.colorize(integrity), cia.colorize(availability), coloredCell(status),
) )
) )
#align(right)[ #align(right)[

View File

@ -25,7 +25,7 @@
== Findings == Findings
The penetration test revealed #context([ The penetration test revealed #context([
#let num = cvss.riskCategoryStats.values().map(v => v.final()).sum() #let num = cvss.riskCategories.values().map(v => v.state.final()).sum()
#if num == 1 { #if num == 1 {
[ #num finding ] [ #num finding ]
} else { } else {
@ -37,12 +37,12 @@
columns: (16.66%, 16.66%, 16.66%, 16.66%, 16.66%, 16.66%), columns: (16.66%, 16.66%, 16.66%, 16.66%, 16.66%, 16.66%),
align: center, align: center,
[Critical], [High], [Medium], [Low], [None], [Other], [Critical], [High], [Medium], [Low], [None], [Other],
table.cell(context(cvss.riskCategoryStats.Critical.final()), fill: red, align: center), table.cell(context(cvss.riskCategories.at("CRITICAL").state.final()), fill: red, align: center),
table.cell(context(cvss.riskCategoryStats.High.final()), fill: orange, align: center), table.cell(context(cvss.riskCategories.at("HIGH").state.final()), fill: orange, align: center),
table.cell(context(cvss.riskCategoryStats.Medium.final()), fill: yellow, align: center), table.cell(context(cvss.riskCategories.at("MEDIUM").state.final()), fill: yellow, align: center),
table.cell(context(cvss.riskCategoryStats.Low.final()), fill: lime, align: center), table.cell(context(cvss.riskCategories.at("LOW").state.final()), fill: lime, align: center),
table.cell(context(cvss.riskCategoryStats.None.final()), fill: white, align: center), table.cell(context(cvss.riskCategories.at("NONE").state.final()), fill: white, align: center),
table.cell(context(cvss.riskCategoryStats.Other.final()), fill: gray, align: center), table.cell(context(cvss.riskCategories.at("OTHER").state.final()), fill: gray, align: center),
) )
== Recommendations & Next Steps == Recommendations & Next Steps