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-Casbin.NET.git


The following commit(s) were added to refs/heads/master by this push:
     new 771066d  fix: handle escaped quotes and brackets in literal in-lists
771066d is described below

commit 771066d0e94df76f84e10c7fffbf648c4b926151
Author: Yang Luo <[email protected]>
AuthorDate: Sun Oct 25 22:02:20 2026 +0800

    fix: handle escaped quotes and brackets in literal in-lists
---
 Casbin.UnitTests/ModelTests/EnforcerTest.cs  | 26 ++++++++++++++++++++++
 Casbin.UnitTests/UtilTests/StringUtilTest.cs | 18 ++++++++++++++++
 Casbin/Util/StringUtil.cs                    | 32 +++++++++++++++++-----------
 3 files changed, 63 insertions(+), 13 deletions(-)

diff --git a/Casbin.UnitTests/ModelTests/EnforcerTest.cs 
b/Casbin.UnitTests/ModelTests/EnforcerTest.cs
index 578c2c6..5ebdf69 100644
--- a/Casbin.UnitTests/ModelTests/EnforcerTest.cs
+++ b/Casbin.UnitTests/ModelTests/EnforcerTest.cs
@@ -1291,6 +1291,32 @@ public class EnforcerTest
 
     #endregion
 
+    [Fact]
+    public void TestInOperatorWithLiteralList()
+    {
+        string text =
+            "[request_definition]\n"
+            + "r = sub, obj, act\n"
+            + "\n"
+            + "[policy_definition]\n"
+            + "p = sub, obj, act\n"
+            + "\n"
+            + "[policy_effect]\n"
+            + "e = some(where (p.eft == allow))\n"
+            + "\n"
+            + "[matchers]\n"
+            + "m = r.sub == p.sub && r.obj in (\"a, \\\"b,c\\\"\", \"f(x)\") 
&& r.act in ['read', 'write']\n";
+
+        Enforcer e = new(DefaultModel.CreateFromText(text));
+        e.AddPolicy("alice", "any", "any");
+
+        Assert.True(e.Enforce("alice", "a, \"b,c\"", "read"));
+        Assert.True(e.Enforce("alice", "f(x)", "write"));
+        Assert.False(e.Enforce("alice", "a", "read"));
+        Assert.False(e.Enforce("alice", "f(x)", "delete"));
+        Assert.False(e.Enforce("bob", "f(x)", "read"));
+    }
+
 #if !NET452
     #region ExpressionHandler Tests
 
diff --git a/Casbin.UnitTests/UtilTests/StringUtilTest.cs 
b/Casbin.UnitTests/UtilTests/StringUtilTest.cs
index bd5daf7..1a697c1 100644
--- a/Casbin.UnitTests/UtilTests/StringUtilTest.cs
+++ b/Casbin.UnitTests/UtilTests/StringUtilTest.cs
@@ -61,6 +61,24 @@ public class StringUtilTest
         ]
     ];
 
+    public static IEnumerable<object[]> ReplaceInListOperatorTestData =
+    [
+        ["r.obj in (\"a\", \"b\")", "(r.obj == \"a\" || r.obj == \"b\")"],
+        ["r.obj in [\"a\", \"b\"]", "(r.obj == \"a\" || r.obj == \"b\")"],
+        ["r.obj in (\"a\")", "(r.obj == \"a\")"],
+        ["r.obj in (\"a,b\", \"c\")", "(r.obj == \"a,b\" || r.obj == \"c\")"],
+        ["r.obj in (\"a, \\\"b,c\\\"\", \"d\")", "(r.obj == \"a, \\\"b,c\\\"\" 
|| r.obj == \"d\")"],
+        ["r.obj in (\"a\\\\\", \"b\")", "(r.obj == \"a\\\\\" || r.obj == 
\"b\")"],
+        ["r.obj in (\"f(x)\", \"[y]\")", "(r.obj == \"f(x)\" || r.obj == 
\"[y]\")"],
+        ["r.obj in [\"f(x)\", \"[y]\"]", "(r.obj == \"f(x)\" || r.obj == 
\"[y]\")"],
+        ["r.obj in (p.obj)", "r.obj in (p.obj)"]
+    ];
+
+    [Theory]
+    [MemberData(nameof(ReplaceInListOperatorTestData))]
+    public void TestReplaceInListOperator(string expressionString, string 
expected) =>
+        Assert.Equal(expected, 
StringUtil.ReplaceInListOperator(expressionString));
+
     [Theory]
     [MemberData(nameof(RemoveCommentsTestData))]
     public void TestRemoveComments(string except, string actual) => 
Assert.Equal(except, actual.RemoveComments());
diff --git a/Casbin/Util/StringUtil.cs b/Casbin/Util/StringUtil.cs
index c6cbd73..d82dda1 100644
--- a/Casbin/Util/StringUtil.cs
+++ b/Casbin/Util/StringUtil.cs
@@ -9,7 +9,7 @@ namespace Casbin.Util
     {
         private static readonly Regex s_evalRegex = 
new(@"\beval\((?<rule>[^)]*)\)");
         private static readonly Regex s_inOperatorRegex = 
new(@"([a-zA-Z0-9_.()""]+) in ([a-zA-Z0-9_.()""]+)");
-        private static readonly Regex s_inListOperatorRegex = 
new(@"([a-zA-Z0-9_.]+)\s+in\s+(?:\(([^()]*)\)|\[([^\[\]]*)\])");
+        private static readonly Regex s_inListOperatorRegex = 
new(@"([a-zA-Z0-9_.]+)\s+in\s+(?:\(((?:""(?:[^""\\]|\\.)*""|[^()""])*)\)|\[((?:""(?:[^""\\]|\\.)*""|[^\[\]""])*)\])");
 
         /// <summary>
         /// Removes the comments starting with # in the text.
@@ -138,28 +138,34 @@ namespace Casbin.Util
             List<string> values = new();
             int start = 0;
             bool inQuotes = false;
-            for (int i = 0; i <= list.Length; i++)
+            for (int i = 0; i < list.Length; i++)
             {
-                if (i < list.Length && list[i] is '"')
+                switch (list[i])
                 {
-                    inQuotes = !inQuotes;
+                    case '\\' when inQuotes:
+                        i++;
+                        break;
+                    case '"':
+                        inQuotes = !inQuotes;
+                        break;
+                    case ',' when inQuotes is false:
+                        AddValue(list.Substring(start, i - start));
+                        start = i + 1;
+                        break;
                 }
+            }
 
-                if (i < list.Length && (inQuotes || list[i] is not ','))
-                {
-                    continue;
-                }
+            AddValue(list.Substring(start));
+            return values;
 
-                string value = list.Substring(start, i - start).Trim();
+            void AddValue(string value)
+            {
+                value = value.Trim();
                 if (value.Length is not 0)
                 {
                     values.Add(value);
                 }
-
-                start = i + 1;
             }
-
-            return values;
         }
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to