Skip to content

Commit

Permalink
Merge pull request #123 from swimlane/7_0_0
Browse files Browse the repository at this point in the history
7.0.0
  • Loading branch information
MSAdministrator authored Aug 25, 2022
2 parents bd44fb4 + 30ae9b9 commit d209890
Show file tree
Hide file tree
Showing 17 changed files with 583 additions and 84 deletions.
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ per-file-ignores =
logger.py:F841,
configuration.py:E501,
attck.py:E501
base.py:E501
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 7.0.0 - 2022-08-18

- Added an interactive console menu system. You can access it by using the --interactive flag.

## 6.1.0 - 2022-06-13

- Updated to pyattck-data 2.1.0
Expand Down Expand Up @@ -33,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Fixed issue with mitigations not being accessible in enterprise techniques
- Added ability to access nested subtechniques (or not) using
nested_subtechniques paramater when instantiating Attck object
nested_techniques parameter when instantiating Attck object

## 2.0.5 - 2020-05-19

Expand Down
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
```
A Python package to interact with MITRE ATT&CK Frameworks

> Current Version is 6.1.0
> Current Version is 7.0.0
**pyattck** is a light-weight framework for MITRE ATT&CK Frameworks. This package extracts details from the MITRE Enterprise, PRE-ATT&CK, Mobile, and ICS Frameworks.

Expand Down Expand Up @@ -50,6 +50,7 @@ The **pyattck** package allows you to:
* Access sub-techniques as nested objects or you can turn it off and access as normal technique
* Access compliance controls (currently NIST 800-53 v5) related to a MITRE ATT&CK Technique
* pyattck now utilizes structured data models. More information can be found at [pyattck-data](https://github.com/swimlane/pyattck-data)
* Run an interactive console menu system to access pyattck data

# Table of Contents

Expand Down Expand Up @@ -153,6 +154,28 @@ Here are the accessible objects under the [ICS](docs/ics.md) property:

For more information on object types under the `ics` property, see [ICS](docs/ics.md).

## Interactive Menu Usage

To utilize the new interactive menu system within pyattck, you must set `interactive` to `True`. By doing so, it will launch the interactive console menu system.

Using a script your can launch this by running:

```python
from pyattck import Attck

Attck(interactive=True)
```

Or you can also run interactive mode on the command line:

```bash
pyattck --interactive
```

Checkout a gif example below:

![](images/pyattck_interactive_menu.gif)

## Configuration

`pyattck` allows you to configure if you store external data and where it is stored.
Expand Down
22 changes: 22 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,28 @@ Here are the accessible objects under the [ICS](ics.md) property:

For more information on object types under the `ics` property, see [ICS](ics.md).

## Interactive Menu Usage

To utilize the new interactive menu system within pyattck, you must set `interactive` to `True`. By doing so, it will launch the interactive console menu system.

Using a script your can launch this by running:

```python
from pyattck import Attck

