VYPR
High severity7.2NVD Advisory· Published Aug 16, 2024· Updated Apr 15, 2026

CVE-2024-43370

CVE-2024-43370

Description

gettext.js is a GNU gettext port for node and the browser. There is a cross-site scripting (XSS) injection if .po dictionary definition files are corrupted. This vulnerability has been patched in version 2.0.3. As a workaround, control the origin of the definition catalog to prevent the use of this flaw in the definition of plural forms.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
gettext.jsnpm
< 2.0.32.0.3

Patches

2
8150aeba8331

Merge pull request #81 from guillaumepotier/fix-cve

https://github.com/guillaumepotier/gettext.jsGuillaume PotierAug 14, 2024via ghsa
10 files changed · +45 9
  • dist/gettext.amd.js+5 1 modified
    @@ -94,9 +94,13 @@ define(function () { 'use strict';
             // plural forms list available here http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
             var pf_re = new RegExp('^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_\(\)])+');
     
    -        if (!pf_re.test(plural_form))
    +        var match = plural_form.match(pf_re);
    +
    +        if (!match || match[0] !== plural_form)
               throw new Error(strfmt('The plural form "%1" is not valid', plural_form));
     
    +        console.log('>>> Plural form:', plural_form);
    +
             // Careful here, this is a hidden eval() equivalent..
             // Risk should be reasonable though since we test the plural_form through regex before
             // taken from https://github.com/Orange-OpenSource/gettext.js/blob/master/lib.gettext.js
    
  • dist/gettext.amd.min.js+1 1 modified
    @@ -1,2 +1,2 @@
     define((function(){"use strict";
    -/*! gettext.js - Guillaume Potier - MIT Licensed */return function(t){t=t||{},this&&(this.__version="2.0.0");var r={domain:"messages",locale:"undefined"!=typeof document&&document.documentElement.getAttribute("lang")||"en",plural_func:function(t){return{nplurals:2,plural:1!=t?1:0}},ctxt_delimiter:String.fromCharCode(4)},e=function(t){var r=typeof t;return"function"===r||"object"===r&&!!t},n={},l=t.locale||r.locale,a=t.domain||r.domain,o={},i={},u=t.ctxt_delimiter||r.ctxt_delimiter;t.messages&&(o[a]={},o[a][l]=t.messages),t.plural_forms&&(i[l]=t.plural_forms);var s=function(t){var r=arguments;return t.replace(/%%/g,"%% ").replace(/%(\d+)/g,(function(t,e){return r[e]})).replace(/%% /g,"%")},p=function(t){return-1!==t.indexOf(u)?t.split(u)[1]:t},c=function(t){for(var r=[t],e=t.lastIndexOf("-");e>0;)t=t.slice(0,e),r.push(t),e=t.lastIndexOf("-");return r},f=function(t){var r=(t=t.replace("_","-")).search(/[.@]/);return-1!=r&&(t=t.slice(0,r)),t},d=function(t){if(!new RegExp("^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_()])+").test(t))throw new Error(s('The plural form "%1" is not valid',t));return new Function("n","var plural, nplurals; "+t+" return { nplurals: nplurals, plural: (plural === true ? 1 : (plural ? plural : 0)) };")},g=function(t,r,e){return e.plural_form?(e.plural_func?a=e.plural_func(r):(n[l]||(n[l]=d(i[l])),a=n[l](r)),(void 0===a.plural||a.plural>a.nplurals||t.length<=a.plural)&&(a.plural=0),s.apply(this,[p(t[a.plural])].concat(Array.prototype.slice.call(arguments,3)))):s.apply(this,[p(t[0])].concat(Array.prototype.slice.call(arguments,3)));var a};return{strfmt:s,expand_locale:c,__:function(){return this.gettext.apply(this,arguments)},_n:function(){return this.ngettext.apply(this,arguments)},_p:function(){return this.pgettext.apply(this,arguments)},setMessages:function(t,r,n,l){if(!t||!r||!n)throw new Error("You must provide a domain, a locale and messages");if("string"!=typeof t||"string"!=typeof r||!e(n))throw new Error("Invalid arguments");return r=f(r),l&&(i[r]=l),o[t]||(o[t]={}),o[t][r]=n,this},loadJSON:function(t,n){if(e(t)||(t=JSON.parse(t)),!t[""]||!t[""].language||!t[""]["plural-forms"])throw new Error('Wrong JSON, it must have an empty key ("") with "language" and "plural-forms" information');var l=t[""];return delete t[""],this.setMessages(n||r.domain,l.language,t,l["plural-forms"])},setLocale:function(t){return l=f(t),this},getLocale:function(){return l},textdomain:function(t){return t?(a=t,this):a},gettext:function(t){return this.dcnpgettext.apply(this,[void 0,void 0,t,void 0,void 0].concat(Array.prototype.slice.call(arguments,1)))},ngettext:function(t,r,e){return this.dcnpgettext.apply(this,[void 0,void 0,t,r,e].concat(Array.prototype.slice.call(arguments,3)))},pgettext:function(t,r){return this.dcnpgettext.apply(this,[void 0,t,r,void 0,void 0].concat(Array.prototype.slice.call(arguments,2)))},dcnpgettext:function(t,e,n,i,s){if(t=t||a,"string"!=typeof n)throw new Error(this.strfmt('Msgid "%1" is not a valid translatable string',n));var p,f,d,h={plural_form:!1},m=e?e+u+n:n,y=c(l);for(var v in y)if(d=y[v],f=o[t]&&o[t][d]&&o[t][d][m],f=i?f&&"string"!=typeof o[t][d][m]:f&&"string"==typeof o[t][d][m])break;return f?p=o[t][d][m]:(p=n,h.plural_func=r.plural_func),i?(h.plural_form=!0,g.apply(this,[f?p:[n,i],s,h].concat(Array.prototype.slice.call(arguments,5)))):g.apply(this,[[p],s,h].concat(Array.prototype.slice.call(arguments,5)))}}}}));
    \ No newline at end of file
    +/*! gettext.js - Guillaume Potier - MIT Licensed */return function(t){t=t||{},this&&(this.__version="2.0.0");var r={domain:"messages",locale:"undefined"!=typeof document&&document.documentElement.getAttribute("lang")||"en",plural_func:function(t){return{nplurals:2,plural:1!=t?1:0}},ctxt_delimiter:String.fromCharCode(4)},e=function(t){var r=typeof t;return"function"===r||"object"===r&&!!t},n={},l=t.locale||r.locale,a=t.domain||r.domain,o={},i={},u=t.ctxt_delimiter||r.ctxt_delimiter;t.messages&&(o[a]={},o[a][l]=t.messages),t.plural_forms&&(i[l]=t.plural_forms);var s=function(t){var r=arguments;return t.replace(/%%/g,"%% ").replace(/%(\d+)/g,(function(t,e){return r[e]})).replace(/%% /g,"%")},p=function(t){return-1!==t.indexOf(u)?t.split(u)[1]:t},c=function(t){for(var r=[t],e=t.lastIndexOf("-");e>0;)t=t.slice(0,e),r.push(t),e=t.lastIndexOf("-");return r},f=function(t){var r=(t=t.replace("_","-")).search(/[.@]/);return-1!=r&&(t=t.slice(0,r)),t},d=function(t){var r=new RegExp("^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_()])+"),e=t.match(r);if(!e||e[0]!==t)throw new Error(s('The plural form "%1" is not valid',t));return console.log(">>> Plural form:",t),new Function("n","var plural, nplurals; "+t+" return { nplurals: nplurals, plural: (plural === true ? 1 : (plural ? plural : 0)) };")},g=function(t,r,e){return e.plural_form?(e.plural_func?a=e.plural_func(r):(n[l]||(n[l]=d(i[l])),a=n[l](r)),(void 0===a.plural||a.plural>a.nplurals||t.length<=a.plural)&&(a.plural=0),s.apply(this,[p(t[a.plural])].concat(Array.prototype.slice.call(arguments,3)))):s.apply(this,[p(t[0])].concat(Array.prototype.slice.call(arguments,3)));var a};return{strfmt:s,expand_locale:c,__:function(){return this.gettext.apply(this,arguments)},_n:function(){return this.ngettext.apply(this,arguments)},_p:function(){return this.pgettext.apply(this,arguments)},setMessages:function(t,r,n,l){if(!t||!r||!n)throw new Error("You must provide a domain, a locale and messages");if("string"!=typeof t||"string"!=typeof r||!e(n))throw new Error("Invalid arguments");return r=f(r),l&&(i[r]=l),o[t]||(o[t]={}),o[t][r]=n,this},loadJSON:function(t,n){if(e(t)||(t=JSON.parse(t)),!t[""]||!t[""].language||!t[""]["plural-forms"])throw new Error('Wrong JSON, it must have an empty key ("") with "language" and "plural-forms" information');var l=t[""];return delete t[""],this.setMessages(n||r.domain,l.language,t,l["plural-forms"])},setLocale:function(t){return l=f(t),this},getLocale:function(){return l},textdomain:function(t){return t?(a=t,this):a},gettext:function(t){return this.dcnpgettext.apply(this,[void 0,void 0,t,void 0,void 0].concat(Array.prototype.slice.call(arguments,1)))},ngettext:function(t,r,e){return this.dcnpgettext.apply(this,[void 0,void 0,t,r,e].concat(Array.prototype.slice.call(arguments,3)))},pgettext:function(t,r){return this.dcnpgettext.apply(this,[void 0,t,r,void 0,void 0].concat(Array.prototype.slice.call(arguments,2)))},dcnpgettext:function(t,e,n,i,s){if(t=t||a,"string"!=typeof n)throw new Error(this.strfmt('Msgid "%1" is not a valid translatable string',n));var p,f,d,h={plural_form:!1},m=e?e+u+n:n,y=c(l);for(var v in y)if(d=y[v],f=o[t]&&o[t][d]&&o[t][d][m],f=i?f&&"string"!=typeof o[t][d][m]:f&&"string"==typeof o[t][d][m])break;return f?p=o[t][d][m]:(p=n,h.plural_func=r.plural_func),i?(h.plural_form=!0,g.apply(this,[f?p:[n,i],s,h].concat(Array.prototype.slice.call(arguments,5)))):g.apply(this,[[p],s,h].concat(Array.prototype.slice.call(arguments,5)))}}}}));
    \ No newline at end of file
    
  • dist/gettext.cjs.js+5 1 modified
    @@ -94,9 +94,13 @@ var i18n = function (options) {
          // plural forms list available here http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
          var pf_re = new RegExp('^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_\(\)])+');
     
    -     if (!pf_re.test(plural_form))
    +     var match = plural_form.match(pf_re);
    +
    +     if (!match || match[0] !== plural_form)
            throw new Error(strfmt('The plural form "%1" is not valid', plural_form));
     
    +     console.log('>>> Plural form:', plural_form);
    +
          // Careful here, this is a hidden eval() equivalent..
          // Risk should be reasonable though since we test the plural_form through regex before
          // taken from https://github.com/Orange-OpenSource/gettext.js/blob/master/lib.gettext.js
    
  • dist/gettext.cjs.min.js+1 1 modified
    @@ -1,2 +1,2 @@
     "use strict";
    -/*! gettext.js - Guillaume Potier - MIT Licensed */var i18n=function(t){t=t||{},this&&(this.__version="2.0.0");var r={domain:"messages",locale:"undefined"!=typeof document&&document.documentElement.getAttribute("lang")||"en",plural_func:function(t){return{nplurals:2,plural:1!=t?1:0}},ctxt_delimiter:String.fromCharCode(4)},e=function(t){var r=typeof t;return"function"===r||"object"===r&&!!t},n={},l=t.locale||r.locale,a=t.domain||r.domain,o={},i={},u=t.ctxt_delimiter||r.ctxt_delimiter;t.messages&&(o[a]={},o[a][l]=t.messages),t.plural_forms&&(i[l]=t.plural_forms);var s=function(t){var r=arguments;return t.replace(/%%/g,"%% ").replace(/%(\d+)/g,(function(t,e){return r[e]})).replace(/%% /g,"%")},p=function(t){return-1!==t.indexOf(u)?t.split(u)[1]:t},c=function(t){for(var r=[t],e=t.lastIndexOf("-");e>0;)t=t.slice(0,e),r.push(t),e=t.lastIndexOf("-");return r},f=function(t){var r=(t=t.replace("_","-")).search(/[.@]/);return-1!=r&&(t=t.slice(0,r)),t},d=function(t){if(!new RegExp("^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_()])+").test(t))throw new Error(s('The plural form "%1" is not valid',t));return new Function("n","var plural, nplurals; "+t+" return { nplurals: nplurals, plural: (plural === true ? 1 : (plural ? plural : 0)) };")},g=function(t,r,e){return e.plural_form?(e.plural_func?a=e.plural_func(r):(n[l]||(n[l]=d(i[l])),a=n[l](r)),(void 0===a.plural||a.plural>a.nplurals||t.length<=a.plural)&&(a.plural=0),s.apply(this,[p(t[a.plural])].concat(Array.prototype.slice.call(arguments,3)))):s.apply(this,[p(t[0])].concat(Array.prototype.slice.call(arguments,3)));var a};return{strfmt:s,expand_locale:c,__:function(){return this.gettext.apply(this,arguments)},_n:function(){return this.ngettext.apply(this,arguments)},_p:function(){return this.pgettext.apply(this,arguments)},setMessages:function(t,r,n,l){if(!t||!r||!n)throw new Error("You must provide a domain, a locale and messages");if("string"!=typeof t||"string"!=typeof r||!e(n))throw new Error("Invalid arguments");return r=f(r),l&&(i[r]=l),o[t]||(o[t]={}),o[t][r]=n,this},loadJSON:function(t,n){if(e(t)||(t=JSON.parse(t)),!t[""]||!t[""].language||!t[""]["plural-forms"])throw new Error('Wrong JSON, it must have an empty key ("") with "language" and "plural-forms" information');var l=t[""];return delete t[""],this.setMessages(n||r.domain,l.language,t,l["plural-forms"])},setLocale:function(t){return l=f(t),this},getLocale:function(){return l},textdomain:function(t){return t?(a=t,this):a},gettext:function(t){return this.dcnpgettext.apply(this,[void 0,void 0,t,void 0,void 0].concat(Array.prototype.slice.call(arguments,1)))},ngettext:function(t,r,e){return this.dcnpgettext.apply(this,[void 0,void 0,t,r,e].concat(Array.prototype.slice.call(arguments,3)))},pgettext:function(t,r){return this.dcnpgettext.apply(this,[void 0,t,r,void 0,void 0].concat(Array.prototype.slice.call(arguments,2)))},dcnpgettext:function(t,e,n,i,s){if(t=t||a,"string"!=typeof n)throw new Error(this.strfmt('Msgid "%1" is not a valid translatable string',n));var p,f,d,h={plural_form:!1},m=e?e+u+n:n,y=c(l);for(var v in y)if(d=y[v],f=o[t]&&o[t][d]&&o[t][d][m],f=i?f&&"string"!=typeof o[t][d][m]:f&&"string"==typeof o[t][d][m])break;return f?p=o[t][d][m]:(p=n,h.plural_func=r.plural_func),i?(h.plural_form=!0,g.apply(this,[f?p:[n,i],s,h].concat(Array.prototype.slice.call(arguments,5)))):g.apply(this,[[p],s,h].concat(Array.prototype.slice.call(arguments,5)))}}};module.exports=i18n;
    \ No newline at end of file
    +/*! gettext.js - Guillaume Potier - MIT Licensed */var i18n=function(t){t=t||{},this&&(this.__version="2.0.0");var r={domain:"messages",locale:"undefined"!=typeof document&&document.documentElement.getAttribute("lang")||"en",plural_func:function(t){return{nplurals:2,plural:1!=t?1:0}},ctxt_delimiter:String.fromCharCode(4)},e=function(t){var r=typeof t;return"function"===r||"object"===r&&!!t},n={},l=t.locale||r.locale,a=t.domain||r.domain,o={},i={},u=t.ctxt_delimiter||r.ctxt_delimiter;t.messages&&(o[a]={},o[a][l]=t.messages),t.plural_forms&&(i[l]=t.plural_forms);var s=function(t){var r=arguments;return t.replace(/%%/g,"%% ").replace(/%(\d+)/g,(function(t,e){return r[e]})).replace(/%% /g,"%")},p=function(t){return-1!==t.indexOf(u)?t.split(u)[1]:t},c=function(t){for(var r=[t],e=t.lastIndexOf("-");e>0;)t=t.slice(0,e),r.push(t),e=t.lastIndexOf("-");return r},f=function(t){var r=(t=t.replace("_","-")).search(/[.@]/);return-1!=r&&(t=t.slice(0,r)),t},d=function(t){var r=new RegExp("^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_()])+"),e=t.match(r);if(!e||e[0]!==t)throw new Error(s('The plural form "%1" is not valid',t));return console.log(">>> Plural form:",t),new Function("n","var plural, nplurals; "+t+" return { nplurals: nplurals, plural: (plural === true ? 1 : (plural ? plural : 0)) };")},g=function(t,r,e){return e.plural_form?(e.plural_func?a=e.plural_func(r):(n[l]||(n[l]=d(i[l])),a=n[l](r)),(void 0===a.plural||a.plural>a.nplurals||t.length<=a.plural)&&(a.plural=0),s.apply(this,[p(t[a.plural])].concat(Array.prototype.slice.call(arguments,3)))):s.apply(this,[p(t[0])].concat(Array.prototype.slice.call(arguments,3)));var a};return{strfmt:s,expand_locale:c,__:function(){return this.gettext.apply(this,arguments)},_n:function(){return this.ngettext.apply(this,arguments)},_p:function(){return this.pgettext.apply(this,arguments)},setMessages:function(t,r,n,l){if(!t||!r||!n)throw new Error("You must provide a domain, a locale and messages");if("string"!=typeof t||"string"!=typeof r||!e(n))throw new Error("Invalid arguments");return r=f(r),l&&(i[r]=l),o[t]||(o[t]={}),o[t][r]=n,this},loadJSON:function(t,n){if(e(t)||(t=JSON.parse(t)),!t[""]||!t[""].language||!t[""]["plural-forms"])throw new Error('Wrong JSON, it must have an empty key ("") with "language" and "plural-forms" information');var l=t[""];return delete t[""],this.setMessages(n||r.domain,l.language,t,l["plural-forms"])},setLocale:function(t){return l=f(t),this},getLocale:function(){return l},textdomain:function(t){return t?(a=t,this):a},gettext:function(t){return this.dcnpgettext.apply(this,[void 0,void 0,t,void 0,void 0].concat(Array.prototype.slice.call(arguments,1)))},ngettext:function(t,r,e){return this.dcnpgettext.apply(this,[void 0,void 0,t,r,e].concat(Array.prototype.slice.call(arguments,3)))},pgettext:function(t,r){return this.dcnpgettext.apply(this,[void 0,t,r,void 0,void 0].concat(Array.prototype.slice.call(arguments,2)))},dcnpgettext:function(t,e,n,i,s){if(t=t||a,"string"!=typeof n)throw new Error(this.strfmt('Msgid "%1" is not a valid translatable string',n));var p,f,d,m={plural_form:!1},h=e?e+u+n:n,y=c(l);for(var v in y)if(d=y[v],f=o[t]&&o[t][d]&&o[t][d][h],f=i?f&&"string"!=typeof o[t][d][h]:f&&"string"==typeof o[t][d][h])break;return f?p=o[t][d][h]:(p=n,m.plural_func=r.plural_func),i?(m.plural_form=!0,g.apply(this,[f?p:[n,i],s,m].concat(Array.prototype.slice.call(arguments,5)))):g.apply(this,[[p],s,m].concat(Array.prototype.slice.call(arguments,5)))}}};module.exports=i18n;
    \ No newline at end of file
    
  • dist/gettext.esm.js+5 1 modified
    @@ -92,9 +92,13 @@ var i18n = function (options) {
          // plural forms list available here http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
          var pf_re = new RegExp('^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_\(\)])+');
     
    -     if (!pf_re.test(plural_form))
    +     var match = plural_form.match(pf_re);
    +
    +     if (!match || match[0] !== plural_form)
            throw new Error(strfmt('The plural form "%1" is not valid', plural_form));
     
    +     console.log('>>> Plural form:', plural_form);
    +
          // Careful here, this is a hidden eval() equivalent..
          // Risk should be reasonable though since we test the plural_form through regex before
          // taken from https://github.com/Orange-OpenSource/gettext.js/blob/master/lib.gettext.js
    
  • dist/gettext.esm.min.js+1 1 modified
    @@ -1,2 +1,2 @@
     /*! gettext.js - Guillaume Potier - MIT Licensed */
    -var i18n=function(t){t=t||{},this&&(this.__version="2.0.0");var r={domain:"messages",locale:"undefined"!=typeof document&&document.documentElement.getAttribute("lang")||"en",plural_func:function(t){return{nplurals:2,plural:1!=t?1:0}},ctxt_delimiter:String.fromCharCode(4)},e=function(t){var r=typeof t;return"function"===r||"object"===r&&!!t},n={},l=t.locale||r.locale,a=t.domain||r.domain,o={},i={},u=t.ctxt_delimiter||r.ctxt_delimiter;t.messages&&(o[a]={},o[a][l]=t.messages),t.plural_forms&&(i[l]=t.plural_forms);var s=function(t){var r=arguments;return t.replace(/%%/g,"%% ").replace(/%(\d+)/g,(function(t,e){return r[e]})).replace(/%% /g,"%")},p=function(t){return-1!==t.indexOf(u)?t.split(u)[1]:t},c=function(t){for(var r=[t],e=t.lastIndexOf("-");e>0;)t=t.slice(0,e),r.push(t),e=t.lastIndexOf("-");return r},f=function(t){var r=(t=t.replace("_","-")).search(/[.@]/);return-1!=r&&(t=t.slice(0,r)),t},d=function(t){if(!new RegExp("^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_()])+").test(t))throw new Error(s('The plural form "%1" is not valid',t));return new Function("n","var plural, nplurals; "+t+" return { nplurals: nplurals, plural: (plural === true ? 1 : (plural ? plural : 0)) };")},g=function(t,r,e){return e.plural_form?(e.plural_func?a=e.plural_func(r):(n[l]||(n[l]=d(i[l])),a=n[l](r)),(void 0===a.plural||a.plural>a.nplurals||t.length<=a.plural)&&(a.plural=0),s.apply(this,[p(t[a.plural])].concat(Array.prototype.slice.call(arguments,3)))):s.apply(this,[p(t[0])].concat(Array.prototype.slice.call(arguments,3)));var a};return{strfmt:s,expand_locale:c,__:function(){return this.gettext.apply(this,arguments)},_n:function(){return this.ngettext.apply(this,arguments)},_p:function(){return this.pgettext.apply(this,arguments)},setMessages:function(t,r,n,l){if(!t||!r||!n)throw new Error("You must provide a domain, a locale and messages");if("string"!=typeof t||"string"!=typeof r||!e(n))throw new Error("Invalid arguments");return r=f(r),l&&(i[r]=l),o[t]||(o[t]={}),o[t][r]=n,this},loadJSON:function(t,n){if(e(t)||(t=JSON.parse(t)),!t[""]||!t[""].language||!t[""]["plural-forms"])throw new Error('Wrong JSON, it must have an empty key ("") with "language" and "plural-forms" information');var l=t[""];return delete t[""],this.setMessages(n||r.domain,l.language,t,l["plural-forms"])},setLocale:function(t){return l=f(t),this},getLocale:function(){return l},textdomain:function(t){return t?(a=t,this):a},gettext:function(t){return this.dcnpgettext.apply(this,[void 0,void 0,t,void 0,void 0].concat(Array.prototype.slice.call(arguments,1)))},ngettext:function(t,r,e){return this.dcnpgettext.apply(this,[void 0,void 0,t,r,e].concat(Array.prototype.slice.call(arguments,3)))},pgettext:function(t,r){return this.dcnpgettext.apply(this,[void 0,t,r,void 0,void 0].concat(Array.prototype.slice.call(arguments,2)))},dcnpgettext:function(t,e,n,i,s){if(t=t||a,"string"!=typeof n)throw new Error(this.strfmt('Msgid "%1" is not a valid translatable string',n));var p,f,d,h={plural_form:!1},m=e?e+u+n:n,y=c(l);for(var v in y)if(d=y[v],f=o[t]&&o[t][d]&&o[t][d][m],f=i?f&&"string"!=typeof o[t][d][m]:f&&"string"==typeof o[t][d][m])break;return f?p=o[t][d][m]:(p=n,h.plural_func=r.plural_func),i?(h.plural_form=!0,g.apply(this,[f?p:[n,i],s,h].concat(Array.prototype.slice.call(arguments,5)))):g.apply(this,[[p],s,h].concat(Array.prototype.slice.call(arguments,5)))}}};export default i18n;
    \ No newline at end of file
    +var i18n=function(t){t=t||{},this&&(this.__version="2.0.0");var r={domain:"messages",locale:"undefined"!=typeof document&&document.documentElement.getAttribute("lang")||"en",plural_func:function(t){return{nplurals:2,plural:1!=t?1:0}},ctxt_delimiter:String.fromCharCode(4)},e=function(t){var r=typeof t;return"function"===r||"object"===r&&!!t},n={},l=t.locale||r.locale,a=t.domain||r.domain,o={},i={},u=t.ctxt_delimiter||r.ctxt_delimiter;t.messages&&(o[a]={},o[a][l]=t.messages),t.plural_forms&&(i[l]=t.plural_forms);var s=function(t){var r=arguments;return t.replace(/%%/g,"%% ").replace(/%(\d+)/g,(function(t,e){return r[e]})).replace(/%% /g,"%")},p=function(t){return-1!==t.indexOf(u)?t.split(u)[1]:t},c=function(t){for(var r=[t],e=t.lastIndexOf("-");e>0;)t=t.slice(0,e),r.push(t),e=t.lastIndexOf("-");return r},f=function(t){var r=(t=t.replace("_","-")).search(/[.@]/);return-1!=r&&(t=t.slice(0,r)),t},d=function(t){var r=new RegExp("^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_()])+"),e=t.match(r);if(!e||e[0]!==t)throw new Error(s('The plural form "%1" is not valid',t));return console.log(">>> Plural form:",t),new Function("n","var plural, nplurals; "+t+" return { nplurals: nplurals, plural: (plural === true ? 1 : (plural ? plural : 0)) };")},g=function(t,r,e){return e.plural_form?(e.plural_func?a=e.plural_func(r):(n[l]||(n[l]=d(i[l])),a=n[l](r)),(void 0===a.plural||a.plural>a.nplurals||t.length<=a.plural)&&(a.plural=0),s.apply(this,[p(t[a.plural])].concat(Array.prototype.slice.call(arguments,3)))):s.apply(this,[p(t[0])].concat(Array.prototype.slice.call(arguments,3)));var a};return{strfmt:s,expand_locale:c,__:function(){return this.gettext.apply(this,arguments)},_n:function(){return this.ngettext.apply(this,arguments)},_p:function(){return this.pgettext.apply(this,arguments)},setMessages:function(t,r,n,l){if(!t||!r||!n)throw new Error("You must provide a domain, a locale and messages");if("string"!=typeof t||"string"!=typeof r||!e(n))throw new Error("Invalid arguments");return r=f(r),l&&(i[r]=l),o[t]||(o[t]={}),o[t][r]=n,this},loadJSON:function(t,n){if(e(t)||(t=JSON.parse(t)),!t[""]||!t[""].language||!t[""]["plural-forms"])throw new Error('Wrong JSON, it must have an empty key ("") with "language" and "plural-forms" information');var l=t[""];return delete t[""],this.setMessages(n||r.domain,l.language,t,l["plural-forms"])},setLocale:function(t){return l=f(t),this},getLocale:function(){return l},textdomain:function(t){return t?(a=t,this):a},gettext:function(t){return this.dcnpgettext.apply(this,[void 0,void 0,t,void 0,void 0].concat(Array.prototype.slice.call(arguments,1)))},ngettext:function(t,r,e){return this.dcnpgettext.apply(this,[void 0,void 0,t,r,e].concat(Array.prototype.slice.call(arguments,3)))},pgettext:function(t,r){return this.dcnpgettext.apply(this,[void 0,t,r,void 0,void 0].concat(Array.prototype.slice.call(arguments,2)))},dcnpgettext:function(t,e,n,i,s){if(t=t||a,"string"!=typeof n)throw new Error(this.strfmt('Msgid "%1" is not a valid translatable string',n));var p,f,d,h={plural_form:!1},m=e?e+u+n:n,y=c(l);for(var v in y)if(d=y[v],f=o[t]&&o[t][d]&&o[t][d][m],f=i?f&&"string"!=typeof o[t][d][m]:f&&"string"==typeof o[t][d][m])break;return f?p=o[t][d][m]:(p=n,h.plural_func=r.plural_func),i?(h.plural_form=!0,g.apply(this,[f?p:[n,i],s,h].concat(Array.prototype.slice.call(arguments,5)))):g.apply(this,[[p],s,h].concat(Array.prototype.slice.call(arguments,5)))}}};export default i18n;
    \ No newline at end of file
    
  • dist/gettext.iife.js+5 1 modified
    @@ -95,9 +95,13 @@ var i18n = (function () {
             // plural forms list available here http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
             var pf_re = new RegExp('^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_\(\)])+');
     
    -        if (!pf_re.test(plural_form))
    +        var match = plural_form.match(pf_re);
    +
    +        if (!match || match[0] !== plural_form)
               throw new Error(strfmt('The plural form "%1" is not valid', plural_form));
     
    +        console.log('>>> Plural form:', plural_form);
    +
             // Careful here, this is a hidden eval() equivalent..
             // Risk should be reasonable though since we test the plural_form through regex before
             // taken from https://github.com/Orange-OpenSource/gettext.js/blob/master/lib.gettext.js
    
  • dist/gettext.iife.min.js+1 1 modified
    @@ -1,2 +1,2 @@
     var i18n=function(){"use strict";
    -/*! gettext.js - Guillaume Potier - MIT Licensed */return function(t){t=t||{},this&&(this.__version="2.0.0");var r={domain:"messages",locale:"undefined"!=typeof document&&document.documentElement.getAttribute("lang")||"en",plural_func:function(t){return{nplurals:2,plural:1!=t?1:0}},ctxt_delimiter:String.fromCharCode(4)},n=function(t){var r=typeof t;return"function"===r||"object"===r&&!!t},e={},l=t.locale||r.locale,a=t.domain||r.domain,o={},i={},u=t.ctxt_delimiter||r.ctxt_delimiter;t.messages&&(o[a]={},o[a][l]=t.messages),t.plural_forms&&(i[l]=t.plural_forms);var s=function(t){var r=arguments;return t.replace(/%%/g,"%% ").replace(/%(\d+)/g,(function(t,n){return r[n]})).replace(/%% /g,"%")},p=function(t){return-1!==t.indexOf(u)?t.split(u)[1]:t},c=function(t){for(var r=[t],n=t.lastIndexOf("-");n>0;)t=t.slice(0,n),r.push(t),n=t.lastIndexOf("-");return r},f=function(t){var r=(t=t.replace("_","-")).search(/[.@]/);return-1!=r&&(t=t.slice(0,r)),t},d=function(t){if(!new RegExp("^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_()])+").test(t))throw new Error(s('The plural form "%1" is not valid',t));return new Function("n","var plural, nplurals; "+t+" return { nplurals: nplurals, plural: (plural === true ? 1 : (plural ? plural : 0)) };")},g=function(t,r,n){return n.plural_form?(n.plural_func?a=n.plural_func(r):(e[l]||(e[l]=d(i[l])),a=e[l](r)),(void 0===a.plural||a.plural>a.nplurals||t.length<=a.plural)&&(a.plural=0),s.apply(this,[p(t[a.plural])].concat(Array.prototype.slice.call(arguments,3)))):s.apply(this,[p(t[0])].concat(Array.prototype.slice.call(arguments,3)));var a};return{strfmt:s,expand_locale:c,__:function(){return this.gettext.apply(this,arguments)},_n:function(){return this.ngettext.apply(this,arguments)},_p:function(){return this.pgettext.apply(this,arguments)},setMessages:function(t,r,e,l){if(!t||!r||!e)throw new Error("You must provide a domain, a locale and messages");if("string"!=typeof t||"string"!=typeof r||!n(e))throw new Error("Invalid arguments");return r=f(r),l&&(i[r]=l),o[t]||(o[t]={}),o[t][r]=e,this},loadJSON:function(t,e){if(n(t)||(t=JSON.parse(t)),!t[""]||!t[""].language||!t[""]["plural-forms"])throw new Error('Wrong JSON, it must have an empty key ("") with "language" and "plural-forms" information');var l=t[""];return delete t[""],this.setMessages(e||r.domain,l.language,t,l["plural-forms"])},setLocale:function(t){return l=f(t),this},getLocale:function(){return l},textdomain:function(t){return t?(a=t,this):a},gettext:function(t){return this.dcnpgettext.apply(this,[void 0,void 0,t,void 0,void 0].concat(Array.prototype.slice.call(arguments,1)))},ngettext:function(t,r,n){return this.dcnpgettext.apply(this,[void 0,void 0,t,r,n].concat(Array.prototype.slice.call(arguments,3)))},pgettext:function(t,r){return this.dcnpgettext.apply(this,[void 0,t,r,void 0,void 0].concat(Array.prototype.slice.call(arguments,2)))},dcnpgettext:function(t,n,e,i,s){if(t=t||a,"string"!=typeof e)throw new Error(this.strfmt('Msgid "%1" is not a valid translatable string',e));var p,f,d,h={plural_form:!1},m=n?n+u+e:e,y=c(l);for(var v in y)if(d=y[v],f=o[t]&&o[t][d]&&o[t][d][m],f=i?f&&"string"!=typeof o[t][d][m]:f&&"string"==typeof o[t][d][m])break;return f?p=o[t][d][m]:(p=e,h.plural_func=r.plural_func),i?(h.plural_form=!0,g.apply(this,[f?p:[e,i],s,h].concat(Array.prototype.slice.call(arguments,5)))):g.apply(this,[[p],s,h].concat(Array.prototype.slice.call(arguments,5)))}}}}();
    \ No newline at end of file
    +/*! gettext.js - Guillaume Potier - MIT Licensed */return function(t){t=t||{},this&&(this.__version="2.0.0");var r={domain:"messages",locale:"undefined"!=typeof document&&document.documentElement.getAttribute("lang")||"en",plural_func:function(t){return{nplurals:2,plural:1!=t?1:0}},ctxt_delimiter:String.fromCharCode(4)},n=function(t){var r=typeof t;return"function"===r||"object"===r&&!!t},e={},l=t.locale||r.locale,a=t.domain||r.domain,o={},i={},u=t.ctxt_delimiter||r.ctxt_delimiter;t.messages&&(o[a]={},o[a][l]=t.messages),t.plural_forms&&(i[l]=t.plural_forms);var s=function(t){var r=arguments;return t.replace(/%%/g,"%% ").replace(/%(\d+)/g,(function(t,n){return r[n]})).replace(/%% /g,"%")},p=function(t){return-1!==t.indexOf(u)?t.split(u)[1]:t},c=function(t){for(var r=[t],n=t.lastIndexOf("-");n>0;)t=t.slice(0,n),r.push(t),n=t.lastIndexOf("-");return r},f=function(t){var r=(t=t.replace("_","-")).search(/[.@]/);return-1!=r&&(t=t.slice(0,r)),t},d=function(t){var r=new RegExp("^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_()])+"),n=t.match(r);if(!n||n[0]!==t)throw new Error(s('The plural form "%1" is not valid',t));return console.log(">>> Plural form:",t),new Function("n","var plural, nplurals; "+t+" return { nplurals: nplurals, plural: (plural === true ? 1 : (plural ? plural : 0)) };")},g=function(t,r,n){return n.plural_form?(n.plural_func?a=n.plural_func(r):(e[l]||(e[l]=d(i[l])),a=e[l](r)),(void 0===a.plural||a.plural>a.nplurals||t.length<=a.plural)&&(a.plural=0),s.apply(this,[p(t[a.plural])].concat(Array.prototype.slice.call(arguments,3)))):s.apply(this,[p(t[0])].concat(Array.prototype.slice.call(arguments,3)));var a};return{strfmt:s,expand_locale:c,__:function(){return this.gettext.apply(this,arguments)},_n:function(){return this.ngettext.apply(this,arguments)},_p:function(){return this.pgettext.apply(this,arguments)},setMessages:function(t,r,e,l){if(!t||!r||!e)throw new Error("You must provide a domain, a locale and messages");if("string"!=typeof t||"string"!=typeof r||!n(e))throw new Error("Invalid arguments");return r=f(r),l&&(i[r]=l),o[t]||(o[t]={}),o[t][r]=e,this},loadJSON:function(t,e){if(n(t)||(t=JSON.parse(t)),!t[""]||!t[""].language||!t[""]["plural-forms"])throw new Error('Wrong JSON, it must have an empty key ("") with "language" and "plural-forms" information');var l=t[""];return delete t[""],this.setMessages(e||r.domain,l.language,t,l["plural-forms"])},setLocale:function(t){return l=f(t),this},getLocale:function(){return l},textdomain:function(t){return t?(a=t,this):a},gettext:function(t){return this.dcnpgettext.apply(this,[void 0,void 0,t,void 0,void 0].concat(Array.prototype.slice.call(arguments,1)))},ngettext:function(t,r,n){return this.dcnpgettext.apply(this,[void 0,void 0,t,r,n].concat(Array.prototype.slice.call(arguments,3)))},pgettext:function(t,r){return this.dcnpgettext.apply(this,[void 0,t,r,void 0,void 0].concat(Array.prototype.slice.call(arguments,2)))},dcnpgettext:function(t,n,e,i,s){if(t=t||a,"string"!=typeof e)throw new Error(this.strfmt('Msgid "%1" is not a valid translatable string',e));var p,f,d,h={plural_form:!1},m=n?n+u+e:e,y=c(l);for(var v in y)if(d=y[v],f=o[t]&&o[t][d]&&o[t][d][m],f=i?f&&"string"!=typeof o[t][d][m]:f&&"string"==typeof o[t][d][m])break;return f?p=o[t][d][m]:(p=e,h.plural_func=r.plural_func),i?(h.plural_form=!0,g.apply(this,[f?p:[e,i],s,h].concat(Array.prototype.slice.call(arguments,5)))):g.apply(this,[[p],s,h].concat(Array.prototype.slice.call(arguments,5)))}}}}();
    \ No newline at end of file
    
  • lib/gettext.js+3 1 modified
    @@ -92,7 +92,9 @@ var i18n = function (options) {
          // plural forms list available here http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
          var pf_re = new RegExp('^\\s*nplurals\\s*=\\s*[0-9]+\\s*;\\s*plural\\s*=\\s*(?:\\s|[-\\?\\|&=!<>+*/%:;n0-9_\(\)])+');
     
    -     if (!pf_re.test(plural_form))
    +     var match = plural_form.match(pf_re);
    +
    +     if (!match || match[0] !== plural_form)
            throw new Error(strfmt('The plural form "%1" is not valid', plural_form));
     
          // Careful here, this is a hidden eval() equivalent..
    
  • tests/tests.js+18 0 modified
    @@ -206,6 +206,24 @@
                             expect(e.message).to.be('The plural form "nplurals=2; plural=[not valid];" is not valid');
                         }
                     });
    +                it('should fail another unvalid plural form', function () {
    +                    i18n = new window.i18n({ locale: 'foo' });
    +                    i18n.setMessages('messages', 'foo', {
    +                        "There is %1 apple": [
    +                            "Il y a %1 pomme",
    +                            "Il y a %1 pommes"
    +                        ]
    +                    }, 'nplurals=2; plural=n>1; console.log(`PWNED!`);');
    +
    +                    // do not throw error on default plural form if key does not have a translation
    +                    expect(i18n.ngettext('foo', 'bar', 2)).to.be('bar');
    +
    +                    try {
    +                        i18n.ngettext('There is %1 apple', 'There are %1 apples', 42);
    +                    } catch (e) {
    +                        expect(e.message).to.be('The plural form "nplurals=2; plural=n>1; console.log(`PWNED!`);" is not valid');
    +                    }
    +                });
                     it('should handle multiple locale & pluals cohabitation', function () {
                         i18n = new window.i18n({ locale: 'foo' });
                         i18n.setMessages('messages', 'foo', {
    

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

4

News mentions

0

No linked articles in our index yet.