1# Copyright 2021 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Module for getting the configuration CIFuzz needs to run.""" 15 16import logging 17import enum 18import os 19import json 20 21import environment 22 23 24def _get_project_repo_name(): 25 return os.path.basename(environment.get('GITHUB_REPOSITORY', '')) 26 27 28def _get_pr_ref(event): 29 if event == 'pull_request': 30 return environment.get('GITHUB_REF') 31 return None 32 33 34def _get_sanitizer(): 35 return os.getenv('SANITIZER', 'address').lower() 36 37 38def _get_project_name(): 39 # TODO(metzman): Remove OSS-Fuzz reference. 40 return os.getenv('OSS_FUZZ_PROJECT_NAME') 41 42 43def _is_dry_run(): 44 """Returns True if configured to do a dry run.""" 45 return environment.get_bool('DRY_RUN', 'false') 46 47 48def get_project_src_path(workspace): 49 """Returns the manually checked out path of the project's source if specified 50 or None.""" 51 # TODO(metzman): Get rid of MANUAL_SRC_PATH when Skia switches to 52 # PROJECT_SRC_PATH. 53 path = os.getenv('PROJECT_SRC_PATH', os.getenv('MANUAL_SRC_PATH')) 54 if not path: 55 logging.debug('No PROJECT_SRC_PATH.') 56 return path 57 58 logging.debug('PROJECT_SRC_PATH set.') 59 if os.path.isabs(path): 60 return path 61 62 # If |src| is not absolute, assume we are running in GitHub actions. 63 # TODO(metzman): Don't make this assumption. 64 return os.path.join(workspace, path) 65 66 67DEFAULT_LANGUAGE = 'c++' 68 69 70def _get_language(): 71 """Returns the project language.""" 72 # Get language from environment. We took this approach because the convenience 73 # given to OSS-Fuzz users by not making them specify the language again (and 74 # getting it from the project.yaml) is outweighed by the complexity in 75 # implementing this. A lot of the complexity comes from our unittests not 76 # setting a proper projet at this point. 77 return os.getenv('LANGUAGE', DEFAULT_LANGUAGE) 78 79 80# pylint: disable=too-few-public-methods,too-many-instance-attributes 81 82 83class BaseConfig: 84 """Object containing constant configuration for CIFuzz.""" 85 86 class Platform(enum.Enum): 87 """Enum representing the different platforms CIFuzz runs on.""" 88 EXTERNAL_GITHUB = 0 # Non-OSS-Fuzz on GitHub actions. 89 INTERNAL_GITHUB = 1 # OSS-Fuzz on GitHub actions. 90 INTERNAL_GENERIC_CI = 2 # OSS-Fuzz on any CI. 91 92 def __init__(self): 93 self.workspace = os.getenv('GITHUB_WORKSPACE') 94 self.project_name = _get_project_name() 95 # Check if failures should not be reported. 96 self.dry_run = _is_dry_run() 97 self.sanitizer = _get_sanitizer() 98 self.build_integration_path = os.getenv('BUILD_INTEGRATION_PATH') 99 self.language = _get_language() 100 event_path = os.getenv('GITHUB_EVENT_PATH') 101 self.is_github = bool(event_path) 102 logging.debug('Is github: %s.', self.is_github) 103 # TODO(metzman): Parse env like we do in ClusterFuzz. 104 self.low_disk_space = environment.get('LOW_DISK_SPACE', False) 105 106 @property 107 def is_internal(self): 108 """Returns True if this is an OSS-Fuzz project.""" 109 return not self.build_integration_path 110 111 @property 112 def platform(self): 113 """Returns the platform CIFuzz is runnning on.""" 114 if not self.is_internal: 115 return self.Platform.EXTERNAL_GITHUB 116 if self.is_github: 117 return self.Platform.INTERNAL_GITHUB 118 return self.Platform.INTERNAL_GENERIC_CI 119 120 121class RunFuzzersConfig(BaseConfig): 122 """Class containing constant configuration for running fuzzers in CIFuzz.""" 123 124 RUN_FUZZERS_MODES = {'batch', 'ci'} 125 126 def __init__(self): 127 super().__init__() 128 self.fuzz_seconds = int(os.environ.get('FUZZ_SECONDS', 600)) 129 self.run_fuzzers_mode = os.environ.get('RUN_FUZZERS_MODE', 'ci').lower() 130 if self.run_fuzzers_mode not in self.RUN_FUZZERS_MODES: 131 raise Exception( 132 ('Invalid RUN_FUZZERS_MODE %s not one of allowed choices: %s.' % 133 self.run_fuzzers_mode, self.RUN_FUZZERS_MODES)) 134 135 136class BuildFuzzersConfig(BaseConfig): 137 """Class containing constant configuration for building fuzzers in CIFuzz.""" 138 139 def _get_config_from_event_path(self, event): 140 event_path = os.getenv('GITHUB_EVENT_PATH') 141 if not event_path: 142 return 143 with open(event_path, encoding='utf-8') as file_handle: 144 event_data = json.load(file_handle) 145 if event == 'push': 146 self.base_commit = event_data['before'] 147 logging.debug('base_commit: %s', self.base_commit) 148 else: 149 self.pr_ref = 'refs/pull/{0}/merge'.format( 150 event_data['pull_request']['number']) 151 logging.debug('pr_ref: %s', self.pr_ref) 152 153 self.git_url = event_data['repository']['html_url'] 154 155 def __init__(self): 156 """Get the configuration from CIFuzz from the environment. These variables 157 are set by GitHub or the user.""" 158 # TODO(metzman): Some of this config is very CI-specific. Move it into the 159 # CI class. 160 super().__init__() 161 self.project_repo_name = _get_project_repo_name() 162 self.commit_sha = os.getenv('GITHUB_SHA') 163 event = os.getenv('GITHUB_EVENT_NAME') 164 165 self.pr_ref = None 166 self.git_url = None 167 self.base_commit = None 168 self._get_config_from_event_path(event) 169 170 self.base_ref = os.getenv('GITHUB_BASE_REF') 171 self.project_src_path = get_project_src_path(self.workspace) 172 173 self.allowed_broken_targets_percentage = os.getenv( 174 'ALLOWED_BROKEN_TARGETS_PERCENTAGE') 175 self.bad_build_check = environment.get_bool('BAD_BUILD_CHECK', 'true') 176 177 # TODO(metzman): Use better system for interpreting env vars. What if env 178 # var is set to '0'? 179 self.keep_unaffected_fuzz_targets = bool( 180 os.getenv('KEEP_UNAFFECTED_FUZZERS')) 181