阻止识别结果引用未授权字体素材

补齐识别阶段字体引用校验
增加伪造字体绑定回归测试
This commit is contained in:
2026-09-09 00:41:35 +08:00
parent aff40f794d
commit dda6b3946a
@@ -348,6 +348,17 @@ fn validate_confidence(nodes: &[RecognitionNode]) -> Result<(), String> {
}) { }) {
return Err("识别阶段不能返回已绑定的 SpriteAssetId".to_string()); return Err("识别阶段不能返回已绑定的 SpriteAssetId".to_string());
} }
if node.components.iter().any(|component| {
matches!(
component,
Component::Text(crate::ui_editor::component::text::TextComponent {
font: crate::ui_editor::component::text::FontSource::Bound(_),
..
})
)
}) {
return Err("识别阶段不能返回已绑定的字体素材".to_string());
}
if let Confidence::UnSure(reason) = &node.confidence { if let Confidence::UnSure(reason) = &node.confidence {
if reason.trim().is_empty() { if reason.trim().is_empty() {
return Err("UnSure 必须包含审阅原因".to_string()); return Err("UnSure 必须包含审阅原因".to_string());
@@ -464,19 +475,23 @@ mod tests {
let oversized = (0..=MAX_RECOGNITION_TREE_NODES) let oversized = (0..=MAX_RECOGNITION_TREE_NODES)
.map(|_| serde_json::json!({"children": []})) .map(|_| serde_json::json!({"children": []}))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert!(validate_recognition_response_shape(&serde_json::json!({ assert!(
"trees": [{"children": oversized}] validate_recognition_response_shape(&serde_json::json!({
})) "trees": [{"children": oversized}]
.is_err()); }))
.is_err()
);
let mut nested = serde_json::json!({"children": []}); let mut nested = serde_json::json!({"children": []});
for _ in 0..MAX_RECOGNITION_TREE_DEPTH { for _ in 0..MAX_RECOGNITION_TREE_DEPTH {
nested = serde_json::json!({"children": [nested]}); nested = serde_json::json!({"children": [nested]});
} }
assert!(validate_recognition_response_shape(&serde_json::json!({ assert!(
"trees": [{"children": [nested]}] validate_recognition_response_shape(&serde_json::json!({
})) "trees": [{"children": [nested]}]
.is_err()); }))
.is_err()
);
} }
#[test] #[test]
@@ -521,6 +536,30 @@ mod tests {
assert!(validate_confidence(&[node]).is_err()); assert!(validate_confidence(&[node]).is_err());
} }
#[test]
fn recognition_rejects_bound_font_references() {
let mut text = crate::ui_editor::component::text::TextComponent::default();
text.font = crate::ui_editor::component::text::FontSource::Bound(
crate::ui_editor::utils::FontAssetId::new("font").expect("valid font id"),
);
let node = RecognitionNode {
global_pos_x_px: 0,
global_pos_y_px: 0,
width_px: 1,
height_px: 1,
local_anchor: Anchor::Preset(PresetAnchor {
horizontal: HorizontalAnchor::Left,
vertical: VerticalAnchor::Top,
}),
name: "文本".to_string(),
description: String::new(),
children: Vec::new(),
confidence: Confidence::Confident,
components: vec![Component::Text(text)],
};
assert!(validate_confidence(&[node]).is_err());
}
#[test] #[test]
fn conversion_stays_in_the_tree_image_coordinate_system() { fn conversion_stays_in_the_tree_image_coordinate_system() {
let image_id = UIDesignImageId::new("slave").expect("valid image id"); let image_id = UIDesignImageId::new("slave").expect("valid image id");
@@ -572,11 +611,13 @@ mod tests {
children: Vec::new(), children: Vec::new(),
}; };
assert!(validate_tree_image_ids( assert!(
&[tree(page.clone()), tree(slave.clone())], validate_tree_image_ids(
&[page.clone(), slave.clone()], &[tree(page.clone()), tree(slave.clone())],
) &[page.clone(), slave.clone()],
.is_ok()); )
.is_ok()
);
assert!( assert!(
validate_tree_image_ids(&[tree(page.clone())], &[page.clone(), slave.clone()]).is_err() validate_tree_image_ids(&[tree(page.clone())], &[page.clone(), slave.clone()]).is_err()
); );