diff --git a/specfile/sanitizer.py b/specfile/sanitizer.py index 8f49c34..fad809c 100644 --- a/specfile/sanitizer.py +++ b/specfile/sanitizer.py @@ -345,6 +345,29 @@ def sanitize_shell_expansion(body: str) -> str: Sanitized shell expansion body, or %{nil} if sanitization is not possible. """ + def unescape_quoted_backslashes(s): + # process backslashes inside quoted shell string literals + try: + lex = shlex.shlex(s, posix=False) + lex.whitespace_split = True + lex.commenters = "" + parts = [] + for token in lex: + if ( + len(token) >= 2 + and token[0] in ("'", '"') + and token[-1] == token[0] + ): + inner = token[1:-1].replace("\\\\", "\\") + parts.append(token[0] + inner + token[0]) + else: + parts.append(token) + return " ".join(parts) + except ValueError: + return s + + body = unescape_quoted_backslashes(body) + def strip_quotes(s): s = s.strip() if len(s) >= 2 and s[0] in "'\"" and s[-1] == s[0]: @@ -1069,5 +1092,10 @@ def sanitize_nodes(nodes): i += 1 return "".join(result) - sanitized = sanitize_nodes(ValueParser.parse(value)) + try: + nodes = ValueParser.parse(value) + except Exception: + return "%{nil}", 0, 1 + else: + sanitized = sanitize_nodes(nodes) return sanitized, converted, removed diff --git a/tests/unit/test_sanitizer.py b/tests/unit/test_sanitizer.py index 20b41fe..4baa826 100644 --- a/tests/unit/test_sanitizer.py +++ b/tests/unit/test_sanitizer.py @@ -323,6 +323,10 @@ def test_pipe_to_tr(body, expected): "echo 1.2.3-rc4 | sed 's/-/~/g'", '%{lua:print((rpm.expand("%{quote:1.2.3-rc4}"):gsub("-", "~")))}', ), + ( + "echo %{version} | sed 's/\\\\./_/g'", + '%{lua:print((rpm.expand("%{version}"):gsub("%.", "_")))}', + ), ], ) def test_pipe_to_sed(body, expected): @@ -338,6 +342,15 @@ def test_chained_sed(): ) +def test_chained_sed_rpm_escaped(): + assert Sanitizer.sanitize_shell_expansion( + "echo '%canonical_project_name' | sed --regexp-extended 's:-:_:g;s:\\\\.:_:g'" + ) == ( + '%{lua:local v=(rpm.expand("%{canonical_project_name}"):gsub("-", "_"))' + ' print((v:gsub("%.", "_")))}' + ) + + @pytest.mark.parametrize( "body, expected", [