fix: sanitize app status summary (#19075)

Fixes https://github.com/coder/coder/issues/18875
This commit is contained in:
Cian Johnston
2025-07-29 15:24:11 +01:00
committed by GitHub
parent 29486f9d4e
commit 812d72c5bb
5 changed files with 86 additions and 3 deletions
+40
View File
@@ -2,7 +2,12 @@ package strings
import (
"fmt"
"strconv"
"strings"
"unicode"
"github.com/acarl005/stripansi"
"github.com/microcosm-cc/bluemonday"
)
// JoinWithConjunction joins a slice of strings with commas except for the last
@@ -28,3 +33,38 @@ func Truncate(s string, n int) string {
}
return s[:n]
}
var bmPolicy = bluemonday.StrictPolicy()
// UISanitize sanitizes a string for display in the UI.
// The following transformations are applied, in order:
// - HTML tags are removed using bluemonday's strict policy.
// - ANSI escape codes are stripped using stripansi.
// - Consecutive backslashes are replaced with a single backslash.
// - Non-printable characters are removed.
// - Whitespace characters are replaced with spaces.
// - Multiple spaces are collapsed into a single space.
// - Leading and trailing whitespace is trimmed.
func UISanitize(in string) string {
if unq, err := strconv.Unquote(`"` + in + `"`); err == nil {
in = unq
}
in = bmPolicy.Sanitize(in)
in = stripansi.Strip(in)
var b strings.Builder
var spaceSeen bool
for _, r := range in {
if unicode.IsSpace(r) {
if !spaceSeen {
_, _ = b.WriteRune(' ')
spaceSeen = true
}
continue
}
spaceSeen = false
if unicode.IsPrint(r) {
_, _ = b.WriteRune(r)
}
}
return strings.TrimSpace(b.String())
}
+39
View File
@@ -3,6 +3,7 @@ package strings_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/util/strings"
@@ -37,3 +38,41 @@ func TestTruncate(t *testing.T) {
})
}
}
func TestUISanitize(t *testing.T) {
t.Parallel()
for _, tt := range []struct {
s string
expected string
}{
{"normal text", "normal text"},
{"\tfoo \r\\nbar ", "foo bar"},
{"通常のテキスト", "通常のテキスト"},
{"foo\nbar", "foo bar"},
{"foo\tbar", "foo bar"},
{"foo\rbar", "foo bar"},
{"foo\x00bar", "foobar"},
{"\u202Eabc", "abc"},
{"\u200Bzero width", "zero width"},
{"foo\x1b[31mred\x1b[0mbar", "fooredbar"},
{"foo\u0008bar", "foobar"},
{"foo\x07bar", "foobar"},
{"foo\uFEFFbar", "foobar"},
{"<a href='javascript:alert(1)'>link</a>", "link"},
{"<style>body{display:none}</style>", ""},
{"<html>HTML</html>", "HTML"},
{"<br>line break", "line break"},
{"<link rel='stylesheet' href='evil.css'>", ""},
{"<img src=1 onerror=alert(1)>", ""},
{"<!-- comment -->visible", "visible"},
{"<script>alert('xss')</script>", ""},
{"<iframe src='evil.com'></iframe>", ""},
} {
t.Run(tt.expected, func(t *testing.T) {
t.Parallel()
actual := strings.UISanitize(tt.s)
assert.Equal(t, tt.expected, actual)
})
}
}