From e5a9e46138041876c650bc2c3eab4b5dde28b2ea Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Jan 11 2024 16:19:47 +0000 Subject: get_directive: don't error out on substring mismatch This function is designed to retrieve a value from an ini-like file. In particular PKI CS.cfg. In an attempt to be more efficient a substring search, using startswith(), is used before calling a regular expression match. The problem is that if the requested directive is a substring of a different one then it will pass the startswith() and fail the regular expression match with a ValueError, assuming it is malformed. There is no need for this. The caller must be able to handle None as a response anyway. So continue if no match is found. This was seen when PKI dropped storing certificate blobs in CS.cfg. The CA certificate is stored in ca.signing.cert. If it isn't present then ca.signing.certnickname will match the substring but not the directive. This should not be treated as an error. Fixes: https://pagure.io/freeipa/issue/9506 Signed-off-by: Rob Crittenden Reviewed-By: Florence Blanc-Renaud --- diff --git a/ipapython/directivesetter.py b/ipapython/directivesetter.py index f4e496c..732e1c2 100644 --- a/ipapython/directivesetter.py +++ b/ipapython/directivesetter.py @@ -182,6 +182,9 @@ def get_directive(filename, directive, separator=' '): if separator == ' ': separator = '[ \t]+' + if directive is None: + return None + result = None with open(filename, "r") as fd: for line in fd: @@ -193,7 +196,7 @@ def get_directive(filename, directive, separator=' '): if match: value = match.group(1) else: - raise ValueError("Malformed directive: {}".format(line)) + continue result = unquote_directive_value(value.strip(), '"') result = result.strip(' ') diff --git a/ipatests/test_ipapython/test_directivesetter.py b/ipatests/test_ipapython/test_directivesetter.py index 08a3012..ff86559 100644 --- a/ipatests/test_ipapython/test_directivesetter.py +++ b/ipatests/test_ipapython/test_directivesetter.py @@ -18,6 +18,10 @@ WHITESPACE_CONFIG = [ 'foobar\t2\n', ] +SUBSTRING_CONFIG = [ + 'foobar=2\n', +] + class test_set_directive_lines: def test_remove_directive(self): @@ -88,6 +92,7 @@ class test_set_directive: class test_get_directive: def test_get_directive(self, tmpdir): + """Test retrieving known values from a config file""" configfile = tmpdir.join('config') configfile.write(''.join(EXAMPLE_CONFIG)) @@ -97,6 +102,34 @@ class test_get_directive: assert '2' == directivesetter.get_directive(str(configfile), 'foobar', separator='=') + assert None is directivesetter.get_directive(str(configfile), + 'notfound', + separator='=') + + def test_get_directive_substring(self, tmpdir): + """Test retrieving values from a config file where there is + a similar substring that is not present. + """ + configfile = tmpdir.join('config') + configfile.write(''.join(SUBSTRING_CONFIG)) + + assert None is directivesetter.get_directive(str(configfile), + 'foo', + separator='=') + assert '2' == directivesetter.get_directive(str(configfile), + 'foobar', + separator='=') + + def test_get_directive_none(self, tmpdir): + """Test retrieving a value from a config file where the + directive is None. i.e. don't fail. + """ + configfile = tmpdir.join('config') + configfile.write(''.join(EXAMPLE_CONFIG)) + + assert None is directivesetter.get_directive(str(configfile), + None, + separator='=') class test_get_directive_whitespace: