# Fuzzing using fuzz targets and the CLI Creating fuzz targets and executing those via CLI commands is straightforward and similar to what you would expect from other fuzzers. How to do so is described in detail in the following sections. ## Setting up Jazzer.js Before you can use Jazzer.js, you have to add the required dependency `@jazzer.js/core` to your project. To do so, execute the following command in your project root directory. ```shell npm install --save-dev @jazzer.js/core ``` This will install Jazzer.js and all required dependencies in your project. ## Creating a fuzz target Jazzer.js requires an entry point for the fuzzer, this is commonly referred to as fuzz target. A simple example is shown below. ```js module.exports.fuzz = function (data) { myAwesomeCode(data.toString()); }; ``` A fuzz target module needs to export a function called `fuzz`, which takes a `Buffer` parameter and executes the actual code under test. The `Buffer`, a subclass of `Uint8Array`, can be used to create needed parameters for the actual code under test. However, `Buffer` is not the nicest abstraction to work with. For that reason, Jazzer.js provides the wrapper class `FuzzedDataProvider`, which allows reading primitive types from the `Buffer`. An example on how to use the fuzzer input with the `FuzzedDataProvider` class is shown below. ```js const { FuzzedDataProvider } = require("@jazzer.js/core"); module.exports.fuzz = function (fuzzerInputData) { const data = new FuzzedDataProvider(fuzzerInputData); const intParam = data.consumeIntegral(4); const stringParam = data.consumeString(4, "utf-8"); myAwesomeCode(intParam, stringParam); }; ``` For more information on how to use the `FuzzedDataProvider` class, please refer to the [example](../examples/FuzzedDataProvider/fuzz.js), the [tests](../packages/core/FuzzedDataProvider.test.ts), and the [implementation](../packages/core/FuzzedDataProvider.ts) of the `FuzzedDataProvider` class. ### Fuzz target execution modes Jazzer.js supports asynchronous fuzz targets out of the box, no special handling or configuration is needed. #### Promise based execution The resolution of a `Promise`, returned by a fuzz target, is awaited before the next fuzzing input is provided. This enables the fuzzing of `async`/`await` and `Promise` based code. An example of a `Promise` based fuzz target can be found at [tests/promise/fuzz.js](../tests/promise/fuzz.js). #### Done callback based execution If the fuzz target takes a callback function as second parameter, the fuzzer will await its invocation before providing the next input. Invoking the callback function without a parameter indicates a successful execution, whereas invoking it with a parameter indicates a failure. In the error case, the passed in object is normally of type `string` or `Error` and used during reporting of the test execution. An example of a done callback based fuzz target can be found at [tests/done_callback/fuzz.js](../tests/done_callback/fuzz.js). #### Synchronous execution Asynchronous code needs careful synchronization between the [Node.js Event Loop](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/) and the fuzzing thread, hence, provides a lower throughput compared to synchronous fuzzing. Despite that, asynchronous fuzzing is the default mode of Jazzer.js due to its prevalence in the JavaScript ecosystem and because it works for all fuzz targets. Solely synchronous code can participate in the enhanced performance of synchronous fuzzing by setting the `--sync` flag when starting the fuzzer. ### Using TypeScript to write fuzz targets It is also possible to use [TypeScript](https://www.typescriptlang.org), or in that matter any other language transpiling to JavaScript, to write fuzz targets, as long as a module exporting a `fuzz` function is generated. An example on how to use TypeScript to fuzz a library can be found at [examples/js-yaml/package.json](../examples/js-yaml/package.json). **Note**: Directly executing fuzz targets written in TypeScript is **NOT** supported! However, it is possible to use the [Jest integration](jest-integration.md) to execute Jest fuzz tests written in TypeScript. ### ESM support Jazzer.js instruments ES modules via a [Node.js loader hook](https://nodejs.org/api/module.html#customization-hooks) (`module.register`). Coverage counters, compare hooks, and function hooks all work on ESM code — the fuzzer sees the same feedback it gets from CJS modules. **Requirements:** Node.js >= 20.6. Function hooks (bug detectors) additionally require Node.js >= 20.11 (for `transferList` support in `module.register`). On older Node versions, ESM loading still works but modules are not instrumented. #### Minimal ESM fuzz target ```js // fuzz.js (or fuzz.mjs) import { parseInput } from "my-library"; export function fuzz(data) { parseInput(data.toString()); } ``` ```json { "type": "module", "main": "fuzz.js", "scripts": { "fuzz": "jazzer fuzz -i my-library corpus" }, "devDependencies": { "@jazzer.js/core": "^4.0.0" } } ``` ```shell npm run fuzz ``` The `-i` flag tells Jazzer.js which packages to instrument. Without it, everything outside `node_modules` is instrumented by default. #### Direct ESM vs. Jest ESM — when to use which There are two ways to fuzz ESM code with Jazzer.js: | | Direct (`npx jazzer`) | Jest (`@jazzer.js/jest-runner`) | | ----------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------ | | **How ESM is instrumented** | Loader hook (`module.register`) — native ESM stays ESM | Babel transform via `jest.config` — ESM is converted to CJS at test time | | **Node.js requirement** | >= 20.6 (function hooks: >= 20.11) | Any supported Node.js | | **Fuzz target format** | `export function fuzz(data)` in a `.js`/`.mjs` file | `it.fuzz(name, fn)` inside a `.fuzz.cjs` test file | | **Async targets** | Works out of the box (default mode) | Works out of the box | | **Regression testing** | `--mode=regression` replays the corpus | Default Jest mode replays corpus seeds automatically | | **IDE integration** | None (CLI only) | VS Code / IntelliJ run individual inputs | | **Multiple targets per file** | No — one exported `fuzz` function per file | Yes — multiple `it.fuzz()` blocks in one file | | **Corpus management** | Manual directory, passed as positional arg | Automatic per-test directories | **Rule of thumb:** use the Jest integration when you want multiple fuzz tests in one file, IDE debugging, or need to support Node < 20.6. Use direct ESM when you want a minimal setup with no Babel/Jest indirection — just your `.mjs` target and `npx jazzer`. #### Jest ESM setup (Babel transform approach) When fuzzing an ESM library through Jest, the fuzz tests themselves must be CJS (`.fuzz.cjs`), and Jest's Babel transform converts the library's ESM imports to `require()` calls so that Jazzer.js's CJS instrumentation hooks can intercept them: ```js // jest.config.cjs module.exports = { projects: [ { testRunner: "@jazzer.js/jest-runner", testMatch: ["/fuzz/**/*.fuzz.cjs"], transform: { "\\.js$": [ "babel-jest", { plugins: ["@babel/plugin-transform-modules-commonjs"] }, ], }, transformIgnorePatterns: ["/node_modules/"], }, ], }; ``` This requires `@babel/core`, `babel-jest`, and `@babel/plugin-transform-modules-commonjs` as dev dependencies. ## Running the fuzz target After adding `@jazzer.js/core` as a `dev-dependency` to a project, the fuzzer can execute a fuzz target using the `jazzer` npm command. To do so, use `npx`: ```shell npx jazzer ``` Or add a new script to your `package.json`: ```json "scripts": { "fuzz": "jazzer " } ``` Inputs triggering issues, like uncaught exceptions, timeouts, etc., are stored in the current working directory with an auto-generated name. The general command format is: ```text jazzer [corpus...] [-- ] ``` Detailed documentation and some example calls are available on the command line using the `--help` flag. In addition, every argument is described in [fuzz-settings.md](./fuzz-settings.md) in more detail. Here we list some of the most important parameters: | Parameter | Description | | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `` | Import path to the fuzz target module. | | `[corpus...]` | Paths to the corpus directories. If not given, no initial seeds are used nor interesting inputs saved. | | `-f`, `--fuzzEntryPoint` | Name of the fuzz test entry point. It must be an exported function with a single [Buffer](https://nodejs.org/api/buffer.html) parameter. Default is `fuzz`. | | `-i`, `--includes` / `-e`, `--excludes` | Part of filepath names to include/exclude in the instrumentation. A tailing `/` should be used to include directories and prevent confusion with filenames. `*` can be used to include all files. Can be specified multiple times. Default will include everything outside the `node_modules` directory. If either of these flags are set the default value for the other is ignored. | | `--sync` | Enables synchronous fuzzing. **May only be used for entirely synchronous code**. | | `-h`, `--customHooks` | Filenames with custom hooks. Several hooks per file are possible. See further details in [docs/fuzz-settings.md](fuzz-settings.md#customhooks--arraystring). | | `--help` | Detailed help message containing all flags. | | `-- ` | Parameters after `--` are forwarded to the internal fuzzing engine (`libFuzzer`). Available settings can be found in its [options documentation](https://www.llvm.org/docs/LibFuzzer.html#options). | ## Coverage report generation To generate a coverage report, add the `--coverage` flag to the Jazzer.js CLI. In the following example, the `--coverage` flag is combined with the mode flag `--mode=regression` that only uses existing corpus entries without performing any fuzzing. ```shell npx jazzer --mode=regression --coverage -- ``` Alternatively, you can add a new script to your `package.json`: ```json "scripts": { "coverage": "jazzer --mode=regression -i fileToInstrument -i anotherFileToInstrument --coverage " } ``` Files matched by the flags `--includes` or `--customHooks`, and not matched by the flag `--excludes` will be included in the coverage report. It is recommended to disable coverage report generation during fuzzing, because of the substantial overhead that it adds. ### Coverage report directory By default, the coverage reports can be found in the `./coverage` directory. This default directory can be changed by setting the flag `--coverageDirectory=`. ### Coverage reporters The desired report format can be set by the flag `--coverageReporters`, which by default is set to `--coverageReporters json text lcov clover`. See [here](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) for a list of supported coverage reporters.