Skip to content

Commit c3addb6

Browse files
author
434b
committed
First approach to for a toggable printableStringConsumer
1 parent 3e86576 commit c3addb6

File tree

2 files changed

+98
-10
lines changed

2 files changed

+98
-10
lines changed

packages/core/FuzzedDataProvider.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,24 @@ describe("FuzzedDataProvider checks", () => {
945945
expect(strings).toContain("or si");
946946
expect(strings).toContain("t ame");
947947
});
948+
it("verifyPrintableString", () => {
949+
const data = new FuzzedDataProvider(Buffer.from(Data));
950+
const consumedStrAsArr = [...data.consumeString(1024, "ascii", true)];
951+
consumedStrAsArr.forEach((c) => {
952+
const charAsNum = c.charCodeAt(0);
953+
expect(charAsNum >= 32 && charAsNum <= 126).toBeTruthy();
954+
});
955+
});
956+
it("verifyNonPrintableString", () => {
957+
const data = new FuzzedDataProvider(Buffer.from(Data));
958+
const consumedStrAsArr = [...data.consumeString(1024)];
959+
expect(
960+
consumedStrAsArr.some((ele) => {
961+
const eleAsNum = ele.charCodeAt(0);
962+
return eleAsNum < 32 || eleAsNum > 126;
963+
})
964+
).toBeTruthy();
965+
});
948966
});
949967

950968
const Data = Buffer.from([

packages/core/FuzzedDataProvider.ts

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -372,33 +372,100 @@ export class FuzzedDataProvider {
372372
* is not sufficiently long.
373373
* @param maxLength the maximum length of the string
374374
* @param encoding the encoding of the string
375+
* @param printable - a boolean, which defaults to false that indicates whether consumed strings
376+
* should be forced to contain only valid printable characters
375377
* @returns a `string` of length between 0 and `maxLength` (inclusive)
376378
*/
377379
consumeString(
378380
maxLength: number,
379-
encoding: BufferEncoding | undefined = "ascii"
381+
encoding: BufferEncoding | undefined = "ascii",
382+
printable: boolean | undefined = false
380383
): string {
381384
if (maxLength < 0) throw new Error("maxLength must be non-negative");
385+
let result;
382386
const arrayLength = Math.min(maxLength, this._remainingBytes);
383-
const result = this.data.toString(
384-
encoding,
385-
this.dataPtr,
386-
this.dataPtr + arrayLength
387-
);
387+
388+
if (printable) {
389+
result = this.bufToPrintableString(
390+
this.data,
391+
this.dataPtr,
392+
this.dataPtr + arrayLength,
393+
encoding
394+
);
395+
} else {
396+
result = this.data.toString(
397+
encoding,
398+
this.dataPtr,
399+
this.dataPtr + arrayLength
400+
);
401+
}
388402
this.dataPtr += arrayLength;
389403
this._remainingBytes -= arrayLength;
390404
return result;
391405
}
392406

407+
/**
408+
* Wrapping subtraction function that returns a number within a set limit
409+
* @param min is a number representing the minuend
410+
* @param sub is a number representing the subtrahend
411+
* @param lim sets the upper boundary
412+
* @returns a number n with the constraint 0 <= n < lim, as long as lim >=2
413+
*/
414+
wrappingSub(min: number, sub: number, lim: number): number {
415+
if (lim < 2) {
416+
throw new Error("Upper boundary should be greater than or equal to 2");
417+
}
418+
return (min - sub + lim) % lim;
419+
}
420+
421+
/**
422+
* A simple conversion helper that yields the ASCII representaion of a character.
423+
* @param char is a single character from a string represented as a number
424+
* @returns a 'string' from a single character that was represented by a number
425+
*/
426+
toAscii(char: number): number {
427+
if (char < 32 || char > 126) {
428+
char = (this.wrappingSub(char, 32, 255) % 95) + 32;
429+
}
430+
return char;
431+
}
432+
433+
/**
434+
* Helper function that converts the given string type into one that only
435+
* contains printable characters. This conversion produces seemingly arbitrary
436+
* (non-smart) but printable strings, IFF the entropy of the input buffer is
437+
* high enough. Limitations are that there are collisions such that, e.g.:
438+
* the conversion of values: `0x1`, `0x42`, and `0xa2` all yield the character `B`.
439+
* @param buf - Buffer that contains arbitrary values
440+
* @param min - lower bound at which processing of the provided `Buffer` shall begin
441+
* @param max - upper bound, analogous to the lower bound
442+
* @param encoding - a valid `BufferEncoding`.
443+
* @returns a string that was sanitized and only contains printable characters
444+
*/
445+
bufToPrintableString(
446+
buf: Buffer,
447+
min: number,
448+
max: number,
449+
encoding: BufferEncoding
450+
): string {
451+
for (let i = min; i < max; i++) {
452+
buf[i] = this.toAscii(buf[i]);
453+
}
454+
return buf.subarray(min, max).toString(encoding);
455+
}
456+
393457
/**
394458
* Consumes the remaining bytes of the fuzzer input as a string.
395459
* @param encoding - the encoding of the string
460+
* @param printable - a boolean, which defaults to false that indicates whether consumed strings
461+
* should be forced to contain only valid printable characters
396462
* @returns a string constructed from the remaining bytes of the fuzzer input using the given encoding
397463
*/
398464
consumeRemainingAsString(
399-
encoding: BufferEncoding | undefined = "ascii"
465+
encoding: BufferEncoding | undefined = "ascii",
466+
printable: boolean | undefined = false
400467
): string {
401-
return this.consumeString(this._remainingBytes, encoding);
468+
return this.consumeString(this._remainingBytes, encoding, printable);
402469
}
403470

404471
/**
@@ -408,16 +475,19 @@ export class FuzzedDataProvider {
408475
* @param maxArrayLength the maximum length of the array
409476
* @param maxStringLength the maximum length of the strings
410477
* @param encoding the encoding of the strings
478+
* @param printable - a boolean, which defaults to false that indicates whether consumed strings
479+
* should be forced to contain only valid printable characters
411480
* @returns an array containing strings constructed from the remaining bytes of the fuzzer input using the given encoding
412481
*/
413482
consumeStringArray(
414483
maxArrayLength: number,
415484
maxStringLength: number,
416-
encoding: BufferEncoding | undefined = "ascii"
485+
encoding: BufferEncoding | undefined = "ascii",
486+
printable: boolean | undefined = false
417487
) {
418488
const strs = [];
419489
while (strs.length < maxArrayLength && this.remainingBytes > 0) {
420-
const str = this.consumeString(maxStringLength, encoding);
490+
const str = this.consumeString(maxStringLength, encoding, printable);
421491
if (str) {
422492
strs.push(str);
423493
}

0 commit comments

Comments
 (0)