Skip to content

Commit 297db4b

Browse files
committed
fix: prevent prototype pollution in MO/PO parsers and headers
1 parent ba1f861 commit 297db4b

4 files changed

Lines changed: 21 additions & 10 deletions

File tree

lib/moparser.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,11 @@ Parser.prototype._addString = function (msgid, msgstr) {
163163
msgstr = msgstr.split('\u0000');
164164
translation.msgstr = [].concat(msgstr || []);
165165

166-
if (!this._table.translations[msgctxt]) {
167-
this._table.translations[msgctxt] = {};
166+
if (!Object.hasOwn(this._table.translations, msgctxt)) {
167+
Object.defineProperty(this._table.translations, msgctxt, { value: {}, writable: true, enumerable: true, configurable: true });
168168
}
169169

170-
this._table.translations[msgctxt][msgid] = translation;
170+
Object.defineProperty(this._table.translations[msgctxt], msgid, { value: translation, writable: true, enumerable: true, configurable: true });
171171
};
172172

173173
/**

lib/poparser.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -464,19 +464,19 @@ Parser.prototype._normalize = function (tokens) {
464464
table.obsolete = {};
465465
}
466466

467-
if (!table.obsolete[msgctxt]) {
468-
table.obsolete[msgctxt] = {};
467+
if (!Object.hasOwn(table.obsolete, msgctxt)) {
468+
Object.defineProperty(table.obsolete, msgctxt, { value: {}, writable: true, enumerable: true, configurable: true });
469469
}
470470

471471
delete tokens[i].obsolete;
472472

473-
table.obsolete[msgctxt][tokens[i].msgid] = tokens[i];
473+
Object.defineProperty(table.obsolete[msgctxt], tokens[i].msgid, { value: tokens[i], writable: true, enumerable: true, configurable: true });
474474

475475
continue;
476476
}
477477

478-
if (!table.translations[msgctxt]) {
479-
table.translations[msgctxt] = {};
478+
if (!Object.hasOwn(table.translations, msgctxt)) {
479+
Object.defineProperty(table.translations, msgctxt, { value: {}, writable: true, enumerable: true, configurable: true });
480480
}
481481

482482
if (!table.headers && !msgctxt && !tokens[i].msgid) {
@@ -486,7 +486,7 @@ Parser.prototype._normalize = function (tokens) {
486486

487487
this._validateToken(tokens[i], table.translations, msgctxt, nplurals);
488488

489-
table.translations[msgctxt][tokens[i].msgid] = tokens[i];
489+
Object.defineProperty(table.translations[msgctxt], tokens[i].msgid, { value: tokens[i], writable: true, enumerable: true, configurable: true });
490490
}
491491

492492
return table;

lib/shared.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function parseHeader (str = '') {
3232

3333
key = HEADERS.get(key.toLowerCase()) || key;
3434

35-
headers[key] = value;
35+
Object.defineProperty(headers, key, { value, writable: true, enumerable: true, configurable: true });
3636
}
3737

3838
return headers;

test/po-parser-test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,15 @@ describe('PO Parser', () => {
240240
});
241241
});
242242
});
243+
244+
describe('prototype pollution', () => {
245+
it('should not pollute Object.prototype via msgctxt', () => {
246+
const po = 'msgctxt "__proto__"\nmsgid "polluted"\nmsgstr "yes"\n';
247+
248+
gettextParser.po.parse(po);
249+
250+
assert.strictEqual({}.polluted, undefined, 'Object.prototype should not be polluted');
251+
assert.strictEqual(({}).polluted, undefined, 'Object.prototype should not be polluted');
252+
});
253+
});
243254
});

0 commit comments

Comments
 (0)