implement mark submitted data as false positive, reference #1915

pull/2208/head
Paul Werther 2021-05-19 13:31:56 +02:00
parent db63ee978d
commit a79ce72535
8 changed files with 138 additions and 8 deletions

View File

@ -135,3 +135,18 @@ func (as *Server) CampaignComplete(w http.ResponseWriter, r *http.Request) {
JSONResponse(w, models.Response{Success: true, Message: "Campaign completed successfully!"}, http.StatusOK)
}
}
func (as *Server) FalsePositive(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.ParseInt(vars["id"], 0, 64)
rid, _ := vars["rid"]
switch {
case r.Method == "GET":
err := models.MarkEvent(id, rid)
if err != nil {
JSONResponse(w, models.Response{Success: false, Message: "Error marking event as false positive"}, http.StatusInternalServerError)
return
}
JSONResponse(w, models.Response{Success: true, Message: "Event marked as false positive!"}, http.StatusOK)
}
}

View File

@ -67,6 +67,7 @@ func (as *Server) registerRoutes() {
router.HandleFunc("/campaigns/{id:[0-9]+}/results", as.CampaignResults)
router.HandleFunc("/campaigns/{id:[0-9]+}/summary", as.CampaignSummary)
router.HandleFunc("/campaigns/{id:[0-9]+}/complete", as.CampaignComplete)
router.HandleFunc("/falsepositive/{id:[0-9]+}/rid/{rid:[a-zA-Z0-9]+}", as.FalsePositive)
router.HandleFunc("/groups/", as.Groups)
router.HandleFunc("/groups/summary", as.GroupsSummary)
router.HandleFunc("/groups/{id:[0-9]+}", as.Group)

View File

@ -0,0 +1,6 @@
-- +goose Up
-- SQL in section 'Up' is executed when this migration is applied
ALTER TABLE `events` ADD COLUMN false_positive BOOLEAN DEFAULT 0;
-- +goose Down
-- SQL section 'Down' is executed when this migration is rolled back

View File

@ -0,0 +1,6 @@
-- +goose Up
-- SQL in section 'Up' is executed when this migration is applied
ALTER TABLE events ADD COLUMN false_positive BOOLEAN DEFAULT 0;
-- +goose Down
-- SQL section 'Down' is executed when this migration is rolled back

View File

@ -74,12 +74,13 @@ type CampaignStats struct {
// Event contains the fields for an event
// that occurs during the campaign
type Event struct {
Id int64 `json:"-"`
CampaignId int64 `json:"campaign_id"`
Email string `json:"email"`
Time time.Time `json:"time"`
Message string `json:"message"`
Details string `json:"details"`
Id int64 `json:"id"`
CampaignId int64 `json:"campaign_id"`
Email string `json:"email"`
Time time.Time `json:"time"`
Message string `json:"message"`
Details string `json:"details"`
FalsePositive bool `json:"false_positive"`
}
// EventDetails is a struct that wraps common attributes we want to store
@ -154,6 +155,33 @@ func (c *Campaign) UpdateStatus(s string) error {
return db.Table("campaigns").Where("id=?", c.Id).Update("status", s).Error
}
// Sets the False Positive-field for the specified eventid to true.
// Checks if all Events with the specific email in that campaign where data has been submitted are set to false_positive "true" and if thats the case changes
// the status in the "results" table for that recipient back to "Clicked Link" hence it won´t be count in the Submitted statistic until new data is submitted
func MarkEvent(eveId int64, rid string) error {
var mailcount int64
var fpcount int64
s := Event{}
query := db.Table("events").Where("id=?", eveId).Update("false_positive", true).Error
if query != nil {
log.Errorf("Problem editing false_positive in database: Table \"events\" on id %d", eveId)
}
mailquery := db.Table("events").Where("id = ?", eveId)
mailquery.Select("id, campaign_id, email, time, message, details, false_positive")
err := mailquery.Scan(&s).Error
if err != nil {
log.Error(err)
return err
}
//Counter to check if all Data Submitted is flaged as false/positive
db.Table("events").Select("Count(*)").Where("email = ? AND message = ?", s.Email, "Submitted Data").Count(&mailcount)
db.Table("events").Select("Count(*)").Where("email = ? AND message = ? AND false_positive = ?", s.Email, "Submitted Data", true).Count(&fpcount)
if mailcount == fpcount {
return db.Table("results").Where("campaign_id = ? AND r_id = ?", s.CampaignId, rid).Update("status", "Clicked Link").Error
}
return query
}
// AddEvent creates a new campaign event in the database
func AddEvent(e *Event, campaignID int64) error {
e.CampaignId = campaignID

6
static/css/main.css vendored
View File

@ -741,4 +741,10 @@ table.dataTable {
}
#password-strength-container {
height: 40px;
}
.ms-1 {
margin-left: 1em;
}
.mt-1 {
margin-top: 1em;
}

View File

@ -42,6 +42,12 @@ var statuses = {
icon: "fa-exclamation",
point: "ct-point-clicked"
},
"True/False":{
color: "#6c7a89",
label: "label-info",
icon: "fa-eraser",
point: "ct-point-true_false"
},
//not a status, but is used for the campaign timeline and user timeline
"Email Reported": {
color: "#45d6ef",
@ -178,7 +184,7 @@ function completeCampaign() {
return new Promise(function (resolve, reject) {
api.campaignId.complete(campaign.id)
.success(function (msg) {
resolve()
resolve()
})
.error(function (data) {
reject(data.responseJSON.message)
@ -236,6 +242,56 @@ function exportAsCSV(scope) {
$("#exportButton").html(exportHTML)
}
function false_positive(rid, eventid, cid, repo) {
if(repo === 'true'){
Swal.fire({
title: "Are you sure?",
text: "This will flag the submitted data as False Positive and substract it from the count. Please make sure that they are invalid!",
type: "question",
animation: false,
showCancelButton: true,
reverseButtons: true,
allowOutsideClick: false,
showLoaderOnConfirm: true
}).then(function (result) {
if (result.value){
api.eventId.falsepositive(eventid, rid).success((function() {
refresh();
} ));
}
})
}
else{ // will be called if Reported is not marked
Swal.fire({
title: "Are you sure?",
text: "This will flag the submitted data as False Positive and substract it from the count. Please make sure that they are invalid!",
type: "question",
animation: false,
showCancelButton: true,
input: 'checkbox',
inputValue: 1,
inputPlaceholder: 'Mark as reported too',
confirmButtonText: "Continue",
confirmButtonColor: "#428bca",
reverseButtons: true,
allowOutsideClick: false,
showLoaderOnConfirm: true
}).then(function (result) {
if (result.value && !result.reported){ // Reports the rid as Reported and the submitted data as false positive
report_mail(escapeHtml(rid), cid);
api.eventId.falsepositive(eventid, rid).success((function() {
refresh();
} ));
} else if (result.value === 0){ // Marks the Event with the submitted data as false positive
api.eventId.falsepositive(eventid, rid).success((function() {
refresh();
}));
}
})
}
}
function replay(event_idx) {
request = campaign.timeline[event_idx]
details = JSON.parse(request.details)
@ -400,7 +456,13 @@ function renderTimeline(data) {
}
if (event.message == "Submitted Data") {
results += '<div class="timeline-replay-button"><button onclick="replay(' + i + ')" class="btn btn-success">'
results += '<i class="fa fa-refresh"></i> Replay Credentials</button></div>'
results += '<i class="fa fa-refresh"></i> Replay Credentials</button>'
if(campaign.timeline[i].false_positive === true){
results += '<div class="alert alert-warning mt-1" role="alert">Marked as False Positive!</div></div>'
}
else{
results += '<button onclick="false_positive(\'' + record.id + '\',\'' + campaign.timeline[i].id + '\',\'' + campaign.id + '\',\'' + record.reported + '\')" id="false-positive-button" class="btn btn-warning ms-1"><i class="fa fa-ban"></i> False Positive</button></div>'
}
results += '<div class="timeline-event-details"><i class="fa fa-caret-right"></i> View Details</div>'
}
if (details.payload) {

View File

@ -255,6 +255,12 @@ var api = {
return query("/users/" + id, "DELETE", {}, true)
}
},
eventId:{
//marks an event as false positiv in the database
falsepositive: function (eventid, rid) {
return query("/falsepositive/" + eventid + "/rid/" + rid, "GET", {}, true)
}
},
webhooks: {
get: function() {
return query("/webhooks/", "GET", {}, false)