def main():
    """Runs OSS-Fuzz project's fuzzers for CI tools.
  This is the entrypoint for the run_fuzzers github action.
  This action can be added to any OSS-Fuzz project's workflow that uses Github.

  NOTE: libfuzzer binaries must be located in the ${GITHUB_WORKSPACE}/out
  directory in order for this action to be used. This action will only fuzz the
  binary's that are located in that directory. It is reccomended that you add
  the build_fuzzers action preceding this one.

  NOTE: Any crash report will be in the filepath:
  ${GITHUB_WORKSPACE}/out/testcase
  This can be used in parallel with the upload-artifact action to surface the
  logs.

  Required environment variables:
    FUZZ_SECONDS: The length of time in seconds that fuzzers are to be run.
    GITHUB_WORKSPACE: The shared volume directory where input artifacts are.
    DRY_RUN: If true, no failures will surface.
    OSS_FUZZ_PROJECT_NAME: The name of the relevant OSS-Fuzz project.

  Returns:
    0 on success or 1 on Failure.
  """
    fuzz_seconds = int(os.environ.get('FUZZ_SECONDS', 600))
    workspace = os.environ.get('GITHUB_WORKSPACE')
    oss_fuzz_project_name = os.environ.get('OSS_FUZZ_PROJECT_NAME')
    # Check if failures should not be reported.
    dry_run = (os.environ.get('DRY_RUN').lower() == 'true')

    # The default return code when an error occurs.
    error_code = 1
    if dry_run:
        # A testcase file is required in order for CIFuzz to surface bugs.
        # If the file does not exist, the action will crash attempting to upload it.
        # The dry run needs this file because it is set to upload a test case both
        # on successful runs and on failures.
        out_dir = os.path.join(workspace, 'out', 'artifacts')
        os.makedirs(out_dir, exist_ok=True)

        # Sets the default return code on error to success.
        error_code = 0

    if not workspace:
        logging.error(
            'This script needs to be run in the Github action context.')
        return error_code
    # Run the specified project's fuzzers from the build.
    run_status, bug_found = cifuzz.run_fuzzers(fuzz_seconds, workspace,
                                               oss_fuzz_project_name)
    if not run_status:
        logging.error('Error occured while running in workspace %s.',
                      workspace)
        return error_code
    if bug_found:
        logging.info('Bug found.')
        if not dry_run:
            # Return 2 when a bug was found by a fuzzer causing the CI to fail.
            return 2
    return 0
Beispiel #2
0
 def test_invalid_fuzz_seconds(self):
   """Tests run_fuzzers with an invalid fuzz seconds."""
   with tempfile.TemporaryDirectory() as tmp_dir:
     out_path = os.path.join(tmp_dir, 'out')
     os.mkdir(out_path)
     run_success, bug_found = cifuzz.run_fuzzers(EXAMPLE_PROJECT, 0, out_path)
   self.assertFalse(run_success)
   self.assertFalse(bug_found)
Beispiel #3
0
 def test_run_with_undefined_sanitizer(self):
     """Test run_fuzzers with a valid build."""
     run_success, bug_found = cifuzz.run_fuzzers(10,
                                                 UNDEFINED_FUZZER_DIR,
                                                 'curl',
                                                 sanitizer='undefined')
     self.assertTrue(run_success)
     self.assertFalse(bug_found)
Beispiel #4
0
 def test_run_with_memory_sanitizer(self):
     """Test run_fuzzers with a valid build."""
     run_success, bug_found = cifuzz.run_fuzzers(10,
                                                 MEMORY_FUZZER_DIR,
                                                 'curl',
                                                 sanitizer='memory')
     self.assertTrue(run_success)
     self.assertFalse(bug_found)
Beispiel #5
0
 def test_invalid_build(self):
   """Tests run_fuzzers with an invalid ASAN build."""
   with tempfile.TemporaryDirectory() as tmp_dir:
     out_path = os.path.join(tmp_dir, 'out')
     os.mkdir(out_path)
     run_success, bug_found = cifuzz.run_fuzzers(10, tmp_dir, EXAMPLE_PROJECT)
   self.assertFalse(run_success)
   self.assertFalse(bug_found)
Beispiel #6
0
 def test_invalid_fuzz_seconds(self):
   """Tests run_fuzzers with an invalid fuzz seconds."""
   with tempfile.TemporaryDirectory() as tmp_dir, unittest.mock.patch.object(
       cifuzz, 'is_project_sanitizer', return_value=True):
     out_path = os.path.join(tmp_dir, 'out')
     os.mkdir(out_path)
     run_success, bug_found = cifuzz.run_fuzzers(0, tmp_dir, EXAMPLE_PROJECT)
   self.assertFalse(run_success)
   self.assertFalse(bug_found)
Beispiel #7
0
 def _test_run_with_sanitizer(self, fuzzer_dir, sanitizer):
     """Calls run_fuzzers on fuzzer_dir and |sanitizer| and asserts
 the run succeeded and that no bug was found."""
     run_success, bug_found = cifuzz.run_fuzzers(10,
                                                 fuzzer_dir,
                                                 'curl',
                                                 sanitizer=sanitizer)
     self.assertTrue(run_success)
     self.assertFalse(bug_found)
