Просмотр исходного кода

fix: 全面审查修复 - 消除崩溃风险与安全隐患

CRITICAL 修复:
- endecode-lib.js: jwtDecode 使用未定义变量 pos 导致 ReferenceError
- timestamp: FILETIME 转换改用 BigInt 避免 64 位整数精度丢失
- menu.js: Service Worker 中 window.close 改为安全的 self 检查

HIGH 修复:
- background.js: _colorPickerCapture 增加 tabs 空数组保护
- menu.js: 3 处 executeScript 回调增加 resp 空值校验
- qr-code: 条形码错误信息 HTML 转义防 XSS
- background.js: inject-scripts-to-tab 条件不满足时正确回调
- options: filteredTools 搜索防 tool.tips undefined 崩溃

MEDIUM 修复:
- timestamp: localeToStamp 校验失败后补 return
- trans-radix: mounted 中补调 loadPatchHotfix
- en-decode: activeTab 空引用保护
- 25 个工具统一 loadPatchHotfix 安全守卫 (typeof + length < 50000)

遗漏的 evalCore 清理:
- dynamic/index.js, devtools/index.js, monkey.js 中残留的
  evalCore.getEvalInstance 全部替换为 new Function / reduce

其他:
- CI: Node.js 20→22, 去除 cache:npm 依赖, 适配 Node24
- svg-converter: 顶部导航改为标准 panel-heading 格式

Made-with: Cursor
Alien 6 часов назад
Родитель
Сommit
8da523cf50

+ 5 - 3
.github/workflows/ci.yml

@@ -5,6 +5,9 @@ on:
   pull_request:
     branches: [master, main]
 
+env:
+  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+
 jobs:
   test:
     runs-on: ubuntu-latest
@@ -12,7 +15,6 @@ jobs:
       - uses: actions/checkout@v4
       - uses: actions/setup-node@v4
         with:
-          node-version: 20
-          cache: npm
-      - run: npm ci || npm install
+          node-version: 22
+      - run: npm install --no-package-lock
       - run: npm test

+ 1 - 1
apps/aiagent/index.js

@@ -87,7 +87,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 4 - 0
apps/background/background.js

@@ -406,7 +406,9 @@ let BgPageInstance = (function () {
 
     let _colorPickerCapture = function(params) {
         chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
+            if (!tabs || !tabs.length) return;
             chrome.tabs.captureVisibleTab(null, {format: 'png'}, function (dataUrl) {
+                if (chrome.runtime.lastError || !dataUrl) return;
                 let pickerParams = { setPickerImage: true, pickerImage: dataUrl };
                 InjectTools.inject(tabs[0].id, {
                     func: (p) => { window.colorpickerNoPage && window.colorpickerNoPage(p); },
@@ -585,6 +587,8 @@ let BgPageInstance = (function () {
                                 target: { tabId: sender.tab.id },
                                 files: request.files
                             }, () => { callback && callback(true); });
+                        } else {
+                            callback && callback(false);
                         }
                         return true;
                     // 加载本地脚本文件

+ 21 - 10
apps/background/menu.js

@@ -45,9 +45,12 @@ export default (function () {
                             target: {tabId:tab.id,allFrames:false},
                             args: [info.selectionText || ''],
                             func: (text) => text
-                        }, resp => globalThis.FeHelperBg.DynamicToolRunner({
-                            tool, withContent: resp[0].result
-                        }));
+                        }, resp => {
+                            if (chrome.runtime.lastError || !resp || !resp[0]) return;
+                            globalThis.FeHelperBg.DynamicToolRunner({
+                                tool, withContent: resp[0].result
+                            });
+                        });
                     };
                     break;
 
@@ -58,9 +61,12 @@ export default (function () {
                             target: {tabId:tab.id,allFrames:false},
                             args: [info.linkUrl || info.srcUrl || info.selectionText || info.pageUrl || ''],
                             func: (text) => text
-                        }, resp => globalThis.FeHelperBg.DynamicToolRunner({
-                            tool, withContent: resp[0].result
-                        }));
+                        }, resp => {
+                            if (chrome.runtime.lastError || !resp || !resp[0]) return;
+                            globalThis.FeHelperBg.DynamicToolRunner({
+                                tool, withContent: resp[0].result
+                            });
+                        });
                     };
                     break;
 
