From cfcaa85b81233d364defb1dc2afdd236dbd55059 Mon Sep 17 00:00:00 2001
From: Dominik Rumian <drumian@redhat.com>
Date: Feb 08 2022 10:37:54 +0000
Subject: Fix: Extra arguments now use shell-escaping


Fixes splitting extra arguments on whitespaces. Extra
arguments are converted to their shell-escaped
version.

JIRA: RHELCMP-7350
Fixes: https://pagure.io/rpkg/issue/587

Signed-off-by: Dominik Rumian <drumian@redhat.com>

---

diff --git a/pyrpkg/__init__.py b/pyrpkg/__init__.py
index 3bc1829..7c03f45 100644
--- a/pyrpkg/__init__.py
+++ b/pyrpkg/__init__.py
@@ -22,6 +22,7 @@ import os
 import posixpath
 import random
 import re
+import shlex
 import shutil
 import subprocess
 import sys
@@ -1525,6 +1526,19 @@ class Commands(object):
         self._run_command(cmd, cwd=self.path)
         return
 
+    def process_extra_args(self, cmd, extra_args, cmd_name='rpmbuild'):
+        """Ensures that extra args are escaped in shell-compatible way
+
+        :param list cmd: rpm command
+        :param list extra_args: additional arguments that are passed to
+            the command.
+        :param string cmd_name: name of the command, used for debug log
+        """
+        extra_args = list(map(lambda arg: shlex.quote(arg), extra_args))
+        cmd.extend(extra_args)
+        self.log.debug("Extra args '{0}' are passed to {1} "
+                       "command".format(extra_args, cmd_name))
+
     def clone(self, repo, path=None, branch=None, bare_dir=None,
               anon=False, target=None, depth=None, extra_args=None):
         """Clone a repo, optionally check out a specific branch.
@@ -1581,9 +1595,7 @@ class Commands(object):
             # --bare and --origin are incompatible
             cmd.extend(['--origin', self.default_branch_remote])
         if extra_args:
-            cmd.extend(extra_args)
-            self.log.debug("Extra args '{0}' are passed to git clone "
-                           "command".format(extra_args))
+            self.process_extra_args(cmd, extra_args, 'git clone')
         if target:
             self.log.debug('Cloning into: %s', target)
             cmd.append(target)
@@ -2543,9 +2555,7 @@ class Commands(object):
             for entry in define:
                 cmd.extend(['--define', entry])
         if extra_args:
-            cmd.extend(extra_args)
-            self.log.debug("Extra args '{0}' are passed to rpmbuild "
-                           "command".format(extra_args))
+            self.process_extra_args(cmd, extra_args)
         if short:
             cmd.append('--short-circuit')
         if nocheck:
@@ -2619,9 +2629,7 @@ class Commands(object):
             for entry in define:
                 cmd.extend(['--define', entry])
         if extra_args:
-            cmd.extend(extra_args)
-            self.log.debug("Extra args '{0}' are passed to rpmbuild "
-                           "command".format(extra_args))
+            self.process_extra_args(cmd, extra_args)
         if short:
             cmd.append('--short-circuit')
         if nocheck:
@@ -2753,9 +2761,7 @@ class Commands(object):
             for entry in define:
                 cmd.extend(['--define', entry])
         if extra_args:
-            cmd.extend(extra_args)
-            self.log.debug("Extra args '{0}' are passed to rpmbuild "
-                           "command".format(extra_args))
+            self.process_extra_args(cmd, extra_args)
         if self.quiet:
             cmd.append('--quiet')
         if buildrootdir:
@@ -3137,9 +3143,7 @@ class Commands(object):
             for entry in define:
                 cmd.extend(['--define', entry])
         if extra_args:
-            cmd.extend(extra_args)
-            self.log.debug("Extra args '{0}' are passed to rpmbuild "
-                           "command".format(extra_args))
+            self.process_extra_args(cmd, extra_args)
         if self.quiet:
             cmd.append('--quiet')
         if buildrootdir:
@@ -3186,9 +3190,7 @@ class Commands(object):
             for entry in define:
                 cmd.extend(['--define', entry])
         if extra_args:
-            cmd.extend(extra_args)
-            self.log.debug("Extra args '{0}' are passed to rpmbuild "
-                           "command".format(extra_args))
+            self.process_extra_args(cmd, extra_args)
         if self.quiet:
             cmd.append('--quiet')
         if buildrootdir:
@@ -3299,9 +3301,7 @@ class Commands(object):
             for entry in define:
                 cmd.extend(['--define', entry])
         if extra_args:
-            cmd.extend(extra_args)
-            self.log.debug("Extra args '{0}' are passed to rpmbuild "
-                           "command".format(extra_args))
+            self.process_extra_args(cmd, extra_args)
         if self.quiet:
             cmd.append('--quiet')
         if buildrootdir:
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 0304a11..dd21a54 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -7,6 +7,7 @@ import hashlib
 import logging
 import os
 import re
+import shlex
 import shutil
 import subprocess
 import sys
@@ -618,7 +619,7 @@ class TestSrpm(CliTestCase):
             cli.srpm()
 
         expected_cmd = ['rpmbuild'] + cli.cmd.rpmdefines + \
-            ['--define', '"name body"', '--undefine', 'python', '--nodeps', '-bs',
+            ['--define', shlex.quote('"name body"'), '--undefine', 'python', '--nodeps', '-bs',
              os.path.join(cli.cmd.path, cli.cmd.spec)]
         _run_command.assert_called_once_with(expected_cmd, shell=True)
 
@@ -700,6 +701,24 @@ class TestPrep(CliTestCase):
         ]
         _run_command.assert_called_once_with(rpmbuild, shell=True)
 
+    @patch('pyrpkg.Commands._run_command')
+    def test_prep_extra_args_with_space(self, _run_command):
+        first_arg_with_space = 'first with space'
+        second_arg_with_space = 'second with space'
+        cli_cmd = ['rpkg', '--path', self.cloned_repo_path, '--release', 'rhel-8', 'prep', '--',
+                   '--define', first_arg_with_space, second_arg_with_space]
+
+        with patch('sys.argv', new=cli_cmd):
+            cli = self.new_cli()
+            cli.prep()
+
+        spec = os.path.join(cli.cmd.path, cli.cmd.spec)
+        rpmbuild = ['rpmbuild'] + cli.cmd.rpmdefines + ['--define',
+                                                        shlex.quote(first_arg_with_space),
+                                                        shlex.quote(second_arg_with_space),
+                                                        '--nodeps', '-bp', spec]
+        _run_command.assert_called_once_with(rpmbuild, shell=True)
+
 
 class TestInstall(CliTestCase):
 

