Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
20 changes: 20 additions & 0 deletions test/enforcer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,26 @@ test('test ABAC single eval() with r. in unexpected places', async () => {
await testEnforce(e, { id: 3 }, ({ owner: { id: 2 } } as unknown) as string, 'read', false);
});

test('test escapeAssertion with string literals (issue)', async () => {
// Test case from GitHub issue: escapeAssertion should not replace r./p. inside string literals
const MY_RESOURCE_NAME = 'r.my_resource';
const model = newModel();
model.addDef('r', 'r', 'act, obj');
model.addDef('p', 'p', 'act, obj, rule');
model.addDef('e', 'e', 'some(where (p.eft == allow))');
model.addDef('m', 'm', 'r.act == p.act && r.obj == p.obj && eval(p.rule)');

const enforcer = await newEnforcer(model);
enforcer.addPolicy('alice', MY_RESOURCE_NAME, `p.obj == "${MY_RESOURCE_NAME}"`);

// Should work because string literals are not escaped
await expect(enforcer.enforce('alice', MY_RESOURCE_NAME)).resolves.toBe(true);

// Test with single quotes as well
enforcer.addPolicy('bob', 'p.resource', `p.obj == 'p.resource'`);
await expect(enforcer.enforce('bob', 'p.resource')).resolves.toBe(true);
});

test('TestEnforceSync', async () => {
const m = newModel();
m.addDef('r', 'r', 'sub, obj, act');
Expand Down
24 changes: 24 additions & 0 deletions test/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,27 @@ test('bracketCompatible', () => {
)
).toEqual("g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act || r.obj in ['data2', 'data3'] || r.obj in ['data4', 'data5']");
});

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');
expect(util.escapeAssertion('r.attr.value == p.attr')).toEqual('r_attr.value == p_attr');
expect(util.escapeAssertion('r.attp.value || p.attr')).toEqual('r_attp.value || p_attr');
expect(util.escapeAssertion('r2.attr.value == p2.attr')).toEqual('r2_attr.value == p2_attr');
expect(util.escapeAssertion('r2.attp.value || p2.attr')).toEqual('r2_attp.value || p2_attr');
expect(util.escapeAssertion('r.attp.value &&p.attr')).toEqual('r_attp.value &&p_attr');
expect(util.escapeAssertion('r.attp.value >p.attr')).toEqual('r_attp.value >p_attr');
expect(util.escapeAssertion('r.attp.value <p.attr')).toEqual('r_attp.value <p_attr');
expect(util.escapeAssertion('r.attp.value +p.attr')).toEqual('r_attp.value +p_attr');
expect(util.escapeAssertion('r.attp.value -p.attr')).toEqual('r_attp.value -p_attr');
expect(util.escapeAssertion('r.attp.value *p.attr')).toEqual('r_attp.value *p_attr');
expect(util.escapeAssertion('r.attp.value /p.attr')).toEqual('r_attp.value /p_attr');
expect(util.escapeAssertion('!r.attp.value /p.attr')).toEqual('!r_attp.value /p_attr');
expect(util.escapeAssertion('g(r.sub, p.sub) == p.attr')).toEqual('g(r_sub, p_sub) == p_attr');
expect(util.escapeAssertion('g(r.sub,p.sub) == p.attr')).toEqual('g(r_sub,p_sub) == p_attr');
expect(util.escapeAssertion('(r.attp.value || p.attr)p.u')).toEqual('(r_attp.value || p_attr)p_u');
// 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"');
});