@@ -11,13 +11,19 @@ defaults = require('./defaults').defaults
11
11
isEmpty = (thing ) ->
12
12
return typeof thing is " object" && thing? && Object .keys (thing).length is 0
13
13
14
- isValidKey = (key ) ->
15
- return key != ' __proto__' && key != ' constructor' && key != ' prototype'
16
-
17
14
processItem = (processors , item , key ) ->
18
15
item = process (item, key) for process in processors
19
16
return item
20
17
18
+ defineProperty = (obj , key , value ) ->
19
+ # make sure the descriptor hasn't been prototype polluted
20
+ descriptor = Object .create null
21
+ descriptor .value = value
22
+ descriptor .writeable = true
23
+ descriptor .enumerable = true
24
+ descriptor .configurable = true
25
+ Object .defineProperty obj, key, descriptor
26
+
21
27
class exports.Parser extends events
22
28
constructor : (opts ) ->
23
29
# if this was called without 'new', create an instance with new and return
@@ -55,14 +61,14 @@ class exports.Parser extends events
55
61
@ emit err
56
62
57
63
assignOrPush : (obj , key , newValue ) =>
58
- return if not isValidKey (key)
59
64
if key not of obj
60
65
if not @options .explicitArray
61
- obj[key] = newValue
66
+ defineProperty obj, key, newValue
62
67
else
63
- obj[key] = [newValue]
68
+ defineProperty obj, key, [newValue]
64
69
else
65
- obj[key] = [obj[key]] if not (obj[key] instanceof Array )
70
+ unless obj[key] instanceof Array
71
+ defineProperty obj, key, [obj[key]]
66
72
obj[key].push newValue
67
73
68
74
reset : =>
@@ -114,11 +120,10 @@ class exports.Parser extends events
114
120
obj[attrkey] = {}
115
121
newValue = if @options .attrValueProcessors then processItem (@options .attrValueProcessors , node .attributes [key], key) else node .attributes [key]
116
122
processedKey = if @options .attrNameProcessors then processItem (@options .attrNameProcessors , key) else key
117
- if isValidKey (processedKey)
118
- if @options .mergeAttrs
119
- @ assignOrPush obj, processedKey, newValue
120
- else
121
- obj[attrkey][processedKey] = newValue
123
+ if @options .mergeAttrs
124
+ @ assignOrPush obj, processedKey, newValue
125
+ else
126
+ defineProperty obj[attrkey], processedKey, newValue
122
127
123
128
# need a place to store the node name
124
129
obj[" #name" ] = if @options .tagNameProcessors then processItem (@options .tagNameProcessors , node .name ) else node .name
@@ -188,7 +193,7 @@ class exports.Parser extends events
188
193
# push a clone so that the node in the children array can receive the #name property while the original obj can do without it
189
194
objClone = {}
190
195
for own key of obj
191
- objClone[ key] = obj[key] if isValidKey (key)
196
+ defineProperty objClone, key, obj[key]
192
197
s[@options .childkey ].push objClone
193
198
delete obj[" #name" ]
194
199
# re-check whether we can collapse the node now to just the charkey value
@@ -204,7 +209,7 @@ class exports.Parser extends events
204
209
# avoid circular references
205
210
old = obj
206
211
obj = {}
207
- obj[nodeName] = old
212
+ defineProperty obj, nodeName, old
208
213
209
214
@resultObject = obj
210
215
# parsing has ended, mark that so we won't throw exceptions from
0 commit comments