Repository: opsdroid/opsdroid

Issue:
Potential vulnerability with yaml.loader
# Description
I was listening to Talk Python to Me podcast - the one where they talk about Python vulnerabilities and things that we shouldn't be doing in Python.  One of the things that they mention was using `yaml.load` instead of `yaml.safe_load`. 

I know that when I wrote the bit of code so we can use `!include` I use yaml.loader so perhaps this should be changed to `yaml.safe_load`. One thing I didn't understand was if the `yaml.loader` is now set as the default safe_load or not. (Mostly due to [this issue](https://github.com/yaml/pyyaml/issues/5)).

I tried to swap `yaml.load` for `yaml.safe_load` but tox complained and both the environmental variables and the `!include` tests are failing.

Should I dig deeper and try to make the safe_load work?

Also, this article in hackernoon might be interesting for reference about this issue - [10 common security gotchas in Python and how to avoid them](https://hackernoon.com/10-common-security-gotchas-in-python-and-how-to-avoid-them-e19fbe265e03)


<!-- Love opsdroid? Please consider supporting our collective:
 +👉  https://opencollective.com/opsdroid/donate -->


Discussion:
Funnily enough I was looking into this yesterday!

The recent PRs have been failed due to there being an [open CVE for `pyyaml`](https://github.com/yaml/pyyaml/issues/207). From looking into it I think this vulnerability has been ackowledged for a while but when using the latest version of `pyyaml` with python 3.7 you may experience the vulnerability.

They are planning on releasing a new version of `pyyaml` which addresses this vulnerability. As this issue will affect all existing versions of opsdroid I think we are ok to go ahead and merge PRs which have failed due to this.

I totally agree that we should explore `yaml.safe_load`.  If you have the time to dig deeper that would be great!
Yeah, I was a bit confused due to that PR that said it implements `safe_load` as default although the documentation still tells you to not use `load` and use `safe_load` instead. For some reason, the tests are failing I will look into it further and try to figure out why they are failing 👍 
Getting #574 merged is the priority at the moment. If you could look into it that would be great.

Resolving patch:
diff --git a/opsdroid/loader.py b/opsdroid/loader.py
--- a/opsdroid/loader.py
+++ b/opsdroid/loader.py
@@ -226,7 +226,7 @@ def include_constructor(loader, node):
                                          loader.construct_scalar(node))
 
             with open(included_yaml, 'r') as included:
-                return yaml.load(included)
+                return yaml.safe_load(included)
 
         yaml.add_constructor('!envvar', envvar_constructor)
         yaml.add_constructor('!include', include_constructor)


Test patch:
diff --git a/tests/configs/exploit.yaml b/tests/configs/exploit.yaml
new file mode 100644
--- /dev/null
+++ b/tests/configs/exploit.yaml
@@ -0,0 +1 @@
+!!python/object/apply:os.system ["echo 'Oops!';"]
diff --git a/tests/configs/include_exploit.yaml b/tests/configs/include_exploit.yaml
new file mode 100644
--- /dev/null
+++ b/tests/configs/include_exploit.yaml
@@ -0,0 +1 @@
+!include exploit.yaml
\ No newline at end of file
diff --git a/tests/test_loader.py b/tests/test_loader.py
--- a/tests/test_loader.py
+++ b/tests/test_loader.py
@@ -50,6 +50,14 @@ def test_load_config_file_with_include(self):
         self.assertIsNotNone(config)
         self.assertEqual(config, config2)
 
+    def test_yaml_load_exploit(self):
+        opsdroid, loader = self.setup()
+        config = loader.load_config_file(
+            [os.path.abspath("tests/configs/include_exploit.yaml")])
+        self.assertIsNone(config)
+        # If the command in exploit.yaml is echoed it will return 0
+        self.assertNotEqual(config, 0)
+
     def test_load_config_file_with_env_vars(self):
         opsdroid, loader = self.setup()
         os.environ["ENVVAR"] = 'test'