VYPR
Critical severity9.6OSV Advisory· Published Sep 11, 2025· Updated Apr 15, 2026

CVE-2025-59053

CVE-2025-59053

Description

AIRI is a self-hosted, artificial intelligence based Grok Companion. In v0.7.2-beta.2 in the packages/stage-ui/src/components/MarkdownRenderer.vue path, the Markdown content is processed using the useMarkdown composable, and the processed HTML is rendered directly into the DOM using v-html. An attacker creates a card file containing malicious HTML/JavaScript, then simply processes it using the highlightTagToHtml function (which simply replaces template tags without HTML escaping), and then directly renders it using v-html, leading to cross-site scripting (XSS). The project also exposes the Tauri API, which can be called from the frontend. The MCP plugin exposes a command execution interface function in crates/tauri-plugin-mcp/src/lib.rs. This allows arbitrary command execution. connect_server directly passes the user-supplied command and args parameters to Command::new(command).args(args) without any input validation or whitelisting. Thus, the previous XSS exploit could achieve command execution through this interface. v0.7.2-beta.3 fixes the issue.

Affected products

1

Patches

1
3315634903c9

fix: sanitize v-html inputs with DOMPurify (#573)

https://github.com/moeru-ai/airiMakitoSep 11, 2025via osv
9 files changed · +51 8
  • apps/stage-tamagotchi-electron/src/renderer/pages/settings/airi-card/components/CardDetailDialog.vue+3 1 modified
    @@ -1,6 +1,8 @@
     <script setup lang="ts">
     import type { AiriCard } from '@proj-airi/stage-ui/stores/modules/airi-card'
     
    +import DOMPurify from 'dompurify'
    +
     import { Button } from '@proj-airi/stage-ui/components'
     import { useAiriCardStore } from '@proj-airi/stage-ui/stores/modules/airi-card'
     import { storeToRefs } from 'pinia'
    @@ -84,7 +86,7 @@ function handleActivate() {
     }
     
     function highlightTagToHtml(text: string) {
    -  return text?.replace(/\{\{(.*?)\}\}/g, '<span class="bg-primary-500/20 inline-block">{{ $1 }}</span>').trim()
    +  return DOMPurify.sanitize(text?.replace(/\{\{(.*?)\}\}/g, '<span class="bg-primary-500/20 inline-block">{{ $1 }}</span>').trim())
     }
     
     // Delete confirmation
    
  • apps/stage-tamagotchi/package.json+1 0 modified
    @@ -56,6 +56,7 @@
         "colorjs.io": "^0.5.2",
         "culori": "^4.0.2",
         "date-fns": "^4.1.0",
    +    "dompurify": "^3.2.6",
         "drizzle-kit": "^0.31.4",
         "drizzle-orm": "^0.44.5",
         "jszip": "^3.10.1",
    
  • apps/stage-tamagotchi/src/pages/settings/airi-card/components/CardDetailDialog.vue+3 1 modified
    @@ -1,6 +1,8 @@
     <script setup lang="ts">
     import type { AiriCard } from '@proj-airi/stage-ui/stores/modules/airi-card'
     
    +import DOMPurify from 'dompurify'
    +
     import { Button } from '@proj-airi/stage-ui/components'
     import { useAiriCardStore } from '@proj-airi/stage-ui/stores/modules/airi-card'
     import { storeToRefs } from 'pinia'
    @@ -84,7 +86,7 @@ function handleActivate() {
     }
     
     function highlightTagToHtml(text: string) {
    -  return text?.replace(/\{\{(.*?)\}\}/g, '<span class="bg-primary-500/20 inline-block">{{ $1 }}</span>').trim()
    +  return DOMPurify.sanitize(text?.replace(/\{\{(.*?)\}\}/g, '<span class="bg-primary-500/20 inline-block">{{ $1 }}</span>').trim())
     }
     
     // Delete confirmation
    
  • apps/stage-web/package.json+1 0 modified
    @@ -55,6 +55,7 @@
         "colorjs.io": "^0.5.2",
         "culori": "^4.0.2",
         "date-fns": "^4.1.0",
    +    "dompurify": "^3.2.6",
         "driver.js": "^1.3.6",
         "drizzle-kit": "^0.31.4",
         "drizzle-orm": "^0.44.5",
    
  • apps/stage-web/src/pages/settings/airi-card/components/CardDetailDialog.vue+3 1 modified
    @@ -1,6 +1,8 @@
     <script setup lang="ts">
     import type { AiriCard } from '@proj-airi/stage-ui/stores/modules/airi-card'
     
    +import DOMPurify from 'dompurify'
    +
     import { Button } from '@proj-airi/stage-ui/components'
     import { useAiriCardStore } from '@proj-airi/stage-ui/stores/modules/airi-card'
     import { storeToRefs } from 'pinia'
    @@ -84,7 +86,7 @@ function handleActivate() {
     }
     
     function highlightTagToHtml(text: string) {
    -  return text?.replace(/\{\{(.*?)\}\}/g, '<span class="bg-primary-500/20 inline-block">{{ $1 }}</span>').trim()
    +  return DOMPurify.sanitize(text?.replace(/\{\{(.*?)\}\}/g, '<span class="bg-primary-500/20 inline-block">{{ $1 }}</span>').trim())
     }
     
     // Delete confirmation
    
  • cspell.config.yaml+1 0 modified
    @@ -71,6 +71,7 @@ words:
       - devlogs
       - directml
       - dokidoki
    +  - dompurify
       - dotenvx
       - DownloadLive2DSDK
       - dreamlog
    
  • packages/stage-ui/package.json+1 0 modified
    @@ -93,6 +93,7 @@
         "animejs": "^4.1.3",
         "culori": "^4.0.2",
         "date-fns": "^4.1.0",
    +    "dompurify": "^3.2.6",
         "gpuu": "^1.0.4",
         "jszip": "^3.10.1",
         "localforage": "^1.10.0",
    
  • packages/stage-ui/src/components/MarkdownRenderer.vue+4 2 modified
    @@ -1,4 +1,6 @@
     <script setup lang="ts">
    +import DOMPurify from 'dompurify'
    +
     import { onMounted, ref, watch } from 'vue'
     
     import { useMarkdown } from '../composables/markdown'
    @@ -20,11 +22,11 @@ async function processContent() {
       }
     
       try {
    -    processedContent.value = await process(props.content)
    +    processedContent.value = DOMPurify.sanitize(await process(props.content))
       }
       catch (error) {
         console.warn('Failed to process markdown with syntax highlighting, using fallback:', error)
    -    processedContent.value = processSync(props.content)
    +    processedContent.value = DOMPurify.sanitize(processSync(props.content))
       }
     }
     
    
  • pnpm-lock.yaml+34 3 modified
    @@ -284,7 +284,7 @@ importers:
             version: rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1)
           vite-plugin-vue-devtools:
             specifier: ^8.0.1
    -        version: 8.0.1(@nuxt/kit@4.0.3(magicast@0.3.5))(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))
    +        version: 8.0.1(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))
           vite-plugin-vue-layouts:
             specifier: ^0.11.0
             version: 0.11.0(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))(vue-router@4.5.1(vue@3.5.21(typescript@5.9.2)))(vue@3.5.21(typescript@5.9.2))
    @@ -454,6 +454,9 @@ importers:
           date-fns:
             specifier: ^4.1.0
             version: 4.1.0
    +      dompurify:
    +        specifier: ^3.2.6
    +        version: 3.2.6
           drizzle-kit:
             specifier: ^0.31.4
             version: 0.31.4
    @@ -1164,6 +1167,9 @@ importers:
           date-fns:
             specifier: ^4.1.0
             version: 4.1.0
    +      dompurify:
    +        specifier: ^3.2.6
    +        version: 3.2.6
           driver.js:
             specifier: ^1.3.6
             version: 1.3.6
    @@ -1768,6 +1774,9 @@ importers:
           date-fns:
             specifier: ^4.1.0
             version: 4.1.0
    +      dompurify:
    +        specifier: ^3.2.6
    +        version: 3.2.6
           gpuu:
             specifier: ^1.0.4
             version: 1.0.4
    @@ -2023,7 +2032,7 @@ importers:
             version: rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1)
           vite-plugin-vue-devtools:
             specifier: ^8.0.1
    -        version: 8.0.1(@nuxt/kit@4.0.3(magicast@0.3.5))(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))
    +        version: 8.0.1(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))
           vue-router:
             specifier: ^4.5.1
             version: 4.5.1(vue@3.5.21(typescript@5.9.2))
    @@ -2057,7 +2066,7 @@ importers:
             version: rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1)
           vite-plugin-vue-devtools:
             specifier: ^8.0.1
    -        version: 8.0.1(@nuxt/kit@4.0.3(magicast@0.3.5))(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))
    +        version: 8.0.1(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))
           vue-router:
             specifier: ^4.5.1
             version: 4.5.1(vue@3.5.21(typescript@5.9.2))
    @@ -9588,6 +9597,9 @@ packages:
         resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
         engines: {node: '>= 4'}
     
    +  dompurify@3.2.6:
    +    resolution: {integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==}
    +
       domutils@2.8.0:
         resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
     
    @@ -25071,6 +25083,10 @@ snapshots:
         dependencies:
           domelementtype: 2.3.0
     
    +  dompurify@3.2.6:
    +    optionalDependencies:
    +      '@types/trusted-types': 2.0.7
    +
       domutils@2.8.0:
         dependencies:
           dom-serializer: 1.4.1
    @@ -33075,6 +33091,21 @@ snapshots:
           - supports-color
           - vue
     
    +  vite-plugin-vue-devtools@8.0.1(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2)):
    +    dependencies:
    +      '@vue/devtools-core': 8.0.1(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))
    +      '@vue/devtools-kit': 8.0.1
    +      '@vue/devtools-shared': 8.0.1
    +      execa: 9.6.0
    +      sirv: 3.0.1
    +      vite: rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1)
    +      vite-plugin-inspect: 11.3.3(@nuxt/kit@3.18.1(magicast@0.3.5))(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))
    +      vite-plugin-vue-inspector: 5.3.2(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1))
    +    transitivePeerDependencies:
    +      - '@nuxt/kit'
    +      - supports-color
    +      - vue
    +
       vite-plugin-vue-inspector@5.3.2(rolldown-vite@7.1.5(@types/node@24.3.0)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.5)(yaml@2.8.1)):
         dependencies:
           '@babel/core': 7.28.3
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

0

No linked articles in our index yet.