mirror of
https://github.com/wangwangit/SubsTracker.git
synced 2026-07-01 01:44:26 +08:00
fix: XSS 风险修复
- showToast 改用 textContent 设置消息内容,防止 HTML 注入 - createHoverText 对所有用户输入进行 escapeHtml 转义 - 续订/支付历史/编辑支付模态框中的 subscription.name 和 payment.note 使用 escapeHtml 转义 - debug 页面 adminUsername 转义 - 添加全局 escapeHtml 工具函数
This commit is contained in:
@@ -38,7 +38,7 @@ async function handleDebug(request, env) {
|
||||
<div class="info">
|
||||
<h3>配置信息</h3>
|
||||
<p class="${debugInfo.configExists ? 'success' : 'error'}">配置存在: ${debugInfo.configExists ? '✓' : '✗'}</p>
|
||||
<p>管理员用户名: ${debugInfo.adminUsername}</p>
|
||||
<p>管理员用户名: ${String(debugInfo.adminUsername || '').replace(/</g, '<').replace(/>/g, '>')}</p>
|
||||
<p class="${debugInfo.hasJwtSecret ? 'success' : 'error'}">JWT密钥: ${debugInfo.hasJwtSecret ? '✓' : '✗'} (长度: ${debugInfo.jwtSecretLength})</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -734,6 +734,12 @@
|
||||
return response;
|
||||
}
|
||||
|
||||
// HTML 转义,防止 XSS
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
return String(str).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''');
|
||||
}
|
||||
|
||||
// 农历转换工具函数 - 前端版本
|
||||
const lunarCalendar = {
|
||||
// 农历数据 (1900-2100年)
|
||||
@@ -1039,7 +1045,8 @@ const lunarBiz = {
|
||||
type === 'error' ? 'exclamation-circle' :
|
||||
type === 'warning' ? 'exclamation-triangle' : 'info-circle';
|
||||
|
||||
toast.innerHTML = '<div class="flex items-center"><i class="fas fa-' + icon + ' mr-2"></i><span>' + message + '</span></div><span class="toast-close" onclick="this.parentElement.remove()">×</span>';
|
||||
toast.innerHTML = '<div class="flex items-center"><i class="fas fa-' + icon + ' mr-2"></i><span class="toast-msg"></span></div><span class="toast-close" onclick="this.parentElement.remove()">×</span>';
|
||||
toast.querySelector('.toast-msg').textContent = message;
|
||||
|
||||
container.appendChild(toast);
|
||||
setTimeout(() => toast.classList.add('show'), 50);
|
||||
@@ -1147,12 +1154,12 @@ const lunarBiz = {
|
||||
// 创建带悬浮提示的文本元素
|
||||
function createHoverText(text, maxLength = 30, className = 'text-sm text-gray-900') {
|
||||
if (!text || text.length <= maxLength) {
|
||||
return '<div class="' + className + '">' + text + '</div>';
|
||||
return '<div class="' + className + '">' + escapeHtml(text) + '</div>';
|
||||
}
|
||||
|
||||
const truncated = text.substring(0, maxLength) + '...';
|
||||
const truncated = escapeHtml(text.substring(0, maxLength)) + '...';
|
||||
return '<div class="hover-container">' +
|
||||
'<div class="hover-text ' + className + '" data-full-text="' + text.replace(/"/g, '"') + '">' +
|
||||
'<div class="hover-text ' + className + '" data-full-text="' + escapeHtml(text) + '">' +
|
||||
truncated +
|
||||
'</div>' +
|
||||
'<div class="hover-tooltip"></div>' +
|
||||
@@ -1778,7 +1785,7 @@ const lunarBiz = {
|
||||
' <div class="relative top-20 mx-auto p-5 border w-full max-w-md shadow-lg rounded-md bg-white" onclick="event.stopPropagation()">' +
|
||||
' <div class="flex justify-between items-center pb-3 border-b">' +
|
||||
' <h3 class="text-xl font-semibold text-gray-900">' +
|
||||
' <i class="fas fa-sync-alt mr-2"></i>手动续订 - ' + subscription.name +
|
||||
' <i class="fas fa-sync-alt mr-2"></i>手动续订 - ' + escapeHtml(subscription.name) +
|
||||
' </h3>' +
|
||||
' <button onclick="closeRenewFormModal()" class="text-gray-400 hover:text-gray-500">' +
|
||||
' <i class="fas fa-times text-2xl"></i>' +
|
||||
@@ -2035,7 +2042,7 @@ const lunarBiz = {
|
||||
periodHtml = '<div class="mt-1 ml-6 text-xs text-gray-500"><i class="fas fa-clock mr-1"></i>计费周期: ' + startStr + ' - ' + endStr + '</div>';
|
||||
}
|
||||
|
||||
const noteHtml = payment.note ? '<div class="mt-1 ml-6 text-sm text-gray-600">' + payment.note + '</div>' : '';
|
||||
const noteHtml = payment.note ? '<div class="mt-1 ml-6 text-sm text-gray-600">' + escapeHtml(payment.note) + '</div>' : '';
|
||||
const paymentDataJson = JSON.stringify(payment).replace(/"/g, '"');
|
||||
return `
|
||||
<div class="border-b border-gray-200 py-3 hover:bg-gray-50">
|
||||
@@ -2077,7 +2084,7 @@ const lunarBiz = {
|
||||
<div class="relative top-20 mx-auto p-5 border w-full max-w-2xl shadow-lg rounded-md bg-white" onclick="event.stopPropagation()">
|
||||
<div class="flex justify-between items-center pb-3 border-b">
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
<i class="fas fa-history mr-2"></i>${subscription.name} - 支付历史
|
||||
<i class="fas fa-history mr-2"></i>${escapeHtml(subscription.name)} - 支付历史
|
||||
</h3>
|
||||
<button onclick="closePaymentHistoryModal()" class="text-gray-400 hover:text-gray-500">
|
||||
<i class="fas fa-times text-2xl"></i>
|
||||
@@ -2193,7 +2200,7 @@ const lunarBiz = {
|
||||
<form id="editPaymentForm" class="mt-4 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">订阅名称</label>
|
||||
<input type="text" value="${subscription.name}" disabled
|
||||
<input type="text" value="${escapeHtml(subscription.name)}" disabled
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-100">
|
||||
</div>
|
||||
|
||||
@@ -2211,7 +2218,7 @@ const lunarBiz = {
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">备注</label>
|
||||
<input type="text" id="editPaymentNote" value="${payment.note || ''}"
|
||||
<input type="text" id="editPaymentNote" value="${escapeHtml(payment.note || '')}"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500">
|
||||
</div>
|
||||
|
||||
|
||||
@@ -643,7 +643,8 @@
|
||||
type === 'error' ? 'exclamation-circle' :
|
||||
type === 'warning' ? 'exclamation-triangle' : 'info-circle';
|
||||
|
||||
toast.innerHTML = '<div class="flex items-center"><i class="fas fa-' + icon + ' mr-2"></i><span>' + message + '</span></div><span class="toast-close" onclick="this.parentElement.remove()">×</span>';
|
||||
toast.innerHTML = '<div class="flex items-center"><i class="fas fa-' + icon + ' mr-2"></i><span class="toast-msg"></span></div><span class="toast-close" onclick="this.parentElement.remove()">×</span>';
|
||||
toast.querySelector('.toast-msg').textContent = message;
|
||||
|
||||
container.appendChild(toast);
|
||||
setTimeout(() => toast.classList.add('show'), 50);
|
||||
|
||||
Reference in New Issue
Block a user