Skip to content

Commit afb2bc3

Browse files
authored
[FEAT] - Readme and Dependabot Mods and More! (#5)
* docs(README): correct project name in installation section * chore(deps): remove repository reference from dependabot config * build(Dockerfile): pin image versions for reproducibility * test(fuzz): add fuzz tests for P2P sync message parsing Introduces fuzz tests for the P2P sync message parsing and serialization to ensure robustness against various input scenarios and edge cases. * test(fuzz): add fuzz tests for bitcoin.conf parsing * test(fuzz): add fuzz tests for alert message handling * fix(alert): validate alert length before processing * test(fuzz): add fuzz tests for alert message parsing * fix(load): return advance, token, and err in splitFunc Ensure proper return values from splitFunc for correct functionality. * fix(alert): update minimum raw message length check * fix(model): return model instance in NewBaseModel function * test(fuzz): enhance fuzz tests for alert message parsing Refactored fuzz tests to utilize helper functions for building messages. Added common edge cases and improved validation checks for length fields. * fix(alert): ensure EnforceAtHeight is not empty in freeze UTXO test
1 parent 7792c56 commit afb2bc3

File tree

12 files changed

+974
-10
lines changed

12 files changed

+974
-10
lines changed

.github/dependabot.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# ────────────────────────────────────────────────────────────────
22
# Dependabot Configuration
3-
# Repo: mrz1836/<repo>
43
#
54
# Purpose:
65
# • Keep Go modules, GitHub Actions, DevContainer images/features, and Docker

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM galtbv/builder:ubi9 AS builder
1+
FROM galtbv/builder:ubi9@sha256:a4d5adae0cb776574255bf1c326583fcbd95b0275c81572b0283fee90e33bec4 AS builder
22

33
# Copy in the go src
44
WORKDIR $APP_ROOT/src/github.com/bsv-blockchain/go-alert-system
@@ -10,7 +10,7 @@ COPY go.sum go.sum
1010
RUN CGO_ENABLED=0 go build -a -o $APP_ROOT/src/go-alert-system github.com/bsv-blockchain/go-alert-system/cmd/go-alert-system
1111

1212
# Copy the controller-manager into a thin image
13-
FROM registry.access.redhat.com/ubi9-minimal:9.6
13+
FROM registry.access.redhat.com/ubi9-minimal:9.6@sha256:a2c5a85865a585c3bc8b10f6c269358fdf89fe32be4232885166889d85c76421
1414
WORKDIR /
1515
RUN mkdir /.bitcoin
1616
RUN touch /.bitcoin/alert_system_private_key

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
## 📦 Installation
9191

9292
### Run from source
93-
**alert-system** requires a [supported release of Go](https://golang.org/doc/devel/release.html#policy).
93+
**go-alert-system** requires a [supported release of Go](https://golang.org/doc/devel/release.html#policy).
9494

9595
To run the application, clone this repository locally and run:
9696
```shell script
@@ -322,4 +322,4 @@ The most basic way to show your support is to star :star2: the project, or to ra
322322

323323
## 📝 License
324324

325-
[![License](https://img.shields.io/github/license/bsv-blockchain/go-alert-system.svg?style=flat&v=1)](LICENSE)
325+
[![License](https://img.shields.io/badge/license-OpenBSV-blue?style=flat&logo=springsecurity&logoColor=white)](LICENSE)

app/config/load.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ func splitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
327327
// skip the delimiter in advancing to the next pair
328328
return i + 1, data[0:i], nil
329329
}
330-
return
330+
return advance, token, err
331331
}
332332

333333
// CloseAll will close all connections to all services

app/config/load_fuzz_test.go

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
package config
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"strings"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
// FuzzSplitFunc tests the bitcoin.conf line splitting function
13+
func FuzzSplitFunc(f *testing.F) {
14+
// Seed with valid config lines
15+
f.Add([]byte("rpcuser=bitcoin\nrpcpassword=secret\n"), false)
16+
f.Add([]byte("rpcconnect=127.0.0.1\n"), false)
17+
f.Add([]byte("rpcport=8332\n"), false)
18+
19+
// Seed with edge cases
20+
f.Add([]byte(""), false) // empty
21+
f.Add([]byte(""), true) // empty at EOF
22+
f.Add([]byte("key=value"), true) // no newline at EOF
23+
f.Add([]byte("key=value\n"), false) // with newline
24+
f.Add([]byte("\n"), false) // just newline
25+
f.Add([]byte("===\n"), false) // multiple delimiters
26+
f.Add([]byte("nodelimiter"), false) // no '=' or '\n'
27+
f.Add([]byte("key=value\nkey2=value2\n"), false) // multiple lines
28+
29+
// Seed with special characters
30+
f.Add([]byte("key=value with spaces\n"), false)
31+
f.Add([]byte("key=\n"), false) // empty value
32+
f.Add([]byte("=value\n"), false) // empty key
33+
f.Add([]byte("key==value\n"), false) // double delimiter
34+
35+
f.Fuzz(func(t *testing.T, data []byte, atEOF bool) {
36+
// Should never panic
37+
advance, token, err := splitFunc(data, atEOF)
38+
39+
// Validate return values are consistent
40+
require.GreaterOrEqual(t, advance, 0, "advance should be non-negative")
41+
require.LessOrEqual(t, advance, len(data), "advance should not exceed data length")
42+
43+
if err != nil {
44+
// Error is acceptable
45+
return
46+
}
47+
48+
// If token is returned, it should not exceed data length
49+
if token != nil {
50+
require.LessOrEqual(t, len(token), len(data), "token should not exceed data length")
51+
}
52+
53+
// If atEOF is true and data is empty, should return 0, nil, nil
54+
if atEOF && len(data) == 0 {
55+
require.Equal(t, 0, advance, "advance should be 0 for empty data at EOF")
56+
require.Nil(t, token, "token should be nil for empty data at EOF")
57+
require.NoError(t, err, "error should be nil for empty data at EOF")
58+
}
59+
60+
// If atEOF is true and data is not empty, should return all data
61+
if atEOF && len(data) > 0 {
62+
require.Equal(t, len(data), advance, "should advance by data length at EOF")
63+
require.Equal(t, data, token, "should return all data at EOF")
64+
}
65+
})
66+
}
67+
68+
// FuzzBitcoinConfParsing tests bitcoin.conf parsing logic
69+
func FuzzBitcoinConfParsing(f *testing.F) {
70+
// Seed with valid bitcoin.conf content
71+
validConf := `rpcuser=bitcoin
72+
rpcpassword=secretpassword123
73+
rpcconnect=127.0.0.1
74+
rpcport=8332
75+
`
76+
f.Add([]byte(validConf))
77+
78+
// Seed with various formats
79+
f.Add([]byte("rpcuser=test\nrpcpassword=pass\n"))
80+
f.Add([]byte("key=value\n"))
81+
f.Add([]byte("key=\n"))
82+
f.Add([]byte("=value\n"))
83+
f.Add([]byte("key\n"))
84+
f.Add([]byte(""))
85+
f.Add([]byte("\n"))
86+
f.Add([]byte("===\n"))
87+
88+
// Seed with malformed content
89+
f.Add([]byte("rpcuser=test\nnodelimiter\nrpcpassword=pass\n"))
90+
f.Add([]byte("key==value\n"))
91+
f.Add([]byte("key=value=extra\n"))
92+
93+
// Seed with special characters
94+
f.Add([]byte("rpcuser=user@domain\nrpcpassword=p@ss!#$%\n"))
95+
f.Add([]byte("rpcuser=user with spaces\n"))
96+
f.Add([]byte("# comment line\nrpcuser=test\n"))
97+
98+
// Seed with different line endings
99+
f.Add([]byte("key=value\r\n"))
100+
f.Add([]byte("key=value\r"))
101+
102+
f.Fuzz(func(t *testing.T, data []byte) {
103+
// Create a scanner with the custom split function
104+
scanner := bufio.NewScanner(bytes.NewReader(data))
105+
scanner.Split(splitFunc)
106+
107+
// Parse the config content
108+
confValues := map[string]string{}
109+
lineCount := 0
110+
111+
// Should never panic during scanning
112+
for scanner.Scan() {
113+
lineCount++
114+
kv := scanner.Text()
115+
keyValue := strings.Split(kv, "=")
116+
117+
// Skip lines that don't have exactly 2 parts
118+
if len(keyValue) != 2 {
119+
continue
120+
}
121+
122+
confValues[keyValue[0]] = keyValue[1]
123+
}
124+
125+
// Validate scanner completed without panic
126+
err := scanner.Err()
127+
if err != nil {
128+
// Scanner errors are acceptable for invalid input
129+
return
130+
}
131+
132+
// Validate keys and values don't contain newlines
133+
for key, value := range confValues {
134+
require.NotContains(t, key, "\n", "keys should not contain newlines")
135+
require.NotContains(t, value, "\n", "values should not contain newlines")
136+
}
137+
})
138+
}
139+
140+
// FuzzConfigKeyValueParsing tests key=value parsing logic
141+
func FuzzConfigKeyValueParsing(f *testing.F) {
142+
// Seed with valid key=value pairs
143+
f.Add("rpcuser=bitcoin")
144+
f.Add("rpcpassword=secret123")
145+
f.Add("rpcconnect=127.0.0.1")
146+
f.Add("rpcport=8332")
147+
148+
// Seed with edge cases
149+
f.Add("")
150+
f.Add("=")
151+
f.Add("key=")
152+
f.Add("=value")
153+
f.Add("key")
154+
f.Add("key=value=extra")
155+
f.Add("===")
156+
f.Add("key==value")
157+
158+
// Seed with special characters
159+
f.Add("key=value with spaces")
160+
f.Add("key with spaces=value")
161+
f.Add("key=@#$%^&*()")
162+
f.Add("user@domain=pass!#$")
163+
164+
f.Fuzz(func(t *testing.T, kv string) {
165+
// Parse key=value pair
166+
keyValue := strings.Split(kv, "=")
167+
168+
// Validate split result
169+
require.NotNil(t, keyValue, "split should always return a slice")
170+
require.GreaterOrEqual(t, len(keyValue), 1, "split should return at least one element")
171+
172+
// If exactly 2 parts, validate they form a valid key-value pair
173+
if len(keyValue) == 2 {
174+
key := keyValue[0]
175+
value := keyValue[1]
176+
177+
// Keys and values can be empty, but shouldn't cause issues
178+
_ = key
179+
_ = value
180+
181+
// Test that we can store in a map
182+
testMap := map[string]string{}
183+
testMap[key] = value
184+
185+
require.Equal(t, value, testMap[key], "value should be retrievable from map")
186+
}
187+
188+
// If not exactly 2 parts, it should be skipped (as per the actual code logic)
189+
if len(keyValue) != 2 {
190+
// This is valid behavior - malformed lines are skipped
191+
require.NotEqual(t, 2, len(keyValue), "should skip lines that don't have exactly 2 parts")
192+
}
193+
})
194+
}
195+
196+
// FuzzHostPortParsing tests host:port parsing logic
197+
func FuzzHostPortParsing(f *testing.F) {
198+
// Seed with valid host:port combinations
199+
f.Add("http://127.0.0.1:8332")
200+
f.Add("https://localhost:8332")
201+
f.Add("http://192.168.1.1:18332")
202+
f.Add("https://node.example.com:8332")
203+
204+
// Seed with edge cases
205+
f.Add("")
206+
f.Add(":")
207+
f.Add("127.0.0.1")
208+
f.Add(":8332")
209+
f.Add("http://")
210+
f.Add("https://")
211+
f.Add("http://127.0.0.1")
212+
f.Add("127.0.0.1:8332")
213+
214+
// Seed with malformed input
215+
f.Add("not-a-url")
216+
f.Add("http://localhost:not-a-port")
217+
f.Add("http://[::1]:8332")
218+
f.Add("http://localhost:8332:extra")
219+
220+
f.Fuzz(func(t *testing.T, hostPort string) {
221+
// Simulate the trimming logic from loadBitcoinConfiguration
222+
trimmed := strings.TrimPrefix(hostPort, "http://")
223+
trimmed = strings.TrimPrefix(trimmed, "https://")
224+
225+
// Should never panic
226+
parts := strings.Split(trimmed, ":")
227+
228+
// Validate split result
229+
require.NotNil(t, parts, "split should always return a slice")
230+
require.GreaterOrEqual(t, len(parts), 1, "split should return at least one element")
231+
232+
// If we have at least one part, it could be a valid host
233+
if len(parts) >= 1 {
234+
host := parts[0]
235+
_ = host // host can be any string
236+
}
237+
238+
// If we have at least two parts, second part could be a port
239+
if len(parts) >= 2 {
240+
port := parts[1]
241+
_ = port // port can be any string (validation happens elsewhere)
242+
}
243+
})
244+
}

app/models/alert_message.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,8 @@ func (m *AlertMessage) ReadRaw() error {
272272
m.SetRawMessage(ak)
273273
}
274274

275-
if len(m.GetRawMessage()) < 16 {
276-
// todo DETERMINE ACTUAL PROPER LENGTH
275+
if len(m.GetRawMessage()) < 20 {
276+
// Minimum length: version(4) + sequence(4) + timestamp(8) + alertType(4) = 20 bytes
277277
return ErrAlertTooShort
278278
}
279279
ak := m.GetRawMessage()

0 commit comments

Comments
 (0)