Skip to content

Commit b06f87f

Browse files
committed
fix:修复xss 模板注入漏洞
1 parent 809fc04 commit b06f87f

File tree

7 files changed

+88
-45
lines changed

7 files changed

+88
-45
lines changed

packages/core-common/src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export { Logger } from './log';
22
export { isDev, getOptions, setOptions } from './common';
33
export * from './loadConfig';
4-
4+
export * from './util';
55
import { isDev, getOptions, setOptions } from './common';
66

77
export default {

packages/core-common/src/loadConfig.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ import fs from 'fs';
22
import path, { join } from 'path';
33
import { parse as parseUrl } from 'url';
44

5-
export const tempDir = join(process.cwd() + '/.ssr');
6-
export const cacheDir = join(process.cwd() + '/.ssr/cache');
7-
export const outPutDir = join(process.cwd() + '/.ssr/output');
8-
export const serverDir = join(process.cwd() + '/dist/server');
9-
export const clientDir = join(process.cwd() + '/dist/client');
10-
export const webpackConfigPath = join(process.cwd() + './webpack.config.js');
115
export const cwd = process.cwd();
6+
export const tempDir = join(cwd + '/.ssr');
7+
export const cacheDir = join(cwd + '/.ssr/cache');
8+
export const outPutDir = join(cwd + '/.ssr/output');
9+
export const serverDir = join(cwd + '/dist/server');
10+
export const clientDir = join(cwd + '/dist/client');
11+
export const webpackConfigPath = join(cwd + './webpack.config.js');
1212
export const SSRKEY = Symbol('SSR');
1313

14-
const newOptionsPath = path.resolve(process.cwd(), './config/ssr.config.js');
14+
const newOptionsPath = path.resolve(cwd, './config/ssr.config.js');
1515
const defaultOptions = {
1616
ssr: true, // 全局开启服务端渲染
1717
cache: false, // 全局使用服务端渲染缓存
@@ -23,8 +23,8 @@ const defaultOptions = {
2323
};
2424
let coreOptions = null;
2525

26-
/**umajs-react-ssr方案时,生成前部署构建时扫描其配置文件
27-
* 兼容@umajs/plugin-react-ssr配置
26+
/**
27+
* 兼容@umajs/plugin-react/vue-ssr配置
2828
* @returns
2929
*/
3030
function umajs_plugin_options() {
@@ -34,9 +34,17 @@ function umajs_plugin_options() {
3434
Uma.instance({ ROOT: './app' }).loadConfig();
3535
opt = Uma.config?.ssr || {}; // ssr.config.ts
3636
const reactSsrPlugin = Uma.config?.plugin['react-ssr'];
37+
const vueSsrPlugin = Uma.config?.plugin['vue-ssr'];
38+
const ssrPlugin = Uma.config?.plugin['ssr'];
3739
if (reactSsrPlugin?.options) {
3840
opt = reactSsrPlugin.options;
3941
}
42+
if (vueSsrPlugin?.options) {
43+
opt = vueSsrPlugin.options;
44+
}
45+
if (ssrPlugin?.options) {
46+
opt = ssrPlugin.options;
47+
}
4048
} catch (_error) {}
4149
return opt;
4250
}

packages/core-common/src/util.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const replaceSpecialStr = (str) => {
2+
return str
3+
.replace(/&/g, '&')
4+
.replace(/</g, '&lt;')
5+
.replace(/>/g, '&gt;')
6+
.replace(/ /g, '&nbsp;')
7+
.replace("'", '&#39;')
8+
.replace('"', '&quot;')
9+
.replace(/{{/g, '')
10+
.replace(/}}/g, '');
11+
};
12+
13+
export const filterXssByJson = (json) => {
14+
for (let key in json) {
15+
if (json[key] instanceof Array) {
16+
json[key] = json[key].map((item) => {
17+
return replaceSpecialStr(item);
18+
});
19+
} else {
20+
json[key] = replaceSpecialStr(json[key]);
21+
}
22+
}
23+
return json;
24+
};

packages/core-react/package-lock.json

Lines changed: 17 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core-react/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
"koa-send": "^5.0.0",
5353
"react": "^17.0.2",
5454
"react-dom": "^17.0.2",
55-
"react-router-dom": "^5.2.0"
55+
"react-router-dom": "^5.2.0",
56+
"serialize-javascript": "^6.0.0"
5657
},
5758
"devDependencies": {
5859
"@babel/cli": "^7.14.5",

packages/core-react/src/render.js

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import fs from 'fs';
22
import React from 'react';
3+
import serialize from 'serialize-javascript';
34
import ReactDOMServer from 'react-dom/server';
45
import { StaticRouter } from 'react-router-dom';
5-
import common, { clientDir, serverDir, cacheDir, SSRKEY, Logger } from '@srejs/common';
6+
import common, {
7+
clientDir,
8+
serverDir,
9+
cacheDir,
10+
SSRKEY,
11+
Logger,
12+
filterXssByJson
13+
} from '@srejs/common';
614
import { loadGetInitialProps } from './initialProps';
715
import { DevMiddlewareFileSystem, getEntryList, WebpackReact as webPack } from '@srejs/webpack';
816

@@ -56,17 +64,6 @@ export const readPageHtml = (page) => {
5664
});
5765
};
5866

