Skip to content
Merged
Show file tree
Hide file tree
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
179 changes: 156 additions & 23 deletions WebContent/WEB-INF/jsp/assessment/history.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,167 @@
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@taglib prefix="bs" uri="/WEB-INF/BootStrapHandler.tld"%>

<style>
.history-row { cursor: pointer; }
.vuln-detail-overlay {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.4); z-index: 1040; display: none;
}
.vuln-detail-panel {
position: fixed; top: 0; right: 0; height: 100%;
width: 640px; max-width: 95%;
background: #fff; z-index: 1050;
box-shadow: -2px 0 12px rgba(0,0,0,0.3);
transform: translateX(100%); transition: transform .25s ease;
display: flex; flex-direction: column;
}
.vuln-detail-panel.open { transform: translateX(0); }
.vuln-detail-panel .vuln-detail-topbar {
padding: 12px 18px; border-bottom: 1px solid rgba(255,255,255,0.15);
display: flex; justify-content: space-between; align-items: center;
background: #225080;
}
.vuln-detail-panel .vuln-detail-topbar h4 { margin: 0; font-size: 18px; color: #fff; }
.vuln-detail-panel .vuln-detail-topbar .btn-box-tool { color: #fff; }
.vuln-detail-panel .vuln-detail-body {
padding: 18px; overflow-y: auto; flex: 1;
background: #0f1522; color: #c9d3e0;
}
.vuln-detail-panel .vuln-detail-body a { color: #6fb0ff; }
.vuln-detail-panel .vuln-detail-section-title {
margin-top: 20px; padding-bottom: 5px; border-bottom: 1px solid rgba(255,255,255,0.15);
font-weight: bold; color: #fff;
}
.vuln-detail-panel .vuln-detail-content { word-wrap: break-word; }
.vuln-detail-panel .vuln-detail-content img { max-width: 100%; height: auto; }
.vuln-detail-panel .vuln-detail-body table { color: #c9d3e0; }
.vuln-detail-panel .vuln-detail-body table td,
.vuln-detail-panel .vuln-detail-body table th { border-color: rgba(255,255,255,0.12); }
.vuln-detail-panel .vuln-detail-meta th { color: #8a97a8; }
</style>

<bs:row>
<bs:mco colsize="12">
<bs:box type="primary" title="Assessment History">
<bs:row>
<bs:mco colsize="12">
<bs:datatable columns="Opened, Vuln, Severity, Assessor, Report" classname="primary" id="history">
<s:iterator value="history">
<tr><td>
<s:date name="opened" format="MM/dd/yyyy"/>
</td>
<td>
<s:property value="vuln"/>
</td>
<td>
<s:property value="severity"/>
</td>
<td>
<s:property value="assessor"/>
</td>
<td>
<a href="../service/Report.pdf?guid=<s:property value="report"/>" target="_blank" >Download Report</a>
</td>

<s:if test="assessment.completed == null">
<div class="row" style="margin-bottom:15px;">
<div class="col-md-12">
<button type="button" id="addOpenFindings" class="btn btn-primary">
<i class="fa fa-plus"></i> Add Open Findings to this Assessment
</button>
</div>
</div>
</s:if>

<div class="nav-tabs-custom">
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item active" role="presentation"><a href="#OpenFindings" data-toggle="tab" role="tab">Open Vulnerabilities (<s:property value="openHistory.size()"/>)</a></li>
<li class="nav-item" role="presentation"><a href="#ClosedFindings" data-toggle="tab" role="tab">Closed Vulnerabilities (<s:property value="closedHistory.size()"/>)</a></li>
</ul>

<div class="tab-content">

<div class="tab-pane active" id="OpenFindings">
<table id="openHistory" class="table table-striped table-hover dataTable">
<thead class="theader">
<tr>
<th>Opened</th>
<th>Vuln</th>
<th>Severity</th>
<th>Assessor</th>
<th>Report</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<s:iterator value="openHistory">
<tr class="history-row" data-vulnid="<s:property value="id"/>">
<td><s:date name="opened" format="MM/dd/yyyy"/></td>
<td><s:property value="vuln"/></td>
<td><s:property value="severity"/></td>
<td><s:property value="assessor"/></td>
<td>
<s:if test="report != null && report != ''">
<a href="../service/Report.pdf?guid=<s:property value="report"/>" target="_blank">Download Report</a>
</s:if>
</td>
<td>
<s:if test="assessment.completed == null">
<s:if test="alreadyAdded">
<span class="text-muted"><i class="fa fa-check"></i> Added</span>
</s:if>
<s:else>
<button type="button" class="btn btn-xs btn-primary add-finding-btn" data-vulnid="<s:property value="id"/>">
<i class="fa fa-plus"></i> Add to Assessment
</button>
</s:else>
</s:if>
</td>
</tr>
</s:iterator>
</tbody>
</table>
</div>

<div class="tab-pane" id="ClosedFindings">
<table id="closedHistory" class="table table-striped table-hover dataTable">
<thead class="theader">
<tr>
<th>Opened</th>
<th>Closed</th>
<th>Vuln</th>
<th>Severity</th>
<th>Assessor</th>
<th>Report</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<s:iterator value="closedHistory">
<tr class="history-row" data-vulnid="<s:property value="id"/>">
<td><s:date name="opened" format="MM/dd/yyyy"/></td>
<td><s:date name="closed" format="MM/dd/yyyy"/></td>
<td><s:property value="vuln"/></td>
<td><s:property value="severity"/></td>
<td><s:property value="assessor"/></td>
<td>
<s:if test="report != null && report != ''">
<a href="../service/Report.pdf?guid=<s:property value="report"/>" target="_blank">Download Report</a>
</s:if>
</td>
<td>
<s:if test="assessment.completed == null">
<s:if test="alreadyAdded">
<span class="text-muted"><i class="fa fa-check"></i> Added</span>
</s:if>
<s:else>
<button type="button" class="btn btn-xs btn-primary add-finding-btn" data-vulnid="<s:property value="id"/>">
<i class="fa fa-plus"></i> Add to Assessment
</button>
</s:else>
</s:if>
</td>
</tr>
</s:iterator>
</tbody>
</table>
</div>

</s:iterator>
</bs:datatable>
</bs:mco>
</bs:row>
</div>
</div>
</bs:box>
</bs:mco>
</bs:row>

<!-- Slide-out panel showing full vulnerability details for the clicked history row -->
<div id="vulnDetailOverlay" class="vuln-detail-overlay"></div>
<div id="vulnDetailPanel" class="vuln-detail-panel">
<div class="vuln-detail-topbar">
<h4>Vulnerability Details</h4>
<button type="button" id="vulnDetailClose" class="btn btn-box-tool" aria-label="Close">
<i class="fa fa-times fa-lg"></i>
</button>
</div>
<div id="vulnDetailBody" class="vuln-detail-body"></div>
</div>
69 changes: 69 additions & 0 deletions WebContent/WEB-INF/jsp/assessment/vulnDetailPanel.jsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<div class="vuln-detail-title">
<h4 style="margin-top:0;"><s:property value="detailVuln.name"/></h4>
<span class="label label-default"><s:property value="detailVuln.tracking"/></span>
<span class="label label-primary"><s:property value="detailVuln.overallStr"/></span>
</div>

<table class="table table-condensed vuln-detail-meta" style="margin-top:15px;">
<tr><th style="width:35%;">Impact</th><td><s:property value="detailVuln.impactStr"/></td></tr>
<tr><th>Likelihood</th><td><s:property value="detailVuln.likelyhoodStr"/></td></tr>
<s:if test="detailVuln.category != null">
<tr><th>Category</th><td><s:property value="detailVuln.category.name"/></td></tr>
</s:if>
<s:elseif test="detailVuln.defaultVuln != null && detailVuln.defaultVuln.category != null">
<tr><th>Category</th><td><s:property value="detailVuln.defaultVuln.category.name"/></td></tr>
</s:elseif>
<tr><th>Section</th><td><s:property value="detailVuln.sectionPretty"/></td></tr>
<s:if test="detailVuln.cvssScore != null && detailVuln.cvssScore != ''">
<tr><th>CVSS Score</th><td><s:property value="detailVuln.cvssScore"/></td></tr>
</s:if>
<s:if test="detailVuln.cvssString != null && detailVuln.cvssString != ''">
<tr><th>CVSS Vector</th><td><s:property value="detailVuln.cvssString"/></td></tr>
</s:if>
<s:if test="detailVuln.opened != null">
<tr><th>Opened</th><td><s:date name="detailVuln.opened" format="MM/dd/yyyy"/></td></tr>
</s:if>
<s:if test="detailVuln.closed != null">
<tr><th>Closed</th><td><s:date name="detailVuln.closed" format="MM/dd/yyyy"/></td></tr>
</s:if>
<s:if test="detailVuln.devClosed != null">
<tr><th>Dev Closed</th><td><s:date name="detailVuln.devClosed" format="MM/dd/yyyy"/></td></tr>
</s:if>
<s:if test="detailAssessment != null">
<tr><th>Assessment</th><td>[<s:property value="detailAssessment.appId"/>] <s:property value="detailAssessment.name"/></td></tr>
<s:if test="detailAssessment.finalReport != null">
<tr><th>Report</th><td><a href="../service/Report.pdf?guid=<s:property value="detailAssessment.finalReport.filename"/>" target="_blank">Download Report</a></td></tr>
</s:if>
</s:if>
</table>

<s:if test="detailVuln.description != null && detailVuln.description != ''">
<h5 class="vuln-detail-section-title">Description</h5>
<div class="vuln-detail-content"><s:property value="detailVuln.description" escapeHtml="false"/></div>
</s:if>

<s:if test="detailVuln.recommendation != null && detailVuln.recommendation != ''">
<h5 class="vuln-detail-section-title">Recommendation</h5>
<div class="vuln-detail-content"><s:property value="detailVuln.recommendation" escapeHtml="false"/></div>
</s:if>

<s:if test="detailVuln.details != null && detailVuln.details != ''">
<h5 class="vuln-detail-section-title">Details</h5>
<div class="vuln-detail-content"><s:property value="detailVuln.details" escapeHtml="false"/></div>
</s:if>

<s:if test="detailVuln.customFields != null && detailVuln.customFields.size() > 0">
<h5 class="vuln-detail-section-title">Custom Fields</h5>
<table class="table table-condensed">
<s:iterator value="detailVuln.customFields">
<tr>
<th style="width:35%;"><s:property value="type.key"/></th>
<td><s:property value="value"/></td>
</tr>
</s:iterator>
</table>
</s:if>
2 changes: 1 addition & 1 deletion WebContent/dist/js/overview.js

Large diffs are not rendered by default.

106 changes: 104 additions & 2 deletions WebContent/src/assessment/overview.js
Original file line number Diff line number Diff line change
Expand Up @@ -495,13 +495,115 @@ $(function() {

//<!-- History section -->
$(function() {
$('#history').DataTable({
var historyTableOpts = {
"paging": true,
"lengthChange": false,
"searching": true,
"ordering": true,
"info": true,
"autoWidth": true
"autoWidth": true,
"columnDefs": [{ "orderable": false, "targets": -1 }]
};
$('#openHistory').DataTable(historyTableOpts);
$('#closedHistory').DataTable(historyTableOpts);

// Recalculate column widths when a (sub)tab becomes visible so tables that
// were rendered while hidden lay out correctly.
$('a[data-toggle="tab"], a[data-toggle="pill"]').on('shown.bs.tab', function() {
$.fn.dataTable.tables({ visible: true, api: true }).columns.adjust();
});

function markFindingAdded(btn) {
$(btn).closest('td').html('<span class="text-muted"><i class="fa fa-check"></i> Added</span>');
}

// Clone a single historical finding into the current assessment.
$(document).on('click', '.add-finding-btn', function() {
var btn = this;
var vulnid = $(btn).data('vulnid');
var data = "id=" + assessmentId;
data += "&vulnid=" + vulnid;
data += "&_token=" + global._token;
$(btn).prop('disabled', true);
$.post("AddHistoryFinding", data).done(function(resp) {
global._token = resp.token;
if (typeof resp.message == "undefined") {
markFindingAdded(btn);
alertMessage(resp, "Finding added to this assessment.");
} else {
$(btn).prop('disabled', false);
alertMessage(resp);
}
});
});

// Clone every open finding from prior assessments into the current one.
$("#addOpenFindings").click(function() {
$.confirm({
title: "Add Open Findings",
content: "Clone all open findings from previous assessments into this assessment?",
type: "blue",
columnClass: 'small',
buttons: {
confirm: function() {
var data = "id=" + assessmentId;
data += "&_token=" + global._token;
$.post("AddHistoryFinding", data).done(function(resp) {
global._token = resp.token;
if (typeof resp.message == "undefined") {
$("#openHistory").find(".add-finding-btn").each(function(i, b) {
markFindingAdded(b);
});
alertMessage(resp, "Open findings added to this assessment.");
} else {
alertMessage(resp);
}
});
},
cancel: function() {}
}
});
});

// Move the detail panel to <body> so position:fixed is relative to the
// viewport regardless of any transformed ancestor.
$("#vulnDetailPanel, #vulnDetailOverlay").appendTo("body");

function openVulnDetail(vulnid) {
$("#vulnDetailBody").html('<div style="text-align:center;padding:40px;"><i class="fa fa-spinner fa-spin fa-2x"></i></div>');
$("#vulnDetailOverlay").show();
$("#vulnDetailPanel").addClass("open");
$.get("HistoryVulnDetail", "vulnid=" + vulnid).done(function(resp) {
if (resp && typeof resp == "object") {
if (resp.token) global._token = resp.token;
$("#vulnDetailBody").html('<div class="alert alert-danger">' + (resp.message || "Unable to load finding details.") + '</div>');
} else {
$("#vulnDetailBody").html(resp);
}
}).fail(function() {
$("#vulnDetailBody").html('<div class="alert alert-danger">Failed to load finding details.</div>');
});
}

function closeVulnDetail() {
$("#vulnDetailPanel").removeClass("open");
$("#vulnDetailOverlay").hide();
}

// Clicking a history row opens the detail panel, but not when the click lands
// on the Add button or a link inside the row.
$(document).on("click", ".history-row", function(e) {
if ($(e.target).closest(".add-finding-btn, a").length)
return;
var vulnid = $(this).data("vulnid");
if (vulnid)
openVulnDetail(vulnid);
});

$(document).on("click", "#vulnDetailClose, #vulnDetailOverlay", closeVulnDetail);
$(document).on("keydown", function(e) {
if (e.key === "Escape")
closeVulnDetail();
});

var colors = ["#8E44AD", "#9B59B6", "#2C3E50", "#34495E", "#95A5A6", "#00a65a", "#39cccc", "#00c0ef", "#f39c12", "#dd4b39"];
Expand Down
Loading
Loading