@ganwei-web/ganwei-pc-cli 6.2.9 → 6.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ganwei-iotcenter-index-6.2.3/configuration/moduleConfiguration.json +2 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/env.d.ts +8 -5
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/index.html +1 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/ElementPlusAdapter.css +437 -68
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/reset-6-1.css +1 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/reset-plus.css +396 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/http/createAxios.js +38 -13
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/js/getLanguage.js +19 -12
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/themes/dark-6-1.css +3 -3
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/themes/green-6-1.css +12 -11
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/App.vue +11 -6
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/equipAlarmDialog/index.vue +6 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/expirationReminder/index.vue +7 -3
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/index.scss +4 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.js +35 -3
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.scss +7 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.vue +2 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Navigation/ContractMenu.vue +11 -37
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Navigation/TopNav.vue +3 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Sidebar/LeftContent/index.vue +7 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/router.js +120 -18
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/date.ts +80 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/dom.ts +99 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/env.ts +20 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/file.ts +74 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/index.ts +26 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/number.ts +83 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/performance.ts +69 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/storage.ts +80 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/string.ts +116 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/xss-filter.ts +260 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/views/jumpIframe/index.vue +45 -28
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/tsconfig.json +1 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/vite.config.ts +10 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/public/static/http/createAxios.js +40 -15
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/public/static/js/getLanguage.js +10 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/src/views/login.vue +55 -53
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/src/views/ssoLogin.vue +10 -9
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.development +0 -3
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.production +1 -4
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.test +0 -3
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.eslintrc.cjs +2 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/env.d.ts +9 -5
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/index.html +1 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/package.json +3 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/public/static/http/createAxios.js +38 -13
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/public/static/js/getLanguage.js +10 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/App.vue +1 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/request/api.ts +0 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/request/models/response/template.ts +0 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/date.ts +79 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/dom.ts +99 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/env.ts +20 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/file.ts +74 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/index.ts +29 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/number.ts +83 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/performance.ts +69 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/signalr.ts +564 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/storage.ts +80 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/string.ts +116 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/xss-filter.ts +260 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/views/template.vue +0 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/tsconfig.json +7 -0
- package/ganwei-iotcenter-index-6.2.3/pnpm-lock.yaml +489 -155
- package/package.json +1 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/mixins/judgePermission.js +0 -60
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/setStorage.js +0 -5
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/setStorage.js +0 -5
- /package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/build/{enteryJson.js → entryJson.js} +0 -0
- /package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/{request/models/request/index.ts → enum/template.ts} +0 -0
- /package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/request/models/{response/index.ts → request/template.ts} +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 首字母大写
|
|
3
|
+
*/
|
|
4
|
+
export function capitalize(str: string): string {
|
|
5
|
+
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 驼峰转连字符
|
|
10
|
+
*/
|
|
11
|
+
export function camelToKebab(str: string): string {
|
|
12
|
+
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 连字符转驼峰
|
|
17
|
+
*/
|
|
18
|
+
export function kebabToCamel(str: string): string {
|
|
19
|
+
return str.replace(/-([a-z])/g, (_, char) => char.toUpperCase())
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 手机号脱敏
|
|
24
|
+
*/
|
|
25
|
+
export function maskPhone(phone: string): string {
|
|
26
|
+
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 身份证号脱敏
|
|
31
|
+
*/
|
|
32
|
+
export function maskIdCard(idCard: string): string {
|
|
33
|
+
return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 银行卡号脱敏
|
|
38
|
+
*/
|
|
39
|
+
export function maskBankCard(cardNo: string): string {
|
|
40
|
+
return cardNo.replace(/(\d{4})\d+(\d{4})/, '$1 **** **** $2')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** 邮箱脱敏:t***e@example.com */
|
|
44
|
+
export function maskEmail(email: string): string {
|
|
45
|
+
if (!email || !email.includes('@')) return email
|
|
46
|
+
const [prefix, suffix] = email.split('@')
|
|
47
|
+
const maskedPrefix = prefix.charAt(0) + '***' + prefix.charAt(prefix.length - 1)
|
|
48
|
+
return `${maskedPrefix}@${suffix}`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 姓名脱敏:张*(2字)/ 张**(3字+) */
|
|
52
|
+
export function maskName(name: string): string {
|
|
53
|
+
if (!name) return name
|
|
54
|
+
if (name.length === 2) return name.charAt(0) + '*'
|
|
55
|
+
return name.charAt(0) + '*'.repeat(name.length - 1)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 地址脱敏:北京市朝阳区*** */
|
|
59
|
+
export function maskAddress(address: string): string {
|
|
60
|
+
if (!address || address.length <= 6) return address
|
|
61
|
+
return address.substring(0, 6) + '***'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 截断字符串
|
|
66
|
+
*/
|
|
67
|
+
export function truncate(str: string, length: number, suffix = '...'): string {
|
|
68
|
+
if (str.length <= length) return str
|
|
69
|
+
return str.substring(0, length) + suffix
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 移除 HTML 标签
|
|
74
|
+
*/
|
|
75
|
+
export function stripHtml(html: string): string {
|
|
76
|
+
return html.replace(/<[^>]*>/g, '')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 转义 HTML 特殊字符
|
|
81
|
+
*/
|
|
82
|
+
export function escapeHtml(str: string): string {
|
|
83
|
+
const escapeMap: Record<string, string> = {
|
|
84
|
+
'&': '&',
|
|
85
|
+
'<': '<',
|
|
86
|
+
'>': '>',
|
|
87
|
+
'"': '"',
|
|
88
|
+
"'": '''
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return str.replace(/[&<>"']/g, char => escapeMap[char])
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 解析 URL 参数
|
|
96
|
+
*/
|
|
97
|
+
export function parseQueryString(url: string): Record<string, string> {
|
|
98
|
+
const queryString = url.split('?')[1]
|
|
99
|
+
if (!queryString) return {}
|
|
100
|
+
|
|
101
|
+
return queryString.split('&').reduce((params, param) => {
|
|
102
|
+
const [key, value] = param.split('=')
|
|
103
|
+
params[decodeURIComponent(key)] = decodeURIComponent(value || '')
|
|
104
|
+
return params
|
|
105
|
+
}, {} as Record<string, string>)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 序列化对象为 URL 参数
|
|
110
|
+
*/
|
|
111
|
+
export function stringifyQueryString(obj: Record<string, any>): string {
|
|
112
|
+
return Object.entries(obj)
|
|
113
|
+
.filter(([, value]) => value !== undefined && value !== null)
|
|
114
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
|
115
|
+
.join('&')
|
|
116
|
+
}
|
package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/xss-filter.ts
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XSS 过滤工具
|
|
3
|
+
* 用于过滤和清理用户输入,防止 XSS 攻击
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* HTML 实体编码映射
|
|
8
|
+
*/
|
|
9
|
+
const HTML_ENTITIES: Record<string, string> = {
|
|
10
|
+
'&': '&',
|
|
11
|
+
'<': '<',
|
|
12
|
+
'>': '>',
|
|
13
|
+
'"': '"',
|
|
14
|
+
"'": ''',
|
|
15
|
+
'/': '/',
|
|
16
|
+
'`': '`',
|
|
17
|
+
'=': '='
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 危险 HTML 标签正则
|
|
22
|
+
*/
|
|
23
|
+
const DANGEROUS_TAGS_REGEX =
|
|
24
|
+
/<(script|iframe|object|embed|form|input|button|textarea|select|style|link|meta|base)[^>]*>.*?<\/\1>|<(script|iframe|object|embed|form|input|button|textarea|select|style|link|meta|base)[^>]*\/?>/gi
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 危险属性正则
|
|
28
|
+
*/
|
|
29
|
+
const DANGEROUS_ATTRS_REGEX = /\s(on\w+|href|src|action|formaction|data|dynsrc|lowsrc)\s*=\s*(['"]?)[^'">\s]*\2/gi
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* javascript: 协议正则
|
|
33
|
+
*/
|
|
34
|
+
const JAVASCRIPT_PROTOCOL_REGEX = /javascript\s*:/gi
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 转义 HTML 特殊字符
|
|
38
|
+
* @param str - 要转义的字符串
|
|
39
|
+
* @returns 转义后的字符串
|
|
40
|
+
* @example
|
|
41
|
+
* escapeHtml('<script>alert("xss")</script>')
|
|
42
|
+
* // '<script>alert("xss")</script>'
|
|
43
|
+
*/
|
|
44
|
+
export function escapeHtml(str: string): string {
|
|
45
|
+
if (!str) return ''
|
|
46
|
+
return str.replace(/[&<>"'`=/]/g, char => HTML_ENTITIES[char])
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 反转义 HTML 特殊字符
|
|
51
|
+
* @param str - 要反转义的字符串
|
|
52
|
+
* @returns 反转义后的字符串
|
|
53
|
+
*/
|
|
54
|
+
export function unescapeHtml(str: string): string {
|
|
55
|
+
if (!str) return ''
|
|
56
|
+
const entities: Record<string, string> = {
|
|
57
|
+
'&': '&',
|
|
58
|
+
'<': '<',
|
|
59
|
+
'>': '>',
|
|
60
|
+
'"': '"',
|
|
61
|
+
''': "'",
|
|
62
|
+
'/': '/',
|
|
63
|
+
'`': '`',
|
|
64
|
+
'=': '='
|
|
65
|
+
}
|
|
66
|
+
return str.replace(/&(amp|lt|gt|quot|#x27|#x2F|#x60|#x3D);/g, entity => entities[entity] || entity)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 移除危险 HTML 标签
|
|
71
|
+
* @param html - HTML 字符串
|
|
72
|
+
* @returns 清理后的 HTML
|
|
73
|
+
* @example
|
|
74
|
+
* stripDangerousTags('<script>alert(1)</script><p>安全</p>')
|
|
75
|
+
* // '<p>安全</p>'
|
|
76
|
+
*/
|
|
77
|
+
export function stripDangerousTags(html: string): string {
|
|
78
|
+
if (!html) return ''
|
|
79
|
+
return html.replace(DANGEROUS_TAGS_REGEX, '')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 移除危险属性
|
|
84
|
+
* @param html - HTML 字符串
|
|
85
|
+
* @returns 清理后的 HTML
|
|
86
|
+
* @example
|
|
87
|
+
* stripDangerousAttrs('<div onclick="alert(1)">内容</div>')
|
|
88
|
+
* // '<div>内容</div>'
|
|
89
|
+
*/
|
|
90
|
+
export function stripDangerousAttrs(html: string): string {
|
|
91
|
+
if (!html) return ''
|
|
92
|
+
return html.replace(DANGEROUS_ATTRS_REGEX, '')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 移除 javascript: 协议
|
|
97
|
+
* @param str - 字符串
|
|
98
|
+
* @returns 清理后的字符串
|
|
99
|
+
*/
|
|
100
|
+
export function stripJavascriptProtocol(str: string): string {
|
|
101
|
+
if (!str) return ''
|
|
102
|
+
return str.replace(JAVASCRIPT_PROTOCOL_REGEX, '')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* XSS 过滤(完整版)
|
|
107
|
+
* @param input - 用户输入
|
|
108
|
+
* @param options - 过滤选项
|
|
109
|
+
* @returns 过滤后的安全字符串
|
|
110
|
+
*/
|
|
111
|
+
export interface XssFilterOptions {
|
|
112
|
+
|
|
113
|
+
/** 是否转义 HTML,默认 true */
|
|
114
|
+
escapeHtml?: boolean
|
|
115
|
+
|
|
116
|
+
/** 是否移除危险标签,默认 true */
|
|
117
|
+
stripDangerousTags?: boolean
|
|
118
|
+
|
|
119
|
+
/** 是否移除危险属性,默认 true */
|
|
120
|
+
stripDangerousAttrs?: boolean
|
|
121
|
+
|
|
122
|
+
/** 是否移除 javascript 协议,默认 true */
|
|
123
|
+
stripJavascriptProtocol?: boolean
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function xssFilter(input: string, options: XssFilterOptions = {}): string {
|
|
127
|
+
if (!input) return ''
|
|
128
|
+
|
|
129
|
+
const {
|
|
130
|
+
escapeHtml: shouldEscape = true,
|
|
131
|
+
stripDangerousTags: shouldStripTags = true,
|
|
132
|
+
stripDangerousAttrs: shouldStripAttrs = true,
|
|
133
|
+
stripJavascriptProtocol: shouldStripJsProtocol = true
|
|
134
|
+
} = options
|
|
135
|
+
|
|
136
|
+
let result = input
|
|
137
|
+
|
|
138
|
+
// 移除危险标签
|
|
139
|
+
if (shouldStripTags) {
|
|
140
|
+
result = stripDangerousTags(result)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 移除危险属性
|
|
144
|
+
if (shouldStripAttrs) {
|
|
145
|
+
result = stripDangerousAttrs(result)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 移除 javascript 协议
|
|
149
|
+
if (shouldStripJsProtocol) {
|
|
150
|
+
result = stripJavascriptProtocol(result)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 转义 HTML
|
|
154
|
+
if (shouldEscape) {
|
|
155
|
+
result = escapeHtml(result)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return result
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 过滤 URL,只允许安全协议
|
|
163
|
+
* @param url - URL 字符串
|
|
164
|
+
* @param allowedProtocols - 允许的协议列表
|
|
165
|
+
* @returns 安全的 URL 或空字符串
|
|
166
|
+
* @example
|
|
167
|
+
* filterUrl('javascript:alert(1)') // ''
|
|
168
|
+
* filterUrl('https://example.com') // 'https://example.com'
|
|
169
|
+
*/
|
|
170
|
+
export function filterUrl(
|
|
171
|
+
url: string,
|
|
172
|
+
allowedProtocols: string[] = ['http', 'https', 'mailto', 'tel', 'ftp']
|
|
173
|
+
): string {
|
|
174
|
+
if (!url) return ''
|
|
175
|
+
|
|
176
|
+
// 移除空白字符
|
|
177
|
+
const trimmedUrl = url.trim()
|
|
178
|
+
|
|
179
|
+
// 检查是否为相对路径或锚点
|
|
180
|
+
if (trimmedUrl.startsWith('/') || trimmedUrl.startsWith('#') || trimmedUrl.startsWith('?')) {
|
|
181
|
+
return trimmedUrl
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 提取协议
|
|
185
|
+
const protocolMatch = trimmedUrl.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/)
|
|
186
|
+
if (!protocolMatch) {
|
|
187
|
+
return trimmedUrl // 无协议,视为相对路径
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const protocol = protocolMatch[1].toLowerCase()
|
|
191
|
+
|
|
192
|
+
// 检查协议是否在允许列表中
|
|
193
|
+
if (allowedProtocols.includes(protocol)) {
|
|
194
|
+
return trimmedUrl
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return ''
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 过滤对象中的 XSS
|
|
202
|
+
* @param obj - 要过滤的对象
|
|
203
|
+
* @param options - 过滤选项
|
|
204
|
+
* @returns 过滤后的对象
|
|
205
|
+
*/
|
|
206
|
+
export function filterObject<T extends Record<string, any>>(
|
|
207
|
+
obj: T,
|
|
208
|
+
options: XssFilterOptions = {}
|
|
209
|
+
): T {
|
|
210
|
+
if (!obj || typeof obj !== 'object') return obj
|
|
211
|
+
|
|
212
|
+
const result: Record<string, any> = {}
|
|
213
|
+
|
|
214
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
215
|
+
if (typeof value === 'string') {
|
|
216
|
+
result[key] = xssFilter(value, options)
|
|
217
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
218
|
+
result[key] = filterObject(value, options)
|
|
219
|
+
} else {
|
|
220
|
+
result[key] = value
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return result as T
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* 白名单标签过滤
|
|
229
|
+
* 只保留允许的 HTML 标签
|
|
230
|
+
* @param html - HTML 字符串
|
|
231
|
+
* @param allowedTags - 允许的标签列表
|
|
232
|
+
* @returns 过滤后的 HTML
|
|
233
|
+
* @example
|
|
234
|
+
* filterHtmlTags('<p><script>alert(1)</script>内容</p>', ['p'])
|
|
235
|
+
* // '<p>内容</p>'
|
|
236
|
+
*/
|
|
237
|
+
export function filterHtmlTags(html: string, allowedTags: string[] = []): string {
|
|
238
|
+
if (!html) return ''
|
|
239
|
+
if (allowedTags.length === 0) return escapeHtml(html)
|
|
240
|
+
|
|
241
|
+
// 构建白名单正则
|
|
242
|
+
const allowedTagsPattern = allowedTags.join('|')
|
|
243
|
+
const openTagRegex = new RegExp(`<(${allowedTagsPattern})([^>]*)>`, 'gi')
|
|
244
|
+
const closeTagRegex = new RegExp(`</(${allowedTagsPattern})>`, 'gi')
|
|
245
|
+
|
|
246
|
+
// 先转义所有 HTML
|
|
247
|
+
let result = escapeHtml(html)
|
|
248
|
+
|
|
249
|
+
// 恢复允许的标签
|
|
250
|
+
result = result.replace(
|
|
251
|
+
new RegExp(`<(${allowedTagsPattern})([^&]*)>`, 'gi'),
|
|
252
|
+
(_, tag, attrs) => `<${tag}${unescapeHtml(attrs)}>`
|
|
253
|
+
)
|
|
254
|
+
result = result.replace(
|
|
255
|
+
new RegExp(`</(${allowedTagsPattern})>`, 'gi'),
|
|
256
|
+
(_, tag) => `</${tag}>`
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
return result
|
|
260
|
+
}
|
|
@@ -45,11 +45,18 @@
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"include": [
|
|
48
|
+
"env.d.ts",
|
|
48
49
|
"src/**/*.ts",
|
|
49
50
|
"src/**/*.d.ts",
|
|
50
51
|
"src/**/*.tsx",
|
|
51
52
|
"src/**/*.vue",
|
|
52
53
|
"build/**/*.ts",
|
|
53
54
|
"build/**/*.tsx"
|
|
55
|
+
],
|
|
56
|
+
/** 编译器默认排除的编译文件 */
|
|
57
|
+
"exclude": [
|
|
58
|
+
"node_modules",
|
|
59
|
+
"dist",
|
|
60
|
+
"public"
|
|
54
61
|
]
|
|
55
62
|
}
|