def test_remove_unaffected_fuzz_targets(self, side_effect, expected_dir_len): """Tests that remove_unaffected_fuzzers has the intended effect.""" config = test_helpers.create_run_config( is_github=True, oss_fuzz_project_name=EXAMPLE_PROJECT, workspace='/workspace') workspace = workspace_utils.Workspace(config) deployment = clusterfuzz_deployment.get_clusterfuzz_deployment( config, workspace) # We can't use fakefs in this test because this test executes # utils.is_fuzz_target_local. This function relies on the executable bit # being set, which doesn't work properly in fakefs. with tempfile.TemporaryDirectory() as tmp_dir, mock.patch( 'get_coverage.OSSFuzzCoverage.get_files_covered_by_target' ) as mocked_get_files: with mock.patch('get_coverage._get_oss_fuzz_fuzzer_stats_dir_url', return_value=1): mocked_get_files.side_effect = side_effect shutil.copy(self.TEST_FUZZER_1, tmp_dir) shutil.copy(self.TEST_FUZZER_2, tmp_dir) affected_fuzz_targets.remove_unaffected_fuzz_targets( deployment, tmp_dir, [EXAMPLE_FILE_CHANGED], '') self.assertEqual(expected_dir_len, len(os.listdir(tmp_dir)))
def setUp(self): self.setUpPyfakefs() config = test_helpers.create_run_config(workspace=WORKSPACE, is_github=False) workspace = workspace_utils.Workspace(config) self.deployment = clusterfuzz_deployment.get_clusterfuzz_deployment( config, workspace)
def test_run_fuzz_targets_quits(self, mocked_create_fuzz_target_obj, mocked_run_fuzz_target, mocked_get_fuzz_targets): """Tests that run_fuzz_targets quits on the first crash it finds.""" workspace = 'workspace' out_path = os.path.join(workspace, 'out') self.fs.create_dir(out_path) config = test_helpers.create_run_config(fuzz_seconds=FUZZ_SECONDS, workspace=workspace, project_name=EXAMPLE_PROJECT) runner = run_fuzzers.CiFuzzTargetRunner(config) mocked_get_fuzz_targets.return_value = ['target1', 'target2'] runner.initialize() testcase = os.path.join(workspace, 'testcase') self.fs.create_file(testcase) stacktrace = b'stacktrace' corpus_dir = 'corpus' self.fs.create_dir(corpus_dir) mocked_run_fuzz_target.return_value = fuzz_target.FuzzResult( testcase, stacktrace, corpus_dir) magic_mock = mock.MagicMock() magic_mock.target_name = 'target1' mocked_create_fuzz_target_obj.return_value = magic_mock self.assertTrue(runner.run_fuzz_targets()) self.assertIn('target1-address-testcase', os.listdir(runner.crashes_dir)) self.assertEqual(mocked_run_fuzz_target.call_count, 1)
def setUp(self): self.git_dir = tempfile.TemporaryDirectory() self.addCleanup(self.git_dir.cleanup) self.local_dir = tempfile.TemporaryDirectory() self.addCleanup(self.local_dir.cleanup) self.download_dir = tempfile.TemporaryDirectory() self.addCleanup(self.download_dir.cleanup) with open(os.path.join(self.local_dir.name, 'a'), 'w') as handle: handle.write('') os.makedirs(os.path.join(self.local_dir.name, 'b')) with open(os.path.join(self.local_dir.name, 'b', 'c'), 'w') as handle: handle.write('') self.git_repo = git.git_runner(self.git_dir.name) self.git_repo('init', '--bare') self.config = test_helpers.create_run_config( git_store_repo='file://' + self.git_dir.name, git_store_branch='main', git_store_branch_coverage='cov-branch') self.mock_ci_filestore = mock.MagicMock() self.git_store = git.GitFilestore(self.config, self.mock_ci_filestore)
def test_coverage_report(self, _): """Tests generation of coverage reports end-to-end, from building to generation.""" with test_helpers.docker_temp_dir() as temp_dir: shared = os.path.join(temp_dir, 'shared') os.mkdir(shared) copy_command = ('cp -r /opt/code_coverage /shared && ' 'cp $(which llvm-profdata) /shared && ' 'cp $(which llvm-cov) /shared') assert helper.docker_run([ '-v', f'{shared}:/shared', 'gcr.io/oss-fuzz-base/base-runner', 'bash', '-c', copy_command ]) os.environ['CODE_COVERAGE_SRC'] = os.path.join( shared, 'code_coverage') os.environ['PATH'] += os.pathsep + shared # Do coverage build. build_config = test_helpers.create_build_config( oss_fuzz_project_name=EXAMPLE_PROJECT, project_repo_name='oss-fuzz', workspace=temp_dir, commit_sha='0b95fe1039ed7c38fea1f97078316bfc1030c523', base_commit='da0746452433dc18bae699e355a9821285d863c8', sanitizer=self.SANITIZER, is_github=True, # Needed for test not to fail because of permissions issues. bad_build_check=False) self.assertTrue(build_fuzzers.build_fuzzers(build_config)) # TODO(metzman): Get rid of this here and make 'compile' do this. chmod_command = ('chmod -R +r /out && ' 'find /out -type d -exec chmod +x {} +') assert helper.docker_run([ '-v', f'{os.path.join(temp_dir, "build-out")}:/out', 'gcr.io/oss-fuzz-base/base-builder', 'bash', '-c', chmod_command ]) # Generate report. run_config = test_helpers.create_run_config( fuzz_seconds=FUZZ_SECONDS, workspace=temp_dir, sanitizer=self.SANITIZER, run_fuzzers_mode='coverage', is_github=True) result = run_fuzzers.run_fuzzers(run_config) self.assertEqual(result, run_fuzzers.RunFuzzersResult.NO_BUG_FOUND) expected_summary_path = os.path.join( TEST_DATA_PATH, 'example_coverage_report_summary.json') with open(expected_summary_path) as file_handle: expected_summary = json.loads(file_handle.read()) actual_summary_path = os.path.join(temp_dir, 'cifuzz-coverage', 'report', 'linux', 'summary.json') with open(actual_summary_path) as file_handle: actual_summary = json.loads(file_handle.read()) self.assertEqual(expected_summary, actual_summary)
def setUp(self): self.setUpPyfakefs() config = test_helpers.create_run_config(build_integration_path='/', workspace=WORKSPACE, is_github=False) workspace = config_utils.Workspace(config) self.deployment = clusterfuzz_deployment.get_clusterfuzz_deployment( config, workspace)
def test_get_filestore_unsupported_platform(self): """Tests that get_filestore exceptions given a platform it doesn't support.""" with mock.patch('config_utils.BaseConfig.platform', return_value='other'): run_config = test_helpers.create_run_config() with self.assertRaises(filestore.FilestoreError): filestore_utils.get_filestore(run_config)
def setUp(self): self.setUpPyfakefs() config = test_helpers.create_run_config(workspace=WORKSPACE, is_github=False) workspace = workspace_utils.Workspace(config) self.deployment = clusterfuzz_deployment.get_clusterfuzz_deployment( config, workspace) self.corpus_dir = os.path.join(workspace.corpora, EXAMPLE_FUZZER)
def setUp(self): self.setUpPyfakefs() config = test_helpers.create_run_config(project_name=EXAMPLE_PROJECT, build_integration_path='/', workspace=WORKSPACE, is_github=False) workspace = docker.Workspace(config) self.deployment = clusterfuzz_deployment.get_clusterfuzz_deployment( config, workspace)
def test_old_bug_found(self, _): """Tests run_fuzzers with a bug found in OSS-Fuzz before.""" config = test_helpers.create_run_config(fuzz_seconds=FUZZ_SECONDS, workspace=TEST_DATA_PATH, project_name=EXAMPLE_PROJECT) with tempfile.TemporaryDirectory() as tmp_dir: workspace = os.path.join(tmp_dir, 'workspace') shutil.copytree(TEST_DATA_PATH, workspace) config = test_helpers.create_run_config( fuzz_seconds=FUZZ_SECONDS, workspace=TEST_DATA_PATH, project_name=EXAMPLE_PROJECT) result = run_fuzzers.run_fuzzers(config) self.assertEqual(result, run_fuzzers.RunFuzzersResult.NO_BUG_FOUND) build_dir = os.path.join(TEST_DATA_PATH, 'out', self.BUILD_DIR_NAME) self.assertTrue(os.path.exists(build_dir)) self.assertNotEqual(0, len(os.listdir(build_dir)))
def test_download_corpus_no_artifact(self, _, __, mocked_warning): """Tests that download_corpus_build returns None and doesn't exception when find_artifact can't find an artifact.""" config = test_helpers.create_run_config(github_token=self.github_token) filestore = github_actions.GithubActionsFilestore(config) name = 'corpus-name' dst_dir = 'corpus-dir' self.assertFalse(filestore.download_corpus(name, dst_dir)) mocked_warning.assert_called_with('Could not download artifact: %s.', name)
def setUp(self): test_helpers.patch_environ(self) self.token = 'example githubtoken' self.owner = 'exampleowner' self.repo = 'examplerepo' os.environ['GITHUB_REPOSITORY'] = f'{self.owner}/{self.repo}' os.environ['GITHUB_EVENT_PATH'] = '/fake' self.config = test_helpers.create_run_config(token=self.token) self.local_dir = '/local-dir' self.testcase = os.path.join(self.local_dir, 'testcase')
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.""" with test_helpers.temp_dir_copy(fuzzer_dir) as fuzzer_dir_copy: config = test_helpers.create_run_config(fuzz_seconds=FUZZ_SECONDS, workspace=fuzzer_dir_copy, project_name='curl', sanitizer=sanitizer) result = run_fuzzers.run_fuzzers(config) self.assertEqual(result, run_fuzzers.RunFuzzersResult.NO_BUG_FOUND)
def test_get_http_auth_headers(self): """Tests that get_http_auth_headers returns the correct result.""" github_token = 'example githubtoken' run_config = test_helpers.create_run_config(github_token=github_token) expected_headers = { 'Authorization': f'token {github_token}', 'Accept': 'application/vnd.github.v3+json', } self.assertEqual(expected_headers, github_api.get_http_auth_headers(run_config))
def _create_config(**kwargs): """Creates a config object and then sets every attribute that is a key in |kwargs| to the corresponding value. Asserts that each key in |kwargs| is an attribute of Config.""" defaults = {'is_github': True, 'project_name': EXAMPLE_PROJECT} for default_key, default_value in defaults.items(): if default_key not in kwargs: kwargs[default_key] = default_value return test_helpers.create_run_config(**kwargs)
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) config = test_helpers.create_run_config( fuzz_seconds=FUZZ_SECONDS, workspace=tmp_dir, project_name=EXAMPLE_PROJECT) result = run_fuzzers.run_fuzzers(config) self.assertEqual(result, run_fuzzers.RunFuzzersResult.ERROR)
def setUp(self): self.setUpPyfakefs() config = test_helpers.create_run_config(workspace=WORKSPACE, cfl_platform='other', filestore='no_filestore', no_clusterfuzz_deployment=True) workspace = workspace_utils.Workspace(config) self.deployment = clusterfuzz_deployment.get_clusterfuzz_deployment( config, workspace) self.corpus_dir = os.path.join(workspace.corpora, EXAMPLE_FUZZER)
def test_old_bug_found(self, _): """Tests run_fuzzers with a bug found in OSS-Fuzz before.""" with tempfile.TemporaryDirectory() as tmp_dir: workspace = os.path.join(tmp_dir, 'workspace') shutil.copytree(TEST_DATA_PATH, workspace) config = test_helpers.create_run_config( fuzz_seconds=FUZZ_SECONDS, workspace=workspace, oss_fuzz_project_name=EXAMPLE_PROJECT) result = run_fuzzers.run_fuzzers(config) self.assertEqual(result, run_fuzzers.RunFuzzersResult.NO_BUG_FOUND)
def _create_runner(self, **kwargs): # pylint: disable=no-self-use defaults = { 'fuzz_seconds': FUZZ_SECONDS, 'project_name': EXAMPLE_PROJECT } for default_key, default_value in defaults.items(): if default_key not in kwargs: kwargs[default_key] = default_value config = test_helpers.create_run_config(**kwargs) return run_fuzzers.BaseFuzzTargetRunner(config)
def setUp(self): self.setUpPyfakefs() out_dir = os.path.join(self.WORKSPACE, 'build-out') self.fs.create_dir(out_dir) self.testcase1 = os.path.join(out_dir, 'testcase-aaa') self.fs.create_file(self.testcase1) self.testcase2 = os.path.join(out_dir, 'testcase-bbb') self.fs.create_file(self.testcase2) self.config = test_helpers.create_run_config(fuzz_seconds=FUZZ_SECONDS, workspace=self.WORKSPACE, is_github=True)
def test_get_fuzz_target_runner(self, mode, fuzz_target_runner_cls): """Tests that get_fuzz_target_runner returns the correct runner based on the specified mode.""" with tempfile.TemporaryDirectory() as tmp_dir: run_config = test_helpers.create_run_config( fuzz_seconds=FUZZ_SECONDS, workspace=tmp_dir, oss_fuzz_project_name='example', mode=mode) runner = run_fuzzers.get_fuzz_target_runner(run_config) self.assertTrue(isinstance(runner, fuzz_target_runner_cls))
def test_list_artifacts(self, mocked_list_artifacts): """Tests that _list_artifacts works as intended.""" owner = 'exampleowner' repo = 'examplerepo' os.environ['GITHUB_REPOSITORY'] = '{owner}/{repo}'.format(owner=owner, repo=repo) config = test_helpers.create_run_config(github_token=self.github_token) filestore = github_actions.GithubActionsFilestore(config) filestore._list_artifacts() mocked_list_artifacts.assert_called_with( owner, repo, self._get_expected_http_headers())
def setUp(self, _): # pylint: disable=arguments-differ test_helpers.patch_environ(self) self.owner = 'exampleowner' self.repo = 'examplerepo' os.environ['GITHUB_REPOSITORY'] = f'{self.owner}/{self.repo}' os.environ['GITHUB_EVENT_PATH'] = '/fake' os.environ['CFL_PLATFORM'] = 'github' os.environ['GITHUB_WORKSPACE'] = '/workspace' os.environ['ACTIONS_RUNTIME_TOKEN'] = 'githubtoken' self.config = test_helpers.create_run_config() self.local_dir = '/local-dir' self.testcase = os.path.join(self.local_dir, 'testcase')
def setUp(self): self.setUpPyfakefs() out_dir = os.path.join(self.WORKSPACE, 'build-out') self.fs.create_dir(out_dir) self.testcase1 = os.path.join(out_dir, 'testcase-aaa') self.fs.create_file(self.testcase1) self.testcase2 = os.path.join(out_dir, 'testcase-bbb') self.fs.create_file(self.testcase2) self.config = test_helpers.create_run_config( fuzz_seconds=FUZZ_SECONDS, workspace=self.WORKSPACE, project_name=EXAMPLE_PROJECT, build_integration_path='/', is_github=True)
def test_coverage_report(self, _): """Tests generation of coverage reports end-to-end, from building to generation.""" with tempfile.TemporaryDirectory() as workspace: try: # Do coverage build. build_config = test_helpers.create_build_config( oss_fuzz_project_name=EXAMPLE_PROJECT, project_repo_name='oss-fuzz', workspace=workspace, commit_sha='0b95fe1039ed7c38fea1f97078316bfc1030c523', base_commit='da0746452433dc18bae699e355a9821285d863c8', sanitizer=self.SANITIZER, is_github=True) self.assertTrue(build_fuzzers.build_fuzzers(build_config)) # Generate report. run_config = test_helpers.create_run_config( fuzz_seconds=FUZZ_SECONDS, workspace=workspace, oss_fuzz_project_name=EXAMPLE_PROJECT, sanitizer=self.SANITIZER, run_fuzzers_mode='coverage', is_github=True, # Set build integration path so it's not internal. build_integration_path='/') result = run_fuzzers.run_fuzzers(run_config) self.assertEqual(result, run_fuzzers.RunFuzzersResult.NO_BUG_FOUND) expected_summary_path = os.path.join( TEST_DATA_PATH, 'example_coverage_report_summary.json') with open(expected_summary_path) as file_handle: expected_summary = json.loads(file_handle.read()) actual_summary_path = os.path.join(workspace, 'cifuzz-coverage', 'report', 'linux', 'summary.json') with open(actual_summary_path) as file_handle: actual_summary = json.loads(file_handle.read()) self.assertEqual(expected_summary, actual_summary) finally: # If we don't do this, there will be an exception when the temporary # directory is deleted because there are files there that are only # writeable by root. if os.listdir(workspace): helper.docker_run([ '-v', f'{workspace}:/workspace', '-t', docker.BASE_RUNNER_TAG, '/bin/bash', '-c', 'rm -rf /workspace/*' ])
def _create_config(**kwargs): """Creates a config object and then sets every attribute that is a key in |kwargs| to the corresponding value. Asserts that each key in |kwargs| is an attribute of Config.""" defaults = { 'cfl_platform': 'github', 'oss_fuzz_project_name': EXAMPLE_PROJECT, 'workspace': '/workspace' } for default_key, default_value in defaults.items(): if default_key not in kwargs: kwargs[default_key] = default_value return test_helpers.create_run_config(**kwargs)
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('fuzz_target.FuzzTarget.is_reproducible', side_effect=[True, False]): with tempfile.TemporaryDirectory() as tmp_dir: workspace = os.path.join(tmp_dir, 'workspace') shutil.copytree(TEST_DATA_PATH, workspace) config = test_helpers.create_run_config( fuzz_seconds=FUZZ_SECONDS, workspace=workspace, oss_fuzz_project_name=EXAMPLE_PROJECT) result = run_fuzzers.run_fuzzers(config) self.assertEqual(result, run_fuzzers.RunFuzzersResult.BUG_FOUND)
def test_run_coverage_command(self, mocked_docker_run): # pylint: disable=no-self-use """Tests that run_coverage_command works as intended.""" config = test_helpers.create_run_config(project_name=PROJECT, sanitizer=SANITIZER) workspace = test_helpers.create_workspace() expected_docker_args = [ '--cap-add', 'SYS_PTRACE', '-e', 'FUZZING_ENGINE=libfuzzer', '-e', 'ARCHITECTURE=x86_64', '-e', 'CIFUZZ=True', '-e', f'SANITIZER={SANITIZER}', '-e', 'FUZZING_LANGUAGE=c++', '-e', 'OUT=/workspace/build-out', '-v', f'{workspace.workspace}:{workspace.workspace}', '-e', 'COVERAGE_EXTRA_ARGS=', '-e', 'HTTP_PORT=', '-t', 'gcr.io/oss-fuzz-base/base-runner', 'coverage' ] generate_coverage_report.run_coverage_command(workspace, config) mocked_docker_run.assert_called_with(expected_docker_args)
def test_run_coverage_command(self, mock_execute): # pylint: disable=no-self-use """Tests that run_coverage_command works as intended.""" config = test_helpers.create_run_config(oss_fuzz_project_name=PROJECT, sanitizer=SANITIZER) workspace = test_helpers.create_workspace() generate_coverage_report.run_coverage_command(config, workspace) expected_command = 'coverage' expected_env = { 'SANITIZER': config.sanitizer, 'FUZZING_LANGUAGE': config.language, 'OUT': workspace.out, 'CIFUZZ': 'True', 'FUZZING_ENGINE': 'libfuzzer', 'ARCHITECTURE': 'x86_64', 'FUZZER_ARGS': '-rss_limit_mb=2560 -timeout=25', 'HTTP_PORT': '', 'COVERAGE_EXTRA_ARGS': '', 'CORPUS_DIR': workspace.corpora, 'COVERAGE_OUTPUT_DIR': workspace.coverage_report } mock_execute.assert_called_with(expected_command, env=expected_env)
def test_run_fuzz_targets_quits(self, mocked_create_fuzz_target_obj, mocked_run_fuzz_target, mocked_get_fuzz_targets): """Tests that run_fuzz_targets doesn't quit on the first crash it finds.""" workspace = 'workspace' out_path = os.path.join(workspace, 'out') self.fs.create_dir(out_path) config = test_helpers.create_run_config(fuzz_seconds=FUZZ_SECONDS, workspace=workspace, project_name=EXAMPLE_PROJECT) runner = run_fuzzers.BatchFuzzTargetRunner(config) mocked_get_fuzz_targets.return_value = ['target1', 'target2'] runner.initialize() testcase1 = os.path.join(workspace, 'testcase-aaa') testcase2 = os.path.join(workspace, 'testcase-bbb') self.fs.create_file(testcase1) self.fs.create_file(testcase2) stacktrace = b'stacktrace' call_count = 0 corpus_dir = 'corpus' def mock_run_fuzz_target(_): nonlocal call_count if call_count == 0: testcase = testcase1 elif call_count == 1: testcase = testcase2 assert call_count != 2 call_count += 1 return fuzz_target.FuzzResult(testcase, stacktrace, corpus_dir) mocked_run_fuzz_target.side_effect = mock_run_fuzz_target magic_mock = mock.MagicMock() magic_mock.target_name = 'target1' mocked_create_fuzz_target_obj.return_value = magic_mock self.assertTrue(runner.run_fuzz_targets()) self.assertIn('target1-address-testcase-aaa', os.listdir(runner.crashes_dir)) self.assertEqual(mocked_run_fuzz_target.call_count, 2)