fix: 加固扩展配置注入并缓存 getConfig
- configJSON 注入转义 </script,避免脚本块被提前终止(潜在注入)
- getConfig 解析结果缓存,避免重复 JSON.parse;支持 getConfig({ clone: true }) 返回副本
This commit is contained in:
@@ -440,6 +440,20 @@ function getSubmenuForNavItem(navItem, config) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 JSON 字符串安全嵌入到 <script> 中,避免出现 `</script>` 结束标签导致脚本块被提前终止。
|
||||||
|
* 说明:返回值仍是合法 JSON,JSON.parse 后数据不变。
|
||||||
|
* @param {string} jsonString JSON 字符串
|
||||||
|
* @returns {string} 安全的 JSON 字符串
|
||||||
|
*/
|
||||||
|
function makeJsonSafeForHtmlScript(jsonString) {
|
||||||
|
if (typeof jsonString !== 'string') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonString.replace(/<\/script/gi, '<\\/script');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 准备渲染数据,添加模板所需的特殊属性
|
* 准备渲染数据,添加模板所需的特殊属性
|
||||||
* @param {Object} config 配置对象
|
* @param {Object} config 配置对象
|
||||||
@@ -486,11 +500,13 @@ function prepareRenderData(config) {
|
|||||||
renderData.homePageId = renderData.navigation && renderData.navigation[0] ? renderData.navigation[0].id : null;
|
renderData.homePageId = renderData.navigation && renderData.navigation[0] ? renderData.navigation[0].id : null;
|
||||||
|
|
||||||
// 添加序列化的配置数据,用于浏览器扩展(确保包含 homePageId 等处理结果)
|
// 添加序列化的配置数据,用于浏览器扩展(确保包含 homePageId 等处理结果)
|
||||||
renderData.configJSON = JSON.stringify({
|
renderData.configJSON = makeJsonSafeForHtmlScript(
|
||||||
|
JSON.stringify({
|
||||||
version: process.env.npm_package_version || '1.0.0',
|
version: process.env.npm_package_version || '1.0.0',
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
data: renderData // 使用经过处理的renderData而不是原始config
|
data: renderData // 使用经过处理的renderData而不是原始config
|
||||||
});
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// 为Handlebars模板特别准备navigationData数组
|
// 为Handlebars模板特别准备navigationData数组
|
||||||
renderData.navigationData = renderData.navigation;
|
renderData.navigationData = renderData.navigation;
|
||||||
|
|||||||
@@ -29,14 +29,35 @@ if (window.visualViewport) {
|
|||||||
window.visualViewport.addEventListener('resize', menavUpdateAppHeight);
|
window.visualViewport.addEventListener('resize', menavUpdateAppHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 配置数据缓存:避免浏览器扩展/站点脚本频繁 JSON.parse
|
||||||
|
let menavConfigCacheReady = false;
|
||||||
|
let menavConfigCacheRaw = null;
|
||||||
|
let menavConfigCacheValue = null;
|
||||||
|
|
||||||
// 全局MeNav对象 - 用于浏览器扩展
|
// 全局MeNav对象 - 用于浏览器扩展
|
||||||
window.MeNav = {
|
window.MeNav = {
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
|
|
||||||
// 获取配置数据
|
// 获取配置数据
|
||||||
getConfig: function() {
|
getConfig: function(options) {
|
||||||
const configData = document.getElementById('menav-config-data');
|
const configData = document.getElementById('menav-config-data');
|
||||||
return configData ? JSON.parse(configData.textContent) : null;
|
if (!configData) return null;
|
||||||
|
|
||||||
|
const raw = configData.textContent || '';
|
||||||
|
if (!menavConfigCacheReady || menavConfigCacheRaw !== raw) {
|
||||||
|
menavConfigCacheValue = JSON.parse(raw);
|
||||||
|
menavConfigCacheRaw = raw;
|
||||||
|
menavConfigCacheReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options && options.clone) {
|
||||||
|
if (typeof structuredClone === 'function') {
|
||||||
|
return structuredClone(menavConfigCacheValue);
|
||||||
|
}
|
||||||
|
return JSON.parse(JSON.stringify(menavConfigCacheValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
return menavConfigCacheValue;
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取元素的唯一标识符
|
// 获取元素的唯一标识符
|
||||||
|
|||||||
Reference in New Issue
Block a user