CVE-2017-2809: Improper Control of Generation of Code ('Code Injection') (CWE-94)
value: An exploitable vulnerability exists in the yaml loading functionality of ansible-vault before 1.0.5. A specially crafted vault can execute arbitrary python commands resulting in command execution. An attacker can insert python into the vault to trigger this vulnerability.
CWE description: The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
CVSS v3 base score: 7.8
Published: 2017-09-14T19:29Z
Repository: https://github.com/tomoh1r/ansible-vault
Language: Python
Files changed: .travis.yml, CHANGES.txt, README.rst, README_test.rst, ansible_vault/api.py, ansible_vault/test/file/pwned.txt, ansible_vault/test/test_api.py, setup.cfg, setup.py
Commit date: 2017-09-13 22:10:47 +0900
Commit message: Fix security issue refs #4

* Update version to 1.0.5
* Update to use yaml.safe_load()
* Update to tomoh1r

Diff: diff --git a/.travis.yml b/.travis.yml
index 70055d9..7b6b1ed 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,14 +1,20 @@
 ---
 language: python
 sudo: false
+cache:
+  directories:
+    - $HOME/.cache/pip
 python:
-- 2.7
-env:
-  matrix:
-    - TOXENV=py27
-    - TOXENV=py27-ansible2
+  - "2.7"
+  - "3.3"
+  - "3.4"
+  - "3.5"
+  - "3.6"
 install:
-  - pip install tox
-script: tox
+  - python -m pip install -U setuptools pip
+  - python setup.py setup_test
+script:
+  - python -m pytest
+  - if [ "$TRAVIS_PYTHON_VERSION" == '2.7' ] ; then python -m pip install -U 'ansible<2.0.0' && python -m pytest ; fi
 
 # vim:st=2 sts=2 sw=2:
diff --git a/CHANGES.txt b/CHANGES.txt
index 1e8aa4e..a51a244 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,15 @@
+1.0.5 (2017/09/13)
+
+* Update to use yaml.safe_load()$
+* Update to tomoh1r$
+
+1.0.4 (2015/11/29)
+
+* Apply to Ansible 2
+* Add unit tests
+
 1.0.3 (2015/05/18)
 
 * fix README.rst typo
 
 1.0.2 (2015/05/18)
diff --git a/README.rst b/README.rst
index 0663322..63a8dd3 100644
--- a/README.rst
+++ b/README.rst
@@ -1,11 +1,11 @@
 =============
 ansible-vault
 =============
 
-.. image:: https://travis-ci.org/jptomo/ansible-vault.svg?branch=master
-   :target: https://travis-ci.org/jptomo/ansible-vault
+.. image:: https://travis-ci.org/tomoh1r/ansible-vault.svg?branch=master
+   :target: https://travis-ci.org/tomoh1r/ansible-vault
 
 This project aim to R/W an ansible-vault yaml file
 
 You can install with pip.
 
diff --git a/README_test.rst b/README_test.rst
index be5691d..57ce749 100644
--- a/README_test.rst
+++ b/README_test.rst
@@ -10,6 +10,7 @@ how to test
 
 2. run test
 
    .. code-block:: console
 
+      $ ./venvtest/bin/python setup.py setup_test
       $ ./venvtest/bin/python setup.py test
diff --git a/ansible_vault/api.py b/ansible_vault/api.py
index 3fcc0ad..2f209c1 100644
--- a/ansible_vault/api.py
+++ b/ansible_vault/api.py
@@ -13,11 +13,11 @@ class Vault(object):
         self.password = password
         self.vault = VaultLib(password)
 
     def load(self, stream):
         '''read vault steam and return python object'''
