fix(alert): prevent persisted rule crash loops

This commit is contained in:
naiba committed 2026-09-02 13:30:46 +00:00
1 parent 8f1147c800
commit 19daf4ad3c
8 files changed
+479 -71

No files matched your search

+12
View File
@@ -172,6 +172,12 @@ func validateRule(c *gin.Context, r *model.AlertRule) error {
}
if len(r.Rules) > 0 {
for _, rule := range r.Rules {
if rule == nil {
return singleton.Localizer.ErrorT("rule is not set")
}
if !rule.IsSupportedType() {
return singleton.Localizer.ErrorT("unsupported rule type")
}
switch rule.Cover {
case model.RuleCoverAll, model.RuleCoverIgnoreAll:
default:
@@ -182,10 +188,16 @@ func validateRule(c *gin.Context, r *model.AlertRule) error {
if rule.Duration < 3 {
return singleton.Localizer.ErrorT("duration need to be at least 3")
}
if rule.Duration > model.MaxAlertRuleDuration {
return singleton.Localizer.ErrorT("duration is too large")
}
} else {
if rule.CycleInterval < 1 {
return singleton.Localizer.ErrorT("cycle_interval need to be at least 1")
}
if rule.CycleInterval > model.MaxAlertRuleCycleInterval {
return singleton.Localizer.ErrorT("cycle_interval is too large")
}
if rule.CycleStart == nil {
return singleton.Localizer.ErrorT("cycle_start is not set")
}
@@ -0,0 +1,67 @@
package controller
import (
"math"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/nezhahq/nezha/model"
)
func TestValidateRuleRejectsCrashPayloadsFromMember(t *testing.T) {
ctx := newMemberValidationContext(t)
cycleStart := time.Now().Add(-time.Hour)
tests := []struct {
name string
rule *model.Rule
}{
{
name: "unknown rule type",
rule: &model.Rule{Type: "attacker_controlled", Duration: 3, Cover: model.RuleCoverAll},
},
{
name: "duration overflows int",
rule: &model.Rule{Type: "offline", Duration: math.MaxUint64, Cover: model.RuleCoverAll},
},
{
name: "cycle interval overflows int",
rule: &model.Rule{
Type: "transfer_in_cycle",
CycleStart: &cycleStart,
CycleInterval: math.MaxUint64,
Cover: model.RuleCoverAll,
},
},
{
name: "nil rule",
rule: nil,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
alert := &model.AlertRule{
Common: model.Common{UserID: 200},
Name: "security regression",
Rules: []*model.Rule{test.rule},
}
require.Error(t, validateRule(ctx, alert))
})
}
}
func TestValidateRuleAcceptsSafeDurationBoundary(t *testing.T) {
ctx := newMemberValidationContext(t)
alert := &model.AlertRule{
Common: model.Common{UserID: 200},
Name: "duration boundary",
Rules: []*model.Rule{{
Type: "offline",
Duration: model.MaxAlertRuleDuration,
Cover: model.RuleCoverAll,
}},
}
require.NoError(t, validateRule(ctx, alert))
}