Added support for pie charts for arbitrary events

pull/1929/head
Glenn Wilkinson 2020-08-08 20:34:38 +01:00
parent f212c13375
commit f5509a56c2
2 changed files with 144 additions and 26 deletions

View File

@ -1,5 +1,7 @@
var map = null var map = null
var doPoll = true; var doPoll = true;
var arbEventsPieCharts = true; // Include pie charts for arbitary events or not
// Setting to true will add the arb events to statusMapping as wel as adding HTML chart elements.
// statuses is a helper map to point result statuses to ui classes // statuses is a helper map to point result statuses to ui classes
var statuses = { var statuses = {
@ -104,6 +106,7 @@ var statusMapping = {
"Clicked Link": "clicked", "Clicked Link": "clicked",
"Submitted Data": "submitted_data", "Submitted Data": "submitted_data",
"Email Reported": "reported", "Email Reported": "reported",
//"Opened Word Document" : "opened_word_document"
} }
// This is an underwhelming attempt at an enum // This is an underwhelming attempt at an enum
@ -693,7 +696,7 @@ function poll() {
.success(function (c) { .success(function (c) {
campaign = c campaign = c
updateArbitraryEventData(campaign) // Update data structures with new arbitrary event specifications updateArbitraryEventData(campaign, false) // Update data structures with new arbitrary event specifications
/* Update the timeline */ /* Update the timeline */
var timeline_series_data = [] var timeline_series_data = []
@ -725,13 +728,20 @@ function poll() {
data: timeline_series_data data: timeline_series_data
}) })
/* Update the results donut chart */ /* Update the results donut chart */
var email_series_data = {} //var email_series_data = {}
// Load the initial data // Load the initial data
Object.keys(statusMapping).forEach(function (k) { //Object.keys(statusMapping).forEach(function (k) {
email_series_data[k] = 0 // email_series_data[k] = 0
}); //});
/*
$.each(campaign.results, function (i, result) { $.each(campaign.results, function (i, result) {
email_series_data[result.status]++;
// Don't count arbitrary events, we do this independently to avoid backfill logic.
if (progressListing.includes(result.status)) {
email_series_data[result.status]++;
}
if (result.reported) { if (result.reported) {
email_series_data['Email Reported']++ email_series_data['Email Reported']++
} }
@ -740,7 +750,12 @@ function poll() {
for (var i = 0; i < step; i++) { for (var i = 0; i < step; i++) {
email_series_data[progressListing[i]]++ email_series_data[progressListing[i]]++
} }
}) })*/
// New function for counting events. Doesn't handle backfill, yet.
email_series_data = countCampaignEvents(campaign)
$.each(email_series_data, function (status, count) { $.each(email_series_data, function (status, count) {
var email_data = [] var email_data = []
if (!(status in statusMapping)) { if (!(status in statusMapping)) {
@ -801,7 +816,7 @@ function load() {
campaign = c campaign = c
if (campaign) { if (campaign) {
updateArbitraryEventData(campaign) // Update data structures with new arbitrary event specifications updateArbitraryEventData(campaign, true) // Update data structures with new arbitrary event specifications
$("title").text(c.name + " - Gophish") $("title").text(c.name + " - Gophish")
$("#loading").hide() $("#loading").hide()
@ -865,11 +880,12 @@ function load() {
] ]
}); });
resultsTable.clear(); resultsTable.clear();
var email_series_data = {} //var email_series_data = {}
var timeline_series_data = [] var timeline_series_data = []
Object.keys(statusMapping).forEach(function (k) { //Object.keys(statusMapping).forEach(function (k) {
email_series_data[k] = 0 // email_series_data[k] = 0
}); //});
$.each(campaign.results, function (i, result) { $.each(campaign.results, function (i, result) {
resultsTable.row.add([ resultsTable.row.add([
@ -883,16 +899,34 @@ function load() {
result.reported, result.reported,
moment(result.send_date).format('MMMM Do YYYY, h:mm:ss a') moment(result.send_date).format('MMMM Do YYYY, h:mm:ss a')
]) ])
email_series_data[result.status]++;
/*
// Don't count arbitrary events, we do this independently to avoid backfill logic.
if (progressListing.includes(result.status)) {
email_series_data[result.status]++;
}
if (result.reported) { if (result.reported) {
email_series_data['Email Reported']++ email_series_data['Email Reported']++
} }
//TODO: At some point need to figure out backfilling with arbitrary events
// Possibly just backfill Email sent and Email Opened before getting into more complex
// data structures
// Backfill status values // Backfill status values
var step = progressListing.indexOf(result.status) var step = progressListing.indexOf(result.status)
for (var i = 0; i < step; i++) { for (var i = 0; i < step; i++) {
email_series_data[progressListing[i]]++ email_series_data[progressListing[i]]++
} }
*/
}) })
// New function for counting events. Doesn't handle backfill, yet.
email_series_data = countCampaignEvents(campaign)
resultsTable.draw(); resultsTable.draw();
// Setup tooltips // Setup tooltips
$('[data-toggle="tooltip"]').tooltip() $('[data-toggle="tooltip"]').tooltip()
@ -1041,16 +1075,27 @@ function report_mail(rid, cid) {
/* updateArbitraryData will go through the supplied campaign and add arbitrary event data to three data structure: /* updateArbitraryData will go through the supplied campaign and add arbitrary event data to three data structure:
statuses statuses
statusMapping //TODO statusMapping
progressListing progressListing // Todo, needs more consideration on backfill
*/
function updateArbitraryEventData(campaign){
// We add arbitrary events to the statuses dict and arbitrary event names the progressListing array. The createPies boolean is used to allow us to create the pies on load() but not re-create them from calling poll(), as the
campaign.timeline.forEach(function(event) { highchart info gets overwritten. The problem with this is that if a new arbitrary event comes in while the page is loaded
the poll() won't add the pie. Need to investigate this. TODO
*/
function updateArbitraryEventData(campaign, createPies){
var arbEventNames = [] // Hold unique arb event names. Used to create HTML pie charts if arbEventPieCharts set to true
campaign.timeline.forEach(function(event) { // Step over each event
if (event.message == "Arbitrary Event") { if (event.message == "Arbitrary Event") {
details = JSON.parse(event.details) // TODO Validate this exists details = JSON.parse(event.details) // TODO Validate this exists
// 1. Add title, color, icon, and label properties to statuses dict
title = "Arbitrary Event" title = "Arbitrary Event"
if ("title" in details.payload){ if ("title" in details.payload){
title = String(details.payload.title) title = String(details.payload.title)
@ -1058,7 +1103,6 @@ function updateArbitraryEventData(campaign){
} }
statuses[title] = {"arbitrary event" : 1} // Set true to be arbitrary event, just so we can discern if we need to statuses[title] = {"arbitrary event" : 1} // Set true to be arbitrary event, just so we can discern if we need to
statuses[title]["color"] = "#00FFFF" // Default statuses[title]["color"] = "#00FFFF" // Default
if ("color" in details.payload ){ if ("color" in details.payload ){
color = String(details.payload.color) color = String(details.payload.color)
@ -1080,19 +1124,87 @@ function updateArbitraryEventData(campaign){
statuses[title]["label"] = label statuses[title]["label"] = label
} }
// Add the title to the progressListing array (if it's not already in there) if (!arbEventNames.includes(title)){
if (!progressListing.includes(title)) { arbEventNames.push(title)
progressListing.push(title)
} }
/* How to handle progressListing needs more thought, and probably */
// Add the title to the progressListing array (if it's not already in there)
//if (!progressListing.includes(title)) {
// progressListing.push(title)
//}
} }
}) })
// We add the arbitary event titles to the progress listing // 2.0 If arbEventsPieChart is enabled we add to statusMapping and add HTML charts for the event
// Small problem here is that the ordering will assume anything appended is more serious than 'data submitted' if (arbEventsPieCharts == true && createPies == true) {
//progressListing = progressListing.concat(Object.keys(tmpTitles))
//2.1 Create HTML elements
// Split the array into multiple arrays, each of size 5. This let's us create pie chart rows of five
arbEventNames.sort()
chunkedArbEvents = Array.from({ length: Math.ceil(arbEventNames.length / 5) }, (v, i) => arbEventNames.slice(i * 5, i * 5 + 5) );
$("#arbpie").html('') // i. Clear the div class
html = ''
chunkedArbEvents.forEach(function(chunk){
rowhtml = '<div class="row">\n\t<div style="height:200px;" class="col-lg-1 col-md-1"></div>\n'
chunk.forEach(function(title){
sanitizedEventName = title.toLowerCase().replace(/ /g, "_") // Convert Opened Word Document to opened_word_document.
sanitizedEventName = escapeHtml(sanitizedEventName) // Should maybe do more tests on this. Or even use a short random string rather than the name. e.g {"Opened Word Document" : "7a2f87"}
//i. Add the HTML element
rowhtml += '\t<div id="' + sanitizedEventName + '_chart" style="height:200px;" class="col-lg-2 col-md-2"></div>\n'
//ii. Add to statusMapping
statusMapping[title] = sanitizedEventName
})
rowhtml += '\t<div style="height:200px;" class="col-lg-1 col-md-1"></div>\n</div>\n'
html += rowhtml
})
$("#arbpie").html(html)
}
}
// countCampaignEvents will return a dict of title:count of arbitrary and regular events from a campaign
// Todo: Need to implement backfill logic
function countCampaignEvents(campaign) {
// Add all the default events to a counter dict
eventsCounter = {}
Object.keys(statusMapping).forEach(function (k) {
eventsCounter[k] = 0
});
campaign.timeline.forEach(function(event){
if (event.message == "Arbitrary Event"){
details = JSON.parse(event.details)
title = details.payload.title[0]
} else {
title = event.message
// Backfill logic for non arbitrary events. Todo
}
if (title in eventsCounter) {
eventsCounter[title] += 1
} else {
eventsCounter[title] = 1
}
// Backfill logic here for arb?
})
return eventsCounter
} }
$(document).ready(function () { $(document).ready(function () {

View File

@ -54,8 +54,14 @@
<div id="clicked_chart" style="height:200px;" class="col-lg-2 col-md-2"></div> <div id="clicked_chart" style="height:200px;" class="col-lg-2 col-md-2"></div>
<div id="submitted_data_chart" style="height:200px;" class="col-lg-2 col-md-2"></div> <div id="submitted_data_chart" style="height:200px;" class="col-lg-2 col-md-2"></div>
<div id="reported_chart" style="height:200px;" class="col-lg-2 col-md-2"></div> <div id="reported_chart" style="height:200px;" class="col-lg-2 col-md-2"></div>
<!--<div id="opened_word_document_chart" style="height:200px;" class="col-lg-2 col-md-2" hidden></div>-->
<div style="height:200px;" class="col-lg-1 col-md-1"></div> <div style="height:200px;" class="col-lg-1 col-md-1"></div>
</div> </div>
<!-- Add additional rows for arb events here: -->
<div id="arbpie"></div>
<div class="row" id="resultsMapContainer"> <div class="row" id="resultsMapContainer">
<div class="col-md-6"> <div class="col-md-6">
<p style="text-align:center;">Targets Map</p> <p style="text-align:center;">Targets Map</p>