From 9062c24a7e837372ea62c46b23caf9ad93531930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20Fouren?= Date: Sat, 4 Feb 2023 15:55:55 +0800 Subject: [PATCH 1/2] Allow search strings to escape special chars ' ', ':' and '-' using '\' --- client/lib/lib.go | 56 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/client/lib/lib.go b/client/lib/lib.go index 27dab30..4feb8eb 100644 --- a/client/lib/lib.go +++ b/client/lib/lib.go @@ -1098,7 +1098,7 @@ func MakeWhereQueryFromSearch(ctx *context.Context, db *gorm.DB, query string) ( tx := db.Model(&data.HistoryEntry{}).Where("true") for _, token := range tokens { if strings.HasPrefix(token, "-") { - if strings.Contains(token, ":") { + if containsUnescaped(token, ":") { query, v1, v2, err := parseAtomizedToken(ctx, token[1:]) if err != nil { return nil, err @@ -1111,7 +1111,7 @@ func MakeWhereQueryFromSearch(ctx *context.Context, db *gorm.DB, query string) ( } tx = tx.Where("NOT "+query, v1, v2, v3) } - } else if strings.Contains(token, ":") { + } else if containsUnescaped(token, ":") { query, v1, v2, err := parseAtomizedToken(ctx, token) if err != nil { return nil, err @@ -1150,14 +1150,14 @@ func Search(ctx *context.Context, db *gorm.DB, query string, limit int) ([]*data } func parseNonAtomizedToken(token string) (string, interface{}, interface{}, interface{}, error) { - wildcardedToken := "%" + token + "%" + wildcardedToken := "%" + deEscape(token) + "%" return "(command LIKE ? OR hostname LIKE ? OR current_working_directory LIKE ?)", wildcardedToken, wildcardedToken, wildcardedToken, nil } func parseAtomizedToken(ctx *context.Context, token string) (string, interface{}, interface{}, error) { - splitToken := strings.SplitN(token, ":", 2) - field := splitToken[0] - val := splitToken[1] + splitToken := splitEscaped(token, ':', 2) + field := deEscape(splitToken[0]) + val := deEscape(splitToken[1]) switch field { case "user": return "(local_username = ?)", val, nil, nil @@ -1237,7 +1237,49 @@ func tokenize(query string) ([]string, error) { if query == "" { return []string{}, nil } - return strings.Split(query, " "), nil + return splitEscaped(query, ' ', -1), nil +} + +func splitEscaped(query string, separator byte, maxSplit int) []string { + var token []byte + var tokens []string + var splits = 1 + for i := 0; i < len(query); i++ { + if (maxSplit < 0 || splits < maxSplit) && query[i] == separator { + tokens = append(tokens, string(token)) + token = token[:0] + splits++ + } else if query[i] == '\\' && i+1 < len(query) { + token = append(token, query[i], query[i+1]) + i++ + } else { + token = append(token, query[i]) + } + } + tokens = append(tokens, string(token)) + return tokens +} + +func containsUnescaped(query string, token string) bool { + for i := 0; i < len(query); i++ { + if query[i] == '\\' && i+1 < len(query) { + i++ + } else if query[i:i+len(token)] == token { + return true + } + } + return false +} + +func deEscape(query string) string { + var newQuery []byte + for i := 0; i < len(query); i++ { + if query[i] == '\\' && i+1 < len(query) { + i++ + } + newQuery = append(newQuery, query[i]) + } + return string(newQuery) } func GetDumpRequests(config hctx.ClientConfig) ([]*shared.DumpRequest, error) { From f08cac491c031bb9e5b3d5d222de082d99342e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20Fouren?= Date: Sun, 5 Feb 2023 01:58:27 +0800 Subject: [PATCH 2/2] Use rune instead of byte when traversing strings --- client/lib/lib.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/client/lib/lib.go b/client/lib/lib.go index 4feb8eb..192961f 100644 --- a/client/lib/lib.go +++ b/client/lib/lib.go @@ -1240,20 +1240,21 @@ func tokenize(query string) ([]string, error) { return splitEscaped(query, ' ', -1), nil } -func splitEscaped(query string, separator byte, maxSplit int) []string { - var token []byte +func splitEscaped(query string, separator rune, maxSplit int) []string { + var token []rune var tokens []string - var splits = 1 - for i := 0; i < len(query); i++ { - if (maxSplit < 0 || splits < maxSplit) && query[i] == separator { + splits := 1 + runeQuery := []rune(query) + for i := 0; i < len(runeQuery); i++ { + if (maxSplit < 0 || splits < maxSplit) && runeQuery[i] == separator { tokens = append(tokens, string(token)) token = token[:0] splits++ - } else if query[i] == '\\' && i+1 < len(query) { - token = append(token, query[i], query[i+1]) + } else if runeQuery[i] == '\\' && i+1 < len(runeQuery) { + token = append(token, runeQuery[i], runeQuery[i+1]) i++ } else { - token = append(token, query[i]) + token = append(token, runeQuery[i]) } } tokens = append(tokens, string(token)) @@ -1261,10 +1262,11 @@ func splitEscaped(query string, separator byte, maxSplit int) []string { } func containsUnescaped(query string, token string) bool { - for i := 0; i < len(query); i++ { - if query[i] == '\\' && i+1 < len(query) { + runeQuery := []rune(query) + for i := 0; i < len(runeQuery); i++ { + if runeQuery[i] == '\\' && i+1 < len(runeQuery) { i++ - } else if query[i:i+len(token)] == token { + } else if string(runeQuery[i:i+len(token)]) == token { return true } } @@ -1272,12 +1274,13 @@ func containsUnescaped(query string, token string) bool { } func deEscape(query string) string { - var newQuery []byte - for i := 0; i < len(query); i++ { - if query[i] == '\\' && i+1 < len(query) { + runeQuery := []rune(query) + var newQuery []rune + for i := 0; i < len(runeQuery); i++ { + if runeQuery[i] == '\\' && i+1 < len(runeQuery) { i++ } - newQuery = append(newQuery, query[i]) + newQuery = append(newQuery, runeQuery[i]) } return string(newQuery) }