mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-12 23:54:01 +09:00
fix(observability-proxy): refuse every LogsQL options block
A substring match on ignore_global_time_filter does not see the option once it is written as a quoted name with an escape, or split by a comment: the LogsQL lexer unquotes option names and joins words across a comment, so the option still applies and lifts the time window the proxy enforces. None of the query options is needed by a tenant, so the options keyword followed by a parenthesis is refused outright, skipping whitespace and comments exactly as the lexer does. Assisted-by: LLM Signed-off-by: ZverGuy <maximbel2003@gmail.com>
This commit is contained in:
1 parent
8b0ac342b5
commit
cbca8ac3c9
2 files changed
+48
No files matched your search
@@ -24,6 +24,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// LogsLimits bound what one logs request may make vlselect do.
|
||||
@@ -66,6 +68,13 @@ func BuildLogsParams(endpoint string, in url.Values, namespace string, now time.
|
||||
if strings.Contains(strings.ToLower(query), "ignore_global_time_filter") {
|
||||
return nil, errorf(http.StatusBadRequest, "ignore_global_time_filter is not allowed")
|
||||
}
|
||||
// The substring check alone is bypassable: VictoriaLogs unquotes a
|
||||
// quoted option name and joins words split by a comment. No option is
|
||||
// needed by a tenant (the others tune concurrency or the time filter),
|
||||
// so any options(...) block is refused.
|
||||
if hasOptionsBlock(query) {
|
||||
return nil, errorf(http.StatusBadRequest, "query options are not allowed")
|
||||
}
|
||||
out := url.Values{}
|
||||
out.Set("query", query)
|
||||
out.Set("extra_stream_filters", "{kubernetes_namespace_name="+strconv.Quote(namespace)+"}")
|
||||
@@ -301,6 +310,42 @@ func parseTime(s string, now time.Time) (time.Time, error) {
|
||||
return time.Time{}, fmt.Errorf("unsupported time %q", s)
|
||||
}
|
||||
|
||||
// hasOptionsBlock reports whether the options keyword is followed by "(",
|
||||
// skipping whitespace and "#" comments the way the LogsQL lexer does. A
|
||||
// quoted "options" is never the keyword, but matching it too only refuses
|
||||
// a search for that literal phrase.
|
||||
func hasOptionsBlock(query string) bool {
|
||||
lower := strings.ToLower(query)
|
||||
for i := strings.Index(lower, "options"); i >= 0; {
|
||||
rest := lower[i+len("options"):]
|
||||
for rest != "" {
|
||||
r, size := utf8.DecodeRuneInString(rest)
|
||||
switch {
|
||||
case unicode.IsSpace(r):
|
||||
rest = rest[size:]
|
||||
continue
|
||||
case r == '#':
|
||||
if n := strings.IndexByte(rest, '\n'); n >= 0 {
|
||||
rest = rest[n+1:]
|
||||
} else {
|
||||
rest = ""
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(rest, "(") {
|
||||
return true
|
||||
}
|
||||
next := strings.Index(lower[i+1:], "options")
|
||||
if next < 0 {
|
||||
return false
|
||||
}
|
||||
i += next + 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isDigits(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
|
||||
@@ -120,6 +120,9 @@ func TestBuildLogsParamsRejects(t *testing.T) {
|
||||
{"ignore_global_time_filter", "query", url.Values{"query": {`* options(ignore_global_time_filter=true)`}}, http.StatusBadRequest},
|
||||
{"ignore_global_time_filter in subquery", "hits", url.Values{"query": {`* | union (options(ignore_global_time_filter=true) error)`}}, http.StatusBadRequest},
|
||||
{"ignore_global_time_filter on tail", "tail", url.Values{"query": {`options(IGNORE_GLOBAL_TIME_FILTER=true) *`}}, http.StatusBadRequest},
|
||||
{"options with a quoted escaped name", "query", url.Values{"query": {`options("ignore_global_time_filte\x72"=true) *`}}, http.StatusBadRequest},
|
||||
{"options with a comment inside the name", "query", url.Values{"query": {"options(ignore_global#c\n_time_filter=true) *"}}, http.StatusBadRequest},
|
||||
{"options after a comment and unicode space", "tail", url.Values{"query": {"OPTIONS #c\n\u00a0(concurrency=1000) *"}}, http.StatusBadRequest},
|
||||
{"inverted range", "query", url.Values{"start": {"2026-11-04T11:00:00Z"}, "end": {"2026-11-04T10:00:00Z"}}, http.StatusBadRequest},
|
||||
{"inverted range on tail", "tail", url.Values{"start": {"2026-11-04T11:00:00Z"}, "end": {"2026-11-04T10:00:00Z"}}, http.StatusBadRequest},
|
||||
{"bad start", "query", url.Values{"start": {"yesterday"}}, http.StatusBadRequest},
|
||||
|
||||
Reference in new issue
Block a user