diff --git a/web/apps/web/package.json b/web/apps/web/package.json index 5a7de574..b0343e98 100644 --- a/web/apps/web/package.json +++ b/web/apps/web/package.json @@ -90,7 +90,8 @@ "uuid": "^10.0.0", "zod": "^3.23.8", "zustand": "^4.5.4", - "zustand-computed-state": "^0.1.8" + "zustand-computed-state": "^0.1.8", + "isomorphic-dompurify": "^2.14.0" }, "devDependencies": { "@shellagent/eslint-config": "workspace:*", diff --git a/web/apps/web/src/components/app/node-form/widgets/highlight-input/variable-value-block/components/index.tsx b/web/apps/web/src/components/app/node-form/widgets/highlight-input/variable-value-block/components/index.tsx index 4e65c138..bb8c1b5d 100644 --- a/web/apps/web/src/components/app/node-form/widgets/highlight-input/variable-value-block/components/index.tsx +++ b/web/apps/web/src/components/app/node-form/widgets/highlight-input/variable-value-block/components/index.tsx @@ -3,6 +3,7 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { $getNodeByKey, COMMAND_PRIORITY_EDITOR } from 'lexical'; import { memo, useRef, useEffect } from 'react'; +import { sanitize } from 'isomorphic-dompurify'; import { cn } from '@/utils/cn'; @@ -125,21 +126,23 @@ const VariableValueBlockComponent = ({ }; const renderParts = () => { - return parts - .map(part => { - if (part.type === PartType.TEXT) { - return part.content; - } else if (part.type === PartType.VARIABLE) { - return ` { + if (part.type === PartType.TEXT) { + return part.content; + } else if (part.type === PartType.VARIABLE) { + return `${part.display}`; - } - return ''; - }) - .join(''); + } + return ''; + }) + .join(''), + ); }; return ( diff --git a/web/apps/web/src/components/app/plugins/comfyui/widgets/comfyui-editor.tsx b/web/apps/web/src/components/app/plugins/comfyui/widgets/comfyui-editor.tsx index 16a80eac..60f33e62 100644 --- a/web/apps/web/src/components/app/plugins/comfyui/widgets/comfyui-editor.tsx +++ b/web/apps/web/src/components/app/plugins/comfyui/widgets/comfyui-editor.tsx @@ -21,6 +21,7 @@ import { import { observer } from 'mobx-react-lite'; import React, { useEffect, useRef } from 'react'; import { Box, Flex } from 'react-system'; +import { sanitize } from 'isomorphic-dompurify'; import { DEFAULT_MODAL_STYLES, @@ -226,7 +227,9 @@ export const ComfyUIEditorModal = observer(() => { ]}>
') || '', + __html: sanitize( + model.messageDetail?.replaceAll('\n', '
') || '', + ), }} /> diff --git a/web/apps/web/src/services/base.ts b/web/apps/web/src/services/base.ts index 95947d9b..d12f8293 100755 --- a/web/apps/web/src/services/base.ts +++ b/web/apps/web/src/services/base.ts @@ -128,11 +128,27 @@ export const customFetch = async ( { toastId: 'login-error' }, ); setTimeout(() => { + const currentUrl = window.location.href; + const isValidRedirectUrl = (() => { + try { + const url = new URL(currentUrl); + return ( + url.hostname.endsWith('myshell.fun') || + url.hostname.endsWith('myshell.ai') || + url.hostname.endsWith('myshell.life') + ); + } catch (e) { + return false; + } + })(); + + const redirectUrl = isValidRedirectUrl + ? currentUrl + : 'https://myshell.ai'; + window.location.href = `${ process.env.NEXT_PUBLIC_LOGIN_URL - }?login=true&redirect=${decodeURIComponent( - window.location.href, - )}`; + }?login=true&redirect=${encodeURIComponent(redirectUrl)}`; }, 3000); } diff --git a/web/packages/form-engine/package.json b/web/packages/form-engine/package.json index 8ec87586..18070e02 100644 --- a/web/packages/form-engine/package.json +++ b/web/packages/form-engine/package.json @@ -30,6 +30,7 @@ "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-error-boundary": "^4.0.13", + "sval": "^0.6.1", "tailwind-merge": "^2.5.2", "zod": "^3.23.8", "zustand": "^4.5.5" diff --git a/web/packages/form-engine/src/utils/exec.spec.ts b/web/packages/form-engine/src/utils/exec.spec.ts new file mode 100644 index 00000000..ca80445c --- /dev/null +++ b/web/packages/form-engine/src/utils/exec.spec.ts @@ -0,0 +1,45 @@ +import { exec } from './exec'; +import Sval from 'sval'; + +describe('exec', () => { + it('should return the result of the code', () => { + const result = exec(`$this.value === "image"`, { + $this: { + value: 'text', + }, + }); + expect(result).toBe(false); + }); + + it('exec sval ver', () => { + const result = exec(`$this.value === "image"`, { + $this: { + value: 'image', + }, + }); + expect(result).toBe(true); + + const result2 = exec(`$this.value === "image"`, { + $this: { + value: 'text', + }, + }); + expect(result2).toBe(false); + }); + + it('sval', () => { + const interpreter = new Sval({ + ecmaVer: 'latest', + sourceType: 'script', + sandBox: true, + }); + const code = ` +globalThis.$this = { + value: "text" +} +exports.a = $this.value === "image" + `; + interpreter.run(code); + expect(interpreter.exports.a).toBe(false); + }); +}); diff --git a/web/packages/form-engine/src/utils/exec.ts b/web/packages/form-engine/src/utils/exec.ts index 4cd8f28e..8d0b8b45 100644 --- a/web/packages/form-engine/src/utils/exec.ts +++ b/web/packages/form-engine/src/utils/exec.ts @@ -1,18 +1,29 @@ import { TContext } from '../types'; +import Sval from 'sval'; -function exec(code: string, scope: TContext) { - try { - const str = ` - var ${'____data'} = arguments[0]; - with(${'____data'}) { - return ${code} - } - `; +function safeExec(expression: string, scope: TContext) { + const interpreter = new Sval({ + ecmaVer: 'latest', + sourceType: 'script', + sandBox: true, + }); - return new Function(str)(scope); - } catch (e) { - console.log(e); - } + let globalThisAssignments = ''; + + Object.keys(scope).forEach(key => { + const value = scope[key]; + globalThisAssignments += `globalThis[${JSON.stringify( + key, + )}] = ${JSON.stringify(value)};\n`; + }); + + const code = ` +${globalThisAssignments} +exports.a = ${expression};`; + + interpreter.run(code); + + return interpreter.exports.a; } -export { exec }; +export { safeExec as exec }; diff --git a/web/packages/form-engine/wallaby.js b/web/packages/form-engine/wallaby.js new file mode 100644 index 00000000..685c3f40 --- /dev/null +++ b/web/packages/form-engine/wallaby.js @@ -0,0 +1,5 @@ +module.exports = function (wallaby) { + return { + autoDetect: ['jest'], + }; +}; diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index b378d916..c2cb2b85 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -591,6 +591,9 @@ importers: inversify-react: specifier: ^1.1.1 version: 1.1.1(inversify@6.0.2)(react@18.3.1) + isomorphic-dompurify: + specifier: ^2.14.0 + version: 2.14.0(bufferutil@4.0.8)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10) json-source-map: specifier: ^0.6.1 version: 0.6.1 @@ -953,6 +956,9 @@ importers: react-error-boundary: specifier: ^4.0.13 version: 4.0.13(react@18.3.1) + sval: + specifier: ^0.6.1 + version: 0.6.1 tailwind-merge: specifier: ^2.5.2 version: 2.5.2 @@ -3029,79 +3035,67 @@ packages: resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-arm@1.0.5': resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-s390x@1.0.4': resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-x64@1.0.4': resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.0.4': resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.0.4': resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-linux-arm64@0.33.5': resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm@0.33.5': resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-linux-s390x@0.33.5': resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-linux-x64@0.33.5': resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-linuxmusl-arm64@0.33.5': resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-linuxmusl-x64@0.33.5': resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-wasm32@0.33.5': resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} @@ -3415,56 +3409,48 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@next/swc-linux-arm64-gnu@14.2.5': resolution: {integrity: sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@next/swc-linux-arm64-musl@14.2.12': resolution: {integrity: sha512-EfD9L7o9biaQxjwP1uWXnk3vYZi64NVcKUN83hpVkKocB7ogJfyH2r7o1pPnMtir6gHZiGCeHKagJ0yrNSLNHw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@next/swc-linux-arm64-musl@14.2.5': resolution: {integrity: sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@next/swc-linux-x64-gnu@14.2.12': resolution: {integrity: sha512-iQ+n2pxklJew9IpE47hE/VgjmljlHqtcD5UhZVeHICTPbLyrgPehaKf2wLRNjYH75udroBNCgrSSVSVpAbNoYw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@next/swc-linux-x64-gnu@14.2.5': resolution: {integrity: sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@next/swc-linux-x64-musl@14.2.12': resolution: {integrity: sha512-rFkUkNwcQ0ODn7cxvcVdpHlcOpYxMeyMfkJuzaT74xjAa5v4fxP4xDk5OoYmPi8QNLDs3UgZPMSBmpBuv9zKWA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@next/swc-linux-x64-musl@14.2.5': resolution: {integrity: sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@next/swc-win32-arm64-msvc@14.2.12': resolution: {integrity: sha512-PQFYUvwtHs/u0K85SG4sAdDXYIPXpETf9mcEjWc0R4JmjgMKSDwIU/qfZdavtP6MPNiMjuKGXHCtyhR/M5zo8g==} @@ -4760,28 +4746,24 @@ packages: engines: {node: '>=10'} cpu: [arm64] os: [linux] - libc: [glibc] '@swc/core-linux-arm64-musl@1.7.12': resolution: {integrity: sha512-WKtanqasnJ9cBD1tMsmOzZzxJ0Hg2sfJC7UNs2Z4meNPBK4xwOrhpSq8Q9GE4xgoLeSEhU3MmQnbfJKRq3mAZQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - libc: [musl] '@swc/core-linux-x64-gnu@1.7.12': resolution: {integrity: sha512-NQ0bb9eCIp2z2WdRyELzfWc1LDJJ99OYdxT+CIwW9ixPVgAerOv0Oc+BkdijLw5VeYMGlK6JEI4HdLvQE34f1g==} engines: {node: '>=10'} cpu: [x64] os: [linux] - libc: [glibc] '@swc/core-linux-x64-musl@1.7.12': resolution: {integrity: sha512-D8Tegag3/045wvGiq3NFNbKVDnkocNcl5hdKQtEvZ3b1u3nHGu+xqmPteUh4Ps+GB/gbpB3o/eYNO5JPm0R66Q==} engines: {node: '>=10'} cpu: [x64] os: [linux] - libc: [musl] '@swc/core-win32-arm64-msvc@1.7.12': resolution: {integrity: sha512-x8DWG4fCkwI6CmC8U1YMxVTab9Fe4DmCCX6dLrTqqpFPXlVwgdKA9PNBSXsUUtHjvqAB/9cGgmpmNHuNJRa1dA==} @@ -11484,6 +11466,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + sval@0.6.1: + resolution: {integrity: sha512-e4rndan/xKWMrzrqfZpBxt4IVEJofkaSQ+DFnCvcxGKEWumKHtKQDwcNQUUNt2grMAnksluv0tKnPyoE2SWxYw==} + swap-case@1.1.2: resolution: {integrity: sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==} @@ -17863,27 +17848,28 @@ snapshots: acorn-walk: 7.2.0 optional: true - acorn-import-attributes@1.9.5(acorn@8.12.1): + acorn-import-attributes@1.9.5(acorn@8.14.0): dependencies: - acorn: 8.12.1 + acorn: 8.14.0 acorn-jsx@5.3.2(acorn@7.4.1): dependencies: acorn: 7.4.1 - acorn-jsx@5.3.2(acorn@8.12.1): + acorn-jsx@5.3.2(acorn@8.14.0): dependencies: - acorn: 8.12.1 + acorn: 8.14.0 acorn-walk@7.2.0: {} acorn-walk@8.3.3: dependencies: - acorn: 8.12.1 + acorn: 8.14.0 acorn@7.4.1: {} - acorn@8.12.1: {} + acorn@8.12.1: + optional: true acorn@8.14.0: {} @@ -20181,8 +20167,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -25702,6 +25688,10 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + sval@0.6.1: + dependencies: + acorn: 8.14.0 + swap-case@1.1.2: dependencies: lower-case: 1.1.4 @@ -25831,7 +25821,7 @@ snapshots: terser@5.31.6: dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.12.1 + acorn: 8.14.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -26010,7 +26000,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 20.16.1 - acorn: 8.12.1 + acorn: 8.14.0 acorn-walk: 8.3.3 arg: 4.1.3 create-require: 1.1.1 @@ -26030,7 +26020,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 16.18.105 - acorn: 8.12.1 + acorn: 8.14.0 acorn-walk: 8.3.3 arg: 4.1.3 create-require: 1.1.1 @@ -26050,7 +26040,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 20.5.1 - acorn: 8.12.1 + acorn: 8.14.0 acorn-walk: 8.3.3 arg: 4.1.3 create-require: 1.1.1 @@ -26070,7 +26060,7 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 22.4.1 - acorn: 8.12.1 + acorn: 8.14.0 acorn-walk: 8.3.3 arg: 4.1.3 create-require: 1.1.1 @@ -26325,7 +26315,7 @@ snapshots: unplugin@1.14.1(webpack-sources@3.2.3): dependencies: - acorn: 8.12.1 + acorn: 8.14.0 webpack-virtual-modules: 0.6.2 optionalDependencies: webpack-sources: 3.2.3 @@ -26621,8 +26611,8 @@ snapshots: '@webassemblyjs/ast': 1.12.1 '@webassemblyjs/wasm-edit': 1.12.1 '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.12.1 - acorn-import-attributes: 1.9.5(acorn@8.12.1) + acorn: 8.14.0 + acorn-import-attributes: 1.9.5(acorn@8.14.0) browserslist: 4.23.3 chrome-trace-event: 1.0.4 enhanced-resolve: 5.17.1 @@ -26652,8 +26642,8 @@ snapshots: '@webassemblyjs/ast': 1.12.1 '@webassemblyjs/wasm-edit': 1.12.1 '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.12.1 - acorn-import-attributes: 1.9.5(acorn@8.12.1) + acorn: 8.14.0 + acorn-import-attributes: 1.9.5(acorn@8.14.0) browserslist: 4.23.3 chrome-trace-event: 1.0.4 enhanced-resolve: 5.17.1