Integrating a JavaScript project
The process of integrating a project written in JavaScript for Node.js with OSS-Fuzz is very similar to the general Setting up a new project process. The key specifics of integrating a JavaScript project are outlined below.
Jazzer.js
JavaScript fuzzing in OSS-Fuzz is powered by Jazzer.js, which is installed during the build step. As Jazzer.js operates directly on the JavaScript source code level, it can be applied to any project written in a language that can be transpiled into JavaScript such as TypeScript. More information on how Jazzer.js fuzz targets look like can be found in its README’s Usage section.
Project files
Example project
We recommend viewing javascript-example as an example of a simple JavaScript fuzzing project. We also recommend having a look at typescript-example as an example of how to fuzz TypeScript projects. This example also demonstrates how to use Jazzer.js fuzzed data provider.
project.yaml
The language
attribute must be specified as follows:
language: javascript
The only supported fuzzing engine is libFuzzer (libfuzzer
). So far, native sanitizers such as AddressSanitizer (address
) and UndefinedBehaviorSanitizer (undefined
) are not supported. They would only be needed for projects that have native addons, which is a rather infrequent use case for JavaScript projects. If you have a project where you need ASan or UBSan, please create open an issue on Jazzer.js GitHub repo. None (none
) is the default sanitizer for JavaScript projects, so setting it up in project.yaml
is optional.
fuzzing_engines:
- libfuzzer
sanitizers:
- none
Dockerfile
The Dockerfile should start by FROM gcr.io/oss-fuzz-base/base-builder-javascript
The OSS-Fuzz base Docker images already come with Node.js 19 and npm
pre-installed. Apart from that, you should usually not need to do more than to clone the project, set a WORKDIR
, and copy any necessary files, or install any project-specific dependencies here as you normally would.
Fuzzers
In the simplest case, every fuzzer consists of a single JavaScript file that exports a function named fuzz
taking a single argument of type Buffer. An example fuzz target could thus be a file fuzz_string_compare.js
with contents:
/**
* @param { Buffer } data
*/
module.exports.fuzz = function (data) {
const s = data.toString();
if (s.length !== 16) {
return;
}
if (
s.slice(0, 8) === "Awesome " &&
s.slice(8, 15) === "Fuzzing" &&
s[15] === "!"
) {
throw Error("Welcome to Awesome Fuzzing!");
}
};
build.sh
The OSS-Fuzz base docker image for JavaScript comes with the compile_javascript_fuzzer
script preinstalled. In build.sh
, you should install dependencies for your project, and if necessary compile the code into JavaScript. Then, you can use the script to build the fuzzers. The script ensures that @Jazzer.js/core is installed so that its CLI can be used to execute your fuzz tests. It also generates a wrapper script that can be used as a drop-in replacement for libFuzzer. This means that the generated script accepts the same command line flags for libFuzzer. Under the hood these flags are simply forwarded to the libFuzzer native addon used by Jazzer.js.
A usage example from the javascript-example project is
compile_javascript_fuzzer example fuzz_string_compare.js --sync
Arguments are:
- relative path of the project in the $SRC directory
- relative path to the fuzz test inside the project
- remaining arguments are forwarded to the Jazzer.js CLI
The javascript-example project contains an example of a build.sh
for JavaScript projects.
FuzzedDataProvider
Jazzer.js provides a FuzzedDataProvider
that can simplify the task of creating a fuzz target by translating the raw input bytes received from the fuzzer into useful primitive JavaScript types. Its functionality is similar to FuzzedDataProviders
available in other languages, such as Java and C++.
A fuzz target using the FuzzedDataProvider
would look as follows:
const { FuzzedDataProvider } = require("@jazzer.js/core");
/**
* @param { Buffer } fuzzerInputData
*/
module.exports.fuzz = function (fuzzerInputData) {
const data = new FuzzedDataProvider(fuzzerInputData);
const i = data.consumeIntegral(4);
const s = data.consumeRemainingAsString();
exploreMe(i, s);
};