diff --git a/library/network_connections.py b/library/network_connections.py index 6b17fd25..6881b162 100644 --- a/library/network_connections.py +++ b/library/network_connections.py @@ -301,7 +301,23 @@ def KeyValid(cls, name): @classmethod def ValueEscape(cls, value): - + """Quote a value for an ifcfg file, which is shell syntax. + + Two quoting styles are produced, matching the Bash Reference + Manual: + + ANSI-C quoting, $'...', used when the value contains control + characters (Bash Reference Manual 3.1.2.4). Backslash escapes + are decoded per the ANSI C standard, so \\nnn is the eight-bit + character whose value is the *octal* value nnn, one to three + octal digits. Backslash and single quote are escaped with a + preceding backslash. + + Double quoting, "...", used otherwise (Bash Reference Manual + 3.1.2.3). Within double quotes the characters $, `, \\ and " + retain their special meaning and are escaped with a preceding + backslash. + """ r = getattr(cls, "_re_ValueEscape", None) if r is None: r = re.compile("^[a-zA-Z_0-9-.]*$") @@ -314,8 +330,8 @@ def ValueEscape(cls, value): # needs ansic escaping due to ANSI control characters (newline) s = "$'" for c in value: - if ord(c) < ord(c): - s += "\\" + str(ord(c)) + if ord(c) < ord(" "): + s += "\\%03o" % ord(c) elif c == "\\" or c == "'": s += "\\" + c else: diff --git a/tests/unit/test_network_connections.py b/tests/unit/test_network_connections.py index 12177f70..f3a09cf5 100644 --- a/tests/unit/test_network_connections.py +++ b/tests/unit/test_network_connections.py @@ -5657,5 +5657,19 @@ def unstable_fetch(): self.assertEqual(fetch_mock.call_count, 51) +class TestIfcfgUtilValueEscape(unittest.TestCase): + def test_plain_value_is_not_quoted(self): + self.assertEqual(IfcfgUtil.ValueEscape("eth0"), "eth0") + + def test_control_char_escaped_as_octal(self): + self.assertEqual(IfcfgUtil.ValueEscape("line1\nline2"), "$'line1\\012line2'") + + def test_control_char_with_quote_and_backslash(self): + self.assertEqual(IfcfgUtil.ValueEscape("a\n'b\\c"), "$'a\\012\\'b\\\\c'") + + def test_double_quoting_path_is_unchanged(self): + self.assertEqual(IfcfgUtil.ValueEscape('a "b" $c'), '"a \\"b\\" \\$c"') + + if __name__ == "__main__": unittest.main()