diff --git a/addons/cia.typ b/addons/cia.typ index 33116b1..af45643 100644 --- a/addons/cia.typ +++ b/addons/cia.typ @@ -8,6 +8,8 @@ table.cell(str, fill: yellow, align: center) } else if str == "N" { table.cell(str, fill: lime, align: center) + } else if str == "-" { + table.cell(str, fill: white, align: center) } else { panic("Unknown CIA state: " + str) } diff --git a/addons/cvss.typ b/addons/cvss.typ index 8e9e187..92f13e0 100644 --- a/addons/cvss.typ +++ b/addons/cvss.typ @@ -33,58 +33,62 @@ } // Create a small CIA table to be included for every finding -#let createTable(attackVector: "N", attackComplexity: "L", privilegesRequired: "N", userInteraction: "N", scope: "U", confidentiality: "H", integrity: "H", availability: "H") = { +#let createTable(attackVector: "-", attackComplexity: "-", privilegesRequired: "-", userInteraction: "-", scope: "-", confidentiality: "-", integrity: "-", availability: "-") = { // Check values - panicOnInvalid(attackVector, ("N", "A", "L", "P")) - panicOnInvalid(attackComplexity, ("L", "H")) - panicOnInvalid(privilegesRequired, ("N", "L", "H")) - panicOnInvalid(userInteraction, ("N", "R")) - panicOnInvalid(scope, ("U", "C")) - panicOnInvalid(confidentiality, ("H", "L", "N")) - panicOnInvalid(integrity, ("H", "L", "N")) - panicOnInvalid(availability, ("H", "L", "N")) - - // Calculate base result, see https://www.first.org/cvss/v3-1/specification-document#7-1-Base-Metrics-Equations - let issLookup = ("H": 0.56, "L": 0.22, "N": 0) - let attackVectorLookup = ("N": 0.85, "A": 0.62, "L": 0.55, "P": 0.2) - let attackComplexityLookup = ("L": 0.77, "H": 0.44) - let privilegesLookup = ("N": 0.85, "L": if scope == "U" { 0.62 } else { 0.68 }, "H": if scope == "U" { 0.27 } else { 0.5 }) - let userInteractionLookup = ("N": 0.85, "R": 0.62) - let iss = 1 - ((1 - issLookup.at(confidentiality)) * (1 - issLookup.at(integrity)) * (1 - issLookup.at(availability))) - let impact = if scope == "U" { 6.42 * iss } else { 7.52 * (iss - 0.029) - 3.25 * (iss - 0.02)} - let exploitability = 8.22 * attackVectorLookup.at(attackVector) * attackComplexityLookup.at(attackComplexity) * privilegesLookup.at(privilegesRequired) * userInteractionLookup.at(userInteraction) - let baseScore = if impact <= 0 { 0 } else { if scope == "U" { calc.round(calc.min(impact + exploitability, 10), digits: 1) } else { calc.round(calc.min(1.08 * (impact + exploitability), 10), digits: 1) } } + panicOnInvalid(attackVector, ("N", "A", "L", "P", "-")) + panicOnInvalid(attackComplexity, ("L", "H", "-")) + panicOnInvalid(privilegesRequired, ("N", "L", "H", "-")) + panicOnInvalid(userInteraction, ("N", "R", "-")) + panicOnInvalid(scope, ("U", "C", "-")) + panicOnInvalid(confidentiality, ("H", "L", "N", "-")) + panicOnInvalid(integrity, ("H", "L", "N", "-")) + panicOnInvalid(availability, ("H", "L", "N", "-")) let status = "?" - if baseScore >= 9.0 { - status = "CRITICAL" - } else if baseScore >= 7.0 { - status = "HIGH" - } else if baseScore >= 4.0 { - status = "MEDIUM" - } else if baseScore >= 0.1 { - status = "LOW" + if((attackVector, attackComplexity, privilegesRequired, userInteraction, scope, confidentiality, integrity, availability).find(x => x == "-") == none) { + // Calculate base result, see https://www.first.org/cvss/v3-1/specification-document#7-1-Base-Metrics-Equations + let issLookup = ("H": 0.56, "L": 0.22, "N": 0) + let attackVectorLookup = ("N": 0.85, "A": 0.62, "L": 0.55, "P": 0.2) + let attackComplexityLookup = ("L": 0.77, "H": 0.44) + let privilegesLookup = ("N": 0.85, "L": if scope == "U" { 0.62 } else { 0.68 }, "H": if scope == "U" { 0.27 } else { 0.5 }) + let userInteractionLookup = ("N": 0.85, "R": 0.62) + let iss = 1 - ((1 - issLookup.at(confidentiality)) * (1 - issLookup.at(integrity)) * (1 - issLookup.at(availability))) + let impact = if scope == "U" { 6.42 * iss } else { 7.52 * (iss - 0.029) - 3.25 * (iss - 0.02)} + let exploitability = 8.22 * attackVectorLookup.at(attackVector) * attackComplexityLookup.at(attackComplexity) * privilegesLookup.at(privilegesRequired) * userInteractionLookup.at(userInteraction) + let baseScore = if impact <= 0 { 0 } else { if scope == "U" { calc.round(calc.min(impact + exploitability, 10), digits: 1) } else { calc.round(calc.min(1.08 * (impact + exploitability), 10), digits: 1) } } + + if baseScore >= 9.0 { + status = "CRITICAL" + } else if baseScore >= 7.0 { + status = "HIGH" + } else if baseScore >= 4.0 { + status = "MEDIUM" + } else if baseScore >= 0.1 { + status = "LOW" + } else { + status = "NONE" + } } else { - status = "NONE" + // At least one value is unspecified, so this finding will be categorized as "other" and CVSS Score calculation is skipped + status = "OTHER" } - block( - [ - #block( - spacing: 0.4em, - table( - columns: (1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr), - align: center, - stroke: 1pt, - table.cell(colspan: 5)[*Exploitability Metrics*], - table.cell(colspan: 3)[*Impact Metrics*], - table.cell(rowspan: 2, align: bottom)[*#sym.sum*], - [*AV*], [*AC*], [*PR*], [*UI*], [*S*], [*C*], [*I*], [*A*], - attackVector, attackComplexity, privilegesRequired, userInteraction, scope, cia.colorize(confidentiality), cia.colorize(integrity), cia.colorize(availability), coloredCell(status), - ) - ) - #align(right)[ - #text( + stack( + dir: ttb, + table( + columns: (1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr), + align: center, + stroke: 1pt, + table.cell(colspan: 5)[*Exploitability Metrics*], + table.cell(colspan: 3)[*Impact Metrics*], + table.cell(rowspan: 2, align: bottom)[*#sym.sum*], + [*AV*], [*AC*], [*PR*], [*UI*], [*S*], [*C*], [*I*], [*A*], + attackVector, attackComplexity, privilegesRequired, userInteraction, scope, cia.colorize(confidentiality), cia.colorize(integrity), cia.colorize(availability), coloredCell(status), + ), + v(.25em), + align( + right, + text( size: 10pt, fill: gray, "CVSS:3.1/AV:" + attackVector + @@ -95,8 +99,8 @@ "/C:" + confidentiality + "/I:" + integrity + "/A:" + availability - )] - ] + ) + ) ) updateRiskCategoryStats(status) diff --git a/findings.typ b/findings.typ index 033bb90..48e0840 100644 --- a/findings.typ +++ b/findings.typ @@ -6,7 +6,16 @@ == Administration Interfaces reachable -#cvss.createTable(confidentiality: "N", integrity: "N", availability: "N") +#cvss.createTable( + attackVector: "N", + attackComplexity: "L", + privilegesRequired: "N", + userInteraction: "N", + scope: "U", + confidentiality: "N", + integrity: "N", + availability: "N", +) === Description