mirror of
https://github.com/wshobson/agents.git
synced 2026-03-18 09:37:15 +00:00
Add domain expert agents with comprehensive skill sets: - service-mesh-expert (cloud-infrastructure): Istio/Linkerd patterns, mTLS, observability - event-sourcing-architect (backend-development): CQRS, event stores, projections, sagas - vector-database-engineer (llm-application-dev): embeddings, similarity search, hybrid search - monorepo-architect (developer-essentials): Nx, Turborepo, Bazel, pnpm workspaces - threat-modeling-expert (security-scanning): STRIDE, attack trees, security requirements Update all documentation to reflect correct counts: - 67 plugins, 99 agents, 107 skills, 71 commands
686 lines
22 KiB
Markdown
686 lines
22 KiB
Markdown
---
|
|
name: attack-tree-construction
|
|
description: Build comprehensive attack trees to visualize threat paths. Use when mapping attack scenarios, identifying defense gaps, or communicating security risks to stakeholders.
|
|
---
|
|
|
|
# Attack Tree Construction
|
|
|
|
Systematic attack path visualization and analysis.
|
|
|
|
## When to Use This Skill
|
|
|
|
- Visualizing complex attack scenarios
|
|
- Identifying defense gaps and priorities
|
|
- Communicating risks to stakeholders
|
|
- Planning defensive investments
|
|
- Penetration test planning
|
|
- Security architecture review
|
|
|
|
## Core Concepts
|
|
|
|
### 1. Attack Tree Structure
|
|
|
|
```
|
|
[Root Goal]
|
|
|
|
|
┌────────────┴────────────┐
|
|
│ │
|
|
[Sub-goal 1] [Sub-goal 2]
|
|
(OR node) (AND node)
|
|
│ │
|
|
┌─────┴─────┐ ┌─────┴─────┐
|
|
│ │ │ │
|
|
[Attack] [Attack] [Attack] [Attack]
|
|
(leaf) (leaf) (leaf) (leaf)
|
|
```
|
|
|
|
### 2. Node Types
|
|
|
|
| Type | Symbol | Description |
|
|
|------|--------|-------------|
|
|
| **OR** | Oval | Any child achieves goal |
|
|
| **AND** | Rectangle | All children required |
|
|
| **Leaf** | Box | Atomic attack step |
|
|
|
|
### 3. Attack Attributes
|
|
|
|
| Attribute | Description | Values |
|
|
|-----------|-------------|--------|
|
|
| **Cost** | Resources needed | $, $$, $$$ |
|
|
| **Time** | Duration to execute | Hours, Days, Weeks |
|
|
| **Skill** | Expertise required | Low, Medium, High |
|
|
| **Detection** | Likelihood of detection | Low, Medium, High |
|
|
|
|
## Templates
|
|
|
|
### Template 1: Attack Tree Data Model
|
|
|
|
```python
|
|
from dataclasses import dataclass, field
|
|
from enum import Enum
|
|
from typing import List, Dict, Optional, Union
|
|
import json
|
|
|
|
class NodeType(Enum):
|
|
OR = "or"
|
|
AND = "and"
|
|
LEAF = "leaf"
|
|
|
|
|
|
class Difficulty(Enum):
|
|
TRIVIAL = 1
|
|
LOW = 2
|
|
MEDIUM = 3
|
|
HIGH = 4
|
|
EXPERT = 5
|
|
|
|
|
|
class Cost(Enum):
|
|
FREE = 0
|
|
LOW = 1
|
|
MEDIUM = 2
|
|
HIGH = 3
|
|
VERY_HIGH = 4
|
|
|
|
|
|
class DetectionRisk(Enum):
|
|
NONE = 0
|
|
LOW = 1
|
|
MEDIUM = 2
|
|
HIGH = 3
|
|
CERTAIN = 4
|
|
|
|
|
|
@dataclass
|
|
class AttackAttributes:
|
|
difficulty: Difficulty = Difficulty.MEDIUM
|
|
cost: Cost = Cost.MEDIUM
|
|
detection_risk: DetectionRisk = DetectionRisk.MEDIUM
|
|
time_hours: float = 8.0
|
|
requires_insider: bool = False
|
|
requires_physical: bool = False
|
|
|
|
|
|
@dataclass
|
|
class AttackNode:
|
|
id: str
|
|
name: str
|
|
description: str
|
|
node_type: NodeType
|
|
attributes: AttackAttributes = field(default_factory=AttackAttributes)
|
|
children: List['AttackNode'] = field(default_factory=list)
|
|
mitigations: List[str] = field(default_factory=list)
|
|
cve_refs: List[str] = field(default_factory=list)
|
|
|
|
def add_child(self, child: 'AttackNode') -> None:
|
|
self.children.append(child)
|
|
|
|
def calculate_path_difficulty(self) -> float:
|
|
"""Calculate aggregate difficulty for this path."""
|
|
if self.node_type == NodeType.LEAF:
|
|
return self.attributes.difficulty.value
|
|
|
|
if not self.children:
|
|
return 0
|
|
|
|
child_difficulties = [c.calculate_path_difficulty() for c in self.children]
|
|
|
|
if self.node_type == NodeType.OR:
|
|
return min(child_difficulties)
|
|
else: # AND
|
|
return max(child_difficulties)
|
|
|
|
def calculate_path_cost(self) -> float:
|
|
"""Calculate aggregate cost for this path."""
|
|
if self.node_type == NodeType.LEAF:
|
|
return self.attributes.cost.value
|
|
|
|
if not self.children:
|
|
return 0
|
|
|
|
child_costs = [c.calculate_path_cost() for c in self.children]
|
|
|
|
if self.node_type == NodeType.OR:
|
|
return min(child_costs)
|
|
else: # AND
|
|
return sum(child_costs)
|
|
|
|
def to_dict(self) -> Dict:
|
|
"""Convert to dictionary for serialization."""
|
|
return {
|
|
"id": self.id,
|
|
"name": self.name,
|
|
"description": self.description,
|
|
"type": self.node_type.value,
|
|
"attributes": {
|
|
"difficulty": self.attributes.difficulty.name,
|
|
"cost": self.attributes.cost.name,
|
|
"detection_risk": self.attributes.detection_risk.name,
|
|
"time_hours": self.attributes.time_hours,
|
|
},
|
|
"mitigations": self.mitigations,
|
|
"children": [c.to_dict() for c in self.children]
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class AttackTree:
|
|
name: str
|
|
description: str
|
|
root: AttackNode
|
|
version: str = "1.0"
|
|
|
|
def find_easiest_path(self) -> List[AttackNode]:
|
|
"""Find the path with lowest difficulty."""
|
|
return self._find_path(self.root, minimize="difficulty")
|
|
|
|
def find_cheapest_path(self) -> List[AttackNode]:
|
|
"""Find the path with lowest cost."""
|
|
return self._find_path(self.root, minimize="cost")
|
|
|
|
def find_stealthiest_path(self) -> List[AttackNode]:
|
|
"""Find the path with lowest detection risk."""
|
|
return self._find_path(self.root, minimize="detection")
|
|
|
|
def _find_path(
|
|
self,
|
|
node: AttackNode,
|
|
minimize: str
|
|
) -> List[AttackNode]:
|
|
"""Recursive path finding."""
|
|
if node.node_type == NodeType.LEAF:
|
|
return [node]
|
|
|
|
if not node.children:
|
|
return [node]
|
|
|
|
if node.node_type == NodeType.OR:
|
|
# Pick the best child path
|
|
best_path = None
|
|
best_score = float('inf')
|
|
|
|
for child in node.children:
|
|
child_path = self._find_path(child, minimize)
|
|
score = self._path_score(child_path, minimize)
|
|
if score < best_score:
|
|
best_score = score
|
|
best_path = child_path
|
|
|
|
return [node] + (best_path or [])
|
|
else: # AND
|
|
# Must traverse all children
|
|
path = [node]
|
|
for child in node.children:
|
|
path.extend(self._find_path(child, minimize))
|
|
return path
|
|
|
|
def _path_score(self, path: List[AttackNode], metric: str) -> float:
|
|
"""Calculate score for a path."""
|
|
if metric == "difficulty":
|
|
return sum(n.attributes.difficulty.value for n in path if n.node_type == NodeType.LEAF)
|
|
elif metric == "cost":
|
|
return sum(n.attributes.cost.value for n in path if n.node_type == NodeType.LEAF)
|
|
elif metric == "detection":
|
|
return sum(n.attributes.detection_risk.value for n in path if n.node_type == NodeType.LEAF)
|
|
return 0
|
|
|
|
def get_all_leaf_attacks(self) -> List[AttackNode]:
|
|
"""Get all leaf attack nodes."""
|
|
leaves = []
|
|
self._collect_leaves(self.root, leaves)
|
|
return leaves
|
|
|
|
def _collect_leaves(self, node: AttackNode, leaves: List[AttackNode]) -> None:
|
|
if node.node_type == NodeType.LEAF:
|
|
leaves.append(node)
|
|
for child in node.children:
|
|
self._collect_leaves(child, leaves)
|
|
|
|
def get_unmitigated_attacks(self) -> List[AttackNode]:
|
|
"""Find attacks without mitigations."""
|
|
return [n for n in self.get_all_leaf_attacks() if not n.mitigations]
|
|
|
|
def export_json(self) -> str:
|
|
"""Export tree to JSON."""
|
|
return json.dumps({
|
|
"name": self.name,
|
|
"description": self.description,
|
|
"version": self.version,
|
|
"root": self.root.to_dict()
|
|
}, indent=2)
|
|
```
|
|
|
|
### Template 2: Attack Tree Builder
|
|
|
|
```python
|
|
class AttackTreeBuilder:
|
|
"""Fluent builder for attack trees."""
|
|
|
|
def __init__(self, name: str, description: str):
|
|
self.name = name
|
|
self.description = description
|
|
self._node_stack: List[AttackNode] = []
|
|
self._root: Optional[AttackNode] = None
|
|
|
|
def goal(self, id: str, name: str, description: str = "") -> 'AttackTreeBuilder':
|
|
"""Set the root goal (OR node by default)."""
|
|
self._root = AttackNode(
|
|
id=id,
|
|
name=name,
|
|
description=description,
|
|
node_type=NodeType.OR
|
|
)
|
|
self._node_stack = [self._root]
|
|
return self
|
|
|
|
def or_node(self, id: str, name: str, description: str = "") -> 'AttackTreeBuilder':
|
|
"""Add an OR sub-goal."""
|
|
node = AttackNode(
|
|
id=id,
|
|
name=name,
|
|
description=description,
|
|
node_type=NodeType.OR
|
|
)
|
|
self._current().add_child(node)
|
|
self._node_stack.append(node)
|
|
return self
|
|
|
|
def and_node(self, id: str, name: str, description: str = "") -> 'AttackTreeBuilder':
|
|
"""Add an AND sub-goal (all children required)."""
|
|
node = AttackNode(
|
|
id=id,
|
|
name=name,
|
|
description=description,
|
|
node_type=NodeType.AND
|
|
)
|
|
self._current().add_child(node)
|
|
self._node_stack.append(node)
|
|
return self
|
|
|
|
def attack(
|
|
self,
|
|
id: str,
|
|
name: str,
|
|
description: str = "",
|
|
difficulty: Difficulty = Difficulty.MEDIUM,
|
|
cost: Cost = Cost.MEDIUM,
|
|
detection: DetectionRisk = DetectionRisk.MEDIUM,
|
|
time_hours: float = 8.0,
|
|
mitigations: List[str] = None
|
|
) -> 'AttackTreeBuilder':
|
|
"""Add a leaf attack node."""
|
|
node = AttackNode(
|
|
id=id,
|
|
name=name,
|
|
description=description,
|
|
node_type=NodeType.LEAF,
|
|
attributes=AttackAttributes(
|
|
difficulty=difficulty,
|
|
cost=cost,
|
|
detection_risk=detection,
|
|
time_hours=time_hours
|
|
),
|
|
mitigations=mitigations or []
|
|
)
|
|
self._current().add_child(node)
|
|
return self
|
|
|
|
def end(self) -> 'AttackTreeBuilder':
|
|
"""Close current node, return to parent."""
|
|
if len(self._node_stack) > 1:
|
|
self._node_stack.pop()
|
|
return self
|
|
|
|
def build(self) -> AttackTree:
|
|
"""Build the attack tree."""
|
|
if not self._root:
|
|
raise ValueError("No root goal defined")
|
|
return AttackTree(
|
|
name=self.name,
|
|
description=self.description,
|
|
root=self._root
|
|
)
|
|
|
|
def _current(self) -> AttackNode:
|
|
if not self._node_stack:
|
|
raise ValueError("No current node")
|
|
return self._node_stack[-1]
|
|
|
|
|
|
# Example usage
|
|
def build_account_takeover_tree() -> AttackTree:
|
|
"""Build attack tree for account takeover scenario."""
|
|
return (
|
|
AttackTreeBuilder("Account Takeover", "Gain unauthorized access to user account")
|
|
.goal("G1", "Take Over User Account")
|
|
|
|
.or_node("S1", "Steal Credentials")
|
|
.attack(
|
|
"A1", "Phishing Attack",
|
|
difficulty=Difficulty.LOW,
|
|
cost=Cost.LOW,
|
|
detection=DetectionRisk.MEDIUM,
|
|
mitigations=["Security awareness training", "Email filtering"]
|
|
)
|
|
.attack(
|
|
"A2", "Credential Stuffing",
|
|
difficulty=Difficulty.TRIVIAL,
|
|
cost=Cost.LOW,
|
|
detection=DetectionRisk.HIGH,
|
|
mitigations=["Rate limiting", "MFA", "Password breach monitoring"]
|
|
)
|
|
.attack(
|
|
"A3", "Keylogger Malware",
|
|
difficulty=Difficulty.MEDIUM,
|
|
cost=Cost.MEDIUM,
|
|
detection=DetectionRisk.MEDIUM,
|
|
mitigations=["Endpoint protection", "MFA"]
|
|
)
|
|
.end()
|
|
|
|
.or_node("S2", "Bypass Authentication")
|
|
.attack(
|
|
"A4", "Session Hijacking",
|
|
difficulty=Difficulty.MEDIUM,
|
|
cost=Cost.LOW,
|
|
detection=DetectionRisk.LOW,
|
|
mitigations=["Secure session management", "HTTPS only"]
|
|
)
|
|
.attack(
|
|
"A5", "Authentication Bypass Vulnerability",
|
|
difficulty=Difficulty.HIGH,
|
|
cost=Cost.LOW,
|
|
detection=DetectionRisk.LOW,
|
|
mitigations=["Security testing", "Code review", "WAF"]
|
|
)
|
|
.end()
|
|
|
|
.or_node("S3", "Social Engineering")
|
|
.and_node("S3.1", "Account Recovery Attack")
|
|
.attack(
|
|
"A6", "Gather Personal Information",
|
|
difficulty=Difficulty.LOW,
|
|
cost=Cost.FREE,
|
|
detection=DetectionRisk.NONE
|
|
)
|
|
.attack(
|
|
"A7", "Call Support Desk",
|
|
difficulty=Difficulty.MEDIUM,
|
|
cost=Cost.FREE,
|
|
detection=DetectionRisk.MEDIUM,
|
|
mitigations=["Support verification procedures", "Security questions"]
|
|
)
|
|
.end()
|
|
.end()
|
|
|
|
.build()
|
|
)
|
|
```
|
|
|
|
### Template 3: Mermaid Diagram Generator
|
|
|
|
```python
|
|
class MermaidExporter:
|
|
"""Export attack trees to Mermaid diagram format."""
|
|
|
|
def __init__(self, tree: AttackTree):
|
|
self.tree = tree
|
|
self._lines: List[str] = []
|
|
self._node_count = 0
|
|
|
|
def export(self) -> str:
|
|
"""Export tree to Mermaid flowchart."""
|
|
self._lines = ["flowchart TD"]
|
|
self._export_node(self.tree.root, None)
|
|
return "\n".join(self._lines)
|
|
|
|
def _export_node(self, node: AttackNode, parent_id: Optional[str]) -> str:
|
|
"""Recursively export nodes."""
|
|
node_id = f"N{self._node_count}"
|
|
self._node_count += 1
|
|
|
|
# Node shape based on type
|
|
if node.node_type == NodeType.OR:
|
|
shape = f"{node_id}(({node.name}))"
|
|
elif node.node_type == NodeType.AND:
|
|
shape = f"{node_id}[{node.name}]"
|
|
else: # LEAF
|
|
# Color based on difficulty
|
|
style = self._get_leaf_style(node)
|
|
shape = f"{node_id}[/{node.name}/]"
|
|
self._lines.append(f" style {node_id} {style}")
|
|
|
|
self._lines.append(f" {shape}")
|
|
|
|
if parent_id:
|
|
connector = "-->" if node.node_type != NodeType.AND else "==>"
|
|
self._lines.append(f" {parent_id} {connector} {node_id}")
|
|
|
|
for child in node.children:
|
|
self._export_node(child, node_id)
|
|
|
|
return node_id
|
|
|
|
def _get_leaf_style(self, node: AttackNode) -> str:
|
|
"""Get style based on attack attributes."""
|
|
colors = {
|
|
Difficulty.TRIVIAL: "fill:#ff6b6b", # Red - easy attack
|
|
Difficulty.LOW: "fill:#ffa06b",
|
|
Difficulty.MEDIUM: "fill:#ffd93d",
|
|
Difficulty.HIGH: "fill:#6bcb77",
|
|
Difficulty.EXPERT: "fill:#4d96ff", # Blue - hard attack
|
|
}
|
|
color = colors.get(node.attributes.difficulty, "fill:#gray")
|
|
return color
|
|
|
|
|
|
class PlantUMLExporter:
|
|
"""Export attack trees to PlantUML format."""
|
|
|
|
def __init__(self, tree: AttackTree):
|
|
self.tree = tree
|
|
|
|
def export(self) -> str:
|
|
"""Export tree to PlantUML."""
|
|
lines = [
|
|
"@startmindmap",
|
|
f"* {self.tree.name}",
|
|
]
|
|
self._export_node(self.tree.root, lines, 1)
|
|
lines.append("@endmindmap")
|
|
return "\n".join(lines)
|
|
|
|
def _export_node(self, node: AttackNode, lines: List[str], depth: int) -> None:
|
|
"""Recursively export nodes."""
|
|
prefix = "*" * (depth + 1)
|
|
|
|
if node.node_type == NodeType.OR:
|
|
marker = "[OR]"
|
|
elif node.node_type == NodeType.AND:
|
|
marker = "[AND]"
|
|
else:
|
|
diff = node.attributes.difficulty.name
|
|
marker = f"<<{diff}>>"
|
|
|
|
lines.append(f"{prefix} {marker} {node.name}")
|
|
|
|
for child in node.children:
|
|
self._export_node(child, lines, depth + 1)
|
|
```
|
|
|
|
### Template 4: Attack Path Analysis
|
|
|
|
```python
|
|
from typing import Set, Tuple
|
|
|
|
class AttackPathAnalyzer:
|
|
"""Analyze attack paths and coverage."""
|
|
|
|
def __init__(self, tree: AttackTree):
|
|
self.tree = tree
|
|
|
|
def get_all_paths(self) -> List[List[AttackNode]]:
|
|
"""Get all possible attack paths."""
|
|
paths = []
|
|
self._collect_paths(self.tree.root, [], paths)
|
|
return paths
|
|
|
|
def _collect_paths(
|
|
self,
|
|
node: AttackNode,
|
|
current_path: List[AttackNode],
|
|
all_paths: List[List[AttackNode]]
|
|
) -> None:
|
|
"""Recursively collect all paths."""
|
|
current_path = current_path + [node]
|
|
|
|
if node.node_type == NodeType.LEAF:
|
|
all_paths.append(current_path)
|
|
return
|
|
|
|
if not node.children:
|
|
all_paths.append(current_path)
|
|
return
|
|
|
|
if node.node_type == NodeType.OR:
|
|
# Each child is a separate path
|
|
for child in node.children:
|
|
self._collect_paths(child, current_path, all_paths)
|
|
else: # AND
|
|
# Must combine all children
|
|
child_paths = []
|
|
for child in node.children:
|
|
child_sub_paths = []
|
|
self._collect_paths(child, [], child_sub_paths)
|
|
child_paths.append(child_sub_paths)
|
|
|
|
# Combine paths from all AND children
|
|
combined = self._combine_and_paths(child_paths)
|
|
for combo in combined:
|
|
all_paths.append(current_path + combo)
|
|
|
|
def _combine_and_paths(
|
|
self,
|
|
child_paths: List[List[List[AttackNode]]]
|
|
) -> List[List[AttackNode]]:
|
|
"""Combine paths from AND node children."""
|
|
if not child_paths:
|
|
return [[]]
|
|
|
|
if len(child_paths) == 1:
|
|
return [path for paths in child_paths for path in paths]
|
|
|
|
# Cartesian product of all child path combinations
|
|
result = [[]]
|
|
for paths in child_paths:
|
|
new_result = []
|
|
for existing in result:
|
|
for path in paths:
|
|
new_result.append(existing + path)
|
|
result = new_result
|
|
return result
|
|
|
|
def calculate_path_metrics(self, path: List[AttackNode]) -> Dict:
|
|
"""Calculate metrics for a specific path."""
|
|
leaves = [n for n in path if n.node_type == NodeType.LEAF]
|
|
|
|
total_difficulty = sum(n.attributes.difficulty.value for n in leaves)
|
|
total_cost = sum(n.attributes.cost.value for n in leaves)
|
|
total_time = sum(n.attributes.time_hours for n in leaves)
|
|
max_detection = max((n.attributes.detection_risk.value for n in leaves), default=0)
|
|
|
|
return {
|
|
"steps": len(leaves),
|
|
"total_difficulty": total_difficulty,
|
|
"avg_difficulty": total_difficulty / len(leaves) if leaves else 0,
|
|
"total_cost": total_cost,
|
|
"total_time_hours": total_time,
|
|
"max_detection_risk": max_detection,
|
|
"requires_insider": any(n.attributes.requires_insider for n in leaves),
|
|
"requires_physical": any(n.attributes.requires_physical for n in leaves),
|
|
}
|
|
|
|
def identify_critical_nodes(self) -> List[Tuple[AttackNode, int]]:
|
|
"""Find nodes that appear in the most paths."""
|
|
paths = self.get_all_paths()
|
|
node_counts: Dict[str, Tuple[AttackNode, int]] = {}
|
|
|
|
for path in paths:
|
|
for node in path:
|
|
if node.id not in node_counts:
|
|
node_counts[node.id] = (node, 0)
|
|
node_counts[node.id] = (node, node_counts[node.id][1] + 1)
|
|
|
|
return sorted(
|
|
node_counts.values(),
|
|
key=lambda x: x[1],
|
|
reverse=True
|
|
)
|
|
|
|
def coverage_analysis(self, mitigated_attacks: Set[str]) -> Dict:
|
|
"""Analyze how mitigations affect attack coverage."""
|
|
all_paths = self.get_all_paths()
|
|
blocked_paths = []
|
|
open_paths = []
|
|
|
|
for path in all_paths:
|
|
path_attacks = {n.id for n in path if n.node_type == NodeType.LEAF}
|
|
if path_attacks & mitigated_attacks:
|
|
blocked_paths.append(path)
|
|
else:
|
|
open_paths.append(path)
|
|
|
|
return {
|
|
"total_paths": len(all_paths),
|
|
"blocked_paths": len(blocked_paths),
|
|
"open_paths": len(open_paths),
|
|
"coverage_percentage": len(blocked_paths) / len(all_paths) * 100 if all_paths else 0,
|
|
"open_path_details": [
|
|
{"path": [n.name for n in p], "metrics": self.calculate_path_metrics(p)}
|
|
for p in open_paths[:5] # Top 5 open paths
|
|
]
|
|
}
|
|
|
|
def prioritize_mitigations(self) -> List[Dict]:
|
|
"""Prioritize mitigations by impact."""
|
|
critical_nodes = self.identify_critical_nodes()
|
|
paths = self.get_all_paths()
|
|
total_paths = len(paths)
|
|
|
|
recommendations = []
|
|
for node, count in critical_nodes:
|
|
if node.node_type == NodeType.LEAF and node.mitigations:
|
|
recommendations.append({
|
|
"attack": node.name,
|
|
"attack_id": node.id,
|
|
"paths_blocked": count,
|
|
"coverage_impact": count / total_paths * 100,
|
|
"difficulty": node.attributes.difficulty.name,
|
|
"mitigations": node.mitigations,
|
|
})
|
|
|
|
return sorted(recommendations, key=lambda x: x["coverage_impact"], reverse=True)
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### Do's
|
|
- **Start with clear goals** - Define what attacker wants
|
|
- **Be exhaustive** - Consider all attack vectors
|
|
- **Attribute attacks** - Cost, skill, and detection
|
|
- **Update regularly** - New threats emerge
|
|
- **Validate with experts** - Red team review
|
|
|
|
### Don'ts
|
|
- **Don't oversimplify** - Real attacks are complex
|
|
- **Don't ignore dependencies** - AND nodes matter
|
|
- **Don't forget insider threats** - Not all attackers are external
|
|
- **Don't skip mitigations** - Trees are for defense planning
|
|
- **Don't make it static** - Threat landscape evolves
|
|
|
|
## Resources
|
|
|
|
- [Attack Trees by Bruce Schneier](https://www.schneier.com/academic/archives/1999/12/attack_trees.html)
|
|
- [MITRE ATT&CK Framework](https://attack.mitre.org/)
|
|
- [OWASP Attack Surface Analysis](https://owasp.org/www-community/controls/Attack_Surface_Analysis_Cheat_Sheet)
|