97 lines
3.6 KiB
Java
97 lines
3.6 KiB
Java
package com.yunhai.cms.portal;
|
|
|
|
import com.yunhai.cms.entity.CmsSite;
|
|
import com.yunhai.cms.support.CmsIntegrationTestBase;
|
|
import org.junit.jupiter.api.AfterEach;
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
import org.junit.jupiter.api.DisplayName;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.test.web.servlet.MockMvc;
|
|
import org.springframework.test.web.servlet.MvcResult;
|
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
|
|
/**
|
|
* 主题微调参数渲染注入(方案 §8.3 C 组 T13-T15,§5.3):
|
|
* 站点 config_json.theme_params 经快照装配 → head 内联 :root 覆盖串(位于皮肤 CSS 之后)。
|
|
*/
|
|
@DisplayName("ThemeRender - 微调参数 CSS 注入(方案 §5.3)")
|
|
class ThemeRenderIT extends CmsIntegrationTestBase {
|
|
|
|
@Autowired
|
|
private MockMvc mockMvc;
|
|
|
|
@BeforeEach
|
|
void seed() {
|
|
purgeTables();
|
|
}
|
|
|
|
@AfterEach
|
|
void clean() {
|
|
purgeTables();
|
|
}
|
|
|
|
private void persistSiteWithConfig(String code, String configJson) {
|
|
CmsSite site = new CmsSite();
|
|
site.setCode(code);
|
|
site.setSchId(1);
|
|
site.setSchName("学校-1");
|
|
site.setName("注入测试门户");
|
|
site.setTheme("default");
|
|
site.setConfigJson(configJson);
|
|
site.setStatus(1);
|
|
siteRepository.saveAndFlush(site);
|
|
}
|
|
|
|
private String homeHtml(String siteCode) throws Exception {
|
|
MvcResult result = mockMvc.perform(get("/portal/{code}/", siteCode))
|
|
.andExpect(status().isOk())
|
|
.andReturn();
|
|
return result.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("T13: 微调参数注入 CSS 变量且位于皮肤 CSS link 之后")
|
|
void themeParamsInjectedAfterSkinCss() throws Exception {
|
|
persistSiteWithConfig("inj1", "{\"theme_params\":{\"primary\":\"#b01a2e\"}}");
|
|
|
|
String html = homeHtml("inj1");
|
|
assertThat(html).as("合法色值应注入").contains("--primary:#b01a2e");
|
|
int cssLinkIndex = html.indexOf("css/main.css");
|
|
int varIndex = html.indexOf("--primary:#b01a2e");
|
|
assertThat(cssLinkIndex).as("皮肤 CSS link 应存在").isGreaterThan(-1);
|
|
assertThat(varIndex).as("内联覆盖串必须在皮肤 CSS link 之后(层叠后写覆盖)")
|
|
.isGreaterThan(cssLinkIndex);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("T14: 非法色值不注入(red;} / javascript:alert(1) 防注入)")
|
|
void illegalColorValuesNotInjected() throws Exception {
|
|
persistSiteWithConfig("inj2", "{\"theme_params\":{\"primary\":\"red;}}\"}}");
|
|
|
|
String html = homeHtml("inj2");
|
|
assertThat(html).doesNotContain("red;}}").doesNotContain("red;}");
|
|
assertThat(html).doesNotContain("--primary:red");
|
|
|
|
purgeTables();
|
|
persistSiteWithConfig("inj3", "{\"theme_params\":{\"primary\":\"javascript:alert(1)\"}}");
|
|
String html3 = homeHtml("inj3");
|
|
assertThat(html3).doesNotContain("javascript:alert(1)");
|
|
assertThat(html3).doesNotContain("--primary:javascript");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("T15: 未声明 key 不注入(按主题 schema 过滤)")
|
|
void undeclaredKeysNotInjected() throws Exception {
|
|
persistSiteWithConfig("inj4", "{\"theme_params\":{\"zzhack\":\"zzvalue\"}}");
|
|
|
|
String html = homeHtml("inj4");
|
|
assertThat(html).doesNotContain("zzhack").doesNotContain("zzvalue");
|
|
}
|
|
}
|