hidingmyname opened a new issue, #276: URL: https://github.com/apache/casbin-cpp/issues/276
**Location** (apache/casbin-cpp @ default branch `0ad64a7`): - [`casbin/util/escape_assertion.cpp:34-51`](https://github.com/apache/casbin-cpp/blob/0ad64a739bd841099c6dbbae4a1f60d12e6cbc0a/casbin/util/escape_assertion.cpp#L34-L51) — `EscapeAssertion` - [`include/casbin/util/util.h:37-43`](https://github.com/apache/casbin-cpp/blob/0ad64a739bd841099c6dbbae4a1f60d12e6cbc0a/include/casbin/util/util.h#L37-L43) — public declaration ```cpp 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`](https://github.com/apache/casbin-cpp/blob/0ad64a739bd841099c6dbbae4a1f60d12e6cbc0a/casbin/enforcer.cpp#L120-L124)), 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** ```cpp #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.: ```text Expected equality of these values: result Which is: "r_sub == \"r_my_resource\"" "r_sub == \"r.my_resource\"" ``` Environment: - Commit: [`0ad64a7`](https://github.com/apache/casbin-cpp/commit/0ad64a739bd841099c6dbbae4a1f60d12e6cbc0a) (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: ```diff --- 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** Match only an `r`/`p` token prefix (optionally numbered) that is preceded by start-of-string, whitespace, `(`, `,` or an operator, as node-casbin does, so single- and double-quoted literals are never rewritten. Existing expectations in [`tests/util_test.cpp`](https://github.com/apache/casbin-cpp/blob/0ad64a739bd841099c6dbbae4a1f60d12e6cbc0a/tests/util_test.cpp#L29-L36) (e.g. `r.attr.value == p.attr` → `r_attr.value == p_attr`) stay as they are. If maintainers would rather not support literals in this helper, documenting the supported input in `util.h` would also resolve this. Not yet tested against this repository. 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. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
