This project is maintained by iankurgarg
The goal of this workshop is to use fuzzing to test a tool called marqdown
, which takes a markdown file, and generates a html rendering of a survey:
See marqdown in use at checkbox.io.
Fuzzing is a random testing techique. Fuzzing can be divided into “black-box” (dumb) and “white-box” (smart) approaches. In this workshop, we focus on “black-box” fuzzing. Generally, black-box fuzzing can be implemented in two ways:
Clone this repository and run npm install
.
We will be using a mutation approach in this workshop. To assist, two files have been provided, simple.md
, and test.md
, which are markdown files read by the program.
Running node main.js
will output:
passed 1000, failed 0, reduced 0
The program is simply reading an input file and for a 1000 times
var markDown = fs.readFileSync('test.md','utf-8');
//var markDown = fs.readFileSync('simple.md','utf-8');
for (var i = 0; i < 1000; i++) {
var mutuatedString = fuzzer.mutate.string(markDown);
try
{
marqdown.render(mutuatedString);
passedTests++;
}
catch(e)
{
failedTests.push( {input:mutuatedString, stack: e.stack} );
}
}
But the fuzzer right now is just returning the same string!
Now, we need to generate mutations to the input file in order to discover failures. Add the following functionality:
With 5% chance, reverse the input string.
Alternate between templates (simple.md/test.md)
With 25% chance, remove a random set of characters, from a random start position:
HINT: See Array.splice
With a 25% chance, insert random characters into the string HINT: See insert array into another
With a 5% chance, repeat.
See random-js for tips on using some helpful random utilities.
// for example, this will execute true for 5% of evaluations.
if( fuzzer.random.bool(0.05) )
Fuzzing may create many inputs that are exercising the same bug. A test suite minification step will attempt to discard test cases that are not any more effective. Use stack trace to help determine if you are triggering the same bug, then only save the minimum tests needed (Inside reducedTests
).
Now that you’ve generated some failing test cases, what can you do?
In a continuous deployment pipeline you could add a fuzzing component on new commits, and then reject them if you can generate failures. You might then generate a report, such as this:
The following commit: “Add new JSON5 parser” failed with the following exception. See attached test case.
SyntaxError: Expected ':' instead of '' JSON5.parse.error
at JSON5.parse.error (/Users/gameweld/workshops/Fuzzing/node_modules/json5/lib/json5.js:50:25)
at JSON5.parse.next (/Users/gameweld/workshops/Fuzzing/node_modules/json5/lib/json5.js:62:17)
at JSON5.parse.object (/Users/gameweld/workshops/Fuzzing/node_modules/json5/lib/json5.js:443:21)
at JSON5.parse.value (/Users/gameweld/workshops/Fuzzing/node_modules/json5/lib/json5.js:467:20)
at Object.parse (/Users/gameweld/workshops/Fuzzing/node_modules/json5/lib/json5.js:491:18)
at ProcessTokens (/Users/gameweld/workshops/Fuzzing/marqdown.js:287:22)
at Object.exports.render (/Users/gameweld/workshops/Fuzzing/marqdown.js:25:18)
at mutationTesting (/Users/gameweld/workshops/Fuzzing/main.js:60:22)
at Object.<anonymous> (/Users/gameweld/workshops/Fuzzing/main.js:84:1)
at Module._compile (module.js:460:26)
Consider a generative approach based on the grammar of markdown.