from textwrap import dedent
import attack_flow.graphviz
from attack_flow.model import (
AttackAction,
AttackCondition,
)
from .fixtures import get_flow_bundle, get_tree_bundle
def test_convert_attack_flow_to_graphviz():
output = attack_flow.graphviz.convert_attack_flow(get_flow_bundle())
assert output == dedent(
"""\
digraph {
\tlabel=<My Flow
(missing description)
Author: Jane Doe <jdoe@example.com>
Created: 2022-08-25 19:26:31
Modified: 2022-08-25 19:26:31>;
\tlabelloc="t";
\t"attack-action--52f2c35a-fa2a-45a4-b84c-46ad9498071f" [label=<
| Action: T1 |
| Name | Action 1 |
| Description | Description of action 1 |
| Confidence | Very Probable |
> shape=plaintext]
\t"attack-action--52f2c35a-fa2a-45a4-b84c-46ad9498071f" -> "attack-condition--64d5bf0b-6acc-4f43-b0f2-aa93a219897a" [label=effect]
\t"attack-action--dd3820fa-bae3-4270-8000-5c4642fa780c" [label=<| Action |
| Name | Action 2 |
| Description | Description of action 2 |
| Confidence | Very Probable |
> shape=plaintext]
\t"attack-action--dd3820fa-bae3-4270-8000-5c4642fa780c" -> "attack-asset--4ae37379-6a11-44c1-b6a8-d11733cfac06" [label=asset]
\t"attack-action--a0847849-a533-4b1f-a94a-720bbd25fc17" [label=<| Action: T3 |
| Name | Action 3 |
| Description | Description of action 3 |
| Confidence | Very Probable |
> shape=plaintext]
\t"attack-action--7ddab166-c83e-4c79-a701-a0dc2a905dd3" [label=<| Action: T4 |
| Name | Action 4 |
| Description | Description of action 4 |
| Confidence | Very Probable |
> shape=plaintext]
\t"attack-condition--64d5bf0b-6acc-4f43-b0f2-aa93a219897a" [label=<| Condition |
| Description | My condition |
> shape=plaintext]
\t"attack-condition--64d5bf0b-6acc-4f43-b0f2-aa93a219897a" -> "attack-operator--8932b181-be87-4f81-851a-ab0b4288406a" [label=on_true]
\t"attack-condition--64d5bf0b-6acc-4f43-b0f2-aa93a219897a" -> "attack-action--7ddab166-c83e-4c79-a701-a0dc2a905dd3" [label=on_false]
\t"attack-operator--8932b181-be87-4f81-851a-ab0b4288406a" [label=OR fillcolor="#ff9900" shape=circle style=filled]
\t"attack-operator--8932b181-be87-4f81-851a-ab0b4288406a" -> "attack-action--dd3820fa-bae3-4270-8000-5c4642fa780c" [label=effect]
\t"attack-operator--8932b181-be87-4f81-851a-ab0b4288406a" -> "attack-action--a0847849-a533-4b1f-a94a-720bbd25fc17" [label=effect]
\t"attack-asset--4ae37379-6a11-44c1-b6a8-d11733cfac06" [label=<| Asset: My Asset |
| Description | |
> shape=plaintext]
\t"attack-asset--4ae37379-6a11-44c1-b6a8-d11733cfac06" -> "infrastructure--79d21912-36b7-4af9-8958-38949dd0d6de" [label=object]
\t"infrastructure--79d21912-36b7-4af9-8958-38949dd0d6de" [label=<| Infrastructure |
| Name | My Infra |
> shape=plaintext]
\t"infrastructure--a75c83f7-147e-4695-b173-0981521b2f01" [label=<| Infrastructure |
| Name | Test Infra |
| Infrastructure Types | workstation |
> shape=plaintext]
\t"attack-action--dd3820fa-bae3-4270-8000-5c4642fa780c" -> "infrastructure--a75c83f7-147e-4695-b173-0981521b2f01" [label="related-to"]
}
"""
)
def test_convert_attack_tree_to_graphviz():
output = attack_flow.graphviz.convert_attack_tree(get_tree_bundle())
assert output == dedent(
"""\
digraph {
\tgraph [rankdir=BT]
\tlabel=<My Flow
(missing description)
Author: Jane Doe <jdoe@example.com>
Created: 2022-08-25 19:26:31
Modified: 2022-08-25 19:26:31>;
\tlabelloc="t";
\t"attack-action--d63857d5-1043-45a4-9397-40ef68db4c5f" [label=<| Action |
| Name | Action 1 |
| Description | Description of action 2 |
| Confidence | Very Probable |
> shape=plaintext]
\t"attack-action--d63857d5-1043-45a4-9397-40ef68db4c5f" -> "attack-action--1994e9f2-11f1-489a-a5e7-3ad4cfd8890a"
\t"attack-action--1994e9f2-11f1-489a-a5e7-3ad4cfd8890a" [label=<| OR T3 |
| Name | My Or Operator |
| Description | this is the description |
| Confidence | Very Probable |
> shape=plaintext]
\t"attack-action--1994e9f2-11f1-489a-a5e7-3ad4cfd8890a" -> "attack-action--a0847849-a533-4b1f-a94a-720bbd25fc17"
\t"attack-action--24fc6003-33f6-4dd7-a929-b6031927940f" [label=<| Action |
| Name | Action 2 |
| Description | Description of action 2 |
| Confidence | Very Probable |
> shape=plaintext]
\t"attack-action--24fc6003-33f6-4dd7-a929-b6031927940f" -> "attack-action--1994e9f2-11f1-489a-a5e7-3ad4cfd8890a"
\t"attack-action--a0847849-a533-4b1f-a94a-720bbd25fc17" [label=<| Action: T3 |
| Name | Action 3 |
| Description | Description of action 3 |
| Confidence | Very Probable |
> shape=plaintext]
\t"attack-action--a0847849-a533-4b1f-a94a-720bbd25fc17" -> "attack-asset--4ae37379-6a11-44c1-b6a8-d11733cfac06"
\t"infrastructure--79d21912-36b7-4af9-8958-38949dd0d6de" [label=<| Infrastructure |
| Name | My Infra |
> shape=plaintext]
\t"attack-asset--4ae37379-6a11-44c1-b6a8-d11733cfac06" [label=<| Asset: My Asset |
| Description | |
> shape=plaintext]
\t"attack-asset--4ae37379-6a11-44c1-b6a8-d11733cfac06" -> "infrastructure--79d21912-36b7-4af9-8958-38949dd0d6de" [label=object]
\t"infrastructure--a75c83f7-147e-4695-b173-0981521b2f01" [label=<| Infrastructure |
| Name | Test Infra |
| Infrastructure Types | workstation |
> shape=plaintext]
\t"attack-action--24fc6003-33f6-4dd7-a929-b6031927940f" -> "infrastructure--a75c83f7-147e-4695-b173-0981521b2f01" [label="related-to"]
\t"attack-condition--64d5bf0b-6acc-4f43-b0f2-aa93a219897a" [label=<| Condition |
| Description | My condition |
> shape=plaintext]
\t"attack-condition--64d5bf0b-6acc-4f43-b0f2-aa93a219897a" -> "attack-action--d63857d5-1043-45a4-9397-40ef68db4c5f" [label=on_true]
\t"attack-condition--64d5bf0b-6acc-4f43-b0f2-aa93a219897a" -> "attack-action--24fc6003-33f6-4dd7-a929-b6031927940f" [label=on_false]
}
"""
)
def test_wrap_action_description():
"""Long descriptions should be wrapped."""
action = AttackAction(
id="attack-action--2f375dbd-4d6e-4036-9efa-d67f7fc93d1e",
technique_id="T1",
name="Action 1",
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
)
assert (
attack_flow.graphviz._get_action_label(action)
== '<| Action: T1 |
| Name | Action 1 |
| Description | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. |
| Confidence | Very Probable |
>'
)
def test_wrap_condition_description():
"""Long descriptions should be wrapped."""
condition = AttackCondition(
id="attack-condition--64d5bf0b-6acc-4f43-b0f2-aa93a219897a",
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
on_true_refs=[],
on_false_refs=[],
)
assert (
attack_flow.graphviz._get_condition_label(condition)
== '<| Condition |
| Description | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. |
>'
)
def test_action_label():
action = AttackAction(
id="attack-action--b5696498-66e8-41b6-87e1-19d2657ac48b",
name="My technique",
description="This technique has no ID to render in the header.",
)
assert (
attack_flow.graphviz._get_action_label(action)
== '<| Action |
| Name | My technique |
| Description | This technique has no ID to render in the header. |
| Confidence | Very Probable |
>'
)
def test_get_operator_label():
action = AttackAction(
id="attack-action--b5696498-66e8-41b6-87e1-19d2657ac48b",
name="My technique",
description="This technique has no ID to render in the header.",
)
assert (
attack_flow.graphviz._get_operator_label(action, operator_type="AND")
== '<| AND |
| Name | My technique |
| Description | This technique has no ID to render in the header. |
| Confidence | Very Probable |
>'
)
def test_get_attack_tree_action_label():
action = AttackAction(
id="attack-action--b5696498-66e8-41b6-87e1-19d2657ac48b",
name="My technique",
description="This technique has no ID to render in the header.",
)
assert (
attack_flow.graphviz._get_attack_tree_action_label(action)
== '<| Action |
| Name | My technique |
| Description | This technique has no ID to render in the header. |
| Confidence | Very Probable |
>'
)