@@ -70,9 +76,12 @@ export default (function () {
                             target: {tabId:tab.id,allFrames:false},
                             args: [info.linkUrl || info.srcUrl || info.selectionText || info.pageUrl || tab.url || ''],
                             func: (text) => text
-                        }, resp => globalThis.FeHelperBg.DynamicToolRunner({
-                            tool, withContent: resp[0].result
-                        }));
+                        }, resp => {
+                            if (chrome.runtime.lastError || !resp || !resp[0]) return;
+                            globalThis.FeHelperBg.DynamicToolRunner({
+                                tool, withContent: resp[0].result
+                            });
+                        });
                     };
                     toolMap[tool].menuConfig[1].onClick = function (info, tab) {
                         chrome.scripting.executeScript({
@@ -186,7 +195,9 @@ export default (function () {
                                     noPage: !!tools[tool].noPage,
                                     query: `tool=${tool}`
                                 });
-                                !!tools[tool].noPage && setTimeout(window.close, 200);
+                                if (tools[tool].noPage && typeof self !== 'undefined' && typeof self.close === 'function') {
+                                    setTimeout(() => self.close(), 200);
+                                }
                             }
                         }];
                     }

+ 5 - 6
apps/background/monkey.js

@@ -40,7 +40,7 @@ export default (() => {
                         let scripts = '(' + ((monkey) => {
                             let injectFunc = () => {
                                 // 执行脚本
-                                try{evalCore.getEvalInstance(window)(monkey.mScript)}catch(x){}
+                                try{ new Function(monkey.mScript)() }catch(x){}
 
                                 parseInt(monkey.mRefresh) && setTimeout(() => {
                                     location.reload(true);
@@ -49,12 +49,11 @@ export default (() => {
 
                             window._fhImportJs = js => {
                                 return fetch(js).then(resp => resp.text()).then(jsText => {
-                                    if(window.evalCore && window.evalCore.getEvalInstance){
-                                         return window.evalCore.getEvalInstance(window)(jsText);
+                                    try { new Function(jsText)(); } catch(e) {
+                                        let el = document.createElement('script');
+                                        el.textContent = jsText;
+                                        document.head.appendChild(el);
                                     }
-                                    let el = document.createElement('script');
-                                    el.textContent = jsText;
-                                    document.head.appendChild(el);
                                 });
                             };
 

+ 1 - 1
apps/chart-maker/main.js

@@ -198,7 +198,7 @@ document.addEventListener('DOMContentLoaded', function() {
                     style.textContent = patch.css;
                     document.head.appendChild(style);
                 }
-                if (patch.js) {
+                if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                     try {
                         new Function(patch.js)();
                     } catch (e) {

+ 1 - 1
apps/code-beautify/index.js

@@ -54,7 +54,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/code-compress/index.js

@@ -43,7 +43,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/datetime-calc/index.js

@@ -1055,7 +1055,7 @@ function loadPatchHotfix() {
                 style.textContent = patch.css;
                 document.head.appendChild(style);
             }
-            if (patch.js) {
+            if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                 try {
                     new Function(patch.js)();
                 } catch (e) {

+ 2 - 2
apps/devtools/index.js

@@ -252,7 +252,7 @@ new Vue({
                     let files = result.jsCss;
 
                     // 获取所有网络文件的总个数,以便于计算进度
-                    let total = evalCore.getEvalInstance(window)(Object.values(files).map(a => a.length).join('+')) + 1;
+                    let total = Object.values(files).reduce((sum, arr) => sum + arr.length, 0) + 1;
                     let loaded = 1;
                     fnProgress && fnProgress(Math.floor(100 * loaded / total) + '%');
 
@@ -313,7 +313,7 @@ new Vue({
             let arrPromise = files.map(file => fetch(`${site}/${demoName}/${file}`).then(resp => resp.text()));
             return Promise.all(arrPromise).then(contents => {
                 // fh-config.js
-                let json = evalCore.getEvalInstance(window)(contents[0]);
+                let json = new Function('return ' + contents[0])();
                 this.addToolConfigs(json);
 
                 // index.html

+ 3 - 1
apps/dynamic/index.js

@@ -58,7 +58,9 @@ let DynamicTool = (() => {
                     document.head.appendChild(node);
                 }
                 allJs = allJs.map(f => values[1][f]).join(';');
-                allJs.length && window.evalCore.getEvalInstance(window)(allJs);
+                if (allJs.length) {
+                    try { new Function(allJs)(); } catch(e) { console.error('动态工具JS执行失败', e); }
+                }
             });
         });
     };

+ 3 - 3
apps/en-decode/endecode-lib.js

@@ -491,9 +491,9 @@ let EncodeUtils = (() => {
                 throw new InvalidTokenError("Invalid token specified: must be three parts");
             }
             
-            for(let part of parts){
-                if (typeof part !== "string") {
-                    throw new InvalidTokenError(`Invalid token specified: missing part #${pos + 1}`);
+            for(let i = 0; i < parts.length; i++){
+                if (typeof parts[i] !== "string" || parts[i].length === 0) {
+                    throw new InvalidTokenError(`Invalid token specified: missing part #${i + 1}`);
                 }        
             }
         

+ 3 - 2
apps/en-decode/index.js

@@ -17,7 +17,8 @@ new Vue({
         // 在tab创建或者更新时候,监听事件,看看是否有参数传递过来
         if (location.protocol === 'chrome-extension:') {
             chrome.tabs.query({currentWindow: true,active: true, }, (tabs) => {
-                let activeTab = tabs.filter(tab => tab.active)[0];
+                let activeTab = tabs && tabs.filter(tab => tab.active)[0];
+                if (!activeTab) return;
                 chrome.runtime.sendMessage({
                     type: 'fh-dynamic-any-thing',
                     thing: 'request-page-content',
@@ -48,7 +49,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/excel2json/index.js

@@ -71,7 +71,7 @@ function loadPatchHotfix() {
                 style.textContent = patch.css;
                 document.head.appendChild(style);
             }
-            if (patch.js) {
+            if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                 try {
                     new Function(patch.js)();
                 } catch (e) {

+ 1 - 1
apps/html2markdown/index.js

@@ -40,7 +40,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/image-base64/index.js

@@ -100,7 +100,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/json-diff/index.js

@@ -413,7 +413,7 @@ window.vueApp = new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/loan-rate/index.js

@@ -350,7 +350,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/mock-data/index.js

@@ -548,7 +548,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 2 - 2
apps/options/index.js

@@ -168,8 +168,8 @@ new Vue({
             if (this.searchKey) {
                 const key = this.searchKey.toLowerCase();
                 result = result.filter(tool => 
-                    tool.name.toLowerCase().includes(key) || 
-                    tool.tips.toLowerCase().includes(key)
+                    (tool.name || '').toLowerCase().includes(key) || 
+                    (tool.tips || '').toLowerCase().includes(key)
                 );
             }
 

+ 1 - 1
apps/page-monkey/index.js

@@ -124,7 +124,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/page-timing/index.js

@@ -314,7 +314,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/password/index.js

@@ -105,7 +105,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/postman/index.js

@@ -83,7 +83,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 2 - 1
apps/qr-code/index.js

@@ -99,7 +99,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {
@@ -139,6 +139,7 @@ new Vue({
                             'ITF14': '需要 14 位纯数字'
                         };
                         var msg = (e && (e.message || String(e))) || '未知错误';
+                        msg = msg.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
                         var hint = hints[this.barcodeFormat];
                         if (hint) msg += '<br><span style="font-size:12px;color:#666">提示:' + hint + '</span>';
                         $('#preview').html('<div style="color:red;text-align:center;padding:20px">条形码生成失败<br>' + msg + '</div>');

+ 1 - 1
apps/screenshot/index.js

@@ -44,7 +44,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/sticky-notes/index.js

@@ -142,7 +142,7 @@ let StickyNotes = (() => {
                     style.textContent = patch.css;
                     document.head.appendChild(style);
                 }
-                if (patch.js) {
+                if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                     try {
                         new Function(patch.js)();
                     } catch (e) {

+ 1 - 226
apps/svg-converter/index.css

@@ -23,134 +23,6 @@ body {
     min-height: 100vh;
 }
 
-/* 导航栏样式 */
-.fe-mod-head {
-    background-color: #3c4043;
-    color: white;
-    padding: 0;
-    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
-}
-
-.mod-head-inner {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    padding: 2px 10px;
-    max-width: 1400px;
-    margin: 0 auto;
-}
-
-.mod-head-title {
-    display: flex;
-    align-items: center;
-}
-
-/* FeHelper品牌样式 */
-.navbar-brand {
-    display: flex;
-    align-items: center;
-}
-
-.navbar-brand img {
-    width: 24px;
-    height: 24px;
-    margin-right: 8px;
-}
-
-.brand-text {
-    font-size: 18px;
-    font-weight: bold;
-    color: #fff;
-    margin-right: 10px;
-}
-
-.brand-subtitle {
-    font-size: 16px;
-    color: #f0f0f0;
-    font-weight: normal;
-    border-left: 1px solid rgba(255, 255, 255, 0.3);
-    padding-left: 10px;
-}
-
-
-.nav-icon {
-    margin-right: 5px;
-    font-style: normal;
-    display: inline-block;
-}
-
-.mod-head-title h1 {
-    font-size: 1.5rem;
-    margin: 0;
-    padding: 0;
-    font-weight: 500;
-}
-
-.back-link {
-    display: inline-block;
-    width: 24px;
-    height: 24px;
-    margin-right: 12px;
-    background: url("../static/img/back-arrow.png") no-repeat center;
-    background-size: contain;
-    opacity: 0.8;
-    transition: opacity 0.3s;
-}
-
-.back-link:hover {
-    opacity: 1;
-}
-
-.mod-head-actions {
-    display: flex;
-    align-items: center;
-    gap: 10px;
-}
-
-.mod-head-btn {
-    padding: 8px 15px;
-    background-color: #4285f4;
-    color: white;
-    border: none;
-    border-radius: 4px;
-    cursor: pointer;
-    font-size: 14px;
-    transition: background-color 0.3s;
-    text-decoration: none;
-    display: inline-flex;
-    align-items: center;
-}
-
-.mod-head-btn:hover {
-    background-color: #3367d6;
-    color: white;
-    text-decoration: none;
-}
-
-.mod-head-btn.donate {
-    background-color: #ea4335;
-}
-
-.mod-head-btn.donate:hover {
-    background-color: #d62516;
-}
-
-/* 工具栏样式 */
-.mod-toolbar {
-    background: #fff;
-    padding: 10px 20px;
-    border-bottom: 1px solid var(--border-color);
-}
-
-.toolbar-inner {
-    display: flex;
-    justify-content: space-between;
-}
-
-.tool-group {
-    display: flex;
-    gap: 10px;
-}
 
 /* 主体内容样式 */
 .mod-main {
@@ -191,7 +63,7 @@ body {
     overflow: hidden;
 }
 
-.panel-title {
+.x-panel-title {
     background: #e9ecef;
     padding: 10px 15px;
     font-weight: bold;
@@ -667,100 +539,3 @@ body {
     opacity: 0.9;
 } 
 
-.mod-head-actions {
-    position: relative;
-}
-
-/* 工具市场按钮样式(保持不变) */
-.mod-head-actions>a.x-other-tools {
-    margin: 1px 0 0;
-    font-size: 13px;
-    cursor: pointer;
-    text-decoration: none;
-    -webkit-user-select: none;
-    user-select: none;
-    color: #333;
-    float: right;
-    background-color: #f5f8ff;
-    padding: 5px 10px;
-    border-radius: 15px;
-    border: 1px solid #d0d9ff;
-    transition: all 0.3s ease;
-    display: flex;
-    align-items: center;
-    position: relative;
-    top: 0px;
-}
-
-.mod-head-actions>a.x-other-tools .icon-plus-circle {
-    display: inline-block;
-    width: 16px;
-    height: 16px;
-    background: url(../static/img/plus-circle.svg) no-repeat center center;
-    background-size: contain;
-    margin-right: 5px;
-}
-
-.mod-head-actions>a.x-other-tools .tool-market-badge {
-    display: inline-block;
-    background-color: #4d89fe;
-    color: white;
-    padding: 2px 6px;
-    border-radius: 10px;
-    margin-left: 5px;
-    font-size: 12px;
-    font-weight: bold;
-}
-
-.mod-head-actions>a.x-other-tools:hover {
-    color: #333;
-    background-color: #e6edff;
-    box-shadow: 0 2px 5px rgba(0,0,0,0.15);
-    transform: translateY(-1px);
-}
-
-
-/* 保持原有的顶部导航样式 */
-.x-donate-link {
-    float: right;
-    line-height: 18px;
-    color: #2563eb;
-    cursor: pointer;
-    text-decoration: none;
-    border: none;
-    white-space: nowrap;
-    margin-right: auto;
-    border-radius: 20px;
-    background-color: #eff6ff;
-    transition: all 0.2s ease;
-    position: relative;
-    display: inline-flex;
-    align-items: center;
-    box-shadow: 0 1px 2px rgba(37, 99, 235, 0.1);
-    position: absolute;
-    right: 215px;
-    top: 4px;
-    padding: 4px 12px;
-    margin: 0 10px;
-    font-size: 12px;
-    font-weight: normal;
-}
-
-.x-donate-link:hover {
-    background-color: #dbeafe;
-    color: #1d4ed8;
-    text-decoration: none;
-    box-shadow: 0 2px 4px rgba(37, 99, 235, 0.15);
-    transform: translateY(-1px);
-}
-
-.x-donate-link>a {
-    font-size: 12px;
-    color: blue;
-    cursor: pointer;
-    text-decoration: underline;
-}
-.x-donate-link>a:hover {
-    text-decoration: underline;
-    color: #f00;
-}

+ 11 - 16
apps/svg-converter/index.html

@@ -18,23 +18,18 @@
         </style>
     </head>
     <body>
-        <div id="pageContainer" v-cloak>
-            <div class="fe-mod-head">
-                <div class="mod-head-inner">
-                    <div class="mod-head-title">
-                        <div class="navbar-brand">
-                            <img src="../static/img/fe-16.png" alt="fehelper"/> 
-                            <span class="brand-text">FeHelper</span>
-                            <span class="brand-subtitle">SVG转图片工具</span>
-                        </div>
-                    </div>
-                    <div class="mod-head-actions">
+        <div class="wrapper" id="pageContainer" v-cloak>
+            <div class="panel panel-default" style="margin-bottom: 0px;">
+                <div class="panel-heading">
+                    <h3 class="panel-title">
+                        <a href="https://fehelper.com" target="_blank" class="x-a-high">
+                            <img src="../static/img/fe-16.png" alt="fehelper"/> FeHelper</a>:SVG转图片工具
                         <a href="#" class="x-donate-link" @click="openDonateModal($event)"><i class="nav-icon">❤&nbsp;</i>打赏鼓励</a>
                         <a class="x-other-tools" @click="openOptionsPage($event)"><i class="icon-plus-circle"></i> 探索更多实用工具 <span class="tool-market-badge">工具市场</span></a>
-                    </div>
+                    </h3>
                 </div>
             </div>
-            <div class="fe-mod-bd">
+            <div class="panel-body">
                 <div class="mod-main">
                     <div class="main-inner">
                         <!-- SVG转图片界面 -->
@@ -43,7 +38,7 @@
                                 <tr>
                                     <td>
                                         <div ref="panelBox" class="x-panel" :class="{'has-image': svgPreviewSrc}">
-                                            <div class="panel-title">SVG源文件</div>
+                                            <div class="x-panel-title">SVG源文件</div>
                                             <div class="panel-content">
                                                 <div class="upload-zone" v-if="!svgPreviewSrc">
                                                     <p>
@@ -115,7 +110,7 @@
                                     </td>
                                     <td>
                                         <div class="x-panel" :class="{'has-image': imgPreviewSrc}">
-                                            <div class="panel-title">转换后图片</div>
+                                            <div class="x-panel-title">转换后图片</div>
                                             <div class="panel-content">
                                                 <div class="result-zone">
                                                     <p v-if="!imgPreviewSrc">转换后的图片将显示在这里</p>
@@ -159,7 +154,7 @@
                     </div>
                 </div>
             </div>
-            
+
             <!-- 添加加载状态指示器 -->
             <div class="loading-overlay" v-if="isProcessing">
                 <div class="loading-container">

+ 1 - 1
apps/svg-converter/index.js

@@ -220,7 +220,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/timestamp/index.html

@@ -64,7 +64,7 @@
             <div class="row">
                 <span>现在的当地时间为:</span>
                 <input class="form-control ui-d-ib" id="txtNowDate" ref="txtNowDate" v-model="txtNowDate" type="text">
-                <input class="mod-time-input form-control ui-d-ib btn btn-primary ui-ml-10 ui-mr-10" id="btnToggle" ref="btnToggle" @click="unixToggle()" class="-e-btn" type="button" value="暂停" tabindex="-1">
+                <input class="mod-time-input form-control ui-d-ib btn btn-primary ui-ml-10 ui-mr-10 -e-btn" id="btnToggle" ref="btnToggle" @click="unixToggle()" type="button" value="暂停" tabindex="-1">
             </div>
             <div class="row ui-mt-10">
                 <span>现在的Unix时间戳:</span>

+ 8 - 5
apps/timestamp/index.js

@@ -78,6 +78,7 @@ new Vue({
             let locale = (new Date(Date.parse(this.txtLocale) - ((new Date()).getTimezoneOffset() + this.curGMT * 60) * 60000)).getTime();
             if (isNaN(locale)) {
                 alert('请输入合法的时间格式,如:2014-04-01 10:01:01,或:2014-01-01');
+                return;
             }
             let base = this.secTo === 's' ? 1000 : 1;
             this.txtDesStamp = Math.round(locale / base);
@@ -120,18 +121,20 @@ new Vue({
                 alert('请先填写 Windows FILETIME');
                 return;
             }
-            let fileTime = parseInt(ft, 10);
-            if (isNaN(fileTime)) {
+            let fileTime;
+            try {
+                fileTime = BigInt(ft);
+            } catch(e) {
                 alert('请输入合法的 FILETIME');
                 return;
             }
-            let unixMs = fileTime / 10000 - 11644473600000;
+            let unixMs = Number(fileTime / 10000n) - 11644473600000;
             let d = new Date(unixMs);
             this.txtFileTimeResult = d.format('yyyy-MM-dd HH:mm:ss.SSS');
         },
         dateToFileTime: function () {
             let unixMs = this.txtNowMs;
-            let fileTime = Math.round((unixMs + 11644473600000) * 10000);
+            let fileTime = BigInt(unixMs + 11644473600000) * 10000n;
             this.txtFileTimeResult = String(fileTime);
         },
 
@@ -148,7 +151,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/trans-color/index.js

@@ -52,7 +52,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 2 - 2
apps/trans-radix/index.js

@@ -13,8 +13,8 @@ new Vue({
     },
 
     mounted: function () {
-        // 进制转换的初始化
         this.radixConvert();
+        this.loadPatchHotfix();
     },
 
     methods: {
@@ -32,7 +32,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {

+ 1 - 1
apps/uuid-gen/index.js

@@ -131,7 +131,7 @@
                     style.textContent = patch.css;
                     document.head.appendChild(style);
                 }
-                if (patch.js) {
+                if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                     var script = document.createElement('script');
                     script.textContent = patch.js;
                     document.head.appendChild(script);

+ 1 - 1
apps/websocket/index.js

@@ -34,7 +34,7 @@ new Vue({
                         style.textContent = patch.css;
                         document.head.appendChild(style);
                     }
-                    if (patch.js) {
+                    if (patch.js && typeof patch.js === 'string' && patch.js.length < 50000) {
                         try {
                             new Function(patch.js)();
                         } catch (e) {