59-
const filterXss = (str) => {
60-
var s = '';
61-
s = str.replace(/&/g, '&amp;');
62-
s = s.replace(/</g, '&lt;');
63-
s = s.replace(/>/g, '&gt;');
64-
s = s.replace(/ /g, '&nbsp;');
65-
s = s.replace("'", '&#39;');
66-
s = s.replace('"', '&quot;');
67-
return s;
68-
};
69-
7067
const writeFileHander = (cacheDir, cacheUrl, Content) => {
7168
fs.exists(cacheUrl, (exists) => {
7269
if (exists) {
@@ -107,6 +104,7 @@ export const renderServer = async (ctx, initProps, ssr = true) => {
107104
const context = {};
108105
let props = {};
109106
var { page, query } = ctx[SSRKEY];
107+
query = filterXssByJson(query);
110108
if (!getEntryList().has(page)) {
111109
return false;
112110
}
@@ -154,32 +152,21 @@ export const renderServer = async (ctx, initProps, ssr = true) => {
154152
});
155153
ctx.res.end();
156154
} else {
157-
// 加载 index.html 的内容
158155
let data = await readPageHtml(page);
159-
//进行xss过滤
160-
for (let key in query) {
161-
if (query[key] instanceof Array) {
162-
query[key] = query[key].map((item) => {
163-
return filterXss(item);
164-
});
165-
} else {
166-
query[key] = filterXss(query[key]);
167-
}
168-
}
156+
const ssrData = {
157+
initProps: props,
158+
page,
159+
path: ctx[SSRKEY].path,
160+
query,
161+
options: ctx[SSRKEY].options
162+
};
169163
let rootNode = ctx[SSRKEY].options.rootNode;
170164
let replaceReg = new RegExp(`<div id="${rootNode}"><\/div>`);
171165
// 把渲染后的 React HTML 插入到 div 中
172166
let document = data.replace(
173167
replaceReg,
174168
`<div id="${rootNode}">${Html}</div>
175-
<script>var __SSR_DATA__ =
176-
{
177-
initProps:${JSON.stringify(props || {})},
178-
page: "${page}",
179-
path:"${ctx[SSRKEY].path}",
180-
query:${JSON.stringify(query || {})},
181-
options:${JSON.stringify(ctx[SSRKEY].options || {})}
182-
}
169+
<script>var __SSR_DATA__ = ${serialize(ssrData, { isJSON: true })}
183170
</script>`
184171
);
185172
document = renderDocumentHead(document, props);

packages/core-vue/src/render.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import fs from 'fs';
22
import { createRenderer } from 'vue-server-renderer';
3-
import common, { clientDir, serverDir, cacheDir, SSRKEY, Logger } from '@srejs/common';
3+
import common, {
4+
clientDir,
5+
serverDir,
6+
cacheDir,
7+
SSRKEY,
8+
Logger,
9+
filterXssByJson
10+
} from '@srejs/common';
411
import serialize from 'serialize-javascript';
512
import {
613
VueDevMiddlewareFileSystem as DevMiddlewareFileSystem,
@@ -97,6 +104,7 @@ export const checkModules = async (page) => {
97104
export const renderServer = async (ctx, initProps = {}, ssr = true) => {
98105
let context = { url: ctx.req.url };
99106
var { page, query } = ctx[SSRKEY];
107+
query = filterXssByJson(query);
100108
if (!getVueEntryList().has(page)) {
101109
return false;
102110
}

0 commit comments

Comments
 (0)