Skip to content

Commit ec5ff0d

Browse files
authored
Add validation for nat-addr (#5257)
1 parent bf26745 commit ec5ff0d

File tree

3 files changed

+146
-0
lines changed

3 files changed

+146
-0
lines changed

pkg/node/export_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright 2025 The Swarm Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package node
6+
7+
var ValidatePublicAddress = validatePublicAddress

pkg/node/node.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"net/http"
2121
"path/filepath"
2222
"runtime"
23+
"strconv"
2324
"sync"
2425
"sync/atomic"
2526
"time"
@@ -222,6 +223,10 @@ func NewBee(
222223
return nil, fmt.Errorf("tracer: %w", err)
223224
}
224225

226+
if err := validatePublicAddress(o.NATAddr); err != nil {
227+
return nil, fmt.Errorf("invalid NAT address %s: %w", o.NATAddr, err)
228+
}
229+
225230
ctx, ctxCancel := context.WithCancel(ctx)
226231
defer func() {
227232
// if there's been an error on this function
@@ -1463,3 +1468,38 @@ func isChainEnabled(o *Options, swapEndpoint string, logger log.Logger) bool {
14631468
logger.Info("starting with an enabled chain backend")
14641469
return true // all other modes operate require chain enabled
14651470
}
1471+
1472+
func validatePublicAddress(addr string) error {
1473+
if addr == "" {
1474+
return nil
1475+
}
1476+
1477+
host, port, err := net.SplitHostPort(addr)
1478+
if err != nil {
1479+
return fmt.Errorf("%w", err)
1480+
}
1481+
if host == "" {
1482+
return errors.New("host is empty")
1483+
}
1484+
if port == "" {
1485+
return errors.New("port is empty")
1486+
}
1487+
if _, err := strconv.ParseUint(port, 10, 16); err != nil {
1488+
return fmt.Errorf("port is not a valid number: %w", err)
1489+
}
1490+
if host == "localhost" {
1491+
return errors.New("localhost is not a valid address")
1492+
}
1493+
ip := net.ParseIP(host)
1494+
if ip == nil {
1495+
return errors.New("not a valid IP address")
1496+
}
1497+
if ip.IsLoopback() {
1498+
return errors.New("loopback address is not a valid address")
1499+
}
1500+
if ip.IsPrivate() {
1501+
return errors.New("private address is not a valid address")
1502+
}
1503+
1504+
return nil
1505+
}

pkg/node/node_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2025 The Swarm Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package node_test
6+
7+
import (
8+
"testing"
9+
10+
"github.com/ethersphere/bee/v2/pkg/node"
11+
)
12+
13+
func TestValidatePublicAddress(t *testing.T) {
14+
t.Parallel()
15+
16+
testCases := []struct {
17+
name string
18+
addr string
19+
expErr bool
20+
}{
21+
{
22+
name: "empty host",
23+
addr: ":1635",
24+
expErr: true,
25+
},
26+
{
27+
name: "localhost",
28+
addr: "localhost:1635",
29+
expErr: true,
30+
},
31+
{
32+
name: "loopback",
33+
addr: "127.0.0.1:1635",
34+
expErr: true,
35+
},
36+
{
37+
name: "loopback ipv6",
38+
addr: "[::1]:1635",
39+
expErr: true,
40+
},
41+
{
42+
name: "missing port",
43+
addr: "1.2.3.4",
44+
expErr: true,
45+
},
46+
{
47+
name: "empty port",
48+
addr: "1.2.3.4:",
49+
expErr: true,
50+
},
51+
{
52+
name: "invalid port number",
53+
addr: "1.2.3.4:abc",
54+
expErr: true,
55+
},
56+
{
57+
name: "valid",
58+
addr: "1.2.3.4:1635",
59+
expErr: false,
60+
},
61+
{
62+
name: "valid ipv6",
63+
addr: "[2001:db8::1]:1635",
64+
expErr: false,
65+
},
66+
{
67+
name: "empty",
68+
addr: "",
69+
expErr: false,
70+
},
71+
{
72+
name: "invalid IP",
73+
addr: "not-an-ip:8080",
74+
expErr: true,
75+
},
76+
{
77+
name: "private IP",
78+
addr: "192.168.1.1:8080",
79+
expErr: true,
80+
},
81+
{
82+
name: "hostname",
83+
addr: "example.com:8080",
84+
expErr: true,
85+
},
86+
}
87+
88+
for _, tc := range testCases {
89+
t.Run(tc.name, func(t *testing.T) {
90+
err := node.ValidatePublicAddress(tc.addr)
91+
if tc.expErr && err == nil {
92+
t.Fatal("expected error, but got none")
93+
}
94+
if !tc.expErr && err != nil {
95+
t.Fatalf("expected no error, but got: %v", err)
96+
}
97+
})
98+
}
99+
}

0 commit comments

Comments
 (0)