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 07d10d463aa3413f9545f858a10af44b9ae05377
Author: Szymon Janc <[email protected]>
AuthorDate: Mon Oct 19 17:46:37 2026 +0200

    newt/install: Fix panic on long answer to upgrade prompt
    
    The "Proceed? [Y/n]" prompt read the answer with bufio ReadLine. For
    a line longer than the reader buffer ReadLine returns more=true with
    a nil error, which was then passed to util.ChildNewtError(nil) and
    caused a nil pointer dereference.
    
    Read whole lines with ReadString instead, using a single reader for
    all attempts, so a long answer is treated as an invalid response.
    An answer terminated by EOF without newline is still accepted.
    
    Assisted-by: Claude:claude-opus-5-5
---
 newt/install/install.go      | 13 +++++++--
 newt/install/install_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+), 3 deletions(-)

diff --git a/newt/install/install.go b/newt/install/install.go
index 36faf4fd..2d48eea5 100644
--- a/newt/install/install.go
+++ b/newt/install/install.go
@@ -66,6 +66,7 @@ package install
 import (
        "bufio"
        "fmt"
+       "io"
        "os"
        "strings"
 
@@ -376,14 +377,20 @@ func (inst *Installer) installPrompt(vm 
deprepo.VersionMap, op installOp,
                }
        }
 
+       return askProceed(bufio.NewReader(os.Stdin))
+}
+
+// Asks the user whether to proceed and reads the answer from the specified
+// reader.  Returns true if the user wants to proceed.
+func askProceed(rd *bufio.Reader) (bool, error) {
        for {
                fmt.Printf("Proceed? [Y/n] ")
-               line, more, err := bufio.NewReader(os.Stdin).ReadLine()
-               if more || err != nil {
+               line, err := rd.ReadString('\n')
+               if err != nil && (err != io.EOF || len(line) == 0) {
                        return false, util.ChildNewtError(err)
                }
 
-               trimmed := strings.ToLower(strings.TrimSpace(string(line)))
+               trimmed := strings.ToLower(strings.TrimSpace(line))
                if len(trimmed) == 0 || strings.HasPrefix(trimmed, "y") {
                        // User wants to proceed.
                        return true, nil
diff --git a/newt/install/install_test.go b/newt/install/install_test.go
new file mode 100644
index 00000000..2d776cd4
--- /dev/null
+++ b/newt/install/install_test.go
@@ -0,0 +1,68 @@
+/**
+ * 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 install
+
+import (
+       "bufio"
+       "strings"
+       "testing"
+)
+
+func TestAskProceed(t *testing.T) {
+       long := strings.Repeat("x", 10000)
+
+       tests := []struct {
+               name    string
+               input   string
+               proceed bool
+               err     bool
+       }{
+               {"empty", "\n", true, false},
+               {"yes", "y\n", true, false},
+               {"no", "n\n", false, false},
+               {"yes without newline", "Y", true, false},
+               {"eof", "", false, true},
+               {"invalid then no", "foo\nn\n", false, false},
+               {"long line then yes", long + "\ny\n", true, false},
+               {"long line then no", long + "\nno\n", false, false},
+               {"long yes line", "y" + long + "\n", true, false},
+               {"long line then eof", long, false, true},
+       }
+
+       for _, tc := range tests {
+               t.Run(tc.name, func(t *testing.T) {
+                       rd := bufio.NewReader(strings.NewReader(tc.input))
+                       proceed, err := askProceed(rd)
+                       if tc.err {
+                               if err == nil {
+                                       t.Fatalf("expected error")
+                               }
+                               return
+                       }
+                       if err != nil {
+                               t.Fatalf("unexpected error: %v", err)
+                       }
+                       if proceed != tc.proceed {
+                               t.Fatalf("expected proceed=%v, got %v",
+                                       tc.proceed, proceed)
+                       }
+               })
+       }
+}

Reply via email to