-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Short description
After some investigation, it looks like after #7172, Ref.String() in ast/term.go (around line 1124) stopped using p.String() (which produces a JSON-escaped literal, it uses strconv.Quote(string(str)) under the covers) and instead writes the raw contents of the String term.
As a result, sequences like "\t" in a reference key now become an actual tab (ASCII 9) instead of the two‐byte escape (\ + t, ASCII 92,116).
Why this is a problem
The release notes don’t mention this change, so it appears to be an unintended regression. Existing code that depended on Ref.String() producing a properly escaped Go/JSON identifier will now receive raw control characters (e.g. ASCII 9 for \t), breaking downstream consumers that expect escape sequences.
Note: I think this also applies to \n or other escaped keys, which supports multiline rules, etc...
Details:
- OPA version: v1.0.0
- Example query, input, data, and policy that OPA was given: sample code in "Steps To Reproduce"
- Example output that OPA returned: sample code and output in "Steps To Reproduce"
Steps To Reproduce
// Before PR #7172 (OPA v0.68.0)
ref := ast.MustParseRef(`data.policy["ma\tin"]`)
fmt.Printf("%v\n", []byte(ref.String()))
// → [100 97 116 97 46 112 111 108 105 99 121 91 34 109 **97 92 116** 105 110 34 93]
// After PR #7172 (OPA v1.0.0)
ref := ast.MustParseRef(`data.policy["ma\tin"]`)
fmt.Printf("%v\n", []byte(ref.String()))
// → [100 97 116 97 46 112 111 108 105 99 121 91 34 109 **97 9** 105 110 34 93]
More details
This problem surfaced when I called:
ast.ParseBodyWithOpts(ref.String(), ast.ParserOptions{SkipRules: true})
The parser (see
Line 1651 in c99bf28
| p.errorf(p.s.Loc(), "illegal string literal: %s", p.s.lit) |
parsing rego query: 1 error occurred:
1:13: rego_parse_error: illegal string literal: "ma in"
data.policy["ma in"]
Rather than changing Ref.String()—which has been released for some time, maybe we can update func (p *Parser) parseString() *Term so it accepts both escaped (\t) and raw control characters in string literals.