This is an automated email from the ASF dual-hosted git repository. sjanc pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git
commit 3a7a7e58bb4718257c518017b9e828aea60adda4 Author: Szymon Janc <[email protected]> AuthorDate: Mon Oct 19 17:45:44 2026 +0200 newt/parse: Fix crash on empty parentheses in expressions An empty parenthesized group, e.g. "X == ()" or "() < 1", was parsed into a nil operand node. Evaluating such a binary operator later dereferenced the nil node and newt crashed with a nil pointer panic. Treat an empty parenthesized group as a parse error. Assisted-by: Claude:claude-opus-5-5 --- newt/parse/parse.go | 3 ++ newt/parse/parse_test.go | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/newt/parse/parse.go b/newt/parse/parse.go index 1d474ec3..05ed0e49 100644 --- a/newt/parse/parse.go +++ b/newt/parse/parse.go @@ -304,6 +304,9 @@ func Parse(tokens []Token) (*Node, error) { return nil, fmt.Errorf("unexpected %s after parenthesis", tokens[len(stripped)+2].Text) } + if len(stripped) == 0 { + return nil, fmt.Errorf("empty parentheses") + } return Parse(stripped) } diff --git a/newt/parse/parse_test.go b/newt/parse/parse_test.go new file mode 100644 index 00000000..0413e21e --- /dev/null +++ b/newt/parse/parse_test.go @@ -0,0 +1,76 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package parse + +import ( + "testing" + + "mynewt.apache.org/newt/newt/cfgv" +) + +func TestParseEmptyParens(t *testing.T) { + settings := cfgv.NewSettingsFromMap(map[string]string{"X": "1"}) + + exprs := []string{ + "()", + "X == ()", + "() == 1", + "X != ()", + "X < ()", + "() >= X", + "X && ()", + "!()", + "(())", + "X == (())", + } + for _, expr := range exprs { + _, err := ParseAndEval(expr, settings) + if err == nil { + t.Errorf("expected error for expression [%s]", expr) + } + } +} + +func TestParseValid(t *testing.T) { + settings := cfgv.NewSettingsFromMap(map[string]string{ + "X": "1", + "Y": "0", + }) + + exprs := map[string]bool{ + "": true, + "X": true, + "(X)": true, + "((X) == (1))": true, + "X == (Y)": false, + "!(Y)": true, + "X > Y": true, + } + for expr, want := range exprs { + got, err := ParseAndEval(expr, settings) + if err != nil { + t.Errorf("unexpected error for expression [%s]: %v", expr, err) + continue + } + if got != want { + t.Errorf("expression [%s]: got %v, want %v", expr, got, want) + } + } +}
