Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion persist/string-adapter/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ func (a *Adapter) LoadPolicy(model model.Model) error {
if str == "" {
continue
}
_ = persist.LoadPolicyLine(str, model)
if err := persist.LoadPolicyLine(str, model); err != nil {
return err

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Not blocking, just so it is a deliberate choice: returning here leaves model holding every rule parsed before the bad line. The file adapter does the same, so this is consistent rather than new, and Enforcer.loadPolicyFromAdapter loads into a scratch copy and swaps only on success, so the partial model is not reachable through NewEnforcer or Enforcer.LoadPolicy. It is visible only to code calling adapter.LoadPolicy on a model it owns.

}
Comment on lines 49 to +54

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Trim before the handler, the way loadPolicyFile does, so the two adapters agree on what a line is and not just on error propagation.

Without it, " # a note" becomes missing required section # where it used to be skipped. It also leaves " " panicking in LoadPolicyArray at key[:1], because str == "" does not catch a whitespace-only line. To be clear, that panic is not introduced by this PR: I reproduced it on the pre-change loop body too. The trim just closes it for free.

Suggested change
if str == "" {
continue
}
_ = persist.LoadPolicyLine(str, model)
if err := persist.LoadPolicyLine(str, model); err != nil {
return err
}
if line := strings.TrimSpace(str); line != "" {
if err := persist.LoadPolicyLine(line, model); err != nil {
return err
}
}

strings is already imported. With this applied, both inputs above load cleanly, the malformed-quote case still errors, gofmt and vet are clean, and go test ./... is green.

}

return nil
Expand Down
13 changes: 13 additions & 0 deletions persist/string-adapter/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,16 @@ g, alice, data_group_admin
t.Error("unexpected enforce result")
}
}

// Test_LoadPolicyMalformedLine verifies that a malformed policy line (here an
// unterminated quoted field) makes LoadPolicy return an error instead of
// silently producing a model that lacks the rule.
func Test_LoadPolicyMalformedLine(t *testing.T) {
a := NewAdapter(`p, alice, data1, "read`)
m := model.NewModel()

err := a.LoadPolicy(m)
if err == nil {
t.Fatal("LoadPolicy() error = nil, want a parse error for the unterminated quoted field")
}
}
Comment on lines +178 to +186

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This does fail before the change, so it earns its place. But it is close to vacuous: model.NewModel() has no model text loaded, and against that model a well-formed line errors too.

"p, alice, data1, read"    -> missing required section p
"p, alice, data1, \"read"  -> parse error ... extraneous or missing " in quoted-field

The assertion cannot tell those apart, so it would keep passing if the csv quoting behaviour regressed entirely. Loading a real model and pinning the error message fixes both:

Suggested change
func Test_LoadPolicyMalformedLine(t *testing.T) {
a := NewAdapter(`p, alice, data1, "read`)
m := model.NewModel()
err := a.LoadPolicy(m)
if err == nil {
t.Fatal("LoadPolicy() error = nil, want a parse error for the unterminated quoted field")
}
}
func Test_LoadPolicyMalformedLine(t *testing.T) {
conf := `
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
`
m := model.NewModel()
if err := m.LoadModelFromText(conf); err != nil {
t.Fatal(err)
}
// A well-formed line must still load against this model, so the failure
// below can only come from the unterminated quote.
if err := NewAdapter(`p, alice, data1, read`).LoadPolicy(m); err != nil {
t.Fatalf("well-formed line should load, got %v", err)
}
err := NewAdapter(`p, alice, data1, "read`).LoadPolicy(m)
if err == nil {
t.Fatal("LoadPolicy() error = nil, want a parse error for the unterminated quoted field")
}
if !strings.Contains(err.Error(), "quoted-field") {
t.Fatalf("want a csv quoting error, got %v", err)
}
}

strings is already imported in this file. I checked this passes on the branch and still fails against the pre-change loop, on the same assertion as before.

Loading