From bddb9fdb283c3e3f5af44d46386468e4ca225ab3 Mon Sep 17 00:00:00 2001 From: Jordan Wright Date: Wed, 5 Apr 2017 22:20:00 -0500 Subject: [PATCH] Changing UI Refresh to a Longer Poll + Manual Refresh (#587) * Added refresh button and reduced lag for large campaigns (#565) * Amended refresh function to allow manual refreshes Moved the function out of document.ready and tied it to the button. Also reduced automatic refresh to 60 seconds, to make it easier for medium - large campaigns to be viewed without lagging. * Amended refresh function to allow manual refreshes * Update campaign_results.html * Re-building the campaign_results JS --- static/js/dist/app/campaign_results.min.js | 2 +- static/js/src/app/campaign_results.js | 1391 ++++++++++---------- templates/campaign_results.html | 3 + 3 files changed, 704 insertions(+), 692 deletions(-) diff --git a/static/js/dist/app/campaign_results.min.js b/static/js/dist/app/campaign_results.min.js index a685f661..4ca745ad 100644 --- a/static/js/dist/app/campaign_results.min.js +++ b/static/js/dist/app/campaign_results.min.js @@ -1 +1 @@ -function dismiss(){$("#modal\\.flashes").empty(),$("#modal").modal("hide"),$("#resultsTable").dataTable().DataTable().clear().draw()}function deleteCampaign(){swal({title:"Are you sure?",text:"This will delete the campaign. This can't be undone!",type:"warning",animation:!1,showCancelButton:!0,confirmButtonText:"Delete Campaign",confirmButtonColor:"#428bca",reverseButtons:!0,allowOutsideClick:!1,preConfirm:function(){return new Promise(function(e,t){api.campaignId.delete(campaign.id).success(function(t){e()}).error(function(e){t(e.responseJSON.message)})})}}).then(function(){swal("Campaign Deleted!","This campaign has been deleted!","success"),$('button:contains("OK")').on("click",function(){location.href="/campaigns"})})}function completeCampaign(){swal({title:"Are you sure?",text:"Gophish will stop processing events for this campaign",type:"warning",animation:!1,showCancelButton:!0,confirmButtonText:"Complete Campaign",confirmButtonColor:"#428bca",reverseButtons:!0,allowOutsideClick:!1,preConfirm:function(){return new Promise(function(e,t){api.campaignId.complete(campaign.id).success(function(t){e()}).error(function(e){t(e.responseJSON.message)})})}}).then(function(){swal("Campaign Completed!","This campaign has been completed!","success"),$("#complete_button")[0].disabled=!0,$("#complete_button").text("Completed!"),doPoll=!1})}function exportAsCSV(e){exportHTML=$("#exportButton").html();var t=null;switch(e){case"results":t=campaign.results;break;case"events":t=campaign.timeline}if(t){$("#exportButton").html('');var a=Papa.unparse(t,{}),i=new Blob([a],{type:"text/csv;charset=utf-8;"});if(navigator.msSaveBlob)navigator.msSaveBlob(i,e+".csv");else{var s=window.URL.createObjectURL(i),l=document.createElement("a");l.href=s,l.setAttribute("download",e+".csv"),document.body.appendChild(l),l.click(),document.body.removeChild(l)}$("#exportButton").html(exportHTML)}}function replay(e){function t(){form.attr({action:url}),form.appendTo("body").submit().remove()}request=campaign.timeline[e],details=JSON.parse(request.details),url=null,form=$("
").attr({method:"POST",target:"_blank"}),$.each(Object.keys(details.payload),function(e,t){return"rid"==t||("__original_url"==t?(url=details.payload[t],!0):void $("").attr({name:t}).val(details.payload[t]).appendTo(form))}),swal({title:"Where do you want the credentials submitted to?",input:"text",showCancelButton:!0,inputPlaceholder:"http://example.com/login",inputValue:url||"",inputValidator:function(e){return new Promise(function(t,a){e?t():a("Invalid URL.")})}}).then(function(e){url=e,t()})}function renderTimeline(e){return record={first_name:e[2],last_name:e[3],email:e[4],position:e[5]},results='
Timeline for '+escapeHtml(record.first_name)+" "+escapeHtml(record.last_name)+'
Email: '+escapeHtml(record.email)+'
',$.each(campaign.timeline,function(e,t){t.email&&t.email!=record.email||(results+='
',results+='
'+escapeHtml(t.message)+' '+moment(t.time).format("MMMM Do YYYY h:mm a")+"",t.details&&("Submitted Data"==t.message&&(results+='
',results+='
View Details
'),details=JSON.parse(t.details),details.payload&&(results+='
',results+=' ',results+=" ",$.each(Object.keys(details.payload),function(e,t){return"rid"==t||(results+=" ",results+=" ",results+=" ",void(results+=" "))}),results+="
ParameterValue(s)
"+escapeHtml(t)+""+escapeHtml(details.payload[t])+"
",results+="
"),details.error&&(results+='
View Details
',results+='
',results+='Error '+details.error,results+="
")),results+="
")}),results+="
",results}function poll(){api.campaignId.results(campaign.id).success(function(e){campaign=e;var t={series:[{name:"Events",data:[]}]};$.each(campaign.timeline,function(e,a){t.series[0].data.push({meta:e,x:new Date(a.time),y:1})});var a=$("#timeline_chart");a.get(0).__chartist__&&a.get(0).__chartist__.update(t);var i={series:[]},s={};$.each(campaign.results,function(e,t){s[t.status]?s[t.status]++:s[t.status]=1}),$("#email_chart_legend").html(""),$.each(s,function(e,t){i.series.push({meta:e,value:t}),$("#email_chart_legend").append('
  • '+e+"
  • ")});var l=$("#email_chart");l.get(0).__chartist__&&(l.get(0).__chartist__.on("draw",function(e){e.element.addClass(statuses[e.meta].slice)}),l.get(0).__chartist__.update(i)),resultsTable=$("#resultsTable").DataTable(),resultsTable.rows().every(function(e,t,a){var i=this.row(e),s=i.data(),l=s[0];$.each(campaign.results,function(t,a){if(a.id==l){var n=statuses[a.status].label||"label-default";return s[6]=''+a.status+"",resultsTable.row(e).data(s).draw(!1),i.child.isShown()&&i.child(renderTimeline(i.data())),!1}})}),bubbles=[],$.each(campaign.results,function(e,t){return 0==t.latitude&&0==t.longitude||(newIP=!0,$.each(bubbles,function(e,a){if(a.ip==t.ip)return bubbles[e].radius+=1,newIP=!1,!1}),void(newIP&&bubbles.push({latitude:t.latitude,longitude:t.longitude,name:t.ip,fillKey:"point",radius:2})))}),map.bubbles(bubbles)})}function load(){campaign.id=window.location.pathname.split("/").slice(-1)[0],api.campaignId.results(campaign.id).success(function(e){if(campaign=e){$("title").text(e.name+" - Gophish"),$("#loading").hide(),$("#campaignResults").show(),$("#page-title").text("Results for "+e.name),"Completed"==e.status&&($("#complete_button")[0].disabled=!0,$("#complete_button").text("Completed!"),doPoll=!1),$('[data-toggle="tooltip"]').tooltip(),$("#resultsTable").on("click",".timeline-event-details",function(){payloadResults=$(this).parent().find(".timeline-event-results"),payloadResults.is(":visible")?($(this).find("i").removeClass("fa-caret-down"),$(this).find("i").addClass("fa-caret-right"),payloadResults.hide()):($(this).find("i").removeClass("fa-caret-right"),$(this).find("i").addClass("fa-caret-down"),payloadResults.show())});var t={series:[{name:"Events",data:[]}]},a={series:[]},i={},s={axisX:{showGrid:!1,type:Chartist.FixedScaleAxis,divisor:5,labelInterpolationFnc:function(e){return moment(e).format("MMMM Do YYYY h:mm a")}},axisY:{type:Chartist.FixedScaleAxis,ticks:[0,1,2],low:0,showLabel:!1},showArea:!1,plugins:[]},l={donut:!0,donutWidth:40,chartPadding:0,showLabel:!1};resultsTable=$("#resultsTable").DataTable({destroy:!0,order:[[2,"asc"]],columnDefs:[{orderable:!1,targets:"no-sort"},{className:"details-control",targets:[1]},{visible:!1,targets:[0]}]}),resultsTable.clear(),$.each(campaign.results,function(e,t){label=statuses[t.status].label||"label-default",resultsTable.row.add([t.id,'',escapeHtml(t.first_name)||"",escapeHtml(t.last_name)||"",escapeHtml(t.email)||"",escapeHtml(t.position)||"",''+t.status+""]).draw(),i[t.status]?i[t.status]++:i[t.status]=1}),$("#resultsTable tbody").on("click","td.details-control",function(){var e=$(this).closest("tr"),t=resultsTable.row(e);t.child.isShown()?(t.child.hide(),e.removeClass("shown"),$(this).find("i").removeClass("fa-caret-down"),$(this).find("i").addClass("fa-caret-right"),t.invalidate("dom").draw(!1)):($(this).find("i").removeClass("fa-caret-right"),$(this).find("i").addClass("fa-caret-down"),t.child(renderTimeline(t.data())).show(),e.addClass("shown"),t.invalidate("dom").draw(!1))}),$.each(campaign.timeline,function(e,a){t.series[0].data.push({meta:e,x:new Date(a.time),y:1})}),$("#email_chart_legend").html(""),$.each(i,function(e,t){a.series.push({meta:e,value:t}),$("#email_chart_legend").append('
  • '+e+"
  • ")});var n=new Chartist.Line("#timeline_chart",t,s);n.on("draw",function(e){if("point"===e.type){var t=statuses[campaign.timeline[e.meta].message].point,a=new Chartist.Svg("circle",{cx:[e.x],cy:[e.y],r:5,fill:"#283F50",meta:e.meta,value:1},t+" ct-timeline-point");e.element.replace(a)}}),$chart=$("#timeline_chart");var o=$chart.append('
    ').find(".chartist-tooltip").hide();$chart.on("mouseenter",".ct-timeline-point",function(){var e=$(this);cidx=e.attr("meta"),html="Event: "+campaign.timeline[cidx].message,campaign.timeline[cidx].email&&(html+="
    Email: "+escapeHtml(campaign.timeline[cidx].email)),o.html(html).show()}),$chart.on("mouseleave",".ct-timeline-point",function(){o.hide()}),$chart.on("mousemove",function(e){o.css({left:(e.offsetX||e.originalEvent.layerX)-o.width()/2-10,top:(e.offsetY+70||e.originalEvent.layerY)-o.height()-40})});var r=new Chartist.Pie("#email_chart",a,l);r.on("draw",function(e){e.element.addClass(statuses[e.meta].slice)}),$piechart=$("#email_chart");var c=$piechart.append('
    ').find(".chartist-tooltip").hide();$piechart.on("mouseenter",".ct-slice-donut",function(){var e=$(this);value=e.attr("ct:value"),label=e.attr("ct:meta"),c.html(label+": "+value.toString()).show()}),$piechart.on("mouseleave",".ct-slice-donut",function(){c.hide()}),$piechart.on("mousemove",function(e){c.css({left:(e.offsetX||e.originalEvent.layerX)-c.width()/2-10,top:(e.offsetY+40||e.originalEvent.layerY)-c.height()-80})}),map||(map=new Datamap({element:document.getElementById("resultsMap"),responsive:!0,fills:{defaultFill:"#ffffff",point:"#283F50"},geographyConfig:{highlightFillColor:"#1abc9c",borderColor:"#283F50"},bubblesConfig:{borderColor:"#283F50"}})),$.each(campaign.results,function(e,t){return 0==t.latitude&&0==t.longitude||(newIP=!0,$.each(bubbles,function(e,a){if(a.ip==t.ip)return bubbles[e].radius+=1,newIP=!1,!1}),void(newIP&&bubbles.push({latitude:t.latitude,longitude:t.longitude,name:t.ip,fillKey:"point",radius:2})))}),map.bubbles(bubbles)}$('a[data-toggle="tab"]').on("shown.bs.tab",function(e){"#overview"==$(e.target).attr("href")&&(map||(map=new Datamap({element:document.getElementById("resultsMap"),responsive:!0,fills:{defaultFill:"#ffffff"},geographyConfig:{highlightFillColor:"#1abc9c",borderColor:"#283F50"}})))})}).error(function(){$("#loading").hide(),errorFlash(" Campaign not found!")})}var map=null,doPoll=!0,statuses={"Email Sent":{slice:"ct-slice-donut-sent",legend:"ct-legend-sent",label:"label-success",icon:"fa-envelope",point:"ct-point-sent"},"Email Opened":{slice:"ct-slice-donut-opened",legend:"ct-legend-opened",label:"label-warning",icon:"fa-envelope",point:"ct-point-opened"},"Clicked Link":{slice:"ct-slice-donut-clicked",legend:"ct-legend-clicked",label:"label-clicked",icon:"fa-mouse-pointer",point:"ct-point-clicked"},Success:{slice:"ct-slice-donut-success",legend:"ct-legend-success",label:"label-danger",icon:"fa-exclamation",point:"ct-point-clicked"},Error:{slice:"ct-slice-donut-error",legend:"ct-legend-error",label:"label-default",icon:"fa-times",point:"ct-point-error"},"Error Sending Email":{slice:"ct-slice-donut-error",legend:"ct-legend-error",label:"label-default",icon:"fa-times",point:"ct-point-error"},"Submitted Data":{slice:"ct-slice-donut-success",legend:"ct-legend-success",label:"label-danger",icon:"fa-exclamation",point:"ct-point-clicked"},Unknown:{slice:"ct-slice-donut-error",legend:"ct-legend-error",label:"label-default",icon:"fa-question",point:"ct-point-error"},Sending:{slice:"ct-slice-donut-sending",legend:"ct-legend-sending",label:"label-primary",icon:"fa-spinner",point:"ct-point-sending"},"Campaign Created":{label:"label-success",icon:"fa-rocket"}},campaign={},bubbles=[];$(document).ready(function(){function e(){doPoll&&($("#refresh_message").show(),poll(),$("#refresh_message").hide(),setTimeout(e,1e4))}load(),setTimeout(e,1e4)}); \ No newline at end of file +function dismiss(){$("#modal\\.flashes").empty(),$("#modal").modal("hide"),$("#resultsTable").dataTable().DataTable().clear().draw()}function deleteCampaign(){swal({title:"Are you sure?",text:"This will delete the campaign. This can't be undone!",type:"warning",animation:!1,showCancelButton:!0,confirmButtonText:"Delete Campaign",confirmButtonColor:"#428bca",reverseButtons:!0,allowOutsideClick:!1,preConfirm:function(){return new Promise(function(e,t){api.campaignId.delete(campaign.id).success(function(t){e()}).error(function(e){t(e.responseJSON.message)})})}}).then(function(){swal("Campaign Deleted!","This campaign has been deleted!","success"),$('button:contains("OK")').on("click",function(){location.href="/campaigns"})})}function completeCampaign(){swal({title:"Are you sure?",text:"Gophish will stop processing events for this campaign",type:"warning",animation:!1,showCancelButton:!0,confirmButtonText:"Complete Campaign",confirmButtonColor:"#428bca",reverseButtons:!0,allowOutsideClick:!1,preConfirm:function(){return new Promise(function(e,t){api.campaignId.complete(campaign.id).success(function(t){e()}).error(function(e){t(e.responseJSON.message)})})}}).then(function(){swal("Campaign Completed!","This campaign has been completed!","success"),$("#complete_button")[0].disabled=!0,$("#complete_button").text("Completed!"),doPoll=!1})}function exportAsCSV(e){exportHTML=$("#exportButton").html();var t=null;switch(e){case"results":t=campaign.results;break;case"events":t=campaign.timeline}if(t){$("#exportButton").html('');var a=Papa.unparse(t,{}),i=new Blob([a],{type:"text/csv;charset=utf-8;"});if(navigator.msSaveBlob)navigator.msSaveBlob(i,e+".csv");else{var s=window.URL.createObjectURL(i),l=document.createElement("a");l.href=s,l.setAttribute("download",e+".csv"),document.body.appendChild(l),l.click(),document.body.removeChild(l)}$("#exportButton").html(exportHTML)}}function replay(e){function t(){form.attr({action:url}),form.appendTo("body").submit().remove()}request=campaign.timeline[e],details=JSON.parse(request.details),url=null,form=$("").attr({method:"POST",target:"_blank"}),$.each(Object.keys(details.payload),function(e,t){return"rid"==t||("__original_url"==t?(url=details.payload[t],!0):void $("").attr({name:t}).val(details.payload[t]).appendTo(form))}),swal({title:"Where do you want the credentials submitted to?",input:"text",showCancelButton:!0,inputPlaceholder:"http://example.com/login",inputValue:url||"",inputValidator:function(e){return new Promise(function(t,a){e?t():a("Invalid URL.")})}}).then(function(e){url=e,t()})}function renderTimeline(e){return record={first_name:e[2],last_name:e[3],email:e[4],position:e[5]},results='
    Timeline for '+escapeHtml(record.first_name)+" "+escapeHtml(record.last_name)+'
    Email: '+escapeHtml(record.email)+'
    ',$.each(campaign.timeline,function(e,t){t.email&&t.email!=record.email||(results+='
    ',results+='
    '+escapeHtml(t.message)+' '+moment(t.time).format("MMMM Do YYYY h:mm a")+"",t.details&&("Submitted Data"==t.message&&(results+='
    ',results+='
    View Details
    '),details=JSON.parse(t.details),details.payload&&(results+='
    ',results+=' ',results+=" ",$.each(Object.keys(details.payload),function(e,t){return"rid"==t||(results+=" ",results+=" ",results+=" ",void(results+=" "))}),results+="
    ParameterValue(s)
    "+escapeHtml(t)+""+escapeHtml(details.payload[t])+"
    ",results+="
    "),details.error&&(results+='
    View Details
    ',results+='
    ',results+='Error '+details.error,results+="
    ")),results+="
    ")}),results+="
    ",results}function poll(){api.campaignId.results(campaign.id).success(function(e){campaign=e;var t={series:[{name:"Events",data:[]}]};$.each(campaign.timeline,function(e,a){t.series[0].data.push({meta:e,x:new Date(a.time),y:1})});var a=$("#timeline_chart");a.get(0).__chartist__&&a.get(0).__chartist__.update(t);var i={series:[]},s={};$.each(campaign.results,function(e,t){s[t.status]?s[t.status]++:s[t.status]=1}),$("#email_chart_legend").html(""),$.each(s,function(e,t){i.series.push({meta:e,value:t}),$("#email_chart_legend").append('
  • '+e+"
  • ")});var l=$("#email_chart");l.get(0).__chartist__&&(l.get(0).__chartist__.on("draw",function(e){e.element.addClass(statuses[e.meta].slice)}),l.get(0).__chartist__.update(i)),resultsTable=$("#resultsTable").DataTable(),resultsTable.rows().every(function(e,t,a){var i=this.row(e),s=i.data(),l=s[0];$.each(campaign.results,function(t,a){if(a.id==l){var n=statuses[a.status].label||"label-default";return s[6]=''+a.status+"",resultsTable.row(e).data(s).draw(!1),i.child.isShown()&&i.child(renderTimeline(i.data())),!1}})}),bubbles=[],$.each(campaign.results,function(e,t){return 0==t.latitude&&0==t.longitude||(newIP=!0,$.each(bubbles,function(e,a){if(a.ip==t.ip)return bubbles[e].radius+=1,newIP=!1,!1}),void(newIP&&bubbles.push({latitude:t.latitude,longitude:t.longitude,name:t.ip,fillKey:"point",radius:2})))}),map.bubbles(bubbles),$("#refresh_message").hide(),$("#refresh_btn").show()})}function load(){campaign.id=window.location.pathname.split("/").slice(-1)[0],api.campaignId.results(campaign.id).success(function(e){if(campaign=e){$("title").text(e.name+" - Gophish"),$("#loading").hide(),$("#campaignResults").show(),$("#page-title").text("Results for "+e.name),"Completed"==e.status&&($("#complete_button")[0].disabled=!0,$("#complete_button").text("Completed!"),doPoll=!1),$('[data-toggle="tooltip"]').tooltip(),$("#resultsTable").on("click",".timeline-event-details",function(){payloadResults=$(this).parent().find(".timeline-event-results"),payloadResults.is(":visible")?($(this).find("i").removeClass("fa-caret-down"),$(this).find("i").addClass("fa-caret-right"),payloadResults.hide()):($(this).find("i").removeClass("fa-caret-right"),$(this).find("i").addClass("fa-caret-down"),payloadResults.show())});var t={series:[{name:"Events",data:[]}]},a={series:[]},i={},s={axisX:{showGrid:!1,type:Chartist.FixedScaleAxis,divisor:5,labelInterpolationFnc:function(e){return moment(e).format("MMMM Do YYYY h:mm a")}},axisY:{type:Chartist.FixedScaleAxis,ticks:[0,1,2],low:0,showLabel:!1},showArea:!1,plugins:[]},l={donut:!0,donutWidth:40,chartPadding:0,showLabel:!1};resultsTable=$("#resultsTable").DataTable({destroy:!0,order:[[2,"asc"]],columnDefs:[{orderable:!1,targets:"no-sort"},{className:"details-control",targets:[1]},{visible:!1,targets:[0]}]}),resultsTable.clear(),$.each(campaign.results,function(e,t){label=statuses[t.status].label||"label-default",resultsTable.row.add([t.id,'',escapeHtml(t.first_name)||"",escapeHtml(t.last_name)||"",escapeHtml(t.email)||"",escapeHtml(t.position)||"",''+t.status+""]).draw(),i[t.status]?i[t.status]++:i[t.status]=1}),$("#resultsTable tbody").on("click","td.details-control",function(){var e=$(this).closest("tr"),t=resultsTable.row(e);t.child.isShown()?(t.child.hide(),e.removeClass("shown"),$(this).find("i").removeClass("fa-caret-down"),$(this).find("i").addClass("fa-caret-right"),t.invalidate("dom").draw(!1)):($(this).find("i").removeClass("fa-caret-right"),$(this).find("i").addClass("fa-caret-down"),t.child(renderTimeline(t.data())).show(),e.addClass("shown"),t.invalidate("dom").draw(!1))}),$.each(campaign.timeline,function(e,a){t.series[0].data.push({meta:e,x:new Date(a.time),y:1})}),$("#email_chart_legend").html(""),$.each(i,function(e,t){a.series.push({meta:e,value:t}),$("#email_chart_legend").append('
  • '+e+"
  • ")});var n=new Chartist.Line("#timeline_chart",t,s);n.on("draw",function(e){if("point"===e.type){var t=statuses[campaign.timeline[e.meta].message].point,a=new Chartist.Svg("circle",{cx:[e.x],cy:[e.y],r:5,fill:"#283F50",meta:e.meta,value:1},t+" ct-timeline-point");e.element.replace(a)}}),$chart=$("#timeline_chart");var o=$chart.append('
    ').find(".chartist-tooltip").hide();$chart.on("mouseenter",".ct-timeline-point",function(){var e=$(this);cidx=e.attr("meta"),html="Event: "+campaign.timeline[cidx].message,campaign.timeline[cidx].email&&(html+="
    Email: "+escapeHtml(campaign.timeline[cidx].email)),o.html(html).show()}),$chart.on("mouseleave",".ct-timeline-point",function(){o.hide()}),$chart.on("mousemove",function(e){o.css({left:(e.offsetX||e.originalEvent.layerX)-o.width()/2-10,top:(e.offsetY+70||e.originalEvent.layerY)-o.height()-40})});var r=new Chartist.Pie("#email_chart",a,l);r.on("draw",function(e){e.element.addClass(statuses[e.meta].slice)}),$piechart=$("#email_chart");var c=$piechart.append('
    ').find(".chartist-tooltip").hide();$piechart.on("mouseenter",".ct-slice-donut",function(){var e=$(this);value=e.attr("ct:value"),label=e.attr("ct:meta"),c.html(label+": "+value.toString()).show()}),$piechart.on("mouseleave",".ct-slice-donut",function(){c.hide()}),$piechart.on("mousemove",function(e){c.css({left:(e.offsetX||e.originalEvent.layerX)-c.width()/2-10,top:(e.offsetY+40||e.originalEvent.layerY)-c.height()-80})}),map||(map=new Datamap({element:document.getElementById("resultsMap"),responsive:!0,fills:{defaultFill:"#ffffff",point:"#283F50"},geographyConfig:{highlightFillColor:"#1abc9c",borderColor:"#283F50"},bubblesConfig:{borderColor:"#283F50"}})),$.each(campaign.results,function(e,t){return 0==t.latitude&&0==t.longitude||(newIP=!0,$.each(bubbles,function(e,a){if(a.ip==t.ip)return bubbles[e].radius+=1,newIP=!1,!1}),void(newIP&&bubbles.push({latitude:t.latitude,longitude:t.longitude,name:t.ip,fillKey:"point",radius:2})))}),map.bubbles(bubbles)}$('a[data-toggle="tab"]').on("shown.bs.tab",function(e){"#overview"==$(e.target).attr("href")&&(map||(map=new Datamap({element:document.getElementById("resultsMap"),responsive:!0,fills:{defaultFill:"#ffffff"},geographyConfig:{highlightFillColor:"#1abc9c",borderColor:"#283F50"}})))})}).error(function(){$("#loading").hide(),errorFlash(" Campaign not found!")})}function refresh(){doPoll&&($("#refresh_message").show(),$("#refresh_btn").hide(),poll(),clearTimeout(setRefresh),setRefresh=setTimeout(refresh,6e4))}var map=null,doPoll=!0,statuses={"Email Sent":{slice:"ct-slice-donut-sent",legend:"ct-legend-sent",label:"label-success",icon:"fa-envelope",point:"ct-point-sent"},"Email Opened":{slice:"ct-slice-donut-opened",legend:"ct-legend-opened",label:"label-warning",icon:"fa-envelope",point:"ct-point-opened"},"Clicked Link":{slice:"ct-slice-donut-clicked",legend:"ct-legend-clicked",label:"label-clicked",icon:"fa-mouse-pointer",point:"ct-point-clicked"},Success:{slice:"ct-slice-donut-success",legend:"ct-legend-success",label:"label-danger",icon:"fa-exclamation",point:"ct-point-clicked"},Error:{slice:"ct-slice-donut-error",legend:"ct-legend-error",label:"label-default",icon:"fa-times",point:"ct-point-error"},"Error Sending Email":{slice:"ct-slice-donut-error",legend:"ct-legend-error",label:"label-default",icon:"fa-times",point:"ct-point-error"},"Submitted Data":{slice:"ct-slice-donut-success",legend:"ct-legend-success",label:"label-danger",icon:"fa-exclamation",point:"ct-point-clicked"},Unknown:{slice:"ct-slice-donut-error",legend:"ct-legend-error",label:"label-default",icon:"fa-question",point:"ct-point-error"},Sending:{slice:"ct-slice-donut-sending",legend:"ct-legend-sending",label:"label-primary",icon:"fa-spinner",point:"ct-point-sending"},"Campaign Created":{label:"label-success",icon:"fa-rocket"}},campaign={},bubbles=[],setRefresh;$(document).ready(function(){load(),setRefresh=setTimeout(refresh,6e4)}); \ No newline at end of file diff --git a/static/js/src/app/campaign_results.js b/static/js/src/app/campaign_results.js index 0b65d33f..6b099377 100644 --- a/static/js/src/app/campaign_results.js +++ b/static/js/src/app/campaign_results.js @@ -1,691 +1,700 @@ -var map = null -var doPoll = true; - -// statuses is a helper map to point result statuses to ui classes -var statuses = { - "Email Sent": { - slice: "ct-slice-donut-sent", - legend: "ct-legend-sent", - label: "label-success", - icon: "fa-envelope", - point: "ct-point-sent" - }, - "Email Opened": { - slice: "ct-slice-donut-opened", - legend: "ct-legend-opened", - label: "label-warning", - icon: "fa-envelope", - point: "ct-point-opened" - }, - "Clicked Link": { - slice: "ct-slice-donut-clicked", - legend: "ct-legend-clicked", - label: "label-clicked", - icon: "fa-mouse-pointer", - point: "ct-point-clicked" - }, - "Success": { - slice: "ct-slice-donut-success", - legend: "ct-legend-success", - label: "label-danger", - icon: "fa-exclamation", - point: "ct-point-clicked" - }, - "Error": { - slice: "ct-slice-donut-error", - legend: "ct-legend-error", - label: "label-default", - icon: "fa-times", - point: "ct-point-error" - }, - "Error Sending Email": { - slice: "ct-slice-donut-error", - legend: "ct-legend-error", - label: "label-default", - icon: "fa-times", - point: "ct-point-error" - }, - "Submitted Data": { - slice: "ct-slice-donut-success", - legend: "ct-legend-success", - label: "label-danger", - icon: "fa-exclamation", - point: "ct-point-clicked" - }, - "Unknown": { - slice: "ct-slice-donut-error", - legend: "ct-legend-error", - label: "label-default", - icon: "fa-question", - point: "ct-point-error" - }, - "Sending": { - slice: "ct-slice-donut-sending", - legend: "ct-legend-sending", - label: "label-primary", - icon: "fa-spinner", - point: "ct-point-sending" - }, - "Campaign Created": { - label: "label-success", - icon: "fa-rocket" - } -} - -var campaign = {} -var bubbles = [] - -function dismiss() { - $("#modal\\.flashes").empty() - $("#modal").modal('hide') - $("#resultsTable").dataTable().DataTable().clear().draw() -} - -// Deletes a campaign after prompting the user -function deleteCampaign() { - swal({ - title: "Are you sure?", - text: "This will delete the campaign. This can't be undone!", - type: "warning", - animation: false, - showCancelButton: true, - confirmButtonText: "Delete Campaign", - confirmButtonColor: "#428bca", - reverseButtons: true, - allowOutsideClick: false, - preConfirm: function() { - return new Promise(function(resolve, reject) { - api.campaignId.delete(campaign.id) - .success(function(msg) { - resolve() - }) - .error(function(data) { - reject(data.responseJSON.message) - }) - }) - } - }).then(function() { - swal( - 'Campaign Deleted!', - 'This campaign has been deleted!', - 'success' - ); - $('button:contains("OK")').on('click', function() { - location.href = '/campaigns' - }) - }) -} - -// Completes a campaign after prompting the user -function completeCampaign() { - swal({ - title: "Are you sure?", - text: "Gophish will stop processing events for this campaign", - type: "warning", - animation: false, - showCancelButton: true, - confirmButtonText: "Complete Campaign", - confirmButtonColor: "#428bca", - reverseButtons: true, - allowOutsideClick: false, - preConfirm: function() { - return new Promise(function(resolve, reject) { - api.campaignId.complete(campaign.id) - .success(function(msg) { - resolve() - }) - .error(function(data) { - reject(data.responseJSON.message) - }) - }) - } - }).then(function() { - swal( - 'Campaign Completed!', - 'This campaign has been completed!', - 'success' - ); - $('#complete_button')[0].disabled = true; - $('#complete_button').text('Completed!') - doPoll = false; - }) -} - -// Exports campaign results as a CSV file -function exportAsCSV(scope) { - exportHTML = $("#exportButton").html() - var csvScope = null - switch (scope) { - case "results": - csvScope = campaign.results - break; - case "events": - csvScope = campaign.timeline - break; - } - if (!csvScope) { - return - } - $("#exportButton").html('') - var csvString = Papa.unparse(csvScope, {}) - var csvData = new Blob([csvString], { - type: 'text/csv;charset=utf-8;' - }); - if (navigator.msSaveBlob) { - navigator.msSaveBlob(csvData, scope + '.csv'); - } else { - var csvURL = window.URL.createObjectURL(csvData); - var dlLink = document.createElement('a'); - dlLink.href = csvURL; - dlLink.setAttribute('download', scope + '.csv'); - document.body.appendChild(dlLink) - dlLink.click(); - document.body.removeChild(dlLink) - } - $("#exportButton").html(exportHTML) -} - -function replay(event_idx) { - request = campaign.timeline[event_idx] - details = JSON.parse(request.details) - url = null - form = $('').attr({ - method: 'POST', - target: '_blank', - }) - /* Create a form object and submit it */ - $.each(Object.keys(details.payload), function(i, param) { - if (param == "rid") { - return true; - } - if (param == "__original_url") { - url = details.payload[param]; - return true; - } - $('').attr({ - name: param, - }).val(details.payload[param]).appendTo(form); - }) - /* Ensure we know where to send the user */ - // Prompt for the URL - swal({ - title: 'Where do you want the credentials submitted to?', - input: 'text', - showCancelButton: true, - inputPlaceholder: "http://example.com/login", - inputValue: url || "", - inputValidator: function(value) { - return new Promise(function(resolve, reject) { - if (value) { - resolve(); - } else { - reject('Invalid URL.'); - } - }); - } - }).then(function(result) { - url = result - submitForm() - }) - return - submitForm() - - function submitForm() { - form.attr({ - action: url - }) - form.appendTo('body').submit().remove() - } -} - -function renderTimeline(data) { - record = { - "first_name": data[2], - "last_name": data[3], - "email": data[4], - "position": data[5] - } - results = '
    ' + - '
    Timeline for ' + escapeHtml(record.first_name) + ' ' + escapeHtml(record.last_name) + - '
    Email: ' + escapeHtml(record.email) + '' + - '
    ' - $.each(campaign.timeline, function(i, event) { - if (!event.email || event.email == record.email) { - // Add the event - results += '
    ' + - '
    ' - results += - '
    ' + - '
    ' + - '
    ' + escapeHtml(event.message) + - ' ' + moment(event.time).format('MMMM Do YYYY h:mm a') + '' - if (event.details) { - if (event.message == "Submitted Data") { - results += '
    ' - results += '
    View Details
    ' - } - details = JSON.parse(event.details) - if (details.payload) { - results += '
    ' - results += ' ' - results += ' ' - $.each(Object.keys(details.payload), function(i, param) { - if (param == "rid") { - return true; - } - results += ' ' - results += ' ' - results += ' ' - results += ' ' - }) - results += '
    ParameterValue(s)
    ' + escapeHtml(param) + '' + escapeHtml(details.payload[param]) + '
    ' - results += '
    ' - } - if (details.error) { - results += '
    View Details
    ' - results += '
    ' - results += 'Error ' + details.error - results += '
    ' - } - } - results += '
    ' - } - }) - results += '
    ' - return results -} - - -/* poll - Queries the API and updates the UI with the results - * - * Updates: - * * Timeline Chart - * * Email (Donut) Chart - * * Map Bubbles - * * Datatables - */ -function poll() { - api.campaignId.results(campaign.id) - .success(function(c) { - campaign = c - /* Update the timeline */ - var timeline_data = { - series: [{ - name: "Events", - data: [] - }] - } - $.each(campaign.timeline, function(i, event) { - timeline_data.series[0].data.push({ - meta: i, - x: new Date(event.time), - y: 1 - }) - }) - var timeline_chart = $("#timeline_chart") - if (timeline_chart.get(0).__chartist__) { - timeline_chart.get(0).__chartist__.update(timeline_data) - } - /* Update the results donut chart */ - var email_data = { - series: [] - } - var email_series_data = {} - $.each(campaign.results, function(i, result) { - if (!email_series_data[result.status]) { - email_series_data[result.status] = 1 - } else { - email_series_data[result.status]++; - } - }) - $("#email_chart_legend").html("") - $.each(email_series_data, function(status, count) { - email_data.series.push({ - meta: status, - value: count - }) - $("#email_chart_legend").append('
  • ' + status + '
  • ') - }) - var email_chart = $("#email_chart") - if (email_chart.get(0).__chartist__) { - email_chart.get(0).__chartist__.on('draw', function(data) { - data.element.addClass(statuses[data.meta].slice) - }) - // Update with the latest data - email_chart.get(0).__chartist__.update(email_data) - } - /* Update the datatable */ - resultsTable = $("#resultsTable").DataTable() - resultsTable.rows().every(function(i, tableLoop, rowLoop) { - var row = this.row(i) - var rowData = row.data() - var rid = rowData[0] - $.each(campaign.results, function(j, result) { - if (result.id == rid) { - var label = statuses[result.status].label || "label-default"; - rowData[6] = "" + result.status + "" - resultsTable.row(i).data(rowData).draw(false) - if (row.child.isShown()) { - row.child(renderTimeline(row.data())) - } - return false - } - }) - }) - /* Update the map information */ - bubbles = [] - $.each(campaign.results, function(i, result) { - // Check that it wasn't an internal IP - if (result.latitude == 0 && result.longitude == 0) { - return true; - } - newIP = true - $.each(bubbles, function(i, bubble) { - if (bubble.ip == result.ip) { - bubbles[i].radius += 1 - newIP = false - return false - } - }) - if (newIP) { - bubbles.push({ - latitude: result.latitude, - longitude: result.longitude, - name: result.ip, - fillKey: "point", - radius: 2 - }) - } - }) - map.bubbles(bubbles) - }) -} - -function load() { - campaign.id = window.location.pathname.split('/').slice(-1)[0] - api.campaignId.results(campaign.id) - .success(function(c) { - campaign = c - if (campaign) { - $("title").text(c.name + " - Gophish") - $("#loading").hide() - $("#campaignResults").show() - // Set the title - $("#page-title").text("Results for " + c.name) - if (c.status == "Completed") { - $('#complete_button')[0].disabled = true; - $('#complete_button').text('Completed!'); - doPoll = false; - } - // Setup tooltips - $('[data-toggle="tooltip"]').tooltip() - // Setup viewing the details of a result - $("#resultsTable").on("click", ".timeline-event-details", function() { - // Show the parameters - payloadResults = $(this).parent().find(".timeline-event-results") - if (payloadResults.is(":visible")) { - $(this).find("i").removeClass("fa-caret-down") - $(this).find("i").addClass("fa-caret-right") - payloadResults.hide() - } else { - $(this).find("i").removeClass("fa-caret-right") - $(this).find("i").addClass("fa-caret-down") - payloadResults.show() - } - }) - // Setup our graphs - var timeline_data = { - series: [{ - name: "Events", - data: [] - }] - } - var email_data = { - series: [] - } - var email_legend = {} - var email_series_data = {} - var timeline_opts = { - axisX: { - showGrid: false, - type: Chartist.FixedScaleAxis, - divisor: 5, - labelInterpolationFnc: function(value) { - return moment(value).format('MMMM Do YYYY h:mm a') - } - }, - axisY: { - type: Chartist.FixedScaleAxis, - ticks: [0, 1, 2], - low: 0, - showLabel: false - }, - showArea: false, - plugins: [] - } - var email_opts = { - donut: true, - donutWidth: 40, - chartPadding: 0, - showLabel: false - } - // Setup the results table - resultsTable = $("#resultsTable").DataTable({ - destroy: true, - "order": [ - [2, "asc"] - ], - columnDefs: [{ - orderable: false, - targets: "no-sort" - }, { - className: "details-control", - "targets": [1] - }, { - "visible": false, - "targets": [0] - }] - }); - resultsTable.clear(); - $.each(campaign.results, function(i, result) { - label = statuses[result.status].label || "label-default"; - resultsTable.row.add([ - result.id, - "", - escapeHtml(result.first_name) || "", - escapeHtml(result.last_name) || "", - escapeHtml(result.email) || "", - escapeHtml(result.position) || "", - "" + result.status + "" - ]).draw() - if (!email_series_data[result.status]) { - email_series_data[result.status] = 1 - } else { - email_series_data[result.status]++; - } - }) - // Setup the individual timelines - $('#resultsTable tbody').on('click', 'td.details-control', function() { - var tr = $(this).closest('tr'); - var row = resultsTable.row(tr); - if (row.child.isShown()) { - // This row is already open - close it - row.child.hide(); - tr.removeClass('shown'); - $(this).find("i").removeClass("fa-caret-down") - $(this).find("i").addClass("fa-caret-right") - row.invalidate('dom').draw(false) - } else { - // Open this row - $(this).find("i").removeClass("fa-caret-right") - $(this).find("i").addClass("fa-caret-down") - row.child(renderTimeline(row.data())).show(); - tr.addClass('shown'); - row.invalidate('dom').draw(false) - } - }); - // Setup the graphs - $.each(campaign.timeline, function(i, event) { - timeline_data.series[0].data.push({ - meta: i, - x: new Date(event.time), - y: 1 - }) - }) - $("#email_chart_legend").html("") - $.each(email_series_data, function(status, count) { - email_data.series.push({ - meta: status, - value: count - }) - $("#email_chart_legend").append('
  • ' + status + '
  • ') - }) - var timeline_chart = new Chartist.Line('#timeline_chart', timeline_data, timeline_opts) - timeline_chart.on('draw', function(data) { - if (data.type === "point") { - var point_style = statuses[campaign.timeline[data.meta].message].point - var circle = new Chartist.Svg("circle", { - cx: [data.x], - cy: [data.y], - r: 5, - fill: "#283F50", - meta: data.meta, - value: 1, - }, point_style + ' ct-timeline-point') - data.element.replace(circle) - } - }) - // Setup the overview chart listeners - $chart = $("#timeline_chart") - var $toolTip = $chart - .append('
    ') - .find('.chartist-tooltip') - .hide(); - $chart.on('mouseenter', '.ct-timeline-point', function() { - var $point = $(this) - cidx = $point.attr('meta') - html = "Event: " + campaign.timeline[cidx].message - if (campaign.timeline[cidx].email) { - html += '
    ' + "Email: " + escapeHtml(campaign.timeline[cidx].email) - } - $toolTip.html(html).show() - }); - $chart.on('mouseleave', '.ct-timeline-point', function() { - $toolTip.hide(); - }); - $chart.on('mousemove', function(event) { - $toolTip.css({ - left: (event.offsetX || event.originalEvent.layerX) - $toolTip.width() / 2 - 10, - top: (event.offsetY + 70 || event.originalEvent.layerY) - $toolTip.height() - 40 - }); - }); - - var email_chart = new Chartist.Pie("#email_chart", email_data, email_opts) - email_chart.on('draw', function(data) { - data.element.addClass(statuses[data.meta].slice) - }) - // Setup the average chart listeners - $piechart = $("#email_chart") - var $pietoolTip = $piechart - .append('
    ') - .find('.chartist-tooltip') - .hide(); - - $piechart.on('mouseenter', '.ct-slice-donut', function() { - var $point = $(this) - value = $point.attr('ct:value') - label = $point.attr('ct:meta') - $pietoolTip.html(label + ': ' + value.toString()).show(); - }); - - $piechart.on('mouseleave', '.ct-slice-donut', function() { - $pietoolTip.hide(); - }); - $piechart.on('mousemove', function(event) { - $pietoolTip.css({ - left: (event.offsetX || event.originalEvent.layerX) - $pietoolTip.width() / 2 - 10, - top: (event.offsetY + 40 || event.originalEvent.layerY) - $pietoolTip.height() - 80 - }); - }); - if (!map) { - map = new Datamap({ - element: document.getElementById("resultsMap"), - responsive: true, - fills: { - defaultFill: "#ffffff", - point: "#283F50" - }, - geographyConfig: { - highlightFillColor: "#1abc9c", - borderColor: "#283F50" - }, - bubblesConfig: { - borderColor: "#283F50" - } - }); - } - $.each(campaign.results, function(i, result) { - // Check that it wasn't an internal IP - if (result.latitude == 0 && result.longitude == 0) { - return true; - } - newIP = true - $.each(bubbles, function(i, bubble) { - if (bubble.ip == result.ip) { - bubbles[i].radius += 1 - newIP = false - return false - } - }) - if (newIP) { - bubbles.push({ - latitude: result.latitude, - longitude: result.longitude, - name: result.ip, - fillKey: "point", - radius: 2 - }) - } - }) - map.bubbles(bubbles) - } - // Load up the map data (only once!) - $('a[data-toggle="tab"]').on('shown.bs.tab', function(e) { - if ($(e.target).attr('href') == "#overview") { - if (!map) { - map = new Datamap({ - element: document.getElementById("resultsMap"), - responsive: true, - fills: { - defaultFill: "#ffffff" - }, - geographyConfig: { - highlightFillColor: "#1abc9c", - borderColor: "#283F50" - } - }); - } - } - }) - }) - .error(function() { - $("#loading").hide() - errorFlash(" Campaign not found!") - }) -} -$(document).ready(function() { - load(); - // Start the polling loop - function refresh() { - if (!doPoll) { - return; - } - $("#refresh_message").show() - poll() - $("#refresh_message").hide() - setTimeout(refresh, 10000) - }; - // Start the polling loop - setTimeout(refresh, 10000) -}) +var map = null +var doPoll = true; + +// statuses is a helper map to point result statuses to ui classes +var statuses = { + "Email Sent": { + slice: "ct-slice-donut-sent", + legend: "ct-legend-sent", + label: "label-success", + icon: "fa-envelope", + point: "ct-point-sent" + }, + "Email Opened": { + slice: "ct-slice-donut-opened", + legend: "ct-legend-opened", + label: "label-warning", + icon: "fa-envelope", + point: "ct-point-opened" + }, + "Clicked Link": { + slice: "ct-slice-donut-clicked", + legend: "ct-legend-clicked", + label: "label-clicked", + icon: "fa-mouse-pointer", + point: "ct-point-clicked" + }, + "Success": { + slice: "ct-slice-donut-success", + legend: "ct-legend-success", + label: "label-danger", + icon: "fa-exclamation", + point: "ct-point-clicked" + }, + "Error": { + slice: "ct-slice-donut-error", + legend: "ct-legend-error", + label: "label-default", + icon: "fa-times", + point: "ct-point-error" + }, + "Error Sending Email": { + slice: "ct-slice-donut-error", + legend: "ct-legend-error", + label: "label-default", + icon: "fa-times", + point: "ct-point-error" + }, + "Submitted Data": { + slice: "ct-slice-donut-success", + legend: "ct-legend-success", + label: "label-danger", + icon: "fa-exclamation", + point: "ct-point-clicked" + }, + "Unknown": { + slice: "ct-slice-donut-error", + legend: "ct-legend-error", + label: "label-default", + icon: "fa-question", + point: "ct-point-error" + }, + "Sending": { + slice: "ct-slice-donut-sending", + legend: "ct-legend-sending", + label: "label-primary", + icon: "fa-spinner", + point: "ct-point-sending" + }, + "Campaign Created": { + label: "label-success", + icon: "fa-rocket" + } +} + +var campaign = {} +var bubbles = [] + +function dismiss() { + $("#modal\\.flashes").empty() + $("#modal").modal('hide') + $("#resultsTable").dataTable().DataTable().clear().draw() +} + +// Deletes a campaign after prompting the user +function deleteCampaign() { + swal({ + title: "Are you sure?", + text: "This will delete the campaign. This can't be undone!", + type: "warning", + animation: false, + showCancelButton: true, + confirmButtonText: "Delete Campaign", + confirmButtonColor: "#428bca", + reverseButtons: true, + allowOutsideClick: false, + preConfirm: function() { + return new Promise(function(resolve, reject) { + api.campaignId.delete(campaign.id) + .success(function(msg) { + resolve() + }) + .error(function(data) { + reject(data.responseJSON.message) + }) + }) + } + }).then(function() { + swal( + 'Campaign Deleted!', + 'This campaign has been deleted!', + 'success' + ); + $('button:contains("OK")').on('click', function() { + location.href = '/campaigns' + }) + }) +} + +// Completes a campaign after prompting the user +function completeCampaign() { + swal({ + title: "Are you sure?", + text: "Gophish will stop processing events for this campaign", + type: "warning", + animation: false, + showCancelButton: true, + confirmButtonText: "Complete Campaign", + confirmButtonColor: "#428bca", + reverseButtons: true, + allowOutsideClick: false, + preConfirm: function() { + return new Promise(function(resolve, reject) { + api.campaignId.complete(campaign.id) + .success(function(msg) { + resolve() + }) + .error(function(data) { + reject(data.responseJSON.message) + }) + }) + } + }).then(function() { + swal( + 'Campaign Completed!', + 'This campaign has been completed!', + 'success' + ); + $('#complete_button')[0].disabled = true; + $('#complete_button').text('Completed!') + doPoll = false; + }) +} + +// Exports campaign results as a CSV file +function exportAsCSV(scope) { + exportHTML = $("#exportButton").html() + var csvScope = null + switch (scope) { + case "results": + csvScope = campaign.results + break; + case "events": + csvScope = campaign.timeline + break; + } + if (!csvScope) { + return + } + $("#exportButton").html('') + var csvString = Papa.unparse(csvScope, {}) + var csvData = new Blob([csvString], { + type: 'text/csv;charset=utf-8;' + }); + if (navigator.msSaveBlob) { + navigator.msSaveBlob(csvData, scope + '.csv'); + } else { + var csvURL = window.URL.createObjectURL(csvData); + var dlLink = document.createElement('a'); + dlLink.href = csvURL; + dlLink.setAttribute('download', scope + '.csv'); + document.body.appendChild(dlLink) + dlLink.click(); + document.body.removeChild(dlLink) + } + $("#exportButton").html(exportHTML) +} + +function replay(event_idx) { + request = campaign.timeline[event_idx] + details = JSON.parse(request.details) + url = null + form = $('').attr({ + method: 'POST', + target: '_blank', + }) + /* Create a form object and submit it */ + $.each(Object.keys(details.payload), function(i, param) { + if (param == "rid") { + return true; + } + if (param == "__original_url") { + url = details.payload[param]; + return true; + } + $('').attr({ + name: param, + }).val(details.payload[param]).appendTo(form); + }) + /* Ensure we know where to send the user */ + // Prompt for the URL + swal({ + title: 'Where do you want the credentials submitted to?', + input: 'text', + showCancelButton: true, + inputPlaceholder: "http://example.com/login", + inputValue: url || "", + inputValidator: function(value) { + return new Promise(function(resolve, reject) { + if (value) { + resolve(); + } else { + reject('Invalid URL.'); + } + }); + } + }).then(function(result) { + url = result + submitForm() + }) + return + submitForm() + + function submitForm() { + form.attr({ + action: url + }) + form.appendTo('body').submit().remove() + } +} + +function renderTimeline(data) { + record = { + "first_name": data[2], + "last_name": data[3], + "email": data[4], + "position": data[5] + } + results = '
    ' + + '
    Timeline for ' + escapeHtml(record.first_name) + ' ' + escapeHtml(record.last_name) + + '
    Email: ' + escapeHtml(record.email) + '' + + '
    ' + $.each(campaign.timeline, function(i, event) { + if (!event.email || event.email == record.email) { + // Add the event + results += '
    ' + + '
    ' + results += + '
    ' + + '
    ' + + '
    ' + escapeHtml(event.message) + + ' ' + moment(event.time).format('MMMM Do YYYY h:mm a') + '' + if (event.details) { + if (event.message == "Submitted Data") { + results += '
    ' + results += '
    View Details
    ' + } + details = JSON.parse(event.details) + if (details.payload) { + results += '
    ' + results += ' ' + results += ' ' + $.each(Object.keys(details.payload), function(i, param) { + if (param == "rid") { + return true; + } + results += ' ' + results += ' ' + results += ' ' + results += ' ' + }) + results += '
    ParameterValue(s)
    ' + escapeHtml(param) + '' + escapeHtml(details.payload[param]) + '
    ' + results += '
    ' + } + if (details.error) { + results += '
    View Details
    ' + results += '
    ' + results += 'Error ' + details.error + results += '
    ' + } + } + results += '
    ' + } + }) + results += '
    ' + return results +} + + +/* poll - Queries the API and updates the UI with the results + * + * Updates: + * * Timeline Chart + * * Email (Donut) Chart + * * Map Bubbles + * * Datatables + */ +function poll() { + api.campaignId.results(campaign.id) + .success(function(c) { + campaign = c + /* Update the timeline */ + var timeline_data = { + series: [{ + name: "Events", + data: [] + }] + } + $.each(campaign.timeline, function(i, event) { + timeline_data.series[0].data.push({ + meta: i, + x: new Date(event.time), + y: 1 + }) + }) + var timeline_chart = $("#timeline_chart") + if (timeline_chart.get(0).__chartist__) { + timeline_chart.get(0).__chartist__.update(timeline_data) + } + /* Update the results donut chart */ + var email_data = { + series: [] + } + var email_series_data = {} + $.each(campaign.results, function(i, result) { + if (!email_series_data[result.status]) { + email_series_data[result.status] = 1 + } else { + email_series_data[result.status]++; + } + }) + $("#email_chart_legend").html("") + $.each(email_series_data, function(status, count) { + email_data.series.push({ + meta: status, + value: count + }) + $("#email_chart_legend").append('
  • ' + status + '
  • ') + }) + var email_chart = $("#email_chart") + if (email_chart.get(0).__chartist__) { + email_chart.get(0).__chartist__.on('draw', function(data) { + data.element.addClass(statuses[data.meta].slice) + }) + // Update with the latest data + email_chart.get(0).__chartist__.update(email_data) + } + /* Update the datatable */ + resultsTable = $("#resultsTable").DataTable() + resultsTable.rows().every(function(i, tableLoop, rowLoop) { + var row = this.row(i) + var rowData = row.data() + var rid = rowData[0] + $.each(campaign.results, function(j, result) { + if (result.id == rid) { + var label = statuses[result.status].label || "label-default"; + rowData[6] = "" + result.status + "" + resultsTable.row(i).data(rowData).draw(false) + if (row.child.isShown()) { + row.child(renderTimeline(row.data())) + } + return false + } + }) + }) + /* Update the map information */ + bubbles = [] + $.each(campaign.results, function(i, result) { + // Check that it wasn't an internal IP + if (result.latitude == 0 && result.longitude == 0) { + return true; + } + newIP = true + $.each(bubbles, function(i, bubble) { + if (bubble.ip == result.ip) { + bubbles[i].radius += 1 + newIP = false + return false + } + }) + if (newIP) { + bubbles.push({ + latitude: result.latitude, + longitude: result.longitude, + name: result.ip, + fillKey: "point", + radius: 2 + }) + } + }) + map.bubbles(bubbles) + $("#refresh_message").hide() + $("#refresh_btn").show() + }) +} + +function load() { + campaign.id = window.location.pathname.split('/').slice(-1)[0] + api.campaignId.results(campaign.id) + .success(function(c) { + campaign = c + if (campaign) { + $("title").text(c.name + " - Gophish") + $("#loading").hide() + $("#campaignResults").show() + // Set the title + $("#page-title").text("Results for " + c.name) + if (c.status == "Completed") { + $('#complete_button')[0].disabled = true; + $('#complete_button').text('Completed!'); + doPoll = false; + } + // Setup tooltips + $('[data-toggle="tooltip"]').tooltip() + // Setup viewing the details of a result + $("#resultsTable").on("click", ".timeline-event-details", function() { + // Show the parameters + payloadResults = $(this).parent().find(".timeline-event-results") + if (payloadResults.is(":visible")) { + $(this).find("i").removeClass("fa-caret-down") + $(this).find("i").addClass("fa-caret-right") + payloadResults.hide() + } else { + $(this).find("i").removeClass("fa-caret-right") + $(this).find("i").addClass("fa-caret-down") + payloadResults.show() + } + }) + // Setup our graphs + var timeline_data = { + series: [{ + name: "Events", + data: [] + }] + } + var email_data = { + series: [] + } + var email_legend = {} + var email_series_data = {} + var timeline_opts = { + axisX: { + showGrid: false, + type: Chartist.FixedScaleAxis, + divisor: 5, + labelInterpolationFnc: function(value) { + return moment(value).format('MMMM Do YYYY h:mm a') + } + }, + axisY: { + type: Chartist.FixedScaleAxis, + ticks: [0, 1, 2], + low: 0, + showLabel: false + }, + showArea: false, + plugins: [] + } + var email_opts = { + donut: true, + donutWidth: 40, + chartPadding: 0, + showLabel: false + } + // Setup the results table + resultsTable = $("#resultsTable").DataTable({ + destroy: true, + "order": [ + [2, "asc"] + ], + columnDefs: [{ + orderable: false, + targets: "no-sort" + }, { + className: "details-control", + "targets": [1] + }, { + "visible": false, + "targets": [0] + }] + }); + resultsTable.clear(); + $.each(campaign.results, function(i, result) { + label = statuses[result.status].label || "label-default"; + resultsTable.row.add([ + result.id, + "", + escapeHtml(result.first_name) || "", + escapeHtml(result.last_name) || "", + escapeHtml(result.email) || "", + escapeHtml(result.position) || "", + "" + result.status + "" + ]).draw() + if (!email_series_data[result.status]) { + email_series_data[result.status] = 1 + } else { + email_series_data[result.status]++; + } + }) + // Setup the individual timelines + $('#resultsTable tbody').on('click', 'td.details-control', function() { + var tr = $(this).closest('tr'); + var row = resultsTable.row(tr); + if (row.child.isShown()) { + // This row is already open - close it + row.child.hide(); + tr.removeClass('shown'); + $(this).find("i").removeClass("fa-caret-down") + $(this).find("i").addClass("fa-caret-right") + row.invalidate('dom').draw(false) + } else { + // Open this row + $(this).find("i").removeClass("fa-caret-right") + $(this).find("i").addClass("fa-caret-down") + row.child(renderTimeline(row.data())).show(); + tr.addClass('shown'); + row.invalidate('dom').draw(false) + } + }); + // Setup the graphs + $.each(campaign.timeline, function(i, event) { + timeline_data.series[0].data.push({ + meta: i, + x: new Date(event.time), + y: 1 + }) + }) + $("#email_chart_legend").html("") + $.each(email_series_data, function(status, count) { + email_data.series.push({ + meta: status, + value: count + }) + $("#email_chart_legend").append('
  • ' + status + '
  • ') + }) + var timeline_chart = new Chartist.Line('#timeline_chart', timeline_data, timeline_opts) + timeline_chart.on('draw', function(data) { + if (data.type === "point") { + var point_style = statuses[campaign.timeline[data.meta].message].point + var circle = new Chartist.Svg("circle", { + cx: [data.x], + cy: [data.y], + r: 5, + fill: "#283F50", + meta: data.meta, + value: 1, + }, point_style + ' ct-timeline-point') + data.element.replace(circle) + } + }) + // Setup the overview chart listeners + $chart = $("#timeline_chart") + var $toolTip = $chart + .append('
    ') + .find('.chartist-tooltip') + .hide(); + $chart.on('mouseenter', '.ct-timeline-point', function() { + var $point = $(this) + cidx = $point.attr('meta') + html = "Event: " + campaign.timeline[cidx].message + if (campaign.timeline[cidx].email) { + html += '
    ' + "Email: " + escapeHtml(campaign.timeline[cidx].email) + } + $toolTip.html(html).show() + }); + $chart.on('mouseleave', '.ct-timeline-point', function() { + $toolTip.hide(); + }); + $chart.on('mousemove', function(event) { + $toolTip.css({ + left: (event.offsetX || event.originalEvent.layerX) - $toolTip.width() / 2 - 10, + top: (event.offsetY + 70 || event.originalEvent.layerY) - $toolTip.height() - 40 + }); + }); + + var email_chart = new Chartist.Pie("#email_chart", email_data, email_opts) + email_chart.on('draw', function(data) { + data.element.addClass(statuses[data.meta].slice) + }) + // Setup the average chart listeners + $piechart = $("#email_chart") + var $pietoolTip = $piechart + .append('
    ') + .find('.chartist-tooltip') + .hide(); + + $piechart.on('mouseenter', '.ct-slice-donut', function() { + var $point = $(this) + value = $point.attr('ct:value') + label = $point.attr('ct:meta') + $pietoolTip.html(label + ': ' + value.toString()).show(); + }); + + $piechart.on('mouseleave', '.ct-slice-donut', function() { + $pietoolTip.hide(); + }); + $piechart.on('mousemove', function(event) { + $pietoolTip.css({ + left: (event.offsetX || event.originalEvent.layerX) - $pietoolTip.width() / 2 - 10, + top: (event.offsetY + 40 || event.originalEvent.layerY) - $pietoolTip.height() - 80 + }); + }); + if (!map) { + map = new Datamap({ + element: document.getElementById("resultsMap"), + responsive: true, + fills: { + defaultFill: "#ffffff", + point: "#283F50" + }, + geographyConfig: { + highlightFillColor: "#1abc9c", + borderColor: "#283F50" + }, + bubblesConfig: { + borderColor: "#283F50" + } + }); + } + $.each(campaign.results, function(i, result) { + // Check that it wasn't an internal IP + if (result.latitude == 0 && result.longitude == 0) { + return true; + } + newIP = true + $.each(bubbles, function(i, bubble) { + if (bubble.ip == result.ip) { + bubbles[i].radius += 1 + newIP = false + return false + } + }) + if (newIP) { + bubbles.push({ + latitude: result.latitude, + longitude: result.longitude, + name: result.ip, + fillKey: "point", + radius: 2 + }) + } + }) + map.bubbles(bubbles) + } + // Load up the map data (only once!) + $('a[data-toggle="tab"]').on('shown.bs.tab', function(e) { + if ($(e.target).attr('href') == "#overview") { + if (!map) { + map = new Datamap({ + element: document.getElementById("resultsMap"), + responsive: true, + fills: { + defaultFill: "#ffffff" + }, + geographyConfig: { + highlightFillColor: "#1abc9c", + borderColor: "#283F50" + } + }); + } + } + }) + }) + .error(function() { + $("#loading").hide() + errorFlash(" Campaign not found!") + }) +} + +var setRefresh +function refresh() { + if (!doPoll) { + return; + } + $("#refresh_message").show() + $("#refresh_btn").hide() + poll() + clearTimeout(setRefresh) + setRefresh = setTimeout(refresh, 60000) +}; + + + +$(document).ready(function() { + load(); + // Start the polling loop + + // Start the polling loop + setRefresh = setTimeout(refresh, 60000) +}) diff --git a/templates/campaign_results.html b/templates/campaign_results.html index 6cb998a7..3f43cd32 100644 --- a/templates/campaign_results.html +++ b/templates/campaign_results.html @@ -54,6 +54,9 @@ + Refreshing