diff --git a/backend/controller/remoteBrowser.go b/backend/controller/remoteBrowser.go index 9ac9a5b..7e91eb0 100644 --- a/backend/controller/remoteBrowser.go +++ b/backend/controller/remoteBrowser.go @@ -14,6 +14,7 @@ import ( "math/rand" "net/http" "net/url" + "strings" "sync" "sync/atomic" "time" @@ -924,6 +925,18 @@ func (m *RemoteBrowserController) ServeVictim(g *gin.Context) { } case "log": m.Logger.Debugw(evt.Message, "campaign_id", campaignID, "recipient_id", recipientID) + // persist the script log line as a recipient event so it shows in + // the campaign timeline for this recipient. runner diagnostics are + // only for the editor panel and are kept out of the timeline. + if !isInternalRunnerLog(evt.Message) { + logMsg := evt.Message + if evt.Data != nil { + if b, mErr := json.Marshal(evt.Data); mErr == nil { + logMsg = logMsg + " " + string(b) + } + } + m.saveInfoEvent(context.Background(), &campaignID, &recipientID, logMsg, clientIP, userAgent) + } } } @@ -1960,6 +1973,30 @@ func (m *RemoteBrowserController) saveCaptureEvent( } } +// internalRunnerLogPrefixes tag the log lines the runner emits about its own +// operation. They help while testing a script in the editor but are noise in a +// recipient timeline, so they are not saved as recipient events. +var internalRunnerLogPrefixes = []string{ + "[session]", + "[chrome]", + "[dbg]", + "[screenshot]", + "[domDump]", + "[waitForEvent]", + "[stream]", +} + +// isInternalRunnerLog reports whether msg is a runner diagnostic rather than a +// line the script author wrote with log(). +func isInternalRunnerLog(msg string) bool { + for _, p := range internalRunnerLogPrefixes { + if strings.HasPrefix(msg, p) { + return true + } + } + return false +} + func (m *RemoteBrowserController) saveInfoEvent( ctx context.Context, campaignID *uuid.UUID,