attack-tree-construction 实战:用 Python 数据模型构建与量化分析攻击树(Agentic Security 技能包)
攻击树(Attack Tree)是威胁建模中最直观的可视化工具:以攻击者目标为根节点,逐层分解出子目标与具体攻击步骤,让安全团队一眼看清"系统可能被怎样攻破"。本篇文章以 agents24/agents 仓库中 security-scanning 插件的 attack-tree-construction 技能为骨架,完整讲解该技能配套的四套可运行模板——数据模型、流式构建器、Mermaid/PlantUML 导出器与攻击路径分析器,并结合仓库中 threat-modeling-expert 等配套资源,说明如何在安全架构评审、渗透测试规划与防御投资决策中落地使用。读完本文,你将获得一套可直接复制运行的攻击树建模代码,掌握 OR/AND/叶节点聚合规则、最易/最省/最隐蔽路径搜索,以及基于路径覆盖率的缓解措施优先级排序方法。
技能定位:attack-tree-construction 在仓库中的角色
在 agents24/agents 这个 Multi-harness agentic plugin marketplace 中,security-scanning 插件是 SAST 分析与漏洞扫描领域的核心模块,位于 plugins/security-scanning。该插件由以下组件构成:
- agents:security-auditor(DevSecOps 与合规审计专家)、threat-modeling-expert(精通 STRIDE、PASTA、攻击树与安全需求提取);
- commands:
security-dependencies、security-hardening、security-sast三个斜杠命令; - skills:
attack-tree-construction、sast-configuration、security-requirement-extraction、stride-analysis-patterns、threat-mitigation-mapping五个渐进式技能。
技能目录遵循渐进式披露(progressive disclosure)约定:SKILL.md 提供核心概念与使用时机,详细模板放在 references/details.md 中按需加载。正如 docs/agent-skills.md 中描述的那样,attack-tree-construction 的职责是"构建将威胁场景映射到漏洞的攻击树"。本技能与仓库内其他威胁建模技能构成完整链路:先用 stride-analysis-patterns 系统识别威胁,再用 attack-tree-construction 对关键路径建树,继而由 security-requirement-extraction 提炼安全需求,最后通过 threat-mitigation-mapping 映射缓解控制。
核心概念回顾:攻击树结构与节点类型
SKILL.md(见 SKILL.md)定义了攻击树的三要素:
[Root Goal]
|
┌────────────┴────────────┐
│ │
[Sub-goal 1] [Sub-goal 2]
(OR node) (AND node)
│ │
┌─────┴─────┐ ┌─────┴─────┐
│ │ │ │
[Attack] [Attack] [Attack] [Attack]
(leaf) (leaf) (leaf) (leaf)
| 类型 | 图形符号 | 语义 |
|---|---|---|
| OR | 椭圆 | 任一子节点达成即可实现目标 |
| AND | 矩形 | 所有子节点都必须达成 |
| Leaf | 方框 | 原子攻击步骤(不可再分解) |
每个叶节点需要标注攻击属性:成本(Cost)、耗时(Time)、技能要求(Skill)、被发现可能性(Detection)。SKILL.md 同时给出最佳实践:从明确目标出发、穷举所有攻击向量、为攻击标注属性、随威胁演进持续更新、用红队评审校验——同时避免过度简化、忽略 AND 依赖、遗漏内部威胁、跳过缓解设计、把威胁模型做成静态文档。
模板一:Attack Tree 数据模型
references/details.md 的第一个模板定义了攻击树的完整数据模型,包含四个枚举与三个数据类,是后续所有模板的基础。
属性枚举:难度、成本、检测风险
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
注意两个细节:其一,Difficulty(攻击者所需技能)与 SKILL.md 中的文字化"Low/Medium/High"相比,细化为五档(TRIVIAL 到 EXPERT);Cost 细化为五档、DetectionRisk 细化为五档,数值上从低到高递增,为后续量化计算提供了可比较的标度。其二,这些 Enum 的数值成员会在路径聚合算法中直接参与数学运算,因此顺序与数值必须稳定。
AttackAttributes:单步攻击的属性载体
@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
AttackAttributes 除了三个枚举型属性外,还包含 time_hours(预计执行小时数)、requires_insider(是否需要内部人员配合)、requires_physical(是否需要物理接触)三个扩展字段,为内部威胁与物理攻击场景提供建模能力——这与 SKILL.md "Don't forget insider threats" 的最佳实践一一对应。
AttackNode:树的节点单元
@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]
}
节点携带 mitigations(缓解措施列表)与 cve_refs(关联 CVE 编号列表)两个关键字段,让攻击树不止于"威胁地图",还能直接服务防御规划与漏洞关联分析。
节点上的聚合算法是理解整个数据模型的核心,两条规则贯穿全文:
- OR 节点聚合:难度取子节点最小值(攻击者会选最省力的分支),成本取子节点最小值;
- AND 节点聚合:难度取子节点最大值(木桶效应,最难的步骤决定整条路径),成本取子节点之和(所有步骤都要花钱)。
to_dict() 提供了无递归依赖的 JSON 序列化接口,供导出与持久化使用。
AttackTree:树级分析 API
@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)
AttackTree 提供的分析 API 值得逐一点评:
- find_easiest_path / find_cheapest_path / find_stealthiest_path:回答"攻击者最可能走哪条路"这一核心问题。实现中,OR 节点选取子路径得分最低者(
_path_score只对叶节点累加对应属性值),AND 节点则必须串联全部子路径。这三个指标恰好对应 SKILL.md 属性表中的 Skill(难度)、Cost、Detection 三维评估。 - get_unmitigated_attacks:直接返回所有"没有缓解措施"的叶节点,是防御缺口审计的即查即用接口,可以推断它应与
threat-mitigation-mapping技能的"Critical Gaps reporting"配合产出。 - export_json:输出带缩进的 JSON 文档,便于存入安全知识库或交给下游工具。
模板二:AttackTreeBuilder 流式构建器与账户接管实战示例
手写嵌套 AttackNode 容易出错,第二个模板提供了流式(fluent)构建器,用链式调用把树的"深度优先构造"变得直观:
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]
构建器的设计要点:
- 维护一个
_node_stack栈模拟遍历状态,goal()建立根节点,or_node()/and_node()入栈,end()弹栈返回父节点,attack()只在当前栈顶挂叶节点——这保证任何时刻新增的节点都精确挂到"当前位置"; attack()的参数默认值与AttackAttributes保持一致(难度/成本/检测风险默认 MEDIUM,耗时默认 8 小时),实际建模时可以只覆盖关心的维度;build()在缺少根目标时抛出ValueError,防止构造出无意义的不完整树。
实战:账户接管攻击树(Account Takeover)
模板随后给出了完整的账户接管场景示例,演示了 OR(凭据窃取、认证绕过、社会工程三条互为替代的路径)、AND(S3.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()
)
该示例在信息密度上覆盖了模型几乎全部能力:Cost.FREE(成本为零的公开信息收集)、DetectionRisk.NONE(无法被检测的被动步骤)、AND 组合、以及逐条映射的缓解措施。安全团队可直接复用此模式为任意系统(认证、支付、云基础设施等)建立类似的攻击树。
模板三:Mermaid 与 PlantUML 可视化导出
攻击树的价值在于沟通,模板三提供两个导出器,把内存中的树转换为团队熟悉的可视化格式。
MermaidExporter:按难度着色
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
导出器的视觉编码设计专业且自洽:
- 形状区分类型:OR 用圆角双圆
(( )),AND 用矩形[ ],叶节点用斜边六边形/ /,与 SKILL.md 的符号约定一致; - 连接符区分语义:普通分支用
-->,AND 组合用粗箭头==>,强化"所有子节点必须同时成立"的视觉暗示; - 颜色区分风险:按难度从红(TRIVIAL,最易被利用、风险最高)渐变到蓝(EXPERT,最难),即"越红越危险",方便管理者一眼锁定最紧迫的攻击面。
PlantUMLExporter:思维导图输出
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)
PlantUML 版输出 @startmindmap 思维导图,用文本标记 [OR]、[AND] 与 <<DIFFICULTY>> 标注节点类型与难度,适合直接粘贴到支持 PlantUML 的文档平台。
模板四:AttackPathAnalyzer 路径枚举与量化分析
最后一个模板把攻击树从"静态图"升级为"可计算的决策模型":
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)
该分析器提供的四类能力分别解决安全规划中的四个问题:
- get_all_paths():枚举全部攻击路径。实现要点是 AND 节点子路径的笛卡尔积组合(
_combine_and_paths),即 AND 分支的每一种子路径组合都是一条完整攻击路径。注意路径数量随 AND 分支指数增长,从源码实现可以推断它更适合中小规模树。 - calculate_path_metrics():对单条路径输出步骤数、总难度/平均难度、总成本、总耗时、最大检测风险、是否涉及内部人员/物理接触等量化指标,输出可直接用于风险评分卡。
- identify_critical_nodes():统计每个节点出现在多少条路径中,按出现次数降序返回——出现在路径最多的叶节点,就是缓解价值最高的"关键节点"。
- coverage_analysis(mitigated_attacks):输入已缓解攻击的 id 集合,输出总路径数、已阻断路径数、剩余开放路径数、覆盖率百分比(blocked/total×100),并附上前 5 条未覆盖路径的名称与指标,天然对接 threat-mitigation-mapping 的"Critical Gaps 报告"。
- prioritize_mitigations():综合关键节点统计与缓解信息,输出按覆盖率影响降序排列的缓解建议,每个建议包含攻击名、阻断路径数、覆盖率影响百分比、难度与对应缓解措施——这直接支撑 SKILL.md 中"Planning defensive investments"的使用场景,也呼应 threat-modeling-expert 工作流的"Score and prioritize threats"步骤。
把攻击树接入完整威胁建模工作流
在 agents24/agents 仓库中,attack-tree-construction 不是孤立工具,而是 security-scanning 插件威胁建模链路的一环。推荐的组合方式是:
- 用 stride-analysis-patterns 对系统各组件执行 STRIDE 分类(Spoofing/Tampering/Repudiation/Info Disclosure/DoS/Elevation),找出需要深入的高风险面;
- 对高风险面调用本技能的四套模板建树:
AttackTreeBuilder建模 →AttackPathAnalyzer找最易/最省/最隐蔽路径与未缓解攻击 →MermaidExporter/PlantUMLExporter出图汇报; - 由 security-requirement-extraction 把树中未缓解的攻击转化为带验收标准的安全需求;
- 用 threat-mitigation-mapping 将威胁映射到预防性/检测性/纠正性控制,形成修复路线图。
这也与 threat-modeling-expert 的八步工作流(界定范围与信任边界 → 数据流图 → 识别资产与入口 → STRIDE 逐组件分析 → 对关键路径构建攻击树 → 打分排序 → 设计缓解 → 记录残余风险)完全吻合。若需要执行 SAST、依赖扫描与加固等后续动作,可配合该插件的 security-sast、security-dependencies、security-hardening 三个命令使用(详见 docs/usage.md)。
小结
attack-tree-construction 技能用约 600 行 Python 把攻击树方法论落成了四套可直接运行、可直接扩展的工程模板:数据模型负责表达与聚合,流式构建器负责快速建模,双格式导出器负责沟通汇报,路径分析器负责量化决策。配合仓库中 security-scanning 插件其余四个技能,即可在 Agent 工作流中完成从"威胁识别 → 攻击树建模 → 路径量化 → 需求提取 → 缓解映射"的完整威胁建模闭环。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00