mirror of
https://github.com/luxfi/node.git
synced 2026-10-30 15:24:05 +00:00
The module already called itself github.com/luxfi/node while containing no node in Go: cmd/luxd was twenty-one lines that printed the size of a few types, and the Go column of the matrix measured a VM plugin. A repo that fetches the thing it claims to be has not replaced it, so the Go sources move in — 1515 files, the daemon and everything under it — and the build reaches outside this module for nothing. Rust and C++ sit beside it unchanged and are still measured against it by the differential. That is the point of one repo: the reference implementation and the two ports are the same checkout, so parity is a thing you can run rather than a thing you assert. Verified: builds, and the closure carries no lux-private — checked at build time rather than believed. The daemon starts twelve chains on a local network (P X C Q Z A B G K M F, plus D through its plugin) and reports healthy.
258 lines
7.3 KiB
Go
258 lines
7.3 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package security
|
|
|
|
import (
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestInputValidator(t *testing.T) {
|
|
v := NewInputValidator()
|
|
|
|
t.Run("ValidateStringLength", func(t *testing.T) {
|
|
// Valid length
|
|
require.NoError(t, v.ValidateStringLength("short string"))
|
|
|
|
// Too long
|
|
longString := strings.Repeat("a", v.MaxStringLength+1)
|
|
require.Error(t, v.ValidateStringLength(longString))
|
|
})
|
|
|
|
t.Run("ValidateFilePath", func(t *testing.T) {
|
|
// Use platform-specific paths
|
|
if runtime.GOOS == "windows" {
|
|
v.AllowedPathPrefixes = []string{"C:\\tmp", "C:\\var\\tmp"}
|
|
|
|
// Valid paths
|
|
require.NoError(t, v.ValidateFilePath("C:\\tmp\\test.txt"))
|
|
require.NoError(t, v.ValidateFilePath("C:\\var\\tmp\\data.log"))
|
|
|
|
// Path traversal attempts
|
|
require.Error(t, v.ValidateFilePath("C:\\tmp\\..\\etc\\passwd"))
|
|
require.Error(t, v.ValidateFilePath("..\\..\\etc\\passwd"))
|
|
|
|
// Not absolute
|
|
require.Error(t, v.ValidateFilePath("relative\\path.txt"))
|
|
|
|
// Outside allowed prefixes
|
|
require.Error(t, v.ValidateFilePath("C:\\etc\\passwd"))
|
|
require.Error(t, v.ValidateFilePath("C:\\Users\\user\\file.txt"))
|
|
} else {
|
|
v.AllowedPathPrefixes = []string{"/tmp", "/var/tmp"}
|
|
|
|
// Valid paths
|
|
require.NoError(t, v.ValidateFilePath("/tmp/test.txt"))
|
|
require.NoError(t, v.ValidateFilePath("/var/tmp/data.log"))
|
|
|
|
// Path traversal attempts
|
|
require.Error(t, v.ValidateFilePath("/tmp/../etc/passwd"))
|
|
require.Error(t, v.ValidateFilePath("../../etc/passwd"))
|
|
|
|
// Not absolute
|
|
require.Error(t, v.ValidateFilePath("relative/path.txt"))
|
|
|
|
// Outside allowed prefixes
|
|
require.Error(t, v.ValidateFilePath("/etc/passwd"))
|
|
require.Error(t, v.ValidateFilePath("/home/user/file.txt"))
|
|
}
|
|
})
|
|
|
|
t.Run("ValidateIPAddress", func(t *testing.T) {
|
|
// Valid IPs
|
|
require.NoError(t, v.ValidateIPAddress("192.168.1.1"))
|
|
require.NoError(t, v.ValidateIPAddress("10.0.0.1"))
|
|
require.NoError(t, v.ValidateIPAddress("::1"))
|
|
require.NoError(t, v.ValidateIPAddress("2001:db8::1"))
|
|
|
|
// Valid hostnames
|
|
require.NoError(t, v.ValidateIPAddress("example.com"))
|
|
require.NoError(t, v.ValidateIPAddress("sub.domain.example.com"))
|
|
require.NoError(t, v.ValidateIPAddress("localhost"))
|
|
|
|
// With port
|
|
require.NoError(t, v.ValidateIPAddress("192.168.1.1:8080"))
|
|
require.NoError(t, v.ValidateIPAddress("example.com:443"))
|
|
|
|
// Invalid
|
|
require.Error(t, v.ValidateIPAddress("not_a_valid-host!"))
|
|
require.Error(t, v.ValidateIPAddress("192.168.1.256")) // Invalid octet
|
|
require.Error(t, v.ValidateIPAddress(strings.Repeat("a", 254))) // Too long
|
|
})
|
|
|
|
t.Run("ValidatePort", func(t *testing.T) {
|
|
// Valid ports
|
|
require.NoError(t, v.ValidatePort(80))
|
|
require.NoError(t, v.ValidatePort(443))
|
|
require.NoError(t, v.ValidatePort(8080))
|
|
require.NoError(t, v.ValidatePort(65535))
|
|
|
|
// Invalid ports
|
|
require.Error(t, v.ValidatePort(0))
|
|
require.Error(t, v.ValidatePort(-1))
|
|
require.Error(t, v.ValidatePort(65536))
|
|
require.Error(t, v.ValidatePort(100000))
|
|
})
|
|
}
|
|
|
|
func TestSafeIntConversion(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value int64
|
|
bitSize int
|
|
wantErr bool
|
|
}{
|
|
{"int8 valid", 127, 8, false},
|
|
{"int8 overflow", 128, 8, true},
|
|
{"int8 underflow", -129, 8, true},
|
|
{"int16 valid", 32767, 16, false},
|
|
{"int16 overflow", 32768, 16, true},
|
|
{"int32 valid", 2147483647, 32, false},
|
|
{"int32 overflow", 2147483648, 32, true},
|
|
{"int64 valid", 9223372036854775807, 64, false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := SafeIntConversion(tt.value, tt.bitSize)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSafeUintConversion(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
value int64
|
|
bitSize int
|
|
wantErr bool
|
|
}{
|
|
{"uint8 valid", 255, 8, false},
|
|
{"uint8 overflow", 256, 8, true},
|
|
{"uint8 negative", -1, 8, true},
|
|
{"uint16 valid", 65535, 16, false},
|
|
{"uint16 overflow", 65536, 16, true},
|
|
{"uint32 valid", 4294967295, 32, false},
|
|
{"uint32 overflow", 4294967296, 32, true},
|
|
{"uint64 valid", 9223372036854775807, 64, false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := SafeUintConversion(tt.value, tt.bitSize)
|
|
if tt.wantErr {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConstantTimeCompare(t *testing.T) {
|
|
tests := []struct {
|
|
a, b string
|
|
expected bool
|
|
}{
|
|
{"password", "password", true},
|
|
{"password", "Password", false},
|
|
{"short", "longer", false},
|
|
{"", "", true},
|
|
{"abc", "def", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
result := ConstantTimeCompare(tt.a, tt.b)
|
|
require.Equal(t, tt.expected, result, "Comparing %q and %q", tt.a, tt.b)
|
|
}
|
|
}
|
|
|
|
func TestSanitizeSQL(t *testing.T) {
|
|
tests := []struct {
|
|
input string
|
|
expected string
|
|
}{
|
|
{"normal input", "normal input"},
|
|
{"input; DROP TABLE users", "input DROP TABLE users"},
|
|
{"input--comment", "inputcomment"},
|
|
{"input /* comment */", "input comment "},
|
|
{"input'quote", "input''quote"},
|
|
{`input"doublequote`, `input""doublequote`},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
result := SanitizeSQL(tt.input)
|
|
require.Equal(t, tt.expected, result, "Sanitizing %q", tt.input)
|
|
}
|
|
}
|
|
|
|
func TestValidateAlphanumeric(t *testing.T) {
|
|
// Valid
|
|
require.NoError(t, ValidateAlphanumeric("abc123"))
|
|
require.NoError(t, ValidateAlphanumeric("ABC"))
|
|
require.NoError(t, ValidateAlphanumeric("123"))
|
|
|
|
// Invalid
|
|
require.Error(t, ValidateAlphanumeric("abc-123"))
|
|
require.Error(t, ValidateAlphanumeric("abc_123"))
|
|
require.Error(t, ValidateAlphanumeric("abc 123"))
|
|
require.Error(t, ValidateAlphanumeric("abc@123"))
|
|
}
|
|
|
|
func TestValidateEmail(t *testing.T) {
|
|
// Valid
|
|
require.NoError(t, ValidateEmail("[email protected]"))
|
|
require.NoError(t, ValidateEmail("[email protected]"))
|
|
require.NoError(t, ValidateEmail("[email protected]"))
|
|
|
|
// Invalid
|
|
require.Error(t, ValidateEmail("notanemail"))
|
|
require.Error(t, ValidateEmail("@example.com"))
|
|
require.Error(t, ValidateEmail("user@"))
|
|
require.Error(t, ValidateEmail("[email protected]"))
|
|
require.Error(t, ValidateEmail(strings.Repeat("a", 250)+"@example.com"))
|
|
}
|
|
|
|
func TestValidateURL(t *testing.T) {
|
|
// Valid
|
|
require.NoError(t, ValidateURL("http://example.com"))
|
|
require.NoError(t, ValidateURL("https://example.com"))
|
|
require.NoError(t, ValidateURL("https://example.com/path"))
|
|
|
|
// Invalid - dangerous patterns
|
|
require.Error(t, ValidateURL("javascript:alert(1)"))
|
|
require.Error(t, ValidateURL("data:text/html,<script>alert(1)</script>"))
|
|
require.Error(t, ValidateURL("vbscript:msgbox"))
|
|
require.Error(t, ValidateURL("file:///etc/passwd"))
|
|
|
|
// Invalid - wrong protocol
|
|
require.Error(t, ValidateURL("ftp://example.com"))
|
|
require.Error(t, ValidateURL("example.com"))
|
|
}
|
|
|
|
func TestRateLimiter(t *testing.T) {
|
|
rl := NewRateLimiter(3, 60) // 3 requests per 60 seconds
|
|
clientID := "test-client"
|
|
now := time.Now().Unix()
|
|
|
|
// First 3 requests should be allowed
|
|
require.True(t, rl.Allow(clientID, now))
|
|
require.True(t, rl.Allow(clientID, now+1))
|
|
require.True(t, rl.Allow(clientID, now+2))
|
|
|
|
// 4th request should be blocked
|
|
require.False(t, rl.Allow(clientID, now+3))
|
|
|
|
// After window expires, should allow again
|
|
require.True(t, rl.Allow(clientID, now+61))
|
|
}
|