# Buggy code
def _shred_file(self, tmp_path):
    """Securely destroy a decrypted file

    Note standard limitations of GNU shred apply (For flash, overwriting would have no effect
    due to wear leveling; for other storage systems, the async kernel->filesystem->disk calls never
    guarantee data hits the disk; etc). Furthermore, if your tmp dirs is on tmpfs (ramdisks),
    it is a non-issue.

    Nevertheless, some form of overwriting the data (instead of just removing the fs index entry) is
    a good idea. If shred is not available (e.g. on windows, or no core-utils installed), fall back on
    a custom shredding method.
    """

    if not os.path.isfile(tmp_path):
        # file is already gone
        return

    try:
        r = call(["shred", tmp_path])
    except OSError:
        # shred is not available on this system, or some other error occured.
        r = 1

    if r != 0:
        # we could not successfully execute unix shred; therefore, do custom shred.
        self._shred_file_custom(tmp_path)

    os.remove(tmp_path)


# Traceback (ValueError)
nolith at mehetueret in /tmp
$ echo "ciao:\n  test: true" > secrets.yml

nolith at mehetueret in /tmp
$ ansible-vault encrypt secrets.yml
New Vault password:
Confirm New Vault password:
ERROR! Unexpected Exception: insecure string pickle
to see the full traceback, use -vvv

nolith at mehetueret in /tmp
$ ansible-vault encrypt secrets.yml -vvv
No config file found; using defaults
New Vault password:
Confirm New Vault password:
ERROR! Unexpected Exception: insecure string pickle
the full traceback was:

Traceback (most recent call last):
File "/usr/local/Cellar/ansible/2.0.1.0/libexec/bin/ansible-vault", line 86, in <module>
sys.exit(cli.run())
File "/usr/local/Cellar/ansible/2.0.1.0/libexec/lib/python2.7/site-packages/ansible/cli/vault.py", line 120, in run
self.execute()
File "/usr/local/Cellar/ansible/2.0.1.0/libexec/lib/python2.7/site-packages/ansible/cli/__init__.py", line 114, in execute
fn()
File "/usr/local/Cellar/ansible/2.0.1.0/libexec/lib/python2.7/site-packages/ansible/cli/vault.py", line 131, in execute_encrypt
self.editor.encrypt_file(f, output_file=self.options.output_file)
File "/usr/local/Cellar/ansible/2.0.1.0/libexec/lib/python2.7/site-packages/ansible/parsing/vault/__init__.py", line 324, in encrypt_file
self.write_data(ciphertext, output_file or filename)
File "/usr/local/Cellar/ansible/2.0.1.0/libexec/lib/python2.7/site-packages/ansible/parsing/vault/__init__.py", line 409, in write_data
self._shred_file(filename)
File "/usr/local/Cellar/ansible/2.0.1.0/libexec/lib/python2.7/site-packages/ansible/parsing/vault/__init__.py", line 277, in _shred_file
r = call(['shred', tmp_path])
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 522, in call
return Popen(*popenargs, **kwargs).wait()
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 710, in __init__
errread, errwrite)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1334, in _execute_child
child_exception = pickle.loads(data)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1382, in loads
return Unpickler(file).load()
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 858, in load
dispatch[key](self)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 966, in load_string
raise ValueError, "insecure string pickle"
ValueError: insecure string pickle

# Fixed code
def _shred_file(self, tmp_path):
    """Securely destroy a decrypted file

    Note standard limitations of GNU shred apply (For flash, overwriting would have no effect
    due to wear leveling; for other storage systems, the async kernel->filesystem->disk calls never
    guarantee data hits the disk; etc). Furthermore, if your tmp dirs is on tmpfs (ramdisks),
    it is a non-issue.

    Nevertheless, some form of overwriting the data (instead of just removing the fs index entry) is
    a good idea. If shred is not available (e.g. on windows, or no core-utils installed), fall back on
    a custom shredding method.
    """

    if not os.path.isfile(tmp_path):
        # file is already gone
        return

    try:
        r = call(["shred", tmp_path])
    except (OSError, ValueError):
        # shred is not available on this system, or some other error occured.
        # ValueError caught because OS X El Capitan is raising an
        # exception big enough to hit a limit in python2-2.7.11 and below.
        # Symptom is ValueError: insecure pickle when shred is not
        # installed there.
        r = 1

    if r != 0:
        # we could not successfully execute unix shred; therefore, do custom shred.
        self._shred_file_custom(tmp_path)

    os.remove(tmp_path)