adtree-viz
Intro
An Attack-Defense Tree modelling lib that allows user to model attack-defense scenarios using an internal DSL.
Project inspired by https://github.com/hyakuhei/attackTrees and https://github.com/tahti/ADTool2.
The main goals are:
- add support for AND nodes
- be able to break down a large tree into multiple subtrees.
- keep it simple, only Attack and Defense nodes
Usage
Requirements:
GraphvizPython 3.9
Install the library
pip install adtree-viz
Quick start
from adtree.models import Attack, Defence, AndGate, ADTree
from adtree.renderer import Renderer
from adtree.themes import RedBlueFillTheme
tree = ADTree("REFS.01", Attack("the goal", [
Attack("path1", [
Defence("defend path1", [
Attack("path1 defence defeated")
])
]),
Attack("path2", [
Attack("path2.1"),
AndGate([
Attack("path3.1"),
Attack("path3.2"),
]),
]),
]))
theme = RedBlueFillTheme()
renderer = Renderer(theme=theme, output_format="png", view=True)
renderer.render(tree=tree, filename="my-adtree")
The above should produce an attack-defence tree like this:
Composing trees
Trees can be composed of multiple subtrees.
Which of the subtrees get expanded is decided at render time based on the subtrees_to_expand variable.
from adtree.models import Attack, ADTree, ExternalADTree
from adtree.renderer import Renderer
from adtree.themes import NoFormatTheme
some_external_ref = ExternalADTree("EXT.01", "External resource covered by other docs")
some_internal_ref1 = ADTree("INT.01", root_node=Attack("internal path1", [
Attack("path 1.1", [
ADTree("INT.01.A", Attack("nested path 1.1A"))
])
]))
some_internal_ref2 = ADTree("INT.02", root_node=Attack("internal path2", [
Attack("path 2.1")
]))
tree = ADTree("REFS.01", Attack("node1", [
some_external_ref,
some_internal_ref1,
some_internal_ref2
]))
theme = NoFormatTheme()
renderer = Renderer(theme=theme, output_format="png", view=False)
# Default is to not expand
renderer.render(tree=tree, filename="default")
# Optionally expand some nodes
renderer.render(tree=tree, subtrees_to_expand=[some_internal_ref1], filename="partially_expanded")
The above will render two files.
One with all the subtrees collapsed (the default):
And another file with one subtree expanded:
Analysing trees
Currently, there is only one analyser available, the IsDefendedAnalyser. Traverse the tree and mark each nodes as either defended or undefended A node is considered defended if:
- is a Defence node and has no children
- is an Attack node and has a direct defended Defence node as child
- is an Attack or Defence node and all child nodes are defended nodes
- is an AndGate and at least one child node is defended
Example with custom rendering of the defended nodes
from adtree.models import NodeType, Node, Attack, ADTree, Defence, AndGate
from adtree.analysers import IsDefendedAnalyser
from adtree.renderer import Renderer
from adtree.themes import NoFormatTheme
class CustomIsDefendedTheme(NoFormatTheme):
def get_node_attrs_for(self, node: Node):
metadata_attrs = {
"style": "filled"
}
if node.get_node_type() == NodeType.DEFENCE:
metadata_attrs |= {
"shape": "box",
}
if node.get_node_type() == NodeType.AND_GATE:
metadata_attrs |= {
"shape": "triangle",
}
if node.has_metadata(IsDefendedAnalyser.METADATA_KEY):
fillcolor = "#C8FFCB" if node.get_metadata(IsDefendedAnalyser.METADATA_KEY) else "#FFD3D6"
metadata_attrs |= {
"fillcolor": fillcolor,
}
return metadata_attrs
tree = ADTree("REFS.01", Attack("the goal", [
Attack("path1", [
Defence("defend path1", [
Attack("path1 defence defeated")
])
]),
Attack("path2", [
Attack("path2.1", [
Defence("def2.1"),
Attack("path2.1.1")
]),
AndGate([
Attack("path3.1"),
Attack("path3.2", [
Defence("defended")
]),
]),
]),
]))
analyser = IsDefendedAnalyser()
analyser.analyse_tree(tree)
theme = CustomIsDefendedTheme()
renderer = Renderer(theme=theme, output_format="png", view=False)
# Default is to not expand
renderer.render(tree=tree, filename="default")
The above should produce an attack-defence tree like this:
Development
Create a venv
python3.9 -m venv venv
Activate
. venv/bin/activate
Install deps
pip install -r requirements.txt
Run tests
PYTHONPATH=src python -m pytest
Run individual test file
PYTHONPATH=src python -m pytest ./test/adtree/test_theme.py
Run individual test methods
PYTHONPATH=src python -m pytest --capture=no ./test/adtree/test_theme.py -k "metadata"
Release to Github and PyPi
Create tag and push
./release.sh
Manually build and release
Run the below to generate a distributable archive:
python3 -m build
The adtree-viz-x.xx.x.tar.gz archive can be found in the dist folder.
Deploy to PyPi
python3 -m twine upload -r pypi dist/*
# Use __token__ as username
# Use PyPi API TOKEN as password
Metadata
Release files for adtree-viz 0.0.10
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| adtree-viz-0.0.10.tar.gz | 11.2 kB | Without provenance | Details for adtree-viz-0.0.10.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| adtree_viz-0.0.10-py3-none-any.whl | Python 3 | none | any | Details for adtree_viz-0.0.10-py3-none-any.whl |
Total release size: 22.1 kB
Release files / adtree-viz-0.0.10.tar.gz
| Download URL | adtree-viz-0.0.10.tar.gz |
|---|---|
| Size | 11.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
752ca90acdedf81faaeb218b5fecfed7dd11abc0091066c96ebd542696648643
|
|
BLAKE2b-256 checksum How to use checksums |
3766cb169f0f739369ffce799e7907b657495402f6a2ac78720952372f6edb92
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.11.1
|
Release files / adtree_viz-0.0.10-py3-none-any.whl
| Download URL | adtree_viz-0.0.10-py3-none-any.whl |
|---|---|
| Size | 10.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f7aeacd303b2a2c43fd7a2099ebed74c0b66dad7c99f992a269d1e1c82712dba
|
|
BLAKE2b-256 checksum How to use checksums |
e1140a620a0e1dc0ecf347b717be1cb0b7d6808013759c8e2e32b4afb925c384
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.11.1
|
Security for adtree-viz 0.0.10
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.