@@ -26,6 +26,11 @@ export class FuzzedDataProvider {
26
26
private dataPtr = - 1 ;
27
27
/** The number of remaining bytes that can be consumed from the fuzzer input data. */
28
28
_remainingBytes = 0 ;
29
+ /**
30
+ * A lookup table that maps input values to output characters in a cyclical manner.
31
+ * The output characters are evenly distributed across the range of printable ASCII characters (32-126)
32
+ */
33
+ private lookupTable = new Uint8Array ( 256 ) ;
29
34
30
35
static readonly min_float = - 3.4028235e38 ;
31
36
static readonly max_float = 3.4028235e38 ;
@@ -41,6 +46,18 @@ export class FuzzedDataProvider {
41
46
this . dataPtr = 0 ;
42
47
this . _remainingBytes = data . length ;
43
48
}
49
+
50
+ /**
51
+ * Populate the lookup table with a mapping of input values to output characters
52
+ */
53
+ let nextChar = 32 ;
54
+ for ( let i = 0 ; i < 256 ; i ++ ) {
55
+ this . lookupTable [ i ] = nextChar ;
56
+ nextChar ++ ;
57
+ if ( nextChar > 126 ) {
58
+ nextChar = 32 ;
59
+ }
60
+ }
44
61
}
45
62
46
63
/**
@@ -372,33 +389,76 @@ export class FuzzedDataProvider {
372
389
* is not sufficiently long.
373
390
* @param maxLength the maximum length of the string
374
391
* @param encoding the encoding of the string
392
+ * @param printable - a boolean, which defaults to false that indicates whether consumed strings
393
+ * should be forced to contain only valid printable characters
375
394
* @returns a `string` of length between 0 and `maxLength` (inclusive)
376
395
*/
377
396
consumeString (
378
397
maxLength : number ,
379
- encoding : BufferEncoding | undefined = "ascii"
398
+ encoding : BufferEncoding | undefined = "ascii" ,
399
+ printable : boolean | undefined = false
380
400
) : string {
381
401
if ( maxLength < 0 ) throw new Error ( "maxLength must be non-negative" ) ;
402
+ let result ;
382
403
const arrayLength = Math . min ( maxLength , this . _remainingBytes ) ;
383
- const result = this . data . toString (
384
- encoding ,
385
- this . dataPtr ,
386
- this . dataPtr + arrayLength
387
- ) ;
404
+
405
+ if ( printable ) {
406
+ result = this . bufToPrintableString (
407
+ this . data ,
408
+ this . dataPtr ,
409
+ this . dataPtr + arrayLength ,
410
+ encoding
411
+ ) ;
412
+ } else {
413
+ result = this . data . toString (
414
+ encoding ,
415
+ this . dataPtr ,
416
+ this . dataPtr + arrayLength
417
+ ) ;
418
+ }
388
419
this . dataPtr += arrayLength ;
389
420
this . _remainingBytes -= arrayLength ;
390
421
return result ;
391
422
}
392
423
424
+ /**
425
+ * Helper function that converts the given string type into one that only
426
+ * contains printable characters. Elements in `buf` that are already in
427
+ * ASCII printable range are not undergoing any conversion.
428
+ * Known limitations:
429
+ * numbers [32; 97] will have the probability of about 0.01172 of occuring,
430
+ * numbers [98; 126] will have probability of 0.00781 of occurring.
431
+ * @param buf - Buffer that contains arbitrary values
432
+ * @param min - lower bound at which processing of the provided `Buffer` shall begin
433
+ * @param max - upper bound, analogous to the lower bound
434
+ * @param encoding - a valid `BufferEncoding`.
435
+ * @returns a string that was sanitized and only contains printable characters
436
+ */
437
+ bufToPrintableString (
438
+ buf : Buffer ,
439
+ min : number ,
440
+ max : number ,
441
+ encoding : BufferEncoding
442
+ ) : string {
443
+ const newBuf = new Uint8Array ( max - min ) ;
444
+ for ( let i = min ; i < max ; i ++ ) {
445
+ newBuf [ i - min ] = this . lookupTable [ buf [ i ] ] ;
446
+ }
447
+ return new TextDecoder ( encoding ) . decode ( newBuf ) ;
448
+ }
449
+
393
450
/**
394
451
* Consumes the remaining bytes of the fuzzer input as a string.
395
452
* @param encoding - the encoding of the string
453
+ * @param printable - a boolean, which defaults to false that indicates whether consumed strings
454
+ * should be forced to contain only valid printable characters
396
455
* @returns a string constructed from the remaining bytes of the fuzzer input using the given encoding
397
456
*/
398
457
consumeRemainingAsString (
399
- encoding : BufferEncoding | undefined = "ascii"
458
+ encoding : BufferEncoding | undefined = "ascii" ,
459
+ printable : boolean | undefined = false
400
460
) : string {
401
- return this . consumeString ( this . _remainingBytes , encoding ) ;
461
+ return this . consumeString ( this . _remainingBytes , encoding , printable ) ;
402
462
}
403
463
404
464
/**
@@ -408,16 +468,19 @@ export class FuzzedDataProvider {
408
468
* @param maxArrayLength the maximum length of the array
409
469
* @param maxStringLength the maximum length of the strings
410
470
* @param encoding the encoding of the strings
471
+ * @param printable - a boolean, which defaults to false that indicates whether consumed strings
472
+ * should be forced to contain only valid printable characters
411
473
* @returns an array containing strings constructed from the remaining bytes of the fuzzer input using the given encoding
412
474
*/
413
475
consumeStringArray (
414
476
maxArrayLength : number ,
415
477
maxStringLength : number ,
416
- encoding : BufferEncoding | undefined = "ascii"
478
+ encoding : BufferEncoding | undefined = "ascii" ,
479
+ printable : boolean | undefined = false
417
480
) {
418
481
const strs = [ ] ;
419
482
while ( strs . length < maxArrayLength && this . remainingBytes > 0 ) {
420
- const str = this . consumeString ( maxStringLength , encoding ) ;
483
+ const str = this . consumeString ( maxStringLength , encoding , printable ) ;
421
484
if ( str ) {
422
485
strs . push ( str ) ;
423
486
}
0 commit comments