From 030fc20cd628c574837bd0910c3d034ed67da424 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Wed, 1 Apr 2026 14:51:00 +0100 Subject: [PATCH] Harden Proxmox integer parsing bounds --- pkg/proxmox/client.go | 38 +++++++++++++++++++++++++++++--------- pkg/proxmox/client_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/pkg/proxmox/client.go b/pkg/proxmox/client.go index bd72c6173..8d2df205a 100644 --- a/pkg/proxmox/client.go +++ b/pkg/proxmox/client.go @@ -88,9 +88,23 @@ func parseFlexibleIntString(raw string) (int, error) { } func int64ToInt(v int64) (int, error) { - maxInt := int64(^uint(0) >> 1) - minInt := -maxInt - 1 - if v > maxInt || v < minInt { + if strconv.IntSize == 32 { + if v > math.MaxInt32 || v < math.MinInt32 { + return 0, fmt.Errorf("integer %d exceeds int range", v) + } + return int(int32(v)), nil + } + return int(v), nil +} + +func uint64ToInt(v uint64) (int, error) { + if strconv.IntSize == 32 { + if v > math.MaxInt32 { + return 0, fmt.Errorf("integer %d exceeds int range", v) + } + return int(int32(v)), nil + } + if v > math.MaxInt64 { return 0, fmt.Errorf("integer %d exceeds int range", v) } return int(v), nil @@ -100,8 +114,13 @@ func floatToIntTrunc(v float64) (int, error) { if math.IsNaN(v) || math.IsInf(v, 0) { return 0, fmt.Errorf("non-finite float %v cannot be converted to int", v) } - limit := math.Exp2(float64(strconv.IntSize - 1)) - if v >= limit || v < -limit { + var maxAbs float64 + if strconv.IntSize == 32 { + maxAbs = float64(math.MaxInt32) + 1 + } else { + maxAbs = float64(math.MaxInt64) + 1 + } + if v >= maxAbs || v < -maxAbs { return 0, fmt.Errorf("float %v exceeds int range", v) } return int(v), nil @@ -1681,15 +1700,16 @@ func (a *VMIPAddress) UnmarshalJSON(data []byte) error { a.Address = coerceString(raw["ip-address"]) - maxInt := uint64(^uint(0) >> 1) prefix, err := coerceUint64("prefix", raw["prefix"]) if err != nil { prefix = 0 } - if prefix > maxInt { - prefix = maxInt + if prefix > 128 { + prefix = 128 + } + if prefixInt, err := uint64ToInt(prefix); err == nil { + a.Prefix = prefixInt } - a.Prefix = int(prefix) return nil } diff --git a/pkg/proxmox/client_test.go b/pkg/proxmox/client_test.go index f4583d4aa..470fe1022 100644 --- a/pkg/proxmox/client_test.go +++ b/pkg/proxmox/client_test.go @@ -587,6 +587,42 @@ func TestFlexIntUnmarshalJSONRejectsNonFiniteAndOverflow(t *testing.T) { } } +func TestVMIPAddressUnmarshalJSONCapsPrefixToCIDRRange(t *testing.T) { + tests := []struct { + name string + payload string + want int + }{ + { + name: "keeps valid prefix", + payload: `{"ip-address":"10.0.0.10","prefix":24}`, + want: 24, + }, + { + name: "caps overlong ipv6 prefix", + payload: `{"ip-address":"2001:db8::10","prefix":129}`, + want: 128, + }, + { + name: "caps huge prefix before int conversion", + payload: `{"ip-address":"2001:db8::10","prefix":"18446744073709551615"}`, + want: 128, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var got VMIPAddress + if err := json.Unmarshal([]byte(tc.payload), &got); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got.Prefix != tc.want { + t.Fatalf("prefix = %d, want %d", got.Prefix, tc.want) + } + }) + } +} + func TestParseUint64Flexible(t *testing.T) { tests := []struct { name string