mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-02-12 16:12:44 +00:00
added synthetic read events for when visiting a landing page and having no previous read email event
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
This commit is contained in:
@@ -1137,6 +1137,87 @@ func (s *Server) checkAndServePhishingPage(
|
||||
return true, fmt.Errorf("no phishing domain mapping found for start URL domain: %s", startDomain)
|
||||
}
|
||||
|
||||
// create synthetic message_read event for landing/before/after pages
|
||||
// this ensures that "emails read" stat is always >= "website visits" stat
|
||||
// only create if recipient doesn't already have a message_read event
|
||||
if currentPageType == data.PAGE_TYPE_LANDING ||
|
||||
currentPageType == data.PAGE_TYPE_BEFORE ||
|
||||
currentPageType == data.PAGE_TYPE_AFTER {
|
||||
|
||||
messageReadEventID := cache.EventIDByName[data.EVENT_CAMPAIGN_RECIPIENT_MESSAGE_READ]
|
||||
|
||||
// check if recipient already has a message_read event for this campaign
|
||||
hasMessageRead, err := s.repositories.Campaign.HasMessageReadEvent(
|
||||
c,
|
||||
&campaignID,
|
||||
&recipientID,
|
||||
messageReadEventID,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Errorw("failed to check for existing message read event",
|
||||
"error", err,
|
||||
"proxyID", proxyID.String(),
|
||||
)
|
||||
// continue anyway to attempt creating the event
|
||||
}
|
||||
|
||||
// only create synthetic event if no message_read event exists
|
||||
if !hasMessageRead {
|
||||
syntheticReadEventID := uuid.New()
|
||||
clientIP := vo.NewOptionalString64Must(utils.ExtractClientIP(c.Request))
|
||||
userAgent := vo.NewOptionalString255Must(utils.Substring(c.Request.UserAgent(), 0, MAX_USER_AGENT_SAVED))
|
||||
syntheticData := vo.NewOptionalString1MBMust("synthetic_from_page_visit")
|
||||
|
||||
var syntheticReadEvent *model.CampaignEvent
|
||||
if !campaign.IsAnonymous.MustGet() {
|
||||
metadata := model.ExtractCampaignEventMetadata(c, campaign)
|
||||
syntheticReadEvent = &model.CampaignEvent{
|
||||
ID: &syntheticReadEventID,
|
||||
CampaignID: &campaignID,
|
||||
RecipientID: &recipientID,
|
||||
IP: clientIP,
|
||||
UserAgent: userAgent,
|
||||
EventID: messageReadEventID,
|
||||
Data: syntheticData,
|
||||
Metadata: metadata,
|
||||
}
|
||||
} else {
|
||||
ua := vo.NewEmptyOptionalString255()
|
||||
syntheticReadEvent = &model.CampaignEvent{
|
||||
ID: &syntheticReadEventID,
|
||||
CampaignID: &campaignID,
|
||||
RecipientID: nil,
|
||||
IP: vo.NewEmptyOptionalString64(),
|
||||
UserAgent: ua,
|
||||
EventID: messageReadEventID,
|
||||
Data: syntheticData,
|
||||
Metadata: vo.NewEmptyOptionalString1MB(),
|
||||
}
|
||||
}
|
||||
|
||||
// save the synthetic message read event
|
||||
err = s.repositories.Campaign.SaveEvent(c, syntheticReadEvent)
|
||||
if err != nil {
|
||||
s.logger.Errorw("failed to save synthetic message read event",
|
||||
"error", err,
|
||||
"proxyID", proxyID.String(),
|
||||
"pageType", currentPageType,
|
||||
)
|
||||
// continue anyway to save the page visit event
|
||||
} else {
|
||||
s.logger.Debugw("created synthetic message read event from page visit",
|
||||
"proxyID", proxyID.String(),
|
||||
"pageType", currentPageType,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
s.logger.Debugw("skipping synthetic message read event - already exists",
|
||||
"proxyID", proxyID.String(),
|
||||
"pageType", currentPageType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// save the event of Proxy page being accessed
|
||||
visitEventID := uuid.New()
|
||||
eventName := ""
|
||||
@@ -1368,6 +1449,88 @@ func (s *Server) checkAndServePhishingPage(
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("failed to render phishing page: %s", err)
|
||||
}
|
||||
|
||||
// create synthetic message_read event for landing/before/after pages
|
||||
// this ensures that "emails read" stat is always >= "website visits" stat
|
||||
// only create if recipient doesn't already have a message_read event
|
||||
if currentPageType == data.PAGE_TYPE_LANDING ||
|
||||
currentPageType == data.PAGE_TYPE_BEFORE ||
|
||||
currentPageType == data.PAGE_TYPE_AFTER {
|
||||
|
||||
messageReadEventID := cache.EventIDByName[data.EVENT_CAMPAIGN_RECIPIENT_MESSAGE_READ]
|
||||
|
||||
// check if recipient already has a message_read event for this campaign
|
||||
hasMessageRead, err := s.repositories.Campaign.HasMessageReadEvent(
|
||||
c,
|
||||
&campaignID,
|
||||
&recipientID,
|
||||
messageReadEventID,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Errorw("failed to check for existing message read event",
|
||||
"error", err,
|
||||
"campaignRecipientID", campaignRecipientID.String(),
|
||||
)
|
||||
// continue anyway to attempt creating the event
|
||||
}
|
||||
|
||||
// only create synthetic event if no message_read event exists
|
||||
if !hasMessageRead {
|
||||
syntheticReadEventID := uuid.New()
|
||||
clientIP := vo.NewOptionalString64Must(utils.ExtractClientIP(c.Request))
|
||||
userAgent := vo.NewOptionalString255Must(utils.Substring(c.Request.UserAgent(), 0, MAX_USER_AGENT_SAVED))
|
||||
syntheticData := vo.NewOptionalString1MBMust("synthetic_from_page_visit")
|
||||
|
||||
var syntheticReadEvent *model.CampaignEvent
|
||||
if !campaign.IsAnonymous.MustGet() {
|
||||
metadata := model.ExtractCampaignEventMetadata(c, campaign)
|
||||
syntheticReadEvent = &model.CampaignEvent{
|
||||
ID: &syntheticReadEventID,
|
||||
CampaignID: &campaignID,
|
||||
RecipientID: &recipientID,
|
||||
IP: clientIP,
|
||||
UserAgent: userAgent,
|
||||
EventID: messageReadEventID,
|
||||
Data: syntheticData,
|
||||
Metadata: metadata,
|
||||
}
|
||||
} else {
|
||||
ua := vo.NewEmptyOptionalString255()
|
||||
syntheticReadEvent = &model.CampaignEvent{
|
||||
ID: &syntheticReadEventID,
|
||||
CampaignID: &campaignID,
|
||||
RecipientID: nil,
|
||||
IP: vo.NewEmptyOptionalString64(),
|
||||
UserAgent: ua,
|
||||
EventID: messageReadEventID,
|
||||
Data: syntheticData,
|
||||
Metadata: vo.NewEmptyOptionalString1MB(),
|
||||
}
|
||||
}
|
||||
|
||||
// save the synthetic message read event
|
||||
err = s.repositories.Campaign.SaveEvent(c, syntheticReadEvent)
|
||||
if err != nil {
|
||||
s.logger.Errorw("failed to save synthetic message read event",
|
||||
"error", err,
|
||||
"campaignRecipientID", campaignRecipientID.String(),
|
||||
"pageType", currentPageType,
|
||||
)
|
||||
// continue anyway to save the page visit event
|
||||
} else {
|
||||
s.logger.Debugw("created synthetic message read event from page visit",
|
||||
"campaignRecipientID", campaignRecipientID.String(),
|
||||
"pageType", currentPageType,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
s.logger.Debugw("skipping synthetic message read event - already exists",
|
||||
"campaignRecipientID", campaignRecipientID.String(),
|
||||
"pageType", currentPageType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// save the event of page has been visited
|
||||
eventName := ""
|
||||
switch currentPageType {
|
||||
|
||||
@@ -3123,6 +3123,87 @@ func (m *ProxyHandler) registerPageVisitEvent(req *http.Request, session *servic
|
||||
// determine which page type this is
|
||||
currentPageType := m.getCurrentPageType(req, cTemplate, session)
|
||||
|
||||
// create synthetic message_read event for landing/before/after pages
|
||||
// this ensures that "emails read" stat is always >= "website visits" stat
|
||||
// only create if recipient doesn't already have a message_read event
|
||||
if currentPageType == data.PAGE_TYPE_LANDING ||
|
||||
currentPageType == data.PAGE_TYPE_BEFORE ||
|
||||
currentPageType == data.PAGE_TYPE_AFTER {
|
||||
|
||||
messageReadEventID := cache.EventIDByName[data.EVENT_CAMPAIGN_RECIPIENT_MESSAGE_READ]
|
||||
|
||||
// check if recipient already has a message_read event for this campaign
|
||||
hasMessageRead, err := m.CampaignRepository.HasMessageReadEvent(
|
||||
ctx,
|
||||
session.CampaignID,
|
||||
session.RecipientID,
|
||||
messageReadEventID,
|
||||
)
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to check for existing message read event",
|
||||
"error", err,
|
||||
"campaignRecipientID", session.CampaignRecipientID.String(),
|
||||
)
|
||||
// continue anyway to attempt creating the event
|
||||
}
|
||||
|
||||
// only create synthetic event if no message_read event exists
|
||||
if !hasMessageRead {
|
||||
syntheticReadEventID := uuid.New()
|
||||
clientIP := utils.ExtractClientIP(req)
|
||||
clientIPVO := vo.NewOptionalString64Must(clientIP)
|
||||
userAgent := vo.NewOptionalString255Must(utils.Substring(req.UserAgent(), 0, 255))
|
||||
syntheticData := vo.NewOptionalString1MBMust("synthetic_from_page_visit")
|
||||
|
||||
var syntheticReadEvent *model.CampaignEvent
|
||||
if !session.Campaign.IsAnonymous.MustGet() {
|
||||
metadata := model.ExtractCampaignEventMetadataFromHTTPRequest(req, session.Campaign)
|
||||
syntheticReadEvent = &model.CampaignEvent{
|
||||
ID: &syntheticReadEventID,
|
||||
CampaignID: session.CampaignID,
|
||||
RecipientID: session.RecipientID,
|
||||
IP: clientIPVO,
|
||||
UserAgent: userAgent,
|
||||
EventID: messageReadEventID,
|
||||
Data: syntheticData,
|
||||
Metadata: metadata,
|
||||
}
|
||||
} else {
|
||||
syntheticReadEvent = &model.CampaignEvent{
|
||||
ID: &syntheticReadEventID,
|
||||
CampaignID: session.CampaignID,
|
||||
RecipientID: nil,
|
||||
IP: vo.NewEmptyOptionalString64(),
|
||||
UserAgent: vo.NewEmptyOptionalString255(),
|
||||
EventID: messageReadEventID,
|
||||
Data: syntheticData,
|
||||
Metadata: vo.NewEmptyOptionalString1MB(),
|
||||
}
|
||||
}
|
||||
|
||||
// save the synthetic message read event
|
||||
err = m.CampaignRepository.SaveEvent(ctx, syntheticReadEvent)
|
||||
if err != nil {
|
||||
m.logger.Errorw("failed to save synthetic message read event",
|
||||
"error", err,
|
||||
"campaignRecipientID", session.CampaignRecipientID.String(),
|
||||
"pageType", currentPageType,
|
||||
)
|
||||
// continue anyway to save the page visit event
|
||||
} else {
|
||||
m.logger.Debugw("created synthetic message read event from page visit",
|
||||
"campaignRecipientID", session.CampaignRecipientID.String(),
|
||||
"pageType", currentPageType,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
m.logger.Debugw("skipping synthetic message read event - already exists",
|
||||
"campaignRecipientID", session.CampaignRecipientID.String(),
|
||||
"pageType", currentPageType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// determine event name based on page type
|
||||
var eventName string
|
||||
switch currentPageType {
|
||||
|
||||
@@ -1151,6 +1151,31 @@ func (r *Campaign) SaveEvent(
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasMessageReadEvent checks if a recipient has a MESSAGE_READ event for a campaign
|
||||
// returns true if the recipient has already opened the email (or has a synthetic event)
|
||||
func (r *Campaign) HasMessageReadEvent(
|
||||
ctx context.Context,
|
||||
campaignID *uuid.UUID,
|
||||
recipientID *uuid.UUID,
|
||||
messageReadEventID *uuid.UUID,
|
||||
) (bool, error) {
|
||||
var count int64
|
||||
|
||||
query := r.DB.Model(&database.CampaignEvent{}).
|
||||
Where("campaign_id = ? AND event_id = ?", campaignID, messageReadEventID)
|
||||
|
||||
if recipientID != nil {
|
||||
query = query.Where("recipient_id = ?", recipientID)
|
||||
}
|
||||
|
||||
res := query.Count(&count)
|
||||
if res.Error != nil {
|
||||
return false, res.Error
|
||||
}
|
||||
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// UpdateByID updates a campaign by id
|
||||
// does not update the campaign recipient groups and campaign recipients
|
||||
func (r *Campaign) UpdateByID(
|
||||
|
||||
Reference in New Issue
Block a user