Skip to content

Commit bd0c1a2

Browse files
committed
fix: escaping of slash #deploy_branch
1 parent 38fad26 commit bd0c1a2

File tree

5 files changed

+63
-25
lines changed

5 files changed

+63
-25
lines changed

src/linq/peg/grammar/odata.pegjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,15 +343,16 @@ HexDigit
343343
= [a-f] / [A-F] / [0-9]
344344

345345
StringLiteral
346-
= "\'" chars:(Escape / !['\\\n\r] . )* "\'"
347-
{ return { type: 'Literal', value: chars.map(l => l[0] == undefined ? l[1] : l[0] + l[1]).join('').replace(/\\(["'\\])|'(')/g, '$1$2') } }
346+
= "'" chars:(Escape / ![\\'\n\r] . )* "'"
347+
{ return { type: 'Literal', value: chars.map(l => l[0] || l[1]).join('').replace(/\\\\/g, '\\') } }
348348

349349
ArrayLiteral
350350
= LPAR __ elements:(first:Expression rest:(COMMA __ Expression)* { return buildList(first, rest, 2)})? (COMMA __)? __ RPAR __
351351
{ return { type: 'ArrayLiteral', elements: elements } }
352352

353353
Escape
354354
= "''"
355+
/ "\\"
355356
/ "\\" ([btnfr"'\\] / OctalEscape / UnicodeEscape)
356357

357358
OctalEscape

src/linq/peg/parser/odata-parser.js

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ function peg$parse(input, options) {
231231
var peg$r11 = /^[+\-]/;
232232
var peg$r12 = /^[pP]/;
233233
var peg$r13 = /^[0-9A-Fa-f]/;
234-
var peg$r14 = /^['\\\n\r]/;
234+
var peg$r14 = /^[\\'\n\r]/;
235235
var peg$r15 = /^[btnfr"'\\]/;
236236
var peg$r16 = /^[0-3]/;
237237

@@ -266,7 +266,7 @@ function peg$parse(input, options) {
266266
var peg$e28 = peg$classExpectation(["+", "-"], false, false);
267267
var peg$e29 = peg$classExpectation(["p", "P"], false, false);
268268
var peg$e30 = peg$classExpectation([["0", "9"], ["A", "F"], ["a", "f"]], false, false);
269-
var peg$e31 = peg$classExpectation(["'", "\\", "\n", "\r"], false, false);
269+
var peg$e31 = peg$classExpectation(["\\", "'", "\n", "\r"], false, false);
270270
var peg$e32 = peg$anyExpectation();
271271
var peg$e33 = peg$literalExpectation("''", false);
272272
var peg$e34 = peg$literalExpectation("\\", false);
@@ -438,7 +438,7 @@ function peg$parse(input, options) {
438438
var peg$f26 = function(value) { return { type: 'DateLiteral', value: flattenArray(value).join('') }; };
439439
var peg$f27 = function(first, value, last) { return { type: 'DateLiteral', value: flattenArray(value).join('') }; };
440440
var peg$f28 = function() { return { type: 'NumberLiteral', value: text() }; };
441-
var peg$f29 = function(chars) { return { type: 'Literal', value: chars.map(l => l[0] == undefined ? l[1] : l[0] + l[1]).join('').replace(/\\(["'\\])|'(')/g, '$1$2') } };
441+
var peg$f29 = function(chars) { return { type: 'Literal', value: chars.map(l => l[0] || l[1]).join('').replace(/\\\\/g, '\\') } };
442442
var peg$f30 = function(first, rest) { return buildList(first, rest, 2)};
443443
var peg$f31 = function(elements) { return { type: 'ArrayLiteral', elements: elements } };
444444
var peg$currPos = options.peg$currPos | 0;
@@ -4013,38 +4013,47 @@ function peg$parse(input, options) {
40134013
if (peg$silentFails === 0) { peg$fail(peg$e33); }
40144014
}
40154015
if (s0 === peg$FAILED) {
4016-
s0 = peg$currPos;
40174016
if (input.charCodeAt(peg$currPos) === 92) {
4018-
s1 = peg$c18;
4017+
s0 = peg$c18;
40194018
peg$currPos++;
40204019
} else {
4021-
s1 = peg$FAILED;
4020+
s0 = peg$FAILED;
40224021
if (peg$silentFails === 0) { peg$fail(peg$e34); }
40234022
}
4024-
if (s1 !== peg$FAILED) {
4025-
s2 = input.charAt(peg$currPos);
4026-
if (peg$r15.test(s2)) {
4023+
if (s0 === peg$FAILED) {
4024+
s0 = peg$currPos;
4025+
if (input.charCodeAt(peg$currPos) === 92) {
4026+
s1 = peg$c18;
40274027
peg$currPos++;
40284028
} else {
4029-
s2 = peg$FAILED;
4030-
if (peg$silentFails === 0) { peg$fail(peg$e35); }
4029+
s1 = peg$FAILED;
4030+
if (peg$silentFails === 0) { peg$fail(peg$e34); }
40314031
}
4032-
if (s2 === peg$FAILED) {
4033-
s2 = peg$parseOctalEscape();
4032+
if (s1 !== peg$FAILED) {
4033+
s2 = input.charAt(peg$currPos);
4034+
if (peg$r15.test(s2)) {
4035+
peg$currPos++;
4036+
} else {
4037+
s2 = peg$FAILED;
4038+
if (peg$silentFails === 0) { peg$fail(peg$e35); }
4039+
}
40344040
if (s2 === peg$FAILED) {
4035-
s2 = peg$parseUnicodeEscape();
4041+
s2 = peg$parseOctalEscape();
4042+
if (s2 === peg$FAILED) {
4043+
s2 = peg$parseUnicodeEscape();
4044+
}
4045+
}
4046+
if (s2 !== peg$FAILED) {
4047+
s1 = [s1, s2];
4048+
s0 = s1;
4049+
} else {
4050+
peg$currPos = s0;
4051+
s0 = peg$FAILED;
40364052
}
4037-
}
4038-
if (s2 !== peg$FAILED) {
4039-
s1 = [s1, s2];
4040-
s0 = s1;
40414053
} else {
40424054
peg$currPos = s0;
40434055
s0 = peg$FAILED;
40444056
}
4045-
} else {
4046-
peg$currPos = s0;
4047-
s0 = peg$FAILED;
40484057
}
40494058
}
40504059

src/linq/peg/translator/odatatranslator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export class ODataTranslator implements IExpressionVisitor<string> {
5151
return `${expression.value}`
5252

5353
case 'string':
54-
return `'${expression.value.replace(/'/g, '\'')}'`
54+
return `'${expression.value.replace(/'/g, '\'\'')}'`
5555

5656
case 'boolean':
5757
return expression.value ? 'true' : 'false'

src/test/odatavisitor.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ describe('When using OData for ExpressionVisitor', () => {
131131
assert.equal((<Expr.LiteralExpression>expr).value, true)
132132
})
133133

134-
135134
it('should evaluate a expression with date as string', () => {
136135
let reduced = reducer.parseOData('date ge datetime\'2017-05-01Z\''),
137136
expr = reducer.evaluate(reduced, vars)
@@ -254,6 +253,14 @@ describe('When using OData for ExpressionVisitor', () => {
254253
assert.ok((<Expr.ILiteralExpression>expr).value == false, 'Expected a literal of value true')
255254
})
256255

256+
it('should handle escaping of special character \\', () => {
257+
let reduced = reducer.parseOData(`contains(string, '\\\\')`),
258+
expr = reducer.evaluate(reduced, vars)
259+
260+
assert.ok(expr.type == Expr.ExpressionType.Literal, 'Expected a literal')
261+
assert.ok((<Expr.ILiteralExpression>expr).value == false, 'Expected a literal of value false')
262+
})
263+
257264
describe('for IN operator', () => {
258265
it('should evaluate in operator using constant with primary array where it is true', () => {
259266
let reduced = reducer.parseOData('13 in array'),

src/test/translator/odata.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,26 @@ describe('When using Translator', () => {
3636

3737
chai.expect(odata).to.equal(`(contains(customer/name, 'kalle') eq true) or (contains(customer/no, '54'))`)
3838
})
39+
40+
it('should handle escape of character \'', () => {
41+
let expr = visitor.parseOData(`contains(customer/name, 'ka''e')`)
42+
let odata = translator.visit(expr)
43+
44+
chai.expect(odata).to.equal(`contains(customer/name, 'ka''e')`)
45+
})
46+
47+
it('should handle escape of character \\', () => {
48+
let expr = visitor.parseOData(`contains(customer/name, 'ka\\e')`)
49+
let odata = translator.visit(expr)
50+
51+
chai.expect(odata).to.equal(`contains(customer/name, 'ka\\e')`)
52+
})
53+
54+
it('should handle unicode if that is a thing', () => {
55+
let expr = visitor.parseOData(`contains(customer/name, 'ka\\u1128')`)
56+
let odata = translator.visit(expr)
57+
58+
chai.expect(odata).to.equal(`contains(customer/name, 'ka\\u1128')`)
59+
})
3960
})
4061
})

0 commit comments

Comments
 (0)