You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
5.7 KiB
183 lines
5.7 KiB
# Copyright 2019 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.
|
|
"""Module to get the the name of a git repo containing a specific commit
|
|
inside of an OSS-Fuzz project.
|
|
|
|
Example Usage:
|
|
|
|
python detect_repo.py --src_dir /src --example_commit
|
|
b534f03eecd8a109db2b085ab24d419b6486de97
|
|
|
|
Prints the location of the git remote repo as well as the repo's name
|
|
seperated by a space.
|
|
|
|
https://github.com/VirusTotal/yara.git yara
|
|
|
|
"""
|
|
import argparse
|
|
import logging
|
|
import os
|
|
import subprocess
|
|
|
|
GO_PATH = '/root/go/src/'
|
|
|
|
|
|
def main():
|
|
"""Function to get a git repo's url and name referenced by OSS-Fuzz
|
|
Dockerfile.
|
|
|
|
Raises:
|
|
ValueError when a commit or a ref is not provided.
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
description=
|
|
'Finds a specific git repo in an oss-fuzz project\'s docker file.')
|
|
parser.add_argument('--repo_name', help='The name of the git repo.')
|
|
parser.add_argument('--src_dir', help='The location of the possible repo.')
|
|
parser.add_argument('--example_commit',
|
|
help='A commit SHA referencing the project\'s main repo.')
|
|
|
|
args = parser.parse_args()
|
|
if not args.repo_name and not args.example_commit:
|
|
raise ValueError(
|
|
'Requires an example commit or a repo name to find repo location.')
|
|
if args.src_dir:
|
|
src_dir = args.src_dir
|
|
else:
|
|
src_dir = os.environ.get('SRC', '/src')
|
|
|
|
for single_dir in get_dirs_to_search(src_dir, args.repo_name):
|
|
full_path = os.path.join(src_dir, single_dir)
|
|
if not os.path.isdir(full_path):
|
|
continue
|
|
if args.example_commit and check_for_commit(full_path, args.example_commit):
|
|
print('Detected repo:', get_repo(full_path), full_path)
|
|
return
|
|
if args.repo_name and check_for_repo_name(full_path, args.repo_name):
|
|
print('Detected repo:', get_repo(full_path), full_path)
|
|
return
|
|
logging.error('No git repos with specific commit: %s found in %s',
|
|
args.example_commit, src_dir)
|
|
|
|
|
|
def get_dirs_to_search(src_dir, repo_name):
|
|
"""Gets a list of directories to search for the main git repo.
|
|
|
|
Args:
|
|
src_dir: The location set for the projects SRC.
|
|
repo_name: The name of the repo you are searching for.
|
|
|
|
Returns:
|
|
A list of directorys to search.
|
|
"""
|
|
dirs_to_search = os.listdir(src_dir)
|
|
if os.path.exists(GO_PATH) and repo_name:
|
|
for root, dirs, _ in os.walk(GO_PATH):
|
|
for test_dir in dirs:
|
|
if repo_name in test_dir:
|
|
dirs_to_search.append(os.path.join(root, test_dir))
|
|
return dirs_to_search
|
|
|
|
|
|
def get_repo(repo_path):
|
|
"""Gets a git repo link from a specific directory in a docker image.
|
|
|
|
Args:
|
|
repo_path: The directory on the image where the git repo exists.
|
|
|
|
Returns:
|
|
The repo location or None.
|
|
"""
|
|
output, return_code = execute(['git', 'config', '--get', 'remote.origin.url'],
|
|
location=repo_path,
|
|
check_result=True)
|
|
if return_code == 0 and output:
|
|
return output.rstrip()
|
|
return None
|
|
|
|
|
|
def check_for_repo_name(repo_path, expected_repo_name):
|
|
"""Returns True if the repo at |repo_path| repo_name matches
|
|
|expected_repo_name|.
|
|
|
|
Args:
|
|
repo_path: The directory of a git repo.
|
|
expected_repo_name: The name of the target git repo.
|
|
"""
|
|
if not os.path.exists(os.path.join(repo_path, '.git')):
|
|
return False
|
|
|
|
repo_url, _ = execute(['git', 'config', '--get', 'remote.origin.url'],
|
|
location=repo_path)
|
|
# Handle two common cases:
|
|
# https://github.com/google/syzkaller/
|
|
# https://github.com/google/syzkaller.git
|
|
repo_url = repo_url.replace('.git', '').rstrip().rstrip('/')
|
|
actual_repo_name = repo_url.split('/')[-1]
|
|
return actual_repo_name == expected_repo_name
|
|
|
|
|
|
def check_for_commit(repo_path, commit):
|
|
"""Checks a directory for a specific commit.
|
|
|
|
Args:
|
|
repo_path: The name of the directory to test for the commit.
|
|
commit: The commit SHA to check for.
|
|
|
|
Returns:
|
|
True if directory contains that commit.
|
|
"""
|
|
|
|
# Check if valid git repo.
|
|
if not os.path.exists(os.path.join(repo_path, '.git')):
|
|
return False
|
|
|
|
# Check if history fetch is needed.
|
|
if os.path.exists(os.path.join(repo_path, '.git', 'shallow')):
|
|
execute(['git', 'fetch', '--unshallow'], location=repo_path)
|
|
|
|
# Check if commit is in history.
|
|
_, return_code = execute(['git', 'cat-file', '-e', commit],
|
|
location=repo_path)
|
|
return return_code == 0
|
|
|
|
|
|
def execute(command, location, check_result=False):
|
|
"""Runs a shell command in the specified directory location.
|
|
|
|
Args:
|
|
command: The command as a list to be run.
|
|
location: The directory the command is run in.
|
|
check_result: Should an exception be thrown on failed command.
|
|
|
|
Returns:
|
|
The stdout of the command, the error code.
|
|
|
|
Raises:
|
|
RuntimeError: running a command resulted in an error.
|
|
"""
|
|
process = subprocess.Popen(command, stdout=subprocess.PIPE, cwd=location)
|
|
output, err = process.communicate()
|
|
if check_result and (process.returncode or err):
|
|
raise RuntimeError(
|
|
'Error: %s\n running command: %s\n return code: %s\n out %s\n' %
|
|
(err, command, process.returncode, output))
|
|
if output is not None:
|
|
output = output.decode('ascii')
|
|
return output, process.returncode
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|