Skip to content

Migrating from Jazzer.js

Vitiate is a coverage-guided fuzzer built as a native Vitest plugin. Fuzz tests run alongside unit tests in the same runner with no separate toolchain required. See the Quickstart for prerequisites and installation.

Remove Jazzer.js packages and Jest dependencies (if they were only used for fuzzing):

Terminal window
npm uninstall @jazzer.js/core @jazzer.js/jest-runner jest ts-jest @types/jest

Install Vitiate:

Terminal window
npm install --save-dev vitiate

If your fuzz targets use FuzzedDataProvider for structured inputs, also install:

Terminal window
npm install --save-dev @vitiate/fuzzed-data-provider

Then clean up leftover configuration:

  • Remove any overrides or resolutions blocks in package.json that were added for Jazzer.js
  • Delete jest.config.fuzz.js (or whatever Jest config was used for fuzzing)
  • Delete fuzz/globals.d.ts or similar type declaration files for it.fuzz
  • Remove any custom npm scripts that wrap jest --config jest.config.fuzz.js or npx jazzer

Create or update vitest.config.ts with the Vitiate plugin:

import { defineConfig } from "vitest/config";
import { vitiatePlugin } from "@vitiate/core/plugin";
export default defineConfig({
plugins: [vitiatePlugin()],
test: {
projects: [
{ extends: true, test: { name: "unit", include: ["test/**/*.test.ts"] } },
{ extends: true, test: { name: "fuzz", include: ["test/**/*.fuzz.ts"] } },
],
},
});

The projects split keeps unit and fuzz tests in separate Vitest projects so they can have different configurations. Adjust the include globs to match your directory structure.

See Plugin Options for the full configuration reference.

Jazzer.js uses Jest’s describe/it.fuzz pattern. Vitiate uses a standalone fuzz() function:

Jazzer.js:

import "@jazzer.js/jest-runner";
import { parse } from "../src/parser.js";
describe("parser", () => {
it.fuzz("does not crash", (data: Buffer) => {
parse(data.toString());
});
});

Vitiate:

import { fuzz } from "@vitiate/core";
import { parse } from "../src/parser.js";
fuzz("parser does not crash", (data: Buffer) => {
parse(data.toString());
});

Key differences:

  • No describe wrapper needed - fuzz() is a top-level call
  • No import "@jazzer.js/jest-runner" - the plugin handles registration
  • The test name is the first argument to fuzz() (combine the describe and it.fuzz names if needed)

In both Jazzer.js and Vitiate, a fuzz target signals a bug by throwing. The standard pattern is to catch expected errors and let unexpected ones propagate:

fuzz("parse rejects gracefully", (data: Buffer) => {
try {
parse(data.toString());
} catch (error) {
if (error instanceof ParseError) {
return; // Expected - parser rejected invalid input
}
throw error; // Unexpected error - this is a bug
}
});

Any assertion approach works: throw, Node’s built-in assert, or third-party assertion libraries. Vitiate does not prescribe a specific assertion helper.

Vitest’s expect is API-compatible with Jest’s, so most assertion code works unchanged. Just update the import:

import { expect } from "vitest";
Jazzer.jsVitiate
it.fuzz.skip(...)fuzz.skip(...)
it.fuzz.only(...)fuzz.only(...)
-fuzz.todo("name")

See fuzz() API for the complete API reference.

The import path changes but the API is compatible - both follow the same LLVM FuzzedDataProvider design:

Jazzer.js:

import { FuzzedDataProvider } from "@jazzer.js/core";

Vitiate:

import { FuzzedDataProvider } from "@vitiate/fuzzed-data-provider";

See FuzzedDataProvider Reference for the full API.

Jazzer.js uses kebab-case names; Vitiate uses camelCase. Vitiate also adds detectors that Jazzer.js does not have.

Jazzer.js nameVitiate nameTierDefault
command-injectioncommandInjection1On
path-traversalpathTraversal1On
-unsafeEval1On
prototype-pollutionprototypePollution2Off
-redos2Off
-ssrf2Off

Tier 1 detectors are enabled by default. Tier 2 detectors must be explicitly enabled because they hook sensitive APIs and may produce false positives.

If your Jazzer.js config disabled specific detectors, translate the names to camelCase in your Vitiate configuration. See Vulnerability Detectors for usage and Detectors Reference for the full configuration API.

Run npx vitiate init to create seed directories for all discovered fuzz tests:

Terminal window
npx vitiate init

This creates .vitiate/testdata/<hashdir>/seeds/ directories based on your fuzz test names.

If you have crash regressions or seed inputs from Jazzer.js, copy them into the new structure:

Terminal window
# Copy crash regressions
cp old-crashes/* .vitiate/testdata/<hashdir>/crashes/
# Copy seed inputs
cp old-seeds/* .vitiate/testdata/<hashdir>/seeds/

The <hashdir> is a hash-based directory name shown by npx vitiate init. Each test gets its own directory. Timeout artifacts go in the sibling timeouts/ directory.

# Vitiate cached corpus (regenerated by the fuzzer)
.vitiate/corpus/
# SWC WASM plugin compilation cache (created by Vitiate's instrumentation)
.swc/

The .swc/ directory contains platform-specific compiled WASM plugin artifacts that SWC creates during instrumentation. It is safe to delete and will be recreated on the next run. Jazzer.js does not create this directory since it uses a different instrumentation approach.

Commit .vitiate/testdata/ to version control - it contains your crash regressions and seed inputs.

Remove Jazzer.js corpus directories once migration is verified:

  • .cifuzz-corpus/ (Jazzer.js default corpus directory)
  • Any custom corpus or crash directories from your old Jest config
  • Old jest.config.fuzz.js references in CI scripts

See Corpus and Regression Testing for details on how Vitiate manages inputs.

Old (Jazzer.js)New (Vitiate)
jest --config jest.config.fuzz.jsnpx vitiate regression
npx jazzer target.fuzz.tsnpx vitiate fuzz
npx jazzer -m=regression target.fuzz.tsnpx vitiate regression
npx jazzer target.fuzz.ts -- -max_total_time=300npx vitiate fuzz --fuzz-time 300

See CLI Flags for the complete command reference.

Before:

- run: npx jest --config jest.config.fuzz.js

After:

- run: npx vitiate regression

Before:

- run: npx jazzer target.fuzz.ts -- -max_total_time=3600

After:

- run: npx vitiate fuzz --fuzz-time 3600

Update crash artifact paths in any CI steps that upload or archive them - Vitiate stores crashes under .vitiate/testdata/<hashdir>/crashes/ instead of Jazzer.js’s flat output directory.

See CI Fuzzing for complete GitHub Actions examples including corpus caching and crash artifact upload.

  1. Install dependencies: npm ci completes without errors
  2. Unit tests pass: npx vitest run (regression mode runs fuzz tests against any existing corpus)
  3. Regression tests pass: npx vitiate regression replays all committed crash artifacts and seeds
  4. Smoke fuzz: npx vitiate fuzz --fuzz-time 30 runs for 30 seconds without errors