`EscapeAssertion` rewrites dots inside quoted string literals
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 1/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 88/100
Línea de trabajo
Comienza en casbin/util/escape_assertion.cpp, en EscapeAssertion, y compáralo con la declaración en include/casbin/util/util.h. Añade los casos de literales entrecomillados a TestEscapeAssertion en tests/util_test.cpp y luego ejecuta ese binario de prueba para ver el fallo actual. Hecho significa que los literales con comillas simples y dobles conservan sus puntos, mientras que los prefijos r/p de las aserciones siguen reescribiéndose.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Location (apache/casbin-cpp @ default branch 0ad64a7):
casbin/util/escape_assertion.cpp:34-51—EscapeAssertioninclude/casbin/util/util.h:37-43— public declaration
std::string EscapeAssertion(std::string s) {
std::regex regex_s("[a-zA-Z0-9. ]+");
std::sregex_iterator words_begin = std::sregex_iterator(s.begin(), s.end(), regex_s);
auto words_end = std::sregex_iterator();
for (std::sregex_iterator k = words_begin; k != words_end; ++k) {
std::smatch match = *k;
std::string match_str = match.str();
int pos = static_cast<int>(match_str.find("."));
if (pos != -1) {
std::string new_str = match_str.replace(pos, 1, "_");
s = s.replace(match.position(), match.str().length(), new_str);
}
}
return s;
}
Problem
casbin::EscapeAssertion replaces the first . in every [a-zA-Z0-9. ]+ run, including runs inside quoted string literals. The regex doesn't know about quotes: because " and ' are not in the class, the text between the quotes becomes its own match, and its first dot gets replaced.
| input | result | expected |
|---|---|---|
r.sub == "r.my_resource" |
r_sub == "r_my_resource" |
r_sub == "r.my_resource" |
p.obj == 'p.resource' |
p_obj == 'p_resource' |
p_obj == 'p.resource' |
p.sub == p.obj && p.obj == "r.something" |
p_sub == p_obj && p_obj == "r_something" |
p_sub == p_obj && p_obj == "r.something" |
p.obj == "r.my_resource" |
p_obj == "r_my_resource" |
p_obj == "r.my_resource" |
Scope: EscapeAssertion is declared in the public header include/casbin/util/util.h (included by casbin/casbin.h), documented as taking "the value of the matcher and effect assertions". In the current Enforce path it is only applied to the argument names of eval(...) (casbin/enforcer.cpp:120-124), so this is not an enforcement failure: Enforce results are not affected. This is about the result of the public utility for code that calls it directly (model tooling, converters).
Reproduction
#include <iostream>
#include <casbin/casbin.h>
int main() {
std::cout << casbin::EscapeAssertion("r.sub == \"r.my_resource\"") << "\n";
std::cout << casbin::EscapeAssertion("p.obj == 'p.resource'") << "\n";
}
The "result" column above is verbatim gtest output, e.g.:
Expected equality of these values:
result
Which is: "r_sub == \"r_my_resource\""
"r_sub == \"r.my_resource\""
Environment:
- Commit:
0ad64a7(master) - Toolchain: GCC 11.5.0
Already fixed in apache/casbin-node-casbin
PR: https://github.com/apache/casbin-node-casbin/pull/514 — "feat: fix escapeAssertion to preserve string literals" (merged 2025-11-11T17:50:46Z, merge commit 9abab0fb8b5). node-casbin's escapeAssertion also rewrote r./p. inside string literals ("r.my_resource" became "r_my_resource"). It now only rewrites an r/p (optionally numbered) prefix that is preceded by start-of-string, whitespace, a parenthesis or an operator character; since quotes are not in that set, text inside literals is left alone. In node-casbin the helper is on the enforcement path, so the bug there also changed Enforce results; in casbin-cpp only the public helper's output is affected.
How node-casbin fixed it:
--- a/src/util/util.ts
+++ b/src/util/util.ts
@@ -17,11 +17,16 @@
import { mustGetDefaultFileSystem } from '../persist';
-const escapeAssertionReg = new RegExp(/(^|[^A-Za-z0-9_])([rp])[0-9]*\./g);
+const escapeAssertionReg = new RegExp(/([()\s|&,=!><+\-*/]|^)((r|p)[0-9]*)\./g);
function escapeAssertion(s: string): string {
- s = s.replace(escapeAssertionReg, (match, p1, p2) => {
- return p1 + p2 + match.substring(p1.length + p2.length).replace('.', '_');
+ s = s.replace(escapeAssertionReg, (match) => {
+ // Replace only the last dot with underscore (preserve the prefix character)
+ const lastDotIdx = match.lastIndexOf('.');
+ if (lastDotIdx > 0) {
+ return match.substring(0, lastDotIdx) + '_';
+ }
+ return match;
});
return s;
}
--- a/test/util.test.ts
+++ b/test/util.test.ts
@@ -228,3 +228,27 @@ test('bracketCompatible', () => {
...
+test('test escapeAssertion', () => {
+ expect(util.escapeAssertion('r_sub == r_obj.value')).toEqual('r_sub == r_obj.value');
+ expect(util.escapeAssertion('p_sub == r_sub.value')).toEqual('p_sub == r_sub.value');
+ expect(util.escapeAssertion('r.attr.value == p.attr')).toEqual('r_attr.value == p_attr');
...
+ // Test that patterns inside strings are not escaped
+ expect(util.escapeAssertion('r.sub == "a.p.p.l.e"')).toEqual('r_sub == "a.p.p.l.e"');
+ expect(util.escapeAssertion('r.sub == "test.p.value"')).toEqual('r_sub == "test.p.value"');
+});
Suggested fix
Only rewrite an r/p token (optionally numbered) that follows start-of-string, whitespace, a parenthesis, a comma or an operator, as node-casbin now does, so dots inside single- or double-quoted literals are left alone:
diff --git a/casbin/util/escape_assertion.cpp b/casbin/util/escape_assertion.cpp
--- a/casbin/util/escape_assertion.cpp
+++ b/casbin/util/escape_assertion.cpp
@@ -32,22 +32,10 @@ namespace casbin {
* @return the escaped value.
*/
std::string EscapeAssertion(std::string s) {
- std::regex regex_s("[a-zA-Z0-9. ]+");
-
- std::sregex_iterator words_begin = std::sregex_iterator(s.begin(), s.end(), regex_s);
- auto words_end = std::sregex_iterator();
-
- for (std::sregex_iterator k = words_begin; k != words_end; ++k) {
- std::smatch match = *k;
- std::string match_str = match.str();
- int pos = static_cast<int>(match_str.find("."));
- if (pos != -1) {
- std::string new_str = match_str.replace(pos, 1, "_");
- s = s.replace(match.position(), match.str().length(), new_str);
- }
- }
-
- return s;
+ // Only an r/p token (e.g. r., p., r2.) that follows start-of-string, whitespace, a parenthesis, a comma or an
+ // operator is escaped, so dots inside quoted string literals are left alone.
+ static const std::regex regex_s("([()\\s|&,=!><+\\-*/]|^)([rp][0-9]*)\\.");
+ return std::regex_replace(s, regex_s, "$1$2_");
}
} // namespace casbin
Verified on 0ad64a7 (GCC 11.4.0, GoogleTest 1.14.0): with the first three rows of the table above added to TestEscapeAssertion in tests/util_test.cpp, the test fails without this patch and passes with it; no other test results change, including the existing TestEscapeAssertion cases.
I can submit a PR for this.
Related: searched issues/PRs (all states) for EscapeAssertion, escape + string literal, eval rule + dot: no hits. The underscore-in-identifier case has a different cause and is reported separately.
- Lenguaje dominante
- C++
- Estrellas
- 257
- Forks
- 67
- Merge medio
- 10 h 23 min
- PR fusionados (30 d)
- 1
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de apache/casbin-cpp
-
ci: automated PR performance benchmark comparisonQuizá libre de nuevo @knyk-dev la tomó hace 296 días y no hay ningún pull request abierto. Abiertoenhancement
apache/casbin-cpp#266 · 1 asignado ·
-
Porting Casbin-CPP to Real-Time Operating Systems (RTOS)Quizá libre de nuevo @hsluoyz la tomó hace 1275 días y no hay ningún pull request abierto. Abiertoenhancement
apache/casbin-cpp#229 · 1 comentario · 1 asignado ·
-
Use the same performance benchmark tests for all languagesQuizá libre de nuevo @hsluoyz la tomó hace 1573 días y no hay ningún pull request abierto. Abiertoenhancement
apache/casbin-cpp#207 · 3 comentarios · 1 asignado ·
-
Benchmarking pycasbin's performance to compare it with Python's casbin implementationQuizá libre de nuevo @hsluoyz la tomó hace 1745 días y no hay ningún pull request abierto. Abiertoenhancement
apache/casbin-cpp#181 · 1 comentario · 1 asignado ·
-
Make an authorization middleware for Envoy based on CasbinQuizá libre de nuevo @hsluoyz la tomó hace 1949 días y no hay ningún pull request abierto. Abiertoenhancement
apache/casbin-cpp#115 · 1 comentario · 1 asignado ·
Todos los issues de apache/casbin-cpp
Issues similares
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 85/100
objectionary/game-of-life#74 ·
-
bug fmt low priority
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
qbittorrent/qBittorrent#25176 ·
Los mantenedores suelen responder en 2 días
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 85/100
lxqt/lxqt-powermanagement#500 · 1 comentario ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
scp-fs2open/fs2open.github.com#7965 ·
Los mantenedores suelen responder en 1 día