You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@casbin.apache.org by hs...@apache.org on 2026/10/20 14:30:15 UTC

(casbin-node-casbin) branch master updated: fix: protect single-quoted literals in escapeAssertion

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-node-casbin.git


The following commit(s) were added to refs/heads/master by this push:
     new 18d8696  fix: protect single-quoted literals in escapeAssertion
18d8696 is described below

commit 18d8696faa491161516c88f3b576d433907e0859
Author: Yang Luo <hs...@gmail.com>
AuthorDate: Tue Oct 20 22:30:04 2026 +0800

    fix: protect single-quoted literals in escapeAssertion
---
 src/model/model.ts | 10 ----------
 src/util/util.ts   | 24 ++++++++++++++++++++++++
 test/util.test.ts  |  4 ++++
 3 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/src/model/model.ts b/src/model/model.ts
index 49a7cd1..d31666d 100644
--- a/src/model/model.ts
+++ b/src/model/model.ts
@@ -96,18 +96,8 @@ export class Model {
 
       ast.tokens = tokens;
     } else if (sec === 'm') {
-      const stringArguments = value.match(/\"(.*?)\"/g) || [];
-
-      stringArguments.forEach((n, index) => {
-        value = value.replace(n, `$<${index}>`);
-      });
-
       value = util.escapeAssertion(value);
 
-      stringArguments.forEach((n, index) => {
-        value = value.replace(`$<${index}>`, n);
-      });
-
       const invalidOperators = /(?<![&|])&(?!&)|(?<![&|])\|(?!\|)|&{3,}|\|{3,}/g;
       if (invalidOperators.test(value)) {
         throw new Error(`Invalid operator in matcher`);
diff --git a/src/util/util.ts b/src/util/util.ts
index 8ceead7..1646107 100644
--- a/src/util/util.ts
+++ b/src/util/util.ts
@@ -19,7 +19,31 @@ import { mustGetDefaultFileSystem } from '../persist';
 
 const escapeAssertionReg = new RegExp(/([()\s|&,=!><+\-*/]|^)((r|p)[0-9]*)\./g);
 
+// Quoted string literals are kept as they are.
 function escapeAssertion(s: string): string {
+  let res = '';
+  let start = 0;
+  for (let i = 0; i < s.length; i++) {
+    const quote = s[i];
+    if (quote !== '"' && quote !== "'") {
+      continue;
+    }
+    let end = i + 1;
+    while (end < s.length && s[end] !== quote) {
+      if (s[end] === '\\') {
+        end++;
+      }
+      end++;
+    }
+    end = Math.min(end + 1, s.length);
+    res += escapeAssertionUnquoted(s.slice(start, i)) + s.slice(i, end);
+    start = end;
+    i = end - 1;
+  }
+  return res + escapeAssertionUnquoted(s.slice(start));
+}
+
+function escapeAssertionUnquoted(s: string): string {
   s = s.replace(escapeAssertionReg, (match) => {
     // Replace only the last dot with underscore (preserve the prefix character)
     const lastDotIdx = match.lastIndexOf('.');
diff --git a/test/util.test.ts b/test/util.test.ts
index 44cdcc2..d5b9509 100644
--- a/test/util.test.ts
+++ b/test/util.test.ts
@@ -367,4 +367,8 @@ test('test escapeAssertion', () => {
   // 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"');
+  expect(util.escapeAssertion(`r.obj == 'https://p.example.com' && p.sub == r.sub`)).toEqual(
+    `r_obj == 'https://p.example.com' && p_sub == r_sub`
+  );
+  expect(util.escapeAssertion(`r.obj == "a\\"/p.b" || p.obj == '/r.c'`)).toEqual(`r_obj == "a\\"/p.b" || p_obj == '/r.c'`);
 });


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@casbin.apache.org
For additional commands, e-mail: commits-help@casbin.apache.org