Files
hms/apps/miniprogram/src/utils/sanitize-html.ts
iven 652cccf66c fix(mp): 五专家组全面审计修复 — 安全+功能+UX+性能+代码质量
安全修复:
- 移除硬编码管理员凭据 admin/Admin@2026,改用环境变量注入
- 移除 forceSetAuth 全局 bridge 方法,减少攻击面
- sanitizeHtml 从黑名单正则升级为白名单方式
- secure-storage 实现 XOR+Base64 加密存储,不再明文
- 添加旧数据迁移逻辑 migrateLegacyStorage

功能修复:
- 新增咨询创建页(consultation/create),修复"发起咨询"按钮导航失败
- 修复咨询详情页长轮询可能永远不启动(dataLoadedRef → useState)
- 新增 createSession service API
- 预约页面从主包移至分包,配置 commonChunks 优化主包体积

UX 修复:
- 65 处硬编码字号 → var(--tk-font-*) token 替换
  - AI 聊天页 13 处、咨询详情页 14 处、医生端核心页 38 处
- StatusTag 色值对齐设计系统色板
- Loading 文字从 --tk-font-h1(28px) 修正为 --tk-font-body-sm
- EmptyState 文字从 --tk-font-num(30px)/--tk-font-h2(22px) 修正
- 医生端 5 处硬编码颜色 → SCSS 变量
2026-05-21 13:35:46 +08:00

53 lines
1.5 KiB
TypeScript

const ALLOWED_TAGS = new Set([
'p', 'br', 'hr', 'div', 'span',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'ul', 'ol', 'li',
'table', 'thead', 'tbody', 'tr', 'th', 'td',
'strong', 'em', 'b', 'i', 'u', 's', 'del', 'ins',
'blockquote', 'pre', 'code',
'a', 'img',
'dl', 'dt', 'dd',
'sup', 'sub',
]);
const ALLOWED_ATTRS: Record<string, Set<string>> = {
'*': new Set(['class']),
a: new Set(['href', 'title']),
img: new Set(['src', 'alt', 'width', 'height']),
td: new Set(['colspan', 'rowspan']),
th: new Set(['colspan', 'rowspan']),
};
const URL_ATTRS = new Set(['href', 'src']);
const SAFE_URL_RE = /^(?:https?|mailto|tel):|^$/i;
const TAG_RE = /<\/?([a-zA-Z][a-zA-Z0-9]*)\b[^>]*\/?>/g;
const ATTR_RE = /([a-zA-Z][a-zA-Z0-9-]*)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
export function sanitizeHtml(html: string): string {
if (!html) return '';
return html.replace(TAG_RE, (fullMatch, tagName) => {
const tag = tagName.toLowerCase();
if (!ALLOWED_TAGS.has(tag)) return '';
const allowedForTag = ALLOWED_ATTRS[tag] || new Set();
const allowedGlobal = ALLOWED_ATTRS['*'];
const combined = new Set([...allowedForTag, ...allowedGlobal]);
const cleaned = fullMatch.replace(ATTR_RE, (_, attrName, dqVal, sqVal) => {
const attr = attrName.toLowerCase();
const val = dqVal ?? sqVal ?? '';
if (!combined.has(attr)) return '';
if (URL_ATTRS.has(attr) && !SAFE_URL_RE.test(val)) return '';
return ` ${attr}="${val}"`;
});
return cleaned;
});
}