Separate observation from evaluation in blacklist sources

Each source's Query() method previously set r.Listed and r.Severity,
embedding verdict logic inside the prober. Evaluation now lives in a
dedicated Evaluate(SourceResult) (bool, string) method per source,
keeping Query() as pure observation.

A package-level EvaluateResult() helper looks up the source by ID and
delegates to its Evaluate method; rules.go, report.go, types.go, and
provider.go all call this instead of reading pre-set r.Listed/r.Severity
values. An unknownSource sentinel handles results whose source is no
longer registered.
This commit is contained in:
nemunaire 2026-05-15 18:04:10 +08:00
commit c437339bda
13 changed files with 123 additions and 44 deletions

View file

@ -40,18 +40,28 @@ func (*sourceRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts
}}
}
byID := make(map[string]Source, len(Sources()))
for _, s := range Sources() {
byID[s.ID()] = s
}
out := make([]sdk.CheckState, 0, len(data.Results))
for _, r := range data.Results {
out = append(out, evaluateOne(r))
src, ok := byID[r.SourceID]
if !ok {
src = unknownSource{}
}
out = append(out, evaluateOne(r, src))
}
return out
}
func evaluateOne(r SourceResult) sdk.CheckState {
func evaluateOne(r SourceResult, src Source) sdk.CheckState {
subj := r.SourceName
if r.Subject != "" && r.Subject != r.SourceName {
subj = r.SourceName + " / " + r.Subject
}
listed, severity := src.Evaluate(r)
switch {
case !r.Enabled:
return sdk.CheckState{
@ -72,9 +82,9 @@ func evaluateOne(r SourceResult) sdk.CheckState {
Message: subj + ": query failed: " + r.Error,
Code: "source_error",
}
case r.Listed:
case listed:
return sdk.CheckState{
Status: severityToStatus(r.Severity),
Status: severityToStatus(severity),
Subject: subj,
Message: fmt.Sprintf("Listed in %s: %s", subj, joinNonEmpty(r.Reasons, "; ")),
Code: "source_listed",
@ -95,6 +105,17 @@ func evaluateOne(r SourceResult) sdk.CheckState {
}
}
// unknownSource is a sentinel used when a SourceResult references a source ID
// that is no longer in the registry. Evaluate always returns (false, "").
type unknownSource struct{}
func (unknownSource) ID() string { return "" }
func (unknownSource) Name() string { return "unknown" }
func (unknownSource) Options() SourceOptions { return SourceOptions{} }
func (unknownSource) Query(_ context.Context, _, _ string, _ sdk.CheckerOptions) []SourceResult { return nil }
func (unknownSource) Diagnose(_ SourceResult) Diagnosis { return Diagnosis{} }
func (unknownSource) Evaluate(_ SourceResult) (bool, string) { return false, "" }
func severityToStatus(sev string) sdk.Status {
switch sev {
case SeverityCrit: