aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-15 22:11:33 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-15 22:11:33 +0200
commit77a015b96db8727bb12d0869f180a44d087f6319 (patch)
treed6e4e485f4556a676fd92a6792602fec690065be /internal
parent6b38d302daa54140e4e26ad903297a1bba0cbd41 (diff)
downloadkrino-77a015b96db8727bb12d0869f180a44d087f6319.tar.gz
krino-77a015b96db8727bb12d0869f180a44d087f6319.zip
an undecided (stop) rule ends the search for that file
Diffstat (limited to 'internal')
-rw-r--r--internal/engine/exclude_test.go41
-rw-r--r--internal/engine/match.go11
2 files changed, 52 insertions, 0 deletions
diff --git a/internal/engine/exclude_test.go b/internal/engine/exclude_test.go
index 7069623..7d9ce2a 100644
--- a/internal/engine/exclude_test.go
+++ b/internal/engine/exclude_test.go
@@ -576,3 +576,44 @@ func TestDuplicateExcludeFailsClosed(t *testing.T) {
}
}
}
+
+// TestUndecidedStopRuleStops: a (stop) rule whose condition cannot be
+// decided ends the search for that file, so a later rule does not act on a
+// file the stop rule was written to keep (plan 12).
+func TestUndecidedStopRuleStops(t *testing.T) {
+ big := "confidential " + strings.Repeat("x", 2048)
+ h, dl := excludeTree(t, map[string]string{"big.txt": big, "small.txt": "nothing"})
+ main := writeConfig(t, h, `(include "dl")`, map[string]string{"dl": `
+(path "~/dl")
+(max-read 1K)
+(rule "keep" (when (content "confidential")) (stop))
+(rule "all" (move "Out"))
+`})
+ e, errs := Load(main)
+ if len(errs) > 0 {
+ t.Fatal(errs)
+ }
+ r, err := e.Match(context.Background(), e.Dirs[0])
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, fm := range append(append([]FileMatch{}, r.Matched...), r.Unmatched...) {
+ switch fm.File.Rel {
+ case "big.txt":
+ if len(fm.Rules) != 0 || len(fm.Warnings) == 0 {
+ t.Errorf("big.txt: rules %d, warnings %v; want no rule and the warning", len(fm.Rules), fm.Warnings)
+ }
+ case "small.txt":
+ if len(fm.Rules) != 1 {
+ t.Errorf("small.txt: rules %d; want the move", len(fm.Rules))
+ }
+ }
+ }
+ x, err := e.Explain(context.Background(), filepath.Join(dl, "big.txt"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(x.Rules) != 2 || x.Rules[1].Stopped != "stopped by rule keep, which could not be decided" {
+ t.Errorf("explain rules = %+v; want all stopped by the undecided keep", x.Rules)
+ }
+}
diff --git a/internal/engine/match.go b/internal/engine/match.go
index f6e1706..67f0c06 100644
--- a/internal/engine/match.go
+++ b/internal/engine/match.go
@@ -171,6 +171,12 @@ func evalFile(run *matchRun, file scan.File) FileMatch {
}
if res.Unreadable {
f.undecided = true
+ if r.Conf.Stop {
+ // A (stop) rule that cannot be decided ends the search too:
+ // it may be the rule written to keep this file from the
+ // ones below (plan 12).
+ break
+ }
}
if !res.Match {
continue
@@ -321,6 +327,11 @@ func (e *Engine) Explain(ctx context.Context, path string) (*Explanation, error)
match := trace.Value
if trace.Unknown {
f.undecided = true
+ if r.Conf.Stop {
+ rules = append(rules, RuleTrace{Rule: r, Trace: trace})
+ stoppedBy = r.Name + ", which could not be decided"
+ continue
+ }
}
if match {
f.matched = true