From 4b664def64065d47320e7fc2284f58452cebb140 Mon Sep 17 00:00:00 2001 From: Fabrian Ivan Prasetya Date: Mon, 26 Oct 2026 13:27:07 +0700 Subject: [PATCH] fix(number): reject bare dot and repeated plus in string-to-number conversions 1. In trimDecimal, strings with only a dot or signed dot ('.', '+.', '-.') previously matched stringNumberRe, capturing an empty integer part that was then mapped to '0', '+0', or '-0', allowing ToIntE('.') to return 0 with no error while ToFloat64E('.') failed. This change skips trimming for bare dot inputs so strconv.ParseInt/ParseUint can properly reject them as invalid syntax. 2. In parseUint, strings.TrimLeft was used on '+', which inadvertently stripped multiple plus signs (e.g. '++5' parsed as 5). Replaced with strings.TrimPrefix so only a single optional plus sign is stripped. --- number.go | 6 +++++- number_internal_test.go | 3 +++ number_test.go | 12 ++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/number.go b/number.go index a58dc4d1..4f709fa3 100644 --- a/number.go +++ b/number.go @@ -414,7 +414,7 @@ func parseInt[T integer](s string) (T, error) { } func parseUint[T unsigned](s string) (T, error) { - v, err := strconv.ParseUint(strings.TrimLeft(trimDecimal(s), "+"), 0, 0) + v, err := strconv.ParseUint(strings.TrimPrefix(trimDecimal(s), "+"), 0, 0) if err != nil { return 0, err } @@ -529,6 +529,10 @@ func trimDecimal(s string) string { return s } + if s == "." || s == "+." || s == "-." { + return s + } + matches := stringNumberRe.FindStringSubmatch(s) if matches != nil { // matches[1] is the captured integer part with sign diff --git a/number_internal_test.go b/number_internal_test.go index 6d333d05..abda2f21 100644 --- a/number_internal_test.go +++ b/number_internal_test.go @@ -69,6 +69,9 @@ func TestTrimDecimal(t *testing.T) { {"10.foobar", "10.foobar"}, {"10.0i", "10.0i"}, {"10.0E9", "10.0E9"}, + {".", "."}, + {"+.", "+."}, + {"-.", "-."}, } for _, testCase := range testCases { diff --git a/number_test.go b/number_test.go index 9d84d18d..933cfc90 100644 --- a/number_test.go +++ b/number_test.go @@ -464,3 +464,15 @@ func BenchmarkNumber(b *testing.B) { }) } } + +func TestInvalidNumberStrings(t *testing.T) { + c := qt.New(t) + + for _, s := range []string{".", "-.", "+.", "++5", "+-5"} { + _, err := cast.ToIntE(s) + c.Assert(err, qt.IsNotNil, qt.Commentf("ToIntE(%q)", s)) + + _, err = cast.ToUintE(s) + c.Assert(err, qt.IsNotNil, qt.Commentf("ToUintE(%q)", s)) + } +}