Coder Social home page Coder Social logo

fuzzing's Introduction

Fuzzing

In this workshop, we'll learn about techniques related to random testing called fuzzing, and its close relationship to mutation testing.

Setup

Before you get started

Clone this repo locally and install dependencies for our code.

npm install

Fuzzing Concepts

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:

  1. generative: test input is randomly created. Generation can be guided by grammars or other domain knowledge. This approach is commonly used for security testing.
  2. mutation: test input is randomly modified. The test input can be existing templates, input files, or captured network traffic that is replayed. Imagine you were testing Microsoft Word and you had a 200 page document. If you randomly made changes to the document and attempted to open it with Wordβ€”chances are you might be able to discover a bug.

Workshop

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 πŸ—Ή.

For example, the following markdown would be translated from: πŸ“„


What is your favorite pet?
> {rows:3}

### Install

![NPM version](https://badge.fury.io/js/marked.png)

Did running this command work for you?

``` bash
npm install marked --save
```
1. Yes
2. No

### Upload a screenshot
> {upload: true, name: 'hello'}

to: πŸ—Ή

checkbox rendered


See marqdown in use at checkbox.io.

Getting Started

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.

const mtfuzz = require('./lib/driver').mtfuzz;
const fs = require('fs');

// Code under test...
const marqdown = require('./test/marqdown');

// Seed inputs
let mdA = fs.readFileSync('test/test.md','utf-8');
let mdB = fs.readFileSync('test/simple.md','utf-8');

let args = process.argv.slice(2);
const runs = args.length > 0 ? args[0] : 1000;

// Fuzz function 1000 (or given) times, with given seed string inputs.
mtfuzz(runs, [mdA, mdB], (md) => marqdown.render(md) );

Running node index.js will output something like this:

Fuzzing '(md) => marqdown.render(md)' with 1000 randomly generated-inputs.

Finished 1000 runs.
passed: 1000, exceptions: 0, faults: 0

Discovered faults.

The mtfuzz function will run 1000 times, each time:

  • Picking one of the seed inputs.
  • Applying a mutation calling mutater.str(s) to randomly change the string.
  • Passing the random input to system under test, and simply checking for exceptions being thrown (our test oracle).

The code for these steps can be seen in lib/driver.js, which essentially looks something like this:

function mtfuzz(iterations, seeds, testFn)
{    
    var failedTests = [];
    var passedTests = 0;

    mutater.seed(0);
    
    for (var i = 1; i <= iterations; i++) {

        // Toggle between seed files
        let idx = ((i % seeds.length) + seeds.length) % seeds.length;

        // apply random mutation to seed input
        let s = seeds[ idx ];
        let mutuatedString = mutater.str(s);
        
        // run given function under test with input
        try
        {
            testFn(mutuatedString);
            passedTests++;
        }
        catch(e)
        {
            failedTests.push( {input:mutuatedString, stack: e.stack} );
        }
    }
    ...

Fuzzing Seed Inputs

Right now, inside mtfuzz, the call mutater.str(s) is just returning the same string s!

It will be our job to modify this function in order to try all sorts of interesting ways to randomly change a string. And as a consquence, see if we can reveal faults in the code (i.e. Exceptions being thrown).

We will be adding the following functionality, while changing the effects it has on discovering faults:

  1. With 25% chance, remove a random set of characters, using array.splice from a random start position.

HINT: You can use the call mutator.random().integer(0,num) to randomly generate a number in a given range.

πŸ•β€πŸ¦Ί: Run the code, did you see any faults?

  1. With a 25% chance, insert random characters into the string.

HINT: Consider using array.splice with spread operator: array.splice( rand_position, 0, ...rand_string). HINT: See mutator.random().string(10) for creating a random string of length 10.

πŸ•β€πŸ¦Ί: Run the code, did you see any new faults?

  1. With a 50% chance, replace any single quote strings with a random integer.

    HINT: You can use a regex match/replace call like val = val.replace(/'\w+'/g, randnum);

πŸ•β€πŸ¦Ί: Run the code, did you see any new faults?

  1. With a 25% chance, steps 1 and 2 (add a do/while loop).

πŸ•β€πŸ¦Ί: Run the code, did you see any new faults?

See random-js for tips on using some helpful random utilities.

// for example, this will execute true for ~5% of evaluations.
if( mutator.random.bool(0.05) )

Note: Fuzzing may create many inputs that are revealing the same failure. This program attempts to reduce duplicate failures by discarding exceptions that reveal a failure location already found.

var trace = failed.stack.split("\n");
var msg = trace[0];
var at = trace[1];

if( !reduced.hasOwnProperty( at ) )
{
    reduced[at] = `${chalk.red(msg)}\nFailed with input: .mutations/${failed.id}.txt\n${chalk.grey(failed.stack)}`; 
}

Experiments

  1. πŸ”™ Before your do while loop, make a change to always reverse the input string (simply call array.reverse(), which will change the array in memory).

    πŸ€” Do you think this will make the code find more faults or less? Why?

    Revert the change when done with experiment.

  2. βš–οΈ Increase the number of iterations run from 1000 to 15000. Did you find any new faults? Try with an even larger number, 100000. Did that make a difference? Why do you think changing the number of runs might help reveal more faults (or not)?

    HINT: You can simply pass the number of iterations as an argument: node index.js 15000.

lib/mutate.js

Make sure to click the "save" icon each time you edit the file.

function mutateString (mutator, val) {

    // Step 3. Replace single quotes strings with integers

    var array = val.split('');

    if( mutator.random().bool(0.25) )
    {
        // Step 1. Randomly remove a random set of characters, from a random start position.
    }
    if( mutator.random().bool(0.25) )
    {
        // Step 2. Randomly add a set of characters.
    }

    return array.join('');
}

exports.mutateString = mutateString;

You can run node index.js to see what effects your mutations has on the code!

Integrating into pipeline stage

There are several ways this can be added into a pipeline stage. In a continuous deployment pipeline you could add a stage for fuzz testing. If the built program could receive input, then fuzzing could simply performed by sending input into the binary.

Alternatively, you could run fuzzing on specific methods with a codebase. One strategy would involve tagging code with a @Fuzz attribute, which could then be processed by a fuzzing program. Therefore, the fuzzing tool would generate random inputs for all the given methods, looking for failures.

@Fuzz
private void exampleFunction2(String sTest, DataClass[] daTest) {
    System.out.println("\tRunning with values: " + sTest + ", " + Arrays.toString(daTest));
}

An previous undergrad student at NCSU, Johnny Rockett, built such a tool for Java/Maven, available here: https://github.com/johnnyrockett/ROG-Fuzzer

Minification

In order to make revealing a fault easier, it is possible to automatically reduce the input, by systematically deleting the input and checking to see if the fault is still revealed. Typically, the process involves deleting parts of the input, until the smallest version of the input that still produces the error remains.

An effective tool for doing this is creduce:

https://embed.cs.utah.edu/creduce/

Mutation Testing

How is fuzzing related to mutation testing?

Well, rather than randomly mutate inputs for a program, we can simply randomly mutate the program itself. We then check to see if our test suite will pass or fail on the faulty version of the program.

fuzzing's People

Contributors

chrisparnin avatar ssmirr avatar

Forkers

ovieu

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.