This is an automated email from the ASF dual-hosted git repository.
hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin.git
The following commit(s) were added to refs/heads/master by this push:
new 6a018f2 fix: don't treat # and ; inside quoted model strings as
comments
6a018f2 is described below
commit 6a018f27246f5f270f6cfa6ca3e8c82f45812bec
Author: Yang Luo <[email protected]>
AuthorDate: Mon Nov 2 20:55:20 2026 +0800
fix: don't treat # and ; inside quoted model strings as comments
---
config/config.go | 11 +++++++++--
config/config_test.go | 3 +++
config/testdata/testini.ini | 1 +
enforcer_test.go | 29 +++++++++++++++++++++++++++++
util/util.go | 36 ++++++++++++++++++++----------------
util/util_test.go | 6 +++++-
6 files changed, 67 insertions(+), 19 deletions(-)
diff --git a/config/config.go b/config/config.go
index 6b6f2d4..22f4a85 100644
--- a/config/config.go
+++ b/config/config.go
@@ -153,9 +153,16 @@ func (c *Config) parseBuffer(buf *bufio.Reader) error {
canWrite = true
}
+ // a comment marker inside a quoted string is part of
the value
end := len(p)
- for i, value := range p {
- if value == DEFAULT_COMMENT[0] || value ==
DEFAULT_COMMENT_SEM[0] {
+ for i := 0; i < len(p); i++ {
+ if p[i] == '"' || p[i] == '\'' {
+ j := bytes.IndexByte(p[i+1:], p[i])
+ if j < 0 {
+ break
+ }
+ i += j + 1
+ } else if p[i] == DEFAULT_COMMENT[0] || p[i] ==
DEFAULT_COMMENT_SEM[0] {
end = i
break
}
diff --git a/config/config_test.go b/config/config_test.go
index 66b7454..c3e0591 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -50,6 +50,9 @@ func TestGet(t *testing.T) {
if v := config.String("mysql::mysql.master.user"); v != "root" {
t.Errorf("Get failure: expected different value for
mysql::mysql.master.user (expected: [%#v] got: [%#v])", "root", v)
}
+ if v := config.String("mysql::mysql.master.note"); v != `"a#b;c" 'd#e'`
{
+ t.Errorf("Get failure: expected different value for
mysql::mysql.master.note (expected: [%#v] got: [%#v])", `"a#b;c" 'd#e'`, v)
+ }
if v := config.String("mysql::mysql.master.pass"); v != "89dds)2$" {
t.Errorf("Get failure: expected different value for
mysql::mysql.master.pass (expected: [%#v] got: [%#v])", "89dds)2$", v)
}
diff --git a/config/testdata/testini.ini b/config/testdata/testini.ini
index cbb9bab..86bcb39 100644
--- a/config/testdata/testini.ini
+++ b/config/testdata/testini.ini
@@ -17,6 +17,7 @@ mysql.master.host = 10.0.0.1 # host # 10.0.0.1
mysql.master.user = root ; user name
mysql.master.pass = 89dds)2$#d
mysql.master.db = act
+mysql.master.note = "a#b;c" 'd#e' # note
; math config
[math]
diff --git a/enforcer_test.go b/enforcer_test.go
index 918f38e..6e3ba90 100644
--- a/enforcer_test.go
+++ b/enforcer_test.go
@@ -187,6 +187,35 @@ m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
testEnforce(t, e, "bob", "data2", "write", true)
}
+func TestCommentMarkerInMatcherString(t *testing.T) {
+ text := `
+[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.act == "#write" || r.obj == 'a;b') # comment
+`
+ m, err := model.NewModelFromString(text)
+ if err != nil {
+ t.Fatal(err)
+ }
+ e, err := NewEnforcer(m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, _ = e.AddPolicy("alice", "data1", "read")
+
+ testEnforce(t, e, "alice", "data1", "#write", true)
+ testEnforce(t, e, "alice", "a;b", "read", true)
+ testEnforce(t, e, "alice", "data1", "read", false)
+}
+
func TestNotUsedRBACModelInMemory(t *testing.T) {
m := model.NewModel()
m.AddDef("r", "r", "sub, obj, act")
diff --git a/util/util.go b/util/util.go
index d508bfd..0808992 100644
--- a/util/util.go
+++ b/util/util.go
@@ -48,18 +48,7 @@ func EscapeAssertion(s string) string {
if quote != '"' && quote != '\'' {
continue
}
- end := i + 1
- for end < len(s) && s[end] != quote {
- if s[end] == '\\' {
- end++
- }
- end++
- }
- if end < len(s) {
- end++
- } else {
- end = len(s)
- }
+ end := stringLiteralEnd(s, i)
b.WriteString(escapeAssertionUnquoted(s[start:i]))
b.WriteString(s[i:end])
start = end
@@ -69,6 +58,16 @@ func EscapeAssertion(s string) string {
return b.String()
}
+// stringLiteralEnd returns the index just past the string literal that opens
at s[i].
+// A backslash is not an escape character in matchers (see
EscapeStringLiterals), so the
+// literal ends at the next matching quote, or at the end of s when it is not
closed.
+func stringLiteralEnd(s string, i int) int {
+ if j := strings.IndexByte(s[i+1:], s[i]); j >= 0 {
+ return i + j + 2
+ }
+ return len(s)
+}
+
func escapeAssertionUnquoted(s string) string {
return escapeAssertionRegex.ReplaceAllStringFunc(s, func(m string)
string {
// Replace only the last dot with underscore (preserve the
prefix character)
@@ -81,12 +80,17 @@ func escapeAssertionUnquoted(s string) string {
}
// RemoveComments removes the comments starting with # in the text.
+// A # inside a quoted string literal is part of the literal.
func RemoveComments(s string) string {
- pos := strings.Index(s, "#")
- if pos == -1 {
- return s
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ case '#':
+ return strings.TrimSpace(s[:i])
+ case '"', '\'':
+ i = stringLiteralEnd(s, i) - 1
+ }
}
- return strings.TrimSpace(s[0:pos])
+ return s
}
// ArrayEquals determines whether two string arrays are identical.
diff --git a/util/util_test.go b/util/util_test.go
index 4f81133..4580b2f 100644
--- a/util/util_test.go
+++ b/util/util_test.go
@@ -55,7 +55,7 @@ func TestEscapeAssertion(t *testing.T) {
testEscapeAssertion(t, `r.sub == "test.p.value"`, `r_sub ==
"test.p.value"`)
testEscapeAssertion(t, `r.obj == "https://p.example.com"`, `r_obj ==
"https://p.example.com"`)
testEscapeAssertion(t, `r.obj == 'https://p.example.com' && p.sub ==
r.sub`, `r_obj == 'https://p.example.com' && p_sub == r_sub`)
- testEscapeAssertion(t, `keyMatch(r.obj, "/p.*") || r.obj == "a\"/r.b"`,
`keyMatch(r_obj, "/p.*") || r_obj == "a\"/r.b"`)
+ testEscapeAssertion(t, `keyMatch(r.obj, "/p.*") || r.obj == "a\" &&
r.sub == p.sub`, `keyMatch(r_obj, "/p.*") || r_obj == "a\" && r_sub == p_sub`)
}
func testRemoveComments(t *testing.T, s string, res string) {
@@ -74,6 +74,10 @@ func TestRemoveComments(t *testing.T) {
testRemoveComments(t, "r.act == p.act###", "r.act == p.act")
testRemoveComments(t, "### comments", "")
testRemoveComments(t, "r.act == p.act", "r.act == p.act")
+ testRemoveComments(t, `r.act == "#write" && r.sub == p.sub`, `r.act ==
"#write" && r.sub == p.sub`)
+ testRemoveComments(t, `r.act == '#write' # it's a comment`, `r.act ==
'#write'`)
+ testRemoveComments(t, `r.act == "a\" # comments`, `r.act == "a\"`)
+ testRemoveComments(t, `r.act == "#write`, `r.act == "#write`)
}
func testArrayEquals(t *testing.T, a []string, b []string, res bool) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]