Tutorial: Perform fuzz testing in GitLab

Tier: Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated

Coverage-guided fuzz testing sends unexpected, malformed, or random data to your application, and then monitors your application for unstable behaviors and crashes.

This helps you discover bugs and potential security issues that other QA processes may miss.

You should use fuzz testing in addition to other security scanners and your own test processes. If you’re using GitLab CI/CD, you can run fuzz tests as part your CI/CD workflow.

To set up, configure, and perform coverage-guided fuzz testing using JavaScript in this tutorial, you:

  1. Fork the project template to create a project to run the fuzz tests in.
  2. Create the fuzz targets.
  3. Enable coverage-guided fuzz testing in your forked project.
  4. Run the fuzz test to identify security vulnerabilities.
  5. Fix any vulnerabilities identified by the fuzz test.

Fork the project template

First, to create a project to try out fuzz testing in, you must fork the fuzz-testing project template:

  1. Open the fuzz-testing project template.
  2. Fork the project template.
  3. When forking the project template:

You have successfully forked the fuzz-testing project template. Before you can start fuzz testing, remove the relationship between the project template and the fork:

  1. On the left sidebar, select Settings > General.
  2. Expand Advanced.
  3. In the Remove fork relationship section, select Remove fork relationship. Enter the name of the project when prompted.

Your project is ready and you can now create the fuzz test. Next you will create the fuzz targets.

Create the fuzz targets

Now you have a project for fuzz testing, you create the fuzz targets. A fuzz target is a function or program that, given an input, makes a call to the application being tested.

In this tutorial, the fuzz targets call a function of the my-tools.js file using a random buffer as a parameter.

To create the two fuzz target files:

  1. On the left sidebar, select Search or go to and find the fuzz-testing-demo project.
  2. Create a file in the root directory of the project.
  3. Name the file fuzz-sayhello.js and add the following code:

    let tools = require('./my-tools')
    
    function fuzz(buf) {
      const text = buf.toString()
      tools.sayHello(text)
    }
    
    module.exports = {
      fuzz
    }
    

    You can also copy this code from the fuzz-testing-demo/fuzzers/fuzz-sayhello.js project file.

  4. Name the Target Branch add-fuzz-test and write a descriptive commit message.
    • Do not select the Start a new merge request with these changes checkbox yet.
  5. Select Commit changes.
  6. Return to the root directory of the project.
  7. Make sure you are in the add-fuzz-test branch.
  8. Create the second file named fuzz-readme.js and add the following code:

    let tools = require('./my-tools')
    function fuzz(buf) {
        const text = buf.toString()
        tools.readmeContent(text)
    }
    module.exports = {
        fuzz
    }
    

    You can also copy this code from the fuzz-testing-demo/fuzzers/fuzz-readme.js project file.

  9. Write a descriptive commit message.
  10. Make sure the Target Branch is add-fuzz-test.
  11. Select Commit changes.

You now have two fuzz targets that can make calls to the application being tested. Next you will enable the fuzz testing.

Enable coverage-guided fuzz testing

To enable coverage-guided fuzz testing, create a CI/CD pipeline running the gitlab-cov-fuzz CLI to execute the fuzz test on the two fuzz targets.

To create the pipeline file:

  1. Make sure you are in the add-fuzz-test branch.
  2. In the root directory of the fuzz-testing-demo project, create a new file.
  3. Name the file .gitlab-ci.yml and add the following code:

    image: node:18
    
    stages:
      - fuzz
    
    include:
      - template: Coverage-Fuzzing.gitlab-ci.yml
    
    readme_fuzz_target:
      extends: .fuzz_base
      tags: [saas-linux-large-amd64] # Optional
      variables:
        COVFUZZ_ADDITIONAL_ARGS: '--fuzzTime=60'
      script:
        - npm config set @gitlab-org:registry https://gitlab.com/api/v4/packages/npm/ && npm i -g @gitlab-org/jsfuzz
        - ./gitlab-cov-fuzz run --engine jsfuzz -- fuzz-readme.js
    
    hello_fuzzing_target:
      extends: .fuzz_base
      tags: [saas-linux-large-amd64] # Optional
      variables:
        COVFUZZ_ADDITIONAL_ARGS: '--fuzzTime=60'
      script:
        - npm config set @gitlab-org:registry https://gitlab.com/api/v4/packages/npm/ && npm i -g @gitlab-org/jsfuzz
        - ./gitlab-cov-fuzz run --engine jsfuzz -- fuzz-sayhello.js
    

    This step adds the following to your pipeline: - A fuzz stage using a template. - Two jobs, readme_fuzz_target and hello_fuzzing_target. Each job runs using the jsfuzz engine, which reports unhandled exceptions as crashes.

    You can also copy this code from the fuzz-testing-demo/fuzzers/fuzzers.yml project file.

  4. Write a descriptive commit message.
  5. Make sure the Target Branch is add-fuzz-test.
  6. Select Commit changes.

You have successfully enabled coverage-guided fuzz testing. Next you will run the fuzz test using the pipeline you’ve just created.

Run the fuzz test

To run the fuzz test:

  1. On the left sidebar, select Code > Merge requests.
  2. Select New merge request.
  3. In the Source branch section, select the add-fuzz-test branch.
  4. In the Target branch section, make sure that your namespace and the main branch are selected.
  5. Select Compare branches and continue.
  6. Create the merge request.

Creating the merge request triggers a new pipeline, which runs the fuzz test. When the pipeline is finished running, you should see a security vulnerability alert on the merge request page.

To see more information on each vulnerability, select the individual Uncaught-exception links.

You have successfully run the fuzz test and identified vulnerabilities to fix.

Fix the vulnerabilities

The fuzz test identified two security vulnerabilities. To fix those vulnerabilities, you use the my-tools.js library.

To create the my-tools.js file:

  1. Make sure you are in the add-fuzz-test branch of the project.
  2. Go to the root directory of your project and open the my-tools.js file.
  3. Replace the contents of this file with the following code:

    const fs = require('fs')
    
    function sayHello(name) {
      if(name.includes("z")) {
        //throw new Error("😡 error name: " + name)
        console.log("😡 error name: " + name)
      } else {
        return "😀 hello " + name
      }
    }
    
    function readmeContent(name) {
    
      let fileName = name => {
        if(name.includes("w")) {
          return "./README.txt"
        } else {
          return "./README.md"
        }
      }
    
      //const data = fs.readFileSync(fileName(name), 'utf8')
      try {
        const data = fs.readFileSync(fileName(name), 'utf8')
        return data
      } catch (err) {
        console.error(err.message)
        return ""
      }
    
    }
    
    module.exports = {
      sayHello, readmeContent
    }
    

    You can also copy the code from the fuzz-testing-demo/javascript/my-tools.js project file.

  4. Select Commit changes. This triggers another pipeline to run another fuzz test.
  5. When the pipeline is finished, check the merge request Overview page. You should see that the security scan detected no new potential vulnerabilities.
  6. Merge your changes.

Congratulations, you’ve successfully run a fuzz test and fixed the identified security vulnerabilities!

For more information, see coverage-guided fuzz testing.