From 2fbc2eab20cd82685abb70e3d33d88da83484786 Mon Sep 17 00:00:00 2001 From: maximilize <3752128+maximilize@users.noreply.github.com> Date: Mon, 3 Aug 2026 05:15:14 +0200 Subject: [PATCH] fix(has): return false for negative array indices (#2769) The sequence branch only checked the upper bound (len > index), so has(-1) returned true even on an empty array. Guard index >= 0. Co-authored-by: max --- pkg/yqlib/operator_has.go | 2 +- pkg/yqlib/operator_has_test.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/yqlib/operator_has.go b/pkg/yqlib/operator_has.go index d9fc6320..dd60ecdc 100644 --- a/pkg/yqlib/operator_has.go +++ b/pkg/yqlib/operator_has.go @@ -44,7 +44,7 @@ func hasOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi if errParsingInt != nil { return Context{}, errParsingInt } - candidateHasKey = int64(len(contents)) > number + candidateHasKey = number >= 0 && int64(len(contents)) > number } results.PushBack(createBooleanCandidate(candidate, candidateHasKey)) default: diff --git a/pkg/yqlib/operator_has_test.go b/pkg/yqlib/operator_has_test.go index bed11fcb..47a708af 100644 --- a/pkg/yqlib/operator_has_test.go +++ b/pkg/yqlib/operator_has_test.go @@ -71,6 +71,16 @@ var hasOperatorScenarios = []expressionScenario{ "D0, P[4], (!!bool)::true\n", }, }, + { + skipDoc: true, + description: "Negative array index is never present", + document: "[[1, 2, 3], []]", + expression: `.[] | has(-1)`, + expected: []string{ + "D0, P[0], (!!bool)::false\n", + "D0, P[1], (!!bool)::false\n", + }, + }, } func TestHasOperatorScenarios(t *testing.T) {