diff --git a/internal/container/event_generator.go b/internal/container/event_generator.go index 4642612f..85b0c54f 100644 --- a/internal/container/event_generator.go +++ b/internal/container/event_generator.go @@ -87,7 +87,6 @@ func (g *EventGenerator) consumeReader() { logEvent := createEvent(message, streamType) logEvent.ContainerID = g.containerID logEvent.Level = guessLogLevel(logEvent) - escape(logEvent) g.buffer <- logEvent } diff --git a/internal/support/search/search.go b/internal/support/search/search.go index 8d6f9df3..e8e8a42a 100644 --- a/internal/support/search/search.go +++ b/internal/support/search/search.go @@ -10,6 +10,11 @@ import ( orderedmap "github.com/wk8/go-ordered-map/v2" ) +const ( + MarkerStart = "\uE000" + MarkerEnd = "\uE001" +) + func ParseRegex(search string) (*regexp.Regexp, error) { flags := "" @@ -30,7 +35,7 @@ func Search(re *regexp.Regexp, logEvent *container.LogEvent) bool { switch value := logEvent.Message.(type) { case string: if re.MatchString(value) { - logEvent.Message = re.ReplaceAllString(value, "$0") + logEvent.Message = re.ReplaceAllString(value, MarkerStart+"$0"+MarkerEnd) return true } @@ -56,9 +61,9 @@ func searchMapAny(re *regexp.Regexp, orderedMap *orderedmap.OrderedMap[string, a for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { switch value := pair.Value.(type) { case string: - if re.MatchString(value) { + if replaced, matched := searchString(re, value); matched { found = true - orderedMap.Set(pair.Key, re.ReplaceAllString(value, "$0")) + orderedMap.Set(pair.Key, replaced) } case []any: @@ -84,7 +89,7 @@ func searchMapAny(re *regexp.Regexp, orderedMap *orderedmap.OrderedMap[string, a case int, float64, bool: formatted := fmt.Sprintf("%v", value) if re.MatchString(formatted) { - orderedMap.Set(pair.Key, re.ReplaceAllString(formatted, "$0")) + orderedMap.Set(pair.Key, re.ReplaceAllString(formatted, MarkerStart+"$0"+MarkerEnd)) found = true } @@ -101,11 +106,10 @@ func searchMap(re *regexp.Regexp, data map[string]interface{}) bool { for key, value := range data { switch value := value.(type) { case string: - if re.MatchString(value) { - data[key] = re.ReplaceAllString(value, "$0") + if replaced, matched := searchString(re, value); matched { found = true + data[key] = replaced } - case []any: if searchArray(re, value) { found = true @@ -119,7 +123,7 @@ func searchMap(re *regexp.Regexp, data map[string]interface{}) bool { case int, float64, bool: formatted := fmt.Sprintf("%v", value) if re.MatchString(formatted) { - data[key] = re.ReplaceAllString(formatted, "$0") + data[key] = re.ReplaceAllString(formatted, MarkerStart+"$0"+MarkerEnd) found = true } default: @@ -133,9 +137,9 @@ func searchMap(re *regexp.Regexp, data map[string]interface{}) bool { func searchMapString(re *regexp.Regexp, orderedMap *orderedmap.OrderedMap[string, string]) bool { found := false for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { - if re.MatchString(pair.Value) { - orderedMap.Set(pair.Key, re.ReplaceAllString(pair.Value, "$0")) + if replaced, matched := searchString(re, pair.Value); matched { found = true + orderedMap.Set(pair.Key, replaced) } } return found @@ -146,14 +150,14 @@ func searchArray(re *regexp.Regexp, data []any) bool { for i, value := range data { switch value := value.(type) { case string: - if re.MatchString(value) { - data[i] = re.ReplaceAllString(value, "$0") + if replaced, matched := searchString(re, value); matched { found = true + data[i] = replaced } case int, float64, bool: formatted := fmt.Sprintf("%v", value) if re.MatchString(formatted) { - data[i] = re.ReplaceAllString(formatted, "$0") + data[i] = re.ReplaceAllString(formatted, MarkerStart+"$0"+MarkerEnd) found = true } case []any: @@ -169,3 +173,12 @@ func searchArray(re *regexp.Regexp, data []any) bool { return found } + +func searchString(re *regexp.Regexp, value string) (string, bool) { + if re.MatchString(value) { + replaced := re.ReplaceAllString(value, MarkerStart+"$0"+MarkerEnd) + return replaced, true + } + + return value, false +} diff --git a/internal/container/escape.go b/internal/support/web/escape.go similarity index 55% rename from internal/container/escape.go rename to internal/support/web/escape.go index 9ddc0e68..8c843b77 100644 --- a/internal/container/escape.go +++ b/internal/support/web/escape.go @@ -1,16 +1,21 @@ -package container +package support_web import ( "html" + "strings" + "github.com/amir20/dozzle/internal/container" + "github.com/amir20/dozzle/internal/support/search" "github.com/rs/zerolog/log" orderedmap "github.com/wk8/go-ordered-map/v2" ) -func escape(logEvent *LogEvent) { +func EscapeHTMLValues(logEvent *container.LogEvent) { switch value := logEvent.Message.(type) { case string: - logEvent.Message = html.EscapeString(value) + value = html.EscapeString(value) + value = strings.ReplaceAll(value, search.MarkerStart, "") + logEvent.Message = strings.ReplaceAll(value, search.MarkerEnd, "") case *orderedmap.OrderedMap[string, any]: escapeAnyMap(value) @@ -33,7 +38,10 @@ func escapeAnyMap(orderedMap *orderedmap.OrderedMap[string, any]) { for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { switch value := pair.Value.(type) { case string: - orderedMap.Set(pair.Key, html.EscapeString(value)) + value = html.EscapeString(value) + value = strings.ReplaceAll(value, search.MarkerStart, "") + value = strings.ReplaceAll(value, search.MarkerEnd, "") + orderedMap.Set(pair.Key, value) case *orderedmap.OrderedMap[string, any]: escapeAnyMap(value) case *orderedmap.OrderedMap[string, string]: @@ -45,6 +53,9 @@ func escapeAnyMap(orderedMap *orderedmap.OrderedMap[string, any]) { func escapeStringMap(orderedMap *orderedmap.OrderedMap[string, string]) { for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { - orderedMap.Set(pair.Key, html.EscapeString(pair.Value)) + value := html.EscapeString(pair.Value) + value = strings.ReplaceAll(value, search.MarkerStart, "") + value = strings.ReplaceAll(value, search.MarkerEnd, "") + orderedMap.Set(pair.Key, value) } } diff --git a/internal/web/logs.go b/internal/web/logs.go index bf67ad6b..d8956379 100644 --- a/internal/web/logs.go +++ b/internal/web/logs.go @@ -158,6 +158,7 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request) break } + support_web.EscapeHTMLValues(event) // only escape when not exporting buffer.Push(event) } } @@ -391,6 +392,8 @@ loop: if _, ok := levels[logEvent.Level]; !ok { continue } + + support_web.EscapeHTMLValues(logEvent) sseWriter.Message(logEvent) case c := <-newContainers: if _, err := h.hostService.FindContainer(c.Host, c.ID, userLabels); err == nil { @@ -405,6 +408,9 @@ loop: } case backfillEvents := <-backfill: + for _, event := range backfillEvents { + support_web.EscapeHTMLValues(event) + } if err := sseWriter.Event("logs-backfill", backfillEvents); err != nil { log.Error().Err(err).Msg("error encoding container event") }