diff --git a/backend/controller/remoteBrowser.go b/backend/controller/remoteBrowser.go index 7e91eb06..038bbb2e 100644 --- a/backend/controller/remoteBrowser.go +++ b/backend/controller/remoteBrowser.go @@ -14,7 +14,6 @@ import ( "math/rand" "net/http" "net/url" - "strings" "sync" "sync/atomic" "time" @@ -927,8 +926,10 @@ func (m *RemoteBrowserController) ServeVictim(g *gin.Context) { 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) { + // only for the editor panel and are kept out of the timeline. the + // runner marks its own diagnostics Internal, so classification does + // not depend on the message text. + if !evt.Internal { logMsg := evt.Message if evt.Data != nil { if b, mErr := json.Marshal(evt.Data); mErr == nil { @@ -1973,30 +1974,6 @@ 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, diff --git a/backend/remotebrowser/emitter.go b/backend/remotebrowser/emitter.go index 145f4fb2..a3f3b6c1 100644 --- a/backend/remotebrowser/emitter.go +++ b/backend/remotebrowser/emitter.go @@ -15,6 +15,13 @@ type RunEvent struct { Message string `json:"message,omitempty"` // for type=log/error/info Data any `json:"data,omitempty"` // for type=log: optional second arg from log(msg, data) Time string `json:"time"` + // Internal marks a runner diagnostic (session, chrome, capture, frame, ...) + // as opposed to a line the script author wrote with log(). Only script logs + // are recorded on the recipient timeline; internal logs stay in the editor + // panel. This is set by the runner, never derived from the message text, so + // a log line whose text happens to start with a reserved word is classified + // by where it came from, not by what it says. + Internal bool `json:"internal,omitempty"` } // channelEmitter sends events to a buffered channel. All methods are safe to @@ -36,11 +43,24 @@ func (e *channelEmitter) emit(key string, value any) { }) } +// log emits a runner diagnostic. These show in the editor panel but are kept +// off the recipient timeline. Every non script caller in the runner uses this. func (e *channelEmitter) log(msg string, data ...any) { + e.sendLog(msg, true, data...) +} + +// scriptLog emits a line the automation script author wrote with log(). These +// are recorded on the recipient timeline. Only the goja log() binding uses this. +func (e *channelEmitter) scriptLog(msg string, data ...any) { + e.sendLog(msg, false, data...) +} + +func (e *channelEmitter) sendLog(msg string, internal bool, data ...any) { evt := RunEvent{ - Type: "log", - Message: msg, - Time: time.Now().UTC().Format(time.RFC3339Nano), + Type: "log", + Message: msg, + Internal: internal, + Time: time.Now().UTC().Format(time.RFC3339Nano), } if len(data) > 0 { evt.Data = data[0] diff --git a/backend/remotebrowser/runner.go b/backend/remotebrowser/runner.go index a3c831e1..7a5d36e8 100644 --- a/backend/remotebrowser/runner.go +++ b/backend/remotebrowser/runner.go @@ -486,9 +486,9 @@ func (r *Runner) Run(ctx context.Context) error { vm.Set("log", func(call goja.FunctionCall) goja.Value { msg := vmArgStr(call.Argument(0)) if len(call.Arguments) > 1 && !goja.IsUndefined(call.Argument(1)) && !goja.IsNull(call.Argument(1)) { - emitter.log(msg, call.Argument(1).Export()) + emitter.scriptLog(msg, call.Argument(1).Export()) } else { - emitter.log(msg) + emitter.scriptLog(msg) } return goja.Undefined() })