Beispiel #8
0
def main():
    """Runs OSS-Fuzz project's fuzzers for CI tools.
  This script is used to kick off the Github Actions CI tool. It is the
  entrypoint  of the Dockerfile in this directory. This action can be added to
  any OSS-Fuzz project's workflow that uses Github.

  Required environment variables:
    PROJECT_NAME: The name of OSS-Fuzz project.
    FUZZ_TIME: The length of time in seconds that fuzzers are to be run.
    GITHUB_REPOSITORY: The name of the Github repo that called this script.
    GITHUB_SHA: The commit SHA that triggered this script.
    GITHUB_REF: The pull request reference that triggered this script.
    GITHUB_EVENT_NAME: The name of the hook event that triggered this script.

  Returns:
    0 on success or 1 on Failure.
  """
    oss_fuzz_project_name = os.environ.get('PROJECT_NAME')
    fuzz_seconds = int(os.environ.get('FUZZ_SECONDS', 360))
    github_repo_name = os.path.basename(os.environ.get('GITHUB_REPOSITORY'))
    pr_ref = os.environ.get('GITHUB_REF')
    commit_sha = os.environ.get('GITHUB_SHA')
    event = os.environ.get('GITHUB_EVENT_NAME')

    # Get the shared volume directory and create required directorys.
    workspace = os.environ.get('GITHUB_WORKSPACE')
    if not workspace:
        logging.error(
            'This script needs to be run in the Github action context.')
        return 1

    if event == 'push' and not cifuzz.build_fuzzers(oss_fuzz_project_name,
                                                    github_repo_name,
                                                    workspace,
                                                    commit_sha=commit_sha):
        logging.error('Error building fuzzers for project %s with commit %s.',
                      oss_fuzz_project_name, commit_sha)
        return 1
    if event == 'pull_request' and not cifuzz.build_fuzzers(
            oss_fuzz_project_name, github_repo_name, workspace, pr_ref=pr_ref):
        logging.error(
            'Error building fuzzers for project %s with pull request %s.',
            oss_fuzz_project_name, pr_ref)
        return 1

    # Run the specified project's fuzzers from the build.
    run_status, bug_found = cifuzz.run_fuzzers(oss_fuzz_project_name,
                                               fuzz_seconds, workspace)
    if not run_status:
        logging.error('Error occured while running fuzzers for project %s.',
                      oss_fuzz_project_name)
        return 1
    if bug_found:
        logging.info('Bug found.')
        # Return 2 when a bug was found by a fuzzer causing the CI to fail.
        return 2
    return 0
Beispiel #9
0
 def test_old_bug_found(self):
   """Tests run_fuzzers with a bug found in OSS-Fuzz before."""
   with mock.patch.object(fuzz_target.FuzzTarget,
                          'is_reproducible',
                          side_effect=[True, True]):
     run_success, bug_found = cifuzz.run_fuzzers(10, TEST_FILES_PATH,
                                                 EXAMPLE_PROJECT)
     build_dir = os.path.join(TEST_FILES_PATH, 'out', 'oss_fuzz_latest')
     self.assertTrue(os.path.exists(build_dir))
     self.assertNotEqual(0, len(os.listdir(build_dir)))
     self.assertTrue(run_success)
     self.assertFalse(bug_found)
Beispiel #10
0
 def test_valid(self):
   """Test run_fuzzers with a valid build."""
   with tempfile.TemporaryDirectory() as tmp_dir:
     out_path = os.path.join(tmp_dir, 'out')
     workspace_path = os.path.join(tmp_dir, 'workspace')
     os.mkdir(out_path)
     os.mkdir(workspace_path)
     self.assertTrue(
         cifuzz.build_fuzzers(EXAMPLE_PROJECT, 'oss-fuzz',
                              '0b95fe1039ed7c38fea1f97078316bfc1030c523',
                              workspace_path, out_path))
     self.assertTrue(os.path.exists(os.path.join(out_path, 'do_stuff_fuzzer')))
     run_success, bug_found = cifuzz.run_fuzzers(EXAMPLE_PROJECT, 5, out_path)
   self.assertTrue(run_success)
   self.assertTrue(bug_found)
Beispiel #11
0
 def test_new_bug_found(self):
   """Tests run_fuzzers with a valid ASAN build."""
   # Set the first return value to True, then the second to False to
   # emulate a bug existing in the current PR but not on the downloaded
   # OSS-Fuzz build.
   with mock.patch.object(fuzz_target.FuzzTarget,
                          'is_reproducible',
                          side_effect=[True, False]):
     run_success, bug_found = cifuzz.run_fuzzers(10, TEST_FILES_PATH,
                                                 EXAMPLE_PROJECT)
     build_dir = os.path.join(TEST_FILES_PATH, 'out', 'oss_fuzz_latest')
     self.assertTrue(os.path.exists(build_dir))
     self.assertNotEqual(0, len(os.listdir(build_dir)))
     self.assertTrue(run_success)
     self.assertTrue(bug_found)
Beispiel #12
0
 def test_reproduce_false(self):
   """Checks CIFuzz doesn't report an error when a crash isn't reproducible."""
   with tempfile.TemporaryDirectory() as tmp_dir:
     out_path = os.path.join(tmp_dir, 'out')
     os.mkdir(out_path)
     self.assertTrue(
         cifuzz.build_fuzzers(
             EXAMPLE_PROJECT,
             'oss-fuzz',
             tmp_dir,
             commit_sha='0b95fe1039ed7c38fea1f97078316bfc1030c523'))
     with unittest.mock.patch.object(fuzz_target.FuzzTarget,
                                     'is_reproducible',
                                     return_value=False):
       run_success, bug_found = cifuzz.run_fuzzers(5, tmp_dir)
       self.assertTrue(run_success)
       self.assertFalse(bug_found)
Beispiel #13
0
 def test_invalid_out_dir(self):
   """Tests run_fuzzers with an invalid out directory."""
   run_success, bug_found = cifuzz.run_fuzzers(EXAMPLE_PROJECT, 5,
                                               'not/a/valid/path')
   self.assertFalse(run_success)
   self.assertFalse(bug_found)