fix: renforcer limites TLS, timeouts socket et validation config
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled
Co-authored-by: aider (openrouter/openai/gpt-5.3-codex) <aider@aider.chat>
This commit is contained in:
@ -1,8 +1,13 @@
|
||||
package tlsparse
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"ja4sentinel/api"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
@ -203,6 +208,8 @@ func createTLSServerHello(version uint16) []byte {
|
||||
|
||||
func TestNewParser(t *testing.T) {
|
||||
parser := NewParser()
|
||||
defer parser.Close()
|
||||
|
||||
if parser == nil {
|
||||
t.Error("NewParser() returned nil")
|
||||
}
|
||||
@ -288,3 +295,152 @@ func TestExtractTCPMeta_MSSInvalid_NoPanic(t *testing.T) {
|
||||
t.Fatalf("expected MSS_INVALID in options, got %v", meta.Options)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrCreateFlow_RespectsMaxTrackedFlows(t *testing.T) {
|
||||
parser := NewParser()
|
||||
defer parser.Close()
|
||||
|
||||
parser.maxTrackedFlows = 1
|
||||
|
||||
flow1 := parser.getOrCreateFlow(
|
||||
flowKey("192.168.1.1", 12345, "10.0.0.1", 443),
|
||||
"192.168.1.1", 12345, "10.0.0.1", 443,
|
||||
api.IPMeta{}, api.TCPMeta{},
|
||||
)
|
||||
if flow1 == nil {
|
||||
t.Fatal("first flow should be created")
|
||||
}
|
||||
|
||||
flow2 := parser.getOrCreateFlow(
|
||||
flowKey("192.168.1.2", 12346, "10.0.0.1", 443),
|
||||
"192.168.1.2", 12346, "10.0.0.1", 443,
|
||||
api.IPMeta{}, api.TCPMeta{},
|
||||
)
|
||||
if flow2 != nil {
|
||||
t.Fatal("second flow should be nil when maxTrackedFlows is reached")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcess_DropsWhenHelloBufferExceedsLimit(t *testing.T) {
|
||||
parser := NewParserWithTimeout(30 * time.Second)
|
||||
defer parser.Close()
|
||||
|
||||
parser.maxHelloBufferBytes = 10
|
||||
|
||||
srcIP := "192.168.1.10"
|
||||
dstIP := "10.0.0.1"
|
||||
srcPort := uint16(12345)
|
||||
dstPort := uint16(443)
|
||||
|
||||
// TLS-like payload, but intentionally incomplete to trigger accumulation.
|
||||
payloadChunk := []byte{0x16, 0x03, 0x03, 0x00, 0x20, 0x01} // len = 6
|
||||
|
||||
pkt1 := buildRawPacket(t, srcIP, dstIP, srcPort, dstPort, payloadChunk)
|
||||
ch, err := parser.Process(pkt1)
|
||||
if err != nil {
|
||||
t.Fatalf("first Process() error = %v", err)
|
||||
}
|
||||
if ch != nil {
|
||||
t.Fatal("first Process() should not return complete ClientHello")
|
||||
}
|
||||
|
||||
key := flowKey(srcIP, srcPort, dstIP, dstPort)
|
||||
|
||||
parser.mu.RLock()
|
||||
_, existsAfterFirst := parser.flows[key]
|
||||
parser.mu.RUnlock()
|
||||
if !existsAfterFirst {
|
||||
t.Fatal("flow should exist after first chunk")
|
||||
}
|
||||
|
||||
pkt2 := buildRawPacket(t, srcIP, dstIP, srcPort, dstPort, payloadChunk)
|
||||
ch, err = parser.Process(pkt2)
|
||||
if err != nil {
|
||||
t.Fatalf("second Process() error = %v", err)
|
||||
}
|
||||
if ch != nil {
|
||||
t.Fatal("second Process() should not return ClientHello")
|
||||
}
|
||||
|
||||
parser.mu.RLock()
|
||||
_, existsAfterSecond := parser.flows[key]
|
||||
parser.mu.RUnlock()
|
||||
if existsAfterSecond {
|
||||
t.Fatal("flow should be removed when hello buffer exceeds maxHelloBufferBytes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcess_NonTLSNewFlowNotTracked(t *testing.T) {
|
||||
parser := NewParser()
|
||||
defer parser.Close()
|
||||
|
||||
srcIP := "192.168.1.20"
|
||||
dstIP := "10.0.0.2"
|
||||
srcPort := uint16(23456)
|
||||
dstPort := uint16(443)
|
||||
|
||||
// Non-TLS content type (not 22)
|
||||
payload := []byte{0x17, 0x03, 0x03, 0x00, 0x05, 0x00}
|
||||
|
||||
pkt := buildRawPacket(t, srcIP, dstIP, srcPort, dstPort, payload)
|
||||
ch, err := parser.Process(pkt)
|
||||
if err != nil {
|
||||
t.Fatalf("Process() error = %v", err)
|
||||
}
|
||||
if ch != nil {
|
||||
t.Fatal("Process() should return nil for non-TLS new flow")
|
||||
}
|
||||
|
||||
key := flowKey(srcIP, srcPort, dstIP, dstPort)
|
||||
|
||||
parser.mu.RLock()
|
||||
_, exists := parser.flows[key]
|
||||
parser.mu.RUnlock()
|
||||
if exists {
|
||||
t.Fatal("non-TLS new flow should not be tracked")
|
||||
}
|
||||
}
|
||||
|
||||
func buildRawPacket(t *testing.T, srcIP, dstIP string, srcPort, dstPort uint16, payload []byte) api.RawPacket {
|
||||
t.Helper()
|
||||
|
||||
ip := &layers.IPv4{
|
||||
Version: 4,
|
||||
TTL: 64,
|
||||
SrcIP: net.ParseIP(srcIP).To4(),
|
||||
DstIP: net.ParseIP(dstIP).To4(),
|
||||
Protocol: layers.IPProtocolTCP,
|
||||
}
|
||||
|
||||
tcp := &layers.TCP{
|
||||
SrcPort: layers.TCPPort(srcPort),
|
||||
DstPort: layers.TCPPort(dstPort),
|
||||
Seq: 1,
|
||||
ACK: true,
|
||||
Window: 65535,
|
||||
}
|
||||
if err := tcp.SetNetworkLayerForChecksum(ip); err != nil {
|
||||
t.Fatalf("SetNetworkLayerForChecksum() error = %v", err)
|
||||
}
|
||||
|
||||
eth := &layers.Ethernet{
|
||||
SrcMAC: net.HardwareAddr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
|
||||
DstMAC: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
|
||||
EthernetType: layers.EthernetTypeIPv4,
|
||||
}
|
||||
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{
|
||||
FixLengths: true,
|
||||
ComputeChecksums: true,
|
||||
}
|
||||
|
||||
if err := gopacket.SerializeLayers(buf, opts, eth, ip, tcp, gopacket.Payload(payload)); err != nil {
|
||||
t.Fatalf("SerializeLayers() error = %v", err)
|
||||
}
|
||||
|
||||
return api.RawPacket{
|
||||
Data: buf.Bytes(),
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user