fix(tvix/eval): correctly escape ${ in strings

Without this escape, it is possible for Nix to produce escaped
representations which are not literal Nix values again.

This was fixed in upstream Nix in
https://github.com/NixOS/nix/pull/4012 (though only for eval, not in
the REPL) and the updated test is picked from upstream after that commit.

Because we run the C++ Nix tests against our test suite as well, this
also bumps our custom Nix 2.3 to a commit that includes the
cherry-picked fix from the PR above.

Change-Id: I478547ade65f655c606ec46f7143932064192283
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6271
Reviewed-by: grfn <grfn@gws.fyi>
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2022-08-25 14:23:26 +03:00 committed by tazjin
parent 265393301e
commit 39b01c3029
4 changed files with 19 additions and 14 deletions

View file

@ -90,13 +90,14 @@ impl NixString {
}
}
fn nix_escape_char(ch: char) -> Option<&'static str> {
match ch {
'\\' => Some("\\\\"),
'"' => Some("\\\""),
'\n' => Some("\\n"),
'\t' => Some("\\t"),
'\r' => Some("\\r"),
fn nix_escape_char(ch: char, next: Option<&char>) -> Option<&'static str> {
match (ch, next) {
('\\', _) => Some("\\\\"),
('"', _) => Some("\\\""),
('\n', _) => Some("\\n"),
('\t', _) => Some("\\t"),
('\r', _) => Some("\\r"),
('$', Some('{')) => Some("\\$"),
_ => None,
}
}
@ -106,14 +107,17 @@ fn nix_escape_char(ch: char) -> Option<&'static str> {
//
// Note that this does not add the outer pair of surrounding quotes.
fn nix_escape_string(input: &str) -> Cow<str> {
for (i, c) in input.chars().enumerate() {
if let Some(esc) = nix_escape_char(c) {
let mut iter = input.chars().enumerate().peekable();
while let Some((i, c)) = iter.next() {
if let Some(esc) = nix_escape_char(c, iter.peek().map(|(_, c)| c)) {
let mut escaped = String::with_capacity(input.len());
escaped.push_str(&input[..i]);
escaped.push_str(esc);
for c in input[i + 1..].chars() {
match nix_escape_char(c) {
let mut inner_iter = input[i + 1..].chars().peekable();
while let Some(c) = inner_iter.next() {
match nix_escape_char(c, inner_iter.peek()) {
Some(esc) => escaped.push_str(esc),
None => escaped.push(c),
}