From f85bf511034fb87910335df4b99690c491fc72f3 Mon Sep 17 00:00:00 2001 From: Azareal Date: Thu, 31 May 2018 16:51:31 +1000 Subject: [PATCH] Added a couple of simple anti-spam measures. Made progress with an internal error logging component for the Control Panel. Made LogWarning less susceptible to races and slightly improved log contention. Revamped the registration page on Cosora. Added the SanitiseSingleLine and SanitiseBody functions to better centralise sanitisation and to ensure more consistent sanitisation. Zero length spaces are no longer permitted in usernames to help prevent impersonation. More to come in this area. Plugins / internal components can now schedule hourly tasks. Reduced the chances of newlines breaking the visual layout in areas which expect none. Added the register_account_anti_spam phrase. --- common/common.go | 8 ++++++++ common/errors.go | 16 +++++++++++----- common/forum.go | 1 + common/parser.go | 5 ++--- common/tasks.go | 7 +++++++ common/template_init.go | 2 +- common/topic.go | 6 +----- common/topic_store.go | 6 ++---- common/utils.go | 22 ++++++++++++++++++++-- extend/guilds/lib/guilds.go | 5 ++--- gen_router.go | 19 +++++++------------ langs/english.json | 1 + main.go | 17 ++++++++++++++++- panel_routes.go | 8 ++++---- router_gen/main.go | 19 +++++++------------ routes/account.go | 28 ++++++++++++++++++++++------ routes/misc.go | 3 +-- routes/moderate.go | 3 +-- routes/panel/analytics.go | 13 ++++++------- routes/panel/settings.go | 3 +-- routes/reply.go | 6 +++--- routes/topic.go | 9 ++++----- templates/register.html | 13 ++++++++++++- themes/cosora/public/main.css | 19 +++++++++++++++++-- 24 files changed, 157 insertions(+), 82 deletions(-) diff --git a/common/common.go b/common/common.go index 39f6bcc4..b74b83de 100644 --- a/common/common.go +++ b/common/common.go @@ -3,6 +3,7 @@ package common import ( "database/sql" "log" + "sync/atomic" "time" "../query_gen/lib" @@ -23,6 +24,9 @@ const Petabyte int = Terabyte * 1024 var StartTime time.Time var TmplPtrMap = make(map[string]interface{}) +// Anti-spam token with rotated key +var JSTokenBox atomic.Value // TODO: Move this and some of these other globals somewhere else + // ErrNoRows is an alias of sql.ErrNoRows, just in case we end up with non-database/sql datastores var ErrNoRows = sql.ErrNoRows @@ -54,6 +58,10 @@ var ExecutableFileExts = StringList{ "exe", "jar", "phar", "shar", "iso", } +func init() { + JSTokenBox.Store("") +} + // TODO: Write a test for this func (slice StringList) Contains(needle string) bool { for _, item := range slice { diff --git a/common/errors.go b/common/errors.go index dc329c56..bdc85ce8 100644 --- a/common/errors.go +++ b/common/errors.go @@ -7,10 +7,16 @@ import ( "sync" ) -// TODO: Use the error_buffer variable to construct the system log in the Control Panel. Should we log errors caused by users too? Or just collect statistics on those or do nothing? Intercept recover()? Could we intercept the logger instead here? We might get too much information, if we intercept the logger, maybe make it part of the Debug page? +type ErrorItem struct { + error + Stack []byte +} + +// ! The errorBuffer uses o(n) memory, we should probably do something about that +// TODO: Use the errorBuffer variable to construct the system log in the Control Panel. Should we log errors caused by users too? Or just collect statistics on those or do nothing? Intercept recover()? Could we intercept the logger instead here? We might get too much information, if we intercept the logger, maybe make it part of the Debug page? // ? - Should we pass Header / HeaderLite rather than forcing the errors to pull the global Header instance? var errorBufferMutex sync.RWMutex -var errorBuffer []error +var errorBuffer []ErrorItem //var notfoundCountPerSecond int //var nopermsCountPerSecond int @@ -66,11 +72,11 @@ func LogError(err error) { } func LogWarning(err error) { - log.Print(err) - debug.PrintStack() + stack := debug.Stack() + log.Print(err.Error()+"\n", string(stack)) errorBufferMutex.Lock() defer errorBufferMutex.Unlock() - errorBuffer = append(errorBuffer, err) + errorBuffer = append(errorBuffer, ErrorItem{err, stack}) } // TODO: Dump the request? diff --git a/common/forum.go b/common/forum.go index 94f3c22e..6f467b16 100644 --- a/common/forum.go +++ b/common/forum.go @@ -76,6 +76,7 @@ func (forum *Forum) Update(name string, desc string, active bool, preset string) if name == "" { name = forum.Name } + // TODO: Do a line sanitise? Does it matter? preset = strings.TrimSpace(preset) _, err := forumStmts.update.Exec(name, desc, active, preset, forum.ID) if err != nil { diff --git a/common/parser.go b/common/parser.go index af7d44f4..f86231dd 100644 --- a/common/parser.go +++ b/common/parser.go @@ -2,7 +2,6 @@ package common import ( "bytes" - "html" "net/url" "regexp" "strconv" @@ -171,9 +170,9 @@ func PreparseMessage(msg string) string { msg = strings.Replace(msg, "

", "\n\n", -1) msg = strings.Replace(msg, "

", "", -1) msg = strings.Replace(msg, "
", "\n\n", -1) - msg = strings.TrimSpace(msg) // There are a few useful cases for having spaces, but I'd like to stop the WYSIWYG from inserting random lines here and there msg = RunSshook("preparse_preassign", msg) - msg = html.EscapeString(msg) + // There are a few useful cases for having spaces, but I'd like to stop the WYSIWYG from inserting random lines here and there + msg = SanitiseBody(msg) msg = strings.Replace(msg, " ", "", -1) var runes = []rune(msg) diff --git a/common/tasks.go b/common/tasks.go index aeaabde7..00902ceb 100644 --- a/common/tasks.go +++ b/common/tasks.go @@ -22,6 +22,7 @@ type TaskStmts struct { var ScheduledHalfSecondTasks []func() error var ScheduledSecondTasks []func() error var ScheduledFifteenMinuteTasks []func() error +var ScheduledHourTasks []func() error var ShutdownTasks []func() error var taskStmts TaskStmts var lastSync time.Time @@ -53,6 +54,11 @@ func AddScheduledFifteenMinuteTask(task func() error) { ScheduledFifteenMinuteTasks = append(ScheduledFifteenMinuteTasks, task) } +// AddScheduledHourTask is not concurrency safe +func AddScheduledHourTask(task func() error) { + ScheduledHourTasks = append(ScheduledHourTasks, task) +} + // AddShutdownTask is not concurrency safe func AddShutdownTask(task func() error) { ShutdownTasks = append(ShutdownTasks, task) @@ -86,6 +92,7 @@ func HandleExpiredScheduledGroups() error { // TODO: Use AddScheduledSecondTask // TODO: Be a little more granular with the synchronisation +// TODO: Synchronise more things func HandleServerSync() error { // We don't want to run any unnecessary queries when there is nothing to synchronise /*if Config.ServerCount > 1 { diff --git a/common/template_init.go b/common/template_init.go index a48ca4a9..cc55e41c 100644 --- a/common/template_init.go +++ b/common/template_init.go @@ -233,7 +233,7 @@ func CompileTemplates() error { return err } - registerPage := Page{"Registration Page", user, header, tList, nil} + registerPage := Page{"Registration Page", user, header, tList, "nananana"} registerTmpl, err := c.Compile("register.html", "templates/", "common.Page", registerPage, varList) if err != nil { return err diff --git a/common/topic.go b/common/topic.go index 78e8b41a..3b45141d 100644 --- a/common/topic.go +++ b/common/topic.go @@ -11,7 +11,6 @@ import ( "html" "html/template" "strconv" - "strings" "time" "../query_gen/lib" @@ -257,10 +256,7 @@ func (topic *Topic) Delete() error { // TODO: Write tests for this func (topic *Topic) Update(name string, content string) error { - name = html.EscapeString(strings.Replace(html.UnescapeString(name), "\n", "", -1)) - - // TODO: Stop duplicating this logic? - name = strings.TrimSpace(name) + name = SanitiseSingleLine(html.UnescapeString(name)) if name == "" { return ErrNoTitle } diff --git a/common/topic_store.go b/common/topic_store.go index b1bbf5e4..6c9279c2 100644 --- a/common/topic_store.go +++ b/common/topic_store.go @@ -121,7 +121,6 @@ func (mts *DefaultTopicStore) Exists(id int) bool { } func (mts *DefaultTopicStore) Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error) { - topicName = strings.TrimSpace(topicName) if topicName == "" { return 0, ErrNoTitle } @@ -130,9 +129,8 @@ func (mts *DefaultTopicStore) Create(fid int, topicName string, content string, return 0, ErrLongTitle } - content = strings.TrimSpace(content) - parsedContent := ParseMessage(content, fid, "forums") - if strings.TrimSpace(parsedContent) == "" { + parsedContent := strings.TrimSpace(ParseMessage(content, fid, "forums")) + if parsedContent == "" { return 0, ErrNoBody } diff --git a/common/utils.go b/common/utils.go index 50fd7ab6..a95a7c6c 100644 --- a/common/utils.go +++ b/common/utils.go @@ -11,6 +11,7 @@ import ( "encoding/base64" "errors" "fmt" + "html" "math" "os" "strconv" @@ -119,9 +120,8 @@ func ConvertByteUnit(bytes float64) (float64, string) { return bytes / float64(Megabyte), "MB" case bytes >= float64(Kilobyte): return bytes / float64(Kilobyte), "KB" - default: - return bytes, " bytes" } + return bytes, " bytes" } // TODO: Write a test for this @@ -387,6 +387,24 @@ func GetLevels(maxLevel int) []float64 { return out } +// TODO: Write a test for this +// SanitiseSingleLine is a generic function for escaping html entities and removing silly characters from usernames and topic titles. It also strips newline characters +func SanitiseSingleLine(in string) string { + in = strings.Replace(in, "\n", "", -1) + in = strings.Replace(in, "\r", "", -1) + return SanitiseBody(in) +} + +// TODO: Write a test for this +// TODO: Add more strange characters +// TODO: Strip all sub-32s minus \r and \n? +// SanitiseBody is the same as SanitiseSingleLine, but it doesn't strip newline characters +func SanitiseBody(in string) string { + in = strings.Replace(in, "​", "", -1) // Strip Zero length space + in = html.EscapeString(in) + return strings.TrimSpace(in) +} + func BuildSlug(slug string, id int) string { if slug == "" || !Config.BuildSlugs { return strconv.Itoa(id) diff --git a/extend/guilds/lib/guilds.go b/extend/guilds/lib/guilds.go index a3758b93..487becb3 100644 --- a/extend/guilds/lib/guilds.go +++ b/extend/guilds/lib/guilds.go @@ -5,7 +5,6 @@ import ( "context" "database/sql" "errors" - "html" "html/template" "net/http" "strconv" @@ -254,9 +253,9 @@ func RouteCreateGuildSubmit(w http.ResponseWriter, r *http.Request, user common. } var guildActive = true - var guildName = html.EscapeString(r.PostFormValue("group_name")) + var guildName = common.SanitiseSingleLine(r.PostFormValue("group_name")) // TODO: Allow Markdown / BBCode / Limited HTML in the description? - var guildDesc = html.EscapeString(r.PostFormValue("group_desc")) + var guildDesc = common.SanitiseBody(r.PostFormValue("group_desc")) var gprivacy = r.PostFormValue("group_privacy") var guildPrivacy int diff --git a/gen_router.go b/gen_router.go index 37dcffee..ee6e858a 100644 --- a/gen_router.go +++ b/gen_router.go @@ -574,26 +574,21 @@ func (router *GenRouter) RemoveFunc(pattern string) error { return nil } -func (router *GenRouter) StripNewlines(data string) string { - // TODO: Strip out all sub-32s? - return strings.Replace(strings.Replace(data,"\n","",-1),"\r","",-1) -} - func (router *GenRouter) DumpRequest(req *http.Request, prepend string) { var heads string for key, value := range req.Header { for _, vvalue := range value { - heads += "Header '" + router.StripNewlines(key) + "': " + router.StripNewlines(vvalue) + "!!\n" + heads += "Header '" + common.SanitiseSingleLine(key) + "': " + common.SanitiseSingleLine(vvalue) + "!!\n" } } router.requestLogger.Print(prepend + - "\nUA: " + router.StripNewlines(req.UserAgent()) + "\n" + - "Method: " + router.StripNewlines(req.Method) + "\n" + heads + - "req.Host: " + router.StripNewlines(req.Host) + "\n" + - "req.URL.Path: " + router.StripNewlines(req.URL.Path) + "\n" + - "req.URL.RawQuery: " + router.StripNewlines(req.URL.RawQuery) + "\n" + - "req.Referer(): " + router.StripNewlines(req.Referer()) + "\n" + + "\nUA: " + common.SanitiseSingleLine(req.UserAgent()) + "\n" + + "Method: " + common.SanitiseSingleLine(req.Method) + "\n" + heads + + "req.Host: " + common.SanitiseSingleLine(req.Host) + "\n" + + "req.URL.Path: " + common.SanitiseSingleLine(req.URL.Path) + "\n" + + "req.URL.RawQuery: " + common.SanitiseSingleLine(req.URL.RawQuery) + "\n" + + "req.Referer(): " + common.SanitiseSingleLine(req.Referer()) + "\n" + "req.RemoteAddr: " + req.RemoteAddr + "\n") } diff --git a/langs/english.json b/langs/english.json index 0d3f2a45..37dd32b3 100644 --- a/langs/english.json +++ b/langs/english.json @@ -344,6 +344,7 @@ "register_account_email":"Email", "register_account_password":"Password", "register_account_confirm_password":"Confirm Password", + "register_account_anti_spam":"Are you a spambot?", "register_submit_button":"Create Account", "account_menu_head":"My Account", diff --git a/main.go b/main.go index f588f3ec..9029c845 100644 --- a/main.go +++ b/main.go @@ -217,6 +217,12 @@ func main() { fmt.Println("") common.StartTime = time.Now() + jsToken, err := common.GenerateSafeString(80) + if err != nil { + log.Fatal(err) + } + common.JSTokenBox.Store(jsToken) + log.Print("Processing configuration data") err = common.ProcessConfig() if err != nil { @@ -338,7 +344,7 @@ func main() { halfSecondTicker := time.NewTicker(time.Second / 2) secondTicker := time.NewTicker(time.Second) fifteenMinuteTicker := time.NewTicker(15 * time.Minute) - //hourTicker := time.NewTicker(time.Hour) + hourTicker := time.NewTicker(time.Hour) go func() { var runHook = func(name string) { err := common.RunTaskHook(name) @@ -382,6 +388,15 @@ func main() { // TODO: Automatically lock topics, if they're really old, and the associated setting is enabled. // TODO: Publish scheduled posts. runHook("after_fifteen_minute_tick") + case <-hourTicker.C: + runHook("before_hour_tick") + jsToken, err := common.GenerateSafeString(80) + if err != nil { + common.LogError(err) + } + common.JSTokenBox.Store(jsToken) + runTasks(common.ScheduledHourTasks) + runHook("after_hour_tick") } // TODO: Handle the daily clean-up. diff --git a/panel_routes.go b/panel_routes.go index 72440b66..4d1995f7 100644 --- a/panel_routes.go +++ b/panel_routes.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "html" "log" "net/http" "strconv" @@ -189,6 +188,7 @@ func routePanelWordFiltersCreateSubmit(w http.ResponseWriter, r *http.Request, u } isJs := (r.PostFormValue("js") == "1") + // ? - We're not doing a full sanitise here, as it would be useful if admins were able to put down rules for replacing things with HTML, etc. find := strings.TrimSpace(r.PostFormValue("find")) if find == "" { return common.LocalErrorJSQ("You need to specify what word you want to match", w, r, user, isJs) @@ -573,14 +573,14 @@ func routePanelUsersEditSubmit(w http.ResponseWriter, r *http.Request, user comm return common.LocalError("Only administrators can edit the account of other administrators.", w, r, user) } - newname := html.EscapeString(strings.Replace(r.PostFormValue("user-name"), "\n", "", -1)) + newname := common.SanitiseSingleLine(r.PostFormValue("user-name")) if newname == "" { return common.LocalError("You didn't put in a username.", w, r, user) } // TODO: How should activation factor into admin set emails? // TODO: How should we handle secondary emails? Do we even have secondary emails implemented? - newemail := html.EscapeString(strings.Replace(r.PostFormValue("user-email"), "\n", "", -1)) + newemail := common.SanitiseSingleLine(r.PostFormValue("user-email")) if newemail == "" { return common.LocalError("You didn't put in an email address.", w, r, user) } @@ -1222,7 +1222,7 @@ func routePanelThemesMenuItemEdit(w http.ResponseWriter, r *http.Request, user c func routePanelThemesMenuItemSetters(r *http.Request, menuItem common.MenuItem) common.MenuItem { var getItem = func(name string) string { - return html.EscapeString(strings.Replace(r.PostFormValue("item-"+name), "\n", "", -1)) + return common.SanitiseSingleLine(r.PostFormValue("item-" + name)) } menuItem.Name = getItem("name") menuItem.HTMLID = getItem("htmlid") diff --git a/router_gen/main.go b/router_gen/main.go index 723e0c03..793d97e7 100644 --- a/router_gen/main.go +++ b/router_gen/main.go @@ -390,26 +390,21 @@ func (router *GenRouter) RemoveFunc(pattern string) error { return nil } -func (router *GenRouter) StripNewlines(data string) string { - // TODO: Strip out all sub-32s? - return strings.Replace(strings.Replace(data,"\n","",-1),"\r","",-1) -} - func (router *GenRouter) DumpRequest(req *http.Request, prepend string) { var heads string for key, value := range req.Header { for _, vvalue := range value { - heads += "Header '" + router.StripNewlines(key) + "': " + router.StripNewlines(vvalue) + "!!\n" + heads += "Header '" + common.SanitiseSingleLine(key) + "': " + common.SanitiseSingleLine(vvalue) + "!!\n" } } router.requestLogger.Print(prepend + - "\nUA: " + router.StripNewlines(req.UserAgent()) + "\n" + - "Method: " + router.StripNewlines(req.Method) + "\n" + heads + - "req.Host: " + router.StripNewlines(req.Host) + "\n" + - "req.URL.Path: " + router.StripNewlines(req.URL.Path) + "\n" + - "req.URL.RawQuery: " + router.StripNewlines(req.URL.RawQuery) + "\n" + - "req.Referer(): " + router.StripNewlines(req.Referer()) + "\n" + + "\nUA: " + common.SanitiseSingleLine(req.UserAgent()) + "\n" + + "Method: " + common.SanitiseSingleLine(req.Method) + "\n" + heads + + "req.Host: " + common.SanitiseSingleLine(req.Host) + "\n" + + "req.URL.Path: " + common.SanitiseSingleLine(req.URL.Path) + "\n" + + "req.URL.RawQuery: " + common.SanitiseSingleLine(req.URL.RawQuery) + "\n" + + "req.Referer(): " + common.SanitiseSingleLine(req.Referer()) + "\n" + "req.RemoteAddr: " + req.RemoteAddr + "\n") } diff --git a/routes/account.go b/routes/account.go index c52beeca..d3dafc01 100644 --- a/routes/account.go +++ b/routes/account.go @@ -1,8 +1,9 @@ package routes import ( + "crypto/sha256" "database/sql" - "html" + "encoding/hex" "io" "log" "net/http" @@ -45,7 +46,7 @@ func AccountLoginSubmit(w http.ResponseWriter, r *http.Request, user common.User return common.LocalError("You're already logged in.", w, r, user) } - username := html.EscapeString(strings.Replace(r.PostFormValue("username"), "\n", "", -1)) + username := common.SanitiseSingleLine(r.PostFormValue("username")) uid, err := common.Auth.Authenticate(username, r.PostFormValue("password")) if err != nil { return common.LocalError(err.Error(), w, r, user) @@ -91,7 +92,11 @@ func AccountRegister(w http.ResponseWriter, r *http.Request, user common.User) c if user.Loggedin { return common.LocalError("You're already logged in.", w, r, user) } - pi := common.Page{common.GetTitlePhrase("register"), user, header, tList, nil} + h := sha256.New() + h.Write([]byte(common.JSTokenBox.Load().(string))) + h.Write([]byte(user.LastIP)) + jsToken := hex.EncodeToString(h.Sum(nil)) + pi := common.Page{common.GetTitlePhrase("register"), user, header, tList, jsToken} if common.RunPreRenderHook("pre_render_register", w, r, &user, &pi) { return nil } @@ -117,8 +122,19 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user common.U regErrReason += reason + "|" } - username := html.EscapeString(strings.Replace(r.PostFormValue("username"), "\n", "", -1)) - email := html.EscapeString(strings.Replace(r.PostFormValue("email"), "\n", "", -1)) + if r.PostFormValue("tos") != "0" { + regError("You might be a machine", "trap-question") + } + h := sha256.New() + h.Write([]byte(common.JSTokenBox.Load().(string))) + h.Write([]byte(user.LastIP)) + if r.PostFormValue("antispam") != hex.EncodeToString(h.Sum(nil)) { + regError("You might be a machine", "js-antispam") + } + + username := common.SanitiseSingleLine(r.PostFormValue("username")) + // TODO: Add a dedicated function for validating emails + email := common.SanitiseSingleLine(r.PostFormValue("email")) if username == "" { regError("You didn't put in a username.", "no-username") } @@ -390,7 +406,7 @@ func AccountEditUsernameSubmit(w http.ResponseWriter, r *http.Request, user comm return ferr } - newUsername := html.EscapeString(strings.Replace(r.PostFormValue("account-new-username"), "\n", "", -1)) + newUsername := common.SanitiseSingleLine(r.PostFormValue("account-new-username")) err := user.ChangeName(newUsername) if err != nil { return common.LocalError("Unable to change the username. Does someone else already have this name?", w, r, user) diff --git a/routes/misc.go b/routes/misc.go index ffe5fb03..8548e110 100644 --- a/routes/misc.go +++ b/routes/misc.go @@ -3,7 +3,6 @@ package routes import ( "bytes" "database/sql" - "html" "io" "net/http" "path/filepath" @@ -157,7 +156,7 @@ func ChangeTheme(w http.ResponseWriter, r *http.Request, user common.User) commo //headerLite, _ := SimpleUserCheck(w, r, &user) // TODO: Rename isJs to something else, just in case we rewrite the JS side in WebAssembly? isJs := (r.PostFormValue("isJs") == "1") - newTheme := html.EscapeString(r.PostFormValue("newTheme")) + newTheme := common.SanitiseSingleLine(r.PostFormValue("newTheme")) theme, ok := common.Themes[newTheme] if !ok || theme.HideFromThemes { diff --git a/routes/moderate.go b/routes/moderate.go index a3009cfa..913f6b8a 100644 --- a/routes/moderate.go +++ b/routes/moderate.go @@ -1,7 +1,6 @@ package routes import ( - "html" "net/http" "../common" @@ -18,7 +17,7 @@ func IPSearch(w http.ResponseWriter, r *http.Request, user common.User) common.R } // TODO: Reject IP Addresses with illegal characters - var ip = html.EscapeString(r.FormValue("ip")) + var ip = common.SanitiseSingleLine(r.FormValue("ip")) uids, err := common.IPSearch.Lookup(ip) if err != nil { return common.InternalError(err, w, r) diff --git a/routes/panel/analytics.go b/routes/panel/analytics.go index 1d32d381..f670aebb 100644 --- a/routes/panel/analytics.go +++ b/routes/panel/analytics.go @@ -3,7 +3,6 @@ package panel import ( "database/sql" "errors" - "html" "log" "net/http" "strconv" @@ -185,7 +184,7 @@ func AnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user common.Use graph := common.PanelTimeGraph{Series: viewList, Labels: labelList} common.DebugLogf("graph: %+v\n", graph) - pi := common.PanelAnalyticsRoutePage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", html.EscapeString(route), graph, viewItems, timeRange.Range} + pi := common.PanelAnalyticsRoutePage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", common.SanitiseSingleLine(route), graph, viewItems, timeRange.Range} return panelRenderTemplate("panel_analytics_route_views", w, r, user, &pi) } @@ -205,7 +204,7 @@ func AnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user common.Use revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange) // ? Only allow valid agents? The problem with this is that agents wind up getting renamed and it would take a migration to get them all up to snuff - agent = html.EscapeString(agent) + agent = common.SanitiseSingleLine(agent) common.DebugLog("in panel.AnalyticsAgentViews") acc := qgen.Builder.Accumulator() @@ -299,7 +298,7 @@ func AnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user common.Us return common.LocalError(err.Error(), w, r, user) } revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange) - system = html.EscapeString(system) + system = common.SanitiseSingleLine(system) common.DebugLog("in panel.AnalyticsSystemViews") acc := qgen.Builder.Accumulator() @@ -344,7 +343,7 @@ func AnalyticsLanguageViews(w http.ResponseWriter, r *http.Request, user common. return common.LocalError(err.Error(), w, r, user) } revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange) - lang = html.EscapeString(lang) + lang = common.SanitiseSingleLine(lang) common.DebugLog("in panel.AnalyticsLanguageViews") acc := qgen.Builder.Accumulator() @@ -410,7 +409,7 @@ func AnalyticsReferrerViews(w http.ResponseWriter, r *http.Request, user common. graph := common.PanelTimeGraph{Series: viewList, Labels: labelList} common.DebugLogf("graph: %+v\n", graph) - pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", html.EscapeString(domain), "", graph, timeRange.Range} + pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", common.SanitiseSingleLine(domain), "", graph, timeRange.Range} return panelRenderTemplate("panel_analytics_referrer_views", w, r, user, &pi) } @@ -736,7 +735,7 @@ func AnalyticsReferrers(w http.ResponseWriter, r *http.Request, user common.User var refItems []common.PanelAnalyticsAgentsItem for domain, count := range refMap { refItems = append(refItems, common.PanelAnalyticsAgentsItem{ - Agent: html.EscapeString(domain), + Agent: common.SanitiseSingleLine(domain), Count: count, }) } diff --git a/routes/panel/settings.go b/routes/panel/settings.go index cf18af67..33eefa62 100644 --- a/routes/panel/settings.go +++ b/routes/panel/settings.go @@ -3,7 +3,6 @@ package panel import ( "database/sql" "fmt" - "html" "net/http" "strconv" "strings" @@ -104,7 +103,7 @@ func SettingEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, return common.NoPermissions(w, r, user) } - scontent := html.EscapeString(r.PostFormValue("setting-value")) + scontent := common.SanitiseBody(r.PostFormValue("setting-value")) err := headerLite.Settings.Update(sname, scontent) if err != nil { if common.SafeSettingError(err) { diff --git a/routes/reply.go b/routes/reply.go index af9541c0..e3c41343 100644 --- a/routes/reply.go +++ b/routes/reply.go @@ -4,7 +4,6 @@ import ( "crypto/sha256" "database/sql" "encoding/hex" - "html" "io" "log" "net/http" @@ -142,8 +141,9 @@ func CreateReplySubmit(w http.ResponseWriter, r *http.Request, user common.User) // If there are duplicates, then something has gone horribly wrong, so let's ignore them, this'll likely happen during an attack _, exists := pollInputItems[index] - if !exists && len(html.EscapeString(value)) != 0 { - pollInputItems[index] = html.EscapeString(value) + // TODO: Should we use SanitiseBody instead to keep the newlines? + if !exists && len(common.SanitiseSingleLine(value)) != 0 { + pollInputItems[index] = common.SanitiseSingleLine(value) if len(pollInputItems) >= maxPollOptions { break } diff --git a/routes/topic.go b/routes/topic.go index 2fabe805..f77a27a7 100644 --- a/routes/topic.go +++ b/routes/topic.go @@ -5,7 +5,6 @@ import ( "database/sql" "encoding/hex" "encoding/json" - "html" "io" "log" "net/http" @@ -334,7 +333,7 @@ func CreateTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User) return common.NoPermissions(w, r, user) } - topicName := html.EscapeString(strings.Replace(r.PostFormValue("topic-name"), "\n", "", -1)) + topicName := common.SanitiseSingleLine(r.PostFormValue("topic-name")) content := common.PreparseMessage(r.PostFormValue("topic-content")) // TODO: Fully parse the post and store it in the parsed column tid, err := common.Topics.Create(fid, topicName, content, user.ID, user.LastIP) @@ -377,9 +376,9 @@ func CreateTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User) // If there are duplicates, then something has gone horribly wrong, so let's ignore them, this'll likely happen during an attack _, exists := pollInputItems[index] - if !exists && len(html.EscapeString(value)) != 0 { - pollInputItems[index] = html.EscapeString(value) - + // TODO: Should we use SanitiseBody instead to keep the newlines? + if !exists && len(common.SanitiseSingleLine(value)) != 0 { + pollInputItems[index] = common.SanitiseSingleLine(value) if len(pollInputItems) >= maxPollOptions { break } diff --git a/templates/register.html b/templates/register.html index 5a6cb485..669198ab 100644 --- a/templates/register.html +++ b/templates/register.html @@ -21,10 +21,21 @@
{{lang "register_account_confirm_password"}}
-
+
{{/** This is not a TOS, that text is there to fool the spambots **/}} + +
+
+
+
+ {{template "footer.html" . }} diff --git a/themes/cosora/public/main.css b/themes/cosora/public/main.css index 424a1c45..30afef91 100644 --- a/themes/cosora/public/main.css +++ b/themes/cosora/public/main.css @@ -1292,7 +1292,7 @@ textarea { margin-bottom: 10px; } -#create_topic_page .close_form, #create_topic_page .formlabel, #login_page .formlabel, #register_page .formlabel { +#create_topic_page .close_form, #create_topic_page .formlabel, #login_page .formlabel { display: none; } #login_page .formrow:not(:first-child):not(:last-child), #register_page .formrow:not(:first-child):not(:last-child) { @@ -1301,12 +1301,27 @@ textarea { #login_page .formrow:not(:first-child), #register_page .formrow:not(:first-child) { padding-top: 3px; } -#login_page .formrow:not(:last-child), #register_page .formrow:not(:last-child) { +#login_page .formrow:not(:last-child) { padding-bottom: 0px; } #login_page .formrow, #register_page .formrow { padding: 16px; } +#register_page .formrow:not(:last-child) { + padding-bottom: 4px; +} +#register_page .formlabel { + display: block; + font-size: 15px; +} +#register_page .register_button_row { + padding: 12px !important; + padding-top: 0px !important; + margin-top: -2px !important; +} +#register_page .register_button_row .formbutton { + margin-left: 2px; +} /* TODO: Add a generic button_row class and add this to them all? */ .login_button_row {