Repository: swimlane/pyattck (language: python)

Issue:
update to add data_sources reference

Cannot extract DataSource's
I am trying to pull the DataSources for the techniques but I keep getting the follow error:

AttributeError: 'AttckTechnique' object has no attribute 'data_source'

Add ability to set refresh time of data files.
Currently the data files are evaluated and refreshed when they are older than 30 days. I would be great if that was an option that users could override in both the instantiation of the Attck class as well as in the config file.
2.1.0 and 2.1.3 return ValueError: No JSON object could be decoded.
>>> from pyattck import Attck
>>> attck = Attck().update(enterprise=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\dev\swimlane_4_1_env\lib\site-packages\pyattck\attck.py", line 365, in update
    self.__load_data(force=True)
  File "C:\dev\swimlane_4_1_env\lib\site-packages\pyattck\attck.py", line 379, in __load_data
    Attck.__ENTERPRISE_GENERATED_DATA_JSON = self.__datasets.generated_attck_data(force=force)
  File "C:\dev\swimlane_4_1_env\lib\site-packages\pyattck\datasets.py", line 77, in generated_attck_data
    datasets = self.__get_datasets()
  File "C:\dev\swimlane_4_1_env\lib\site-packages\pyattck\datasets.py", line 101, in __get_datasets
    return requests.get(self.__DATASETS_URL).json()
  File "C:\dev\swimlane_4_1_env\lib\site-packages\requests\models.py", line 892, in json
    return complexjson.loads(self.text, **kwargs)
  File "C:\dev\Python2714\Lib\json\__init__.py", line 339, in loads
    return _default_decoder.decode(s)
  File "C:\dev\Python2714\Lib\json\decoder.py", line 364, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "C:\dev\Python2714\Lib\json\decoder.py", line 382, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

Add better management of remote and local external data files



Discussion / hints:

@TiiTcHY Can you please tell me which version of `pyattck` you are using that is causing this error?  Also which operating system are you using it on.

Thanks,

Please upgrade to the latest if possible:

pip install pyattck—upgrade 

if not then I will attempt to resolve this but upgrading is the best option.
Yes I confirmed it gets past that but the breaking changes in 3.0 will require refactoring quite a bit of code to get back to working state. It would be great to have had tests for this against 2.x code base, or some form of backwards compatibility. 
The reason was that the external data was getting too large so I had to switch to git-lfs which means a different url format/path. It was unexpected but needed to happen. I’ll push some new files but I’m not sure how much longer they will be available. I’ll try and get this done tonight or tomorrow morning. Apologies for any interruptions.
Thanks for the explanation and appreciate the response. We happen to have built this into a production SL app so I'll need to work on getting cutover to the latest version. 
No problem and sorry for any troubles.  A quick fix to get up and running is doing something like this to set the value to the new URL (this is for the generated data):

```
from pyattck import Attck

attck = Attck()

attck.__ENTERPRISE_GENERATED_DATA_JSON = 'https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True'
```

@jmeachum are you using this as part of the Swimlane plugin or stand-alone (e.g. python task)?  If you need help either way I'll try and do my best to facilitate/assist.
@MSAdministrator - A couple of stand-alone tasks. We are using it for tagging alerts against the framework and use it for contextual data. I should be able to get us current with 3.0.1 but concerned when this will break our prod application that uses it. If the data can live in the old locations for a couple of more weeks that should give me enough time for testing in dev and deployment to prod.
Is there functionality that has changed ? I’m curious if I can provide a fix without breaking it - just upgrading versions.
As a side note I would love to see how you’re using it in production sometime. 
> just upgrading versions.

For what we are using it for right now, no I don't think there is any functionality that has been broken. It's a matter of refactoring the code to pull the data we need. It's actually a fairly basic use case at this time but has potential to be used in more depth later on. I'd be happy to explain more about how we are using it. I'd prefer to have that communication via email however. Mind sending me your SL email address? I'll try sending it to what I'd think it would be given the naming convention used by other contacts there.
> Is there functionality that has changed ? I’m curious if I can provide a fix without breaking it - just upgrading versions.

So the refactoring was minor on the code side.....the tests were a little more involved. I did write a test to help identify the issue identified however I found some odd behavior between tests, which looks to be related to the use of class variables. When a test instantiates an Attck() class the class variables are None as expected. And when the enterprise property is accessed the data is loaded into the class vars. When a subsequent test is called afterwards those vars carry over to the new one. This probably doesn't cause any issues outside of testing, but aside from resetting them on each test I suppose is the way around that. Any reason for use of class vars vs instance? Below is a test to reproduce the behavior. If this is better to be a different issue I can raise it there. I have a test for the update function as well.
```
@pytest.fixture
def attck():
    yield Attck()

def test_class_vars_set(attck):
    assert attck._Attck__ENTERPRISE_ATTCK_JSON is None
    assert attck._Attck__ENTERPRISE_GENERATED_DATA_JSON is None
    assert attck._Attck__ENTERPRISE_NIST_DATA_JSON is None
    techniques = attck.enterprise.techniques
    assert isinstance(techniques, list)
    assert isinstance(attck._Attck__ENTERPRISE_ATTCK_JSON, dict)
    assert isinstance(attck._Attck__ENTERPRISE_GENERATED_DATA_JSON, dict)
    assert isinstance(attck._Attck__ENTERPRISE_NIST_DATA_JSON, dict)


def test_class_vars_persist_tests(attck):
    assert isinstance(attck._Attck__ENTERPRISE_ATTCK_JSON, dict)
    assert isinstance(attck._Attck__ENTERPRISE_GENERATED_DATA_JSON, dict)
    assert isinstance(attck._Attck__ENTERPRISE_NIST_DATA_JSON, dict)```
@jmeachum thanks for this!  Yes, please create a separate issue and I will take a look at this.  Thanks for pointing it out and you are correct these probably should be instance variables instead of class variables. Originally I choose class variables so that loading of the data would happen once instead of continually loading from disk but this is less of a concern now that Enterprise, Mobile, etc. are in their own classes.
@jmeachum unrelated (kind of) to this but I’m refactoring pyattck’s handling of external data (e.g. enterprise ATT&CK Json, contextual data Json, etc.) and I’m allowing the user to set these paths to either a URL or a local file path when they instantiate the Attck class. Additionally they can pass in a config file like they can now. 

Does this sound useful? (Also adding proxy support).
> @jmeachum unrelated (kind of) to this but I’m refactoring pyattck’s handling of external data (e.g. enterprise ATT&CK Json, contextual data Json, etc.) and I’m allowing the user to set these paths to either a URL or a local file path when they instantiate the Attck class. Additionally they can pass in a config file like they can now.
> 
> Does this sound useful? (Also adding proxy support).

@MSAdministrator - Sorry for the late reply. I've used data_path to do just that, specify a different path on instantiation. I actually use it in a test though to confirm the files are pulled on instantiation by checking that path afterwards. The suggestion on providing a url, I think it could be useful.

Regarding proxy support, if the verify property of requests could be set, that would help for situations where there is an intermediary certificate, e.g. SSL inspection is enabled and the cert is signed by internal CAs. Requests will look at an environment variable to provide the path to certs to use in SSL verification but you wouldn't be able to turn off verification if that is something someone needs to do, e.g. troubleshooting. You could implement either by name or pass **kwargs to requests. Setting the refresh frequency of the files would also be useful, as an argument during instantiation as well as in the config file. I can raise that as a request.


Fix patch:
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
new file mode 100644
index 00000000..b42fa375
--- /dev/null
+++ b/.github/workflows/macos.yml
@@ -0,0 +1,32 @@
+name: Testing pyattck on macOS
+
+on: 
+  push
+
+jobs:
+  build:
+
+    runs-on: macos-latest
+    strategy:
+      matrix:
+        python-version: [3.6, 3.7, 3.8, 3.9]
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v1
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install -r requirements.txt
+    - name: Run Tests on macOS
+      run: |
+        sudo pip install -r requirements.txt
+        sudo pip install -r test-requirements.txt
+        sudo python -m pytest
+    - name: Code Coverage on macOS
+      run: |
+        sudo python -m coverage run -m pytest
+        sudo python -m coverage report
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
new file mode 100644
index 00000000..5b4c7c8b
--- /dev/null
+++ b/.github/workflows/ubuntu.yml
@@ -0,0 +1,32 @@
+name: Testing pyattck on Ubuntu
+
+on: 
+  push
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: [3.6, 3.7, 3.8, 3.9]
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v1
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install -r requirements.txt
+    - name: Run Tests on Ubuntu
+      run: |
+        sudo pip install -r requirements.txt
+        sudo pip install -r test-requirements.txt
+        sudo python -m pytest
+    - name: Code Coverage on Ubuntu
+      run: |
+        sudo python -m coverage run -m pytest
+        sudo python -m coverage report
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
new file mode 100644
index 00000000..bbce0b5b
--- /dev/null
+++ b/.github/workflows/windows.yml
@@ -0,0 +1,34 @@
+name: Testing pyattck on Windows
+
+on: 
+  push
+
+jobs:
+  build:
+
+    runs-on: windows-latest
+    strategy:
+        matrix:
+            python-version: [3.6, 3.7, 3.8, 3.9]
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v1
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install -r requirements.txt
+      shell: pwsh
+    - name: Run Tests on Windows
+      run: |
+        start-process pwsh -verb runas 
+        pip install -r requirements.txt
+        python -m pip install -r test-requirements.txt
+        python -m pytest
+        python -m coverage run -m pytest
+        python -m coverage report
+      shell: cmd
+
diff --git a/README.md b/README.md
index 530c769f..3acd1b45 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
 ![](./images/ubuntu_support.svg)
 ![](./images/macos_support.svg)
 ![](./images/windows_support.svg)
+![](./images/code_coverage.svg)
 
 # Welcome to pyattck's Documentation
 
@@ -16,18 +17,19 @@
 ```
 	A Python package to interact with MITRE ATT&CK Frameworks
 
+> Current Version is 4.0.0
 
 **pyattck** is a light-weight framework for MITRE ATT&CK Frameworks. This package extracts details from the MITRE Enterprise, PRE-ATT&CK, and Mobile Frameworks.
 
 ## Features
 
 The **pyattck** package retrieves all Tactics, Techniques, Actors, Malware, Tools, and Mitigations from the MITRE ATT&CK Frameworks as well as any defined relationships within the MITRE ATT&CK dataset.
+
 In addition, Techniques, Actors, and Tools (if applicable) now have collected data from third-party resources that are accessible via properties on a technique. For more detailed information about these features, see [External Datasets](docs/dataset/dataset.md).
 
 The **pyattck** package allows you to:
 
-  * Update or sync the external datasets by calling the `update()` method on an `Attck` object. By default it checks for updates every 30 days.
-  * Specify a local file path for the MITRE ATT&CK Enterprise Framework json, generated dataset, and/or a config.yml file.
+  * Specify a URL or local file path for the MITRE ATT&CK Enterprise Framework json, generated dataset, and/or a config.yml file.
   * Retrieve an image_logo of an actor (when available). If an image_logo isn't available, it generates an ascii_logo.
   * Search the external dataset for external commands that are similar using `search_commands`.
   * Access data from the MITRE PRE-ATT&CK Framework
@@ -35,6 +37,13 @@ The **pyattck** package allows you to:
   * Access subtechniques as nested objects or you can turn it off and access as normal technique
   * Access compliance controls (currently NIST 800-53) related to a MITRE ATT&CK Technique
 
+# Table of Contents
+
+1. [Installation](#installation)
+2. [Usage Example](#usage-example)
+3. [Configuration](#configuration)
+4. [Notes](#note)
+
 ## Installation
 
 You can install **pyattack** on OS X, Linux, or Windows. You can also install it directly from the source. To install, see the commands under the relevant operating system heading, below.
@@ -47,18 +56,12 @@ The following libraries are required and installed by pyattck:
 requests
 pendulum>=1.2.3,<1.3
 pyfiglet==0.8.post1
-PyYaml>=5.0
-Pillow==7.1.2
+PyYaml>=5.4.1
+Pillow>=6.2.2
 fire==0.3.1
 ```
 
-### OS X & Linux:
-
-```bash
-pip install pyattck
-```
-
-### Windows:
+### macOS, Linux and Windows:
 
 ```bash
 pip install pyattck
@@ -67,7 +70,7 @@ pip install pyattck
 ### Installing from source
 
 ```bash
-git clone git@github.com:swimlane/pyattck.git
+git clone https://github.com/swimlane/pyattck.git
 cd pyattck
 python setup.py install
 ```
@@ -82,7 +85,7 @@ from pyattck import Attck
 attack = Attck()
 ```
 
-By default, `subtechniques` are accessible under each technique object.  You can turn this behavior off by passing `nested_subtechniques=False` when creating your `Attck` object.
+By default, `subtechniques` are accessible under each technique object. You can turn this behavior off by passing `nested_subtechniques=False` when creating your `Attck` object.
 
 As an example, the default behavior looks like the following example:
 
@@ -138,6 +141,51 @@ Here are the accessible objects under the [Mobile](docs/mobile/mobileattck.md) p
 
 For more information on object types under the `mobile` property, see [Mobile](docs/mobile/mobileattck.md).
 
+## Configuration
+
+`pyattck` allows you to configure if you store external data and where it is stored. 
+
+```python
+from pyattck import Attck
+
+attck = Attck(
+    nested_subtechniques=True,
+    use_config=False,
+    save_config=False,
+    config_file_path='~/pyattck/config.yml',
+    data_path='~/pyattck/data',
+    enterprise_attck_json="https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json",
+    pre_attck_json="https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json",
+    mobile_attck_json="https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json",
+    nist_controls_json="https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json",
+    generated_attck_json="https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True",
+    generated_nist_json="https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True",
+    **kwargs
+)
+```
+
+By default, `pyattck` will (now) pull the latest external data from their respective locations using HTTP GET requests. `pyattck` currently pulls from the following locations:
+
+* enterprise_attck_json="https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json"
+* pre_attck_json="https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json"
+* mobile_attck_json="https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json"
+* nist_controls_json="https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json"
+* generated_attck_json="https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True"
+* generated_nist_json="https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True"
+
+You have several options when instantiating the `Attck` object. As of `4.0.0` you can now specify any of the following options:
+
+* use_config - When you specify this argument as `True` pyattck will attempt to retrieve the configuration specified in the `config_file_path` location. If this file is corrupted or cannot be found, we will default to retrieving data from the specified `*_attck_json` locations.
+* save_config - When you specify this argument as `True` pyattck will save the configuration file to the specified location set by `config_file_path`. Additionally, we will save all downloaded files to the `data_path` location specified. If you have specified a local path location instead of a download URL for any of the `*_attck_json` parameters we will save this location in our configuration and reference this location going forward. 
+* config_file_path - The path to store a configuration file. Default is `~/pyattck/config.yml`
+* data_path - The path to store any data files downloaded to the local system. Default is `~/pyattck/data`
+
+### JSON Locations
+
+Additionally, you can specify the location for each individual `*_attck_json` files by passing in either a URI or a local file path. If you have passed in a local file path, we will simply read from this file. 
+
+If you have used the default values or specified an alternative URI location to retrieve these JSON files from, you can additionally pass in `**kwargs` that will be passed along to the `Requests` python package when performing any HTTP requests.
+
 ## Note
 
 We understand that there are many different open-source projects being released, even on a daily basis, but we wanted to provide a straightforward Python package that allowed the user to identify known relationships between all verticals of the MITRE ATT&CK Framework.
@@ -224,14 +272,14 @@ This data set is generated from many different sources. As we continue to add mo
 * [Attack-Technique-Dataset](https://github.com/NewBee119/Attack-Technique-Dataset)
 
 
-
 ```eval_rst
 .. toctree::
    :titlesonly:
 
+   configuration
    pyattck/attck
    dataset/dataset
    enterprise/enterprise
    preattck/preattck
    mobile/mobileattck
-```
\ No newline at end of file
+```
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 00000000..9919ba39
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,53 @@
+# Configuration
+
+`pyattck` allows you to configure if you store external data and as well as where it is stored. Below shows all available parameters when instantiating the `Attck` object.
+
+```python
+from pyattck import Attck
+
+attck = Attck(
+    nested_subtechniques=True,
+    use_config=False,
+    save_config=False,
+    config_file_path='~/pyattck/config.yml',
+    data_path='~/pyattck/data',
+    enterprise_attck_json="https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json",
+    pre_attck_json="https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json",
+    mobile_attck_json="https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json",
+    nist_controls_json="https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json",
+    generated_attck_json="https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True",
+    generated_nist_json="https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True",
+    **kwargs
+)
+```
+
+By default, `pyattck` will pull the latest external data from their respective locations using HTTP GET requests. `pyattck` currently pulls from the following locations:
+
+* enterprise_attck_json="https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json"
+* pre_attck_json="https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json"
+* mobile_attck_json="https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json"
+* nist_controls_json="https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json"
+* generated_attck_json="https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True"
+* generated_nist_json="https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True"
+
+You have several options when instantiating the `Attck` object. As of `4.0.0` you can now specify any of the following options:
+
+* use_config - When you specify this argument as `True` pyattck will attempt to retrieve the configuration specified in the `config_file_path` location. If this file is corrupted or cannot be found, we will default to retrieving data from the specified `*_attck_json` locations.
+* save_config - When you specify this argument as `True` pyattck will save the configuration file to the specified location set by `config_file_path`. Additionally, we will save all downloaded files to the `data_path` location specified. If you have specified a local path location instead of a download URL for any of the `*_attck_json` parameters we will save this location in our configuration and reference this location going forward. 
+* config_file_path - The path to store a configuration file. Default is `~/pyattck/config.yml`
+* data_path - The path to store any data files downloaded to the local system. Default is `~/pyattck/data`
+
+### JSON Locations
+
+Additionally, you can specify the location for each individual `*_attck_json` files by passing in either a URI or a local file path. If you have passed in a local file path, we will simply read from this file. 
+
+If you have used the default values or specified an alternative URI location to retrieve these JSON files from, you can additionally pass in `**kwargs` that will be passed along to the `Requests` python package when performing any HTTP requests.
+
+## Configuration Class
+
+```eval_rst
+.. autoclass:: pyattck.configuration.Configuration
+   :members:
+   :undoc-members:
+   :show-inheritance:
+```
\ No newline at end of file
diff --git a/docs/enterprise/technique.md b/docs/enterprise/technique.md
index ba94afaf..1b639f51 100644
--- a/docs/enterprise/technique.md
+++ b/docs/enterprise/technique.md
@@ -15,6 +15,7 @@ Each technique enables you to access the following properties on the object:
 * queries = A list of dictionary objects containing product, query, and name associated with a technique
 * datasets = A list of raw datasets associated with a technique
 * possible_detections = A list of raw datasets containing possible detection methods for a technique
+* data_sources = A list of raw datasets containing data sources listed for the technique
 
 
 ## AttckTechnique Class
diff --git a/docs/index.md b/docs/index.md
index 066714be..155bd83b 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -2,6 +2,7 @@
 ![](images/ubuntu_support.svg)
 ![](images/macos_support.svg)
 ![](images/windows_support.svg)
+![](images/code_coverage.svg)
 
 # Welcome to pyattck's Documentation
 
@@ -16,6 +17,7 @@
 ```
 	A Python package to interact with MITRE ATT&CK Frameworks
 
+> Current Version is 4.0.0
 
 **pyattck** is a light-weight framework for MITRE ATT&CK Frameworks. This package extracts details from the MITRE Enterprise, PRE-ATT&CK, and Mobile Frameworks.
 
@@ -27,8 +29,7 @@ In addition, Techniques, Actors, and Tools (if applicable) now have collected da
 
 The **pyattck** package allows you to:
 
-  * Update or sync the external datasets by calling the `update()` method on an `Attck` object. By default it checks for updates every 30 days.
-  * Specify a local file path for the MITRE ATT&CK Enterprise Framework json, generated dataset, and/or a config.yml file.
+  * Specify a URL or local file path for the MITRE ATT&CK Enterprise Framework json, generated dataset, and/or a config.yml file.
   * Retrieve an image_logo of an actor (when available). If an image_logo isn't available, it generates an ascii_logo.
   * Search the external dataset for external commands that are similar using `search_commands`.
   * Access data from the MITRE PRE-ATT&CK Framework
@@ -36,6 +37,13 @@ The **pyattck** package allows you to:
   * Access subtechniques as nested objects or you can turn it off and access as normal technique
   * Access compliance controls (currently NIST 800-53) related to a MITRE ATT&CK Technique
 
+# Table of Contents
+
+1. [Installation](#installation)
+2. [Usage Example](#usage-example)
+3. [Configuration](#configuration)
+4. [Notes](#note)
+
 ## Installation
 
 You can install **pyattack** on OS X, Linux, or Windows. You can also install it directly from the source. To install, see the commands under the relevant operating system heading, below.
@@ -48,18 +56,12 @@ The following libraries are required and installed by pyattck:
 requests
 pendulum>=1.2.3,<1.3
 pyfiglet==0.8.post1
-PyYaml>=5.0
-Pillow==7.1.2
+PyYaml>=5.4.1
+Pillow>=6.2.2
 fire==0.3.1
 ```
 
-### OS X & Linux:
-
-```bash
-pip install pyattck
-```
-
-### Windows:
+### macOS, Linux and Windows:
 
 ```bash
 pip install pyattck
@@ -68,7 +70,7 @@ pip install pyattck
 ### Installing from source
 
 ```bash
-git clone git@github.com:swimlane/pyattck.git
+git clone https://github.com/swimlane/pyattck.git
 cd pyattck
 python setup.py install
 ```
@@ -83,7 +85,7 @@ from pyattck import Attck
 attack = Attck()
 ```
 
-By default, `subtechniques` are accessible under each technique object.  You can turn this behavior off by passing `nested_subtechniques=False` when creating your `Attck` object.
+By default, `subtechniques` are accessible under each technique object. You can turn this behavior off by passing `nested_subtechniques=False` when creating your `Attck` object.
 
 As an example, the default behavior looks like the following example:
 
@@ -139,6 +141,51 @@ Here are the accessible objects under the [Mobile](mobile/mobileattck.md) proper
 
 For more information on object types under the `mobile` property, see [Mobile](mobile/mobileattck.md).
 
+## Configuration
+
+`pyattck` allows you to configure if you store external data and where it is stored. 
+
+```python
+from pyattck import Attck
+
+attck = Attck(
+    nested_subtechniques=True,
+    use_config=False,
+    save_config=False,
+    config_file_path='~/pyattck/config.yml',
+    data_path='~/pyattck/data',
+    enterprise_attck_json="https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json",
+    pre_attck_json="https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json",
+    mobile_attck_json="https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json",
+    nist_controls_json="https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json",
+    generated_attck_json="https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True",
+    generated_nist_json="https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True",
+    **kwargs
+)
+```
+
+By default, `pyattck` will (now) pull the latest external data from their respective locations using HTTP GET requests. `pyattck` currently pulls from the following locations:
+
+* enterprise_attck_json="https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json"
+* pre_attck_json="https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json"
+* mobile_attck_json="https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json"
+* nist_controls_json="https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json"
+* generated_attck_json="https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True"
+* generated_nist_json="https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True"
+
+You have several options when instantiating the `Attck` object. As of `4.0.0` you can now specify any of the following options:
+
+* use_config - When you specify this argument as `True` pyattck will attempt to retrieve the configuration specified in the `config_file_path` location. If this file is corrupted or cannot be found, we will default to retrieving data from the specified `*_attck_json` locations.
+* save_config - When you specify this argument as `True` pyattck will save the configuration file to the specified location set by `config_file_path`. Additionally, we will save all downloaded files to the `data_path` location specified. If you have specified a local path location instead of a download URL for any of the `*_attck_json` parameters we will save this location in our configuration and reference this location going forward. 
+* config_file_path - The path to store a configuration file. Default is `~/pyattck/config.yml`
+* data_path - The path to store any data files downloaded to the local system. Default is `~/pyattck/data`
+
+### JSON Locations
+
+Additionally, you can specify the location for each individual `*_attck_json` files by passing in either a URI or a local file path. If you have passed in a local file path, we will simply read from this file. 
+
+If you have used the default values or specified an alternative URI location to retrieve these JSON files from, you can additionally pass in `**kwargs` that will be passed along to the `Requests` python package when performing any HTTP requests.
+
 ## Note
 
 We understand that there are many different open-source projects being released, even on a daily basis, but we wanted to provide a straightforward Python package that allowed the user to identify known relationships between all verticals of the MITRE ATT&CK Framework.
@@ -229,6 +276,7 @@ This data set is generated from many different sources. As we continue to add mo
 .. toctree::
    :titlesonly:
 
+   configuration
    pyattck/attck
    dataset/dataset
    enterprise/enterprise
diff --git a/generateattcks/generateattcks/atomicredteam.py b/generateattcks/generateattcks/atomicredteam.py
index 025e909f..43d262e3 100644
--- a/generateattcks/generateattcks/atomicredteam.py
+++ b/generateattcks/generateattcks/atomicredteam.py
@@ -1,5 +1,4 @@
-import requests, yaml, base64
-from github import Github
+import requests, yaml
 
 from .githubcontroller import GitHubController
 from .attacktemplate import AttackTemplate
@@ -39,33 +38,32 @@ def get(self):
         return return_list
                         
     def __parse_yaml_content(self, content, url):
-        template = AttackTemplate()
-       # print(content)
-        for test in content['atomic_tests']:
-            if 'executor' in test:
-                if 'command' in test['executor']:
-                    if 'input_arguments' in test:
-                        self.temp_command_string = None
-                        for key,val in test['input_arguments'].items():
-                            replacement_string = '#{{{0}}}'.format(key)
-                            if self.temp_command_string is None:
-                                try:
-                                    self.temp_command_string = test['executor']['command'].replace(replacement_string, test['input_arguments'][key]['default'])
-                                except:
-                                    pass
-                            else:
-                                try:
-                                    self.temp_command_string = self.temp_command_string.replace(replacement_string, test['input_arguments'][key]['default'])
-                                except:
-                                    pass
-                            template.add_command(url,self.temp_command_string)
+        if 'atomic_tests' in content:
+            template = AttackTemplate()
+            for test in content['atomic_tests']:
+                if 'executor' in test:
+                    if 'command' in test['executor']:
+                        if 'input_arguments' in test:
                             self.temp_command_string = None
-                    else:
-                        template.add_command(url,test['executor']['command'])
-        template.id = content['attack_technique']
-        template.add_dataset('Atomic Red Team Test - {name}'.format(name=content['display_name']), content)
-        return template.get()      
-        
+                            for key,val in test['input_arguments'].items():
+                                replacement_string = '#{{{0}}}'.format(key)
+                                if self.temp_command_string is None:
+                                    try:
+                                        self.temp_command_string = test['executor']['command'].replace(replacement_string, test['input_arguments'][key]['default'])
+                                    except:
+                                        pass
+                                else:
+                                    try:
+                                        self.temp_command_string = self.temp_command_string.replace(replacement_string, test['input_arguments'][key]['default'])
+                                    except:
+                                        pass
+                                template.add_command(url,self.temp_command_string)
+                                self.temp_command_string = None
+                        else:
+                            template.add_command(url,test['executor']['command'])
+            template.id = content['attack_technique']
+            template.add_dataset('Atomic Red Team Test - {name}'.format(name=content['display_name']), content)
+            return template.get()
 
     def __download_raw_content(self, url):
         response = self.session.get(url)
diff --git a/generateattcks/generateattcks/attckdatasources.py b/generateattcks/generateattcks/attckdatasources.py
new file mode 100644
index 00000000..8823927d
--- /dev/null
+++ b/generateattcks/generateattcks/attckdatasources.py
@@ -0,0 +1,60 @@
+import requests
+import yaml
+from .githubcontroller import GitHubController
+from .attacktemplate import AttackTemplate
+from .base import Base
+
+
+class AttckDatasources(GitHubController, Base):
+
+    """
+    Data Source: https://github.com/mitre-attack/attack-datasources
+    Author: MITRE ATT&CK
+
+    This class is a wrapper for the above data set
+    """
+
+    __RAW_URL = 'https://raw.githubusercontent.com/mitre-attack/attack-datasources/main/{}'
+    __REPO = 'mitre-attack/attack-datasources'
+
+    def __init__(self):
+        super(AttckDatasources, self).__init__()
+        self.session = requests.Session()
+        self._dataset = []
+
+    def get(self):
+        repo = self.github.get_repo(self.__REPO)
+        contents = repo.get_contents("")
+        while contents:
+            file_content = contents.pop(0)
+            if file_content.type == "dir":
+                contents.extend(repo.get_contents(file_content.path))
+            else:
+                if file_content.path.endswith('techniques_to_relationships_mapping.yaml'):
+                    content = self.__download_raw_content(self.__RAW_URL.format(file_content.path))
+                    return self.__parse_yaml_content(content, file_content.path)
+
+    def __parse_yaml_content(self, content, url):
+        return_list = []
+        for item in content:
+            template = AttackTemplate()
+            template.id = item.get('technique_id')
+            template.add_external_reference(item.get('references'))
+            template.add_detection_data_sources({
+                'data_source': item.get('data_source'),
+                'defintion': item.get('defintion'),
+                'collection_layers': item.get('collection_layers'),
+                'data_source_platform': item.get('data_source_platform'),
+                'data_component': item.get('data_component'),
+                'description': item.get('description'),
+                'source_data_element': item.get('source_data_element'),
+                'relationship': item.get('relationship'),
+                'target_data_element': item.get('target_data_element')
+            })
+            return_list.append(template.get())
+        return return_list
+
+    def __download_raw_content(self, url):
+        response = self.session.get(url)
+        if response.status_code == 200:
+            return yaml.load(response.content, Loader=yaml.FullLoader)
diff --git a/generateattcks/generateattcks/c2matrix.py b/generateattcks/generateattcks/c2matrix.py
index aa255a71..b105cb38 100644
--- a/generateattcks/generateattcks/c2matrix.py
+++ b/generateattcks/generateattcks/c2matrix.py
@@ -23,7 +23,7 @@ def get(self):
         response = requests.get(self._URL)
         data = response.text
         return self._parse(data)
-        
+
     def _parse(self, data):
         count = 0
         headers = None
diff --git a/generateattcks/generateattcks/generateattcks.py b/generateattcks/generateattcks/generateattcks.py
index 5095e7c5..5fcfc139 100644
--- a/generateattcks/generateattcks/generateattcks.py
+++ b/generateattcks/generateattcks/generateattcks.py
@@ -18,6 +18,7 @@
 from .elemental import ElementalAttack
 from .malwarearchaeology import MalwareArchaeology
 from .newbeeattackdata import NewBeeAttackDataset
+from .attckdatasources import AttckDatasources
 
 
 class GenerateAttcks(object):
@@ -58,6 +59,7 @@ def get(self):
         self.add_elemental_attack()
         self.add_malware_archaeology()
         self.add_new_bee_attack_data()
+        self.add_attck_datasources()
         technique_list = []
         for technique in self._datasets['techniques']:
             if self.conversion_data.get(technique['technique_id']):
@@ -72,6 +74,10 @@ def get(self):
         self._datasets['techniques'] = technique_list
         return self._datasets
 
+    def add_attck_datasources(self):
+        for item in AttckDatasources().get():
+            self.__add_to_output(item)
+
     def add_new_bee_attack_data(self):
         for item in NewBeeAttackDataset().get():
             self.__add_to_output(item)
@@ -112,7 +118,8 @@ def add_adversary_emulation(self):
 
     def add_atomic_red_team(self):
         for item in AtomicRedTeam().get():
-            self.__add_to_output(item)
+            if item:
+                self.__add_to_output(item)
 
     def add_mitre_stockpile(self):
         stockpile = MitreStockpile()
diff --git a/generated_attck_data.json b/generated_attck_data.json
index c317eac9..fa6593c5 100644
--- a/generated_attck_data.json
+++ b/generated_attck_data.json
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:3a9f1eadac91af1b4228b3d50ba48bac6d5a027e1a4e7d084b197155a9e94d3b
-size 27424874
+oid sha256:5e2aa74af78739ddec23bf2bf76042f76563887450ba6518078057201f2a74fb
+size 31303971
diff --git a/images/code_coverage.svg b/images/code_coverage.svg
new file mode 100644
index 00000000..59d64b37
--- /dev/null
+++ b/images/code_coverage.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20">
+    <linearGradient id="b" x2="0" y2="100%">
+        <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
+        <stop offset="1" stop-opacity=".1"/>
+    </linearGradient>
+    <mask id="a">
+        <rect width="99" height="20" rx="3" fill="#fff"/>
+    </mask>
+    <g mask="url(#a)">
+        <path fill="#555" d="M0 0h63v20H0z"/>
+        <path fill="#97CA00" d="M63 0h36v20H63z"/>
+        <path fill="url(#b)" d="M0 0h99v20H0z"/>
+    </g>
+    <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
+        <text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
+        <text x="31.5" y="14">coverage</text>
+        <text x="80" y="15" fill="#010101" fill-opacity=".3">93%</text>
+        <text x="80" y="14">93%</text>
+    </g>
+</svg>
diff --git a/images/macos_support.svg b/images/macos_support.svg
index f3fa93a6..9085224b 100644
--- a/images/macos_support.svg
+++ b/images/macos_support.svg
@@ -1,1 +1,1 @@
-<svg height="20" width="241.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="round"><rect fill="#fff" height="20" rx="3" width="241.1"/></clipPath><g clip-path="url(#round)"><rect fill="#555" height="20" width="115.6"/><rect fill="#007ec6" height="20" width="125.5" x="115.6"/><rect fill="url(#smooth)" height="20" width="241.1"/></g><g fill="#fff" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110" text-anchor="middle"><image height="14" width="14" x="5" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0icHlZZWxsb3ciIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2ZlNSIgb2Zmc2V0PSIwLjYiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2RhMSIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJweUJsdWUiIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzY5ZiIgb2Zmc2V0PSIwLjQiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzQ2OCIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KCiAgPHBhdGggZD0iTTI3LDE2YzAtNyw5LTEzLDI0LTEzYzE1LDAsMjMsNiwyMywxM2wwLDIyYzAsNy01LDEyLTExLDEybC0yNCwwYy04LDAtMTQsNi0xNCwxNWwwLDEwbC05LDBjLTgsMC0xMy05LTEzLTI0YzAtMTQsNS0yMywxMy0yM2wzNSwwbDAtM2wtMjQsMGwwLTlsMCwweiBNODgsNTB2MSIgZmlsbD0idXJsKCNweUJsdWUpIi8+CiAgPHBhdGggZD0iTTc0LDg3YzAsNy04LDEzLTIzLDEzYy0xNSwwLTI0LTYtMjQtMTNsMC0yMmMwLTcsNi0xMiwxMi0xMmwyNCwwYzgsMCwxNC03LDE0LTE1bDAtMTBsOSwwYzcsMCwxMyw5LDEzLDIzYzAsMTUtNiwyNC0xMywyNGwtMzUsMGwwLDNsMjMsMGwwLDlsMCwweiBNMTQwLDUwdjEiIGZpbGw9InVybCgjcHlZZWxsb3cpIi8+CgogIDxjaXJjbGUgcj0iNCIgY3g9IjY0IiBjeT0iODgiIGZpbGw9IiNGRkYiLz4KICA8Y2lyY2xlIHI9IjQiIGN4PSIzNyIgY3k9IjE1IiBmaWxsPSIjRkZGIi8+Cjwvc3ZnPgo=" y="3"/><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="886.0" transform="scale(0.1)" x="673.0" y="150">python - macOS</text><text lengthAdjust="spacing" textLength="886.0" transform="scale(0.1)" x="673.0" y="140">python - macOS</text><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1773.5" y="150">2.7, 3.5, 3.6, 3.7, 3.8</text><text lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1773.5" y="140">2.7, 3.5, 3.6, 3.7, 3.8</text><a xlink:href=""><rect fill="rgba(0,0,0,0)" height="20" width="115.6"/></a><a xlink:href="https://github.com/swimlane/pyattck/actions?query=workflow%3A%22Testing+pyattck%22"><rect fill="rgba(0,0,0,0)" height="20" width="125.5" x="115.6"/></a></g></svg>
\ No newline at end of file
+<svg height="20" width="241.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="round"><rect fill="#fff" height="20" rx="3" width="241.1"/></clipPath><g clip-path="url(#round)"><rect fill="#555" height="20" width="115.6"/><rect fill="#007ec6" height="20" width="125.5" x="115.6"/><rect fill="url(#smooth)" height="20" width="241.1"/></g><g fill="#fff" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110" text-anchor="middle"><image height="14" width="14" x="5" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0icHlZZWxsb3ciIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2ZlNSIgb2Zmc2V0PSIwLjYiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2RhMSIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJweUJsdWUiIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzY5ZiIgb2Zmc2V0PSIwLjQiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzQ2OCIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KCiAgPHBhdGggZD0iTTI3LDE2YzAtNyw5LTEzLDI0LTEzYzE1LDAsMjMsNiwyMywxM2wwLDIyYzAsNy01LDEyLTExLDEybC0yNCwwYy04LDAtMTQsNi0xNCwxNWwwLDEwbC05LDBjLTgsMC0xMy05LTEzLTI0YzAtMTQsNS0yMywxMy0yM2wzNSwwbDAtM2wtMjQsMGwwLTlsMCwweiBNODgsNTB2MSIgZmlsbD0idXJsKCNweUJsdWUpIi8+CiAgPHBhdGggZD0iTTc0LDg3YzAsNy04LDEzLTIzLDEzYy0xNSwwLTI0LTYtMjQtMTNsMC0yMmMwLTcsNi0xMiwxMi0xMmwyNCwwYzgsMCwxNC03LDE0LTE1bDAtMTBsOSwwYzcsMCwxMyw5LDEzLDIzYzAsMTUtNiwyNC0xMywyNGwtMzUsMGwwLDNsMjMsMGwwLDlsMCwweiBNMTQwLDUwdjEiIGZpbGw9InVybCgjcHlZZWxsb3cpIi8+CgogIDxjaXJjbGUgcj0iNCIgY3g9IjY0IiBjeT0iODgiIGZpbGw9IiNGRkYiLz4KICA8Y2lyY2xlIHI9IjQiIGN4PSIzNyIgY3k9IjE1IiBmaWxsPSIjRkZGIi8+Cjwvc3ZnPgo=" y="3"/><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="886.0" transform="scale(0.1)" x="673.0" y="150">python - macOS</text><text lengthAdjust="spacing" textLength="886.0" transform="scale(0.1)" x="673.0" y="140">python - macOS</text><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1773.5" y="150">3.6, 3.7, 3.8, 3.9</text><text lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1773.5" y="140">3.6, 3.7, 3.8, 3.9</text><a xlink:href=""><rect fill="rgba(0,0,0,0)" height="20" width="115.6"/></a><a xlink:href="https://github.com/swimlane/pyattck/actions?query=workflow%3A%22Testing+pyattck%22"><rect fill="rgba(0,0,0,0)" height="20" width="125.5" x="115.6"/></a></g></svg>
\ No newline at end of file
diff --git a/images/ubuntu_support.svg b/images/ubuntu_support.svg
index 2e973447..9ae2ad86 100644
--- a/images/ubuntu_support.svg
+++ b/images/ubuntu_support.svg
@@ -1,1 +1,1 @@
-<svg height="20" width="242.4" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="round"><rect fill="#fff" height="20" rx="3" width="242.4"/></clipPath><g clip-path="url(#round)"><rect fill="#555" height="20" width="116.9"/><rect fill="#007ec6" height="20" width="125.5" x="116.9"/><rect fill="url(#smooth)" height="20" width="242.4"/></g><g fill="#fff" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110" text-anchor="middle"><image height="14" width="14" x="5" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0icHlZZWxsb3ciIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2ZlNSIgb2Zmc2V0PSIwLjYiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2RhMSIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJweUJsdWUiIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzY5ZiIgb2Zmc2V0PSIwLjQiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzQ2OCIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KCiAgPHBhdGggZD0iTTI3LDE2YzAtNyw5LTEzLDI0LTEzYzE1LDAsMjMsNiwyMywxM2wwLDIyYzAsNy01LDEyLTExLDEybC0yNCwwYy04LDAtMTQsNi0xNCwxNWwwLDEwbC05LDBjLTgsMC0xMy05LTEzLTI0YzAtMTQsNS0yMywxMy0yM2wzNSwwbDAtM2wtMjQsMGwwLTlsMCwweiBNODgsNTB2MSIgZmlsbD0idXJsKCNweUJsdWUpIi8+CiAgPHBhdGggZD0iTTc0LDg3YzAsNy04LDEzLTIzLDEzYy0xNSwwLTI0LTYtMjQtMTNsMC0yMmMwLTcsNi0xMiwxMi0xMmwyNCwwYzgsMCwxNC03LDE0LTE1bDAtMTBsOSwwYzcsMCwxMyw5LDEzLDIzYzAsMTUtNiwyNC0xMywyNGwtMzUsMGwwLDNsMjMsMGwwLDlsMCwweiBNMTQwLDUwdjEiIGZpbGw9InVybCgjcHlZZWxsb3cpIi8+CgogIDxjaXJjbGUgcj0iNCIgY3g9IjY0IiBjeT0iODgiIGZpbGw9IiNGRkYiLz4KICA8Y2lyY2xlIHI9IjQiIGN4PSIzNyIgY3k9IjE1IiBmaWxsPSIjRkZGIi8+Cjwvc3ZnPgo=" y="3"/><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="899.0" transform="scale(0.1)" x="679.5" y="150">python - Ubuntu</text><text lengthAdjust="spacing" textLength="899.0" transform="scale(0.1)" x="679.5" y="140">python - Ubuntu</text><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1786.5" y="150">2.7, 3.5, 3.6, 3.7, 3.8</text><text lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1786.5" y="140">2.7, 3.5, 3.6, 3.7, 3.8</text><a xlink:href=""><rect fill="rgba(0,0,0,0)" height="20" width="116.9"/></a><a xlink:href="https://github.com/swimlane/pyattck/actions?query=workflow%3A%22Testing+pyattck%22"><rect fill="rgba(0,0,0,0)" height="20" width="125.5" x="116.9"/></a></g></svg>
\ No newline at end of file
+<svg height="20" width="242.4" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="round"><rect fill="#fff" height="20" rx="3" width="242.4"/></clipPath><g clip-path="url(#round)"><rect fill="#555" height="20" width="116.9"/><rect fill="#007ec6" height="20" width="125.5" x="116.9"/><rect fill="url(#smooth)" height="20" width="242.4"/></g><g fill="#fff" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110" text-anchor="middle"><image height="14" width="14" x="5" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0icHlZZWxsb3ciIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2ZlNSIgb2Zmc2V0PSIwLjYiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2RhMSIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJweUJsdWUiIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzY5ZiIgb2Zmc2V0PSIwLjQiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzQ2OCIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KCiAgPHBhdGggZD0iTTI3LDE2YzAtNyw5LTEzLDI0LTEzYzE1LDAsMjMsNiwyMywxM2wwLDIyYzAsNy01LDEyLTExLDEybC0yNCwwYy04LDAtMTQsNi0xNCwxNWwwLDEwbC05LDBjLTgsMC0xMy05LTEzLTI0YzAtMTQsNS0yMywxMy0yM2wzNSwwbDAtM2wtMjQsMGwwLTlsMCwweiBNODgsNTB2MSIgZmlsbD0idXJsKCNweUJsdWUpIi8+CiAgPHBhdGggZD0iTTc0LDg3YzAsNy04LDEzLTIzLDEzYy0xNSwwLTI0LTYtMjQtMTNsMC0yMmMwLTcsNi0xMiwxMi0xMmwyNCwwYzgsMCwxNC03LDE0LTE1bDAtMTBsOSwwYzcsMCwxMyw5LDEzLDIzYzAsMTUtNiwyNC0xMywyNGwtMzUsMGwwLDNsMjMsMGwwLDlsMCwweiBNMTQwLDUwdjEiIGZpbGw9InVybCgjcHlZZWxsb3cpIi8+CgogIDxjaXJjbGUgcj0iNCIgY3g9IjY0IiBjeT0iODgiIGZpbGw9IiNGRkYiLz4KICA8Y2lyY2xlIHI9IjQiIGN4PSIzNyIgY3k9IjE1IiBmaWxsPSIjRkZGIi8+Cjwvc3ZnPgo=" y="3"/><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="899.0" transform="scale(0.1)" x="679.5" y="150">python - Ubuntu</text><text lengthAdjust="spacing" textLength="899.0" transform="scale(0.1)" x="679.5" y="140">python - Ubuntu</text><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1786.5" y="150">3.6, 3.7, 3.8, 3.9</text><text lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1786.5" y="140">3.6, 3.7, 3.8, 3.9</text><a xlink:href=""><rect fill="rgba(0,0,0,0)" height="20" width="116.9"/></a><a xlink:href="https://github.com/swimlane/pyattck/actions?query=workflow%3A%22Testing+pyattck%22"><rect fill="rgba(0,0,0,0)" height="20" width="125.5" x="116.9"/></a></g></svg>
\ No newline at end of file
diff --git a/images/windows_support.svg b/images/windows_support.svg
index 5c3038d9..ea29e032 100644
--- a/images/windows_support.svg
+++ b/images/windows_support.svg
@@ -1,1 +1,1 @@
-<svg height="20" width="251.4" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="round"><rect fill="#fff" height="20" rx="3" width="251.4"/></clipPath><g clip-path="url(#round)"><rect fill="#555" height="20" width="125.9"/><rect fill="#007ec6" height="20" width="125.5" x="125.9"/><rect fill="url(#smooth)" height="20" width="251.4"/></g><g fill="#fff" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110" text-anchor="middle"><image height="14" width="14" x="5" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0icHlZZWxsb3ciIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2ZlNSIgb2Zmc2V0PSIwLjYiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2RhMSIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJweUJsdWUiIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzY5ZiIgb2Zmc2V0PSIwLjQiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzQ2OCIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KCiAgPHBhdGggZD0iTTI3LDE2YzAtNyw5LTEzLDI0LTEzYzE1LDAsMjMsNiwyMywxM2wwLDIyYzAsNy01LDEyLTExLDEybC0yNCwwYy04LDAtMTQsNi0xNCwxNWwwLDEwbC05LDBjLTgsMC0xMy05LTEzLTI0YzAtMTQsNS0yMywxMy0yM2wzNSwwbDAtM2wtMjQsMGwwLTlsMCwweiBNODgsNTB2MSIgZmlsbD0idXJsKCNweUJsdWUpIi8+CiAgPHBhdGggZD0iTTc0LDg3YzAsNy04LDEzLTIzLDEzYy0xNSwwLTI0LTYtMjQtMTNsMC0yMmMwLTcsNi0xMiwxMi0xMmwyNCwwYzgsMCwxNC03LDE0LTE1bDAtMTBsOSwwYzcsMCwxMyw5LDEzLDIzYzAsMTUtNiwyNC0xMywyNGwtMzUsMGwwLDNsMjMsMGwwLDlsMCwweiBNMTQwLDUwdjEiIGZpbGw9InVybCgjcHlZZWxsb3cpIi8+CgogIDxjaXJjbGUgcj0iNCIgY3g9IjY0IiBjeT0iODgiIGZpbGw9IiNGRkYiLz4KICA8Y2lyY2xlIHI9IjQiIGN4PSIzNyIgY3k9IjE1IiBmaWxsPSIjRkZGIi8+Cjwvc3ZnPgo=" y="3"/><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="989.0" transform="scale(0.1)" x="724.5" y="150">python - Windows</text><text lengthAdjust="spacing" textLength="989.0" transform="scale(0.1)" x="724.5" y="140">python - Windows</text><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1876.5" y="150">2.7, 3.5, 3.6, 3.7, 3.8</text><text lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1876.5" y="140">2.7, 3.5, 3.6, 3.7, 3.8</text><a xlink:href=""><rect fill="rgba(0,0,0,0)" height="20" width="125.9"/></a><a xlink:href="https://github.com/swimlane/pyattck/actions?query=workflow%3A%22Testing+pyattck%22"><rect fill="rgba(0,0,0,0)" height="20" width="125.5" x="125.9"/></a></g></svg>
\ No newline at end of file
+<svg height="20" width="251.4" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="smooth" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="round"><rect fill="#fff" height="20" rx="3" width="251.4"/></clipPath><g clip-path="url(#round)"><rect fill="#555" height="20" width="125.9"/><rect fill="#007ec6" height="20" width="125.5" x="125.9"/><rect fill="url(#smooth)" height="20" width="251.4"/></g><g fill="#fff" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110" text-anchor="middle"><image height="14" width="14" x="5" xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0icHlZZWxsb3ciIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2ZlNSIgb2Zmc2V0PSIwLjYiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iI2RhMSIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJweUJsdWUiIGdyYWRpZW50VHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzY5ZiIgb2Zmc2V0PSIwLjQiLz4KICAgICAgPHN0b3Agc3RvcC1jb2xvcj0iIzQ2OCIgb2Zmc2V0PSIxIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogIDwvZGVmcz4KCiAgPHBhdGggZD0iTTI3LDE2YzAtNyw5LTEzLDI0LTEzYzE1LDAsMjMsNiwyMywxM2wwLDIyYzAsNy01LDEyLTExLDEybC0yNCwwYy04LDAtMTQsNi0xNCwxNWwwLDEwbC05LDBjLTgsMC0xMy05LTEzLTI0YzAtMTQsNS0yMywxMy0yM2wzNSwwbDAtM2wtMjQsMGwwLTlsMCwweiBNODgsNTB2MSIgZmlsbD0idXJsKCNweUJsdWUpIi8+CiAgPHBhdGggZD0iTTc0LDg3YzAsNy04LDEzLTIzLDEzYy0xNSwwLTI0LTYtMjQtMTNsMC0yMmMwLTcsNi0xMiwxMi0xMmwyNCwwYzgsMCwxNC03LDE0LTE1bDAtMTBsOSwwYzcsMCwxMyw5LDEzLDIzYzAsMTUtNiwyNC0xMywyNGwtMzUsMGwwLDNsMjMsMGwwLDlsMCwweiBNMTQwLDUwdjEiIGZpbGw9InVybCgjcHlZZWxsb3cpIi8+CgogIDxjaXJjbGUgcj0iNCIgY3g9IjY0IiBjeT0iODgiIGZpbGw9IiNGRkYiLz4KICA8Y2lyY2xlIHI9IjQiIGN4PSIzNyIgY3k9IjE1IiBmaWxsPSIjRkZGIi8+Cjwvc3ZnPgo=" y="3"/><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="989.0" transform="scale(0.1)" x="724.5" y="150">python - Windows</text><text lengthAdjust="spacing" textLength="989.0" transform="scale(0.1)" x="724.5" y="140">python - Windows</text><text fill="#010101" fill-opacity=".3" lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1876.5" y="150">3.6, 3.7, 3.8, 3.9</text><text lengthAdjust="spacing" textLength="1155.0" transform="scale(0.1)" x="1876.5" y="140">3.6, 3.7, 3.8, 3.9</text><a xlink:href=""><rect fill="rgba(0,0,0,0)" height="20" width="125.9"/></a><a xlink:href="https://github.com/swimlane/pyattck/actions?query=workflow%3A%22Testing+pyattck%22"><rect fill="rgba(0,0,0,0)" height="20" width="125.5" x="125.9"/></a></g></svg>
\ No newline at end of file
diff --git a/pyattck/__init__.py b/pyattck/__init__.py
index 00879584..765f503c 100644
--- a/pyattck/__init__.py
+++ b/pyattck/__init__.py
@@ -1,2 +1,3 @@
 from .attck import Attck
-from .version import __version__
\ No newline at end of file
+from .configuration import Configuration
+from .utils.version import __version__
diff --git a/pyattck/attck.py b/pyattck/attck.py
index a28906d5..0e329607 100644
--- a/pyattck/attck.py
+++ b/pyattck/attck.py
@@ -1,5 +1,4 @@
 from .configuration import Configuration
-from .datasets import AttckDatasets
 
 
 class Attck(object):
@@ -125,27 +124,47 @@ class Attck(object):
                        # etc.
 
     Arguments:
-        config_file_path (str, optional): Path to a yaml configuration file which contains two key value pairs. Defaults to None.
-        data_path (str, optional): Path to store the external data locally on your system.  Defaults to current user path.
-        force (bool, optional): Force reset configuration file and paths.  Defaults to False.
+        nested_subtechniques (bool, optional): Whether not to iterate over nested subtechniques. Defaults to True.
+        use_config (bool, optional): Specifies if a configuration file should be used or not.  Defaults to False.
+        save_config (bool, optional): Specifies if pyattck should save a configuration file based on the provided 
+                                      values.  Defaults to False.
+        config_file_path (str, optional): Path to a yaml configuration file which contains two key value pairs. 
+                                          Defaults to '~/pyattck/config.yml'.
+        data_path (str, optional): Path to store the external data locally on your system. Defaults to '~/pyattck/data'.
+        enterprise_attck_json (str, optional): A URL or local file path to the MITRE ATT&CK Json file. 
+                                               Defaults to https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json.
+        pre_attck_json (str, optional): A URL or local file path to the MITRE Pre-ATT&CK Json file. 
+                                        Defaults to https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json.
+        mobile_attck_json (str, optional): A URL or local file path to the MITRE Mobile ATT&CK Json file. 
+                                           Defaults to https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json.
+        nist_controls_json (str, optional): A URL or local file path to the NIST Controls Json file. 
+                                            Defaults to https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json.
+        generated_attck_json (str, optional): A URL or local file path to the Generated MITRE ATT&CK Json file. 
+                                              Defaults to https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True.
+        generated_nist_json (str, optional): A URL or local file path to the Generated NIST Controls Mapping Json file. 
+                                             Defaults to https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True.
+        kwargs (dict, optional): Provided kwargs will be passed to any HTTP requests using the Requests library. 
+                                 Defaults to None.
 
     Returns:
         [Attck]: Returns a Attck object that contains all data from MITRE ATT&CK Frameworks
     """
 
-    __ENTERPRISE_ATTCK_JSON = None
-    __MOBILE_ATTCK_JSON = None
-    __PRE_ATTCK_JSON = None
-    __ENTERPRISE_GENERATED_DATA_JSON = None
-    __ENTERPRISE_NIST_DATA_JSON = None
-    __tactics = None
-    __techniques = None
-    __mitigations = None
-    __actors = None
-    __tools = None
-    __malwares = None
-
-    def __init__(self, nested_subtechniques=True, config_file_path=None, data_path='~'):
+    def __init__(
+        self,
+        nested_subtechniques=True,
+        use_config=False,
+        save_config=False,
+        config_file_path='~/pyattck/config.yml',
+        data_path='~/pyattck/data',
+        enterprise_attck_json="https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json",
+        pre_attck_json="https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json",
+        mobile_attck_json="https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json",
+        nist_controls_json="https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json",
+        generated_attck_json="https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True",
+        generated_nist_json="https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True",
+        **kwargs
+        ):
         """
         The main entry point for pyattck.
 
@@ -198,19 +217,42 @@ def __init__(self, nested_subtechniques=True, config_file_path=None, data_path='
                            The default is your user home path.
 
         Args:
-            config_file_path (str, optional): Path to a yaml configuration
-                                              file which contains two key value pairs.
-                                              Defaults to None.
-            data_path (str, optional): Path to store the external data locally on your
-                                       system.  Defaults to current user path.
-            force (bool, optional): Force reset configuration file and paths.
-                                    Defaults to False.
+            nested_subtechniques (bool, optional): Whether not to iterate over nested subtechniques. Defaults to True.
+            use_config (bool, optional): Specifies if a configuration file should be used or not.  Defaults to False.
+            save_config (bool, optional): Specifies if pyattck should save a configuration file based on the 
+                                          provided values.  Defaults to False.
+            config_file_path (str, optional): Path to a yaml configuration file which contains two key value pairs. 
+                                              Defaults to '~/pyattck/config.yml'.
+            data_path (str, optional): Path to store the external data locally on your system.  
+                                       Defaults to '~/pyattck/data'.
+            enterprise_attck_json (str, optional): A URL or local file path to the MITRE ATT&CK Json file. 
+                                                   Defaults to https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json.
+            pre_attck_json (str, optional): A URL or local file path to the MITRE Pre-ATT&CK Json file. 
+                                            Defaults to https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json.
+            mobile_attck_json (str, optional): A URL or local file path to the MITRE Mobile ATT&CK Json file. 
+                                               Defaults to https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json.
+            nist_controls_json (str, optional): A URL or local file path to the NIST Controls Json file. 
+                                                Defaults to https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json.
+            generated_attck_json (str, optional): A URL or local file path to the Generated MITRE ATT&CK Json file. 
+                                                  Defaults to https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True.
+            generated_nist_json (str, optional): A URL or local file path to the Generated NIST Controls Mapping 
+                                                 Json file. 
+                                                 Defaults to https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True.
+            kwargs (dict, optional): Provided kwargs will be passed to any HTTP requests using the Requests library. 
+                                     Defaults to None.
         """
         self.__nested_subtechniques = nested_subtechniques
-        if config_file_path:
-            Configuration.__CONFIG_FILE = config_file_path
-        Configuration().set(data_path=data_path)
-        self.__datasets = AttckDatasets()
+        Configuration.use_config = use_config
+        Configuration.save_config = save_config
+        Configuration.config_file_path = config_file_path
+        Configuration.enterprise_attck_json = enterprise_attck_json
+        Configuration.pre_attck_json = pre_attck_json
+        Configuration.mobile_attck_json = mobile_attck_json
+        Configuration.nist_controls_json = nist_controls_json
+        Configuration.generated_attck_json = generated_attck_json
+        Configuration.generated_nist_json = generated_nist_json
+        Configuration.data_path = data_path
+        Configuration.requests_kwargs = kwargs
 
     @property
     def enterprise(self):
@@ -221,9 +263,8 @@ def enterprise(self):
         Returns:
             Enterprise: Returns an Enterprise object
         """
-        self.__load_data()
         from .enterprise.enterprise import Enterprise
-        return Enterprise(self.__ENTERPRISE_ATTCK_JSON, nested_subtechniques=self.__nested_subtechniques)
+        return Enterprise(nested_subtechniques=self.__nested_subtechniques)
 
     @property
     def preattack(self):
@@ -233,9 +274,8 @@ def preattack(self):
         Returns:
             PreAttack: Returns an PreAttack object
         """
-        self.__load_data(type='preattack')
         from .preattck.preattck import PreAttck
-        return PreAttck(self.__PRE_ATTCK_JSON)
+        return PreAttck()
 
     @property
     def mobile(self):
@@ -245,43 +285,5 @@ def mobile(self):
         Returns:
             PreAttack: Returns an MobileAttack object
         """
-        self.__load_data(type='mobile')
         from .mobile.mobileattck import MobileAttck
-        return MobileAttck(self.__MOBILE_ATTCK_JSON)
-
-    def update(self, enterprise=False, preattack=False, mobile=False):
-        """
-        Calling this method will force update / sync all datasets from
-        external sources
-        """
-        if preattack:
-            self.__load_data(type='preattack', force=True)
-        if mobile:
-            self.__load_data(type='mobile', force=True)
-        if enterprise:
-            self.__load_data(force=True)
-
-    def __load_data(self, type='enterprise', force=False):
-        if type == 'preattack':
-            if not Attck.__PRE_ATTCK_JSON:
-                Attck.__PRE_ATTCK_JSON = self.__datasets.get_data(
-                    data_type='preattck', force=force
-                )
-        elif type == 'mobile':
-            if not Attck.__MOBILE_ATTCK_JSON:
-                Attck.__MOBILE_ATTCK_JSON = self.__datasets.get_data(
-                    data_type='mobile', force=force
-                )
-        else:
-            if not Attck.__ENTERPRISE_ATTCK_JSON:
-                Attck.__ENTERPRISE_ATTCK_JSON = self.__datasets.get_data(
-                    data_type='enterprise', force=force
-                )
-            if not Attck.__ENTERPRISE_GENERATED_DATA_JSON:
-                Attck.__ENTERPRISE_GENERATED_DATA_JSON = self.__datasets.get_data(
-                    data_type='generated_data', force=force
-                )
-            if not Attck.__ENTERPRISE_NIST_DATA_JSON:
-                Attck.__ENTERPRISE_NIST_DATA_JSON = self.__datasets.get_data(
-                    data_type='nist_data', force=force
-                )
+        return MobileAttck()
diff --git a/pyattck/configuration.py b/pyattck/configuration.py
index 3d0d595a..817e5880 100644
--- a/pyattck/configuration.py
+++ b/pyattck/configuration.py
@@ -1,92 +1,232 @@
-import yaml
 import os
+import json
+import warnings
+from urllib.parse import urlparse
+from pathlib import Path
+import yaml
+from requests.api import request
+from .utils.exceptions import UknownFileError
 
 
-class Configuration(object):
-
-    """Sets and gets configuration details.
-
-    This class will set and get a config.yml file which contains the
-    location of data json files used by pyattck.
-
-    Returns:
-        Configuration: Will return a Configuration instance
-    """
-
-    __CONFIG_FILE = os.path.join(
-        os.path.expanduser('~'), 'pyattck', 'config' + '.yml'
-    )
-    __DATASETS_MAP = {
-        'data_path': None,
-        'enterprise': {
-            'url': 'https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json',
-            'filename': 'enterprise_attck.json',
-        },
-        'preattck': {
-            'url': 'https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json',
-            'filename': 'preattack.json',
-        },
-        'mobile': {
-            'url': 'https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json',
-            'filename': 'mobile_attck.json',
-        },
-        'nist_800_53_rev4_controls': {
-            'url': 'https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json',
-            'filename': 'enterprise_attck_nist_800_53_rev4_controls.json',
-        },
-        'generated_data': {
-            'url': 'https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True',
-            'filename': 'enterprise_attck_dataset.json',
-        },
-        'nist_data': {
-            'url': 'https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True',
-            'filename': 'enterprise_attck_nist_data.json',
-        }
-    }
-
-    def get(self):
-        """
-        Calling the get method will return configuration settings within
-        the config.yml file
-
-        Returns:
-            dict: A dictionary containing configuration settings
-        """
-        if os.path.isfile(self.__CONFIG_FILE):
-            with open(self.__CONFIG_FILE) as f:
-                config = yaml.load(f, Loader=yaml.FullLoader)
-                if not config:
-                    self.set()
-                    return self.get()
-                else:
-                    return config
-        else:
-            self.set()
-            return self.get()
+class ConfigurationProperties(type):
+
+    __config_data = None
 
-    def set(self, data_path=None):
-        """
-        This method will set pyattcks configuration file settings.
+    def __download_url_data(cls, url):
+        return request('GET', url, **cls.requests_kwargs).json()
 
-        If no config.yml is found, it will generate one with default settings
+    def _check_if_path(cls, value):
+        if Path(value):
+            return True
+        return False
 
-        Args:
-            data_path (str, optional): Path to store all external data.
-                                       Defaults to ~/pyattck.
-        """
-        if data_path and data_path != '~':
-            self.__DATASETS_MAP['data_path'] = os.path.abspath(data_path)
+    def _check_if_url(cls, value):
+        try:
+            if urlparse(value).scheme in ['http', 'https']:
+                return True
+            return False
+        except:
+            return False
+
+    def __get_absolute_path(cls, path_string):
+        return os.path.abspath(
+            os.path.expanduser(
+                os.path.expandvars(path_string)
+            )
+        )
+
+    def __validate_value_string(cls, value):
+        if cls._check_if_url(value):
+            return value
+        elif cls._check_if_path(value):
+            return value
         else:
-            self.__DATASETS_MAP['data_path'] = os.path.join(os.path.expanduser('~'), 'pyattck')
+            raise Exception('The provided value is neither a URL or file path')
+
+    def __write_to_disk(cls, path, json_data):
+        with open(path, 'w+') as file_obj:
+            json.dump(json_data, file_obj)
+
+    def __save_data(cls):
+        if not os.path.exists(cls.data_path):
+            try:
+                os.makedirs(cls.data_path)
+            except:
+                raise Exception(
+                    'Unable to save data to the provided location: {}'.format(cls.data_path)
+                )
+        for json_data in ['enterprise_attck_json', 'pre_attck_json', 
+                          'mobile_attck_json', 'nist_controls_json', 
+                          'generated_attck_json', 'generated_nist_json']:
+            if cls._check_if_url(getattr(cls, json_data)):
+                path = os.path.join(cls.data_path, "{json_data}.json".format(json_data=json_data))
+                data = cls.__download_url_data(getattr(cls, json_data))
+                cls.__write_to_disk(path, data)
+
+    def __save_config(cls, path, data):
+        # save configuration to disk
+        if not os.path.exists(os.path.dirname(path)):
+            try:
+                os.makedirs(os.path.dirname(path))
+            except:
+                raise Exception('pyattck attempted to create the provided directories but was unable to: {}'.format(path))
+        with open(path, 'w+') as f:
+            if path.endswith('.json'):
+                json.dump(data, f)
+            elif path.endswith('.yml') or path.endswith('.yaml'):
+                yaml.dump(data, f)
+            else:
+                raise UknownFileError(provided_value=path, known_values=['.json', '.yml', '.yaml'])
+
+    def __update_config(cls):
+        cls.__config_data = {
+            'data_path': cls.data_path,
+            'enterprise_attck_json': cls._enterprise_attck_json,
+            'pre_attck_json': cls._pre_attck_json,
+            'mobile_attck_json': cls._mobile_attck_json,
+            'nist_controls_json': cls._nist_controls_json,
+            'generated_attck_json': cls._generated_attck_json,
+            'generated_nist_json': cls._generated_nist_json
+        }
+
+    def get_data(cls, value):
+        data = None
+        if os.path.isfile(value):
+            try:
+                with open(value) as f:
+                    if value.endswith('.json'):
+                        data = json.load(f)
+                    elif value.endswith('.yml') or value.endswith('.yaml'):
+                        data = yaml.load(f, Loader=yaml.FullLoader)
+                    else:
+                        raise UknownFileError(provided_value=value, known_values=['.json', '.yml', '.yaml'])
+            except:
+                warnings.warn("Unable to load data from specified location. Setting configuration data to defaults: '{}'".format(value))
+                cls.__update_config()
+                data = cls.__config_data
+        elif cls._check_if_url(value):
+            data = cls.__download_url_data(value)
+        return data
+
+    @property
+    def requests_kwargs(cls):
+        return cls._requests_kwargs
+
+    @requests_kwargs.setter
+    def requests_kwargs(cls, value):
+        cls._requests_kwargs = value
+
+    @property
+    def use_config(cls):
+        return cls._use_config
+
+    @use_config.setter
+    def use_config(cls, value):
+        cls._use_config = bool(value)
+
+    @property
+    def save_config(cls):
+        return cls._save_config
 
-        self.__write_config(self.__DATASETS_MAP)
+    @save_config.setter
+    def save_config(cls, value):
+        cls._save_config = bool(value)
 
-    def __write_config(self, config):
-        mode = None
-        if not os.path.exists(self.__CONFIG_FILE):
-            mode = 'w+'
-            os.makedirs(os.path.dirname(self.__CONFIG_FILE))
+    @property
+    def config_file_path(cls):
+        return cls.__get_absolute_path(cls._config_file_path)
+
+    @config_file_path.setter
+    def config_file_path(cls, value):
+        cls._config_file_path = cls.__get_absolute_path(value)
+        cls.__update_config()
+
+    @property
+    def data_path(cls):
+        return cls.__get_absolute_path(cls._data_path)
+
+    @data_path.setter
+    def data_path(cls, value):
+        cls._data_path = cls.__get_absolute_path(value)
+        cls.__update_config()
+
+    @property
+    def config_data(cls):
+        if cls.use_config:
+            cls.__config_data = cls.get_data(cls.config_file_path)
+            if not cls.__config_data:
+                cls.__update_config()
         else:
-            mode = 'w'
-        with open(self.__CONFIG_FILE, mode) as outfile:
-            document = yaml.dump(config, outfile)
+            cls.__update_config()
+        if cls.save_config:
+            cls.__save_config(cls.config_file_path, cls.__config_data)
+            cls.__save_data()
+        return cls.__config_data
+
+    @property
+    def enterprise_attck_json(cls):
+        return cls._enterprise_attck_json
+
+    @enterprise_attck_json.setter
+    def enterprise_attck_json(cls, value):
+        cls._enterprise_attck_json = cls.__validate_value_string(value)
+        cls.__update_config()
+
+    @property
+    def pre_attck_json(cls):
+        return cls._pre_attck_json
+
+    @pre_attck_json.setter
+    def pre_attck_json(cls, value):
+        cls._pre_attck_json = cls.__validate_value_string(value)
+        cls.__update_config()
+
+    @property
+    def mobile_attck_json(cls):
+        return cls._mobile_attck_json
+
+    @mobile_attck_json.setter
+    def mobile_attck_json(cls, value):
+        cls._mobile_attck_json = cls.__validate_value_string(value)
+        cls.__update_config()
+
+    @property
+    def nist_controls_json(cls):
+        return cls._nist_controls_json
+
+    @nist_controls_json.setter
+    def nist_controls_json(cls, value):
+        cls._nist_controls_json = cls.__validate_value_string(value)
+        cls.__update_config()
+
+    @property
+    def generated_attck_json(cls):
+        return cls._generated_attck_json
+
+    @generated_attck_json.setter
+    def generated_attck_json(cls, value):
+        cls._generated_attck_json = cls.__validate_value_string(value)
+        cls.__update_config()
+
+    @property
+    def generated_nist_json(cls):
+        return cls._generated_nist_json
+
+    @generated_nist_json.setter
+    def generated_nist_json(cls, value):
+        cls._generated_nist_json = cls.__validate_value_string(value)
+        cls.__update_config()
+
+
+class Configuration(object, metaclass=ConfigurationProperties):
+    _use_config = False
+    _save_config = False
+    _config_file_path = '~/pyattck/config.yml'
+    _data_path = '~/pyattck/data'
+    _enterprise_attck_json = "https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json"
+    _pre_attck_json="https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json"
+    _mobile_attck_json="https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json"
+    _nist_controls_json="https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json"
+    _generated_attck_json="https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True"
+    _generated_nist_json="https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True"
+    _requests_kwargs = {}
diff --git a/pyattck/datasets.py b/pyattck/datasets.py
deleted file mode 100644
index b70dcb41..00000000
--- a/pyattck/datasets.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import requests
-import json
-import pendulum
-import os
-from .configuration import Configuration
-
-
-class AttckDatasets(object):
-
-    """Retrieves and saves datasets used by pyattck.
-
-    AttckDatasets is used to download, save or retrieve datasets for pyattck.
-
-        Default locations to download datasets are as follows:
-            MITRE_ATTCK_JSON_URL = 'https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json'
-            MITRE_PREATTCK_ATTCK_JSON_URL  = 'https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json'
-            MITRE_MOBILE_ATTCK_JSON_URL = 'https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json'
-            DATASETS_URL = 'https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True'
-            NIST_DATASETS_URL = 'https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True'
-    """
-
-    def __init__(self):
-        """
-        The main class to retrieve and save datasets external from this package.
-        """
-        self.__DATASETS_MAP = Configuration().get()
-
-    def __get_json_data(self, url, path, force=False):
-        if force:
-            data = requests.get(url).json()
-            self.__save_locally(path, data)
-            return data
-        else:
-            cached_data = self.__get_cached_data(path)
-            if cached_data:
-                if cached_data.get('last_updated'):
-                    if pendulum.now().add(days=30).to_iso8601_string() >= pendulum.parse(cached_data['last_updated']).to_iso8601_string():
-                        return cached_data
-                    else:
-                        datasets = self.__download_data(url)
-                        self.__save_locally(path, datasets)
-                        return datasets
-                else:
-                    datasets = self.__download_data(url)
-                    self.__save_locally(path, datasets)
-                    return datasets
-            else:
-                data = requests.get(url).json()
-                self.__save_locally(path, data)
-                return data
-
-    def get_data(self, data_type, force=False):
-        """
-        Downloads, saves, or retrieves JSON data
-
-        Args:
-            type (str): Will set the type of data to download/retrieve. Options are
-                        enterprise, preattack, mobile, generated_data, nist_data
-            force (bool, optional): Will force the download of a new JSON file.
-                                    Defaults to False.
-
-        Returns:
-            [dict]: Returns JSON data
-        """
-        if self.__DATASETS_MAP.get(data_type):
-            return self.__get_json_data(
-                self.__DATASETS_MAP[data_type]['url'],
-                os.path.join(self.__DATASETS_MAP['data_path'], self.__DATASETS_MAP[data_type]['filename']),
-                force=force
-            )
-
-    def __download_data(self, url):
-        return requests.get(url).json()
-
-    def __get_cached_data(self, local_file_path):
-        if os.path.isfile(local_file_path):
-            if os.path.getsize(local_file_path) > 0:
-                with open(local_file_path) as f:
-                    try:
-                        return json.load(f)
-                    except:
-                        pass
-        return None
-
-    def __save_locally(self, local_file_path, data):
-        if not data.get('last_updated'):
-            data['last_updated'] = pendulum.now().to_iso8601_string()
-        with open(local_file_path, 'w+') as outfile:
-            try:
-                json.dump(data, outfile)
-            except:
-                pass
diff --git a/pyattck/enterprise/actor.py b/pyattck/enterprise/actor.py
index f5eac8f3..6d683244 100644
--- a/pyattck/enterprise/actor.py
+++ b/pyattck/enterprise/actor.py
@@ -1,8 +1,5 @@
-
 from .attckobject import AttckObject
 from ..utils.logo import Logo
-from ..utils.exceptions import GeneratedDatasetException
-from ..datasets import AttckDatasets
 
 
 class AttckActor(AttckObject):
@@ -116,101 +113,44 @@ def __init__(self, attck_obj = None, **kwargs):
         logo = Logo(self.name.strip().replace(' ','_').lower())
         self.ascii_logo = logo.get_ascii()
         self.image_logo = logo.get_image()
-        self.__retrieve_datasets()
         self.external_dataset = self.__get_actors_dataset()
 
-    def __retrieve_datasets(self):
-        if AttckActor.__ATTCK_DATASETS is None:
-            try:
-                data = AttckDatasets().get_data(data_type='generated_data')
-                if 'actors' in data:
-                    AttckActor.__ATTCK_DATASETS = data['actors']
-            except:
-                raise GeneratedDatasetException('Unable to retrieve generated attack data properties')
-
     def __get_actors_dataset(self):
         return_list = []
-        self.country = []
-        self.operations = []
-        self.attribution_links = []
-        self.known_tools = []
-        self.targets = []
-        self.additional_comments = []
-        self.external_description = []
-        for country in AttckActor.__ATTCK_DATASETS:
+        countries = set()
+        operations = set()
+        attribution_links = set()
+        known_tools = set()
+        targets = set()
+        additional_comments = set()
+        external_description = set()
+        for country in AttckObject.generated_attck_json.get('actors'):
             if country:
-                for k,v in country.items():
-                    for actor in v['actors']:
-                        if 'names' in actor:
-                            if actor['names']:
-                                if self.name in actor['names']:
-                                    if k not in self.country:
-                                        self.country.append(k)
-                                    return_list.append(actor)
-                                    if 'operations' in actor:
-                                        if actor['operations']:
-                                            for operation in actor['operations']:
-                                                if operation not in self.operations:
-                                                    self.operations.append(operation)
-                                    if 'links' in actor:
-                                        if actor['links']:
-                                            for link in actor['links']:
-                                                if link not in self.attribution_links:
-                                                    self.attribution_links.append(link)
-                                    if 'tools' in actor:
-                                        if actor['tools']:
-                                            for tool in actor['tools']:
-                                                if tool not in self.known_tools:
-                                                    self.known_tools.append(tool)
-                                    if 'targets' in actor:
-                                        if actor['targets']:
-                                            if actor['targets'] not in self.targets:
-                                                self.targets.append(actor['targets'])
-                                    if 'comment' in actor:
-                                        if actor['comment']:
-                                            if actor['comment'] not in self.additional_comments:
-                                                self.additional_comments.append(actor['comment'])
-                                    if 'description' in actor:
-                                        if actor['description']:
-                                            if actor['description'] not in self.external_description:
-                                                self.external_description.append(actor['description'])
-                            if self.alias:
-                                for alias in self.alias:
-                                    if alias in actor['names']:
-                                        if k not in self.country:
-                                            self.country.append(k)
-                                        return_list.append(actor)
-                                        if 'operations' in actor:
-                                            if actor['operations']:
-                                                for operation in actor['operations']:
-                                                    if operation not in self.operations:
-                                                        self.operations.append(operation)
-                                        if 'links' in actor:
-                                            if actor['links']:
-                                                for link in actor['links']:
-                                                    if link not in self.attribution_links:
-                                                        self.attribution_links.append(link)
-                                        if 'tools' in actor:
-                                            if actor['tools']:
-                                                for tool in actor['tools']:
-                                                    if tool not in self.known_tools:
-                                                        self.known_tools.append(tool)
-                                        if 'targets' in actor:
-                                            if actor['targets']:
-                                                if actor['targets'] not in self.targets:
-                                                    self.targets.append(actor['targets'])
-                                        if 'comment' in actor:
-                                            if actor['comment']:
-                                                if actor['comment'] not in self.additional_comments:
-                                                    self.additional_comments.append(actor['comment'])
-                                        if 'description' in actor:
-                                            if actor['description']:
-                                                if actor['description'] not in self.external_description:
-                                                    self.external_description.append(actor['description'])
-        if return_list:
-            return return_list
-        else:
-            return None
+                for key,val in country.items():
+                    for actor in val.get('actors'):
+                        if self.name in actor.get('names') or hasattr(self, 'alias') and self.alias and any(x in actor.get('names') for x in self.alias):
+                            countries.add(key)
+                            if actor.get('operations'):
+                                operations.update(actor.get('operations'))
+                            if actor.get('links'):
+                                attribution_links.update(actor.get('links'))
+                            if actor.get('tools'):
+                                known_tools.update(actor.get('tools'))
+                            if actor.get('targets'):
+                                targets.add(actor.get('targets'))
+                            if actor.get('comment'):
+                                additional_comments.update(actor.get('comment',[]))
+                            if actor.get('description'):
+                                external_description.update(actor.get('description'))
+                            return_list.append(actor)
+        self.country = list(countries)
+        self.operations = list(operations)
+        self.attribution_links = list(attribution_links)
+        self.known_tools = list(known_tools)
+        self.targets = list(targets)
+        self.additional_comments = list(additional_comments)
+        self.external_description = list(external_description)
+        return return_list
 
     @property
     def malwares(self):
diff --git a/pyattck/enterprise/attckobject.py b/pyattck/enterprise/attckobject.py
index f512b2d8..1dba6c52 100644
--- a/pyattck/enterprise/attckobject.py
+++ b/pyattck/enterprise/attckobject.py
@@ -1,3 +1,4 @@
+from ..configuration import Configuration
 
 
 class AttckObject(object):
@@ -12,7 +13,10 @@ class AttckObject(object):
     """
 
     _RELATIONSHIPS = None
-    
+    generated_attck_json = Configuration.get_data(Configuration.config_data.get('generated_attck_json'))
+    nist_controls_json = Configuration.get_data(Configuration.config_data.get('nist_controls_json'))['objects']
+    generated_nist_json = Configuration.get_data(Configuration.config_data.get('generated_nist_json'))
+
     def __init__(self, **kwargs):
         """
         Sets standard properties that are found in all child classes as well as
@@ -151,6 +155,8 @@ def _set_id(self, obj):
             for p in obj['external_references']:
                 if p.get('source_name') == 'mitre-attack':
                     return p.get('external_id')
+                elif 'NIST' in p.get('source_name'):
+                    return p.get('external_id')
         return 'No ID Defined'
 
     def _set_wiki(self, obj):
diff --git a/pyattck/enterprise/control.py b/pyattck/enterprise/control.py
index a4b77a52..3fc550f1 100644
--- a/pyattck/enterprise/control.py
+++ b/pyattck/enterprise/control.py
@@ -1,4 +1,7 @@
-class AttckControl:
+from .attckobject import AttckObject
+
+
+class AttckControl(AttckObject):
 
     """An object that represents a compliance control type.
 
@@ -38,7 +41,7 @@ class AttckControl:
         kwargs (dict) -- Takes the raw control Json object
     """
 
-    def __init__(self, **kwargs):
+    def __init__(self, attck_obj = None, **kwargs):
         """
         This class represents a compliance Control as defined from
         external data sources.
@@ -46,51 +49,28 @@ def __init__(self, **kwargs):
         Keyword Arguments:
             kwargs (dict) -- A compliance control JSON object
         """
-        self.id = self._set_id(kwargs.pop('external_references'))
-        self.stix = kwargs.pop('id')
+        super(AttckControl, self).__init__(**kwargs)
+        self.__attck_obj = attck_obj
         for key,val in kwargs.items():
             prop_name = key.replace('x_mitre_','')
             if not hasattr(self, prop_name):
                 setattr(self, prop_name, val)
 
-    def __str__(self):
-        """
-        Returns dictionary string of all properties and
-        values for the instance
-
-        Returns:
-            (str): All properties and values of instance
-        """
-        return_dict = {}
-        for key,val in self.__dict__.items():
-            if not key.startswith('_'):
-                return_dict[key] = val
-        return str(return_dict)
-
-    def __repr__(self):
+    @property
+    def techniques(self):
         """
-        Returns a printable representation of an object
-
-        Returns:
-            str: Returns a printable representation of an object
-        """
-        return "{class_name}('{name}', '{id}')".format(
-            class_name=self.__class__.__name__,
-            name=self.name,
-            id=self.id
-        )
-
-    def _set_id(self, external_references):
-        """
-        Returns the compliance control (external) ID
-
-        Arguments:
-            external_references (list) -- A list of external_references
+        Returns all technique objects as a list that are
+        associated with a Tactic
 
         Returns:
-            (str) -- Returns the compliance control (external) ID
+            [list] -- A list of related technique objects defined
+                      within the Enterprise MITRE ATT&CK Framework
         """
-        for reference in external_references:
-            if 'NIST' in reference.get('source_name'):
-                return reference.get('external_id')
-        return 'No ID Defined'
+        from .technique import AttckTechnique
+        technique_list = []
+        for key,val in AttckObject.generated_nist_json.items():
+            if self.stix in val:
+                for item in self.__attck_obj['objects']:
+                    if 'type' in item and item['type'] == 'attack-pattern':
+                        technique_list.append(AttckTechnique(attck_obj=self.__attck_obj, **item))
+        return technique_list
diff --git a/pyattck/enterprise/enterprise.py b/pyattck/enterprise/enterprise.py
index a3e86a05..9bd94289 100644
--- a/pyattck/enterprise/enterprise.py
+++ b/pyattck/enterprise/enterprise.py
@@ -4,6 +4,8 @@
 from .tools import AttckTools
 from .mitigation import AttckMitigation
 from .tactic import AttckTactic
+from .control import AttckControl
+from ..configuration import Configuration
 
 
 class Enterprise(object):
@@ -257,15 +259,15 @@ class Enterprise(object):
                  MITRE ATT&CK Framework
     """
 
-    __ENTERPRISE_GENERATED_DATA_JSON = None
-    __tactics = None
-    __techniques = None
-    __mitigations = None
-    __actors = None
-    __tools = None
-    __malwares = None
+    __tactics = []
+    __techniques = []
+    __mitigations = []
+    __actors = []
+    __tools = []
+    __malwares = []
+    __controls = []
     
-    def __init__(self, attck_json, nested_subtechniques=True):
+    def __init__(self, nested_subtechniques=True):
         """
         Sets standard properties that are found in all child classes
         as well as provides standard methods used by inherited classes
@@ -275,7 +277,9 @@ def __init__(self, attck_json, nested_subtechniques=True):
             nested_subtechniques (bool) -- Determines if nested subtechniques will
             be used or not. This is passed from attck class
         """
-        self.__attck = attck_json
+        self.__ENTERPRISE_GENERATED_DATA_JSON = None
+        self.__nist_controls_json = Configuration.get_data(Configuration.config_data.get('nist_controls_json'))['objects']
+        self.__attck = Configuration.get_data(Configuration.config_data.get('enterprise_attck_json'))
         self.__nested_subtechniques = nested_subtechniques
 
     @property
@@ -286,13 +290,27 @@ def actors(self):
         Returns:
             (AttckActor) -- (Returns a list of AttckActor objects)
         """
-        if self.__actors is None:
-            self.__actors = []
+        if not self.__actors:
             for group in self.__attck['objects']:
                 if group['type'] == 'intrusion-set':
                     self.__actors.append(AttckActor(attck_obj=self.__attck, **group))
         return self.__actors
 
+    @property
+    def controls(self):
+        """
+        Creates AttckControls objects
+
+        Returns:
+            (AttckControl) -- Returns a list of AttckControl objects
+        """
+        if not self.__controls:
+            for control in self.__nist_controls_json:
+                if control:
+                    print(control)
+                    self.__controls.append(AttckControl(attck_obj=self.__attck, **control))
+        return self.__controls
+
     @property
     def tactics(self):
         """
@@ -301,8 +319,7 @@ def tactics(self):
         Returns:
             (AttckTactic) -- (Returns a list of AttckTactic objects)
         """
-        if self.__tactics is None:
-            self.__tactics = []
+        if not self.__tactics:
             for tactic in self.__attck['objects']:
                 if tactic['type'] == 'x-mitre-tactic':
                     self.__tactics.append(AttckTactic(attck_obj=self.__attck, **tactic))
@@ -316,8 +333,7 @@ def mitigations(self):
         Returns:
             (AttckMitigation) -- (Returns a list of AttckMitigation objects)
         """
-        if self.__mitigations is None:
-            self.__mitigations = []
+        if not self.__mitigations:
             for mitigation in self.__attck['objects']:
                 if mitigation['type'] == 'course-of-action':
                     self.__mitigations.append(AttckMitigation(attck_obj=self.__attck, **mitigation))
@@ -331,8 +347,7 @@ def tools(self):
         Returns:
             (AttckTools) -- Returns a list of AttckTools objects
         """
-        if self.__tools is None:
-            self.__tools = []
+        if not self.__tools:
             for tools in self.__attck['objects']:
                 if tools['type'] == 'tool':
                     self.__tools.append(AttckTools(attck_obj=self.__attck, **tools))
@@ -346,8 +361,7 @@ def malwares(self):
         Returns:
             (AttckMalware) -- Returns a list of AttckMalware objects
         """
-        if self.__malwares is None:
-            self.__malwares = []
+        if not self.__malwares:
             for malware in self.__attck['objects']:
                 if malware['type'] == 'malware':
                     self.__malwares.append(AttckMalware(attck_obj=self.__attck, **malware))
@@ -361,9 +375,8 @@ def techniques(self):
         Returns:
             (AttckTechnique) -- Returns a list of AttckTechnique objects
         """
-        if self.__techniques is None:
+        if not self.__techniques:
             subtechniques = []
-            self.__techniques = []
             for technique in self.__attck["objects"]:
                 if technique.get('type') == 'attack-pattern' and technique.get('revoked') is not True:
                     if self.__nested_subtechniques:
@@ -398,11 +411,11 @@ def search_commands(self, search_term, json=False):
         """
         if json:
             import json
-        from ..datasets import AttckDatasets
+        from ..configuration import Configuration
         return_list = []
-        if not Enterprise.__ENTERPRISE_GENERATED_DATA_JSON:
-            Enterprise.__ENTERPRISE_GENERATED_DATA_JSON = AttckDatasets().get_data('generated_data')
-        for item in Enterprise.__ENTERPRISE_GENERATED_DATA_JSON['techniques']:
+        if not self.__ENTERPRISE_GENERATED_DATA_JSON:
+            self.__ENTERPRISE_GENERATED_DATA_JSON = Configuration.get_data(Configuration.config_data.get('generated_attck_json'))
+        for item in self.__ENTERPRISE_GENERATED_DATA_JSON['techniques']:
             if 'command_list' in item:
                 if item['command_list']:
                     for cmd in item['command_list']:
diff --git a/pyattck/enterprise/technique.py b/pyattck/enterprise/technique.py
index e5be8ccb..29e6c371 100644
--- a/pyattck/enterprise/technique.py
+++ b/pyattck/enterprise/technique.py
@@ -1,6 +1,4 @@
 from .attckobject import AttckObject
-from ..datasets import AttckDatasets
-from ..utils.exceptions import GeneratedDatasetException
 
 
 class AttckTechnique(AttckObject):
@@ -84,11 +82,6 @@ class AttckTechnique(AttckObject):
         AttckObject (dict) -- Takes the MITRE ATT&CK Json object as a kwargs values
     """
 
-    __LOCAL_FOLDER_PATH = None
-    __ATTCK_DATASETS = None
-    __NIST_DATA_MAP = None
-    __NIST_DATASETS = None
-
     def __init__(self, attck_obj = None, **kwargs):
         """
         This class represents a Technique as defined by the
@@ -122,38 +115,19 @@ def __init__(self, attck_obj = None, **kwargs):
         self.contributors = self._set_list_items(kwargs, 'x_mitre_contributors')
         self.revoked = self._set_attribute(kwargs, 'revoked')
         self.deprecated = self._set_attribute(kwargs, 'x_mitre_deprecated')
-        self.subtechnique = self._set_attribute(kwargs, 'x_mitre_is_subtechnique')
+        self.subtechnique = False if self._set_attribute(kwargs, 'x_mitre_is_subtechnique') is None else True
         self.__subtechniques = []
-        self.__retrieve_datasets()
-        self.command_list = self.__get_filtered_dataset(self.id, 'command_list')
-        self.commands = self.__get_filtered_dataset(self.id, 'commands')
-        self.queries = self.__get_filtered_dataset(self.id, 'queries')
-        self.datasets = self.__get_filtered_dataset(self.id, 'parsed_datasets')
-        self.possible_detections = self.__get_filtered_dataset(self.id, 'possible_detections')
-        self.subtechnique = self._set_attribute(kwargs, 'x_mitre_is_subtechnique')
+        self.command_list = self.__get_filtered_dataset('command_list')
+        self.commands = self.__get_filtered_dataset('commands')
+        self.queries = self.__get_filtered_dataset('queries')
+        self.datasets = self.__get_filtered_dataset('parsed_datasets')
+        self.possible_detections = self.__get_filtered_dataset('possible_detections')
         self.tactics = kwargs
         self.set_relationships(self.__attck_obj)
 
-    def __retrieve_datasets(self):
-        if AttckTechnique.__ATTCK_DATASETS is None:
-            try:
-                AttckTechnique.__ATTCK_DATASETS = AttckDatasets().get_data(data_type='generated_data')
-            except:
-                raise GeneratedDatasetException('Unable to retrieve generated attack data properties')
-        if AttckTechnique.__NIST_DATA_MAP is None:
-            try:
-                AttckTechnique.__NIST_DATA_MAP = AttckDatasets().get_data(data_type='nist_data')
-            except:
-                raise GeneratedDatasetException('Unable to retrieve generated NIST Controls data map')
-        if AttckTechnique.__NIST_DATASETS is None:
-            try:
-                AttckTechnique.__NIST_DATASETS = AttckDatasets().get_data(data_type='nist_800_53_rev4_controls')['objects']
-            except:
-                raise GeneratedDatasetException('Unable to retrieve NIST 800-53 Control data')
-
-    def __get_filtered_dataset(self, technique_id, attribute_name):
-        for item in AttckTechnique.__ATTCK_DATASETS['techniques']:
-            if item['technique_id'] == technique_id:
+    def __get_filtered_dataset(self, attribute_name):
+        for item in AttckObject.generated_attck_json['techniques']:
+            if item['technique_id'] == self.id:
                 return item[attribute_name]
 
     def __get_subtechnique_id(self, obj):
@@ -186,9 +160,9 @@ def controls(self):
         """
         from .control import AttckControl
         control_list = []
-        if AttckTechnique.__NIST_DATA_MAP.get(self.stix):
-            for control in AttckTechnique.__NIST_DATASETS:
-                if control.get('id') in AttckTechnique.__NIST_DATA_MAP[self.stix]:
+        if AttckObject.generated_nist_json.get(self.stix):
+            for control in AttckObject.nist_controls_json:
+                if control.get('id') in AttckObject.generated_nist_json[self.stix]:
                     control_list.append(AttckControl(**control))
         return control_list
 
diff --git a/pyattck/enterprise/tools.py b/pyattck/enterprise/tools.py
index 3b1a4a39..67142751 100644
--- a/pyattck/enterprise/tools.py
+++ b/pyattck/enterprise/tools.py
@@ -1,6 +1,4 @@
 from .attckobject import AttckObject
-from ..datasets import AttckDatasets
-from ..utils.exceptions import GeneratedDatasetException
 
 
 class AttckTools(AttckObject):
@@ -121,9 +119,6 @@ class AttckTools(AttckObject):
         AttckObject (dict) -- Takes the MITRE ATT&CK Json object as a kwargs values
     """
 
-    __ATTCK_C2_DATASETS = None
-    __ATTCK_TOOLS_DATASETS = None
-
     def __init__(self, attck_obj = None, **kwargs):
         """
         This class represents a Tool as defined by the
@@ -150,66 +145,35 @@ def __init__(self, attck_obj = None, **kwargs):
         self.wiki = self._set_wiki(kwargs)
         self.contributor = self._set_attribute(kwargs, 'contributor')
         self.set_relationships(self.__attck_obj)
-        self.__retrieve_datasets()
         self.c2_data = self.__get_c2_dataset()
         self.external_dataset =  self.__get_tools_dataset()
 
-    def __retrieve_datasets(self):
-        if AttckTools.__ATTCK_C2_DATASETS is None or AttckTools.__ATTCK_TOOLS_DATASETS is None:
-            try:
-                data = AttckDatasets().get_data(data_type='generated_data')
-            except:
-                raise GeneratedDatasetException('Unable to retrieve generated attack data properties')
-            if AttckTools.__ATTCK_C2_DATASETS is None:
-                if 'c2_data' in data:
-                    AttckTools.__ATTCK_C2_DATASETS = data['c2_data']
-            if AttckTools.__ATTCK_TOOLS_DATASETS is None:
-                if 'tools' in data:
-                    AttckTools.__ATTCK_TOOLS_DATASETS = data['tools']
-
     def __get_tools_dataset(self):
         return_list = []
-        self.additional_names = []
-        self.attribution_links = []
-        self.additional_comments = []
-        self.family = []
-        for tool in AttckTools.__ATTCK_TOOLS_DATASETS['tools']:
-            if 'names' in tool:
-                if tool['names']:
-                    if self.name.lower() in [x.lower() for x in tool['names']]:
-                        return_list.append(tool)
-                        for name in tool['names']:
-                            self.additional_names.append(name)
-                        if 'links' in tool:
-                            for link in tool['links']:
-                                self.attribution_links.append(link)
-                        if 'family' in tool:
-                            for family in tool['family']:
-                                self.family.append(family)
-                        if 'comments' in tool:
-                            self.family.append(tool['comments'])
-                    if self.alias:
-                        for alias in self.alias:
-                            if alias.lower() in [x.lower() for x in tool['names']]:
-                                return_list.append(tool)
-                                for name in tool['names']:
-                                    self.additional_names.append(name)
-                                if 'links' in tool:
-                                    for link in tool['links']:
-                                        self.attribution_links.append(link)
-                                if 'family' in tool:
-                                    for family in tool['family']:
-                                        self.family.append(family)
-                                if 'comments' in tool:
-                                    self.family.append(tool['family'])
-        if return_list:
-            return return_list
-        else:
-            return None
+        self.additional_names = set()
+        self.attribution_links = set()
+        self.additional_comments = set()
+        self.family = set()
+        for tool in AttckObject.generated_attck_json['tools'].get('tools'):
+            if tool.get('names'):
+                if self.name.lower() in [x.lower() for x in tool.get('names')] or hasattr(self, 'alias') and any(x.lower() in tool.get('names') for x in self.alias):
+                    self.additional_names.update(tool.get('names'))
+                    if tool.get('links'):
+                        self.attribution_links.update(tool['links'])
+                    if tool.get('family'):
+                        self.family.update(tool['family'])
+                    if tool.get('comments'):
+                        self.additional_comments.update(tool['comments'])
+                    return_list.append(tool)
+        self.additional_names = list(self.additional_names)
+        self.attribution_links = list(self.attribution_links)
+        self.additional_comments = list(self.additional_comments)
+        self.family = list(self.family)
+        return return_list
 
     def __get_c2_dataset(self):
         return_dict = {}
-        for k,v in AttckTools.__ATTCK_C2_DATASETS.items():
+        for k,v in AttckObject.generated_attck_json['c2_data'].items():
             if self.name.lower() == k.lower():
                 return_dict[k] = v
                 for key,val in v.items():
@@ -226,10 +190,7 @@ def __get_c2_dataset(self):
                                 setattr(self, key, val)
                             except:
                                 setattr(self, 'c2_{}_{}'.format(item.lower(), key), val)
-        if return_dict:
-            return return_dict
-        else:
-            return None
+        return return_dict if return_dict else None
 
     @property
     def techniques(self):
diff --git a/pyattck/mobile/actor.py b/pyattck/mobile/actor.py
index 2a18c01f..96f73d40 100644
--- a/pyattck/mobile/actor.py
+++ b/pyattck/mobile/actor.py
@@ -1,8 +1,6 @@
 
 from .mobileattckobject import MobileAttckObject
 from ..utils.logo import Logo
-from ..utils.exceptions import GeneratedDatasetException
-from ..datasets import AttckDatasets
 
 
 class MobileAttckActor(MobileAttckObject):
@@ -74,8 +72,6 @@ class MobileAttckActor(MobileAttckObject):
                        # etc.
     """
 
-    __ATTCK_DATASETS = None
-
     def __init__(self, mobile_attck_obj = None, **kwargs):
         """
         This class represents a Actor (or group) as defined by
@@ -100,101 +96,44 @@ def __init__(self, mobile_attck_obj = None, **kwargs):
         self.set_relationships(self.__mobile_attck_obj)
         logo = Logo(self.name.strip().replace(' ','_').lower())
         self.ascii_logo = logo.get_ascii()
-        self.__retrieve_datasets()
-
-    def __retrieve_datasets(self):
-        if MobileAttckActor.__ATTCK_DATASETS is None:
-            try:
-                data = AttckDatasets().get_data(data_type='generated_data')
-                if 'actors' in data:
-                    MobileAttckActor.__ATTCK_DATASETS = data['actors']
-            except:
-                raise GeneratedDatasetException('Unable to retrieve generated attack data properties')
         self.external_dataset = self.__get_actors_dataset()
 
     def __get_actors_dataset(self):
         return_list = []
-        self.country = []
-        self.operations = []
-        self.attribution_links = []
-        self.known_tools = []
-        self.targets = []
-        self.additional_comments = []
-        self.external_description = []
-        for country in MobileAttckActor.__ATTCK_DATASETS:
+        countries = set()
+        operations = set()
+        attribution_links = set()
+        known_tools = set()
+        targets = set()
+        additional_comments = set()
+        external_description = set()
+        for country in MobileAttckObject.generated_attck_json.get('actors'):
             if country:
-                for k,v in country.items():
-                    for actor in v['actors']:
-                        if 'names' in actor:
-                            if actor['names']:
-                                if self.name in actor['names']:
-                                    if k not in self.country:
-                                        self.country.append(k)
-                                    return_list.append(actor)
-                                    if 'operations' in actor:
-                                        if actor['operations']:
-                                            for operation in actor['operations']:
-                                                if operation not in self.operations:
-                                                    self.operations.append(operation)
-                                    if 'links' in actor:
-                                        if actor['links']:
-                                            for link in actor['links']:
-                                                if link not in self.attribution_links:
-                                                    self.attribution_links.append(link)
-                                    if 'tools' in actor:
-                                        if actor['tools']:
-                                            for tool in actor['tools']:
-                                                if tool not in self.known_tools:
-                                                    self.known_tools.append(tool)
-                                    if 'targets' in actor:
-                                        if actor['targets']:
-                                            if actor['targets'] not in self.targets:
-                                                self.targets.append(actor['targets'])
-                                    if 'comment' in actor:
-                                        if actor['comment']:
-                                            if actor['comment'] not in self.additional_comments:
-                                                self.additional_comments.append(actor['comment'])
-                                    if 'description' in actor:
-                                        if actor['description']:
-                                            if actor['description'] not in self.external_description:
-                                                self.external_description.append(actor['description'])
-                            if self.alias:
-                                for alias in self.alias:
-                                    if alias in actor['names']:
-                                        if k not in self.country:
-                                            self.country.append(k)
-                                        return_list.append(actor)
-                                        if 'operations' in actor:
-                                            if actor['operations']:
-                                                for operation in actor['operations']:
-                                                    if operation not in self.operations:
-                                                        self.operations.append(operation)
-                                        if 'links' in actor:
-                                            if actor['links']:
-                                                for link in actor['links']:
-                                                    if link not in self.attribution_links:
-                                                        self.attribution_links.append(link)
-                                        if 'tools' in actor:
-                                            if actor['tools']:
-                                                for tool in actor['tools']:
-                                                    if tool not in self.known_tools:
-                                                        self.known_tools.append(tool)
-                                        if 'targets' in actor:
-                                            if actor['targets']:
-                                                if actor['targets'] not in self.targets:
-                                                    self.targets.append(actor['targets'])
-                                        if 'comment' in actor:
-                                            if actor['comment']:
-                                                if actor['comment'] not in self.additional_comments:
-                                                    self.additional_comments.append(actor['comment'])
-                                        if 'description' in actor:
-                                            if actor['description']:
-                                                if actor['description'] not in self.external_description:
-                                                    self.external_description.append(actor['description'])
-        if return_list:
-            return return_list
-        else:
-            return None
+                for key,val in country.items():
+                    for actor in val.get('actors'):
+                        if self.name in actor.get('names') or hasattr(self, 'alias') and self.alias and any(x in actor.get('names') for x in self.alias):
+                            countries.add(key)
+                            if actor.get('operations'):
+                                operations.update(actor.get('operations'))
+                            if actor.get('links'):
+                                attribution_links.update(actor.get('links'))
+                            if actor.get('tools'):
+                                known_tools.update(actor.get('tools'))
+                            if actor.get('targets'):
+                                targets.add(actor.get('targets'))
+                            if actor.get('comment'):
+                                additional_comments.update(actor.get('comment',[]))
+                            if actor.get('description'):
+                                external_description.update(actor.get('description'))
+                            return_list.append(actor)
+        self.country = list(countries)
+        self.operations = list(operations)
+        self.attribution_links = list(attribution_links)
+        self.known_tools = list(known_tools)
+        self.targets = list(targets)
+        self.additional_comments = list(additional_comments)
+        self.external_description = list(external_description)
+        return return_list
 
     @property
     def malwares(self):
diff --git a/pyattck/mobile/mobileattck.py b/pyattck/mobile/mobileattck.py
index 2fdf45ce..afc9edef 100644
--- a/pyattck/mobile/mobileattck.py
+++ b/pyattck/mobile/mobileattck.py
@@ -4,6 +4,7 @@
 from .tools import MobileAttckTools
 from .mitigation import MobileAttckMitigation
 from .tactic import MobileAttckTactic
+from ..configuration import Configuration
 
 
 class MobileAttck(object):
@@ -203,27 +204,13 @@ class MobileAttck(object):
     """
 
     __ENTERPRISE_GENERATED_DATA_JSON = None
-    __tactics = None
-    __techniques = None
-    __mitigations = None
-    __actors = None
-    __tools = None
-    __malwares = None
-
-    def __init__(self, mobile_attck_json):
-        """
-        Sets standard properties that are found in all child classes as well as
-        provides standard methods used by inherited classes
-
-        Arguments:
-            mobile_attck_json (json) - Takes the MITRE Mobile ATT&CK Json object
-                                       as argument
-
-        Returns:
-            [MobileAttck]: Returns a Attck object that contains all data from the
-                           MITRE Mobile ATT&CK Framework
-        """
-        self.__mobile_attck = mobile_attck_json
+    __mobile_attck_json = Configuration.get_data(Configuration.config_data.get('enterprise_attck_json'))
+    __tactics = []
+    __techniques = []
+    __mitigations = []
+    __actors = []
+    __tools = []
+    __malwares = []
 
     @property
     def actors(self):
@@ -233,11 +220,10 @@ def actors(self):
         Returns:
             (MobileAttckActor) -- (Returns a list of MobileAttckActor objects)
         """
-        if self.__actors is None:
-            self.__actors = []
-            for group in self.__mobile_attck['objects']:
+        if not self.__actors:
+            for group in self.__mobile_attck_json['objects']:
                 if group['type'] == 'intrusion-set':
-                    self.__actors.append(MobileAttckActor(mobile_attck_obj=self.__mobile_attck, **group))
+                    self.__actors.append(MobileAttckActor(mobile_attck_obj=self.__mobile_attck_json, **group))
         return self.__actors
 
     @property
@@ -248,11 +234,10 @@ def tactics(self):
         Returns:
             (MobileAttckTactic) -- (Returns a list of MobileAttckTactic objects)
         """
-        if self.__tactics is None:
-            self.__tactics = []
-            for tactic in self.__mobile_attck['objects']:
+        if not self.__tactics:
+            for tactic in self.__mobile_attck_json['objects']:
                 if tactic['type'] == 'x-mitre-tactic':
-                    self.__tactics.append(MobileAttckTactic(mobile_attck_obj=self.__mobile_attck, **tactic))
+                    self.__tactics.append(MobileAttckTactic(mobile_attck_obj=self.__mobile_attck_json, **tactic))
         return self.__tactics
 
     @property
@@ -263,11 +248,10 @@ def mitigations(self):
         Returns:
             (MobileAttckMitigation) -- (Returns a list of MobileAttckMitigation objects)
         """
-        if self.__mitigations is None:
-            self.__mitigations = []
-            for mitigation in self.__mobile_attck['objects']:
+        if not self.__mitigations:
+            for mitigation in self.__mobile_attck_json['objects']:
                 if mitigation['type'] == 'course-of-action':
-                    self.__mitigations.append(MobileAttckMitigation(mobile_attck_obj=self.__mobile_attck, **mitigation))
+                    self.__mitigations.append(MobileAttckMitigation(mobile_attck_obj=self.__mobile_attck_json, **mitigation))
         return self.__mitigations
 
     @property
@@ -278,11 +262,10 @@ def tools(self):
         Returns:
             (MobileAttckTools) -- Returns a list of MobileAttckTools objects
         """
-        if self.__tools is None:
-            self.__tools = []
-            for tools in self.__mobile_attck['objects']:
+        if not self.__tools:
+            for tools in self.__mobile_attck_json['objects']:
                 if tools['type'] == 'tool':
-                    self.__tools.append(MobileAttckTools(mobile_attck_obj=self.__mobile_attck, **tools))
+                    self.__tools.append(MobileAttckTools(mobile_attck_obj=self.__mobile_attck_json, **tools))
         return self.__tools
 
     @property
@@ -293,11 +276,10 @@ def malwares(self):
         Returns:
             (MobileAttckMalware) -- Returns a list of MobileAttckMalware objects
         """
-        if self.__malwares is None:
-            self.__malwares = []
-            for malware in self.__mobile_attck['objects']:
+        if not self.__malwares:
+            for malware in self.__mobile_attck_json['objects']:
                 if malware['type'] == 'malware':
-                    self.__malwares.append(MobileAttckMalware(mobile_attck_obj=self.__mobile_attck, **malware))
+                    self.__malwares.append(MobileAttckMalware(mobile_attck_obj=self.__mobile_attck_json, **malware))
         return self.__malwares
 
     @property
@@ -308,9 +290,8 @@ def techniques(self):
         Returns:
             (MobileAttckTechnique) -- Returns a list of MobileAttckTechnique objects
         """
-        if self.__techniques is None:
-            self.__techniques = []
-            for technique in self.__mobile_attck["objects"]:
+        if not self.__techniques:
+            for technique in self.__mobile_attck_json["objects"]:
                 if technique['type'] == 'attack-pattern':
-                    self.__techniques.append(MobileAttckTechnique(mobile_attck_obj=self.__mobile_attck, **technique))
+                    self.__techniques.append(MobileAttckTechnique(mobile_attck_obj=self.__mobile_attck_json, **technique))
         return self.__techniques
diff --git a/pyattck/mobile/mobileattckobject.py b/pyattck/mobile/mobileattckobject.py
index 5baccf13..7f554d2e 100644
--- a/pyattck/mobile/mobileattckobject.py
+++ b/pyattck/mobile/mobileattckobject.py
@@ -1,3 +1,4 @@
+from ..configuration import Configuration
 
 
 class MobileAttckObject(object):
@@ -13,6 +14,9 @@ class MobileAttckObject(object):
     """
 
     _RELATIONSHIPS = None
+    generated_attck_json = Configuration.get_data(Configuration.config_data.get('generated_attck_json'))
+    nist_controls_json = Configuration.get_data(Configuration.config_data.get('nist_controls_json'))['objects']
+    generated_nist_json = Configuration.get_data(Configuration.config_data.get('generated_nist_json'))
 
     def __init__(self, **kwargs):
         """
diff --git a/pyattck/mobile/technique.py b/pyattck/mobile/technique.py
index d0bc8d4d..c01f5db9 100644
--- a/pyattck/mobile/technique.py
+++ b/pyattck/mobile/technique.py
@@ -1,6 +1,4 @@
 from .mobileattckobject import MobileAttckObject
-from ..datasets import AttckDatasets
-from ..utils.exceptions import GeneratedDatasetException
 
 
 class MobileAttckTechnique(MobileAttckObject):
@@ -67,9 +65,6 @@ class MobileAttckTechnique(MobileAttckObject):
                        # etc.
     """
 
-    __LOCAL_FOLDER_PATH = None
-    __ATTCK_DATASETS = None
-
     def __init__(self, mobile_attck_obj = None, **kwargs):
         """
         Creates an MobileAttckTechnique object.  
@@ -94,25 +89,17 @@ def __init__(self, mobile_attck_obj = None, **kwargs):
         self.deprecated = self._set_attribute(kwargs, 'x_mitre_deprecated')
         self.wiki = self._set_wiki(kwargs)
         self.stix = self._set_attribute(kwargs, 'id')
-        self.__retrieve_datasets()
-        self.command_list = self.__get_filtered_dataset(self.id, 'command_list')
-        self.commands = self.__get_filtered_dataset(self.id, 'commands')
-        self.queries = self.__get_filtered_dataset(self.id, 'queries')
-        self.datasets = self.__get_filtered_dataset(self.id, 'parsed_datasets')
-        self.possible_detections = self.__get_filtered_dataset(self.id, 'possible_detections')
+        self.command_list = self.__get_filtered_dataset('command_list')
+        self.commands = self.__get_filtered_dataset('commands')
+        self.queries = self.__get_filtered_dataset('queries')
+        self.datasets = self.__get_filtered_dataset('parsed_datasets')
+        self.possible_detections = self.__get_filtered_dataset('possible_detections')
         self.tactics = kwargs
         self.set_relationships(self.__mobile_attck_obj)
 
-    def __retrieve_datasets(self):
-        if MobileAttckTechnique.__ATTCK_DATASETS is None:
-            try:
-                MobileAttckTechnique.__ATTCK_DATASETS = AttckDatasets().get_data(data_type='generated_data')
-            except:
-                raise GeneratedDatasetException('Unable to retrieve generated attack data properties')
-
-    def __get_filtered_dataset(self, technique_id, attribute_name):
-        for item in MobileAttckTechnique.__ATTCK_DATASETS['techniques']:
-            if item['technique_id'] == technique_id:
+    def __get_filtered_dataset(self, attribute_name):
+        for item in MobileAttckObject.generated_attck_json['techniques']:
+            if item['technique_id'] == self.id:
                 return item[attribute_name]
 
     @property
diff --git a/pyattck/mobile/tools.py b/pyattck/mobile/tools.py
index bc3e8ae0..e9be2360 100644
--- a/pyattck/mobile/tools.py
+++ b/pyattck/mobile/tools.py
@@ -1,6 +1,4 @@
 from .mobileattckobject import MobileAttckObject
-from ..datasets import AttckDatasets
-from ..utils.exceptions import GeneratedDatasetException
 
 
 class MobileAttckTools(MobileAttckObject):
@@ -117,9 +115,6 @@ class MobileAttckTools(MobileAttckObject):
                     # etc.
     """
 
-    __ATTCK_C2_DATASETS = None
-    __ATTCK_TOOLS_DATASETS = None
-
     def __init__(self, mobile_attck_obj = None, **kwargs):
         """
         Creates an MobileAttckTools object.  
@@ -144,67 +139,35 @@ def __init__(self, mobile_attck_obj = None, **kwargs):
         self.stix = self._set_attribute(kwargs, 'id')
         self.wiki = self._set_wiki(kwargs)
         self.set_relationships(self.__mobile_attck_obj)
-        self.__retrieve_datasets()
         self.c2_data = self.__get_c2_dataset()
         self.external_dataset =  self.__get_tools_dataset()
 
-    def __retrieve_datasets(self):
-        if MobileAttckTools.__ATTCK_C2_DATASETS is None or MobileAttckTools.__ATTCK_TOOLS_DATASETS is None:
-            try:
-                data = AttckDatasets().get_data(data_type='generated_data')
-            except:
-                raise GeneratedDatasetException('Unable to retrieve generated attack data properties')
-            if MobileAttckTools.__ATTCK_C2_DATASETS is None:
-                if 'c2_data' in data:
-                    MobileAttckTools.__ATTCK_C2_DATASETS = data['c2_data']
-            if MobileAttckTools.__ATTCK_TOOLS_DATASETS is None:
-                if 'tools' in data:
-                    MobileAttckTools.__ATTCK_TOOLS_DATASETS = data['tools']
-
     def __get_tools_dataset(self):
         return_list = []
-        self.additional_names = []
-        self.attribution_links = []
-        self.additional_comments = []
-        self.family = []
-        for tool in MobileAttckTools.__ATTCK_TOOLS_DATASETS['tools']:
-            if 'names' in tool:
-                if tool['names']:
-                    if self.name.lower() in [x.lower() for x in tool['names']]:
-                        return_list.append(tool)
-                        for name in tool['names']:
-                            self.additional_names.append(name)
-                        if 'links' in tool:
-                            for link in tool['links']:
-                                self.attribution_links.append(link)
-                        if 'family' in tool:
-                            for family in tool['family']:
-                                self.family.append(family)
-                        if 'comments' in tool:
-                            self.family.append(tool['comments'])
-                    if self.alias:
-                        for alias in self.alias:
-                            if alias:
-                                if alias.lower() in [x.lower() for x in tool['names']]:
-                                    return_list.append(tool)
-                                    for name in tool['names']:
-                                        self.additional_names.append(name)
-                                    if 'links' in tool:
-                                        for link in tool['links']:
-                                            self.attribution_links.append(link)
-                                    if 'family' in tool:
-                                        for family in tool['family']:
-                                            self.family.append(family)
-                                    if 'comments' in tool:
-                                        self.family.append(tool['family'])
-        if return_list:
-            return return_list
-        else:
-            return None
+        self.additional_names = set()
+        self.attribution_links = set()
+        self.additional_comments = set()
+        self.family = set()
+        for tool in MobileAttckObject.generated_attck_json['tools'].get('tools'):
+            if tool.get('names'):
+                if self.name.lower() in [x.lower() for x in tool.get('names')] or hasattr(self, 'alias') and any(x.lower() in tool.get('names') for x in self.alias):
+                    self.additional_names.update(tool.get('names'))
+                    if tool.get('links'):
+                        self.attribution_links.update(tool['links'])
+                    if tool.get('family'):
+                        self.family.update(tool['family'])
+                    if tool.get('comments'):
+                        self.additional_comments.update(tool['comments'])
+                    return_list.append(tool)
+        self.additional_names = list(self.additional_names)
+        self.attribution_links = list(self.attribution_links)
+        self.additional_comments = list(self.additional_comments)
+        self.family = list(self.family)
+        return return_list
 
     def __get_c2_dataset(self):
         return_dict = {}
-        for k,v in MobileAttckTools.__ATTCK_C2_DATASETS.items():
+        for k,v in MobileAttckObject.generated_attck_json['c2_data'].items():
             if self.name.lower() == k.lower():
                 return_dict[k] = v
                 for key,val in v.items():
@@ -214,18 +177,14 @@ def __get_c2_dataset(self):
                         setattr(self, 'c2_{}'.format(key), val)
             if self.alias:
                 for item in self.alias:
-                    if item:
-                        if item.lower() == k.lower():
-                            return_dict[k] = v
-                            for key,val in v.items():
-                                try:
-                                    setattr(self, key, val)
-                                except:
-                                    setattr(self, 'c2_{}_{}'.format(item.lower(), key), val)
-        if return_dict:
-            return return_dict
-        else:
-            return None
+                    if item and item.lower() == k.lower():
+                        return_dict[k] = v
+                        for key,val in v.items():
+                            try:
+                                setattr(self, key, val)
+                            except:
+                                setattr(self, 'c2_{}_{}'.format(item.lower(), key), val)
+        return return_dict if return_dict else None
 
     @property
     def techniques(self):
diff --git a/pyattck/preattck/actor.py b/pyattck/preattck/actor.py
index a2ac89af..fe21e6ed 100644
--- a/pyattck/preattck/actor.py
+++ b/pyattck/preattck/actor.py
@@ -1,7 +1,5 @@
 from .preattckobject import PreAttckObject
 from ..utils.logo import Logo
-from ..utils.exceptions import GeneratedDatasetException
-from ..datasets import AttckDatasets
 
 
 class PreAttckActor(PreAttckObject):
@@ -74,8 +72,6 @@ class PreAttckActor(PreAttckObject):
                        # etc.
     """
 
-    __PREATTCK_DATASETS = None
-
     def __init__(self, preattck_obj = None, **kwargs):
         """
         Creates objects that are categorized as
@@ -102,101 +98,44 @@ def __init__(self, preattck_obj = None, **kwargs):
         self.set_relationships(self.__preattck_obj)
         logo = Logo(self.name.strip().replace(' ','_').lower())
         self.ascii_logo = logo.get_ascii()
-        self.__retrieve_datasets()
         self.external_dataset = self.__get_actors_dataset()
 
-    def __retrieve_datasets(self):
-        if PreAttckActor.__PREATTCK_DATASETS is None:
-            try:
-                data = AttckDatasets().get_data(data_type='generated_data')
-                if 'actors' in data:
-                    PreAttckActor.__PREATTCK_DATASETS = data['actors']
-            except:
-                raise GeneratedDatasetException('Unable to retrieve generated attack data properties')
-
     def __get_actors_dataset(self):
         return_list = []
-        self.country = []
-        self.operations = []
-        self.attribution_links = []
-        self.known_tools = []
-        self.targets = []
-        self.additional_comments = []
-        self.external_description = []
-        for country in PreAttckActor.__PREATTCK_DATASETS:
+        countries = set()
+        operations = set()
+        attribution_links = set()
+        known_tools = set()
+        targets = set()
+        additional_comments = set()
+        external_description = set()
+        for country in PreAttckObject.generated_attck_json.get('actors'):
             if country:
-                for k,v in country.items():
-                    for actor in v['actors']:
-                        if 'names' in actor:
-                            if actor['names']:
-                                if self.name in actor['names']:
-                                    if k not in self.country:
-                                        self.country.append(k)
-                                    return_list.append(actor)
-                                    if 'operations' in actor:
-                                        if actor['operations']:
-                                            for operation in actor['operations']:
-                                                if operation not in self.operations:
-                                                    self.operations.append(operation)
-                                    if 'links' in actor:
-                                        if actor['links']:
-                                            for link in actor['links']:
-                                                if link not in self.attribution_links:
-                                                    self.attribution_links.append(link)
-                                    if 'tools' in actor:
-                                        if actor['tools']:
-                                            for tool in actor['tools']:
-                                                if tool not in self.known_tools:
-                                                    self.known_tools.append(tool)
-                                    if 'targets' in actor:
-                                        if actor['targets']:
-                                            if actor['targets'] not in self.targets:
-                                                self.targets.append(actor['targets'])
-                                    if 'comment' in actor:
-                                        if actor['comment']:
-                                            if actor['comment'] not in self.additional_comments:
-                                                self.additional_comments.append(actor['comment'])
-                                    if 'description' in actor:
-                                        if actor['description']:
-                                            if actor['description'] not in self.external_description:
-                                                self.external_description.append(actor['description'])
-                            if self.alias:
-                                for alias in self.alias:
-                                    if alias in actor['names']:
-                                        if k not in self.country:
-                                            self.country.append(k)
-                                        return_list.append(actor)
-                                        if 'operations' in actor:
-                                            if actor['operations']:
-                                                for operation in actor['operations']:
-                                                    if operation not in self.operations:
-                                                        self.operations.append(operation)
-                                        if 'links' in actor:
-                                            if actor['links']:
-                                                for link in actor['links']:
-                                                    if link not in self.attribution_links:
-                                                        self.attribution_links.append(link)
-                                        if 'tools' in actor:
-                                            if actor['tools']:
-                                                for tool in actor['tools']:
-                                                    if tool not in self.known_tools:
-                                                        self.known_tools.append(tool)
-                                        if 'targets' in actor:
-                                            if actor['targets']:
-                                                if actor['targets'] not in self.targets:
-                                                    self.targets.append(actor['targets'])
-                                        if 'comment' in actor:
-                                            if actor['comment']:
-                                                if actor['comment'] not in self.additional_comments:
-                                                    self.additional_comments.append(actor['comment'])
-                                        if 'description' in actor:
-                                            if actor['description']:
-                                                if actor['description'] not in self.external_description:
-                                                    self.external_description.append(actor['description'])
-        if return_list:
-            return return_list
-        else:
-            return None
+                for key,val in country.items():
+                    for actor in val.get('actors'):
+                        if self.name in actor.get('names') or hasattr(self, 'alias') and self.alias and any(x in actor.get('names') for x in self.alias):
+                            countries.add(key)
+                            if actor.get('operations'):
+                                operations.update(actor.get('operations'))
+                            if actor.get('links'):
+                                attribution_links.update(actor.get('links'))
+                            if actor.get('tools'):
+                                known_tools.update(actor.get('tools'))
+                            if actor.get('targets'):
+                                targets.add(actor.get('targets'))
+                            if actor.get('comment'):
+                                additional_comments.update(actor.get('comment',[]))
+                            if actor.get('description'):
+                                external_description.update(actor.get('description'))
+                            return_list.append(actor)
+        self.country = list(countries)
+        self.operations = list(operations)
+        self.attribution_links = list(attribution_links)
+        self.known_tools = list(known_tools)
+        self.targets = list(targets)
+        self.additional_comments = list(additional_comments)
+        self.external_description = list(external_description)
+        return return_list
 
     @property
     def techniques(self):
diff --git a/pyattck/preattck/preattck.py b/pyattck/preattck/preattck.py
index 6694c6ab..f9229206 100644
--- a/pyattck/preattck/preattck.py
+++ b/pyattck/preattck/preattck.py
@@ -1,6 +1,7 @@
 from .technique import PreAttckTechnique
 from .actor import PreAttckActor
 from .tactic import PreAttckTactic
+from ..configuration import Configuration
 
 
 class PreAttck(object):
@@ -91,27 +92,10 @@ class PreAttck(object):
                        # etc.
     """
 
-    __tactics = None
-    __techniques = None
-    __mitigations = None
-    __actors = None
-    __tools = None
-    __malwares = None
-
-    def __init__(self, preattck_json):
-        """
-        Sets standard properties that are found in all child classes
-        as well as provides standard methods used by inherited classes
-
-        Arguments:
-            preattck_json (json) - Takes the MITRE PRE-ATT&CK Json
-                                   object as argument
-
-        Returns:
-            [PreAttck]: Returns a Attck object that contains all data
-                        from the MITRE PRE-ATT&CK Framework
-        """
-        self.__preattck = preattck_json
+    __tactics = []
+    __techniques = []
+    __actors = []
+    __preattck_json = Configuration.get_data(Configuration.config_data.get('pre_attck_json'))
 
     @property
     def actors(self):
@@ -121,11 +105,10 @@ def actors(self):
         Returns:
             PreAttckActor: Returns a list of PreAttckActor objects
         """
-        if self.__actors is None:
-            self.__actors = []
-            for group in self.__preattck['objects']:
+        if not self.__actors:
+            for group in self.__preattck_json['objects']:
                 if group['type'] == 'intrusion-set':
-                    self.__actors.append(PreAttckActor(preattck_obj=self.__preattck, **group))
+                    self.__actors.append(PreAttckActor(preattck_obj=self.__preattck_json, **group))
         return self.__actors
 
     @property
@@ -136,11 +119,10 @@ def tactics(self):
         Returns:
             PreAttckTactic: Returns a list of PreAttckTactic objects
         """
-        if self.__tactics is None:
-            self.__tactics = []
-            for tactic in self.__preattck['objects']:
+        if not self.__tactics:
+            for tactic in self.__preattck_json['objects']:
                 if tactic['type'] == 'x-mitre-tactic':
-                    self.__tactics.append(PreAttckTactic(preattck_obj=self.__preattck, **tactic))
+                    self.__tactics.append(PreAttckTactic(preattck_obj=self.__preattck_json, **tactic))
         return self.__tactics
 
     @property
@@ -151,9 +133,8 @@ def techniques(self):
         Returns:
             PreAttckTechnique: Returns a list of PreAttckTechnique objects
         """
-        if self.__techniques is None:
-            self.__techniques = []
-            for technique in self.__preattck["objects"]:
+        if not self.__techniques:
+            for technique in self.__preattck_json["objects"]:
                 if (technique['type'] == 'attack-pattern'):
-                    self.__techniques.append(PreAttckTechnique(preattck_obj=self.__preattck, **technique))
+                    self.__techniques.append(PreAttckTechnique(preattck_obj=self.__preattck_json, **technique))
         return self.__techniques
diff --git a/pyattck/preattck/preattckobject.py b/pyattck/preattck/preattckobject.py
index 1f00a470..6189ceee 100644
--- a/pyattck/preattck/preattckobject.py
+++ b/pyattck/preattck/preattckobject.py
@@ -1,3 +1,6 @@
+from ..configuration import Configuration
+
+
 class PreAttckObject(object):
 
     """MITRE PRE-ATT&CK parent object.
@@ -8,6 +11,9 @@ class PreAttckObject(object):
     """
 
     _RELATIONSHIPS = None
+    generated_attck_json = Configuration.get_data(Configuration.config_data.get('generated_attck_json'))
+    nist_controls_json = Configuration.get_data(Configuration.config_data.get('nist_controls_json'))['objects']
+    generated_nist_json = Configuration.get_data(Configuration.config_data.get('generated_nist_json'))
 
     def __init__(self, **kwargs):
         """
diff --git a/pyattck/preattck/technique.py b/pyattck/preattck/technique.py
index 9661e010..584e4a0f 100644
--- a/pyattck/preattck/technique.py
+++ b/pyattck/preattck/technique.py
@@ -69,8 +69,6 @@ class PreAttckTechnique(PreAttckObject):
         AttckObject (dict) -- Takes the Mitre ATT&CK Json object as a kwargs values
     """
 
-    __LOCAL_FOLDER_PATH = None
-
     def __init__(self, preattck_obj = None, **kwargs):
         """
         This class represents a Techniques as defined by the
@@ -94,8 +92,18 @@ def __init__(self, preattck_obj = None, **kwargs):
         self.deprecated = self._set_attribute(kwargs, 'x_mitre_deprecated')
         self.stix = self._set_attribute(kwargs, 'id')
         self.wiki = self._set_wiki(kwargs)
+        self.command_list = self.__get_filtered_dataset('command_list')
+        self.commands = self.__get_filtered_dataset('commands')
+        self.queries = self.__get_filtered_dataset('queries')
+        self.datasets = self.__get_filtered_dataset('parsed_datasets')
+        self.possible_detections = self.__get_filtered_dataset('possible_detections')
         self.set_relationships(self.__preattck_obj)
 
+    def __get_filtered_dataset(self, attribute_name):
+        for item in PreAttckObject.generated_attck_json['techniques']:
+            if item['technique_id'] == self.id:
+                return item[attribute_name]
+
     @property
     def tactics(self):
         """
diff --git a/pyattck/utils/exceptions.py b/pyattck/utils/exceptions.py
index de41310a..2a38d82c 100644
--- a/pyattck/utils/exceptions.py
+++ b/pyattck/utils/exceptions.py
@@ -3,5 +3,23 @@ class GeneratedDatasetException(Exception):
     """
     Raised when unable to retrieve generated datasets and related properties
     """
+    pass
+
+class ConfigurationException(Exception):
 
+    """Raised when unable to load or read the pyattck configuration file.
+    """
     pass
+
+class UknownFileError(ValueError):
+    """Raised when the provided file extension is unkown or is not json, yml or yaml
+    """
+    def __init__(self, provided_value=None, known_values=None):
+        if provided_value and known_values:
+            if isinstance(known_values, list):
+                super().__init__("The provided value {} is unknown. Please provide a file path with one of these '{}' extensions.".format(
+                    provided_value,
+                    ','.join([x for x in known_values])
+                ))
+        else:
+            pass
\ No newline at end of file
diff --git a/pyattck/utils/logo.py b/pyattck/utils/logo.py
index b9175e77..ff38c21d 100644
--- a/pyattck/utils/logo.py
+++ b/pyattck/utils/logo.py
@@ -1,6 +1,9 @@
 import os
 
-from PIL import Image
+try:
+    from PIL import Image
+except:
+    import Image
 from pyfiglet import Figlet
 
 ASCII_CHARS = ['.',',',':',';','+','*','?','%','S','#','@']
diff --git a/pyattck/utils/version.py b/pyattck/utils/version.py
new file mode 100644
index 00000000..629042ea
--- /dev/null
+++ b/pyattck/utils/version.py
@@ -0,0 +1,2 @@
+__version_info__ = (4, 0, 0)
+__version__ = ".".join(map(str, __version_info__))
\ No newline at end of file
diff --git a/pyattck/version.py b/pyattck/version.py
deleted file mode 100644
index c359da63..00000000
--- a/pyattck/version.py
+++ /dev/null
@@ -1,2 +0,0 @@
-__version_info__ = (2, 0, 0)
-__version__ = ".".join(map(str, __version_info__))
diff --git a/pylama.ini b/pylama.ini
new file mode 100644
index 00000000..8d31e56f
--- /dev/null
+++ b/pylama.ini
@@ -0,0 +1,15 @@
+[pylama]
+format = pylint
+skip = */.tox/*,*/.env/*
+linters = pylint,mccabe
+ignore = F0401,C0111,E731
+
+[pylama:pyflakes]
+builtins = _
+
+[pylama:pycodestyle]
+max_line_length = 120
+
+[pylama:pylint]
+max_line_length = 120
+disable = R
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 61a60e76..2bbaffab 100644
--- a/setup.py
+++ b/setup.py
@@ -4,9 +4,14 @@ def parse_requirements(requirement_file):
     with open(requirement_file) as f:
         return f.readlines()
 
+version = dict()
+with open("./pyattck/utils/version.py") as fp:
+    exec(fp.read(), version)
+
+
 setup(
     name='pyattck',
-    version='3.0.1',
+    version=version['__version__'],
     packages=find_packages(exclude=['tests*']),
     license='MIT',
     description='A Python package to interact with the Mitre ATT&CK Frameworks',
@@ -17,7 +22,7 @@ def parse_requirements(requirement_file):
     url='https://github.com/swimlane/pyattck',
     author='Swimlane',
     author_email='info@swimlane.com',
-    python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*,!=3.3.*, !=3.4.*, !=3.5, <4',
+    python_requires='>=3.6, <4',
     package_data={
         'pyattck':  ['data/actors/*.png']
     },


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
deleted file mode 100644
index 6f96178b..00000000
--- a/.github/workflows/test.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-name: Testing pyattck
-
-on: 
-  push
-
-jobs:
-  build:
-
-    runs-on: ${{ matrix.os }}
-    strategy:
-        matrix:
-            os: [windows-latest, ubuntu-latest, macos-latest]
-            python-version: [2.7, 3.6, 3.7, 3.8, 3.9]
-
-    steps:
-    - uses: actions/checkout@v2
-    - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python@v1
-      with:
-        python-version: ${{ matrix.python-version }}
-    - name: Install dependencies
-      run: |
-        python -m pip install --upgrade pip
-        pip install -U -r requirements.txt
-        pip install -U -r test-requirements.txt
-    - name: Run Tests
-      run: |
-        python -m pytest
\ No newline at end of file
diff --git a/test-requirements.txt b/test-requirements.txt
index 55b033e9..5704ffa2 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,1 +1,4 @@
-pytest
\ No newline at end of file
+pytest
+pylama==7.7.1
+coverage
+Pillow>=6.2.2
\ No newline at end of file
diff --git a/tests/conftest.py b/tests/conftest.py
index 9c48ea60..1b3946b9 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -4,9 +4,28 @@
 @pytest.fixture
 def attck_fixture():
     from pyattck import Attck
-    return Attck()
+    yield Attck(
+        use_config=False,
+        save_config=False,
+        enterprise_attck_json= "https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json",
+        pre_attck_json="https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json",
+        mobile_attck_json="https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json",
+        nist_controls_json="https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json",
+        generated_attck_json="https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True",
+        generated_nist_json="https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True"
+    )
 
 @pytest.fixture
 def attck_fixture_nested_subtechniques_false():
     from pyattck import Attck
-    return Attck(nested_subtechniques=False)
+    yield Attck(nested_subtechniques=False)
+
+@pytest.fixture
+def attck_configuration():
+    from pyattck import Configuration
+    yield Configuration
+
+@pytest.fixture
+def attck_datasets():
+    from pyattck import Attck
+    yield Attck
\ No newline at end of file
diff --git a/tests/enterprise/tes_controls.py b/tests/enterprise/tes_controls.py
new file mode 100644
index 00000000..7cb7b52b
--- /dev/null
+++ b/tests/enterprise/tes_controls.py
@@ -0,0 +1,10 @@
+def test_controls_have_techniques(attck_fixture):
+    """
+    All MITRE Enterprise ATT&CK Malware should have Actors
+
+    Args:
+        attck_fixture ([type]): our default MITRE Enterprise ATT&CK JSON fixture
+    """
+    for control in attck_fixture.enterprise.controls:
+        if control.techniques:
+            assert getattr(control,'techniques')
diff --git a/tests/enterprise/test_attck.py b/tests/enterprise/test_attck.py
deleted file mode 100644
index 34646d26..00000000
--- a/tests/enterprise/test_attck.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import pytest
-import os
-import yaml
-from os.path import expanduser
-
-
-@pytest.mark.parametrize(
-    'target_attribute', 
-    ['techniques', 'tactics', 'mitigations', 'malwares', 'actors', 'tools']
-)
-def test_attck_attribute_is_list(target_attribute):
-    from pyattck import Attck
-    path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'fixtures')#, 'generated_attck_data' + '.json')
-    attck = Attck(data_path=path)
-    enterprise = getattr(attck, 'enterprise')
-    assert isinstance(
-        getattr(enterprise, target_attribute),
-        list
-    )
-
-@pytest.mark.parametrize(
-    'target_attribute', 
-    ['techniques', 'tactics', 'mitigations', 'malwares', 'actors', 'tools']
-)
-@pytest.mark.parametrize(
-    'target_properties',
-    ['id','name','description','reference','created','modified','stix','type']
-)
-
-def test_all_attck_objects_have_standard_properties(target_attribute,target_properties):
-    from pyattck import Attck
-    path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'fixtures')#, 'generated_attck_data' + '.json')
-    attck = Attck(data_path=path)
-    enterprise = getattr(attck, 'enterprise')
-    return_list = []
-    for attribute in getattr(enterprise,target_attribute):
-        if hasattr(attribute, target_properties):
-            return_list.append(getattr(attribute,target_properties))
-    if len(return_list) >= 1:
-        assert True
diff --git a/tests/enterprise/test_configuration.py b/tests/enterprise/test_configuration.py
deleted file mode 100644
index 345a5fcc..00000000
--- a/tests/enterprise/test_configuration.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import pytest
-import os
-import yaml
-from os.path import expanduser
-
-
-def test_config_in_default_location():
-    from pyattck import Attck
-    Attck()
-
-    assert os.path.isfile(os.path.join(expanduser('~'), 'pyattck', 'config' + '.yml'))
-
-def test_config_attck_json_path_default_value():
-    from pyattck import Attck
-    Attck()
-    
-    config_path = os.path.abspath(os.path.join(expanduser('~'), 'pyattck', 'config' + '.yml'))
-    config = None
-    with open(config_path) as f:
-        config = yaml.load(f, Loader=yaml.FullLoader)
-    assert os.path.join(config.get('data_path'), config['enterprise'].get('filename')) == os.path.join(config.get('data_path'), 'enterprise_attck' + '.json')
-
-def test_config_attck_json_path_provided_value(tmpdir):
-    from pyattck import Attck
-    config_path = os.path.abspath(os.path.join(expanduser('~'), 'pyattck', 'config' + '.yml'))
-    path = tmpdir.mkdir('pyattck')
-    Attck(data_path=str(path))
-    
-    config = None
-    with open(config_path) as f:
-        config = yaml.load(f, Loader=yaml.FullLoader)
-    assert os.path.join(config.get('data_path'), config['enterprise'].get('filename')) == os.path.join(config.get('data_path'), 'enterprise_attck' + '.json')
-
-def test_config_dataset_json_path_default_value():
-    from pyattck import Attck
-    Attck()
-    
-    config = None
-    with open(os.path.abspath(os.path.join(expanduser('~'), 'pyattck', 'config' + '.yml'))) as f:
-        config = yaml.load(f, Loader=yaml.FullLoader)
-    assert os.path.join(config.get('data_path'), config['generated_data'].get('filename')) == os.path.abspath(os.path.join(expanduser('~'), 'pyattck', 'enterprise_attck_dataset' + '.json'))
-
-
-def test_config_alternate_location(tmpdir):
-    config_path = str(tmpdir.mkdir('pyattck').join('config.yml'))
-    from pyattck import Attck
-    Attck(config_file_path=config_path)
-    
-    assert len(tmpdir.listdir()) == 1
\ No newline at end of file
diff --git a/tests/enterprise/test_enterprise.py b/tests/enterprise/test_enterprise.py
index a906e61d..41fa54e9 100644
--- a/tests/enterprise/test_enterprise.py
+++ b/tests/enterprise/test_enterprise.py
@@ -1,3 +1,9 @@
+import pytest
+import os
+import yaml
+from os.path import expanduser
+
+
 def test_enterprise_search_commands(attck_fixture):
     """
     All MITRE Enterprise ATT&CK search returns commands
@@ -7,3 +13,38 @@ def test_enterprise_search_commands(attck_fixture):
     """
     if len(attck_fixture.enterprise.search_commands('powershell')) >= 1:
         assert True
+
+@pytest.mark.parametrize(
+    'target_attribute', 
+    ['techniques', 'tactics', 'mitigations', 'malwares', 'actors', 'tools', 'controls']
+)
+def test_attck_attribute_is_list(target_attribute):
+    from pyattck import Attck
+    path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'fixtures')#, 'generated_attck_data' + '.json')
+    attck = Attck(data_path=path)
+    enterprise = getattr(attck, 'enterprise')
+    assert isinstance(
+        getattr(enterprise, target_attribute),
+        list
+    )
+
+@pytest.mark.parametrize(
+    'target_attribute', 
+    ['techniques', 'tactics', 'mitigations', 'malwares', 'actors', 'tools', 'controls']
+)
+@pytest.mark.parametrize(
+    'target_properties',
+    ['id','name','description','reference','created','modified','stix','type']
+)
+
+def test_all_attck_objects_have_standard_properties(target_attribute,target_properties):
+    from pyattck import Attck
+    path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'fixtures')#, 'generated_attck_data' + '.json')
+    attck = Attck(data_path=path)
+    enterprise = getattr(attck, 'enterprise')
+    return_list = []
+    for attribute in getattr(enterprise,target_attribute):
+        if hasattr(attribute, target_properties):
+            return_list.append(getattr(attribute,target_properties))
+    if len(return_list) >= 1:
+        assert True
diff --git a/tests/test_attck.py b/tests/test_attck.py
new file mode 100644
index 00000000..02b01ed8
--- /dev/null
+++ b/tests/test_attck.py
@@ -0,0 +1,95 @@
+import os
+import random
+import tempfile
+import pytest
+
+
+default_config_data = {
+    'data_path': os.path.abspath(os.path.expanduser(os.path.expandvars('~/pyattck/data'))),
+    'enterprise_attck_json': "https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json",
+    'pre_attck_json': "https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json",
+    'mobile_attck_json': "https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json",
+    'nist_controls_json': "https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json",
+    'generated_attck_json': "https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True",
+    'generated_nist_json': "https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True"
+}
+
+
+def get_random_file_or_url():
+    if random.choice(['file', 'url']) == 'file':
+        return tempfile.NamedTemporaryFile().name
+    else:
+        return random.choice(['https://letsautomate.it/article/index.xml', 'https://google.com', 'https://github.com/swimlane/pyattck'])
+
+
+def test_default_config():
+      from pyattck import Attck, Configuration
+      attck = Attck()
+      assert Configuration.config_data == default_config_data
+
+@pytest.mark.parametrize(
+    'target_attribute', 
+    ['enterprise_attck_json', 'pre_attck_json', 'mobile_attck_json', 'nist_controls_json', 'generated_attck_json', 'generated_nist_json']
+)
+def test_setting_json_locations(target_attribute):
+      from pyattck import Attck, Configuration
+
+      enterprise_temp_value = get_random_file_or_url()
+      pre_attck_temp_value = get_random_file_or_url()
+      mobile_temp_value = get_random_file_or_url()
+      nist_controls_temp_value = get_random_file_or_url()
+      generated_attck_temp_value = get_random_file_or_url()
+      generated_nist_temp_value = get_random_file_or_url()
+
+      attck = Attck(
+        enterprise_attck_json=enterprise_temp_value
+      )
+      assert Configuration.enterprise_attck_json == enterprise_temp_value
+
+      attck = Attck(
+        pre_attck_json=pre_attck_temp_value
+      )
+      assert Configuration.pre_attck_json == pre_attck_temp_value
+
+      attck = Attck(
+        mobile_attck_json=mobile_temp_value
+      )
+      assert Configuration.mobile_attck_json == mobile_temp_value
+
+      attck = Attck(
+        nist_controls_json=nist_controls_temp_value
+      )
+      assert Configuration.nist_controls_json == nist_controls_temp_value
+
+      attck = Attck(
+        generated_attck_json=generated_attck_temp_value
+      )
+      assert Configuration.generated_attck_json == generated_attck_temp_value
+
+      attck = Attck(
+        generated_nist_json=generated_nist_temp_value
+      )
+      assert Configuration.generated_nist_json == generated_nist_temp_value
+
+def test_nested_subtechniques():
+      from pyattck import Attck
+      attck = Attck(nested_subtechniques=False)
+      count = 0
+      for technique in attck.enterprise.techniques:
+          if technique.subtechnique:
+              count += 1
+      assert count >= 360
+
+def test_passed_kwargs():
+      from pyattck import Attck, Configuration
+      attck = Attck()
+      assert Configuration.requests_kwargs == {}
+      args = {
+          'verify': False,
+          'proxies': {
+              'http': 'http://10.10.1.10:3128',
+              'https': 'http://10.10.1.10:1080',
+          }
+      }
+      attck = Attck(**args)
+      assert Configuration.requests_kwargs == args
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
new file mode 100644
index 00000000..bce8adc5
--- /dev/null
+++ b/tests/test_configuration.py
@@ -0,0 +1,157 @@
+import os
+import tempfile
+import yaml
+import pytest
+import random
+
+def get_random_file_or_url():
+    if random.choice(['file', 'url']) == 'file':
+        return tempfile.NamedTemporaryFile().name
+    else:
+        return random.choice(['https://letsautomate.it/article/index.xml', 'https://google.com', 'https://github.com/swimlane/pyattck'])
+
+default_config_data = {
+    'data_path': os.path.abspath(os.path.expanduser(os.path.expandvars('~/pyattck/data'))),
+    'enterprise_attck_json': "https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json",
+    'pre_attck_json': "https://raw.githubusercontent.com/mitre/cti/master/pre-attack/pre-attack.json",
+    'mobile_attck_json': "https://raw.githubusercontent.com/mitre/cti/master/mobile-attack/mobile-attack.json",
+    'nist_controls_json': "https://raw.githubusercontent.com/center-for-threat-informed-defense/attack-control-framework-mappings/master/frameworks/nist800-53-r4/stix/nist800-53-r4-controls.json",
+    'generated_attck_json': "https://github.com/swimlane/pyattck/blob/master/generated_attck_data.json?raw=True",
+    'generated_nist_json': "https://github.com/swimlane/pyattck/blob/master/attck_to_nist_controls.json?raw=True"
+}
+
+### Testing Default Configuration Settings
+
+def test_default_configuration_settings_set(attck_configuration):
+    assert attck_configuration.use_config == False
+    assert attck_configuration.save_config == False
+    assert attck_configuration.config_file_path == os.path.abspath(os.path.expanduser(os.path.expandvars('~/pyattck/config.yml')))
+    assert attck_configuration.data_path == os.path.abspath(os.path.expanduser(os.path.expandvars(default_config_data.get('data_path'))))
+
+def test_configuration_save_config(attck_configuration):
+    from pyattck import Attck
+    attck = Attck(save_config=True)
+    assert attck_configuration.save_config == True
+    assert isinstance(attck_configuration.config_data, dict)
+    assert attck_configuration.data_path == attck_configuration.config_data.get('data_path')
+
+@pytest.mark.parametrize(
+    'target_attribute', 
+    ['enterprise_attck_json', 'pre_attck_json', 'mobile_attck_json', 'nist_controls_json', 'generated_attck_json', 'generated_nist_json']
+)
+def test_default_configuration_settings_jsons(attck_configuration, target_attribute):
+    assert getattr(attck_configuration, target_attribute) == default_config_data[target_attribute]
+
+
+def test_configuration_data_can_be_file_path_location(attck_configuration):
+    attck_configuration.use_config = False
+    attck_configuration.save_config = False
+
+    enterprise_temp_value = get_random_file_or_url()
+    pre_attck_temp_value = get_random_file_or_url()
+    mobile_temp_value = get_random_file_or_url()
+    nist_controls_temp_value = get_random_file_or_url()
+    generated_attck_temp_value = get_random_file_or_url()
+    generated_nist_temp_value = get_random_file_or_url()
+    attck_configuration.enterprise_attck_json = enterprise_temp_value
+    attck_configuration.pre_attck_json = pre_attck_temp_value
+    attck_configuration.mobile_attck_json = mobile_temp_value
+    attck_configuration.nist_controls_json = nist_controls_temp_value
+    attck_configuration.generated_nist_json = generated_nist_temp_value
+    attck_configuration.generated_attck_json = generated_attck_temp_value
+
+    config_data = attck_configuration.config_data
+
+    assert attck_configuration.enterprise_attck_json == enterprise_temp_value
+    assert config_data['enterprise_attck_json'] == enterprise_temp_value
+    attck_configuration.enterprise_attck_json = default_config_data['enterprise_attck_json']
+
+    assert attck_configuration.pre_attck_json == pre_attck_temp_value
+    assert config_data['pre_attck_json'] == pre_attck_temp_value
+    attck_configuration.pre_attck_json = default_config_data['pre_attck_json']
+
+    assert attck_configuration.mobile_attck_json == mobile_temp_value
+    assert config_data['mobile_attck_json'] == mobile_temp_value
+    attck_configuration.mobile_attck_json = default_config_data['mobile_attck_json']
+
+    assert attck_configuration.nist_controls_json == nist_controls_temp_value
+    assert config_data['nist_controls_json'] == nist_controls_temp_value
+    attck_configuration.nist_controls_json = default_config_data['nist_controls_json']
+
+    assert attck_configuration.generated_attck_json == generated_attck_temp_value
+    assert config_data['generated_attck_json'] == generated_attck_temp_value
+    attck_configuration.generated_attck_json = default_config_data['generated_attck_json']
+
+    assert attck_configuration.generated_nist_json == generated_nist_temp_value
+    assert config_data['generated_nist_json'] == generated_nist_temp_value
+    attck_configuration.generated_nist_json = default_config_data['generated_nist_json']
+
+    attck_configuration.save_config = True
+    attck_configuration.config_data
+
+
+def test_configuration_settings_use_config_sets_config_values(attck_configuration):
+    attck_configuration.use_config = True
+    assert attck_configuration.use_config == True
+    with tempfile.NamedTemporaryFile('w+', dir=os.path.dirname(os.path.abspath(__file__)), suffix=random.choice(['.json', '.yml', '.yaml'])) as f:
+        yaml.dump(default_config_data, f)
+        attck_configuration.config_file_path = f.name
+        assert attck_configuration.config_data
+        assert isinstance(attck_configuration.config_data, dict)
+        assert attck_configuration.data_path == os.path.abspath(os.path.expanduser(os.path.expandvars(default_config_data.get('data_path'))))
+        assert attck_configuration.enterprise_attck_json == default_config_data['enterprise_attck_json']
+        assert attck_configuration.pre_attck_json == default_config_data['pre_attck_json']
+        assert attck_configuration.mobile_attck_json == default_config_data['mobile_attck_json']
+        assert attck_configuration.nist_controls_json == default_config_data['nist_controls_json']
+        assert attck_configuration.generated_attck_json == default_config_data['generated_attck_json']
+        assert attck_configuration.generated_nist_json == default_config_data['generated_nist_json']
+
+def test_use_config_defaults_to_set_config_data(attck_configuration):
+    attck_configuration.use_config = True
+    assert attck_configuration.use_config == True
+    with tempfile.NamedTemporaryFile(dir=os.path.dirname(os.path.abspath(__file__)), suffix=random.choice(['.json', '.yml', '.yaml'])) as f:
+        f.write(b'')
+        attck_configuration.config_file_path = f.name
+        assert attck_configuration.config_data
+        assert isinstance(attck_configuration.config_data, dict)
+        assert attck_configuration.config_data.get('data_path')
+        assert attck_configuration.config_data.get('enterprise_attck_json')
+        assert attck_configuration.config_data.get('pre_attck_json')
+        assert attck_configuration.config_data.get('mobile_attck_json')
+        assert attck_configuration.config_data.get('nist_controls_json')
+        assert attck_configuration.config_data.get('generated_attck_json')
+        assert attck_configuration.config_data.get('generated_nist_json')
+
+def test_configuration_settings_save_config(attck_configuration):
+    attck_configuration.enterprise_attck_json = default_config_data['enterprise_attck_json']
+    attck_configuration.pre_attck_json = default_config_data['pre_attck_json']
+    attck_configuration.mobile_attck_json = default_config_data['mobile_attck_json']
+    attck_configuration.nist_controls_json = default_config_data['nist_controls_json']
+    attck_configuration.generated_attck_json = default_config_data['generated_attck_json']
+    attck_configuration.generated_nist_json = default_config_data['generated_nist_json']
+    attck_configuration.save_config = False
+    temp_value = tempfile.NamedTemporaryFile(dir=os.path.dirname(os.path.abspath(__file__)), suffix='.yml').name
+    attck_configuration.config_file_path = temp_value
+    assert attck_configuration.config_file_path == temp_value
+    temp_dir = tempfile.TemporaryDirectory(dir=os.path.dirname(os.path.abspath(__file__))).name
+    attck_configuration.data_path = temp_dir
+    assert attck_configuration.data_path == temp_dir
+
+
+def test_configuration_request_kwargs(attck_configuration):
+    assert attck_configuration.requests_kwargs == {}
+    attck_configuration.requests_kwargs = {
+        'verify': False,
+        'proxies': {
+            'http': 'http://10.10.1.10:3128',
+            'https': 'http://10.10.1.10:1080',
+        }
+    }
+    assert isinstance(attck_configuration.requests_kwargs, dict)
+    assert attck_configuration.requests_kwargs == {
+        'verify': False,
+        'proxies': {
+            'http': 'http://10.10.1.10:3128',
+            'https': 'http://10.10.1.10:1080',
+        }
+    }