Files
CLICD/backend/internal/api/security_test.go
T
MengMengCode cd258fd6ac FIX #20
2026-06-17 20:41:11 +08:00

149 lines
3.5 KiB
Go

package api
import (
"fmt"
"testing"
"clicd/internal/config"
)
func TestDetectReflectionAbuseIgnoresSingleDNSResolver(t *testing.T) {
resetSecurityTestConfig()
stats := newTrafficStats()
for i := 0; i < 180; i++ {
stats.add(connEntry{
dstIP: "1.1.1.1",
dstPort: 53,
proto: "udp",
state: "UNREPLIED",
})
}
ss := newSecurityScanner()
ss.detectReflectionAbuse("ct-dns", "10.0.0.2", stats)
if len(ss.alerts) != 0 {
t.Fatalf("normal DNS queries to one resolver should not trigger reflection alert: %+v", ss.alerts)
}
}
func TestDetectReflectionAbuseFlagsWideDNSFanout(t *testing.T) {
resetSecurityTestConfig()
stats := newTrafficStats()
for i := 0; i < 120; i++ {
stats.add(connEntry{
dstIP: fmt.Sprintf("203.0.113.%d", i),
dstPort: 53,
proto: "udp",
state: "UNREPLIED",
})
}
ss := newSecurityScanner()
ss.detectReflectionAbuse("ct-dns", "10.0.0.2", stats)
if len(ss.alerts) != 1 {
t.Fatalf("expected one reflection alert, got %+v", ss.alerts)
}
if got := ss.alerts[0].Type; got != "reflection" {
t.Fatalf("expected reflection alert, got %q", got)
}
}
func TestDetectPortScansUsesHalfOpenConnections(t *testing.T) {
resetSecurityTestConfig()
established := newTrafficStats()
for port := 8000; port < 8020; port++ {
established.add(connEntry{
dstIP: "198.51.100.10",
dstPort: port,
proto: "tcp",
state: "ESTABLISHED",
})
}
ss := newSecurityScanner()
ss.detectPortScans("ct-web", "10.0.0.3", established)
if len(ss.alerts) != 0 {
t.Fatalf("established multi-port connections should not trigger port scan alert: %+v", ss.alerts)
}
halfOpen := newTrafficStats()
for port := 8000; port < 8012; port++ {
halfOpen.add(connEntry{
dstIP: "198.51.100.10",
dstPort: port,
proto: "tcp",
state: "SYN_SENT",
})
}
ss.detectPortScans("ct-web", "10.0.0.3", halfOpen)
if len(ss.alerts) != 1 {
t.Fatalf("expected one port scan alert, got %+v", ss.alerts)
}
if got := ss.alerts[0].Type; got != "port_scan" {
t.Fatalf("expected port_scan alert, got %q", got)
}
}
func TestCancelPendingSecurityStops(t *testing.T) {
resetSecurityTestConfig()
q := &TaskQueue{
tasks: map[string]*Task{},
}
securityTask := &Task{
ID: "task-1",
Type: TaskStop,
ContainerID: 1,
Status: "pending",
User: "system:security",
}
userTask := &Task{
ID: "task-2",
Type: TaskStop,
ContainerID: 2,
Status: "pending",
User: "admin",
}
runningSecurityTask := &Task{
ID: "task-3",
Type: TaskStop,
ContainerID: 3,
Status: "running",
User: "system:security",
}
q.tasks[securityTask.ID] = securityTask
q.tasks[userTask.ID] = userTask
q.tasks[runningSecurityTask.ID] = runningSecurityTask
q.opQueue = []*Task{securityTask, userTask, runningSecurityTask}
if got := q.CancelPendingSecurityStops(); got != 1 {
t.Fatalf("expected one pending security stop to be cancelled, got %d", got)
}
if _, ok := q.tasks[securityTask.ID]; ok {
t.Fatal("pending security stop task was not removed")
}
if _, ok := q.tasks[userTask.ID]; !ok {
t.Fatal("user stop task should not be removed")
}
if _, ok := q.tasks[runningSecurityTask.ID]; !ok {
t.Fatal("running security stop task should be left for worker-side skip")
}
if len(q.opQueue) != 2 {
t.Fatalf("expected op queue to keep two tasks, got %d", len(q.opQueue))
}
}
func resetSecurityTestConfig() {
config.AppConfig = &config.ClicdConfig{
Containers: []config.Container{},
AuditLogs: []config.AuditLog{},
Tasks: []config.SavedTask{},
}
}