fix(data): buildStructTree 用 visited 集防环(不可信结构数据避免无限递归)

This commit is contained in:
gaozheng 2026-06-09 11:38:02 +08:00
parent 2bc22a55d6
commit 695aa8c310
2 changed files with 18 additions and 1 deletions

View File

@ -91,13 +91,17 @@ std::vector<StructTreeNode> buildStructTree(const std::vector<StructNode>& flat)
auto isRootLevel = [&](const StructNode& n) {
return n.parentId.empty() || ids.find(n.parentId) == ids.end();
};
// visited 防环:每个 id 最多进树一次。对正常树(单父)等价于原逻辑;
// 对不可信后端数据的多节点环 / 重复 id 环,避免无限递归(规约:永不信任外部数据)。
std::set<std::string> visited;
std::function<std::vector<StructTreeNode>(const std::string&, bool)> build =
[&](const std::string& parentId, bool root) {
std::vector<StructTreeNode> out;
for (const auto& n : flat) {
const bool belongs = root ? isRootLevel(n) : (n.parentId == parentId);
if (!belongs) continue;
if (n.id == parentId) continue; // 防自环
if (visited.count(n.id)) continue; // 已进树 → 跳过,防环/防重复
visited.insert(n.id);
StructTreeNode t;
t.node = n;
t.isTm = isLeaf(n.id);

View File

@ -106,3 +106,16 @@ TEST(NavDto, BuildStructTreeOrphanParentBecomesRoot) {
TEST(NavDto, BuildStructTreeEmpty) {
EXPECT_TRUE(dto::buildStructTree({}).empty());
}
TEST(NavDto, BuildStructTreeHandlesCycleWithoutInfiniteRecursion) {
// 不可信数据:重复 id 形成可达环R→X→Y→重复X…。必须终止、不崩。
const std::vector<StructNode> flat = {
{"R", "", "", "GS", "", 1},
{"X", "x", "R", "GS", "", 1},
{"Y", "y", "X", "GS", "", 1},
{"X", "x2", "Y", "TM", "", 2}, // 重复 id X父=Y → 若不防环将无限递归
};
const auto roots = dto::buildStructTree(flat); // 不挂起即通过
ASSERT_EQ(roots.size(), 1u);
EXPECT_EQ(roots[0].node.id, "R");
}