feat(puzzle): 实现智能自动填充引擎和安全增强

- 新增拼图元素自动填充引擎 PuzzleElementFillEngine
- 支持基于规则的条件匹配和数据源解析
- 实现机位数量、机位ID等多维度条件策略
- 添加 DEVICE_IMAGE、USER_AVATAR 等数据源类型支持
- 增加景区隔离校验确保模板使用安全性
- 强化图片下载安全校验,防范 SSRF 攻击
- 支持本地文件路径解析和公网 URL 安全检查
- 完善静态值数据源策略支持 localPath 配置
- 优化生成流程中 faceId 和 scenicId 的校验逻辑
- 补充相关单元测试覆盖核心功能点
This commit is contained in:
2025-11-19 17:28:41 +08:00
parent cb17ea527b
commit cfb3625ac0
12 changed files with 748 additions and 57 deletions

View File

@@ -2,18 +2,27 @@ package com.ycwl.basic.puzzle.element;
import com.ycwl.basic.puzzle.element.base.BaseElement;
import com.ycwl.basic.puzzle.element.base.ElementFactory;
import com.ycwl.basic.puzzle.element.enums.ElementType;
import com.ycwl.basic.puzzle.element.impl.ImageElement;
import com.ycwl.basic.puzzle.element.impl.TextElement;
import com.ycwl.basic.puzzle.element.exception.ElementValidationException;
import com.ycwl.basic.puzzle.element.renderer.RenderContext;
import com.ycwl.basic.puzzle.entity.PuzzleElementEntity;
import com.ycwl.basic.puzzle.test.PuzzleTestDataBuilder;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import static org.junit.jupiter.api.Assertions.*;
/**
@@ -27,6 +36,23 @@ class ImageElementTest {
private Graphics2D graphics;
private RenderContext context;
@BeforeAll
static void initRegistry() {
ElementFactory.clearRegistry();
ElementFactory.register(ElementType.TEXT, TextElement.class);
ElementFactory.register(ElementType.IMAGE, ImageElement.class);
}
static class TestableImageElement extends ImageElement {
boolean isSafe(String url) {
return isSafeRemoteUrl(url);
}
BufferedImage load(String path) {
return downloadImage(path);
}
}
@BeforeEach
void setUp() {
BufferedImage canvas = new BufferedImage(800, 600, BufferedImage.TYPE_INT_ARGB);
@@ -121,4 +147,31 @@ class ImageElementTest {
assertTrue(schema.contains("imageFitMode"));
assertTrue(schema.contains("borderRadius"));
}
@Test
void testImageElement_SafeRemoteUrlChecks() {
TestableImageElement element = new TestableImageElement();
assertFalse(element.isSafe("http://127.0.0.1/admin.png"));
assertFalse(element.isSafe("http://localhost/private.png"));
assertFalse(element.isSafe("file:///etc/passwd"));
assertTrue(element.isSafe("https://8.8.8.8/logo.png"));
}
@Test
void testImageElement_LoadLocalImageSuccess() throws IOException {
Path temp = Files.createTempFile("puzzle-image", ".png");
try {
BufferedImage bufferedImage = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
ImageIO.write(bufferedImage, "png", temp.toFile());
TestableImageElement element = new TestableImageElement();
BufferedImage loaded = element.load(temp.toString());
assertNotNull(loaded);
assertEquals(10, loaded.getWidth());
assertEquals(10, loaded.getHeight());
} finally {
Files.deleteIfExists(temp);
}
}
}