`EscapeAssertion` rewrites dots inside quoted string literals
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 1/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 88/100
Direzione di ricerca
Inizia da casbin/util/escape_assertion.cpp, in EscapeAssertion, e confrontalo con la dichiarazione in include/casbin/util/util.h. Aggiungi i casi dei letterali tra virgolette a TestEscapeAssertion in tests/util_test.cpp, quindi esegui quel binario di test per vedere l'errore corrente. Fatto significa che i letterali tra virgolette singole e doppie mantengono i punti, mentre i prefissi r/p delle asserzioni vengono ancora riscritti.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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.
- Lingua principale
- C++
- Stelle
- 257
- Fork
- 67
- Merge medio
- 10h 23m
- PR unite (30g)
- 1
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di apache/casbin-cpp
-
ci: automated PR performance benchmark comparisonForse di nuovo libera @knyk-dev l’ha presa 298 giorni fa e non c’è nessuna pull request aperta. Apertaenhancement
apache/casbin-cpp#266 · 1 assegnatario ·
-
Porting Casbin-CPP to Real-Time Operating Systems (RTOS)Forse di nuovo libera @hsluoyz l’ha presa 1277 giorni fa e non c’è nessuna pull request aperta. Apertaenhancement
apache/casbin-cpp#229 · 1 commento · 1 assegnatario ·
-
Use the same performance benchmark tests for all languagesForse di nuovo libera @hsluoyz l’ha presa 1576 giorni fa e non c’è nessuna pull request aperta. Apertaenhancement
apache/casbin-cpp#207 · 3 commenti · 1 assegnatario ·
-
Benchmarking pycasbin's performance to compare it with Python's casbin implementationForse di nuovo libera @hsluoyz l’ha presa 1748 giorni fa e non c’è nessuna pull request aperta. Apertaenhancement
apache/casbin-cpp#181 · 1 commento · 1 assegnatario ·
-
Make an authorization middleware for Envoy based on CasbinForse di nuovo libera @hsluoyz l’ha presa 1951 giorni fa e non c’è nessuna pull request aperta. Apertaenhancement
apache/casbin-cpp#115 · 1 commento · 1 assegnatario ·
Tutte le issue di apache/casbin-cpp
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
colbymchenry/codegraph#3223 ·
I maintainer di solito rispondono entro 1 giorno
-
The `ufw` decoder requires the kernel's monotonic timestamp, so UFW lines read from journald decode without any fieldForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
arduino/ArduinoCore-megaavr#152 ·
-
bug needs-triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
microsoft/microsoft-ui-xaml#12515 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 2 giorni
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
BlueQuartzSoftware/simplnx#1906 ·
I maintainer di solito rispondono entro 4 giorni