From f9d1a59bb826e1d5b928bc5301dc5fa40f6adc63 Mon Sep 17 00:00:00 2001 From: Dirk Mueller Date: Tue, 11 Jun 2013 18:25:20 +0200 Subject: Handle empty arglists in Filters The various filters either asserted or incorrectly assumed that an empty arglist matched the filter. Add testcases to avoid regressions. Change-Id: If90fbad3d54749ecc645071675402ea2613870a2 --- openstack/common/rootwrap/filters.py | 17 +++++++--------- tests/unit/test_rootwrap.py | 39 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/openstack/common/rootwrap/filters.py b/openstack/common/rootwrap/filters.py index b40fdfd..660434a 100644 --- a/openstack/common/rootwrap/filters.py +++ b/openstack/common/rootwrap/filters.py @@ -47,7 +47,7 @@ class CommandFilter(object): def match(self, userargs): """Only check that the first argument (command) matches exec_path.""" - return os.path.basename(self.exec_path) == userargs[0] + return userargs and os.path.basename(self.exec_path) == userargs[0] def get_command(self, userargs, exec_dirs=[]): """Returns command to execute (with sudo -u if run_as != root).""" @@ -67,7 +67,7 @@ class RegExpFilter(CommandFilter): def match(self, userargs): # Early skip if command or number of args don't match - if (len(self.args) != len(userargs)): + if (not userargs or len(self.args) != len(userargs)): # DENY: argument numbers don't match return False # Compare each arg (anchoring pattern explicitly at end of string) @@ -101,6 +101,9 @@ class PathFilter(CommandFilter): """ def match(self, userargs): + if not userargs or len(userargs) < 2: + return False + command, arguments = userargs[0], userargs[1:] equal_args_num = len(self.args) == len(arguments) @@ -178,7 +181,7 @@ class KillFilter(CommandFilter): super(KillFilter, self).__init__("/bin/kill", *args) def match(self, userargs): - if userargs[0] != "kill": + if not userargs or userargs[0] != "kill": return False args = list(userargs) if len(args) == 3: @@ -229,13 +232,7 @@ class ReadFileFilter(CommandFilter): super(ReadFileFilter, self).__init__("/bin/cat", "root", *args) def match(self, userargs): - if userargs[0] != 'cat': - return False - if userargs[1] != self.file_path: - return False - if len(userargs) != 2: - return False - return True + return (userargs == ['cat', self.file_path]) class IpFilter(CommandFilter): diff --git a/tests/unit/test_rootwrap.py b/tests/unit/test_rootwrap.py index 6e1e6e6..a649660 100644 --- a/tests/unit/test_rootwrap.py +++ b/tests/unit/test_rootwrap.py @@ -40,6 +40,32 @@ class RootwrapTestCase(utils.BaseTestCase): filters.CommandFilter("/bin/cat", "root") # Keep this one last ] + def test_CommandFilter(self): + f = filters.CommandFilter("sleep", 'root', '10') + self.assertFalse(f.match(["sleep2"])) + + # verify that any arguments are accepted + self.assertTrue(f.match(["sleep"])) + self.assertTrue(f.match(["sleep", "anything"])) + self.assertTrue(f.match(["sleep", "10"])) + f = filters.CommandFilter("sleep", 'root') + self.assertTrue(f.match(["sleep", "10"])) + + def test_empty_commandfilter(self): + f = filters.CommandFilter("sleep", "root") + self.assertFalse(f.match([])) + self.assertFalse(f.match(None)) + + def test_empty_regexpfilter(self): + f = filters.RegExpFilter("sleep", "root", "sleep") + self.assertFalse(f.match([])) + self.assertFalse(f.match(None)) + + def test_empty_invalid_regexpfilter(self): + f = filters.RegExpFilter("sleep", "root") + self.assertFalse(f.match(["anything"])) + self.assertFalse(f.match([])) + def test_RegExpFilter_match(self): usercmd = ["ls", "/root"] filtermatch = wrapper.match_filter(self.filters, usercmd) @@ -195,6 +221,9 @@ class RootwrapTestCase(utils.BaseTestCase): # Providing something that is not a pid should be False usercmd = ['kill', 'notapid'] self.assertFalse(f.match(usercmd)) + # no arguments should also be fine + self.assertFalse(f.match([])) + self.assertFalse(f.match(None)) def test_KillFilter_deleted_exe(self): """Makes sure deleted exe's are killed correctly.""" @@ -289,6 +318,12 @@ class RootwrapTestCase(utils.BaseTestCase): self.assertRaises(wrapper.NoFilterMatched, wrapper.match_filter, filter_list, args) + def test_ReadFileFilter_empty_args(self): + goodfn = '/good/file.name' + f = filters.ReadFileFilter(goodfn) + self.assertFalse(f.match([])) + self.assertFalse(f.match(None)) + def test_exec_dirs_search(self): # This test supposes you have /bin/cat or /usr/bin/cat locally f = filters.CommandFilter("cat", "root") @@ -387,6 +422,10 @@ class PathFilterTestCase(utils.BaseTestCase): self.SYMLINK_OUTSIDE_DIR = os.path.join(tmpdir.path, gen_name()) os.symlink(os.path.join('/tmp', 'some_file'), self.SYMLINK_OUTSIDE_DIR) + def test_empty_args(self): + self.assertFalse(self.f.match([])) + self.assertFalse(self.f.match(None)) + def test_argument_pass_constraint(self): f = filters.PathFilter('/bin/chown', 'root', 'pass', 'pass') -- cgit