Merge pull request #47041 from terminalmage/issue46909

Force null bytes to be str types
This commit is contained in:
Nicole Thomas 2018-04-18 10:08:24 -04:00 committed by GitHub
commit d6c59696be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 72 additions and 19 deletions

View file

@ -5230,12 +5230,12 @@ def manage_file(name,
if salt.utils.platform.is_windows():
contents = os.linesep.join(
_splitlines_preserving_trailing_newline(contents))
with salt.utils.files.fopen(tmp, 'w') as tmp_:
with salt.utils.files.fopen(tmp, 'wb') as tmp_:
if encoding:
log.debug('File will be encoded with {0}'.format(encoding))
log.debug('File will be encoded with %s', encoding)
tmp_.write(contents.encode(encoding=encoding, errors=encoding_errors))
else:
tmp_.write(salt.utils.stringutils.to_str(contents))
tmp_.write(salt.utils.stringutils.to_bytes(contents))
try:
differences = get_diff(
@ -5438,12 +5438,12 @@ def manage_file(name,
if salt.utils.platform.is_windows():
contents = os.linesep.join(
_splitlines_preserving_trailing_newline(contents))
with salt.utils.files.fopen(tmp, 'w') as tmp_:
with salt.utils.files.fopen(tmp, 'wb') as tmp_:
if encoding:
log.debug('File will be encoded with {0}'.format(encoding))
log.debug('File will be encoded with %s', encoding)
tmp_.write(contents.encode(encoding=encoding, errors=encoding_errors))
else:
tmp_.write(salt.utils.stringutils.to_str(contents))
tmp_.write(salt.utils.stringutils.to_bytes(contents))
# Copy into place
salt.utils.files.copyfile(tmp,

View file

@ -4574,7 +4574,7 @@ def status(cwd,
password=password,
ignore_retcode=ignore_retcode,
output_encoding=output_encoding)['stdout']
for line in output.split('\0'):
for line in output.split(str('\0')):
try:
state, filename = line.split(None, 1)
except ValueError:

View file

@ -2269,9 +2269,9 @@ def managed(name,
.format(contents_id)
)
contents_are_binary = \
isinstance(use_contents, six.string_types) and '\0' in use_contents
if contents_are_binary:
if isinstance(use_contents, bytes) and b'\0' in use_contents:
contents = use_contents
elif isinstance(use_contents, six.string_types) and str('\0') in use_contents:
contents = use_contents
else:
validated_contents = _validate_str_list(use_contents)

View file

@ -637,7 +637,7 @@ class TCPReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, salt.tra
try:
id_ = payload['load'].get('id', '')
if '\0' in id_:
if str('\0') in id_:
log.error('Payload contains an id with a null byte: %s', payload)
stream.send(self.serial.dumps('bad load: id contains a null byte'))
raise tornado.gen.Return()

View file

@ -662,7 +662,7 @@ class ZeroMQReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin,
try:
id_ = payload['load'].get('id', '')
if '\0' in id_:
if str('\0') in id_:
log.error('Payload contains an id with a null byte: %s', payload)
stream.send(self.serial.dumps('bad load: id contains a null byte'))
raise tornado.gen.Return()

View file

@ -190,19 +190,27 @@ def is_binary(data):
'''
Detects if the passed string of data is binary or text
'''
if not data or not isinstance(data, six.string_types):
if not data or not isinstance(data, (six.string_types, six.binary_type)):
return False
if '\0' in data:
if isinstance(data, six.binary_type):
if b'\0' in data:
return True
elif str('\0') in data:
return True
text_characters = ''.join([chr(x) for x in range(32, 127)] + list('\n\r\t\b'))
# Get the non-text characters (map each character to itself then use the
# 'remove' option to get rid of the text characters.)
if six.PY3:
trans = ''.maketrans('', '', text_characters)
nontext = data.translate(trans)
if isinstance(data, six.binary_type):
import salt.utils.data
nontext = data.translate(None, salt.utils.data.encode(text_characters))
else:
trans = ''.maketrans('', '', text_characters)
nontext = data.translate(trans)
else:
if isinstance(data, unicode): # pylint: disable=incompatible-py3-code
if isinstance(data, six.text_type):
trans_args = ({ord(x): None for x in text_characters},)
else:
trans_args = (None, str(text_characters)) # future lint: blacklisted-function

View file

@ -497,7 +497,7 @@ def valid_id(opts, id_):
Returns if the passed id is valid
'''
try:
if any(x in id_ for x in ('/', '\\', '\0')):
if any(x in id_ for x in ('/', '\\', str('\0'))):
return False
return bool(clean_path(opts['pki_dir'], id_))
except (AttributeError, KeyError, TypeError):

View file

@ -57,6 +57,8 @@ from salt.ext.six.moves import range # pylint: disable=import-error,redefined-b
IS_WINDOWS = salt.utils.platform.is_windows()
BINARY_FILE = b'GIF89a\x01\x00\x01\x00\x80\x00\x00\x05\x04\x04\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;'
STATE_DIR = os.path.join(FILES, 'file', 'base')
if IS_WINDOWS:
FILEPILLAR = 'C:\\Windows\\Temp\\filepillar-python'
@ -2190,7 +2192,8 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
'some-utf8-file-create:',
' file.managed:',
" - name: '{0}'".format(test_file),
" - contents: {0}".format(korean_1),
' - contents: |',
' {0}'.format(korean_1),
' - makedirs: True',
' - replace: True',
' - show_diff: True',
@ -2503,6 +2506,23 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
ret = self.run_function('state.sls', mods=state_file)
self.assertSaltTrueReturn(ret)
def test_binary_contents(self):
'''
This tests to ensure that binary contents do not cause a traceback.
'''
name = os.path.join(TMP, '1px.gif')
try:
ret = self.run_state(
'file.managed',
name=name,
contents=BINARY_FILE)
self.assertSaltTrueReturn(ret)
finally:
try:
os.remove(name)
except OSError:
pass
class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin):
marker_start = '# start'

View file

@ -37,19 +37,44 @@ class StringutilsTestCase(TestCase):
def test_is_binary(self):
self.assertFalse(salt.utils.stringutils.is_binary(LOREM_IPSUM))
# Also test bytestring
self.assertFalse(
salt.utils.stringutils.is_binary(
salt.utils.stringutils.is_binary(LOREM_IPSUM)
)
)
zero_str = '{0}{1}'.format(LOREM_IPSUM, '\0')
self.assertTrue(salt.utils.stringutils.is_binary(zero_str))
# Also test bytestring
self.assertTrue(
salt.utils.stringutils.is_binary(
salt.utils.stringutils.to_bytes(zero_str)
)
)
# To to ensure safe exit if str passed doesn't evaluate to True
self.assertFalse(salt.utils.stringutils.is_binary(''))
self.assertFalse(salt.utils.stringutils.is_binary(b''))
nontext = 3 * (''.join([chr(x) for x in range(1, 32) if x not in (8, 9, 10, 12, 13)]))
almost_bin_str = '{0}{1}'.format(LOREM_IPSUM[:100], nontext[:42])
self.assertFalse(salt.utils.stringutils.is_binary(almost_bin_str))
# Also test bytestring
self.assertFalse(
salt.utils.stringutils.is_binary(
salt.utils.stringutils.to_bytes(almost_bin_str)
)
)
bin_str = almost_bin_str + '\x01'
self.assertTrue(salt.utils.stringutils.is_binary(bin_str))
# Also test bytestring
self.assertTrue(
salt.utils.stringutils.is_binary(
salt.utils.stringutils.to_bytes(bin_str)
)
)
def test_to_str(self):
for x in (123, (1, 2, 3), [1, 2, 3], {1: 23}, None):