-        return yaml.load(self.vault.decrypt(stream))
+        return yaml.safe_load(self.vault.decrypt(stream))
 
     def dump(self, data, stream=None):
         '''encrypt data and print stdout or write to stream'''
         yaml_text = yaml.dump(
             data,
diff --git a/ansible_vault/test/file/pwned.txt b/ansible_vault/test/file/pwned.txt
new file mode 100644
index 0000000..7e21b6e
--- /dev/null
+++ b/ansible_vault/test/file/pwned.txt
@@ -0,0 +1,7 @@
+$ANSIBLE_VAULT;1.1;AES256
+31616433623434626463363932323936663066353063393731346536636437633463633137643032
+3663656431663830396662646132343735623538346330640a363532326262353732636161633431
+61353936346235396464333333653831356638393264343662363362653433353762396663653465
+6439366430336336660a363931663030323665633136363362353162333864653933653763656462
+31656431653333343834623731393263393865353831333963616165613237376630646665306363
+6238373037663462343565643737303136333032386136356438
diff --git a/ansible_vault/test/test_api.py b/ansible_vault/test/test_api.py
index 2535d5c..5a123d3 100644
--- a/ansible_vault/test/test_api.py
+++ b/ansible_vault/test/test_api.py
@@ -1,11 +1,11 @@
 import os
 from tempfile import mkstemp
 
-from testfixtures import ShouldRaise
-
 from ansible.errors import AnsibleError
+from testfixtures import ShouldRaise
+from yaml.constructor import ConstructorError
 
 
 here = os.path.dirname(os.path.abspath(__file__))
 
 
@@ -26,10 +26,16 @@ class TestVaultLoad(object):
         fpath = os.path.join(here, 'file', 'vault.txt')
         vault = self._makeOne('invalid-password')
         with ShouldRaise(AnsibleError('Decryption failed')):
             vault.load(open(fpath).read())
 
+    def test_not_pwned(self):
+        fpath = os.path.join(here, 'file', 'pwned.txt')
+        vault = self._makeOne('password')
+        with ShouldRaise(ConstructorError):
+            vault.load(open(fpath).read())
+
 
 class TestVaultDump(object):
     def _getTargetClass(self):
         from ansible_vault import Vault
         return Vault
diff --git a/setup.cfg b/setup.cfg
index 5aef279..fc590f8 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,2 +1,10 @@
 [metadata]
 description-file = README.rst
+
+[aliases]
+setup_test = develop easy_install ansible-vault[test]
+release = register clean --all sdist
+
+[tools:pytest]
+norecursedirs = venv
+testpaths = ansible_vault/test
diff --git a/setup.py b/setup.py
index c42a8c4..6c4b2f2 100644
--- a/setup.py
+++ b/setup.py
@@ -30,21 +30,23 @@ class PyTest(TestCommand):
         sys.exit(pytest.main(self.pytest_args))
 
 
 setup(
     name='ansible-vault',
-    version='1.0.4',
+    version='1.0.5',
     author='Tomohiro NAKAMURA',
     author_email='quickness.net@gmail.com',
-    url='https://github.com/jptomo/ansible-vault',
+    url='https://github.com/tomoh1r/ansible-vault',
     description='R/W an ansible-vault yaml file',
     long_description=_read('README.rst'),
     packages=find_packages(),
     install_requires=['ansible'],
-    tests_require=['pytest', 'testfixtures'],
     cmdclass={'test': PyTest},
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
     ],
     license='GPLv3',
+    extras_require = {
+        'test': ['pytest', 'testfixtures'],
+    }
 )
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index cb350f9..0000000
--- a/tox.ini
+++ /dev/null
@@ -1,14 +0,0 @@
-[tox]
-envlist = py27, py27-ansible2
-setupdir = .
-
-[testenv]
-basepython = python2.7
-
-[testenv:py27]
-commands = python2.7 setup.py test
-
-[testenv:py27-ansible2]
-commands =
-    pip install -U git+https://github.com/ansible/ansible.git@stable-2.0#egg=ansible
-    python2.7 setup.py test

Vulnerable code: - 2.7
env:
  matrix:
    - TOXENV=py27
    - TOXENV=py27-ansible2
  - pip install tox
script: tox
.. image:: https://travis-ci.org/jptomo/ansible-vault.svg?branch=master
   :target: https://travis-ci.org/jptomo/ansible-vault
        return yaml.load(self.vault.decrypt(stream))
from testfixtures import ShouldRaise

    version='1.0.4',
    url='https://github.com/jptomo/ansible-vault',
    tests_require=['pytest', 'testfixtures'],
[tox]
envlist = py27, py27-ansible2
setupdir = .

[testenv]
basepython = python2.7

[testenv:py27]
commands = python2.7 setup.py test

[testenv:py27-ansible2]
commands =
    pip install -U git+https://github.com/ansible/ansible.git@stable-2.0#egg=ansible
    python2.7 setup.py test
Fixed code: cache:
  directories:
    - $HOME/.cache/pip
  - "2.7"
  - "3.3"
  - "3.4"
  - "3.5"
  - "3.6"
  - python -m pip install -U setuptools pip
  - python setup.py setup_test
script:
  - python -m pytest
  - if [ "$TRAVIS_PYTHON_VERSION" == '2.7' ] ; then python -m pip install -U 'ansible<2.0.0' && python -m pytest ; fi
1.0.5 (2017/09/13)

* Update to use yaml.safe_load()$
* Update to tomoh1r$

1.0.4 (2015/11/29)

* Apply to Ansible 2
* Add unit tests

.. image:: https://travis-ci.org/tomoh1r/ansible-vault.svg?branch=master
   :target: https://travis-ci.org/tomoh1r/ansible-vault
      $ ./venvtest/bin/python setup.py setup_test
        return yaml.safe_load(self.vault.decrypt(stream))
$ANSIBLE_VAULT;1.1;AES256
31616433623434626463363932323936663066353063393731346536636437633463633137643032
3663656431663830396662646132343735623538346330640a363532326262353732636161633431
61353936346235396464333333653831356638393264343662363362653433353762396663653465
6439366430336336660a363931663030323665633136363362353162333864653933653763656462
31656431653333343834623731393263393865353831333963616165613237376630646665306363
6238373037663462343565643737303136333032386136356438
from testfixtures import ShouldRaise
from yaml.constructor import ConstructorError
    def test_not_pwned(self):
        fpath = os.path.join(here, 'file', 'pwned.txt')
        vault = self._makeOne('password')
        with ShouldRaise(ConstructorError):
            vault.load(open(fpath).read())


[aliases]
setup_test = develop easy_install ansible-vault[test]
release = register clean --all sdist

[tools:pytest]
norecursedirs = venv
testpaths = ansible_vault/test
    version='1.0.5',
    url='https://github.com/tomoh1r/ansible-vault',
    extras_require = {
        'test': ['pytest', 'testfixtures'],
    }