From 7e784fc352b90339bb715095ff6faeeeddda94fd Mon Sep 17 00:00:00 2001 From: Tony HB Date: Tue, 14 Apr 2026 20:35:31 -0700 Subject: [PATCH 1/2] handle double backslash in lift --- lift.go | 27 +++++++++++++-------------- lift_test.go | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/lift.go b/lift.go index 3942a88..0079f16 100644 --- a/lift.go +++ b/lift.go @@ -12,18 +12,16 @@ const ( VarPrefix = "vars" ) -var ( - // replace is truly hack city. these are 20 variable names for values that are - // lifted out of expressions via liftLiterals. - replace = []string{ - "a", "b", "c", "d", "e", - "f", "g", "h", "i", "j", - "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", - "u", "v", "w", "x", "y", - "z", - } -) +// replace is truly hack city. these are 20 variable names for values that are +// lifted out of expressions via liftLiterals. +var replace = []string{ + "a", "b", "c", "d", "e", + "f", "g", "h", "i", "j", + "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", + "u", "v", "w", "x", "y", + "z", +} // LiftedArgs represents a set of variables that have been lifted from expressions and // replaced with identifiers, eg `id == "foo"` becomes `id == vars.a`, with "foo" lifted @@ -304,8 +302,9 @@ func (l *liftParser) consumeString(quoteChar byte) argMapValue { for l.idx < len(l.expr) { char := l.expr[l.idx] - if char == '\\' && l.peek() == quoteChar { - // If we're escaping the quote character, ignore it. + if char == '\\' && l.idx < len(l.expr) { + // Escape sequence: skip the backslash and whatever follows it. + // This correctly handles \\, \", \', \n, \t, etc. l.idx += 2 length += 2 continue diff --git a/lift_test.go b/lift_test.go index b6abfea..65d68fd 100644 --- a/lift_test.go +++ b/lift_test.go @@ -173,6 +173,23 @@ func TestLiftLiterals(t *testing.T) { "a": "test/yolo", }, }, + { + name: "escaped backslash before closing quote", + expr: `event.name == "foo\\"`, + expectedStr: "event.name == vars.a", + expectedArgs: map[string]any{ + "a": `foo\\`, + }, + }, + { + name: "escaped backslash before closing quote in compound expression", + expr: `async.data.branch.playgroundId == "oqdqzbuppgbtrljtpbmyi\\" && "team/mly6i259eym3jkyvq6txyciu/repo/qhq2ioy772sqo9sboe48kfwv" == async.data.spaceID`, + expectedStr: `async.data.branch.playgroundId == vars.a && vars.b == async.data.spaceID`, + expectedArgs: map[string]any{ + "a": `oqdqzbuppgbtrljtpbmyi\\`, + "b": "team/mly6i259eym3jkyvq6txyciu/repo/qhq2ioy772sqo9sboe48kfwv", + }, + }, } for _, test := range tests { From dd5e6db2f908a0e55f747f3af090d8f149e0dbcd Mon Sep 17 00:00:00 2001 From: Tony HB Date: Tue, 14 Apr 2026 20:44:29 -0700 Subject: [PATCH 2/2] handle single backslash gracefully --- lift.go | 14 ++++++++++---- lift_test.go | 8 ++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lift.go b/lift.go index 0079f16..2907d86 100644 --- a/lift.go +++ b/lift.go @@ -302,7 +302,7 @@ func (l *liftParser) consumeString(quoteChar byte) argMapValue { for l.idx < len(l.expr) { char := l.expr[l.idx] - if char == '\\' && l.idx < len(l.expr) { + if char == '\\' && l.idx+1 < len(l.expr) { // Escape sequence: skip the backslash and whatever follows it. // This correctly handles \\, \", \', \n, \t, etc. l.idx += 2 @@ -324,11 +324,17 @@ func (l *liftParser) consumeString(quoteChar byte) argMapValue { length++ } - // Should never happen: we should always find the ending string quote, as the - // expression should have already been validated. - panic(fmt.Sprintf("unable to parse quoted string: `%s` (offset %d)", l.expr, offset)) + // this is a grossly invalid expr, eg: `event.data.id == "foo\"` + // in this case, we can never parse this string. we always fix this by treating the last backslash + // as a \ literal, innit bruv + if length > 0 && offset+length <= len(l.expr) && l.expr[offset+length-1] == quoteChar { + length-- + } + + return argMapValue{offset: offset, length: length} } +// nolint:unused func (l *liftParser) peek() byte { if (l.idx + 1) >= len(l.expr) { return 0x0 diff --git a/lift_test.go b/lift_test.go index 65d68fd..79d4f95 100644 --- a/lift_test.go +++ b/lift_test.go @@ -190,6 +190,14 @@ func TestLiftLiterals(t *testing.T) { "b": "team/mly6i259eym3jkyvq6txyciu/repo/qhq2ioy772sqo9sboe48kfwv", }, }, + { + name: "trailing escaped quote treated as literal backslash", + expr: `event.data.id == "foo\"`, + expectedStr: `event.data.id == vars.a`, + expectedArgs: map[string]any{ + "a": `foo\`, + }, + }, } for _, test := range tests {