From dc2f5f4be871337f8840d828cbe6dffee42f3d26 Mon Sep 17 00:00:00 2001 From: Khaled Yakdan Date: Tue, 31 Jan 2023 08:31:54 -0800 Subject: [PATCH] Integrate Jazzer.js (#9466) This PR enables using Jazzer.js for fuzzing Node.js projects in OSS-Fuzz. Part of #8324 --------- Co-authored-by: jonathanmetzman <31354670+jonathanmetzman@users.noreply.github.com> --- .github/workflows/project_tests.yml | 1 + .../new-project-guide/javascript_lang.md | 140 ++++++++++++++++++ infra/base-images/base-builder/Dockerfile | 3 +- infra/base-images/base-builder/compile | 15 ++ .../base-builder/compile_javascript_fuzzer | 42 ++++++ .../base-builder/install_javascript.sh | 6 + infra/base-images/base-runner/Dockerfile | 3 + infra/base-images/base-runner/bad_build_check | 13 ++ .../base-runner/install_javascript.sh | 21 +++ infra/helper.py | 5 +- projects/javascript-example/Dockerfile | 30 ++++ projects/javascript-example/build.sh | 24 +++ projects/javascript-example/fuzz_promise.js | 51 +++++++ .../javascript-example/fuzz_string_compare.js | 32 ++++ .../fuzz_value_profiling.js | 39 +++++ projects/javascript-example/package.json | 5 + projects/javascript-example/project.yaml | 12 ++ projects/typescript-example/Dockerfile | 30 ++++ projects/typescript-example/build.sh | 29 ++++ .../typescript-example/fuzz_explore_me.ts | 27 ++++ projects/typescript-example/package.json | 12 ++ projects/typescript-example/project.yaml | 12 ++ projects/typescript-example/target.ts | 8 + projects/typescript-example/tsconfig.json | 18 +++ 24 files changed, 576 insertions(+), 2 deletions(-) create mode 100644 docs/getting-started/new-project-guide/javascript_lang.md create mode 100755 infra/base-images/base-builder/compile_javascript_fuzzer create mode 100755 infra/base-images/base-runner/install_javascript.sh create mode 100644 projects/javascript-example/Dockerfile create mode 100755 projects/javascript-example/build.sh create mode 100644 projects/javascript-example/fuzz_promise.js create mode 100644 projects/javascript-example/fuzz_string_compare.js create mode 100644 projects/javascript-example/fuzz_value_profiling.js create mode 100644 projects/javascript-example/package.json create mode 100644 projects/javascript-example/project.yaml create mode 100644 projects/typescript-example/Dockerfile create mode 100755 projects/typescript-example/build.sh create mode 100644 projects/typescript-example/fuzz_explore_me.ts create mode 100644 projects/typescript-example/package.json create mode 100644 projects/typescript-example/project.yaml create mode 100644 projects/typescript-example/target.ts create mode 100644 projects/typescript-example/tsconfig.json diff --git a/.github/workflows/project_tests.yml b/.github/workflows/project_tests.yml index c82aaf149..104602921 100644 --- a/.github/workflows/project_tests.yml +++ b/.github/workflows/project_tests.yml @@ -19,6 +19,7 @@ jobs: engine: - libfuzzer sanitizer: + - none - address - memory - undefined diff --git a/docs/getting-started/new-project-guide/javascript_lang.md b/docs/getting-started/new-project-guide/javascript_lang.md new file mode 100644 index 000000000..3f6895fbf --- /dev/null +++ b/docs/getting-started/new-project-guide/javascript_lang.md @@ -0,0 +1,140 @@ +--- +layout: default +title: Integrating a JavaScript project +parent: Setting up a new project +grand_parent: Getting started +nav_order: 4 +permalink: /getting-started/new-project-guide/javascript-lang/ +--- + +# Integrating a JavaScript project +{: .no_toc} + +- TOC +{:toc} +--- + +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]({{ site.baseurl }}/getting-started/new-project-guide/) +process. The key specifics of integrating a JavaScript project are outlined below. + +## Jazzer.js + +JavaScript fuzzing in OSS-Fuzz is powered by +[Jazzer.js](https://github.com/CodeIntelligenceTesting/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](https://github.com/CodeIntelligenceTesting/jazzer.js#usage). + +## Project files + +### Example project + +We recommend viewing +[javascript-example](https://github.com/google/oss-fuzz/tree/master/projects/javascript-example) +as an example of a simple JavaScript fuzzing project. We also recommend having a look at +[typescript-example](https://github.com/google/oss-fuzz/tree/master/projects/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: + +```yaml +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](https://github.com/CodeIntelligenceTesting/jazzer.js). None (`none`) is the default sanitizer for +JavaScript projects, so setting it up in `project.yaml` is optional. + +```yaml +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](https://nodejs.org/api/buffer.html). +An example fuzz target could thus be a file `fuzz_string_compare.js` with contents: + +```javascript +/** + * @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](https://github.com/google/oss-fuzz/blob/master/infra/base-images/base-builder/compile_javascript_fuzzer) 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](https://www.npmjs.com/package/@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 + +```shell +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](https://github.com/CodeIntelligenceTesting/jazzer.js/blob/main/docs/fuzz-targets.md#running-the-fuzz-target) + +The [javascript-example](https://github.com/google/oss-fuzz/blob/master/projects/javascript-example/build.sh) +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](https://codeintelligencetesting.github.io/jazzer-docs/jazzer-api/com/code_intelligence/jazzer/api/FuzzedDataProvider.html) and +[C++](https://github.com/google/fuzzing/blob/master/docs/split-inputs.md). + +A fuzz target using the `FuzzedDataProvider` would look as follows: + +```javascript +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); +}; +``` diff --git a/infra/base-images/base-builder/Dockerfile b/infra/base-images/base-builder/Dockerfile index 841076a0d..ce8dad1cc 100644 --- a/infra/base-images/base-builder/Dockerfile +++ b/infra/base-images/base-builder/Dockerfile @@ -143,13 +143,14 @@ RUN precompile_centipede COPY cargo compile compile_afl compile_libfuzzer compile_honggfuzz \ compile_centipede \ compile_go_fuzzer \ + compile_javascript_fuzzer \ compile_native_go_fuzzer \ compile_python_fuzzer \ compile_fuzztests.sh \ python_coverage_helper.py \ debug_afl srcmap \ write_labels.py bazel_build_fuzz_tests \ - # Go, java, and swift installation scripts. + # Go, JavaScript, Java, Python, Rust, and Swift installation scripts. install_go.sh \ install_javascript.sh \ install_java.sh \ diff --git a/infra/base-images/base-builder/compile b/infra/base-images/base-builder/compile index f30da3f5c..5144f4cc9 100755 --- a/infra/base-images/base-builder/compile +++ b/infra/base-images/base-builder/compile @@ -37,6 +37,21 @@ if [ "$FUZZING_LANGUAGE" = "jvm" ]; then fi fi +if [ "$FUZZING_LANGUAGE" = "javascript" ]; then + if [ "$FUZZING_ENGINE" != "libfuzzer" ]; then + echo "ERROR: JavaScript projects can be fuzzed with libFuzzer engine only." + exit 1 + fi + if [ "$SANITIZER" != "coverage" ] && [ "$SANITIZER" != "none" ]; then + echo "ERROR: JavaScript projects cannot be fuzzed with sanitizers." + exit 1 + fi + if [ "$ARCHITECTURE" != "x86_64" ]; then + echo "ERROR: JavaScript projects can be fuzzed on x86_64 architecture only." + exit 1 + fi +fi + if [ "$FUZZING_LANGUAGE" = "python" ]; then if [ "$FUZZING_ENGINE" != "libfuzzer" ]; then echo "ERROR: Python projects can be fuzzed with libFuzzer engine only." diff --git a/infra/base-images/base-builder/compile_javascript_fuzzer b/infra/base-images/base-builder/compile_javascript_fuzzer new file mode 100755 index 000000000..42b47fc3c --- /dev/null +++ b/infra/base-images/base-builder/compile_javascript_fuzzer @@ -0,0 +1,42 @@ +#!/bin/bash -eu +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +project=$1 +# Path the fuzz target source file relative to the project's root. +fuzz_target=$2 +# Arguments to pass to Jazzer.js +jazzerjs_args=${@:3} + +# Copy source code into the $OUT directory and install Jazzer.js into the project. +if [ ! -d $OUT/$project ]; then + pushd $SRC/$project + npm install @jazzer.js/core + popd + cp -r $SRC/$project $OUT/$project +fi + +fuzzer_basename=$(basename -s .js $fuzz_target) +fuzzer_dir=$(dirname $fuzz_target) + +# Create an execution wrapper that executes Jazzer.js with the correct arguments. +echo "#!/bin/bash +# LLVMFuzzerTestOneInput so that the wrapper script is recognized as a fuzz target for 'check_build'. +this_dir=\$(dirname \"\$0\") +cd $project/$fuzzer_dir +\$this_dir/$project/node_modules/.bin/jazzer $fuzzer_basename $jazzerjs_args -- \$@" > $OUT/$fuzzer_basename + +chmod +x $OUT/$fuzzer_basename diff --git a/infra/base-images/base-builder/install_javascript.sh b/infra/base-images/base-builder/install_javascript.sh index c8d06c832..ab2784721 100755 --- a/infra/base-images/base-builder/install_javascript.sh +++ b/infra/base-images/base-builder/install_javascript.sh @@ -14,3 +14,9 @@ # limitations under the License. # ################################################################################ +# Install Node.js v19.x +curl -fsSL https://deb.nodesource.com/setup_19.x | bash - +apt-get update && apt-get install -y nodejs + +# Install latest versions of npm +npm install --global npm diff --git a/infra/base-images/base-runner/Dockerfile b/infra/base-images/base-runner/Dockerfile index bc034e198..963d524b2 100755 --- a/infra/base-images/base-runner/Dockerfile +++ b/infra/base-images/base-runner/Dockerfile @@ -82,6 +82,9 @@ RUN wget https://repo1.maven.org/maven2/org/jacoco/org.jacoco.cli/0.8.7/org.jaco echo "37df187b76888101ecd745282e9cd1ad4ea508d6 /opt/jacoco-agent.jar" | shasum --check && \ echo "c1814e7bba5fd8786224b09b43c84fd6156db690 /opt/jacoco-cli.jar" | shasum --check +COPY install_javascript.sh / +RUN /install_javascript.sh && rm /install_javascript.sh + # Do this last to make developing these files easier/faster due to caching. COPY bad_build_check \ coverage \ diff --git a/infra/base-images/base-runner/bad_build_check b/infra/base-images/base-runner/bad_build_check index 542b386ad..412175b19 100755 --- a/infra/base-images/base-runner/bad_build_check +++ b/infra/base-images/base-runner/bad_build_check @@ -315,6 +315,13 @@ function check_mixed_sanitizers { return 0 fi + if [ "${FUZZING_LANGUAGE:-}" = "javascript" ]; then + # Jazzer.js currently does not support using sanitizers with native Node.js addons. + # This is not relevant anyways since supporting this will be done by preloading + # the sanitizers in the wrapper script starting Jazzer.js. + return 0 + fi + if [ "${FUZZING_LANGUAGE:-}" = "python" ]; then # Sanitizer runtime is loaded via LD_PRELOAD, so this check does not apply. return 0 @@ -402,6 +409,12 @@ function check_architecture { return 0; fi + if [ "${FUZZING_LANGUAGE:-}" = "javascript" ]; then + # Jazzer.js fuzzers are wrapper scripts that start the fuzz target with + # the Jazzer.js CLI. + return 0; + fi + if [ "${FUZZING_LANGUAGE:-}" = "python" ]; then FUZZER=${FUZZER}.pkg fi diff --git a/infra/base-images/base-runner/install_javascript.sh b/infra/base-images/base-runner/install_javascript.sh new file mode 100755 index 000000000..7985df71a --- /dev/null +++ b/infra/base-images/base-runner/install_javascript.sh @@ -0,0 +1,21 @@ +#!/bin/bash -eux +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ +# Install Node.js v19.x. +apt-get update && apt-get install -y curl + +curl -fsSL https://deb.nodesource.com/setup_19.x | bash - +apt-get update && apt-get install -y nodejs diff --git a/infra/helper.py b/infra/helper.py index dc9d23033..8dbf9c7a2 100755 --- a/infra/helper.py +++ b/infra/helper.py @@ -172,7 +172,10 @@ def main(): # pylint: disable=too-many-branches,too-many-return-statements # We have different default values for `sanitizer` depending on the `engine`. # Some commands do not have `sanitizer` argument, so `hasattr` is necessary. if hasattr(args, 'sanitizer') and not args.sanitizer: - args.sanitizer = constants.DEFAULT_SANITIZER + if args.project.language == 'javascript': + args.sanitizer = 'none' + else: + args.sanitizer = constants.DEFAULT_SANITIZER if args.command == 'generate': result = generate(args) diff --git a/projects/javascript-example/Dockerfile b/projects/javascript-example/Dockerfile new file mode 100644 index 000000000..85c00ed94 --- /dev/null +++ b/projects/javascript-example/Dockerfile @@ -0,0 +1,30 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +FROM gcr.io/oss-fuzz-base/base-builder-javascript + +COPY build.sh $SRC/ + +# For real projects, you would clone your repo in the next step. +RUN mkdir -p $SRC/example + +# Ideally, you have already configured fuzz tests in your repo so that they +# run (in Jazzer.js regression mode) as part of unit testing. Keeping the fuzz +# tests in sync with the source code ensures that they are adjusted continue +# to work after code changes. Here, we copy them into the example project directory. +COPY fuzz_string_compare.js fuzz_promise.js fuzz_value_profiling.js package.json $SRC/example/ + +WORKDIR $SRC/example diff --git a/projects/javascript-example/build.sh b/projects/javascript-example/build.sh new file mode 100755 index 000000000..4247e1869 --- /dev/null +++ b/projects/javascript-example/build.sh @@ -0,0 +1,24 @@ +#!/bin/bash -eu +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +# Install dependencies. +npm install + +# Build Fuzzers. +compile_javascript_fuzzer example fuzz_promise.js +compile_javascript_fuzzer example fuzz_string_compare.js --sync +compile_javascript_fuzzer example fuzz_value_profiling.js --sync diff --git a/projects/javascript-example/fuzz_promise.js b/projects/javascript-example/fuzz_promise.js new file mode 100644 index 000000000..2b76e07f0 --- /dev/null +++ b/projects/javascript-example/fuzz_promise.js @@ -0,0 +1,51 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +let lastInvocationCount = 0; +let invocationCount = lastInvocationCount + 1; + +/** + * @param { Buffer } data + */ +module.exports.fuzz = function (data) { + return new Promise((resolve, reject) => { + if (data.length < 3) { + resolve(invocationCount++); + return; + } + setTimeout(() => { + let one = data.readInt8(0); + let two = data.readInt8(1); + let three = data.readInt8(2); + if (one + two + three === 42) { + reject( + new Error( + `${one} + ${two} + ${three} = 42 (invocation ${invocationCount})` + ) + ); + } else { + resolve(invocationCount++); + } + }, 10); + }).then((value) => { + if (value !== lastInvocationCount + 1) { + throw new Error( + `Invalid invocation order, received ${value} but last invocation was ${lastInvocationCount}.` + ); + } + lastInvocationCount = value; + }); +}; diff --git a/projects/javascript-example/fuzz_string_compare.js b/projects/javascript-example/fuzz_string_compare.js new file mode 100644 index 000000000..16806bce8 --- /dev/null +++ b/projects/javascript-example/fuzz_string_compare.js @@ -0,0 +1,32 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * @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!"); + } +}; diff --git a/projects/javascript-example/fuzz_value_profiling.js b/projects/javascript-example/fuzz_value_profiling.js new file mode 100644 index 000000000..36204441f --- /dev/null +++ b/projects/javascript-example/fuzz_value_profiling.js @@ -0,0 +1,39 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * @param {number} n + */ +function encrypt(n) { + return n ^ 0x11223344; +} + +/** + * @param { Buffer } data + */ +module.exports.fuzz = function (data) { + if (data.length < 16) { + return; + } + if ( + encrypt(data.readInt32BE(0)) === 0x50555637 && + encrypt(data.readInt32BE(4)) === 0x7e4f5664 && + encrypt(data.readInt32BE(8)) === 0x5757493e && + encrypt(data.readInt32BE(12)) === 0x784c5465 + ) { + throw Error("XOR with a constant is not a secure encryption method ;-)"); + } +}; diff --git a/projects/javascript-example/package.json b/projects/javascript-example/package.json new file mode 100644 index 000000000..0960f7de4 --- /dev/null +++ b/projects/javascript-example/package.json @@ -0,0 +1,5 @@ +{ + "name": "jazzerjs-examples", + "version": "1.0.0", + "description": "Examples of fuzz tests for Jazzer.js" +} diff --git a/projects/javascript-example/project.yaml b/projects/javascript-example/project.yaml new file mode 100644 index 000000000..521c0030a --- /dev/null +++ b/projects/javascript-example/project.yaml @@ -0,0 +1,12 @@ +homepage: https://github.com/CodeIntelligenceTesting/jazzer.js +language: javascript +main_repo: https://github.com/CodeIntelligenceTesting/jazzer.js +fuzzing_engines: +- libfuzzer +sanitizers: +- none +vendor_ccs: +- yakdan@code-intelligence.com +- norbert.schneider@code-intelligence.com +- peter.samarin@code-intelligence.com +- christopher.krah@code-intelligence.com diff --git a/projects/typescript-example/Dockerfile b/projects/typescript-example/Dockerfile new file mode 100644 index 000000000..41c64d1cb --- /dev/null +++ b/projects/typescript-example/Dockerfile @@ -0,0 +1,30 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +FROM gcr.io/oss-fuzz-base/base-builder-javascript + +COPY build.sh $SRC/ + +# For real projects, you would clone your repo in the next step. +RUN mkdir -p $SRC/example + +# Ideally, you have already configured fuzz tests in your repo so that they +# run (in Jazzer.js regression mode) as part of unit testing. Keeping the fuzz +# tests in sync with the source code ensures that they are adjusted continue +# to work after code changes. Here, we copy them into the example project directory. +COPY fuzz_explore_me.ts target.ts package.json tsconfig.json $SRC/example/ + +WORKDIR $SRC/example diff --git a/projects/typescript-example/build.sh b/projects/typescript-example/build.sh new file mode 100755 index 000000000..c1b364125 --- /dev/null +++ b/projects/typescript-example/build.sh @@ -0,0 +1,29 @@ +#!/bin/bash -eu +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ + +# Install dependencies. +npm install + +# Install Jazzer.js before building the code since use the fuzzed data provider +# in the fuzz test +npm install --save-dev @jazzer.js/core + +# Compile TypeScript code. +npm run build + +# Build Fuzzers. +compile_javascript_fuzzer example dist/fuzz_explore_me.js --sync diff --git a/projects/typescript-example/fuzz_explore_me.ts b/projects/typescript-example/fuzz_explore_me.ts new file mode 100644 index 000000000..6aa468dcd --- /dev/null +++ b/projects/typescript-example/fuzz_explore_me.ts @@ -0,0 +1,27 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/////////////////////////////////////////////////////////////////////////////// + +import { FuzzedDataProvider } from "@jazzer.js/core" +import { exploreMe } from "./target"; + +export function fuzz(data: Buffer) { + const fdp = new FuzzedDataProvider(data) + exploreMe( + fdp.consumeIntegral(4), + fdp.consumeIntegral(4), + fdp.consumeRemainingAsString() + ) +} diff --git a/projects/typescript-example/package.json b/projects/typescript-example/package.json new file mode 100644 index 000000000..6299588a6 --- /dev/null +++ b/projects/typescript-example/package.json @@ -0,0 +1,12 @@ +{ + "name": "jazzerjs-typescript-example", + "version": "1.0.0", + "description": "An example how to fuzz TypeScript projects with Jazzer.js", + "scripts": { + "build": "tsc" + }, + "devDependencies": { + "@types/node": "^18.11.18", + "typescript": "^4.7.4" + } +} diff --git a/projects/typescript-example/project.yaml b/projects/typescript-example/project.yaml new file mode 100644 index 000000000..521c0030a --- /dev/null +++ b/projects/typescript-example/project.yaml @@ -0,0 +1,12 @@ +homepage: https://github.com/CodeIntelligenceTesting/jazzer.js +language: javascript +main_repo: https://github.com/CodeIntelligenceTesting/jazzer.js +fuzzing_engines: +- libfuzzer +sanitizers: +- none +vendor_ccs: +- yakdan@code-intelligence.com +- norbert.schneider@code-intelligence.com +- peter.samarin@code-intelligence.com +- christopher.krah@code-intelligence.com diff --git a/projects/typescript-example/target.ts b/projects/typescript-example/target.ts new file mode 100644 index 000000000..1a366a5f6 --- /dev/null +++ b/projects/typescript-example/target.ts @@ -0,0 +1,8 @@ +export function exploreMe(a: number, b: number, c: string ) { + if (a > 2000 && + b > 20000 && + b - a < 10000 + && c === "Hello World!") { + throw Error("Crash!") + } +} \ No newline at end of file diff --git a/projects/typescript-example/tsconfig.json b/projects/typescript-example/tsconfig.json new file mode 100644 index 000000000..dcbfa352f --- /dev/null +++ b/projects/typescript-example/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "commonjs", + "moduleResolution": "node", + "allowJs": true, + "checkJs": true, + "rootDir": ".", + "outDir": "./dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "declaration": true, + "composite": true, + "sourceMap": true + } +}