1
0
Fork 0
forked from External/grumble

Filter text according to server rules before storing.

This commit is contained in:
Mikkel Krautz 2011-05-29 02:28:57 +02:00
parent 4e89b124fb
commit 739cd1ca9b
3 changed files with 163 additions and 11 deletions

View file

@ -194,6 +194,8 @@ func (channel *Channel) Freeze() (fc frozenChannel, err os.Error) {
} }
fc.Links = links fc.Links = links
fc.DescriptionBlob = channel.DescriptionBlob
return return
} }

View file

@ -225,8 +225,11 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
// Extract the description and perform sanity checks. // Extract the description and perform sanity checks.
if chanstate.Description != nil { if chanstate.Description != nil {
description = *chanstate.Description description, err = server.FilterText(*chanstate.Description)
// fixme(mkrautz): Check length if err != nil {
client.sendPermissionDeniedType("TextTooLong")
return
}
} }
// Extract the the name of channel and check whether it's valid. // Extract the the name of channel and check whether it's valid.
@ -467,11 +470,15 @@ func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
// Description change // Description change
if chanstate.Description != nil { if chanstate.Description != nil {
key, err := blobstore.Put([]byte(*chanstate.Description)) if len(description) == 0 {
if err != nil { channel.DescriptionBlob = ""
server.Panicf("Blobstore error: %v", err.String()) } else {
key, err := blobstore.Put([]byte(description))
if err != nil {
server.Panicf("Blobstore error: %v", err.String())
}
channel.DescriptionBlob = key
} }
channel.DescriptionBlob = key
} }
// Position change // Position change
@ -654,17 +661,27 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
// Only allow empty text. // Only allow empty text.
if len(comment) > 0 { if len(comment) > 0 {
client.Panic("Cannot clear another user's comment") client.sendPermissionDeniedType("TextTooLong")
return return
} }
} }
// todo(mkrautz): Check if the text is allowed. filtered, err := server.FilterText(comment)
if err != nil {
client.sendPermissionDeniedType("TextTooLong")
return
}
userstate.Comment = proto.String(filtered)
} }
// Texture change // Texture change
if userstate.Texture != nil { if userstate.Texture != nil {
// Check the length of the texture maximg := server.cfg.IntValue("MaxImageMessageLength")
if maximg > 0 && len(userstate.Texture) > maximg {
client.sendPermissionDeniedType("TextTooLong")
return
}
} }
// Registration // Registration
@ -960,8 +977,17 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) {
return return
} }
// fixme(mkrautz): Check text message length. filtered, err := server.FilterText(*txtmsg.Message)
// fixme(mkrautz): Sanitize text as well. if err != nil {
client.sendPermissionDeniedType("TextTooLong")
return
}
if len(filtered) == 0 {
return
}
txtmsg.Message = proto.String(filtered)
clients := make(map[uint32]*Client) clients := make(map[uint32]*Client)

124
server.go
View file

@ -31,6 +31,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"xml"
) )
// The default port a Murmur server listens on // The default port a Murmur server listens on
@ -1190,6 +1191,129 @@ func (server *Server) IsBanned(conn net.Conn) bool {
return false return false
} }
// Filter incoming text according to the server's current rules.
func (server *Server) FilterText(text string) (filtered string, err os.Error) {
// This function filters incoming text from clients according to three server settings:
//
// AllowHTML:
// If false, all HTML shall be stripped.
// When stripping br tags, append a newline to the output stream.
// When stripping p tags, append a newline after the end tag.
//
// MaxTextMessageLength:
// Text length for "plain" messages (messages without images)
//
// MaxImageTextMessageLength:
// Text length for messages with images.
max := server.cfg.IntValue("MaxTextMessageLength")
maximg := server.cfg.IntValue("MaxImageMessageLength")
if !server.cfg.BoolValue("AllowHTML") {
if strings.Index(text, "<") == -1 {
filtered = strings.TrimSpace(text)
} else {
// Strip away all HTML
out := bytes.NewBuffer(nil)
buf := bytes.NewBufferString(text)
parser := xml.NewParser(buf)
parser.Strict = false
parser.AutoClose = xml.HTMLAutoClose
parser.Entity = xml.HTMLEntity
for {
tok, err := parser.Token()
if err == os.EOF {
break
} else if err != nil {
return "", err
}
switch t := tok.(type) {
case xml.CharData:
out.Write(t)
case xml.EndElement:
if t.Name.Local == "p" || t.Name.Local == "br" {
out.WriteString("\n")
}
}
}
filtered = strings.TrimSpace(out.String())
}
if max != 0 && len(filtered) > max {
return "", os.NewError("Message exceeds max length")
}
} else {
// No limits
if max == 0 && maximg == 0 {
return text, nil
}
// Too big for images?
if maximg != 0 && len(text) > maximg {
return "", os.NewError("Message exceeds max image message length")
}
// Under max plain length?
if max == 0 || len(text) <= max {
return text, nil
}
// Over max length, under image limit. If text doesn't include
// any HTML, this is a no-go. If there is XML, we can attempt to
// strip away data URIs to see if we can get the message to fit
// into the plain message limit.
if strings.Index(text, "<") == -1 {
return "", os.NewError("Over plain length")
}
// Simplify the received HTML data by stripping away data URIs
out := bytes.NewBuffer(nil)
buf := bytes.NewBufferString(text)
parser := xml.NewParser(buf)
parser.Strict = false
parser.AutoClose = xml.HTMLAutoClose
parser.Entity = xml.HTMLEntity
for {
tok, err := parser.Token()
if err == os.EOF {
break
} else if err != nil {
return "", err
}
switch t := tok.(type) {
case xml.CharData:
out.Write(t)
case xml.StartElement:
out.WriteString("<")
xml.Escape(out, []byte(t.Name.Local))
for _, attr := range t.Attr {
if t.Name.Local == "img" && attr.Name.Local == "src" {
continue
}
out.WriteString(" ")
xml.Escape(out, []byte(attr.Name.Local))
out.WriteString(`="`)
out.WriteString(attr.Value)
out.WriteString(`"`)
}
out.WriteString(">")
case xml.EndElement:
out.WriteString("</")
xml.Escape(out, []byte(t.Name.Local))
out.WriteString(">")
}
}
filtered = strings.TrimSpace(out.String())
if len(filtered) > max {
return "", os.NewError("Data URI stripped message longer than max length")
}
}
return
}
// The accept loop of the server. // The accept loop of the server.
func (s *Server) ListenAndMurmur() { func (s *Server) ListenAndMurmur() {
// Launch the event handler goroutine // Launch the event handler goroutine