From d6b7a5925f20ba7ca3d9f210188a3f1ceba07cf1 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Wed, 29 Jan 2020 09:11:02 -0500 Subject: [PATCH 1/2] Restrict /invite/{code} route to valid chars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, loading something like /invite/fFdblk😄 would return a 500, due to a mix of collations in MySQL while SELECTing for an invite with an ID of 'fFdblk😄'. This restricts the route to [a-zA-Z0-9] chars, to prevent this. --- routes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes.go b/routes.go index ba531fb..023aca2 100644 --- a/routes.go +++ b/routes.go @@ -161,7 +161,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router { // Handle special pages first write.HandleFunc("/login", handler.Web(viewLogin, UserLevelNoneRequired)) write.HandleFunc("/signup", handler.Web(handleViewLanding, UserLevelNoneRequired)) - write.HandleFunc("/invite/{code}", handler.Web(handleViewInvite, UserLevelOptional)).Methods("GET") + write.HandleFunc("/invite/{code:[a-zA-Z0-9]+}", handler.Web(handleViewInvite, UserLevelOptional)).Methods("GET") // TODO: show a reader-specific 404 page if the function is disabled write.HandleFunc("/read", handler.Web(viewLocalTimeline, UserLevelReader)) RouteRead(handler, UserLevelReader, write.PathPrefix("/read").Subrouter()) From 51700cc7da47ad205187435ff8beaec2535b926f Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Thu, 30 Jan 2020 10:36:29 +0100 Subject: [PATCH 2/2] Ignore "mix of collations" error on invite SELECT This adds the `isIgnorableError` method and calls it when error checking in `GetUserInvite()`, returning "not found" if the rror comes up. --- database-no-sqlite.go | 14 +++++++++++++- database-sqlite.go | 12 ++++++++++++ database.go | 5 +++-- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/database-no-sqlite.go b/database-no-sqlite.go index a3d50fc..03d1a32 100644 --- a/database-no-sqlite.go +++ b/database-no-sqlite.go @@ -1,7 +1,7 @@ // +build !sqlite,!wflib /* - * Copyright © 2019 A Bunch Tell LLC. + * Copyright © 2019-2020 A Bunch Tell LLC. * * This file is part of WriteFreely. * @@ -28,3 +28,15 @@ func (db *datastore) isDuplicateKeyErr(err error) bool { return false } + +func (db *datastore) isIgnorableError(err error) bool { + if db.driverName == driverMySQL { + if mysqlErr, ok := err.(*mysql.MySQLError); ok { + return mysqlErr.Number == mySQLErrCollationMix + } + } else { + log.Error("isIgnorableError: failed check for unrecognized driver '%s'", db.driverName) + } + + return false +} diff --git a/database-sqlite.go b/database-sqlite.go index 3741169..bd77e6a 100644 --- a/database-sqlite.go +++ b/database-sqlite.go @@ -48,3 +48,15 @@ func (db *datastore) isDuplicateKeyErr(err error) bool { return false } + +func (db *datastore) isIgnorableError(err error) bool { + if db.driverName == driverMySQL { + if mysqlErr, ok := err.(*mysql.MySQLError); ok { + return mysqlErr.Number == mySQLErrCollationMix + } + } else { + log.Error("isIgnorableError: failed check for unrecognized driver '%s'", db.driverName) + } + + return false +} diff --git a/database.go b/database.go index ef52d84..4948452 100644 --- a/database.go +++ b/database.go @@ -1,5 +1,5 @@ /* - * Copyright © 2018 A Bunch Tell LLC. + * Copyright © 2018-2020 A Bunch Tell LLC. * * This file is part of WriteFreely. * @@ -37,6 +37,7 @@ import ( const ( mySQLErrDuplicateKey = 1062 + mySQLErrCollationMix = 1267 driverMySQL = "mysql" driverSQLite = "sqlite3" @@ -2281,7 +2282,7 @@ func (db *datastore) GetUserInvite(id string) (*Invite, error) { var i Invite err := db.QueryRow("SELECT id, max_uses, created, expires, inactive FROM userinvites WHERE id = ?", id).Scan(&i.ID, &i.MaxUses, &i.Created, &i.Expires, &i.Inactive) switch { - case err == sql.ErrNoRows: + case err == sql.ErrNoRows, db.isIgnorableError(err): return nil, impart.HTTPError{http.StatusNotFound, "Invite doesn't exist."} case err != nil: log.Error("Failed selecting invite: %v", err)