Skip to content

Commit d0c394f

Browse files
committed
Fix __proto__ property emit
The `__proto__` property is a special use case. It can be defined as a computed property in an object expression, or via `defineProperty`. If it’s assigned via a non-computed property in an object expression or via a regular property assignment, even computed, this sets the prototype of the object instead. This makes the generated code potentially vulnerable to prototype pollution.
1 parent 392a534 commit d0c394f

File tree

3 files changed

+77
-15
lines changed

3 files changed

+77
-15
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Used as input
2+
// { preserveReferences: true }
3+
export default ((
4+
$1 = {
5+
name: '$1'
6+
},
7+
$0 = {
8+
['__proto__']: $1,
9+
name: '$0'
10+
}
11+
) => (
12+
Object.defineProperties($1, {
13+
['__proto__']: {
14+
value: $0,
15+
configurable: true,
16+
enumerable: true,
17+
writable: true
18+
}
19+
}),
20+
$0
21+
))()
22+
23+
// -------------------------------------------------------------------------------------------------
24+
25+
// Default output
26+
// { preserveReferences: false }
27+
// Recursive references are not supported without preserveReferences
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Used as input
2+
// { preserveReferences: true }
3+
export default {
4+
['__proto__']: {}
5+
}
6+
7+
// -------------------------------------------------------------------------------------------------
8+
9+
// Default output
10+
// { preserveReferences: false }
11+
const withoutPreserveReferences = {
12+
['__proto__']: {}
13+
}

src/estree-util-value-to-estree.ts

+37-15
Original file line numberDiff line numberDiff line change
@@ -326,15 +326,15 @@ function symbolToEstree(symbol: symbol): Expression {
326326
* The ESTree properry node.
327327
*/
328328
function property(key: string | symbol, value: Expression): Property {
329-
const computed = typeof key !== 'string'
329+
const isString = typeof key === 'string'
330330

331331
return {
332332
type: 'Property',
333333
method: false,
334334
shorthand: false,
335-
computed,
335+
computed: key === '__proto__' || !isString,
336336
kind: 'init',
337-
key: computed ? symbolToEstree(key) : literal(key),
337+
key: isString ? literal(key) : symbolToEstree(key),
338338
value
339339
}
340340
}
@@ -794,7 +794,15 @@ export function valueToEstree(value: unknown, options: Options = {}): Expression
794794

795795
const properties: Property[] = []
796796
if (Object.getPrototypeOf(val) == null) {
797-
properties.push(property('__proto__', literal(null)))
797+
properties.push({
798+
type: 'Property',
799+
method: false,
800+
shorthand: false,
801+
computed: false,
802+
kind: 'init',
803+
key: identifier('__proto__'),
804+
value: literal(null)
805+
})
798806
}
799807

800808
const object = val as Record<string | symbol, unknown>
@@ -826,17 +834,31 @@ export function valueToEstree(value: unknown, options: Options = {}): Expression
826834
childContext &&
827835
namedContexts.indexOf(childContext) >= namedContexts.indexOf(context)
828836
) {
829-
childContext.assignment = {
830-
type: 'AssignmentExpression',
831-
operator: '=',
832-
left: {
833-
type: 'MemberExpression',
834-
computed: true,
835-
optional: false,
836-
object: identifier(context.name!),
837-
property: generate(key)
838-
},
839-
right: childContext.assignment || generate(child)
837+
if (key === '__proto__') {
838+
propertyDescriptors.push(
839+
property(key, {
840+
type: 'ObjectExpression',
841+
properties: [
842+
property('value', generate(child)),
843+
property('configurable', literal(true)),
844+
property('enumerable', literal(true)),
845+
property('writable', literal(true))
846+
]
847+
})
848+
)
849+
} else {
850+
childContext.assignment = {
851+
type: 'AssignmentExpression',
852+
operator: '=',
853+
left: {
854+
type: 'MemberExpression',
855+
computed: true,
856+
optional: false,
857+
object: identifier(context.name!),
858+
property: generate(key)
859+
},
860+
right: childContext.assignment || generate(child)
861+
}
840862
}
841863
} else {
842864
properties.push(property(key, generate(child)))

0 commit comments

Comments
 (0)