@@ -70,4 +70,41 @@ describe("deepCopy", () => {
7070 expect ( copy . b . c ) . toBe ( 99 ) ;
7171 expect ( original . a ) . toBe ( 1 ) ;
7272 } ) ;
73+
74+ describe ( "prototype pollution resistance" , ( ) => {
75+ it ( "should not set the prototype from a JSON-parsed __proto__ key" , ( ) => {
76+ // JSON.parse treats __proto__ as a literal own property, unlike object literals.
77+ const malicious = JSON . parse ( '{"__proto__": {"polluted": "yes"}}' ) as Record < string , unknown > ;
78+
79+ const copy = deepCopy ( malicious ) as Record < string , unknown > ;
80+
81+ expect ( Object . getPrototypeOf ( copy ) ) . toBe ( Object . prototype ) ;
82+ expect ( copy . polluted ) . toBeUndefined ( ) ;
83+ } ) ;
84+
85+ it ( "should not copy a literal constructor key from external data" , ( ) => {
86+ const malicious = JSON . parse ( '{"constructor": {"prototype": {"polluted": "yes"}}}' ) as Record <
87+ string ,
88+ unknown
89+ > ;
90+
91+ const copy = deepCopy ( malicious ) ;
92+
93+ expect ( Object . hasOwn ( copy , "constructor" ) ) . toBe ( false ) ;
94+ // Sanity: ensure Object.prototype was not polluted by the copy operation.
95+ expect ( ( { } as Record < string , unknown > ) . polluted ) . toBeUndefined ( ) ;
96+ } ) ;
97+
98+ it ( "should preserve safe keys when dangerous keys are present" , ( ) => {
99+ const malicious = JSON . parse (
100+ '{"__proto__": {"polluted": "yes"}, "safe": 1, "other": "keep"}' ,
101+ ) as Record < string , unknown > ;
102+
103+ const copy = deepCopy ( malicious ) as Record < string , unknown > ;
104+
105+ expect ( copy . safe ) . toBe ( 1 ) ;
106+ expect ( copy . other ) . toBe ( "keep" ) ;
107+ expect ( copy . polluted ) . toBeUndefined ( ) ;
108+ } ) ;
109+ } ) ;
73110} ) ;
0 commit comments