fix: constrain preview page and agent parameters to safe scalar values

This commit is contained in:
kl
2026-10-08 14:11:19 +08:00
parent 93931f6a7a
commit 84369c032b
2 changed files with 98 additions and 3 deletions

View File

@@ -86,8 +86,8 @@ public class OnlinePreviewController {
highlightall= KkFileUtils.htmlEscape(highlightall);
model.addAttribute("highlightall", highlightall);
model.addAttribute("page", page);
model.addAttribute("kkagent", kkagent);
model.addAttribute("page", normalizePage(page));
model.addAttribute("kkagent", Boolean.toString(Boolean.parseBoolean(kkagent)));
model.addAttribute("file", fileAttribute);
FilePreview filePreview = previewFactory.get(fileAttribute);
logger.info("预览文件url:{},previewType:{}", fileUrl, fileAttribute.getType());
@@ -98,6 +98,17 @@ public class OnlinePreviewController {
return filePreview.filePreviewHandle(fileUrl, model, fileAttribute); //统一在这里处理 url
}
private static String normalizePage(String page) {
if (page != null && page.matches("[0-9]{1,10}")) {
try {
return Integer.toString(Integer.parseInt(page));
} catch (NumberFormatException ignored) {
// Out-of-range values use the same default as missing values.
}
}
return "0";
}
@GetMapping( "/picturesPreview")
public String picturesPreview(@RequestParam String urls,
@RequestParam(required = false) String key,
@@ -287,4 +298,4 @@ public class OnlinePreviewController {
cacheService.addQueueTask(fileUrls);
return "success";
}
}
}

View File

@@ -0,0 +1,84 @@
package cn.keking.web.controller;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.service.FileHandlerService;
import cn.keking.service.FilePreview;
import cn.keking.service.FilePreviewFactory;
import freemarker.template.Configuration;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.ui.ExtendedModelMap;
import java.io.StringWriter;
import java.util.Base64;
import static org.junit.jupiter.api.Assertions.*;
class PreviewParameterSecurityTests {
private String previousKey;
@BeforeEach void setup() {
previousKey = ConfigConstants.getKey();
ConfigConstants.setKeyValue("false");
}
@AfterEach void restore() { ConfigConstants.setKeyValue(previousKey); }
private ExtendedModelMap preview(String page, String agent) {
FileHandlerService handler = new FileHandlerService(null) {
@Override public FileAttribute getFileAttribute(String url, HttpServletRequest request) {
FileAttribute file = new FileAttribute();
file.setName("document.pdf");
file.setSuffix("pdf");
return file;
}
};
FilePreviewFactory factory = new FilePreviewFactory(null) {
@Override public FilePreview get(FileAttribute file) {
return (url, model, attribute) -> {
model.addAttribute("pdfUrl", url);
return "pdf";
};
}
};
ExtendedModelMap model = new ExtendedModelMap();
OnlinePreviewController controller = new OnlinePreviewController(factory, handler, null, null);
String url = Base64.getEncoder().encodeToString("https://files.example/document.pdf".getBytes());
assertEquals("pdf", controller.onlinePreview(url, null, null, "false", page, agent,
model, new MockHttpServletRequest()));
return model;
}
@ParameterizedTest
@ValueSource(strings = {"';alert(1);//", "\";alert(1);//", "1</script><script>alert(1)</script>",
"1;alert(1)", "-1", "2147483648", "999999999999999999999"})
void rendersOnlySafeScalarValuesThroughControllerAndPdfTemplate(String payload) throws Exception {
ExtendedModelMap model = preview(payload, payload);
assertEquals("0", model.get("page"));
assertEquals("false", model.get("kkagent"));
model.put("baseUrl", "https://preview.example/");
for (String name : new String[]{"watermarkXSpace", "watermarkYSpace", "watermarkAlpha",
"watermarkWidth", "watermarkHeight", "watermarkAngle"}) {
model.put(name, "0");
}
Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
cfg.setClassForTemplateLoading(getClass(), "/web");
cfg.setClassicCompatible(true);
StringWriter output = new StringWriter();
cfg.getTemplate("pdf.ftl").process(model, output);
String html = output.toString();
assertTrue(html.contains("var kkagent = 'false';"));
assertTrue(html.contains("#page=0"));
assertFalse(html.contains("alert(1)"));
}
@Test void preservesSupportedValues() {
assertEquals("12", preview("0012", "false").get("page"));
assertEquals("2147483647", preview("2147483647", "true").get("page"));
assertEquals("true", preview("1", "true").get("kkagent"));
}
}