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-cpp.git


The following commit(s) were added to refs/heads/master by this push:
     new 048542b  fix: unquote double-quoted fields when loading CSV policy 
lines
048542b is described below

commit 048542b0d1f9e033578d7ae81e89c2f1524abd93
Author: Yang Luo <[email protected]>
AuthorDate: Sat Oct 31 09:17:14 2026 +0800

    fix: unquote double-quoted fields when loading CSV policy lines
---
 casbin/persist/adapter.cpp                         | 36 ++++++++++++++++++++--
 casbin/persist/adapter.h                           |  3 ++
 .../persist/file_adapter/filtered_file_adapter.cpp |  4 +--
 include/casbin/persist/adapter.h                   |  3 ++
 tests/enforcer_test.cpp                            | 12 ++++++++
 5 files changed, 53 insertions(+), 5 deletions(-)

diff --git a/casbin/persist/adapter.cpp b/casbin/persist/adapter.cpp
index 5e5f304..4b0c9b2 100644
--- a/casbin/persist/adapter.cpp
+++ b/casbin/persist/adapter.cpp
@@ -16,6 +16,8 @@
 
 #include "casbin/pch.h"
 
+#include <cctype>
+
 #ifndef ADAPTER_CPP
 #define ADAPTER_CPP
 
@@ -24,14 +26,42 @@
 
 namespace casbin {
 
+// SplitPolicyLine splits a CSV policy line into fields. Unquoted fields are 
trimmed, a field wrapped in double quotes
+// may contain commas and keeps its inner whitespace, and "" inside it stands 
for one double quote.
+std::vector<std::string> SplitPolicyLine(const std::string& line) {
+    std::vector<std::string> tokens;
+    std::string token;
+    bool in_quotes = false;
+    bool quoted = false;
+    for (size_t i = 0; i < line.size(); i++) {
+        char c = line[i];
+        if (in_quotes) {
+            if (c != '"')
+                token += c;
+            else if (i + 1 < line.size() && line[i + 1] == '"')
+                token += line[++i];
+            else
+                in_quotes = false;
+        } else if (c == ',') {
+            tokens.push_back(quoted ? token : Trim(token));
+            token.clear();
+            quoted = false;
+        } else if (c == '"' && !quoted && Trim(token).empty()) {
+            in_quotes = quoted = true;
+        } else if (!quoted || !std::isspace(static_cast<unsigned char>(c))) {
+            token += c;
+        }
+    }
+    tokens.push_back(quoted ? token : Trim(token));
+    return tokens;
+}
+
 // LoadPolicyLine loads a text line as a policy rule to model.
 void LoadPolicyLine(const std::string& line, const std::shared_ptr<Model>& 
model) {
     if (line.empty() || line.find('#') == 0)
         return;
 
-    std::vector<std::string> tokens = Split(line, ",", -1);
-    for (int i = 0; i < tokens.size(); i++)
-        tokens[i] = Trim(tokens[i]);
+    std::vector<std::string> tokens = SplitPolicyLine(line);
 
     std::string key = tokens[0];
     std::string sec = key.substr(0, 1);
diff --git a/casbin/persist/adapter.h b/casbin/persist/adapter.h
index cfba6eb..9e7ec03 100644
--- a/casbin/persist/adapter.h
+++ b/casbin/persist/adapter.h
@@ -24,6 +24,9 @@
 
 namespace casbin {
 
+// SplitPolicyLine splits a CSV policy line into fields, unquoting 
double-quoted fields.
+std::vector<std::string> SplitPolicyLine(const std::string& line);
+
 // LoadPolicyLine loads a text line as a policy rule to model.
 void LoadPolicyLine(const std::string& line, const std::shared_ptr<Model>& 
model);
 
diff --git a/casbin/persist/file_adapter/filtered_file_adapter.cpp 
b/casbin/persist/file_adapter/filtered_file_adapter.cpp
index 3960390..011cf80 100644
--- a/casbin/persist/file_adapter/filtered_file_adapter.cpp
+++ b/casbin/persist/file_adapter/filtered_file_adapter.cpp
@@ -16,7 +16,7 @@ bool FilteredFileAdapter ::filterLine(std::string line, 
Filter* filter) {
     if (filter == NULL)
         return false;
 
-    std::vector<std::string> p = Split(line, ",");
+    std::vector<std::string> p = SplitPolicyLine(line);
     if (p.size() == 0)
         return true;
 
@@ -34,7 +34,7 @@ bool FilteredFileAdapter 
::filterWords(std::vector<std::string> line, std::vecto
     if (line.size() < filter.size() + 1)
         return true;
 
-    bool skip_line;
+    bool skip_line = false;
     for (int i = 0; i < filter.size(); i++) {
         if (filter[i].length() > 0 && Trim(filter[i]) != Trim(line[i + 1])) {
             skip_line = true;
diff --git a/include/casbin/persist/adapter.h b/include/casbin/persist/adapter.h
index 390577c..820f3a1 100644
--- a/include/casbin/persist/adapter.h
+++ b/include/casbin/persist/adapter.h
@@ -24,6 +24,9 @@
 
 namespace casbin {
 
+// SplitPolicyLine splits a CSV policy line into fields, unquoting 
double-quoted fields.
+std::vector<std::string> SplitPolicyLine(const std::string& line);
+
 // LoadPolicyLine loads a text line as a policy rule to model.
 void LoadPolicyLine(const std::string& line, const std::shared_ptr<Model>& 
model);
 
diff --git a/tests/enforcer_test.cpp b/tests/enforcer_test.cpp
index 8a4e0a5..acb1061 100644
--- a/tests/enforcer_test.cpp
+++ b/tests/enforcer_test.cpp
@@ -17,6 +17,7 @@
  */
 
 #include <casbin/casbin.h>
+#include <casbin/persist/string_adapter.h>
 #include <gtest/gtest.h>
 
 #include "config_path.h"
@@ -114,6 +115,17 @@ TEST(TestEnforcer, TestThreeParams) {
     ASSERT_EQ(e.Enforce({"bob", "data2", "write"}), true);
 }
 
+TEST(TestEnforcer, TestQuotedPolicy) {
+    auto adapter = std::make_shared<casbin::StringAdapter>("p, admin, /, 
GET\np,    \"admin\",  /,  POST\n\"p\", \"admin\", \"/a,b\", PUT\np, \"say 
\"\"hi\"\"\", /, GET");
+    casbin::Enforcer e(basic_model_path, adapter);
+
+    ASSERT_EQ(e.Enforce({"admin", "/", "GET"}), true);
+    ASSERT_EQ(e.Enforce({"admin", "/", "POST"}), true);
+    ASSERT_EQ(e.Enforce({"admin", "/a,b", "PUT"}), true);
+    ASSERT_EQ(e.Enforce({"say \"hi\"", "/", "GET"}), true);
+    ASSERT_EQ(e.Enforce({"\"admin\"", "/", "POST"}), false);
+}
+
 TEST(TestEnforcer, TestVectorParams) {
     casbin::Enforcer e(basic_model_without_spaces_path, basic_policy_path);
 


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

Reply via email to