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]