@@ -6,6 +6,7 @@ import TaskQueue from './TaskQueue';
66import { RelationOp } from './ParseOp' ;
77import type { Op } from './ParseOp' ;
88import type ParseObject from './ParseObject' ;
9+ import { isDangerousKey } from "./isDangerousKey" ;
910
1011export type AttributeMap = Record < string , any > ;
1112export type OpsMap = Record < string , Op > ;
@@ -21,17 +22,25 @@ export interface State {
2122
2223export function defaultState ( ) : State {
2324 return {
24- serverData : { } ,
25- pendingOps : [ { } ] ,
26- objectCache : { } ,
25+ serverData : Object . create ( null ) ,
26+ pendingOps : [ Object . create ( null ) ] ,
27+ objectCache : Object . create ( null ) ,
2728 tasks : new TaskQueue ( ) ,
2829 existed : false ,
2930 } ;
3031}
3132
3233export function setServerData ( serverData : AttributeMap , attributes : AttributeMap ) {
3334 for ( const attr in attributes ) {
34- if ( typeof attributes [ attr ] !== 'undefined' ) {
35+ // Skip properties from prototype chain
36+ if ( ! Object . prototype . hasOwnProperty . call ( attributes , attr ) ) {
37+ continue ;
38+ }
39+ // Skip dangerous keys that could pollute prototypes
40+ if ( isDangerousKey ( attr ) ) {
41+ continue ;
42+ }
43+ if ( typeof attributes [ attr ] !== "undefined" ) {
3544 serverData [ attr ] = attributes [ attr ] ;
3645 } else {
3746 delete serverData [ attr ] ;
@@ -40,6 +49,10 @@ export function setServerData(serverData: AttributeMap, attributes: AttributeMap
4049}
4150
4251export function setPendingOp ( pendingOps : OpsMap [ ] , attr : string , op ?: Op ) {
52+ // Skip dangerous keys that could pollute prototypes
53+ if ( isDangerousKey ( attr ) ) {
54+ return ;
55+ }
4356 const last = pendingOps . length - 1 ;
4457 if ( op ) {
4558 pendingOps [ last ] [ attr ] = op ;
@@ -49,13 +62,13 @@ export function setPendingOp(pendingOps: OpsMap[], attr: string, op?: Op) {
4962}
5063
5164export function pushPendingState ( pendingOps : OpsMap [ ] ) {
52- pendingOps . push ( { } ) ;
65+ pendingOps . push ( Object . create ( null ) ) ;
5366}
5467
5568export function popPendingState ( pendingOps : OpsMap [ ] ) : OpsMap {
5669 const first = pendingOps . shift ( ) ;
5770 if ( ! pendingOps . length ) {
58- pendingOps [ 0 ] = { } ;
71+ pendingOps [ 0 ] = Object . create ( null ) ;
5972 }
6073 return first ;
6174}
@@ -64,6 +77,14 @@ export function mergeFirstPendingState(pendingOps: OpsMap[]) {
6477 const first = popPendingState ( pendingOps ) ;
6578 const next = pendingOps [ 0 ] ;
6679 for ( const attr in first ) {
80+ // Skip properties from prototype chain
81+ if ( ! Object . prototype . hasOwnProperty . call ( first , attr ) ) {
82+ continue ;
83+ }
84+ // Skip dangerous keys that could pollute prototypes
85+ if ( isDangerousKey ( attr ) ) {
86+ continue ;
87+ }
6788 if ( next [ attr ] && first [ attr ] ) {
6889 const merged = next [ attr ] . mergeWith ( first [ attr ] ) ;
6990 if ( merged ) {
@@ -81,6 +102,10 @@ export function estimateAttribute(
81102 object : ParseObject ,
82103 attr : string
83104) : any {
105+ // Skip dangerous keys that could pollute prototypes
106+ if ( isDangerousKey ( attr ) ) {
107+ return undefined ;
108+ }
84109 let value = serverData [ attr ] ;
85110 for ( let i = 0 ; i < pendingOps . length ; i ++ ) {
86111 if ( pendingOps [ i ] [ attr ] ) {
@@ -101,13 +126,21 @@ export function estimateAttributes(
101126 pendingOps : OpsMap [ ] ,
102127 object : ParseObject
103128) : AttributeMap {
104- const data = { } ;
129+ const data = Object . create ( null ) ;
105130 let attr ;
106131 for ( attr in serverData ) {
107132 data [ attr ] = serverData [ attr ] ;
108133 }
109134 for ( let i = 0 ; i < pendingOps . length ; i ++ ) {
110135 for ( attr in pendingOps [ i ] ) {
136+ // Skip properties from prototype chain
137+ if ( ! Object . prototype . hasOwnProperty . call ( pendingOps [ i ] , attr ) ) {
138+ continue ;
139+ }
140+ // Skip dangerous keys that could pollute prototypes
141+ if ( isDangerousKey ( attr ) ) {
142+ continue ;
143+ }
111144 if ( pendingOps [ i ] [ attr ] instanceof RelationOp ) {
112145 if ( object . id ) {
113146 data [ attr ] = ( pendingOps [ i ] [ attr ] as RelationOp ) . applyTo ( data [ attr ] , object , attr ) ;
@@ -125,7 +158,7 @@ export function estimateAttributes(
125158 if ( ! isNaN ( nextKey ) ) {
126159 object [ key ] = [ ] ;
127160 } else {
128- object [ key ] = { } ;
161+ object [ key ] = Object . create ( null ) ;
129162 }
130163 } else {
131164 if ( Array . isArray ( object [ key ] ) ) {
@@ -165,7 +198,7 @@ function nestedSet(obj, key, value) {
165198 if ( ! isNaN ( nextPath ) ) {
166199 obj [ path ] = [ ] ;
167200 } else {
168- obj [ path ] = { } ;
201+ obj [ path ] = Object . create ( null ) ;
169202 }
170203 }
171204 obj = obj [ path ] ;
@@ -184,6 +217,14 @@ export function commitServerChanges(
184217) {
185218 const ParseObject = CoreManager . getParseObject ( ) ;
186219 for ( const attr in changes ) {
220+ // Skip properties from prototype chain
221+ if ( ! Object . prototype . hasOwnProperty . call ( changes , attr ) ) {
222+ continue ;
223+ }
224+ // Skip dangerous keys that could pollute prototypes
225+ if ( isDangerousKey ( attr ) ) {
226+ continue ;
227+ }
187228 const val = changes [ attr ] ;
188229 nestedSet ( serverData , attr , val ) ;
189230 if (
0 commit comments