Attck(interactive=True)
```

Or you can also run interactive mode on the command line:

```bash
pyattck --interactive
```

Checkout a gif example below:

![](images/pyattck_interactive_menu.gif)

## Configuration

`pyattck` allows you to configure if you store external data and where it is stored.
Expand Down
Binary file added images/pyattck_interactive_menu.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
210 changes: 145 additions & 65 deletions poetry.lock

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions pyattck/attck.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class Attck(Base):
Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json.
generated_nist_json (str, optional): A URL or local file path to the Generated NIST Controls Mapping Json file.
Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json.
interactive (bool, optional): If True, runs the interactive menu within pyattck. Default is False.
kwargs (dict, optional): Provided kwargs will be passed to any HTTP requests using the Requests library.
Defaults to None.
Expand All @@ -152,6 +153,7 @@ def __init__(
ics_attck_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_ics_attck_v1.json",
nist_controls_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json",
generated_nist_json="https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json",
interactive=False,
**kwargs
):
"""
Expand Down Expand Up @@ -216,6 +218,7 @@ def __init__(
Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/merged_nist_controls_v1.json
generated_nist_json (str, optional): A URL or local file path to the Generated NIST Controls Mapping Json
file. Defaults to https://swimlane-pyattck.s3.us-west-2.amazonaws.com/attck_to_nist_controls.json.
interactive (bool, optional): If True, runs the interactive menu within pyattck. Default is False.
kwargs (dict, optional): Provided kwargs will be passed to any HTTP requests using the Requests library.
Defaults to None.
"""
Expand All @@ -235,6 +238,10 @@ def __init__(
generated_nist_json=generated_nist_json,
),
)
if interactive:
from .utils.interactive import Interactive

Interactive(self).generate()

@property
def enterprise(self):
Expand Down
13 changes: 13 additions & 0 deletions pyattck/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,16 @@
class Base(metaclass=LoggingBase):

config = None
LOGO = "CgouX19fX19fICAgX19fXyAgICBfX19fICBfX18gICAuX19fX19fX19fX18uX19fX19fX19fX18uICBfX19fX18gIF9fICBfX18KfCAgIF8gIFwgIFwgICBcICAvICAgLyAvICAgXCAgfCAgICAgICAgICAgfCAgICAgICAgICAgfCAvICAgICAgfHwgIHwvICAvCnwgIHxfKSAgfCAgXCAgIFwvICAgLyAvICBeICBcIGAtLS18ICB8LS0tLWAtLS18ICB8LS0tLWB8ICAsLS0tLSd8ICAnICAvCnwgICBfX18vICAgIFxfICAgIF8vIC8gIC9fXCAgXCAgICB8ICB8ICAgICAgICB8ICB8ICAgICB8ICB8ICAgICB8ICAgIDwKfCAgfCAgICAgICAgICB8ICB8ICAvICBfX19fXyAgXCAgIHwgIHwgICAgICAgIHwgIHwgICAgIHwgIGAtLS0tLnwgIC4gIFwKfCBffCAgICAgICAgICB8X198IC9fXy8gICAgIFxfX1wgIHxfX3wgICAgICAgIHxfX3wgICAgICBcX19fX19ffHxfX3xcX19cCgo="
FRAMEWORKS = ["enterprise", "ics", "mobile", "preattack"]
ATTCK_TYPES = [
"actors",
"controls",
"data_components",
"data_sources",
"malwares",
"mitigations",
"tactics",
"techniques",
"tools",
]
3 changes: 1 addition & 2 deletions pyattck/__main__.py → pyattck/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@


def main(args=None):
attck = Attck()
fire.Fire(attck)
fire.Fire(Attck)


if __name__ == "__main__":
Expand Down
20 changes: 13 additions & 7 deletions pyattck/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ class Configuration:
@nist_controls_json.validator
@generated_nist_json.validator
def _validate_json_value(self, attribute, value):
valid = False
valid = is_path(value)
valid = is_url(value)
if not valid:
path_valid = False
url_valid = False
path_valid = is_path(value)
url_valid = is_url(value)
if not path_valid and not url_valid:
raise Exception("The provided value is neither a URL or file path")


Expand Down Expand Up @@ -145,10 +146,15 @@ def get_data(self, value: str) -> dict:
else:
return self._read_from_disk(getattr(self.config, value))

def _save_config(self, config_file_path: str, config_dict: dict) -> None:
"""Saves the config to the provided path."""
self._save_to_disk(config_file_path, config_dict)
self._save_json_data()

def __attrs_post_init__(self):
"""Contains options and configuration for pyattck."""
if self.save_config:
self._save_to_disk(self.config_file_path, asdict(self.config))
self._save_json_data()
self._save_config(config_file_path=self.config_file_path, config_dict=asdict(self.config))
if self.use_config:
self.config = self._read_from_disk(self.config_file_path)
self._save_config(config_file_path=self.config_file_path, config_dict=asdict(self.config))
object.__setattr__(self, "config", self._read_from_disk(self.config_file_path))
32 changes: 32 additions & 0 deletions pyattck/utils/interactive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Main interactive menu for pyattck."""
from ..base import Base
from .layout import CustomLayout
from .menu import Menu


class Interactive(Base):
"""Generates the interactive menu, options, and drives the display of the menu system."""

_framework = None
_type = None
_object = None

def __init__(self, attck_instance) -> None:
"""A pyattck Attck instance class."""
self._attck_instance = attck_instance

def generate(self):
"""Generates the interactive console for pyattck."""
main_menu = Menu()
main_menu.prompt = "Select the appropriate MITRE ATT&CK Framework:\n"
for framework in self.FRAMEWORKS:
obj_menu = Menu()
obj_menu.prompt = "Select an entity below:\n"
for obj in dir(getattr(self._attck_instance, framework)):
if not obj.startswith("_") and obj in self.ATTCK_TYPES:
item_menu = Menu()
for item in getattr(getattr(self._attck_instance, framework), obj):
item_menu.add_option(getattr(item, "name"), CustomLayout(item))
obj_menu.add_option(obj, item_menu)
main_menu.add_option(framework, obj_menu, True)
main_menu.run()
Loading

0 comments on commit d209890

Please sign in to comment.