@@ -152,4 +152,64 @@ describe('Struct', function() {
152152 assert ( refData . field6 . bit9 ) ;
153153 assert . deepEqual ( refData . field6 . toArray ( ) , [ 'bit2' , 'bit9' ] ) ;
154154 } ) ;
155+
156+ describe ( 'prototype-chain property guard' , function ( ) {
157+ let S ;
158+ before ( function ( ) {
159+ S = Struct ( 'GuardedStruct' , { a : DataTypes . uint8 } ) ;
160+ } ) ;
161+
162+ it ( 'should reject `constructor` as a field name' , function ( ) {
163+ assert . throws (
164+ ( ) => new S ( { constructor : 'attacker-controlled' } ) ,
165+ / u n e x p e c t e d p r o p e r t y / ,
166+ 'constructor is on Object.prototype, not an own property of defs' ,
167+ ) ;
168+ } ) ;
169+
170+ it ( 'should reject `toString` as a field name' , function ( ) {
171+ assert . throws (
172+ ( ) => new S ( { toString : ( ) => 'pwned' } ) ,
173+ / u n e x p e c t e d p r o p e r t y / ,
174+ ) ;
175+ } ) ;
176+
177+ it ( 'should still accept declared own-property field names' , function ( ) {
178+ const instance = new S ( { a : 42 } ) ;
179+ assert . strictEqual ( instance . a , 42 ) ;
180+ } ) ;
181+
182+ it ( 'should still reject undeclared field names' , function ( ) {
183+ assert . throws (
184+ ( ) => new S ( { b : 1 } ) ,
185+ / u n e x p e c t e d p r o p e r t y / ,
186+ ) ;
187+ } ) ;
188+
189+ it ( 'should produce a stable error message even when a `constructor` field is declared' , function ( ) {
190+ // Edge case: if a struct legitimately declares a `constructor` field
191+ // and the caller sets it before an unexpected key is seen, the throw
192+ // path must not depend on `this.constructor.name`.
193+ const Weird = Struct ( 'WeirdStruct' , {
194+ constructor : DataTypes . uint8 ,
195+ a : DataTypes . uint8 ,
196+ } ) ;
197+ assert . throws (
198+ ( ) => new Weird ( { constructor : 1 , badKey : 2 } ) ,
199+ err => err instanceof TypeError && / ^ W e i r d S t r u c t : b a d K e y i s a n u n e x p e c t e d p r o p e r t y $ / . test ( err . message ) ,
200+ ) ;
201+ } ) ;
202+
203+ it ( 'should report the actual subclass name in the error when subclassed' , function ( ) {
204+ // Struct-generated classes can be subclassed; the error message should
205+ // identify the actual class being instantiated, not the underlying
206+ // Struct name. This is what `new.target.name` gives us.
207+ const Base = Struct ( 'BaseStruct' , { a : DataTypes . uint8 } ) ;
208+ class Extended extends Base { }
209+ assert . throws (
210+ ( ) => new Extended ( { unexpected : 1 } ) ,
211+ err => err instanceof TypeError && / ^ E x t e n d e d : u n e x p e c t e d i s a n u n e x p e c t e d p r o p e r t y $ / . test ( err . message ) ,
212+ ) ;
213+ } ) ;
214+ } ) ;
155215} ) ;
0 commit comments