Skip to content

Commit

Permalink
Initial support for ECMAScript modules (#239)
Browse files Browse the repository at this point in the history
This is not yet a full solution as full ES6 projects won't get
instrumented yet.
Loading projects that (partially) rely on ES6 won't crash anymore though

Note: A follow up PR will attempt to resolve the remaining problems

Authored-by: 0xricksanchez <christopher.krah@code-intelligence.com>
  • Loading branch information
0xricksanchez committed Dec 23, 2022
1 parent 10f70a5 commit 0048593
Show file tree
Hide file tree
Showing 22 changed files with 253 additions and 121 deletions.
6 changes: 1 addition & 5 deletions .github/workflows/run-all-tests.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
name: "🔍 Tests"

name: "\U0001F50D Tests"
on:
pull_request:
branches: [main]
workflow_dispatch:

jobs:
linting:
name: lint
Expand All @@ -25,7 +23,6 @@ jobs:
run: npm run build --workspace=@jazzer.js/fuzzer
- name: check formatting and linting
run: npm run check

unit_tests:
name: unit tests
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -63,7 +60,6 @@ jobs:
run: npm run build --workspace=@jazzer.js/fuzzer
- name: run all tests
run: npm run test

auto-merge:
needs:
- linting
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ timeout-*

# Editors
.idea
.vscode

# Build dir
dist
Expand Down
70 changes: 63 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
<div style="text-align: center;">
<img src="https://7466322.fs1.hubspotusercontent-na1.net/hubfs/7466322/Logos/CI%20Logos/Jazzer.js%20logo.png" height=150px alt="Jazzer.js logo">
</div>

# Jazzer.js
<div align="center">
<h1>Jazzer.js</h1>
<div style="text-align: center">
<img
src="https://7466322.fs1.hubspotusercontent-na1.net/hubfs/7466322/Logos/CI%20Logos/Jazzer.js%20logo.png"
height="150px"
alt="Jazzer.js logo"
/>
</div>
<hr />
<a href="https://img.shields.io/npm/v/@jazzer.js/core">
<img src="https://img.shields.io/npm/v/@jazzer.js/core"/>
</a>
<a href="https://github.com/CodeIntelligenceTesting/jazzer.js/actions/workflows/run-all-tests.yaml">
<img src="https://github.com/CodeIntelligenceTesting/jazzer.js/actions/workflows/run-all-tests.yaml/badge.svg?branch=main"/>
</a>
<a href="https://github.com/CodeIntelligenceTesting/jazzer.js/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/CodeIntelligenceTesting/jazzer.js"/>
</a>
<br />

<a href="https://www.code-intelligence.com/" target="_blank">Website</a> |
<a href="https://www.code-intelligence.com/blog" target="_blank">Blog</a> |
<a href="https://twitter.com/CI_Fuzz" target="_blank">Twitter</a>

[![NPM](https://img.shields.io/npm/v/@jazzer.js/core)](https://img.shields.io/npm/v/@jazzer.js/core)
![GitHub Actions](https://github.com/CodeIntelligenceTesting/jazzer.js/workflows/Tests/badge.svg)
</div>

Jazzer.js is a coverage-guided, in-process fuzzer for the
[Node.js](https://nodejs.org) platform developed by
Expand Down Expand Up @@ -37,6 +55,12 @@ To use Jazzer.js in your own project follow these few simple steps:
const fuzzerData = data.toString();
myAwesomeCode(fuzzerData);
};

// Alternatively, using ES6 syntax is also possible
export function fuzz(data /*: Buffer */) {
const fuzzerData = data.toString();
myAwesomeCode(fuzzerData);
}
```

3. Start the fuzzer using the fuzz target
Expand Down Expand Up @@ -119,6 +143,38 @@ as long as a modules 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).

#### ⚠️ Using Jazzer.js on pure ESM projects ⚠️

ESM bring a couple of challenges to the table, which are currently not fully
solved. Jazzer.js does have general ESM support as in your project should be
loaded properly. If your project internally still relies on calls to
`require()`, all of these dependencies will be hooked. However, _pure_
ECMAScript projects will currently not be instrumented!

One such example that Jazzer.js can handle just fine can be found at
[examples/protobufjs/fuzz.js](examples/protobufjs/fuzz.js):

```js
import proto from "protobufjs";
import { temporaryWriteSync } from "tempy";

export function fuzz(data: Buffer) {
try {
// Fuzz logic
} catch (e) {
// Handle expected error ogic here
}
}
```

You also have to adapt your `package.json` accordingly, by adding:

```json
{
"type": "module"
}
```

### Running the fuzzer

After adding `@jazzer.js/core` as dev-dependency to a project the fuzzer can be
Expand Down
4 changes: 4 additions & 0 deletions examples/done_callback/fuzz.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
* limitations under the License.
*/

/**
* @param { Buffer } data
* @param { Function } done
*/
module.exports.fuzz = function (data, done) {
if (data.length < 3) {
done();
Expand Down
51 changes: 0 additions & 51 deletions examples/done_callback/package-lock.json

This file was deleted.

3 changes: 2 additions & 1 deletion examples/jest_integration/integration.fuzz.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

/* eslint no-undef: 0, no-constant-condition: 0, @typescript-eslint/no-var-requires:0 */

const target = require("./target");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const target = await import("./target.js");

describe("My describe", () => {
it.fuzz("My fuzz test", (data) => {
Expand Down
1 change: 1 addition & 0 deletions examples/jest_integration/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "jazzerjs-jest-integration-example",
"version": "1.0.0",
"type": "module",
"description": "An example showing how Jazzer.js integrates with Jest",
"scripts": {
"test": "jest",
Expand Down
26 changes: 16 additions & 10 deletions examples/jest_integration/target.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
* limitations under the License.
*/

const fuzzMe = function (data) {
/**
* @param { Buffer } data
*/
export function fuzzMe(data) {
const s = data.toString();
if (s.length !== 16) {
return;
Expand All @@ -26,23 +29,26 @@ const fuzzMe = function (data) {
) {
throw Error("Welcome to Awesome Fuzzing!");
}
};
}

const callbackFuzzMe = function (data, done) {
/**
* @param { Buffer } data
* @param { Function } done
*/
export function callbackFuzzMe(data, done) {
// Use setImmediate here to unblock the event loop but still have better
// performance compared to setTimeout.
setImmediate(() => {
fuzzMe(data);
done();
});
};
}

const asyncFuzzMe = function (data) {
/**
* @param { Buffer } data
*/
export function asyncFuzzMe(data) {
return new Promise((resolve) => {
callbackFuzzMe(data, resolve);
});
};

module.exports.fuzzMe = fuzzMe;
module.exports.callbackFuzzMe = callbackFuzzMe;
module.exports.asyncFuzzMe = asyncFuzzMe;
}
32 changes: 32 additions & 0 deletions examples/jpeg_es6/fuzz.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// The code in this file is based on the examples available in JSFuzz:
// https://gitlab.com/gitlab-org/security-products/analyzers/fuzzers/jsfuzz/-/blob/34a694a8c73bfe0895c4e24784ba5b6dfe964b94/examples/jpeg/fuzz.js
// The original code is available under the Apache License 2.0.

// eslint-disable-next-line @typescript-eslint/no-var-requires
import { decode } from "jpeg-js";

/**
* @param { Buffer } data
*/
export function fuzz(data) {
try {
decode(data);
} catch (error) {
// Those are "valid" exceptions. we can't catch them in one line as
// jpeg-js doesn't export/inherit from one exception class/style.
if (!ignoredError(error)) throw error;
}
}

function ignoredError(error) {
return !!ignored.find((message) => error.message.indexOf(message) !== -1);
}

const ignored = [
"JPEG",
"length octect",
"Failed to",
"DecoderBuffer",
"invalid table spec",
"SOI not found",
];
19 changes: 19 additions & 0 deletions examples/jpeg_es6/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "jpeg-es6-fuzz",
"version": "1.0.0",
"type": "module",
"description": "",
"main": "fuzz.js",
"author": "",
"license": "ISC",
"dependencies": {
"jpeg-js": "^0.4.4"
},
"scripts": {
"fuzz": "jazzer fuzz -i jpeg-js -e nothing --sync",
"dryRun": "jazzer fuzz -i jpeg-js -e nothing --sync -- -runs=100 -seed=123456789"
},
"devDependencies": {
"@jazzer.js/core": "file:../../packages/core"
}
}
2 changes: 1 addition & 1 deletion examples/js-yaml/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es2015",
"target": "ES2022",
"module": "commonjs",
"moduleResolution": "node",
"allowJs": true,
Expand Down
7 changes: 3 additions & 4 deletions examples/maze/fuzz.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
// This example is inspired by the MazeFuzzer Example in Jazzer. See:
// https://github.com/CodeIntelligenceTesting/jazzer/blob/8c8e87b22645ba7681e72cef0caaf05bab492b75/examples/src/main/java/com/example/MazeFuzzer.java

// eslint-disable-next-line @typescript-eslint/no-var-requires
const jazzer = require("@jazzer.js/core").jazzer;
import { jazzer } from "@jazzer.js/core";

const mazeString = [
" ███████████████████",
Expand Down Expand Up @@ -50,7 +49,7 @@ mazeString.forEach((line) => maze.push(line.split("")));
/**
* @param { Buffer } commands
*/
module.exports.fuzz = function (commands) {
export function fuzz(commands) {
executeCommands(commands, (x, y, won) => {
if (won) {
throw "Jazzer.js has found the treasure";
Expand All @@ -65,7 +64,7 @@ module.exports.fuzz = function (commands) {
printMaze();
}
});
};
}

function executeCommands(commands, executeAndCheckResult) {
let x = 0;
Expand Down
1 change: 1 addition & 0 deletions examples/maze/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "jazzerjs-maze-example",
"type": "module",
"version": "1.0.0",
"description": "An example showing you can give Jazzer.js more feedback signals to reach deep program states",
"scripts": {
Expand Down
36 changes: 36 additions & 0 deletions examples/protobufjs/fuzz.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import proto from "protobufjs";
import { temporaryWriteSync } from "tempy";

/**
* @param { Buffer } data
*/
export function fuzz(data) {
try {
const file = temporaryWriteSync(data);
const root = proto.loadSync(file);
if (root.toString().length >= 30) {
console.log("== Input: " + data.toString() + "\n== " + root.toString());
}
} catch (e) {
if (
e.name !== "SyntaxError" &&
e.message &&
!e.message.includes("illegal token") &&
!e.message.includes("illegal string") &&
!e.message.includes("illegal path") &&
!e.message.includes("illegal comment") &&
!e.message.includes("illegal reference") &&
!e.message.includes("illegal name") &&
!e.message.includes("illegal type") &&
!e.message.includes("illegal value") &&
!e.message.includes("illegal service") &&
!e.message.includes("name must be a string") &&
!e.message.includes("path must be relative") &&
!e.message.includes("duplicate name") &&
!e.message.includes("Unexpected token") &&
!e.message.includes("Unexpected end")
) {
throw e;
}
}
}

0 comments on commit 0048593

Please sign in to comment.