Skip to content

Commit

Permalink
Add support for CVSS v4
Browse files Browse the repository at this point in the history
Reuses the same score and vector fields as V3. The calculator has a
button to switch between V3 and V4, and will automatically switch based
on the CVSS vector on page loaded or if one is put into the field.
  • Loading branch information
ColonelThirtyTwo committed Aug 28, 2024
1 parent 041376a commit 4622436
Show file tree
Hide file tree
Showing 7 changed files with 1,067 additions and 75 deletions.
12 changes: 6 additions & 6 deletions ghostwriter/reporting/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,16 +193,16 @@ class Finding(models.Model):
help_text="Provide notes for your team that describes how the finding is intended to be used or edited during editing",
)
cvss_score = models.FloatField(
"CVSS Score v3.0",
"CVSS Score",
blank=True,
null=True,
help_text="Set the CVSS score for this finding",
)
cvss_vector = models.CharField(
"CVSS Vector v3.0",
"CVSS Vector",
blank=True,
default="",
max_length=54,
max_length=255,
help_text="Set the CVSS vector for this finding",
)
tags = TaggableManager(blank=True)
Expand Down Expand Up @@ -641,16 +641,16 @@ class ReportFindingLink(models.Model):
help_text="Assign the task of editing this finding to a specific operator - defaults to the operator that added it to the report",
)
cvss_score = models.FloatField(
"CVSS Score v3.0",
"CVSS Score",
blank=True,
null=True,
help_text="Set the CVSS score for this finding",
)
cvss_vector = models.CharField(
"CVSS Vector v3.0",
"CVSS Vector",
blank=True,
default="",
max_length=54,
max_length=255,
help_text="Set the CVSS vector for this finding",
)
extra_fields = models.JSONField(default=dict)
Expand Down
30 changes: 29 additions & 1 deletion ghostwriter/reporting/templates/snippets/cvss_scripts.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% load static %}

<!-- CVSS JS & CSS -->
<!-- CVSS V3 Calculator -->
<!--
Copyright (c) 2019, FIRST.ORG, INC.
All rights reserved.
Expand All @@ -22,6 +22,34 @@
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<!-- CVSS V4 Calculator -->
<!--
Copyright (c) 2023 FIRST.ORG, Inc., Red Hat, and contributors
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->

<link rel="stylesheet" href="{% static 'css/cvss_styles.css' %}">
<script src="{% static 'js/cvss/v3_calc.js' %}"></script>
<script src="{% static 'js/cvss/v4_score.js' %}"></script>
<script src="{% static 'js/cvss/v4_vector.js' %}"></script>
<script src="{% static 'js/cvss/ui.js' %}"></script>
131 changes: 115 additions & 16 deletions ghostwriter/reporting/templates/snippets/cvss_ui.html

Large diffs are not rendered by default.

45 changes: 14 additions & 31 deletions ghostwriter/static/css/cvss_styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,19 @@
margin: 0.5em 0;
}

.cvss-calculator h2 {
font-size: 2em;
margin: 0;
padding: 0;
}

.cvss-calculator h3 {
font-size: 1.5em;
margin: 0;
padding: 0;
}

.cvss-calculator h4 {
font-size: 1em;
margin: 0;
padding: 0;
Expand Down Expand Up @@ -63,24 +75,6 @@
color: #ffffff;
}

#vector {
margin: 0 1em;
padding: 0;
}

#vectorString {
display: none;
border: 0;
padding: 0;
margin: 0;
background-color: #090;
color: #ffffff;
font-weight: bold;
font-size: 0.8em;
width: 80em;
max-width: 100%;
}

.scoreRating {
position: absolute;
top: -36px;
Expand Down Expand Up @@ -163,28 +157,17 @@
color: var(--info-rating-color);
}

#baseMetricScore,
#temporalMetricScore,
#environmentalMetricScore {
.baseMetricScore {
display: block;
font-size: 32px;
line-height: 32px;
font-weight: normal;
margin-top: 4px;
}

#baseSeverity,
#temporalSeverity,
#environmentalSeverity {
.baseSeverity {
font-size: 16px;
font-weight: normal;
margin-bottom: 5px;
display: block;
}

