diff --git a/cmd/gomuks/media.go b/cmd/gomuks/media.go
index 858d974..e5bb635 100644
--- a/cmd/gomuks/media.go
+++ b/cmd/gomuks/media.go
@@ -38,6 +38,7 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/hlog"
_ "golang.org/x/image/webp"
+ "golang.org/x/net/html"
"go.mau.fi/util/exhttp"
"go.mau.fi/util/ffmpeg"
@@ -147,7 +148,7 @@ func isAllowedAvatarMime(mime string) bool {
func (w *avatarResponseWriter) WriteHeader(statusCode int) {
if statusCode != http.StatusOK && statusCode != http.StatusNotModified {
- data := []byte(fmt.Sprintf(fallbackAvatarTemplate, w.bgColor, w.character))
+ data := []byte(fmt.Sprintf(fallbackAvatarTemplate, w.bgColor, html.EscapeString(w.character)))
w.Header().Set("Content-Type", "image/svg+xml")
w.Header().Set("Content-Length", strconv.Itoa(len(data)))
w.Header().Del("Content-Disposition")
diff --git a/web/src/api/media.ts b/web/src/api/media.ts
index ec3d2b2..9822d2a 100644
--- a/web/src/api/media.ts
+++ b/web/src/api/media.ts
@@ -40,9 +40,17 @@ function makeFallbackAvatar(backgroundColor: string, fallbackCharacter: string):
${fallbackCharacter}
+ >${escapeHTMLChar(fallbackCharacter)}
`)
+}
+function escapeHTMLChar(char: string): string {
+ switch (char) {
+ case "&": return "&"
+ case "<": return "<"
+ case ">": return ">"
+ default: return char
+ }
}
export const getAvatarURL = (userID: UserID, content?: Partial): string | undefined => {