Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interfaces/prompting/patterns: add checks for duplicate pattern variants #14319

1 change: 1 addition & 0 deletions interfaces/prompting/patterns/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
if err != nil {
// This should never occur
logger.Noticef("patterns: cannot parse pattern variant '%s': %v", variant, err)
length, lengthUnchanged, moreRemain = v.NextVariant()

Check warning on line 83 in interfaces/prompting/patterns/render.go

View check run for this annotation

Codecov / codecov/patch

interfaces/prompting/patterns/render.go#L83

Added line #L83 was not covered by tests
continue
}
observe(i, variant)
Expand Down
35 changes: 35 additions & 0 deletions interfaces/prompting/patterns/render_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,41 @@ func (s *renderSuite) TestRenderAllVariants(c *C) {
c.Check(variants, HasLen, parsed.NumVariants())
}

func (s *renderSuite) TestRenderAllVariantsConflicts(c *C) {
// all variants are the exact same path
for _, testCase := range []struct {
pattern string
expectedVariants []string
}{
{
"/{fo{obar},foo{b{ar,ar},bar,{ba}r}}",
[]string{"/foobar"},
},
{
"/{{foo/{bar,baz},123},{123,foo{/bar,/baz}}}",
[]string{
"/foo/bar",
"/foo/baz",
"/123",
"/123",
"/foo/bar",
"/foo/baz",
},
},
} {
scanned, err := scan(testCase.pattern)
c.Assert(err, IsNil)
parsed, err := parse(scanned)
c.Assert(err, IsNil)
variants := make([]string, 0, len(testCase.expectedVariants))
renderAllVariants(parsed, func(index int, variant PatternVariant) {
variants = append(variants, variant.String())
})
c.Check(variants, DeepEquals, testCase.expectedVariants)
c.Check(variants, HasLen, parsed.NumVariants())
}
}

func (s *renderSuite) TestNextVariant(c *C) {
pattern := "/{,usr/}lib{,32,64,x32}/{,@{multiarch}/{,atomics/}}ld{-*,64}.so*"
scanned, err := scan(pattern)
Expand Down
13 changes: 11 additions & 2 deletions interfaces/prompting/requestrules/requestrules.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,9 @@
switch {
case !exists:
newVariantEntries[variantStr] = newEntry
case conflictingVariantEntry.RuleID == rule.ID:
// Rule has duplicate variant, so ignore it
return

Check warning on line 466 in interfaces/prompting/requestrules/requestrules.go

View check run for this annotation

Codecov / codecov/patch

interfaces/prompting/requestrules/requestrules.go#L464-L466

Added lines #L464 - L466 were not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why the new test TestAddRuleDuplicateVariants isn't covering this?? Investigating...

case rdb.isRuleWithIDExpired(conflictingVariantEntry.RuleID, rule.Timestamp):
expiredRules[conflictingVariantEntry.RuleID] = true
newVariantEntries[variantStr] = newEntry
Expand Down Expand Up @@ -540,17 +543,23 @@
err := fmt.Errorf("internal error: no rules in the rule tree for user %d, snap %q, interface %q, permission %q", rule.User, rule.Snap, rule.Interface, permission)
return []error{err}
}
seenVariants := make(map[string]bool, rule.Constraints.PathPattern.NumVariants())
var errs []error
removeVariant := func(index int, variant patterns.PatternVariant) {
variantEntry, exists := permVariants.VariantEntries[variant.String()]
variantStr := variant.String()
if seenVariants[variantStr] {
return
}

Check warning on line 552 in interfaces/prompting/requestrules/requestrules.go

View check run for this annotation

Codecov / codecov/patch

interfaces/prompting/requestrules/requestrules.go#L551-L552

Added lines #L551 - L552 were not covered by tests
seenVariants[variantStr] = true
variantEntry, exists := permVariants.VariantEntries[variantStr]
if !exists {
// Database was left inconsistent, should not occur
errs = append(errs, fmt.Errorf(`internal error: path pattern variant not found in the rule tree: %q`, variant))
} else if variantEntry.RuleID != rule.ID {
// Database was left inconsistent, should not occur
errs = append(errs, fmt.Errorf(`internal error: path pattern variant maps to different rule ID: %q: %s`, variant, variantEntry.RuleID.String()))
} else {
delete(permVariants.VariantEntries, variant.String())
delete(permVariants.VariantEntries, variantStr)
}
}
rule.Constraints.PathPattern.RenderAllVariants(removeVariant)
Expand Down
24 changes: 24 additions & 0 deletions interfaces/prompting/requestrules/requestrules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,30 @@ func addRuleFromTemplate(c *C, rdb *requestrules.RuleDB, template *addRuleConten
return rdb.AddRule(partial.User, partial.Snap, partial.Interface, constraints, partial.Outcome, partial.Lifespan, partial.Duration)
}

func (s *requestrulesSuite) TestAddRuleDuplicateVariants(c *C) {
rdb, err := requestrules.New(s.defaultNotifyRule)
c.Assert(err, IsNil)

ruleContents := &addRuleContents{
User: s.defaultUser,
Snap: "nextcloud",
Interface: "home",
PathPattern: "/home/test/{{foo/{bar,baz},123},{123,foo{/bar,/baz}}}",
Permissions: []string{"read"},
Outcome: prompting.OutcomeAllow,
Lifespan: prompting.LifespanForever,
Duration: "",
}

var addedRules []*requestrules.Rule
rule, err := addRuleFromTemplate(c, rdb, ruleContents, ruleContents)
c.Check(err, IsNil)
c.Check(rule, NotNil)
addedRules = append(addedRules, rule)
s.checkWrittenRuleDB(c, addedRules)
s.checkNewNoticesSimple(c, nil, rule)
}

func (s *requestrulesSuite) TestAddRuleErrors(c *C) {
rdb, err := requestrules.New(s.defaultNotifyRule)
c.Assert(err, IsNil)
Expand Down
Loading