div#scriptWarning {
border: solid red 2px;
background: #f5dddd;
padding: 1em 1em 1em 1em;
margin: 0.4em 0;
}
110 changes: 89 additions & 21 deletions ghostwriter/static/js/cvss/ui.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,66 @@
(function() {

function setCvssBadge(score) {
function setCvssBadge(version, score) {
const severity = CVSS.severityRating(score);
document.getElementById("scoreRating").className = "scoreRating " + severity.toLowerCase();
document.getElementById("baseMetricScore").textContent = score;
document.getElementById("baseSeverity").textContent = "(" + severity + ")";
document.querySelector(`#cvss-${version}-calculator .scoreRating`).className = "scoreRating " + severity.toLowerCase();
document.querySelector(`#cvss-${version}-calculator .baseMetricScore`).textContent = score;
document.querySelector(`#cvss-${version}-calculator .baseSeverity`).textContent = "(" + severity + ")";
}

function showCalculatorVersion(version) {
if(version === "v3") {
document.getElementById("cvss-v3-calculator").style.removeProperty("display");
document.getElementById("cvss-v4-calculator").style.display = "none";
} else if(version === "v4") {
document.getElementById("cvss-v3-calculator").style.display = "none";
document.getElementById("cvss-v4-calculator").style.removeProperty("display");
} else {
throw new Error("Invalid version (this is a bug): " + version)
}
}

const V3_FIELDS = ["AV", "AC", "PR", "UI", "S", "C", "I", "A"];
const V4_FIELDS = ["AV", "AC", "AT", "PR", "UI", "VC", "VI", "VA", "SC", "SI", "SA"];

function CVSSV3CalcFromVector(setScore) {
const cvssSelected = CVSS.parseVector(document.getElementById("id_cvss_vector").value);
if(!cvssSelected)
return;
function onVectorChanged(setScore) {
const vectorStr = document.getElementById("id_cvss_vector").value

const cvssv3Selected = CVSS.parseVector(vectorStr);
if(cvssv3Selected) {
const score = CVSS.calculateCVSSFromObject(cvssv3Selected).baseMetricScore;
setCvssBadge("v3", score);
if(setScore) {
// Set score when editing the vector but not when loading
document.getElementById('id_cvss_score').value = score;
}

const score = CVSS.calculateCVSSFromObject(cvssSelected);
setCvssBadge(score.baseMetricScore);
if(setScore) {
// Set score when editing the vector but not when loading
document.getElementById('id_cvss_score').value = baseMetricScore;
// Populate buttons
for (const name of V3_FIELDS) {
document.querySelector(`input[name="cvssv3_${name}"][value="${cvssv3Selected[name]}"]`).checked = true;
}
showCalculatorVersion("v3");
return;
}

// Populate buttons
for (const name of V3_FIELDS) {
document.querySelector(`input[name="cvssv3_${name}"][value="${cvssSelected[name]}"]`).checked = true;
const cvssv4Selected = cvssv4FromVector(vectorStr);
if(cvssv4Selected) {
const score = cvssv4Score(cvssv4Selected);
setCvssBadge("v4", score);
if(setScore) {
// Set score when editing the vector but not when loading
document.getElementById('id_cvss_score').value = score;
}

// Populate buttons
for (const name of V4_FIELDS) {
document.querySelector(`input[name="cvssv4_${name}"][value="${cvssv4Selected[name]}"]`).checked = true;
}
showCalculatorVersion("v4");
return;
}
}

function CVSSV3AutoCalc() {
function onV3ButtonChanged() {
const cvssSelected = {};
for(const name of V3_FIELDS) {
const selected = document.querySelector(`input[name="cvssv3_${name}"]:checked`);
Expand All @@ -42,18 +75,53 @@
}

document.getElementById('id_cvss_score').value = output.baseMetricScore;
setCvssBadge(output.baseMetricScore);
setCvssBadge("v3", output.baseMetricScore);
document.getElementById('id_cvss_vector').value = output.vectorString;
}

function onV4ButtonChanged() {
const cvssSelected = {};
for (const name of V4_FIELDS) {
const selected = document.querySelector(`input[name="cvssv4_${name}"]:checked`);
if (!selected)
return;
cvssSelected[name] = selected.value;
}
// Fill in optional metrics that we don't show
for (const [name, _] of cvssv4_expectedMetricOrder.slice(11)) {
cvssSelected[name] = "X";
}

const score = cvssv4Score(cvssSelected);
const vector = cvssv4ToVector(cvssSelected);

document.getElementById('id_cvss_score').value = score;
setCvssBadge("v4", score);
document.getElementById('id_cvss_vector').value = vector;
}

$(document).ready(function() {
CVSSV3CalcFromVector(false);
document.getElementById("id_cvss_vector").addEventListener("input", () => CVSSV3CalcFromVector(true));
onVectorChanged(false);
document.getElementById("id_cvss_vector").addEventListener("input", () => onVectorChanged(true));

for(const field of V3_FIELDS) {
for(const el of document.querySelectorAll(`input[name="cvssv3_${field}"]`)) {
el.addEventListener("input", () => CVSSV3AutoCalc());
el.addEventListener("input", () => onV3ButtonChanged());
}
}
for(const field of V4_FIELDS) {
for(const el of document.querySelectorAll(`input[name="cvssv4_${field}"]`)) {
el.addEventListener("input", () => onV4ButtonChanged());
}
}

document.querySelector("#cvss-v3-calculator button.cvss-switch").addEventListener("click", ev => {
ev.preventDefault();
showCalculatorVersion("v4");
});
document.querySelector("#cvss-v4-calculator button.cvss-switch").addEventListener("click", ev => {
ev.preventDefault();
showCalculatorVersion("v3");
});
});
})();
Loading

0 comments on commit 4622436

Please sign in to comment.