Skip to content

Commit 9faeecc

Browse files
authored
Merge pull request #3139 from hapijs/chore/backport-messages-proto
fix: prevent messages proto injection
2 parents 566e73f + 8d0b808 commit 9faeecc

3 files changed

Lines changed: 56 additions & 6 deletions

File tree

lib/messages.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,24 @@ exports.compile = function (messages, target) {
5151
Assert(typeof message === 'object' && !Array.isArray(message), 'Invalid message for', code);
5252

5353
const language = code;
54-
target[language] = target[language] || {};
54+
55+
// Don't reuse an inherited object, otherwise a language named __proto__ or constructor writes on the prototype
56+
57+
const localizedTarget = Object.prototype.hasOwnProperty.call(target, language) ? target[language] : {};
58+
target[language] = localizedTarget;
5559

5660
for (code in message) {
5761
const localized = message[code];
5862

5963
if (code === 'root' ||
6064
Template.isTemplate(localized)) {
6165

62-
target[language][code] = localized;
66+
localizedTarget[code] = localized;
6367
continue;
6468
}
6569

6670
Assert(typeof localized === 'string', 'Invalid message for', code, 'in', language);
67-
target[language][code] = new Template(localized);
71+
localizedTarget[code] = new Template(localized);
6872
}
6973
}
7074

@@ -157,20 +161,24 @@ exports.merge = function (base, extended) {
157161
Assert(typeof message === 'object' && !Array.isArray(message), 'Invalid message for', code);
158162

159163
const language = code;
160-
target[language] = target[language] || {};
164+
165+
// Same as in compile(), don't reuse an inherited object
166+
167+
const localizedTarget = Object.prototype.hasOwnProperty.call(target, language) ? target[language] : {};
168+
target[language] = localizedTarget;
161169

162170
for (code in message) {
163171
const localized = message[code];
164172

165173
if (code === 'root' ||
166174
Template.isTemplate(localized)) {
167175

168-
target[language][code] = localized;
176+
localizedTarget[code] = localized;
169177
continue;
170178
}
171179

172180
Assert(typeof localized === 'string', 'Invalid message for', code, 'in', language);
173-
target[language][code] = new Template(localized);
181+
localizedTarget[code] = new Template(localized);
174182
}
175183
}
176184

test/base.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,6 +2027,30 @@ describe('any', () => {
20272027
Helper.validate(schema, [[{ a: 1 }, false, '"a" angustus']]);
20282028
});
20292029

2030+
it('does not pollute the prototype with a language named __proto__', () => {
2031+
2032+
try {
2033+
Joi.any().messages({ ['__proto__']: { isAdmin: 'true' } });
2034+
Joi.any().prefs({ messages: { ['__proto__']: { isAdmin: 'true' } } });
2035+
2036+
expect({}.isAdmin).to.not.exist();
2037+
}
2038+
finally {
2039+
delete Object.prototype.isAdmin;
2040+
}
2041+
});
2042+
2043+
it('does not pollute Object with a language named constructor', () => {
2044+
2045+
try {
2046+
Joi.any().messages({ constructor: { 'number.min': 'too small' } });
2047+
expect(Object['number.min']).to.not.exist();
2048+
}
2049+
finally {
2050+
delete Object['number.min'];
2051+
}
2052+
});
2053+
20302054
it('errors on invalid message value', () => {
20312055

20322056
expect(() => Joi.number().min(10).message(12)).to.throw('Invalid message options');

test/extend.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,24 @@ describe('extension', () => {
566566
expect(special.validate(1).error).to.be.an.error('shit happens');
567567
});
568568

569+
it('does not pollute the prototype with a language named __proto__', () => {
570+
571+
try {
572+
Joi.extend({
573+
type: 'special',
574+
base: Joi.string(),
575+
messages: {
576+
['__proto__']: { isAdmin: 'true' }
577+
}
578+
});
579+
580+
expect({}.isAdmin).to.not.exist();
581+
}
582+
finally {
583+
delete Object.prototype.isAdmin;
584+
}
585+
});
586+
569587
it('overrides specific error messages with template', () => {
570588

571589
const custom = Joi.extend({

0 commit comments

Comments
 (0)