@@ -40,16 +40,37 @@ const oversizedLengthStdout = String.fromCharCode(oversizedLengthHeader[0]) +
4040const unsignedOversizedLengthStdout = String . fromCharCode ( unsignedOversizedLengthHeader [ 0 ] ) +
4141 Buffer . from ( unsignedOversizedLengthHeader . subarray ( 1 ) ) . toString ( 'utf-8' ) ;
4242// FF 0F followed by a small, plausible size (8) and 8 payload bytes. Unlike the
43- // oversized headers above, this passes the size check and reaches the
44- // deserializer, which throws because the payload is not a real message.
43+ // oversized headers above, this passes the size check, but its payload does not
44+ // begin with the inner v8 header a real frame carries, so it is treated as
45+ // stdout instead of reaching the deserializer.
4546// Regression fixture for https://github.com/nodejs/node/issues/66164
4647const plausibleSizeFalseHeader = Buffer . from ( [
47- 0xff , 0x0f , // v8 serializer header magic
48- 0x00 , 0x00 , 0x00 , 0x08 , // payload size of 8 bytes
48+ 0xff , 0x0f , // V8 serializer header magic
49+ 0x00 , 0x00 , 0x00 , 0x08 , // Payload size of 8 bytes
4950 0x41 , 0x42 , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 , // "ABCDEFGH", not a real payload
5051] ) ;
5152const plausibleSizeFalseHeaderStdout = String . fromCharCode ( plausibleSizeFalseHeader [ 0 ] ) +
5253 Buffer . from ( plausibleSizeFalseHeader . subarray ( 1 ) ) . toString ( 'utf-8' ) ;
54+ // FF 0F, a valid size, then the inner v8 header a real frame repeats, followed
55+ // by a byte that is not a valid serialized value. This passes the inner header
56+ // check and reaches the deserializer, which throws. This is what a genuine
57+ // report-protocol regression looks like, so the parser must let the error
58+ // surface instead of hiding it as stdout.
59+ const headeredCorruptFrame = Buffer . from ( [
60+ 0xff , 0x0f , // Outer v8 serializer header magic
61+ 0x00 , 0x00 , 0x00 , 0x03 , // Payload size of 3 bytes
62+ 0xff , 0x0f , // Inner v8 header that a real frame repeats
63+ 0xee , // Not a valid serialized value
64+ ] ) ;
65+ // FF 0F with a declared size of 1, then more header bytes. The payload is
66+ // shorter than the inner v8 header a real frame carries, so it can never be a
67+ // real frame. The length guard must reject it as stdout without reaching the
68+ // deserializer.
69+ const shortPayloadFalseHeader = Buffer . from ( [
70+ 0xff , 0x0f , // Outer v8 serializer header magic
71+ 0x00 , 0x00 , 0x00 , 0x01 , // Payload size of 1 byte, too short for a header
72+ 0xff , 0x0f , // Trailing bytes that also look like a header
73+ ] ) ;
5374
5475function collectStdout ( reported ) {
5576 return reported
@@ -182,16 +203,17 @@ describe('v8 deserializer', common.mustCall(() => {
182203
183204 it ( 'should not crash when stdout mimics a v8 frame with a plausible size' , async ( ) => {
184205 // Regression test for https://github.com/nodejs/node/issues/66164
185- // The bytes reach the deserializer and it throws. The parser must emit
186- // them as stdout instead of letting the error abort the whole run.
206+ // The payload does not start with the inner v8 header that a real frame
207+ // carries, so the parser treats the bytes as stdout instead of handing
208+ // them to the deserializer and aborting the whole run.
187209 const reported = await collectReported ( [ plausibleSizeFalseHeader ] ) ;
188210 assert ( reported . every ( ( event ) => event . type === 'test:stdout' ) ) ;
189211 assert . strictEqual ( collectStdout ( reported ) , plausibleSizeFalseHeaderStdout ) ;
190212 } ) ;
191213
192214 it ( 'should resync and parse a real message after a plausible-size false frame' , async ( ) => {
193215 // The poison bytes followed by a real serialized message. The parser must
194- // recover from the failed deserialize and still report the real event.
216+ // reject the poison as stdout and still report the real event.
195217 const reported = await collectReported ( [
196218 plausibleSizeFalseHeader ,
197219 ...chunks ,
@@ -226,6 +248,22 @@ describe('v8 deserializer', common.mustCall(() => {
226248 assert . strictEqual ( collectStdout ( reported ) , plausibleSizeFalseHeaderStdout ) ;
227249 } ) ;
228250
251+ it ( 'should surface a genuinely corrupt frame instead of hiding it' , ( ) => {
252+ // A frame with both v8 headers and a valid size but an invalid value is
253+ // what a real report-protocol regression looks like, not stray stdout.
254+ // The parser must let the deserialize error surface instead of silently
255+ // turning it into stdout.
256+ assert . throws ( ( ) => fileTest . parseMessage ( headeredCorruptFrame ) , / d e s e r i a l i z e / ) ;
257+ } ) ;
258+
259+ it ( 'should treat a frame whose payload is shorter than the header as stdout' , async ( ) => {
260+ // The declared size is smaller than the inner v8 header, so the length
261+ // guard must reject the bytes as stdout instead of reaching the
262+ // deserializer.
263+ const reported = await collectReported ( [ shortPayloadFalseHeader ] ) ;
264+ assert ( reported . every ( ( event ) => event . type === 'test:stdout' ) ) ;
265+ } ) ;
266+
229267 const headerPosition = headerLength * 2 + 4 ;
230268 for ( let i = 0 ; i < headerPosition + 5 ; i ++ ) {
231269 const message = `should deserialize a serialized message split into two chunks {...${ i } ,${ i + 1 } ...}` ;
0 commit comments