@@ -120,4 +120,192 @@ describe('decode', () => {
120120 count : 15 ,
121121 } ) ;
122122 } ) ;
123+
124+ describe ( 'Prototype Pollution Protection' , ( ) => {
125+ beforeEach ( ( ) => {
126+ // Clear any pollution before each test
127+ delete Object . prototype . polluted ;
128+ delete Object . prototype . malicious ;
129+ delete Object . prototype . exploit ;
130+ } ) ;
131+
132+ afterEach ( ( ) => {
133+ // Clean up after tests
134+ delete Object . prototype . polluted ;
135+ delete Object . prototype . malicious ;
136+ delete Object . prototype . exploit ;
137+ } ) ;
138+
139+ it ( 'should not pollute Object.prototype when decoding object with __proto__ key' , ( ) => {
140+ const testObj = { } ;
141+ const maliciousInput = {
142+ normalKey : 'value' ,
143+ __proto__ : { polluted : 'yes' } ,
144+ } ;
145+
146+ const result = decode ( maliciousInput ) ;
147+
148+ // Verify Object.prototype was not polluted
149+ expect ( testObj . polluted ) . toBeUndefined ( ) ;
150+ expect ( { } . polluted ) . toBeUndefined ( ) ;
151+ expect ( Object . prototype . polluted ) . toBeUndefined ( ) ;
152+
153+ // Verify result only has own property
154+ expect ( Object . prototype . hasOwnProperty . call ( result , '__proto__' ) ) . toBe ( false ) ;
155+ expect ( result . normalKey ) . toBe ( 'value' ) ;
156+ } ) ;
157+
158+ it ( 'should not pollute Object.prototype when decoding object with constructor key' , ( ) => {
159+ const testObj = { } ;
160+ const maliciousInput = {
161+ normalKey : 'value' ,
162+ constructor : { polluted : 'yes' } ,
163+ } ;
164+
165+ const result = decode ( maliciousInput ) ;
166+
167+ // Verify Object.prototype was not polluted
168+ expect ( testObj . polluted ) . toBeUndefined ( ) ;
169+ expect ( { } . polluted ) . toBeUndefined ( ) ;
170+ expect ( Object . prototype . polluted ) . toBeUndefined ( ) ;
171+
172+ // Verify result doesn't contain constructor from prototype chain
173+ expect ( result . normalKey ) . toBe ( 'value' ) ;
174+ } ) ;
175+
176+ it ( 'should not pollute Object.prototype when decoding object with prototype key' , ( ) => {
177+ const testObj = { } ;
178+ const maliciousInput = {
179+ normalKey : 'value' ,
180+ prototype : { polluted : 'yes' } ,
181+ } ;
182+
183+ const result = decode ( maliciousInput ) ;
184+
185+ // Verify Object.prototype was not polluted
186+ expect ( testObj . polluted ) . toBeUndefined ( ) ;
187+ expect ( { } . polluted ) . toBeUndefined ( ) ;
188+ expect ( Object . prototype . polluted ) . toBeUndefined ( ) ;
189+
190+ // Verify result contains only own properties
191+ expect ( result . normalKey ) . toBe ( 'value' ) ;
192+ } ) ;
193+
194+ it ( 'should not pollute Object.prototype when decoding nested objects with dangerous keys' , ( ) => {
195+ const testObj = { } ;
196+ const maliciousInput = {
197+ nested : {
198+ __proto__ : { polluted : 'nested' } ,
199+ data : 'value' ,
200+ } ,
201+ normal : 'key' ,
202+ } ;
203+
204+ const result = decode ( maliciousInput ) ;
205+
206+ // Verify Object.prototype was not polluted
207+ expect ( testObj . polluted ) . toBeUndefined ( ) ;
208+ expect ( { } . polluted ) . toBeUndefined ( ) ;
209+ expect ( Object . prototype . polluted ) . toBeUndefined ( ) ;
210+
211+ // Verify result structure
212+ expect ( result . normal ) . toBe ( 'key' ) ;
213+ expect ( result . nested ) . toBeDefined ( ) ;
214+ expect ( result . nested . data ) . toBe ( 'value' ) ;
215+ expect ( Object . prototype . hasOwnProperty . call ( result . nested , '__proto__' ) ) . toBe ( false ) ;
216+ } ) ;
217+
218+ it ( 'should not pollute Object.prototype when decoding arrays with objects containing dangerous keys' , ( ) => {
219+ const testObj = { } ;
220+ const maliciousInput = [
221+ { __proto__ : { polluted : 'array1' } } ,
222+ { constructor : { malicious : 'array2' } } ,
223+ { normalKey : 'value' } ,
224+ ] ;
225+
226+ const result = decode ( maliciousInput ) ;
227+
228+ // Verify Object.prototype was not polluted
229+ expect ( testObj . polluted ) . toBeUndefined ( ) ;
230+ expect ( testObj . malicious ) . toBeUndefined ( ) ;
231+ expect ( { } . polluted ) . toBeUndefined ( ) ;
232+ expect ( { } . malicious ) . toBeUndefined ( ) ;
233+ expect ( Object . prototype . polluted ) . toBeUndefined ( ) ;
234+ expect ( Object . prototype . malicious ) . toBeUndefined ( ) ;
235+
236+ // Verify result array
237+ expect ( Array . isArray ( result ) ) . toBe ( true ) ;
238+ expect ( result . length ) . toBe ( 3 ) ;
239+ expect ( result [ 2 ] . normalKey ) . toBe ( 'value' ) ;
240+ } ) ;
241+
242+ it ( 'should only decode own properties, not inherited ones' , ( ) => {
243+ const parent = { inherited : 'parent' } ;
244+ const child = Object . create ( parent ) ;
245+ child . own = 'child' ;
246+
247+ const result = decode ( child ) ;
248+
249+ // Should only include own property
250+ expect ( result . own ) . toBe ( 'child' ) ;
251+ expect ( result . inherited ) . toBeUndefined ( ) ;
252+ } ) ;
253+
254+ it ( 'should not decode properties from prototype chain' , ( ) => {
255+ Object . prototype . exploit = 'malicious' ;
256+ const obj = { normalKey : 'value' } ;
257+
258+ const result = decode ( obj ) ;
259+
260+ // Should not include prototype property
261+ expect ( result . normalKey ) . toBe ( 'value' ) ;
262+ expect ( Object . prototype . hasOwnProperty . call ( result , 'exploit' ) ) . toBe ( false ) ;
263+
264+ delete Object . prototype . exploit ;
265+ } ) ;
266+
267+ it ( 'should not pollute Object.prototype when decoding Parse type with dangerous className' , ( ) => {
268+ const testObj = { } ;
269+ const maliciousInput = {
270+ __type : 'Pointer' ,
271+ className : '__proto__' ,
272+ objectId : 'test123' ,
273+ } ;
274+
275+ // This should be handled by ParseObject.fromJSON
276+ decode ( maliciousInput ) ;
277+
278+ // Verify Object.prototype was not polluted
279+ expect ( testObj . polluted ) . toBeUndefined ( ) ;
280+ expect ( { } . polluted ) . toBeUndefined ( ) ;
281+ expect ( Object . prototype . polluted ) . toBeUndefined ( ) ;
282+ } ) ;
283+
284+ it ( 'should not pollute Object.prototype when decoding deeply nested dangerous keys' , ( ) => {
285+ const testObj = { } ;
286+ const maliciousInput = {
287+ level1 : {
288+ level2 : {
289+ level3 : {
290+ __proto__ : { polluted : 'deep' } ,
291+ normalData : 'value' ,
292+ } ,
293+ } ,
294+ } ,
295+ } ;
296+
297+ const result = decode ( maliciousInput ) ;
298+
299+ // Verify Object.prototype was not polluted
300+ expect ( testObj . polluted ) . toBeUndefined ( ) ;
301+ expect ( { } . polluted ) . toBeUndefined ( ) ;
302+ expect ( Object . prototype . polluted ) . toBeUndefined ( ) ;
303+
304+ // Verify result structure is preserved (without dangerous keys)
305+ expect ( result . level1 . level2 . level3 . normalData ) . toBe ( 'value' ) ;
306+ expect ( Object . prototype . hasOwnProperty . call ( result . level1 . level2 . level3 , '__proto__' ) ) . toBe (
307+ false
308+ ) ;
309+ } ) ;
310+ } ) ;
123311} ) ;
0 commit comments