From 39b01c302937fe3e91c15758b520c3ecc5379c7b Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Thu, 25 Aug 2022 14:23:26 +0300 Subject: 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 Reviewed-by: sterni Tested-by: BuildkiteCI --- tvix/eval/src/value/string.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'tvix/eval/src/value') diff --git a/tvix/eval/src/value/string.rs b/tvix/eval/src/value/string.rs index f352281be36a..51b0f0345457 100644 --- a/tvix/eval/src/value/string.rs +++ b/tvix/eval/src/value/string.rs @@ -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 { - 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), } -- cgit 1.4.1