mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-18036] Sanitize sql LIKE terms on search endpoints (#12044)
* Sanitize sql LIKE terms on search endpoints * Add search term sanitization in additional places
This commit is contained in:
committed by
Daniel Schalla
parent
8b969426f0
commit
814c234443
@@ -2259,17 +2259,7 @@ func (s SqlChannelStore) SearchMore(userId string, teamId string, term string) (
|
||||
}
|
||||
|
||||
func (s SqlChannelStore) buildLIKEClause(term string, searchColumns string) (likeClause, likeTerm string) {
|
||||
likeTerm = term
|
||||
|
||||
// These chars must be removed from the like query.
|
||||
for _, c := range ignoreLikeSearchChar {
|
||||
likeTerm = strings.Replace(likeTerm, c, "", -1)
|
||||
}
|
||||
|
||||
// These chars must be escaped in the like query.
|
||||
for _, c := range escapeLikeSearchChar {
|
||||
likeTerm = strings.Replace(likeTerm, c, "*"+c, -1)
|
||||
}
|
||||
likeTerm = sanitizeSearchTerm(term, "*")
|
||||
|
||||
if likeTerm == "" {
|
||||
return
|
||||
@@ -2429,6 +2419,7 @@ func (s SqlChannelStore) getSearchGroupChannelsQuery(userId, term string, isPost
|
||||
|
||||
for idx, term := range terms {
|
||||
argName := fmt.Sprintf("Term%v", idx)
|
||||
term = sanitizeSearchTerm(term, "\\")
|
||||
likeClauses = append(likeClauses, fmt.Sprintf(baseLikeClause, ":"+argName))
|
||||
args[argName] = "%" + term + "%"
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ func (s SqlComplianceStore) ComplianceExport(job *model.Compliance) ([]*model.Co
|
||||
keywordQuery = "AND ("
|
||||
|
||||
for index, keyword := range keywords {
|
||||
keyword = sanitizeSearchTerm(keyword, "\\")
|
||||
if index >= 1 {
|
||||
keywordQuery += " OR LOWER(Posts.Message) LIKE :Keyword" + strconv.Itoa(index)
|
||||
} else {
|
||||
|
||||
@@ -147,6 +147,8 @@ func (es SqlEmojiStore) Delete(emoji *model.Emoji, time int64) *model.AppError {
|
||||
func (es SqlEmojiStore) Search(name string, prefixOnly bool, limit int) ([]*model.Emoji, *model.AppError) {
|
||||
var emojis []*model.Emoji
|
||||
|
||||
name = sanitizeSearchTerm(name, "\\")
|
||||
|
||||
term := ""
|
||||
if !prefixOnly {
|
||||
term = "%"
|
||||
|
||||
@@ -853,7 +853,7 @@ func (s *SqlGroupStore) groupsBySyncableBaseQuery(st model.GroupSyncableType, t
|
||||
}
|
||||
|
||||
if len(opts.Q) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", opts.Q)
|
||||
pattern := fmt.Sprintf("%%%s%%", sanitizeSearchTerm(opts.Q, "\\"))
|
||||
operatorKeyword := "ILIKE"
|
||||
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
||||
operatorKeyword = "LIKE"
|
||||
@@ -919,7 +919,7 @@ func (s *SqlGroupStore) GetGroups(page, perPage int, opts model.GroupSearchOpts)
|
||||
}
|
||||
|
||||
if len(opts.Q) > 0 {
|
||||
pattern := fmt.Sprintf("%%%s%%", opts.Q)
|
||||
pattern := fmt.Sprintf("%%%s%%", sanitizeSearchTerm(opts.Q, "\\"))
|
||||
operatorKeyword := "ILIKE"
|
||||
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
||||
operatorKeyword = "LIKE"
|
||||
|
||||
@@ -293,6 +293,8 @@ func (s SqlTeamStore) GetByName(name string) (*model.Team, *model.AppError) {
|
||||
func (s SqlTeamStore) SearchAll(term string) ([]*model.Team, *model.AppError) {
|
||||
var teams []*model.Team
|
||||
|
||||
term = sanitizeSearchTerm(term, "\\")
|
||||
|
||||
if _, err := s.GetReplica().Select(&teams, "SELECT * FROM Teams WHERE Name LIKE :Term OR DisplayName LIKE :Term", map[string]interface{}{"Term": term + "%"}); err != nil {
|
||||
return nil, model.NewAppError("SqlTeamStore.SearchAll", "store.sql_team.search_all_team.app_error", nil, "term="+term+", "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -303,6 +305,8 @@ func (s SqlTeamStore) SearchAll(term string) ([]*model.Team, *model.AppError) {
|
||||
func (s SqlTeamStore) SearchOpen(term string) ([]*model.Team, *model.AppError) {
|
||||
var teams []*model.Team
|
||||
|
||||
term = sanitizeSearchTerm(term, "\\")
|
||||
|
||||
if _, err := s.GetReplica().Select(&teams, "SELECT * FROM Teams WHERE Type = 'O' AND AllowOpenInvite = true AND (Name LIKE :Term OR DisplayName LIKE :Term)", map[string]interface{}{"Term": term + "%"}); err != nil {
|
||||
return nil, model.NewAppError("SqlTeamStore.SearchOpen", "store.sql_team.search_open_team.app_error", nil, "term="+term+", "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -313,6 +317,8 @@ func (s SqlTeamStore) SearchOpen(term string) ([]*model.Team, *model.AppError) {
|
||||
func (s SqlTeamStore) SearchPrivate(term string) ([]*model.Team, *model.AppError) {
|
||||
var teams []*model.Team
|
||||
|
||||
term = sanitizeSearchTerm(term, "\\")
|
||||
|
||||
query :=
|
||||
`SELECT *
|
||||
FROM
|
||||
|
||||
@@ -179,6 +179,7 @@ func (s SqlUserAccessTokenStore) GetByUser(userId string, offset, limit int) ([]
|
||||
}
|
||||
|
||||
func (s SqlUserAccessTokenStore) Search(term string) ([]*model.UserAccessToken, *model.AppError) {
|
||||
term = sanitizeSearchTerm(term, "\\")
|
||||
tokens := []*model.UserAccessToken{}
|
||||
params := map[string]interface{}{"Term": term + "%"}
|
||||
query := `
|
||||
|
||||
@@ -409,11 +409,13 @@ func applyRoleFilter(query sq.SelectBuilder, role string, isPostgreSQL bool) sq.
|
||||
return query
|
||||
}
|
||||
|
||||
roleParam := fmt.Sprintf("%%%s%%", role)
|
||||
if isPostgreSQL {
|
||||
roleParam := fmt.Sprintf("%%%s%%", sanitizeSearchTerm(role, "\\"))
|
||||
return query.Where("u.Roles LIKE LOWER(?)", roleParam)
|
||||
}
|
||||
|
||||
roleParam := fmt.Sprintf("%%%s%%", sanitizeSearchTerm(role, "*"))
|
||||
|
||||
return query.Where("u.Roles LIKE ? ESCAPE '*'", roleParam)
|
||||
}
|
||||
|
||||
@@ -1222,15 +1224,6 @@ func (us SqlUserStore) SearchInChannel(channelId string, term string, options *m
|
||||
return us.performSearch(query, term, options)
|
||||
}
|
||||
|
||||
var escapeLikeSearchChar = []string{
|
||||
"%",
|
||||
"_",
|
||||
}
|
||||
|
||||
var ignoreLikeSearchChar = []string{
|
||||
"*",
|
||||
}
|
||||
|
||||
var spaceFulltextSearchChar = []string{
|
||||
"<",
|
||||
">",
|
||||
@@ -1265,15 +1258,7 @@ func generateSearchQuery(query sq.SelectBuilder, terms []string, fields []string
|
||||
}
|
||||
|
||||
func (us SqlUserStore) performSearch(query sq.SelectBuilder, term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) {
|
||||
// These chars must be removed from the like query.
|
||||
for _, c := range ignoreLikeSearchChar {
|
||||
term = strings.Replace(term, c, "", -1)
|
||||
}
|
||||
|
||||
// These chars must be escaped in the like query.
|
||||
for _, c := range escapeLikeSearchChar {
|
||||
term = strings.Replace(term, c, "*"+c, -1)
|
||||
}
|
||||
term = sanitizeSearchTerm(term, "*")
|
||||
|
||||
searchType := USER_SEARCH_TYPE_NAMES_NO_FULL_NAME
|
||||
if options.AllowEmails {
|
||||
|
||||
@@ -8,11 +8,27 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/gorp"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
)
|
||||
|
||||
var escapeLikeSearchChar = []string{
|
||||
"%",
|
||||
"_",
|
||||
}
|
||||
|
||||
func sanitizeSearchTerm(term string, escapeChar string) string {
|
||||
term = strings.Replace(term, escapeChar, "", -1)
|
||||
|
||||
for _, c := range escapeLikeSearchChar {
|
||||
term = strings.Replace(term, c, escapeChar+c, -1)
|
||||
}
|
||||
|
||||
return term
|
||||
}
|
||||
|
||||
// Converts a list of strings into a list of query parameters and a named parameter map that can
|
||||
// be used as part of a SQL query.
|
||||
func MapStringsToQueryParams(list []string, paramPrefix string) (string, map[string]interface{}) {
|
||||
|
||||
@@ -2,6 +2,8 @@ package sqlstore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMapStringsToQueryParams(t *testing.T) {
|
||||
@@ -30,3 +32,29 @@ func TestMapStringsToQueryParams(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSanitizeSearchTerm(t *testing.T) {
|
||||
term := "test"
|
||||
result := sanitizeSearchTerm(term, "\\")
|
||||
require.Equal(t, result, term)
|
||||
|
||||
term = "%%%"
|
||||
expected := "\\%\\%\\%"
|
||||
result = sanitizeSearchTerm(term, "\\")
|
||||
require.Equal(t, result, expected)
|
||||
|
||||
term = "%\\%\\%"
|
||||
expected = "\\%\\%\\%"
|
||||
result = sanitizeSearchTerm(term, "\\")
|
||||
require.Equal(t, result, expected)
|
||||
|
||||
term = "%_test_%"
|
||||
expected = "\\%\\_test\\_\\%"
|
||||
result = sanitizeSearchTerm(term, "\\")
|
||||
require.Equal(t, result, expected)
|
||||
|
||||
term = "**test_%"
|
||||
expected = "test*_*%"
|
||||
result = sanitizeSearchTerm(term, "*")
|
||||
require.Equal(t, result, expected)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user