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) {
// Don't count arbitrary events, we do this independently to avoid backfill logic.
if (progressListing.includes(result.status)) {
email_series_data[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')
]) ])
/*
// Don't count arbitrary events, we do this independently to avoid backfill logic.
if (progressListing.includes(result.status)) {
email_series_data[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){ The createPies boolean is used to allow us to create the pies on load() but not re-create them from calling poll(), as the
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
// We add arbitrary events to the statuses dict and arbitrary event names the progressListing array.
campaign.timeline.forEach(function(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>