Visitar URL original
[bugfix] Fix locale('__proto__') corrupting the global locale (#6442) · moment/moment@0d10504 · GitHub
Skip to content

Commit 0d10504

Browse files
[bugfix] Fix locale('__proto__') corrupting the global locale (#6442)
* Fix locale('__proto__') corrupting the global locale The internal locale registry is a plain object, so loadLocale() checked whether a locale was already loaded with locales[name] !== undefined. For names like __proto__, constructor, or prototype, that lookup walks the prototype chain and returns an inherited value instead of a real locale entry, which then gets stored as the global locale and breaks every later formatting call until a valid locale is set again. Switch both lookups in loadLocale to hasOwnProp so an unrecognized name is always treated as not found, the same way it would be for any other unknown locale string. * [bugfix] Guard locale fallback lookup --------- Co-authored-by: Matt Johnson-Pint <mattjohnsonpint@gmail.com>
1 parent 99842f1 commit 0d10504

2 files changed

Lines changed: 75 additions & 3 deletions

File tree

‎src/lib/locale/locales.js‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import isArray from '../utils/is-array';
22
import isUndefined from '../utils/is-undefined';
3+
import hasOwnProp from '../utils/has-own-prop';
34
import { deprecateSimple } from '../utils/deprecate';
45
import { mergeConfigs } from './set';
56
import { Locale } from './constructor';
@@ -73,12 +74,15 @@ function loadLocale(name) {
7374
normalizedName;
7475

7576
// Preserve exact custom locale names before trying the canonical built-in name.
76-
if (locales[name] !== undefined) {
77+
// Use hasOwnProp rather than a plain lookup so that names like "__proto__",
78+
// "constructor", or "prototype" can't resolve to an inherited Object.prototype
79+
// property instead of a real (or missing) locale entry.
80+
if (hasOwnProp(locales, name)) {
7781
return locales[name];
7882
}
7983

8084
normalizedName = normalizeLocale(name);
81-
if (locales[normalizedName] !== undefined) {
85+
if (hasOwnProp(locales, normalizedName)) {
8286
return locales[normalizedName];
8387
}
8488

@@ -100,7 +104,9 @@ function loadLocale(name) {
100104
locales[normalizedName] = null; // null means not found
101105
}
102106
}
103-
return locales[normalizedName];
107+
if (hasOwnProp(locales, normalizedName)) {
108+
return locales[normalizedName];
109+
}
104110
}
105111

106112
// This function will load locale and then set the global locale. If

‎src/test/moment/locale.js‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,3 +1070,69 @@ test('when in strict mode with inexact parsing, treat periods in min-weekdays li
10701070
// assert.equal(moment('EE', 'MMMM').month(), 1, 'non-strict parse short month 1 with MMMM');
10711071
// assert.equal(moment('EEE', 'MMMM').month(), 2, 'non-strict parse short month 2 with MMMM');
10721072
// });
1073+
1074+
test('locale(name) with an Object.prototype property name does not corrupt the global locale', function (assert) {
1075+
var originalLocale = moment.locale();
1076+
1077+
each(
1078+
[
1079+
'__proto__',
1080+
'constructor',
1081+
'hasOwnProperty',
1082+
'isPrototypeOf',
1083+
'propertyIsEnumerable',
1084+
'prototype',
1085+
'toLocaleString',
1086+
'toString',
1087+
'valueOf',
1088+
],
1089+
function (name) {
1090+
moment.locale('en');
1091+
moment.locale(name);
1092+
assert.equal(
1093+
moment.locale(),
1094+
'en',
1095+
'locale(' + name + ') should not change the current locale'
1096+
);
1097+
assert.equal(
1098+
typeof moment().format,
1099+
'function',
1100+
'formatting should still work after locale(' + name + ')'
1101+
);
1102+
assert.equal(
1103+
moment().format('LLLL'),
1104+
moment().locale('en').format('LLLL'),
1105+
'the global locale data should still behave like a real Locale after locale(' +
1106+
name +
1107+
')'
1108+
);
1109+
}
1110+
);
1111+
1112+
moment.locale(originalLocale);
1113+
});
1114+
1115+
test('locale(name) ignores inherited entries when dynamic loading is skipped', function (assert) {
1116+
var inheritedName = 'invalid.locale',
1117+
originalLocale = moment.locale();
1118+
1119+
Object.prototype[inheritedName] = { _abbr: inheritedName };
1120+
1121+
try {
1122+
moment.locale('en');
1123+
moment.locale(inheritedName);
1124+
assert.equal(
1125+
moment.locale(),
1126+
'en',
1127+
'an inherited entry should not change the current locale'
1128+
);
1129+
assert.equal(
1130+
moment().format('LLLL'),
1131+
moment().locale('en').format('LLLL'),
1132+
'formatting should keep using the current locale'
1133+
);
1134+
} finally {
1135+
delete Object.prototype[inheritedName];
1136+
moment.locale(originalLocale);
1137+
}
1138+
});

0 commit comments

Comments
 (0)