mirror of
https://github.com/trailofbits/buttercup
synced 2026-06-21 14:11:39 +00:00
Sliced Parser (#265)
* sliced parser * add local target search * update orchestrator * more fixes * test more * remove ext refs * lints * fix list * docs * update
This commit is contained in:
@@ -13,7 +13,7 @@ dependencies = [
|
||||
"langchain-openai~=0.3.2",
|
||||
"langfuse ~= 2.59.2",
|
||||
"langchain ~= 0.3.18",
|
||||
"clusterfuzz==2.6.0",
|
||||
"six>=1.17.0",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
@@ -33,4 +33,5 @@ dev = ["pytest>=8.3.4", "ruff>=0.9.2"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 120
|
||||
exclude = ["./src/buttercup/common/datastructures"]
|
||||
exclude = ["./src/buttercup/common/datastructures", "./src/buttercup/common/clusterfuzz_env", "./src/buttercup/common/clusterfuzz_parser", "src/buttercup/common/clusterfuzz_utils.py"]
|
||||
|
||||
|
||||
@@ -0,0 +1,869 @@
|
||||
# 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.
|
||||
"""Environment functions."""
|
||||
|
||||
import ast
|
||||
import functools
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import six
|
||||
import yaml
|
||||
|
||||
from buttercup.common.clusterfuzz_env import fuzzing
|
||||
|
||||
# Tools supporting customization of options via ADDITIONAL_{TOOL_NAME}_OPTIONS.
|
||||
# FIXME: Support ADDITIONAL_UBSAN_OPTIONS and ADDITIONAL_LSAN_OPTIONS in an
|
||||
# ASAN instrumented build.
|
||||
SUPPORTED_MEMORY_TOOLS_FOR_OPTIONS = [
|
||||
'HWASAN', 'ASAN', 'KASAN', 'CFI', 'MSAN', 'TSAN', 'UBSAN', 'NOSANITIZER'
|
||||
]
|
||||
|
||||
SANITIZER_NAME_MAP = {
|
||||
'ASAN': 'address',
|
||||
'CFI': 'cfi',
|
||||
'MSAN': 'memory',
|
||||
'TSAN': 'thread',
|
||||
'UBSAN': 'undefined',
|
||||
'NOSANITIZER': 'nosanitizer',
|
||||
}
|
||||
|
||||
COMMON_SANITIZER_OPTIONS = {
|
||||
'handle_abort': 1,
|
||||
'handle_segv': 1,
|
||||
'handle_sigbus': 1,
|
||||
'handle_sigfpe': 1,
|
||||
'handle_sigill': 1,
|
||||
'print_summary': 1,
|
||||
'use_sigaltstack': 1,
|
||||
}
|
||||
|
||||
|
||||
def _eval_value(value_string):
|
||||
"""Returns evaluated value."""
|
||||
try:
|
||||
return ast.literal_eval(value_string)
|
||||
except:
|
||||
# String fallback.
|
||||
return value_string
|
||||
|
||||
|
||||
def join_memory_tool_options(options):
|
||||
"""Joins a dict holding memory tool options into a string that can be set in
|
||||
the environment."""
|
||||
return ':'.join('%s=%s' % (key, str(value))
|
||||
for key, value in sorted(six.iteritems(options)))
|
||||
|
||||
|
||||
def _maybe_convert_to_int(value):
|
||||
"""Returns the int representation contained by string |value| if it contains
|
||||
one. Otherwise returns |value|."""
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
return value
|
||||
|
||||
|
||||
# Matches anything that isn't an unquoted (ie: not between two single or two
|
||||
# double quotes) colon.
|
||||
UNQUOTED_COLON_REGEX = re.compile('((?:[^\'":]|\'[^\']*\'|"[^"]*")+)')
|
||||
|
||||
|
||||
def _parse_memory_tool_options(options_str):
|
||||
"""Parses memory tool options into a dict."""
|
||||
parsed = {}
|
||||
|
||||
for item in UNQUOTED_COLON_REGEX.split(options_str):
|
||||
# Regex split can give us empty strings at the beginning and the end. Skip
|
||||
# these.
|
||||
if not item:
|
||||
continue
|
||||
# Regex split gives us each ':'. Skip these.
|
||||
if item == ':':
|
||||
continue
|
||||
values = item.split('=', 1)
|
||||
if len(values) != 2:
|
||||
# TODO(mbarbella): Factor this out of environment, and switch to logging
|
||||
# an error and continuing. This error should be recoverable.
|
||||
raise ValueError('Invalid memory tool option "%s"' % item)
|
||||
|
||||
option_name = values[0]
|
||||
option_value = _maybe_convert_to_int(values[1])
|
||||
parsed[option_name] = option_value
|
||||
|
||||
return parsed
|
||||
|
||||
|
||||
def _quote_value_if_needed(value):
|
||||
"""Quote environment value as needed for certain platforms like Windows."""
|
||||
result = value
|
||||
if ' ' in result or ':' in result:
|
||||
result = '"%s"' % result
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def copy():
|
||||
"""Return a safe copy of the environment."""
|
||||
environment_copy = os.environ.copy()
|
||||
return environment_copy
|
||||
|
||||
|
||||
def disable_lsan():
|
||||
"""Disable leak detection (if enabled)."""
|
||||
if get_current_memory_tool_var() != 'ASAN_OPTIONS':
|
||||
return
|
||||
|
||||
sanitizer_options = get_memory_tool_options('ASAN_OPTIONS', {})
|
||||
sanitizer_options['detect_leaks'] = 0
|
||||
set_memory_tool_options('ASAN_OPTIONS', sanitizer_options)
|
||||
|
||||
|
||||
def get_asan_options(redzone_size, malloc_context_size, quarantine_size_mb,
|
||||
bot_platform, leaks, disable_ubsan):
|
||||
"""Generates default ASAN options."""
|
||||
asan_options = {}
|
||||
|
||||
# Default options needed for all cases.
|
||||
asan_options['alloc_dealloc_mismatch'] = 0
|
||||
asan_options['print_scariness'] = 1
|
||||
asan_options['strict_memcmp'] = 0
|
||||
|
||||
# Set provided redzone size.
|
||||
if redzone_size:
|
||||
asan_options['redzone'] = redzone_size
|
||||
|
||||
# This value is used in determining whether to report OOM crashes or not.
|
||||
set_value('REDZONE', redzone_size)
|
||||
|
||||
# Set maximum number of stack frames to report.
|
||||
if malloc_context_size:
|
||||
asan_options['malloc_context_size'] = malloc_context_size
|
||||
|
||||
# Set quarantine size.
|
||||
if quarantine_size_mb:
|
||||
asan_options['quarantine_size_mb'] = quarantine_size_mb
|
||||
|
||||
# Test for leaks if this is an LSan-enabled job type.
|
||||
if get_value('LSAN') and leaks and not get_value('USE_EXTRA_SANITIZERS'):
|
||||
lsan_options = join_memory_tool_options(get_lsan_options())
|
||||
set_value('LSAN_OPTIONS', lsan_options)
|
||||
asan_options['detect_leaks'] = 1
|
||||
else:
|
||||
remove_key('LSAN_OPTIONS')
|
||||
asan_options['detect_leaks'] = 0
|
||||
|
||||
# FIXME: Support container overflow on Android.
|
||||
if is_android(bot_platform):
|
||||
asan_options['detect_container_overflow'] = 0
|
||||
|
||||
# Enable stack use-after-return.
|
||||
asan_options['detect_stack_use_after_return'] = 1
|
||||
asan_options['max_uar_stack_size_log'] = 16
|
||||
|
||||
# Other less important default options for all cases.
|
||||
asan_options.update({
|
||||
'allocator_may_return_null': 1,
|
||||
'allow_user_segv_handler': 0,
|
||||
'check_malloc_usable_size': 0,
|
||||
'detect_odr_violation': 0,
|
||||
'fast_unwind_on_fatal': 1,
|
||||
'print_suppressions': 0,
|
||||
})
|
||||
|
||||
# Add common sanitizer options.
|
||||
asan_options.update(COMMON_SANITIZER_OPTIONS)
|
||||
|
||||
# FIXME: For Windows, rely on online symbolization since llvm-symbolizer.exe
|
||||
# in build archive does not work.
|
||||
asan_options['symbolize'] = int(bot_platform == 'WINDOWS')
|
||||
|
||||
# For Android, allow user defined segv handler to work.
|
||||
if is_android(bot_platform):
|
||||
asan_options['allow_user_segv_handler'] = 1
|
||||
|
||||
# Check if UBSAN is enabled as well for this ASAN build.
|
||||
# If yes, set UBSAN_OPTIONS and enable suppressions.
|
||||
if get_value('UBSAN'):
|
||||
if disable_ubsan:
|
||||
ubsan_options = get_ubsan_disabled_options()
|
||||
else:
|
||||
ubsan_options = get_ubsan_options()
|
||||
|
||||
# Remove |symbolize| explicitly to avoid overridding ASan defaults.
|
||||
ubsan_options.pop('symbolize', None)
|
||||
|
||||
set_value('UBSAN_OPTIONS', join_memory_tool_options(ubsan_options))
|
||||
|
||||
return asan_options
|
||||
|
||||
|
||||
def get_current_memory_tool_var():
|
||||
"""Get the environment variable name for the current job type's sanitizer."""
|
||||
memory_tool_name = get_memory_tool_name(get_value('JOB_NAME'))
|
||||
if not memory_tool_name:
|
||||
return None
|
||||
|
||||
return memory_tool_name + '_OPTIONS'
|
||||
|
||||
|
||||
def get_memory_tool_options(env_var, default_value=None):
|
||||
"""Get the current memory tool options as a dict. Returns |default_value| if
|
||||
|env_var| isn't set. Otherwise returns a dictionary containing the memory tool
|
||||
options and their values."""
|
||||
env_value = get_value(env_var)
|
||||
if env_value is not None:
|
||||
return _parse_memory_tool_options(env_value)
|
||||
|
||||
return default_value
|
||||
|
||||
|
||||
def get_instrumented_libraries_paths():
|
||||
"""Get the instrumented libraries path for the current sanitizer."""
|
||||
memory_tool_name = get_memory_tool_name(get_value('JOB_NAME'))
|
||||
if not memory_tool_name:
|
||||
return None
|
||||
|
||||
if memory_tool_name == 'MSAN':
|
||||
if 'no-origins' in get_value('BUILD_URL', ''):
|
||||
memory_tool_name += '_NO_ORIGINS'
|
||||
else:
|
||||
memory_tool_name += '_CHAINED'
|
||||
|
||||
paths = get_value('INSTRUMENTED_LIBRARIES_PATHS_' + memory_tool_name)
|
||||
if not paths:
|
||||
return None
|
||||
|
||||
return paths.split(':')
|
||||
|
||||
|
||||
def get_default_tool_path(tool_name):
|
||||
"""Get the default tool for this platform (from scripts/ dir)."""
|
||||
if is_android():
|
||||
# For android devices, we do symbolization on the host machine, which is
|
||||
# linux. So, we use the linux version of llvm-symbolizer.
|
||||
platform_override = 'linux'
|
||||
else:
|
||||
# No override needed, use default.
|
||||
platform_override = None
|
||||
|
||||
tool_filename = get_executable_filename(tool_name)
|
||||
tool_path = os.path.join(
|
||||
get_platform_resources_directory(platform_override), tool_filename)
|
||||
return tool_path
|
||||
|
||||
def get_sanitizer_options_for_display():
|
||||
"""Return a list of sanitizer options with quoted values."""
|
||||
result = []
|
||||
for tool in SUPPORTED_MEMORY_TOOLS_FOR_OPTIONS:
|
||||
options_variable = tool + '_OPTIONS'
|
||||
options_value = os.getenv(options_variable)
|
||||
if not options_value:
|
||||
continue
|
||||
result.append('{options_variable}={options_value}'.format(
|
||||
options_variable=options_variable, options_value=options_value))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_llvm_symbolizer_path():
|
||||
"""Get the path of the llvm-symbolizer binary."""
|
||||
llvm_symbolizer_path = get_value('LLVM_SYMBOLIZER_PATH')
|
||||
|
||||
if llvm_symbolizer_path and os.path.exists(llvm_symbolizer_path):
|
||||
# Make sure that llvm symbolizer binary is executable.
|
||||
os.chmod(llvm_symbolizer_path, 0o750)
|
||||
|
||||
return_code = subprocess.call(
|
||||
[llvm_symbolizer_path, '--help'],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
if return_code == 0:
|
||||
# llvm-symbolize works, return it.
|
||||
return llvm_symbolizer_path
|
||||
|
||||
# Either
|
||||
# 1. llvm-symbolizer was not found in build archive. OR
|
||||
# 2. llvm-symbolizer fails due to dependency issue, clang regression, etc.
|
||||
# So, use our own version of llvm-symbolizer.
|
||||
llvm_symbolizer_path = get_default_tool_path('llvm-symbolizer')
|
||||
|
||||
# Make sure that we have a default llvm-symbolizer for this platform.
|
||||
if not os.path.exists(llvm_symbolizer_path):
|
||||
return None
|
||||
|
||||
# Make sure that llvm symbolizer binary is executable.
|
||||
os.chmod(llvm_symbolizer_path, 0o750)
|
||||
return llvm_symbolizer_path
|
||||
|
||||
|
||||
def get_root_directory():
|
||||
"""Return root directory."""
|
||||
return get_value('ROOT_DIR')
|
||||
|
||||
|
||||
def get_startup_scripts_directory():
|
||||
"""Return path to startup scripts."""
|
||||
return os.path.join(get_value('ROOT_DIR'), 'src', 'python', 'bot', 'startup')
|
||||
|
||||
|
||||
def get_config_directory():
|
||||
"""Return the path to the configs directory."""
|
||||
config_dir = get_value('CONFIG_DIR_OVERRIDE')
|
||||
if config_dir:
|
||||
return config_dir
|
||||
|
||||
if is_running_on_app_engine():
|
||||
# Root is already src/appengine.
|
||||
return 'config'
|
||||
|
||||
# Running on bot, give path to config folder inside appengine dir.
|
||||
return os.path.join(get_root_directory(), 'src', 'appengine', 'config')
|
||||
|
||||
|
||||
def get_gae_config_directory():
|
||||
"""Return the path to the google appengine configs directory."""
|
||||
return os.path.join(get_config_directory(), 'gae')
|
||||
|
||||
|
||||
def get_gce_config_directory():
|
||||
"""Return the path to the google compute engine configs directory."""
|
||||
return os.path.join(get_config_directory(), 'gce')
|
||||
|
||||
|
||||
def get_resources_directory():
|
||||
"""Return the path to the resources directory."""
|
||||
return os.path.join(get_root_directory(), 'resources')
|
||||
|
||||
|
||||
def get_platform_resources_directory(platform_override=None):
|
||||
"""Return the path to platform-specific resources directory."""
|
||||
plt = platform_override or platform()
|
||||
|
||||
# Android resources share the same android directory.
|
||||
if is_android(plt):
|
||||
plt = 'ANDROID'
|
||||
|
||||
return os.path.join(get_resources_directory(), 'platform', plt.lower())
|
||||
|
||||
|
||||
def get_suppressions_directory():
|
||||
"""Return the path to the suppressions directory."""
|
||||
return os.path.join(get_config_directory(), 'suppressions')
|
||||
|
||||
|
||||
def get_suppressions_file(sanitizer, suffix='suppressions'):
|
||||
"""Return the path to sanitizer suppressions file, if exists."""
|
||||
sanitizer_suppressions_filename = '{sanitizer}_{suffix}.txt'.format(
|
||||
sanitizer=sanitizer, suffix=suffix)
|
||||
sanitizer_suppressions_file_path = os.path.join(
|
||||
get_suppressions_directory(), sanitizer_suppressions_filename)
|
||||
|
||||
if not os.path.exists(sanitizer_suppressions_file_path):
|
||||
return None
|
||||
|
||||
if not os.path.getsize(sanitizer_suppressions_file_path):
|
||||
return None
|
||||
|
||||
return sanitizer_suppressions_file_path
|
||||
|
||||
|
||||
def get_lsan_options():
|
||||
"""Generates default LSAN options."""
|
||||
lsan_suppressions_path = get_suppressions_file('lsan')
|
||||
lsan_options = {
|
||||
'print_suppressions': 0,
|
||||
}
|
||||
|
||||
# Add common sanitizer options.
|
||||
lsan_options.update(COMMON_SANITIZER_OPTIONS)
|
||||
|
||||
if lsan_suppressions_path:
|
||||
lsan_options['suppressions'] = lsan_suppressions_path
|
||||
|
||||
return lsan_options
|
||||
|
||||
|
||||
def get_kasan_options():
|
||||
"""Generates default KASAN options."""
|
||||
kasan_options = {'symbolize': 0}
|
||||
|
||||
# Add common sanitizer options.
|
||||
kasan_options.update(COMMON_SANITIZER_OPTIONS)
|
||||
|
||||
return kasan_options
|
||||
|
||||
|
||||
def get_msan_options():
|
||||
"""Generates default MSAN options."""
|
||||
msan_options = {'symbolize': 0}
|
||||
|
||||
# Add common sanitizer options.
|
||||
msan_options.update(COMMON_SANITIZER_OPTIONS)
|
||||
|
||||
return msan_options
|
||||
|
||||
|
||||
def get_platform_group():
|
||||
"""Return the platform group (specified via QUEUE_OVERRIDE) if it
|
||||
exists, otherwise platform()."""
|
||||
platform_group = get_value('QUEUE_OVERRIDE')
|
||||
if platform_group:
|
||||
return platform_group
|
||||
|
||||
return platform()
|
||||
|
||||
|
||||
def get_memory_tool_name(job_name):
|
||||
"""Figures out name of memory debugging tool."""
|
||||
for tool in SUPPORTED_MEMORY_TOOLS_FOR_OPTIONS:
|
||||
if tool_matches(tool, job_name):
|
||||
return tool
|
||||
|
||||
# If no tool specified, assume it is ASAN. Also takes care of LSAN job type.
|
||||
return 'ASAN'
|
||||
|
||||
|
||||
def get_memory_tool_display_string(job_name):
|
||||
"""Return memory tool string for a testcase."""
|
||||
memory_tool_name = get_memory_tool_name(job_name)
|
||||
sanitizer_name = SANITIZER_NAME_MAP.get(memory_tool_name)
|
||||
if not sanitizer_name:
|
||||
return 'Memory Tool: %s' % memory_tool_name
|
||||
|
||||
return 'Sanitizer: %s (%s)' % (sanitizer_name, memory_tool_name)
|
||||
|
||||
|
||||
def get_executable_filename(executable_name):
|
||||
"""Return the filename for the given executable."""
|
||||
if platform() != 'WINDOWS':
|
||||
return executable_name
|
||||
|
||||
extension = '.exe'
|
||||
if executable_name.endswith(extension):
|
||||
return executable_name
|
||||
|
||||
return executable_name + extension
|
||||
|
||||
|
||||
def get_tsan_options():
|
||||
"""Generates default TSAN options."""
|
||||
tsan_suppressions_path = get_suppressions_file('tsan')
|
||||
|
||||
tsan_options = {
|
||||
'atexit_sleep_ms': 200,
|
||||
'flush_memory_ms': 2000,
|
||||
'history_size': 3,
|
||||
'print_suppressions': 0,
|
||||
'report_thread_leaks': 0,
|
||||
'report_signal_unsafe': 0,
|
||||
'stack_trace_format': 'DEFAULT',
|
||||
'symbolize': 1,
|
||||
}
|
||||
|
||||
# Add common sanitizer options.
|
||||
tsan_options.update(COMMON_SANITIZER_OPTIONS)
|
||||
|
||||
if tsan_suppressions_path:
|
||||
tsan_options['suppressions'] = tsan_suppressions_path
|
||||
|
||||
return tsan_options
|
||||
|
||||
|
||||
def get_ubsan_options():
|
||||
"""Generates default UBSAN options."""
|
||||
# Note that UBSAN can work together with ASAN as well.
|
||||
ubsan_suppressions_path = get_suppressions_file('ubsan')
|
||||
|
||||
ubsan_options = {
|
||||
'halt_on_error': 1,
|
||||
'print_stacktrace': 1,
|
||||
'print_suppressions': 0,
|
||||
|
||||
# We use -fsanitize=unsigned-integer-overflow as an additional coverage
|
||||
# signal and do not want those errors to be reported by UBSan as bugs.
|
||||
# See https://github.com/google/oss-fuzz/issues/910 for additional info.
|
||||
'silence_unsigned_overflow': 1,
|
||||
'symbolize': 1,
|
||||
}
|
||||
|
||||
# Add common sanitizer options.
|
||||
ubsan_options.update(COMMON_SANITIZER_OPTIONS)
|
||||
|
||||
# TODO(crbug.com/877070): Make this code configurable on a per job basis.
|
||||
if ubsan_suppressions_path and not is_chromeos_system_job():
|
||||
ubsan_options['suppressions'] = ubsan_suppressions_path
|
||||
|
||||
return ubsan_options
|
||||
|
||||
|
||||
def get_ubsan_disabled_options():
|
||||
"""Generates ubsan options """
|
||||
return {
|
||||
'halt_on_error': 0,
|
||||
'print_stacktrace': 0,
|
||||
'print_suppressions': 0,
|
||||
}
|
||||
|
||||
|
||||
def get_value_string(environment_variable, default_value=None):
|
||||
"""Get environment variable (as a string)."""
|
||||
return os.getenv(environment_variable, default_value)
|
||||
|
||||
|
||||
def get_value(environment_variable, default_value=None):
|
||||
"""Return an environment variable value."""
|
||||
value_string = os.getenv(environment_variable)
|
||||
|
||||
# value_string will be None if the variable is not defined.
|
||||
if value_string is None:
|
||||
return default_value
|
||||
|
||||
# Exception for ANDROID_SERIAL. Sometimes serial can be just numbers,
|
||||
# so we don't want to it eval it.
|
||||
if environment_variable == 'ANDROID_SERIAL':
|
||||
return value_string
|
||||
|
||||
# Evaluate the value of the environment variable with string fallback.
|
||||
return _eval_value(value_string)
|
||||
|
||||
|
||||
def _job_substring_match(search_string, job_name):
|
||||
"""Return a bool on whether a string exists in a provided job name or
|
||||
use from environment if available (case insensitive)."""
|
||||
job_name = job_name or get_value('JOB_NAME')
|
||||
if not job_name:
|
||||
return False
|
||||
|
||||
return search_string in job_name.lower()
|
||||
|
||||
|
||||
def is_afl_job(job_name=None):
|
||||
"""Return True if the current job uses AFL."""
|
||||
return get_engine_for_job(job_name) == 'afl'
|
||||
|
||||
|
||||
def is_ios_job(job_name=None):
|
||||
"""Return True if the current job is for iOS."""
|
||||
return _job_substring_match('ios_', job_name)
|
||||
|
||||
|
||||
def is_chromeos_job(job_name=None):
|
||||
"""Return True if the current job is for ChromeOS."""
|
||||
return _job_substring_match('chromeos', job_name)
|
||||
|
||||
|
||||
def is_lkl_job(job_name=None):
|
||||
"""Return True if the current job is for ChromeOS."""
|
||||
return _job_substring_match('lkl', job_name)
|
||||
|
||||
|
||||
def is_chromeos_system_job(job_name=None):
|
||||
"""Return True if the current job is for ChromeOS system (i.e. not libFuzzer
|
||||
or entire Chrome browser for Chrome on ChromeOS)."""
|
||||
return is_chromeos_job(job_name) and get_value('CHROMEOS_SYSTEM')
|
||||
|
||||
|
||||
def is_libfuzzer_job(job_name=None):
|
||||
"""Return True if the current job uses libFuzzer."""
|
||||
return get_engine_for_job(job_name) == 'libFuzzer'
|
||||
|
||||
|
||||
def is_honggfuzz_job(job_name=None):
|
||||
"""Return True if the current job uses honggfuzz."""
|
||||
return get_engine_for_job(job_name) == 'honggfuzz'
|
||||
|
||||
|
||||
def is_kernel_fuzzer_job(job_name=None):
|
||||
"""Return True if the current job uses syzkaller."""
|
||||
return get_engine_for_job(job_name) == 'syzkaller'
|
||||
|
||||
|
||||
def is_centipede_fuzzer_job(job_name=None):
|
||||
"""Return True if the current job uses Centipede."""
|
||||
return get_engine_for_job(job_name) == 'centipede'
|
||||
|
||||
|
||||
def is_engine_fuzzer_job(job_name=None):
|
||||
"""Return True if this is an engine fuzzer."""
|
||||
return bool(get_engine_for_job(job_name))
|
||||
|
||||
|
||||
def get_engine_for_job(job_name=None):
|
||||
"""Get the engine for the given job."""
|
||||
if not job_name:
|
||||
job_name = get_value('JOB_NAME')
|
||||
|
||||
for engine in fuzzing.ENGINES:
|
||||
if engine.lower() in job_name:
|
||||
return engine
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def is_posix():
|
||||
"""Return True if we are on a posix platform (linux/unix and mac os)."""
|
||||
return os.name == 'posix'
|
||||
|
||||
|
||||
def is_trusted_host(ensure_connected=True):
|
||||
"""Return whether or not the current bot is a trusted host."""
|
||||
return get_value('TRUSTED_HOST') and (not ensure_connected or
|
||||
get_value('WORKER_BOT_NAME'))
|
||||
|
||||
|
||||
def is_untrusted_worker():
|
||||
"""Return whether or not the current bot is an untrusted worker."""
|
||||
return get_value('UNTRUSTED_WORKER')
|
||||
|
||||
|
||||
def is_running_on_app_engine():
|
||||
"""Return True if we are running on appengine (local or production)."""
|
||||
return (os.getenv('GAE_ENV') or is_running_on_app_engine_development() or
|
||||
os.getenv('SERVER_SOFTWARE', '').startswith('Google App Engine/'))
|
||||
|
||||
|
||||
def is_running_on_app_engine_development():
|
||||
"""Return True if running on the local development appengine server."""
|
||||
return (os.getenv('GAE_ENV') == 'dev' or
|
||||
os.getenv('SERVER_SOFTWARE', '').startswith('Development/'))
|
||||
|
||||
|
||||
def parse_environment_definition(environment_string):
|
||||
"""Parses a job's environment definition."""
|
||||
if not environment_string:
|
||||
return {}
|
||||
|
||||
definitions = [environment_string.splitlines()]
|
||||
values = {}
|
||||
for definition in definitions:
|
||||
for line in definition:
|
||||
if line.startswith('#') or not line.strip():
|
||||
continue
|
||||
|
||||
m = re.match('([^ =]+)[ ]*=[ ]*(.*)', line)
|
||||
if m:
|
||||
key = m.group(1).strip()
|
||||
value = m.group(2).strip()
|
||||
values[key] = value
|
||||
|
||||
return values
|
||||
|
||||
|
||||
def platform():
|
||||
"""Return the operating system type, unless an override is provided."""
|
||||
environment_override = get_value('OS_OVERRIDE')
|
||||
if environment_override:
|
||||
return environment_override.upper()
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
return 'WINDOWS'
|
||||
if sys.platform.startswith('linux'):
|
||||
return 'LINUX'
|
||||
if sys.platform == 'darwin':
|
||||
return 'MAC'
|
||||
|
||||
raise ValueError('Unsupported platform "%s".' % sys.platform)
|
||||
|
||||
|
||||
def remove_key(key_name):
|
||||
"""Remove environment |key| and its associated value."""
|
||||
if not key_name:
|
||||
return
|
||||
|
||||
if key_name not in os.environ:
|
||||
return
|
||||
|
||||
del os.environ[key_name]
|
||||
|
||||
|
||||
# Used by reset_environment to store the initial environment.
|
||||
_initial_environment = None
|
||||
|
||||
|
||||
def set_common_environment_variables():
|
||||
"""Sets environment variables common for different memory debugging tools."""
|
||||
# G_SLICE = always-malloc: make glib use system malloc.
|
||||
# NSS_DISABLE_UNLOAD = 1: make nss skip dlclosing dynamically loaded modules,
|
||||
# which would result in "obj:*" in backtraces.
|
||||
# NSS_DISABLE_ARENA_FREE_LIST = 1: make nss use system malloc.
|
||||
set_value('G_SLICE', 'always-malloc')
|
||||
set_value('NSS_DISABLE_UNLOAD', 1)
|
||||
set_value('NSS_DISABLE_ARENA_FREE_LIST', 1)
|
||||
set_value('NACL_DANGEROUS_SKIP_QUALIFICATION_TEST', 1)
|
||||
|
||||
|
||||
def set_memory_tool_options(env_var, options_dict):
|
||||
"""Set current memory tool options."""
|
||||
set_value(env_var, join_memory_tool_options(options_dict))
|
||||
|
||||
|
||||
def set_environment_parameters_from_file(file_path):
|
||||
"""Set environment variables from a file."""
|
||||
if not os.path.exists(file_path):
|
||||
return
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
file_data = f.read()
|
||||
|
||||
for line in file_data.splitlines():
|
||||
if line.startswith('#') or not line.strip():
|
||||
continue
|
||||
|
||||
m = re.match('([^ =]+)[ ]*=[ ]*(.*)', line)
|
||||
if m:
|
||||
environment_variable = m.group(1)
|
||||
environment_variable_value = m.group(2)
|
||||
set_value(environment_variable, environment_variable_value)
|
||||
|
||||
|
||||
def update_symbolizer_options(tool_options, symbolize_inline_frames=False):
|
||||
"""Checks and updates the necessary symbolizer options such as
|
||||
`external_symbolizer_path` and `symbolize_inline_frames`."""
|
||||
if 'external_symbolizer_path' not in tool_options:
|
||||
llvm_symbolizer_path = get_llvm_symbolizer_path()
|
||||
if llvm_symbolizer_path:
|
||||
tool_options.update({
|
||||
'external_symbolizer_path':
|
||||
_quote_value_if_needed(llvm_symbolizer_path)
|
||||
})
|
||||
if 'symbolize_inline_frames' not in tool_options:
|
||||
tool_options.update({
|
||||
'symbolize_inline_frames': str(symbolize_inline_frames).lower()
|
||||
})
|
||||
|
||||
def set_default_vars():
|
||||
"""Set default environment vars and values."""
|
||||
env_file_path = os.path.join(get_value('ROOT_DIR'), 'bot', 'env.yaml')
|
||||
with open(env_file_path) as file_handle:
|
||||
env_file_contents = file_handle.read()
|
||||
|
||||
env_vars_and_values = yaml.safe_load(env_file_contents)
|
||||
for variable, value in six.iteritems(env_vars_and_values):
|
||||
# We cannot call set_value here.
|
||||
os.environ[variable] = str(value)
|
||||
|
||||
|
||||
def set_tsan_max_history_size():
|
||||
"""Sets maximum history size for TSAN tool."""
|
||||
tsan_options = get_value('TSAN_OPTIONS')
|
||||
if not tsan_options:
|
||||
return
|
||||
|
||||
tsan_max_history_size = 7
|
||||
for i in range(tsan_max_history_size):
|
||||
tsan_options = (
|
||||
tsan_options.replace('history_size=%d' % i,
|
||||
'history_size=%d' % tsan_max_history_size))
|
||||
|
||||
set_value('TSAN_OPTIONS', tsan_options)
|
||||
|
||||
|
||||
def set_value(environment_variable, value):
|
||||
"""Set an environment variable."""
|
||||
value_str = str(value)
|
||||
environment_variable_str = str(environment_variable)
|
||||
value_str = value_str.replace('%ROOT_DIR%', os.getenv('ROOT_DIR', ''))
|
||||
os.environ[environment_variable_str] = value_str
|
||||
|
||||
|
||||
def tool_matches(tool_name, job_name):
|
||||
"""Return if the memory debugging tool is used in this job."""
|
||||
match_prefix = '(.*[^a-zA-Z]|^)%s'
|
||||
matches_tool = re.match(match_prefix % tool_name.lower(), job_name.lower())
|
||||
return bool(matches_tool)
|
||||
|
||||
|
||||
def appengine_noop(func):
|
||||
"""Wrap a function into no-op and return None if running on App Engine."""
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if is_running_on_app_engine():
|
||||
return None
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def bot_noop(func):
|
||||
"""Wrap a function into no-op and return None if running on bot."""
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
is_bot = not is_running_on_app_engine()
|
||||
if is_bot:
|
||||
return None
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def is_local_development():
|
||||
"""Return True if running in local development environment (e.g. running
|
||||
a bot locally, excludes tests)."""
|
||||
return bool(get_value('LOCAL_DEVELOPMENT') and not get_value('PY_UNITTESTS'))
|
||||
|
||||
|
||||
def local_noop(func):
|
||||
"""Wrap a function into no-op and return None if running in local
|
||||
development environment."""
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if (is_local_development() or is_running_on_app_engine_development()):
|
||||
return None
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def is_ephemeral():
|
||||
"""Return whether or not we are an ephemeral bot."""
|
||||
return get_value('EPHEMERAL')
|
||||
|
||||
|
||||
def is_android(plt=None):
|
||||
"""Return True if we are on android platform."""
|
||||
return 'ANDROID' in (plt or platform())
|
||||
|
||||
|
||||
def is_android_cuttlefish(plt=None):
|
||||
"""Return True if we are on android cuttlefish platform."""
|
||||
return 'ANDROID_X86' in (plt or platform())
|
||||
|
||||
|
||||
def is_android_kernel(plt=None):
|
||||
"""Return True if we are on android kernel platform groups."""
|
||||
return 'ANDROID_KERNEL' in (plt or get_platform_group())
|
||||
|
||||
|
||||
def is_android_real_device():
|
||||
"""Return True if we are on a real android device."""
|
||||
return platform() == 'ANDROID'
|
||||
|
||||
|
||||
def is_lib():
|
||||
"""Whether or not we're in libClusterFuzz."""
|
||||
return get_value('LIB_CF')
|
||||
|
||||
|
||||
def is_i386(job_type):
|
||||
return '_i386' in job_type
|
||||
@@ -0,0 +1,13 @@
|
||||
"""Fuzzing module."""
|
||||
|
||||
PUBLIC_ENGINES = (
|
||||
'libFuzzer',
|
||||
'afl',
|
||||
'honggfuzz',
|
||||
'googlefuzztest',
|
||||
'centipede',
|
||||
)
|
||||
|
||||
PRIVATE_ENGINES = ('syzkaller',)
|
||||
|
||||
ENGINES = PUBLIC_ENGINES + PRIVATE_ENGINES
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
# Default page size of 4KB.
|
||||
NULL_DEREFERENCE_BOUNDARY = 0x1000
|
||||
|
||||
ASSERT_CRASH_ADDRESSES = [
|
||||
0x0000bbadbeef,
|
||||
0x0000fbadbeef,
|
||||
0x00001f75b7dd,
|
||||
0x0000977537dd,
|
||||
0x00009f7537dd,
|
||||
]
|
||||
|
||||
def address_to_integer(address):
|
||||
"""Attempt to convert an address from a string (hex) to an integer."""
|
||||
try:
|
||||
return int(address, 16)
|
||||
except:
|
||||
return 0
|
||||
|
||||
|
||||
def is_null_dereference(int_address):
|
||||
"""Check to see if this is a null dereference crash address."""
|
||||
return int_address < NULL_DEREFERENCE_BOUNDARY
|
||||
|
||||
|
||||
def is_assert_crash_address(int_address):
|
||||
"""Check to see if this is an ASSERT crash based on the address."""
|
||||
return int_address in ASSERT_CRASH_ADDRESSES
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,955 @@
|
||||
# Clusterfuzz commit 46968a275474422db32530a2126dfa30dc7f3d15
|
||||
|
||||
from buttercup.common.clusterfuzz_parser import inspect
|
||||
import re
|
||||
import logging
|
||||
|
||||
logs = logging.getLogger(__name__)
|
||||
|
||||
C_CPP_EXTENSIONS = ['c', 'cc', 'cpp', 'cxx', 'h', 'hh', 'hpp', 'hxx']
|
||||
|
||||
# Patterns which cannot be compiled directly, or which are used for direct
|
||||
# comparison.
|
||||
CHECK_FAILURE_PATTERN = r'Check failed: '
|
||||
JNI_ERROR_STRING = r'JNI DETECTED ERROR IN APPLICATION:'
|
||||
|
||||
# Common log prefix format for Google fatal logs.
|
||||
GOOGLE_LOG_FATAL_PREFIX = r'^F\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+\d+\s+(.*):\d+\]'
|
||||
|
||||
# Compiled regular expressions.
|
||||
ANDROID_ABORT_REGEX = re.compile(r'^Abort message: (.*)')
|
||||
ANDROID_FATAL_EXCEPTION_REGEX = re.compile(r'.*FATAL EXCEPTION.*:')
|
||||
ANDROID_KERNEL_ERROR_REGEX = re.compile(
|
||||
r'.*Internal error: (Oops)?( -|:) (BUG|[0-9a-fA-F]+)')
|
||||
ANDROID_KERNEL_STACK_FRAME_REGEX = re.compile(
|
||||
# e.g. "[ 1998.156940] [<c0667574>] "
|
||||
r'[^(]*\[\<([x0-9a-fA-F]+)\>\]\s+'
|
||||
# e.g. "(msm_vidc_prepare_buf+0xa0/0x124)"; function (3), offset (4)
|
||||
r'\(?(([\w]+)\+([\w]+)/[\w]+)\)?')
|
||||
ANDROID_KERNEL_STACK_FRAME_NO_ADDRESS_REGEX = re.compile(
|
||||
# e.g. "(msm_vidc_prepare_buf+0xa0/0x124)"; function (2), offset (3)
|
||||
r'\(?(([\w]+)\+([\w]+)/[\w]+)\)?')
|
||||
ANDROID_KERNEL_TIME_REGEX = re.compile(r'^\[\s*\d+\.\d+\]\s')
|
||||
# Parentheses are optional.
|
||||
ANDROID_PROCESS_NAME_REGEX = re.compile(r'.*[(](.*)[)]$')
|
||||
ANDROID_SEGV_REGEX = re.compile(r'.*signal.*\(SIG.*fault addr ([^ ]*)(.*)')
|
||||
ASAN_INVALID_FREE_REGEX = re.compile(
|
||||
r'.*AddressSanitizer: '
|
||||
r'attempting free on address which was not malloc\(\)-ed: '
|
||||
r'([xX0-9a-fA-F]+)')
|
||||
ASAN_DOUBLE_FREE_REGEX = re.compile(
|
||||
r'.*(AddressSanitizer).*double-free'
|
||||
r' on (unknown address |address |)([xX0-9a-fA-F]+)')
|
||||
ASAN_MEMCPY_OVERLAP_REGEX = re.compile(
|
||||
r'.*(AddressSanitizer).*memcpy-param-overlap'
|
||||
r'[^\[]*([\[].*[)])')
|
||||
ASAN_REGEX = re.compile(
|
||||
r'.*ERROR: (HWAddressSanitizer|AddressSanitizer)[: ]*[ ]*([^(:;]+)')
|
||||
ASSERT_REGEX = re.compile(
|
||||
r'(?:\[.*?\]|.*\.(?:%s):.*)?' % ('|'.join(C_CPP_EXTENSIONS)) +
|
||||
r'\s*(?:ASSERT(?:ION)? FAIL(?:URE|ED)|panic): (.*)', re.IGNORECASE)
|
||||
ASSERT_REGEX_GOOGLE = re.compile(GOOGLE_LOG_FATAL_PREFIX +
|
||||
r'.*assertion failed at\s.*\sin\s*.*: (.*)')
|
||||
ASSERT_REGEX_GLIBC = re.compile(
|
||||
r'.*:\s*assertion [`\'"]?(.*?)[`\'"]? failed\.?$', re.IGNORECASE)
|
||||
# The 'assertion .* failed' is suffixed with the failure reason. E.g.,
|
||||
# 'assertion __dest < len() failed: sample_array[] index out of bounds',
|
||||
# 'assertion !full() failed: sample_function() called on an empty vector'
|
||||
ASSERT_REGEX_GLIBC_SUFFIXED = re.compile(
|
||||
r'.*\S.*\/.*:\d+:\s*assertion .* failed:\s*(\S.*)')
|
||||
ASSERT_NOT_REACHED_REGEX = re.compile(r'^\s*SHOULD NEVER BE REACHED\s*$')
|
||||
CENTIPEDE_TIMEOUT_REGEX = re.compile(
|
||||
r'^========= Timeout of \d+ seconds exceeded; exiting')
|
||||
CFI_ERROR_REGEX = re.compile(
|
||||
r'(.*): runtime error: control flow integrity check for type (.*) '
|
||||
r'failed during (.*vtable address ([xX0-9a-fA-F]+)|.*)')
|
||||
CFI_INVALID_DOWNCAST_REGEX = re.compile(r'.*note: vtable is of type (.*)')
|
||||
CFI_INVALID_VPTR_REGEX = re.compile(r'.*note: invalid vtable$')
|
||||
CFI_FUNC_DEFINED_HERE_REGEX = re.compile(r'.*note: .* defined here$')
|
||||
CFI_NODEBUG_ERROR_MARKER_REGEX = re.compile(
|
||||
r'CFI: Most likely a control flow integrity violation;.*')
|
||||
CHROME_CHECK_FAILURE_REGEX = re.compile(
|
||||
r'\s*\[[^\]]*[:]([^\](]*).*\].*Check failed[:]\s*(.*)')
|
||||
CHROME_STACK_FRAME_REGEX = re.compile(
|
||||
r'[ ]*(#(?P<frame_id>[0-9]+)[ ]' # frame id (2)
|
||||
r'([xX0-9a-fA-F]+)[ ])' # addr (3)
|
||||
r'([^/\\]+)$') # rest, usually fun (4); may have off
|
||||
CHROME_WIN_STACK_FRAME_REGEX = re.compile(
|
||||
r'[ ]*([^/\\]+) ' # fun (1)
|
||||
r'\[([xX0-9a-fA-F]+)\+' # fun_base (2)
|
||||
r'(\d+)\]' # off[dec] (3)
|
||||
r'( \((.*):(\d+)\))?') # if available, file (5) and line (6)
|
||||
CHROME_MAC_STACK_FRAME_REGEX = re.compile(
|
||||
r'(?P<frame_id>\d+)\s+' # frame id (1)
|
||||
r'(([\w ]+)|(\?\?\?))\s+' # image (2)
|
||||
r'([xX0-9a-fA-F]+)\s+' # addr[hex] (5)
|
||||
r'([^/\\]+)\s*\+\s*' # fun (6)
|
||||
r'(\d+)') # off[dec] (7)
|
||||
MSAN_TSAN_REGEX = re.compile(
|
||||
r'.*(ThreadSanitizer|MemorySanitizer):\s+(?!ABRT)(?!ILL)([^(:]+)')
|
||||
EXTRA_SANITIZERS_COMMAND_INJECTION_REGEX = re.compile(
|
||||
r'===BUG DETECTED: Shell (corruption|injection)===')
|
||||
EXTRA_SANITIZERS_ARBITRARY_FILE_OPEN_REGEX = re.compile(
|
||||
r'===BUG DETECTED: Arbitrary file open===')
|
||||
EXTRA_SANITIZERS_ARBITRARY_DNS = re.compile(
|
||||
r'===BUG DETECTED: Arbitrary domain name resolution===')
|
||||
EXTRA_SANITIZERS_PYSECSAN = re.compile(r'===BUG DETECTED: PySecSan:')
|
||||
FATAL_ERROR_GENERIC_FAILURE = re.compile(r'#\s+()(.*)')
|
||||
FATAL_ERROR_CHECK_FAILURE = re.compile(
|
||||
r'#\s+(Check failed: |RepresentationChangerError: node #\d+:)(.*)')
|
||||
FATAL_ERROR_DCHECK_FAILURE = re.compile(r'#\s+(Debug check failed: )(.*)')
|
||||
FATAL_ERROR_REGEX = re.compile(r'#\s*Fatal error in (.*)')
|
||||
FATAL_ERROR_LINE_REGEX = re.compile(r'#\s*Fatal error in (.*), line [0-9]+')
|
||||
FATAL_ERROR_UNREACHABLE = re.compile(r'# un(reachable|implemented) code')
|
||||
GENERIC_SEGV_HANDLER_REGEX = re.compile(
|
||||
'Received signal 11 SEGV_[A-Z]+ ([0-9a-f]*)')
|
||||
GOOGLE_CHECK_FAILURE_REGEX = re.compile(GOOGLE_LOG_FATAL_PREFIX +
|
||||
r'\s*Check failed[:]\s*(.*)')
|
||||
GOOGLE_LOG_FATAL_REGEX = re.compile(GOOGLE_LOG_FATAL_PREFIX + r'\s*(.*)')
|
||||
GPU_PROCESS_FAILURE = re.compile(r'.*GPU process exited unexpectedly.*')
|
||||
HWASAN_ALLOCATION_TAIL_OVERWRITTEN_ADDRESS_REGEX = re.compile(
|
||||
r'.*ERROR: HWAddressSanitizer: allocation-tail-overwritten; '
|
||||
r'heap object \[([xX0-9a-fA-F]+),.*of size')
|
||||
JAZZER_JAVA_SECURITY_EXCEPTION_REGEX = re.compile(
|
||||
'== Java Exception: .*FuzzerSecurityIssue')
|
||||
JAZZER_JAVA_EXCEPTION_REGEX = re.compile('== Java Exception: .*')
|
||||
JAVA_EXCEPTION_CRASH_STATE_REGEX = re.compile(r'\s*at (.*)\(.*\)')
|
||||
KERNEL_BUG = re.compile(r'kernel BUG at (.*)')
|
||||
KASAN_ACCESS_TYPE_REGEX = re.compile(r'(Read|Write) of size ([0-9]+)')
|
||||
KASAN_ACCESS_TYPE_ADDRESS_REGEX = re.compile(
|
||||
r'(Read|Write) of size ([0-9]+) at (addr|address) ([a-f0-9]+)')
|
||||
KASAN_CRASH_TYPE_ADDRESS_REGEX = re.compile(
|
||||
r'BUG: KASAN: (.*) (in|on).*(addr|address) ([a-f0-9]+)')
|
||||
KASAN_CRASH_TYPE_ADDRESS_RANGE_REGEX = re.compile(
|
||||
r'KASAN: (.*?) (in|on) range \[([a-z0-9]+)-([a-z0-9]+)\]')
|
||||
KASAN_CRASH_TYPE_FUNCTION_REGEX = re.compile(
|
||||
r'BUG: KASAN: (.*) (in|on).* ([\w]+)\+([\w]+)\/([\w]+)')
|
||||
KASAN_GPF_REGEX = re.compile(r'general protection fault:.*KASAN')
|
||||
KERNEL_PANIC = re.compile(r'Kernel panic - not syncing: (.*)')
|
||||
LIBFUZZER_DEADLY_SIGNAL_REGEX = re.compile(
|
||||
r'.*ERROR:\s*libFuzzer:\s*deadly signal')
|
||||
LIBFUZZER_FUZZ_TARGET_EXITED_REGEX = re.compile(
|
||||
r'.*ERROR:\s*libFuzzer:\s*fuzz target exited')
|
||||
LIBFUZZER_OVERWRITES_CONST_INPUT_REGEX = re.compile(
|
||||
r'.*ERROR:\s*libFuzzer:\s*fuzz target overwrites its const input')
|
||||
LIBFUZZER_TIMEOUT_REGEX = re.compile(r'.*ERROR:\s*libFuzzer:\s*timeout')
|
||||
LIBRARY_NOT_FOUND_ANDROID_REGEX = re.compile(
|
||||
r'.*: library ([`\'"])(.*)\1 not found')
|
||||
LIBRARY_NOT_FOUND_LINUX_REGEX = re.compile(
|
||||
r'.*error while loading shared libraries: ([^:]*): '
|
||||
r'cannot open shared object file')
|
||||
LINUX_GDB_CRASH_TYPE_REGEX = re.compile(r'Program received signal ([a-zA-Z]+),')
|
||||
LINUX_GDB_CRASH_ADDRESS_REGEX = re.compile(r'rip[ ]+([xX0-9a-fA-F]+)')
|
||||
LINUX_GDB_CRASH_ADDRESS_NO_REGISTERS_REGEX = re.compile(
|
||||
r'^(0[xX][0-9a-fA-F]+)\s+in\s+')
|
||||
LSAN_DIRECT_LEAK_REGEX = re.compile(r'Direct leak of ')
|
||||
LSAN_INDIRECT_LEAK_REGEX = re.compile(r'Indirect leak of ')
|
||||
MAC_GDB_CRASH_ADDRESS_REGEX = re.compile(
|
||||
r'Reason:.*at address[^0-9]*([0-9a-zA-Z]+)')
|
||||
OUT_OF_MEMORY_REGEX = re.compile(r'.*(?:%s).*' % '|'.join([
|
||||
r'# Allocation failed.*out of memory',
|
||||
r'::OnNoMemory',
|
||||
r'ERROR.*Sanitizer failed to allocate',
|
||||
r'FatalProcessOutOfMemory', # V8
|
||||
r'Fatal (?:process|JavaScript) out of memory:', # V8
|
||||
r'Fatal JavaScript invalid size error', # V8
|
||||
r'FX_OutOfMemoryTerminate',
|
||||
r'Out of memory\. Dying.',
|
||||
r'Out of memory\. size=',
|
||||
r'Sanitizer: allocation-size-too-big',
|
||||
r'Sanitizer: calloc-overflow',
|
||||
r'Sanitizer: calloc parameters overflow',
|
||||
r'Sanitizer: requested allocation size.*exceeds maximum supported size',
|
||||
r'Sanitizer: out of memory',
|
||||
r'TerminateBecauseOutOfMemory',
|
||||
r'allocator is out of memory trying to allocate',
|
||||
r'blinkGCOutOfMemory',
|
||||
r'couldnt allocate.*Out of memory',
|
||||
r'libFuzzer: out-of-memory \(',
|
||||
r'rss limit exhausted',
|
||||
r'in rust_oom',
|
||||
r'Failure description: out-of-memory', # Centipede.
|
||||
]))
|
||||
RUNTIME_ERROR_REGEX = re.compile(r'#\s*Runtime error in (.*)')
|
||||
RUNTIME_ERROR_LINE_REGEX = re.compile(r'#\s*Runtime error in (.*), line [0-9]+')
|
||||
RUST_ASSERT_REGEX = re.compile(r'thread\s.*\spanicked at \'([^\']*)',
|
||||
re.IGNORECASE)
|
||||
SAN_ABRT_REGEX = re.compile(r'.*[a-zA-Z]+Sanitizer: ABRT ')
|
||||
SAN_BREAKPOINT_REGEX = re.compile(r'.*[a-zA-Z]+Sanitizer: breakpoint ')
|
||||
SAN_CHECK_FAILURE_REGEX = re.compile(
|
||||
r'.*Sanitizer CHECK failed[:]\s*[^ ]*\s*(.*)')
|
||||
SAN_CRASH_TYPE_ADDRESS_REGEX = re.compile(
|
||||
r'[ ]*([^ ]*|Atomic [^ ]*) of size ([^ ]*) at ([^ ]*)')
|
||||
SAN_DEADLYSIGNAL_REGEX = re.compile(
|
||||
r'(Address|Leak|Memory|UndefinedBehavior|Thread)Sanitizer:DEADLYSIGNAL')
|
||||
CONCATENATED_SAN_DEADLYSIGNAL_REGEX = re.compile(
|
||||
r'\n([^\n]*\S[^\n]*)(' + SAN_DEADLYSIGNAL_REGEX.pattern + r')\n')
|
||||
SPLIT_CONCATENATED_SAN_DEADLYSIGNAL_REGEX = r'\n\1\n\2\n'
|
||||
SAN_FPE_REGEX = re.compile(r'.*[a-zA-Z]+Sanitizer: FPE ')
|
||||
SAN_ILL_REGEX = re.compile(r'.*[a-zA-Z]+Sanitizer: ILL ')
|
||||
SAN_TRAP_REGEX = re.compile(r'.*[a-zA-Z]+Sanitizer: TRAP ')
|
||||
SAN_SEGV_CRASH_TYPE_REGEX = re.compile(
|
||||
r'.*The signal is caused by a ([A-Z]+) memory access.')
|
||||
# FIXME: Replace when better ways to check signal crashes are available.
|
||||
SAN_SIGNAL_REGEX = re.compile(r'.*SCARINESS: (\d+) \(signal\)', re.DOTALL)
|
||||
SAN_STACK_FRAME_REGEX = re.compile(
|
||||
# frame id (1)
|
||||
r'\s*#(?P<frame_id>\d+)\s+'
|
||||
# addr (2)
|
||||
r'([xX0-9a-fA-F]+)\s+'
|
||||
# Format is [in {fun}[+{off}]] [{file}[:{line}[:{char}]]] [({mod}[+{off}])]
|
||||
# If there is fun and mod/file info, extract
|
||||
# fun+off, where fun (7, 5, 23), off (8)
|
||||
r'((in\s*(((.*)\+([xX0-9a-fA-F]+))|(.*)) '
|
||||
r'('
|
||||
# file:line:char, where file (12, 16), line (13, 17), char (14)
|
||||
r'(([^ ]+):(\d+):(\d+))|(([^ ]+):(\d+))'
|
||||
# or mod+off, where mod (19, 31), off (21, 32)
|
||||
r'|'
|
||||
r'(\(([^+]+)(\+([xX0-9a-fA-F]+))?\)))'
|
||||
r')'
|
||||
# If there is only fun info, extract
|
||||
r'|'
|
||||
r'(in\s*(((.*)\+([xX0-9a-fA-F]+))|(.*)))'
|
||||
# If there is only mod info, extract
|
||||
r'|'
|
||||
r'(\((((.*)\+([xX0-9a-fA-F]+))|(.*))\))'
|
||||
r')')
|
||||
SAN_ADDR_REGEX = re.compile(r'.*(ERROR: [a-zA-Z]+Sanitizer)[: ]*(.*) on '
|
||||
r'(unknown address |address |)([xX0-9a-fA-F]+)')
|
||||
SAN_SEGV_REGEX = re.compile(r'.*([a-zA-Z]+Sanitizer).*(SEGV|access-violation) '
|
||||
r'on unknown address ([xX0-9a-fA-F]+)')
|
||||
SECURITY_CHECK_FAILURE_REGEX = re.compile(
|
||||
r'.*\[[^\]]*[:]([^\](]*).*\].*Security CHECK failed[:]\s*(.*)\.\s*')
|
||||
SECURITY_DCHECK_FAILURE_REGEX = re.compile(
|
||||
r'.*\[[^\]]*[:]([^\](]*).*\].*Security DCHECK failed[:]\s*(.*)\.\s*')
|
||||
UBSAN_DIVISION_BY_ZERO_REGEX = re.compile(r'.*division by zero.*')
|
||||
UBSAN_FLOAT_CAST_OVERFLOW_REGEX = re.compile(r'.*outside the range of '
|
||||
r'representable values.*')
|
||||
UBSAN_INCORRECT_FUNCTION_POINTER_REGEX = re.compile(
|
||||
r'.*call to function [^\s]+ through pointer to incorrect function type.*')
|
||||
UBSAN_INDEX_OOB_REGEX = re.compile(r'.*out of bounds for type.*')
|
||||
UBSAN_UNSIGNED_INTEGER_OVERFLOW_REGEX = re.compile(
|
||||
r'.*unsigned integer overflow.*')
|
||||
UBSAN_INTEGER_OVERFLOW_REGEX = re.compile(
|
||||
r'.*(integer overflow|'
|
||||
r'(negation|division) of.*cannot be represented in type).*')
|
||||
UBSAN_INVALID_BOOL_VALUE_REGEX = re.compile(
|
||||
r'.*not a valid value for type \'(bool|BOOL)\'.*')
|
||||
UBSAN_INVALID_BUILTIN_REGEX = re.compile(r'.*, which is not a valid argument.*')
|
||||
UBSAN_INVALID_ENUM_VALUE_REGEX = re.compile(r'.*not a valid value for type.*')
|
||||
UBSAN_MISALIGNED_ADDRESS_REGEX = re.compile(r'.*misaligned address.*')
|
||||
UBSAN_NO_RETURN_VALUE_REGEX = re.compile(
|
||||
r'.*reached the end of a value-returning function.*')
|
||||
UBSAN_NULL_ARGUMENT_REGEX = re.compile(
|
||||
r'.*null pointer passed as .*, which is declared to never be null.*')
|
||||
UBSAN_NULL_POINTER_READ_REGEX = re.compile(r'.*load of null pointer.*')
|
||||
UBSAN_NULL_POINTER_REFERENCE_REGEX = re.compile(
|
||||
r'.*(binding to|access within|call on) null pointer.*')
|
||||
UBSAN_NULL_POINTER_WRITE_REGEX = re.compile(r'.*store to null pointer.*')
|
||||
UBSAN_OBJECT_SIZE_REGEX = re.compile(
|
||||
r'.*address .* with insufficient space for an object of type.*')
|
||||
UBSAN_POINTER_OVERFLOW_REGEX = re.compile(
|
||||
r'.*((addition|subtraction) of unsigned offset |'
|
||||
r'pointer index expression with base |'
|
||||
r'applying non-zero offset [0-9]+ to null pointer|'
|
||||
r'applying zero offset to null pointer).*')
|
||||
UBSAN_RETURNS_NONNULL_ATTRIBUTE_REGEX = re.compile(
|
||||
r'.*null pointer returned from function declared to never return null.*')
|
||||
UBSAN_RUNTIME_ERROR_REGEX = re.compile(r'(.*): runtime error: (.*)')
|
||||
UBSAN_SHIFT_ERROR_REGEX = re.compile(r'.*shift.*')
|
||||
UBSAN_UNREACHABLE_REGEX = re.compile(
|
||||
r'.*execution reached an unreachable program point.*')
|
||||
UBSAN_VLA_BOUND_REGEX = re.compile(
|
||||
r'.*variable length array bound evaluates to non-positive value.*')
|
||||
UBSAN_VPTR_REGEX = re.compile(
|
||||
r'(.*): runtime error: '
|
||||
r'(member access within|member call on|downcast of)'
|
||||
r' address ([xX0-9a-fA-F]+) .* of type (.*)')
|
||||
UBSAN_VPTR_INVALID_DOWNCAST_REGEX = re.compile(
|
||||
r'.*note: object is of type (.*)')
|
||||
UBSAN_VPTR_INVALID_OFFSET_REGEX = re.compile(
|
||||
r'.*at offset (\d+) within object of type (.*)')
|
||||
UBSAN_VPTR_INVALID_VPTR_REGEX = re.compile(r'.*note: object has invalid vptr')
|
||||
V8_ABORT_FAILURE_REGEX = re.compile(r'^abort: (CSA_ASSERT failed:.*)')
|
||||
V8_ABORT_METADATA_REGEX = re.compile(r'(.*) \[(.*):\d+\]$')
|
||||
V8_CORRECTNESS_FAILURE_REGEX = re.compile(r'#\s*V8 correctness failure')
|
||||
V8_CORRECTNESS_METADATA_REGEX = re.compile(
|
||||
r'#\s*V8 correctness ((configs|sources|suppression): .*)')
|
||||
V8_ERROR_REGEX = re.compile(r'\s*\[[^\]]*\] V8 error: (.+)\.$')
|
||||
WINDOWS_CDB_STACK_FRAME_REGEX = re.compile(
|
||||
r'([0-9a-zA-Z`]+) ' # Child EBP or SP; remove ` if needed (1)
|
||||
r'([0-9a-zA-Z`]+) ' # RetAddr; remove ` if needed (2)
|
||||
r'([0-9a-zA-Z_]+)' # mod (3)
|
||||
r'!(.*)\+' # fun (4)
|
||||
r'([xX0-9a-fA-F]+)') # off (5)
|
||||
WINDOWS_CDB_STACK_START_REGEX = re.compile(r'ChildEBP RetAddr')
|
||||
WINDOWS_CDB_CRASH_TYPE_ADDRESS_REGEX = re.compile(
|
||||
r'Attempt to (.*) [^ ]* address (.*)')
|
||||
WINDOWS_CDB_CRASH_TYPE_REGEX = re.compile(
|
||||
r'.*DEFAULT_BUCKET_ID[ ]*[:][ ]*([a-zA-Z_]+)')
|
||||
WINDOWS_CDB_STACK_OVERFLOW_REGEX = re.compile(
|
||||
r'.*ExceptionCode: .*\(Stack overflow\).*')
|
||||
WINDOWS_SAN_ILL_REGEX = re.compile(r'.*[a-zA-Z]+Sanitizer: illegal-instruction')
|
||||
|
||||
WYCHEPROOF_JAVA_EXCEPTION = re.compile(
|
||||
r'.*\) (.*\(com\.google\.security\.wycheproof\.[a-zA-z0-9]*\))')
|
||||
|
||||
# Golang specific regular expressions.
|
||||
GOLANG_DIVISION_BY_ZERO_REGEX = re.compile(
|
||||
r'^panic: runtime error: integer divide by zero.*')
|
||||
GOLANG_INDEX_OUT_OF_RANGE_REGEX = re.compile(
|
||||
r'^panic: runtime error: index out of range.*')
|
||||
GOLANG_INVALID_MEMORY_ADDRESS_REGEX = re.compile(
|
||||
r'^panic: runtime error: invalid memory address.*')
|
||||
GOLANG_MAKESLICE_LEN_OUT_OF_RANGE_REGEX = re.compile(
|
||||
r'^panic: runtime error: makeslice: len out of range.*')
|
||||
GOLANG_SLICE_BOUNDS_OUT_OF_RANGE_REGEX = re.compile(
|
||||
r'^panic: runtime error: slice bounds out of range.*')
|
||||
GOLANG_STACK_OVERFLOW_REGEX = re.compile(r'^fatal error: stack overflow.*')
|
||||
|
||||
GOLANG_CRASH_TYPES_MAP = [
|
||||
(GOLANG_DIVISION_BY_ZERO_REGEX, 'Integer divide by zero'),
|
||||
(GOLANG_INDEX_OUT_OF_RANGE_REGEX, 'Index out of range'),
|
||||
(GOLANG_INVALID_MEMORY_ADDRESS_REGEX, 'Invalid memory address'),
|
||||
(GOLANG_MAKESLICE_LEN_OUT_OF_RANGE_REGEX, 'Makeslice: len out of range'),
|
||||
(GOLANG_SLICE_BOUNDS_OUT_OF_RANGE_REGEX, 'Slice bounds out of range'),
|
||||
(GOLANG_STACK_OVERFLOW_REGEX, 'Stack overflow'),
|
||||
]
|
||||
|
||||
GOLANG_FATAL_ERROR_REGEX = re.compile(r'^fatal error: (.*)')
|
||||
|
||||
GOLANG_STACK_FRAME_FUNCTION_REGEX = re.compile(
|
||||
r'^([0-9a-zA-Z\.\-\_\\\/\(\)\*]+)\([x0-9a-f\s,\.{}]*\)$')
|
||||
|
||||
# Python specific regular expressions.
|
||||
PYTHON_UNHANDLED_EXCEPTION = re.compile(
|
||||
r'^\s*=== Uncaught Python exception: ===$')
|
||||
|
||||
PYTHON_CRASH_TYPES_MAP = [
|
||||
(PYTHON_UNHANDLED_EXCEPTION, 'Uncaught exception'),
|
||||
(EXTRA_SANITIZERS_PYSECSAN, 'PySecSan'),
|
||||
]
|
||||
|
||||
PYTHON_STACK_FRAME_FUNCTION_REGEX = re.compile(
|
||||
# File "<embedded stdlib>/gzip.py", line 421, in _read_gzip_header
|
||||
r'^\s*File "([^"]+)", line (\d+), in (.+)$')
|
||||
|
||||
# Mappings of Android kernel error status codes to strings.
|
||||
ANDROID_KERNEL_STATUS_TO_STRING = {
|
||||
0b0001: 'Alignment Fault',
|
||||
0b0100: 'Instruction Cache Maintenance Fault',
|
||||
0b1100: 'L1 Translation',
|
||||
0b1110: 'L2 Translation',
|
||||
0b0101: 'Translation Fault, Section',
|
||||
0b0111: 'Translation Fault, Page',
|
||||
0b0011: 'Access Flag Fault, Section',
|
||||
0b0110: 'Access Flag Fault, Page',
|
||||
0b1001: 'Domain Fault, Section',
|
||||
0b1011: 'Domain Fault, Page',
|
||||
0b1101: 'Permission Fault, Section',
|
||||
0b1111: 'Permissions Fault, Page',
|
||||
}
|
||||
|
||||
# Ignore lists.
|
||||
STACK_FRAME_IGNORE_REGEXES = [
|
||||
# Function names (exact match).
|
||||
r'^abort$',
|
||||
r'^exit$',
|
||||
r'^pthread_create$',
|
||||
r'^pthread_kill$',
|
||||
r'^raise$',
|
||||
r'^tgkill$',
|
||||
r'^__chk_fail$',
|
||||
r'^__fortify_fail$',
|
||||
|
||||
# Function names (startswith).
|
||||
r'^(|__)aeabi_',
|
||||
r'^(|__)memcmp',
|
||||
r'^(|__)memcpy',
|
||||
r'^(|__)memmove',
|
||||
r'^(|__)memset',
|
||||
r'^(|__)strcmp',
|
||||
r'^(|__)strcpy',
|
||||
r'^(|__)strdup',
|
||||
r'^(|__)strlen',
|
||||
r'^(|__)strncpy',
|
||||
r'^<null>',
|
||||
r'^Abort\(',
|
||||
r'^CFCrash',
|
||||
r'^ExitCallback',
|
||||
r'^IsSandboxedProcess',
|
||||
r'^LLVMFuzzerTestOneInput',
|
||||
r'^MSanAtExitWrapper',
|
||||
r'^New',
|
||||
r'^RaiseException',
|
||||
r'^SbSystemBreakIntoDebugger',
|
||||
r'^SignalAction',
|
||||
r'^SignalHandler',
|
||||
r'^TestOneProtoInput',
|
||||
r'^WTF::',
|
||||
r'^WTFCrash',
|
||||
r'^X11Error',
|
||||
r'^_L_unlock_',
|
||||
r'^_\$LT\$',
|
||||
r'^__GI_',
|
||||
r'^__asan::',
|
||||
r'^__asan_',
|
||||
r'^__assert_',
|
||||
r'^__cxa_atexit',
|
||||
r'^__cxa_rethrow',
|
||||
r'^__cxa_throw',
|
||||
r'^__dump_stack',
|
||||
r'^__hwasan::',
|
||||
r'^__hwasan_',
|
||||
r'^__interceptor_',
|
||||
r'^__kasan_',
|
||||
r'^__libc_',
|
||||
r'^__lsan::',
|
||||
r'^__lsan_',
|
||||
r'^__msan::',
|
||||
r'^__msan_',
|
||||
r'^__pthread_kill',
|
||||
r'^__run_exit_handlers',
|
||||
r'^__rust_try',
|
||||
r'^__sanitizer::',
|
||||
r'^__sanitizer_',
|
||||
r'^__tsan::',
|
||||
r'^__tsan_',
|
||||
r'^__ubsan::',
|
||||
r'^__ubsan_',
|
||||
r'^_asan_',
|
||||
r'^_hwasan_',
|
||||
r'^_lsan_',
|
||||
r'^_msan_',
|
||||
r'^_objc_terminate',
|
||||
r'^_sanitizer_',
|
||||
r'^_start',
|
||||
r'^_tsan_',
|
||||
r'^_ubsan_',
|
||||
r'^abort',
|
||||
r'^alloc::',
|
||||
r'^android\.app\.ActivityManagerProxy\.',
|
||||
r'^android\.os\.Parcel\.',
|
||||
r'^art::Thread::CreateNativeThread',
|
||||
r'^asan_',
|
||||
r'^asan\.module_ctor',
|
||||
r'^asan\.module_dtor',
|
||||
r'^calloc',
|
||||
r'^check_memory_region',
|
||||
r'^common_exit',
|
||||
r'^core::fmt::write',
|
||||
r'^delete',
|
||||
r'^demangling_terminate_handler',
|
||||
r'^dump_backtrace',
|
||||
r'^dump_stack',
|
||||
r'^exit_or_terminate_process',
|
||||
r'^fpehandler\(',
|
||||
r'^free',
|
||||
r'^fuzzer::',
|
||||
r'^g_log',
|
||||
r'^generic_cpp_',
|
||||
r'^gsignal',
|
||||
r'^kasan_',
|
||||
r'^libfuzzer_sys::initialize',
|
||||
r'^main',
|
||||
r'^malloc',
|
||||
r'^mozalloc_',
|
||||
r'^new',
|
||||
r'^object_err',
|
||||
r'^operator',
|
||||
r'^panic_abort::',
|
||||
r'^print_trailer',
|
||||
r'^realloc',
|
||||
r'^rust_begin_unwind',
|
||||
r'^rust_fuzzer_test_input',
|
||||
r'^rust_oom',
|
||||
r'^rust_panic',
|
||||
r'^scanf',
|
||||
r'^show_stack',
|
||||
r'^std::__terminate',
|
||||
r'^std::io::Write::write_fmt',
|
||||
r'^std::panic',
|
||||
r'^std::process::abort',
|
||||
r'^std::sys::unix::abort',
|
||||
r'^std::sys_common::backtrace',
|
||||
r'^__rust_start_panic',
|
||||
r'^__scrt_common_main_seh',
|
||||
r'^libgcc_s.so.*',
|
||||
|
||||
# Functions names (contains).
|
||||
r'.*ASAN_OnSIGSEGV',
|
||||
r'.*BaseThreadInitThunk',
|
||||
r'.*DebugBreak',
|
||||
r'.*DefaultDcheckHandler',
|
||||
r'.*ForceCrashOnSigAbort',
|
||||
r'.*MemoryProtection::CMemoryProtector',
|
||||
r'.*PartitionAlloc',
|
||||
r'.*RtlFreeHeap',
|
||||
r'.*RtlInitializeExceptionChain',
|
||||
r'.*RtlReportCriticalFailure',
|
||||
r'.*RtlUserThreadStart',
|
||||
r'.*RtlpHeapHandleError',
|
||||
r'.*RtlpLogHeapFailure',
|
||||
r'.*SkDebugf',
|
||||
r'.*StackDumpSignalHandler',
|
||||
r'.*__android_log_assert',
|
||||
r'.*__tmainCRTStartup',
|
||||
r'.*_asan_rtl_',
|
||||
r'.*agent::asan::',
|
||||
r'.*allocator_shim',
|
||||
r'.*asan_Heap',
|
||||
r'.*asan_check_access',
|
||||
r'.*asan_osx_dynamic\.dylib',
|
||||
r'.*assert',
|
||||
r'.*base::FuzzedDataProvider',
|
||||
r'.*base::allocator',
|
||||
r'.*base::android::CheckException',
|
||||
r'.*base::debug::BreakDebugger',
|
||||
r'.*base::debug::CollectStackTrace',
|
||||
r'.*base::debug::StackTrace::StackTrace',
|
||||
r'.*ieee754\-',
|
||||
r'.*libpthread',
|
||||
r'.*logger',
|
||||
r'.*logging::CheckError',
|
||||
r'.*logging::ErrnoLogMessage',
|
||||
r'.*logging::LogMessage',
|
||||
r'.*stdext::exception::what',
|
||||
r'.*v8::base::OS::Abort',
|
||||
|
||||
# File paths.
|
||||
r'.* base/callback',
|
||||
r'.* /rust(|c)/',
|
||||
r'.*/AOSP\-toolchain/',
|
||||
r'.*/bindings/ToV8\.h',
|
||||
r'.*/crosstool/',
|
||||
r'.*/gcc/',
|
||||
r'.*/glibc\-',
|
||||
r'.*/jemalloc/',
|
||||
r'.*/libc\+\+',
|
||||
r'.*/libc/',
|
||||
r'.*/llvm\-build/',
|
||||
r'.*/minkernel/crts/',
|
||||
r'.*/sanitizer_common/',
|
||||
r'.*/tcmalloc/',
|
||||
r'.*/vc/include/',
|
||||
r'.*/vctools/crt/',
|
||||
r'.*/win_toolchain/',
|
||||
r'.*libc\+\+/',
|
||||
# Clusterfuzz file paths on Windows to ignore.
|
||||
r'c:/clusterfuzz/bot/build',
|
||||
|
||||
# Wrappers from honggfuzz/libhfuzz/memorycmp.c.
|
||||
r'.*/memorycmp\.c',
|
||||
|
||||
# Others (uncategorized).
|
||||
r'.*\+Unknown',
|
||||
r'.*<unknown module>',
|
||||
r'.*Inline Function @',
|
||||
r'^<unknown>$',
|
||||
r'^\[vdso\]$',
|
||||
r'^linux-gate.so.*$',
|
||||
|
||||
# Golang specific frames to ignore.
|
||||
r'^panic$',
|
||||
r'^runtime\.',
|
||||
|
||||
# Fuchsia specific.
|
||||
r'^CrashTrampolineAsm',
|
||||
r'^libc_io_functions_not_implemented_use_fdio_instead',
|
||||
r'^<libclang_rt.asan.so>',
|
||||
r'^__zx_panic',
|
||||
r'^syslog::LogMessage',
|
||||
|
||||
# Android kernel stack frame ignores.
|
||||
r'^print_address_description',
|
||||
r'^_etext',
|
||||
|
||||
# Swift specific.
|
||||
r'^_swift_stdlib_',
|
||||
|
||||
# googlefuzztest specific.
|
||||
r'.*fuzztest::internal::',
|
||||
|
||||
# V8 specific.
|
||||
r'^V8_Fatal',
|
||||
# Ignore error-throwing frames, the bug is in the caller.
|
||||
r'^blink::ReportV8FatalError',
|
||||
r'^v8::api_internal::ToLocalEmpty',
|
||||
|
||||
# google3 specific stack frame ignores.
|
||||
r'^absl::log_internal::',
|
||||
]
|
||||
|
||||
STACK_FRAME_IGNORE_REGEXES_IF_SYMBOLIZED = [
|
||||
r'.*libc\.so',
|
||||
r'.*libc\+\+\.so',
|
||||
r'.*libc\+\+_shared\.so',
|
||||
r'.*libstdc\+\+\.so',
|
||||
r'.*libc-.*\.so',
|
||||
]
|
||||
|
||||
IGNORE_CRASH_TYPES_FOR_ABRT_BREAKPOINT_AND_ILLS = [
|
||||
'Arbitrary file open',
|
||||
'ASSERT',
|
||||
'CHECK failure',
|
||||
'Command injection',
|
||||
'DCHECK failure',
|
||||
'Fatal error',
|
||||
'Security CHECK failure',
|
||||
'Security DCHECK failure',
|
||||
'V8 API error',
|
||||
]
|
||||
|
||||
STATE_STOP_MARKERS = [
|
||||
'Direct leak of',
|
||||
'Uninitialized value was stored to memory at',
|
||||
'allocated by thread',
|
||||
'created by main thread at',
|
||||
'located in stack of thread',
|
||||
'previously allocated by',
|
||||
]
|
||||
|
||||
UBSAN_CRASH_TYPES_MAP = [
|
||||
(UBSAN_DIVISION_BY_ZERO_REGEX, 'Divide-by-zero'),
|
||||
(UBSAN_FLOAT_CAST_OVERFLOW_REGEX, 'Float-cast-overflow'),
|
||||
(UBSAN_INCORRECT_FUNCTION_POINTER_REGEX, 'Incorrect-function-pointer-type'),
|
||||
(UBSAN_INDEX_OOB_REGEX, 'Index-out-of-bounds'),
|
||||
(UBSAN_INVALID_BOOL_VALUE_REGEX, 'Invalid-bool-value'),
|
||||
(UBSAN_INVALID_BUILTIN_REGEX, 'Invalid-builtin-use'),
|
||||
(UBSAN_MISALIGNED_ADDRESS_REGEX, 'Misaligned-address'),
|
||||
(UBSAN_NO_RETURN_VALUE_REGEX, 'No-return-value'),
|
||||
(UBSAN_NULL_ARGUMENT_REGEX, 'Invalid-null-argument'),
|
||||
(UBSAN_NULL_POINTER_READ_REGEX, 'Null-dereference READ'),
|
||||
(UBSAN_NULL_POINTER_REFERENCE_REGEX, 'Null-dereference'),
|
||||
(UBSAN_NULL_POINTER_WRITE_REGEX, 'Null-dereference WRITE'),
|
||||
(UBSAN_OBJECT_SIZE_REGEX, 'Object-size'),
|
||||
(UBSAN_POINTER_OVERFLOW_REGEX, 'Pointer-overflow'),
|
||||
(UBSAN_RETURNS_NONNULL_ATTRIBUTE_REGEX, 'Invalid-null-return'),
|
||||
(UBSAN_SHIFT_ERROR_REGEX, 'Undefined-shift'),
|
||||
(UBSAN_UNREACHABLE_REGEX, 'Unreachable code'),
|
||||
(UBSAN_UNSIGNED_INTEGER_OVERFLOW_REGEX, 'Unsigned-integer-overflow'),
|
||||
(UBSAN_VLA_BOUND_REGEX, 'Non-positive-vla-bound-value'),
|
||||
|
||||
# The following types are supersets of other types, and should be placed
|
||||
# at the end to avoid subsuming crashes from the more specialized types.
|
||||
(UBSAN_INVALID_ENUM_VALUE_REGEX, 'Invalid-enum-value'),
|
||||
(UBSAN_INTEGER_OVERFLOW_REGEX, 'Integer-overflow'),
|
||||
]
|
||||
|
||||
# Additional regexes for cleaning up format.
|
||||
STRIP_STRUCTURE_REGEXES = [
|
||||
re.compile(r'^in (.*)'), # sanitizers have prefix for function if present
|
||||
re.compile(r'^\((.*)\)$'), # sanitizers wrap module if no function
|
||||
]
|
||||
|
||||
# Other constants.
|
||||
LINE_LENGTH_CAP = 80
|
||||
MAX_CRASH_STATE_FRAMES = 3
|
||||
MAX_CYCLE_LENGTH = 10
|
||||
REPEATED_CYCLE_COUNT = 3
|
||||
|
||||
|
||||
|
||||
|
||||
def unsigned_to_signed(address):
|
||||
"""Convert unsigned address to signed int64 (as defined in the proto)."""
|
||||
return (address - 2**64) if address >= 2**63 else address
|
||||
|
||||
|
||||
def format_address_to_dec(address, base=16):
|
||||
"""Addresses may be formatted as decimal, hex string with 0x or 0X prefix,
|
||||
or without any prefix. Convert to decimal int."""
|
||||
if address is None:
|
||||
return None
|
||||
|
||||
address = str(address).replace('`', '').strip()
|
||||
if not address:
|
||||
return None
|
||||
|
||||
# This is required for Chrome Win and Mac stacks, which mix decimal and hex.
|
||||
try_bases = [base, 16] if base != 16 else [base]
|
||||
for base_try in try_bases:
|
||||
try:
|
||||
address = int(address, base_try)
|
||||
return address
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
logs.warn('Error formatting address %s to decimal int64 in bases %s.' %
|
||||
(str(address), str(try_bases)))
|
||||
return None
|
||||
|
||||
|
||||
class StackFrameStructure(object):
|
||||
"""IR for fields a stackframe may contain/expect."""
|
||||
|
||||
def __init__(self,
|
||||
address=None,
|
||||
function_name=None,
|
||||
function_base=None,
|
||||
function_offset=None,
|
||||
filename=None,
|
||||
fileline=None,
|
||||
module_name=None,
|
||||
module_base=None,
|
||||
module_offset=None):
|
||||
self._address = address
|
||||
self._function_name = function_name
|
||||
self._function_base = function_base
|
||||
self._function_offset = function_offset
|
||||
self._filename = filename
|
||||
self._fileline = fileline
|
||||
self._module_name = module_name
|
||||
self._module_base = module_base
|
||||
self._module_offset = module_offset
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
return self._address
|
||||
|
||||
@address.setter
|
||||
def address(self, address):
|
||||
self._address = address
|
||||
|
||||
@property
|
||||
def function_name(self):
|
||||
return self._function_name
|
||||
|
||||
@function_name.setter
|
||||
def function_name(self, function_name):
|
||||
self._function_name = function_name
|
||||
|
||||
@property
|
||||
def function_base(self):
|
||||
return self._function_base
|
||||
|
||||
@function_base.setter
|
||||
def function_base(self, function_base):
|
||||
self._function_base = function_base
|
||||
|
||||
@property
|
||||
def function_offset(self):
|
||||
return self._function_offset
|
||||
|
||||
@function_offset.setter
|
||||
def function_offset(self, function_offset):
|
||||
self._function_offset = function_offset
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
return self._filename
|
||||
|
||||
@filename.setter
|
||||
def filename(self, filename):
|
||||
self._filename = filename
|
||||
|
||||
@property
|
||||
def fileline(self):
|
||||
return self._fileline
|
||||
|
||||
@fileline.setter
|
||||
def fileline(self, fileline):
|
||||
self._fileline = fileline
|
||||
|
||||
@property
|
||||
def module_name(self):
|
||||
return self._module_name
|
||||
|
||||
@module_name.setter
|
||||
def module_name(self, module_name):
|
||||
self._module_name = module_name
|
||||
|
||||
@property
|
||||
def module_base(self):
|
||||
return self._module_base
|
||||
|
||||
@module_base.setter
|
||||
def module_base(self, module_base):
|
||||
self._module_base = module_base
|
||||
|
||||
@property
|
||||
def module_offset(self):
|
||||
return self._module_offset
|
||||
|
||||
@module_offset.setter
|
||||
def module_offset(self, module_offset):
|
||||
self._module_offset = module_offset
|
||||
|
||||
|
||||
class StackFrame(StackFrameStructure):
|
||||
"""IR for canonicalizing stackframe strings."""
|
||||
|
||||
def __init__(self,
|
||||
address=None,
|
||||
function_name=None,
|
||||
function_base=None,
|
||||
function_offset=None,
|
||||
filename=None,
|
||||
fileline=None,
|
||||
module_name=None,
|
||||
module_base=None,
|
||||
module_offset=None,
|
||||
base=16):
|
||||
super(StackFrame, self).__init__(
|
||||
address=format_address_to_dec(address),
|
||||
function_name=function_name,
|
||||
function_base=format_address_to_dec(function_base),
|
||||
function_offset=format_address_to_dec(function_offset),
|
||||
filename=filename,
|
||||
fileline=fileline,
|
||||
module_name=module_name,
|
||||
module_base=format_address_to_dec(module_base),
|
||||
module_offset=format_address_to_dec(module_offset))
|
||||
|
||||
# Base for converting addresses set in frame. Most will be in hex.
|
||||
self._base = base
|
||||
|
||||
def __setattr__(self, field_name, field_value):
|
||||
"""Set attributes, performing conversions as needed for address fields."""
|
||||
if field_name == 'base':
|
||||
self._base = field_value
|
||||
return
|
||||
|
||||
address_fields = [
|
||||
'address',
|
||||
'function_base',
|
||||
'function_offset',
|
||||
'module_base',
|
||||
'module_offset',
|
||||
]
|
||||
if field_name in address_fields:
|
||||
address = format_address_to_dec(field_value, self._base)
|
||||
super(StackFrame, self).__setattr__(field_name, address)
|
||||
return
|
||||
|
||||
super(StackFrame, self).__setattr__(field_name, field_value)
|
||||
|
||||
def __str__(self):
|
||||
s = []
|
||||
for name, member in inspect.getmembers(StackFrame):
|
||||
if not isinstance(member, property):
|
||||
continue
|
||||
s += ['%s: %s' % (name, str(getattr(self, name)))]
|
||||
return ', '.join(s)
|
||||
|
||||
|
||||
class StackFrameSpec(StackFrameStructure):
|
||||
"""Representation paralleling that of StackFrames for pulling out the correct
|
||||
groups in a *_STACK_FRAME_REGEX match."""
|
||||
|
||||
def __init__(self,
|
||||
address=None,
|
||||
function_name=None,
|
||||
function_base=None,
|
||||
function_offset=None,
|
||||
filename=None,
|
||||
fileline=None,
|
||||
module_name=None,
|
||||
module_base=None,
|
||||
module_offset=None,
|
||||
base=16):
|
||||
"""Specify a stackframe format. Each field should be an index into a match.
|
||||
See comments inline *_STACK_FRAME_REGEX for appropriate indices."""
|
||||
address = address if address is not None else []
|
||||
function_name = function_name if function_name is not None else []
|
||||
function_base = function_base if function_base is not None else []
|
||||
function_offset = function_offset if function_offset is not None else []
|
||||
filename = filename if filename is not None else []
|
||||
fileline = fileline if fileline is not None else []
|
||||
module_name = module_name if module_name is not None else []
|
||||
module_base = module_base if module_base is not None else []
|
||||
module_offset = module_offset if module_offset is not None else []
|
||||
super(StackFrameSpec, self).__init__(
|
||||
address=address,
|
||||
function_name=function_name,
|
||||
function_base=function_base,
|
||||
function_offset=function_offset,
|
||||
filename=filename,
|
||||
fileline=fileline,
|
||||
module_name=module_name,
|
||||
module_base=module_base,
|
||||
module_offset=module_offset)
|
||||
|
||||
# Base for converting addresses processed by this spec. Most will be in hex.
|
||||
self._base = base
|
||||
|
||||
@property
|
||||
def base(self):
|
||||
return self._base
|
||||
|
||||
@base.setter
|
||||
def base(self, base):
|
||||
self._base = base
|
||||
|
||||
def parse_stack_frame(self, frame_match):
|
||||
"""Given a match and stackframe specification, populate a stackframe with
|
||||
information from the match."""
|
||||
if frame_match is None:
|
||||
return None
|
||||
|
||||
frame = StackFrame()
|
||||
|
||||
# Set base to use for address translation.
|
||||
frame.base = self.base
|
||||
for name, member in inspect.getmembers(StackFrameSpec):
|
||||
# Pull out only the property fields; we don't care about methods etc.
|
||||
if not isinstance(member, property):
|
||||
continue
|
||||
|
||||
# We've already set the base (which we need to do first); skip.
|
||||
if name == 'base':
|
||||
continue
|
||||
|
||||
# Populate the stackframe field. Try all provided lookup groups.
|
||||
indices = getattr(self, name)
|
||||
if not isinstance(indices, list):
|
||||
indices = [indices]
|
||||
setattr(self, name, indices)
|
||||
for ind in indices:
|
||||
frame_field = frame_match.group(ind)
|
||||
if frame_field is not None and frame_field:
|
||||
setattr(frame, name, frame_field.strip())
|
||||
break
|
||||
|
||||
return frame
|
||||
|
||||
|
||||
# Stackframe format specifications.
|
||||
CHROME_STACK_FRAME_SPEC = StackFrameSpec(
|
||||
address=3, function_name=4)
|
||||
CHROME_WIN_STACK_FRAME_SPEC = StackFrameSpec(
|
||||
function_name=1,
|
||||
function_base=2,
|
||||
function_offset=3,
|
||||
filename=5,
|
||||
fileline=6,
|
||||
base=10)
|
||||
CHROME_MAC_STACK_FRAME_SPEC = StackFrameSpec(
|
||||
address=5, function_name=6, function_offset=7, module_name=2, base=10)
|
||||
SAN_STACK_FRAME_SPEC = StackFrameSpec(
|
||||
address=2,
|
||||
function_name=[7, 5, 23],
|
||||
function_offset=8,
|
||||
filename=[12, 16],
|
||||
fileline=[13, 17],
|
||||
module_name=[19, 31],
|
||||
module_offset=[21, 32])
|
||||
WINDOWS_CDB_STACK_FRAME_SPEC = StackFrameSpec(
|
||||
address=1, function_name=4, function_offset=5, module_name=3)
|
||||
|
||||
|
||||
LINUX_KERNEL_MODULE_STACK_TRACE = 'Linux Kernel Library Stack Trace:'
|
||||
|
||||
#hid-fuzzer: lib/posix-host.c:401: void panic(void): Assertion `0' failed.
|
||||
LINUX_KERNEL_LIBRARY_ASSERT_REGEX = re.compile(
|
||||
r'([^:]+): lib/posix-host\.c:\d+: void panic\(void\): Assertion .*')
|
||||
|
||||
# Linux version 5.4.58+-ab6926695 where 6926695 is the build id.
|
||||
# Unlike in a normal linux version string, we do not know the build hash.
|
||||
LINUX_VERSION_REGEX_LKL = re.compile(r'Linux version .+-(ab([0-9a-f]+)\s)')
|
||||
|
||||
# This is the prefix in the repo.prop for the kernel for all
|
||||
# lkl fuzzers.
|
||||
LKL_REPO_KERNEL_PREFIX = 'kernel/private/lkl'
|
||||
LKL_BUILD_TARGET = 'kernel_kasan.lkl_fuzzers'
|
||||
@@ -0,0 +1,36 @@
|
||||
# Clusterfuzz commit 46968a275474422db32530a2126dfa30dc7f3d15
|
||||
|
||||
def search_bytes_in_file(search_bytes, file_handle):
|
||||
"""Helper to search for bytes in a large binary file without memory
|
||||
issues.
|
||||
"""
|
||||
# TODO(aarya): This is too brittle and will fail if we have a very large
|
||||
# line.
|
||||
for line in file_handle:
|
||||
if search_bytes in line:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def strip_from_left(string, prefix):
|
||||
"""Strip a prefix from start from string."""
|
||||
if not string.startswith(prefix):
|
||||
return string
|
||||
return string[len(prefix):]
|
||||
|
||||
|
||||
def strip_from_right(string, suffix):
|
||||
"""Strip a suffix from end of string."""
|
||||
if not string.endswith(suffix):
|
||||
return string
|
||||
return string[:len(string) - len(suffix)]
|
||||
|
||||
|
||||
def sub_string_exists_in(substring_list, string):
|
||||
"""Return true if one of the substring in the list is found in |string|."""
|
||||
for substring in substring_list:
|
||||
if substring in string:
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -0,0 +1,101 @@
|
||||
# Clusterfuzz commit 46968a275474422db32530a2126dfa30dc7f3d15
|
||||
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
import tempfile
|
||||
import logging
|
||||
from buttercup.common.clusterfuzz_parser import utils
|
||||
from buttercup.common.clusterfuzz_env import environment
|
||||
|
||||
logs = logging.getLogger(__name__)
|
||||
|
||||
EXTRA_BUILD_DIR = '__extra_build'
|
||||
|
||||
ALLOWED_FUZZ_TARGET_EXTENSIONS = ['', '.exe', '.par']
|
||||
FUZZ_TARGET_SEARCH_BYTES = b'LLVMFuzzerTestOneInput'
|
||||
VALID_TARGET_NAME_REGEX = re.compile(r'^[a-zA-Z0-9@_.-]+$')
|
||||
BLOCKLISTED_TARGET_NAME_REGEX = re.compile(r'^(jazzer_driver.*)$')
|
||||
EXTRA_BUILD_DIR = '__extra_build'
|
||||
|
||||
|
||||
|
||||
def is_fuzz_target_local(file_path, file_handle=None):
|
||||
"""Returns whether |file_path| is a fuzz target binary (local path)."""
|
||||
# TODO(hzawawy): Handle syzkaller case.
|
||||
if '@' in file_path:
|
||||
# GFT targets often have periods in the name that get misinterpreted as an
|
||||
# extension.
|
||||
filename = os.path.basename(file_path)
|
||||
file_extension = ''
|
||||
else:
|
||||
filename, file_extension = os.path.splitext(os.path.basename(file_path))
|
||||
|
||||
if not VALID_TARGET_NAME_REGEX.match(filename):
|
||||
# Check fuzz target has a valid name (without any special chars).
|
||||
return False
|
||||
|
||||
if BLOCKLISTED_TARGET_NAME_REGEX.match(filename):
|
||||
# Check fuzz target an explicitly disallowed name (e.g. binaries used for
|
||||
# jazzer-based targets).
|
||||
return False
|
||||
|
||||
if file_extension not in ALLOWED_FUZZ_TARGET_EXTENSIONS:
|
||||
# Ignore files with disallowed extensions (to prevent opening e.g. .zips).
|
||||
return False
|
||||
|
||||
if not file_handle and not os.path.exists(file_path):
|
||||
# Ignore non-existent files for cases when we don't have a file handle.
|
||||
return False
|
||||
|
||||
if filename.endswith('_fuzzer'):
|
||||
return True
|
||||
|
||||
# TODO(aarya): Remove this optimization if it does not show up significant
|
||||
# savings in profiling results.
|
||||
fuzz_target_name_regex = environment.get_value('FUZZER_NAME_REGEX')
|
||||
if fuzz_target_name_regex:
|
||||
return bool(re.match(fuzz_target_name_regex, filename))
|
||||
|
||||
if os.path.exists(file_path) and not stat.S_ISREG(os.stat(file_path).st_mode):
|
||||
# Don't read special files (eg: /dev/urandom).
|
||||
logs.warn('Tried to read from non-regular file: %s.' % file_path)
|
||||
return False
|
||||
|
||||
# Use already provided file handle or open the file.
|
||||
local_file_handle = file_handle or open(file_path, 'rb')
|
||||
|
||||
# TODO(metzman): Bound this call so we don't read forever if something went
|
||||
# wrong.
|
||||
result = utils.search_bytes_in_file(FUZZ_TARGET_SEARCH_BYTES,
|
||||
local_file_handle)
|
||||
|
||||
if not file_handle:
|
||||
# If this local file handle is owned by our function, close it now.
|
||||
# Otherwise, it is caller's responsibility.
|
||||
local_file_handle.close()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
||||
def walk(directory, **kwargs):
|
||||
"""Wrapper around walk to resolve compatibility issues."""
|
||||
return os.walk(directory, **kwargs)
|
||||
|
||||
|
||||
def get_fuzz_targets(path):
|
||||
"""Get list of fuzz targets paths (local)."""
|
||||
fuzz_target_paths = []
|
||||
|
||||
for root, _, files in walk(path):
|
||||
for filename in files:
|
||||
if os.path.basename(root) == EXTRA_BUILD_DIR:
|
||||
# Ignore extra binaries.
|
||||
continue
|
||||
|
||||
file_path = os.path.join(root, filename)
|
||||
if is_fuzz_target_local(file_path):
|
||||
fuzz_target_paths.append(file_path)
|
||||
|
||||
return fuzz_target_paths
|
||||
@@ -1,4 +1,4 @@
|
||||
from clusterfuzz.stacktraces import StackParser
|
||||
from buttercup.common.clusterfuzz_parser import StackParser
|
||||
import logging
|
||||
from buttercup.common.sets import RedisSet
|
||||
from redis import Redis
|
||||
|
||||
@@ -145,7 +145,7 @@ def main():
|
||||
logger.info("Done")
|
||||
elif isinstance(command, ListSettings):
|
||||
print("Available queues:")
|
||||
print("\n".join([f"- {name.value}" for name in get_queue_names()]))
|
||||
print("\n".join([f"- {name}" for name in get_queue_names()]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,87 @@
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from buttercup.common.clusterfuzz_utils import get_fuzz_targets, EXTRA_BUILD_DIR
|
||||
|
||||
|
||||
class TestGetFuzzTargets(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# Create a temporary directory for test files
|
||||
self.test_dir = tempfile.mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
# Clean up the temporary directory after tests
|
||||
for root, _, files in os.walk(self.test_dir, topdown=False):
|
||||
for name in files:
|
||||
os.remove(os.path.join(root, name))
|
||||
os.rmdir(root)
|
||||
|
||||
def create_file(self, path, content=b""):
|
||||
"""Helper method to create a file with optional content"""
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "wb") as f:
|
||||
f.write(content)
|
||||
|
||||
def test_find_valid_fuzz_targets(self):
|
||||
# Create some valid fuzz target files
|
||||
valid_targets = [
|
||||
os.path.join(self.test_dir, "test_fuzzer"),
|
||||
os.path.join(self.test_dir, "subdir", "another_fuzzer"),
|
||||
]
|
||||
|
||||
for target in valid_targets:
|
||||
self.create_file(target, b"LLVMFuzzerTestOneInput")
|
||||
|
||||
# Create some non-fuzz target files
|
||||
self.create_file(os.path.join(self.test_dir, "regular_file"), b"normal content")
|
||||
|
||||
# Get fuzz targets
|
||||
found_targets = get_fuzz_targets(self.test_dir)
|
||||
|
||||
# Sort both lists for comparison
|
||||
self.assertEqual(sorted(found_targets), sorted(valid_targets))
|
||||
|
||||
def test_ignore_extra_build_directory(self):
|
||||
# Create a valid fuzz target in main directory
|
||||
valid_target = os.path.join(self.test_dir, "test_fuzzer")
|
||||
self.create_file(valid_target, b"LLVMFuzzerTestOneInput")
|
||||
|
||||
# Create a fuzz target in __extra_build directory
|
||||
extra_build_target = os.path.join(self.test_dir, EXTRA_BUILD_DIR, "extra_fuzzer")
|
||||
self.create_file(extra_build_target, b"LLVMFuzzerTestOneInput")
|
||||
|
||||
found_targets = get_fuzz_targets(self.test_dir)
|
||||
|
||||
self.assertEqual(found_targets, [valid_target])
|
||||
self.assertNotIn(extra_build_target, found_targets)
|
||||
|
||||
def test_empty_directory(self):
|
||||
# Test with an empty directory
|
||||
found_targets = get_fuzz_targets(self.test_dir)
|
||||
self.assertEqual(found_targets, [])
|
||||
|
||||
@patch("buttercup.common.clusterfuzz_utils.is_fuzz_target_local")
|
||||
def test_file_filtering(self, mock_is_fuzz_target):
|
||||
# Create some test files
|
||||
test_files = [
|
||||
os.path.join(self.test_dir, "test1_fuzzer"),
|
||||
os.path.join(self.test_dir, "test2_fuzzer"),
|
||||
os.path.join(self.test_dir, "not_a_fuzzer"),
|
||||
]
|
||||
|
||||
for file_path in test_files:
|
||||
self.create_file(file_path)
|
||||
|
||||
# Configure mock to only identify specific files as fuzz targets
|
||||
def is_fuzz_target_side_effect(path):
|
||||
return path.endswith("_fuzzer")
|
||||
|
||||
mock_is_fuzz_target.side_effect = is_fuzz_target_side_effect
|
||||
|
||||
found_targets = get_fuzz_targets(self.test_dir)
|
||||
|
||||
# Should only find the files ending with _fuzzer
|
||||
expected_targets = [f for f in test_files if f.endswith("_fuzzer")]
|
||||
self.assertEqual(sorted(found_targets), sorted(expected_targets))
|
||||
@@ -0,0 +1,83 @@
|
||||
import pytest
|
||||
from buttercup.common.stack_parsing import get_crash_data
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_tc_file(name: str) -> Path:
|
||||
"""Get testcase file"""
|
||||
crash_file = Path(__file__).parent / "data" / "stacktrace_corpus" / f"{name}_stacktrace.txt"
|
||||
if not crash_file.exists():
|
||||
raise FileNotFoundError(f"Crash file not found at {crash_file}")
|
||||
|
||||
return crash_file
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def java_crash_testcase() -> Path:
|
||||
"""Get java crash_test_case"""
|
||||
return get_tc_file("java")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def c_crash_testcase() -> Path:
|
||||
"""Get c crash_test_case"""
|
||||
return get_tc_file("c")
|
||||
|
||||
|
||||
def test_get_crash_data_basic():
|
||||
# Simple stacktrace example
|
||||
stacktrace = """
|
||||
#0 0x7f339b644844 in foo::bar::crash() /src/foo/bar.cc:123:4
|
||||
#1 0x7f339b644900 in main /src/main.cc:45:2
|
||||
"""
|
||||
crash_state = get_crash_data(stacktrace)
|
||||
assert crash_state is not None
|
||||
assert isinstance(crash_state, str)
|
||||
|
||||
|
||||
def test_get_crash_data_symbolized():
|
||||
# Test with symbolized stacktrace
|
||||
stacktrace = """
|
||||
#0 foo::bar::crash() /src/foo/bar.cc:123:4
|
||||
#1 main /src/main.cc:45:2
|
||||
"""
|
||||
crash_state = get_crash_data(stacktrace, symbolized=True)
|
||||
assert crash_state is not None
|
||||
assert isinstance(crash_state, str)
|
||||
|
||||
|
||||
def test_get_crash_data_empty():
|
||||
# Test with empty stacktrace
|
||||
stacktrace = ""
|
||||
crash_state = get_crash_data(stacktrace)
|
||||
assert crash_state is not None
|
||||
assert isinstance(crash_state, str)
|
||||
|
||||
|
||||
def test_get_crash_data_invalid():
|
||||
# Test with invalid stacktrace format
|
||||
stacktrace = "This is not a valid stacktrace"
|
||||
crash_state = get_crash_data(stacktrace)
|
||||
assert crash_state is not None
|
||||
assert isinstance(crash_state, str)
|
||||
|
||||
|
||||
def test_java_stacktrace(java_crash_testcase: Path):
|
||||
with open(java_crash_testcase, "r") as f:
|
||||
trace = f.read()
|
||||
|
||||
crash_state = get_crash_data(trace)
|
||||
expected = "org.apache.commons.jxpath.ri.compiler.CoreOperation.parenthesize\norg.apache.commons.jxpath.ri.compiler.CoreOperation.toString\norg.apache.commons.jxpath.ri.compiler.CoreOperation.parenthesize\n"
|
||||
assert crash_state == expected
|
||||
|
||||
|
||||
def test_c_stacktrace(c_crash_testcase: Path):
|
||||
with open(c_crash_testcase, "r") as f:
|
||||
trace = f.read()
|
||||
|
||||
crash_state = get_crash_data(trace)
|
||||
print(crash_state)
|
||||
assert crash_state is not None
|
||||
assert isinstance(crash_state, str)
|
||||
expected = "cil_destroy_block\ncil_destroy_data\ncil_tree_node_destroy\n"
|
||||
assert crash_state == expected
|
||||
Generated
+2
-627
@@ -144,31 +144,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cachetools"
|
||||
version = "4.2.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12.4'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/69/c457a860456cbf80ecc2e44ed4c201b49ec7ad124d769b71f6d0a7935dca/cachetools-4.2.4.tar.gz", hash = "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693", size = 25487 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/c1/4740af52db75e6dbdd57fc7e9478439815bbac549c1c05881be27d19a17d/cachetools-4.2.4-py3-none-any.whl", hash = "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1", size = 10358 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cachetools"
|
||||
version = "5.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12' and python_full_version < '3.12.4'",
|
||||
"python_full_version < '3.12'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d9/74/57df1ab0ce6bc5f6fa868e08de20df8ac58f9c44330c7671ad922d2bbeae/cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95", size = 28044 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/4e/de4ff18bcf55857ba18d3a4bd48c8a9fde6bb0980c9d20b263f05387fd88/cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb", size = 9530 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2024.12.14"
|
||||
@@ -272,39 +247,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clusterfuzz"
|
||||
version = "2.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-api-python-client" },
|
||||
{ name = "google-auth", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "google-auth", version = "2.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "google-auth-oauthlib", version = "0.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "google-auth-oauthlib", version = "1.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "google-cloud-core" },
|
||||
{ name = "google-cloud-datastore" },
|
||||
{ name = "google-cloud-logging" },
|
||||
{ name = "google-cloud-monitoring", version = "2.19.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "google-cloud-monitoring", version = "2.27.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "google-cloud-ndb" },
|
||||
{ name = "google-cloud-storage" },
|
||||
{ name = "grpcio" },
|
||||
{ name = "httplib2" },
|
||||
{ name = "mozprocess" },
|
||||
{ name = "oauth2client" },
|
||||
{ name = "protobuf" },
|
||||
{ name = "psutil" },
|
||||
{ name = "pytz" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "requests" },
|
||||
{ name = "six" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b3/c0/a81a0d56da7331a15b9cc5a91e42ebe5aca9af1545f3830f7639b699a282/clusterfuzz-2.6.0.tar.gz", hash = "sha256:5ecb5375b8606153cb4b703d653201db88837ec7245860d142764abd1eedf3e5", size = 987133 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/47/8b/71cf4f85ef7f3f613aeee91e1050aa2cffbf9c0d717bd4ef5661ace41b2e/clusterfuzz-2.6.0-py3-none-any.whl", hash = "sha256:e3cacc00af7853ff04baa8f79fb72037bcd7786579461decc4a33372e5effddb", size = 1482932 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
@@ -319,7 +261,6 @@ name = "common"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "clusterfuzz" },
|
||||
{ name = "langchain" },
|
||||
{ name = "langchain-core" },
|
||||
{ name = "langchain-openai" },
|
||||
@@ -328,6 +269,7 @@ dependencies = [
|
||||
{ name = "pydantic-settings" },
|
||||
{ name = "pymongo" },
|
||||
{ name = "redis" },
|
||||
{ name = "six" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
@@ -338,7 +280,6 @@ dev = [
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "clusterfuzz", specifier = "==2.6.0" },
|
||||
{ name = "langchain", specifier = "~=0.3.18" },
|
||||
{ name = "langchain-core", specifier = "~=0.3.32" },
|
||||
{ name = "langchain-openai", specifier = "~=0.3.2" },
|
||||
@@ -347,6 +288,7 @@ requires-dist = [
|
||||
{ name = "pydantic-settings", specifier = ">=2.7.1" },
|
||||
{ name = "pymongo", specifier = ">=4.10.1" },
|
||||
{ name = "redis", specifier = ">=5.2.1,<6.0.0" },
|
||||
{ name = "six", specifier = ">=1.17.0" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
@@ -436,318 +378,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-api-core"
|
||||
version = "1.34.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-auth", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "google-auth", version = "2.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "googleapis-common-protos" },
|
||||
{ name = "protobuf" },
|
||||
{ name = "requests" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c8/b0/7c8d4a03960da803a4c471545fd7b3404d2819f1585ba3f3d97e887aa91d/google-api-core-1.34.1.tar.gz", hash = "sha256:3399c92887a97d33038baa4bfd3bf07acc05d474b0171f333e1f641c1364e552", size = 129177 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/11/51/1d325e9b7358f15dca82e1ed91413c5cecb9d4665da6c44cb8dd348deeaa/google_api_core-1.34.1-py3-none-any.whl", hash = "sha256:52bcc9d9937735f8a3986fa0bbf9135ae9cf5393a722387e5eced520e39c774a", size = 120355 },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
grpc = [
|
||||
{ name = "grpcio" },
|
||||
{ name = "grpcio-status" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-api-python-client"
|
||||
version = "2.161.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-api-core" },
|
||||
{ name = "google-auth", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "google-auth", version = "2.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "google-auth-httplib2" },
|
||||
{ name = "httplib2" },
|
||||
{ name = "uritemplate" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0a/50/c8d2d3c4e65e081c4c07b15e4fe35671676c5ecdb3674a167229e83ce49a/google_api_python_client-2.161.0.tar.gz", hash = "sha256:324c0cce73e9ea0a0d2afd5937e01b7c2d6a4d7e2579cdb6c384f9699d6c9f37", size = 12358839 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/e8/ca1efe224166a4c77ac92b4314b90f2fb70fdde1f763c1613ba3b9f50752/google_api_python_client-2.161.0-py2.py3-none-any.whl", hash = "sha256:9476a5a4f200bae368140453df40f9cda36be53fa7d0e9a9aac4cdb859a26448", size = 12869974 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-auth"
|
||||
version = "1.35.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12.4'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "cachetools", version = "4.2.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "pyasn1-modules", marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "rsa", marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "setuptools", marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "six", marker = "python_full_version >= '3.12.4'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9a/97/bf2edc87092301da1936b0df4d9d60e5f4287b6910b7d8f5cc0ea796d620/google-auth-1.35.0.tar.gz", hash = "sha256:b7033be9028c188ee30200b204ea00ed82ea1162e8ac1df4aa6ded19a191d88e", size = 181504 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/7a/1b3eb54caee1b8c73c2c3645f78a382eca4805a301a30c64a078e736e446/google_auth-1.35.0-py2.py3-none-any.whl", hash = "sha256:997516b42ecb5b63e8d80f5632c1a61dddf41d2a4c2748057837e06e00014258", size = 152949 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-auth"
|
||||
version = "2.38.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12' and python_full_version < '3.12.4'",
|
||||
"python_full_version < '3.12'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "cachetools", version = "5.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "pyasn1-modules", marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "rsa", marker = "python_full_version < '3.12.4'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c6/eb/d504ba1daf190af6b204a9d4714d457462b486043744901a6eeea711f913/google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4", size = 270866 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/47/603554949a37bca5b7f894d51896a9c534b9eab808e2520a748e081669d0/google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a", size = 210770 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-auth-httplib2"
|
||||
version = "0.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-auth", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "google-auth", version = "2.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "httplib2" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/56/be/217a598a818567b28e859ff087f347475c807a5649296fb5a817c58dacef/google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05", size = 10842 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/be/8a/fe34d2f3f9470a27b01c9e76226965863f153d5fbe276f83608562e49c04/google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d", size = 9253 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-auth-oauthlib"
|
||||
version = "0.5.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12.4'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "google-auth", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "requests-oauthlib", marker = "python_full_version >= '3.12.4'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f0/d0/3521515d13827eb4c68d3b45972ad49a21b02e486c589fdd3c2aee9b9065/google-auth-oauthlib-0.5.3.tar.gz", hash = "sha256:307d21918d61a0741882ad1fd001c67e68ad81206451d05fc4d26f79de56fc90", size = 20967 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/74/8a2664dc7b5494ebef67f876467d7a2336810affcd0b9f7cf325631314ac/google_auth_oauthlib-0.5.3-py2.py3-none-any.whl", hash = "sha256:9e8ff4ed2b21c174a2d6cc2172c698dbf0b1f686509774c663a83c495091fe09", size = 19395 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-auth-oauthlib"
|
||||
version = "1.2.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12' and python_full_version < '3.12.4'",
|
||||
"python_full_version < '3.12'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "google-auth", version = "2.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "requests-oauthlib", marker = "python_full_version < '3.12.4'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cc/0f/1772edb8d75ecf6280f1c7f51cbcebe274e8b17878b382f63738fd96cee5/google_auth_oauthlib-1.2.1.tar.gz", hash = "sha256:afd0cad092a2eaa53cd8e8298557d6de1034c6cb4a740500b5357b648af97263", size = 24970 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/8e/22a28dfbd218033e4eeaf3a0533b2b54852b6530da0c0fe934f0cc494b29/google_auth_oauthlib-1.2.1-py2.py3-none-any.whl", hash = "sha256:2d58a27262d55aa1b87678c3ba7142a080098cbc2024f903c62355deb235d91f", size = 24930 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-appengine-logging"
|
||||
version = "1.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12.4'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "google-api-core", extra = ["grpc"], marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "proto-plus", marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "protobuf", marker = "python_full_version >= '3.12.4'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9a/09/8124cc4bebb36b6ff23b2fa412e5d09b2a5009fe3f2c7a9f7692b21896a8/google-cloud-appengine-logging-1.4.0.tar.gz", hash = "sha256:fe74f418d0b01ebebe83ae212abf051ad42692a636677e397de3d459e00d7b64", size = 13613 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/83/4a3b64bff9c57beebd405e894228702bd2f06ba093d812a1925c8455fb73/google_cloud_appengine_logging-1.4.0-py2.py3-none-any.whl", hash = "sha256:226721903a2d50b6e51c43e59edb548c0bb08cc5f70e1a5f289d3edf2f09a8c9", size = 15413 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-appengine-logging"
|
||||
version = "1.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12' and python_full_version < '3.12.4'",
|
||||
"python_full_version < '3.12'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "google-api-core", extra = ["grpc"], marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "google-auth", version = "2.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "proto-plus", marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "protobuf", marker = "python_full_version < '3.12.4'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/32/5f/91891f39bfb79ab135a9ed7dfd2f632f6bb6bb93d31004605e4fcf44c55f/google_cloud_appengine_logging-1.6.0.tar.gz", hash = "sha256:2f6ef52dfa88eca3bea078bc3c2b55ea61ff08b5e0c37912027fbb2a5f2d91ce", size = 13827 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/21/86/eaf096fe40906dc99489ee3c8dac916e19f2559435afbfb1af1f16fa742f/google_cloud_appengine_logging-1.6.0-py2.py3-none-any.whl", hash = "sha256:741557e5281e351ccc90f774685a1adb0e5996cc233bcd019ef072280aaad13f", size = 15456 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-audit-log"
|
||||
version = "0.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "googleapis-common-protos" },
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/81/c345efe9261a4b0bd0c5957f1685d2b4cc4522ec4fc7b0861f691d4476e7/google_cloud_audit_log-0.3.0.tar.gz", hash = "sha256:901428b257020d8c1d1133e0fa004164a555e5a395c7ca3cdbb8486513df3a65", size = 25473 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/d9/d2795cae4a41781269413108cc7fcbfbcb595b89212216d56c4ce6e2482e/google_cloud_audit_log-0.3.0-py2.py3-none-any.whl", hash = "sha256:8340793120a1d5aa143605def8704ecdcead15106f754ef1381ae3bab533722f", size = 27341 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-core"
|
||||
version = "1.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-api-core" },
|
||||
{ name = "six" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ae/9e/91c5af8ce7a55bf359d3bd3e31507a091c769c8b59d2951fe4fc14bd9409/google-cloud-core-1.5.0.tar.gz", hash = "sha256:1277a015f8eeb014c48f2ec094ed5368358318f1146cf49e8de389962dc19106", size = 33410 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/36/82/d54bdbdbae02c66ec26c97eb684cfb27af82b3e286497625b815c4741792/google_cloud_core-1.5.0-py2.py3-none-any.whl", hash = "sha256:99a8a15f406f53f2b11bda1f45f952a9cdfbdbba8abf40c75651019d800879f5", size = 27412 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-datastore"
|
||||
version = "1.12.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-api-core", extra = ["grpc"] },
|
||||
{ name = "google-cloud-core" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d0/08/e77558a19fa24997f132a5c987d668776b35c2f006c6e4afb0043a76eba0/google-cloud-datastore-1.12.0.tar.gz", hash = "sha256:c98690833ee2e6341a4b802f278ba17d582ce58eb2e73152516ebc77522d82d7", size = 117206 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/27/e9/1132b0e4dce7d96df7620ce7c465bd99803cc62a467396ea93fee3a82931/google_cloud_datastore-1.12.0-py2.py3-none-any.whl", hash = "sha256:4728893641b26f734bd48810903b3dd590a523448da5ba8101f9b78619c138fa", size = 97329 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-logging"
|
||||
version = "3.1.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-api-core", extra = ["grpc"] },
|
||||
{ name = "google-cloud-appengine-logging", version = "1.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "google-cloud-appengine-logging", version = "1.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "google-cloud-audit-log" },
|
||||
{ name = "google-cloud-core" },
|
||||
{ name = "grpc-google-iam-v1" },
|
||||
{ name = "proto-plus" },
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/cb/ad3b113c65a72de208b6f155719a53e8d205657525d6e737bcb06baac9ae/google-cloud-logging-3.1.2.tar.gz", hash = "sha256:3ed00a8bd2076fee7a1dc053880fcb3c973184807e240f8c3c0929dd748eaf7f", size = 213175 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/26/b4/05fecd87f8280af687e98489663fc67a5faadd1cb2c0ad8e8c8820941067/google_cloud_logging-3.1.2-py2.py3-none-any.whl", hash = "sha256:702b69b01c4b98c50a4e4e7cbe12309de8c6685e99ec100c0c902e1765a45e92", size = 188731 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-monitoring"
|
||||
version = "2.19.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12.4'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "google-api-core", extra = ["grpc"], marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "proto-plus", marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "protobuf", marker = "python_full_version >= '3.12.4'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/11/26/30b48ab7d77695254a3530489ff08145e632dc74b82c7d35a872f92bd58b/google-cloud-monitoring-2.19.0.tar.gz", hash = "sha256:ce1b43929b89e0d1f594e1589b0fa83be47f1fd80fe8bfae77fe1f7732249f06", size = 338654 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/3b/0e82fac631f4e10578afbd80be9d32a4d9ba2e9dd511e66c322ed1b0e0be/google_cloud_monitoring-2.19.0-py2.py3-none-any.whl", hash = "sha256:e42b4dc59b89a8afc09da70b049b91c1ce6d73159ab20833a76650eed9e5ffa8", size = 341699 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-monitoring"
|
||||
version = "2.27.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.12' and python_full_version < '3.12.4'",
|
||||
"python_full_version < '3.12'",
|
||||
]
|
||||
dependencies = [
|
||||
{ name = "google-api-core", extra = ["grpc"], marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "google-auth", version = "2.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "proto-plus", marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "protobuf", marker = "python_full_version < '3.12.4'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2d/da/1e529674dbb57d06d5c28a1502d952b0b9cde944368e950438ef090a0318/google_cloud_monitoring-2.27.0.tar.gz", hash = "sha256:f66552528812e7b6a9f2fb7e0b7c3e7904cc0c13504f4fcb42035722b5da2bc4", size = 388549 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/80/104a46c4be3c4b213391776ebd7ccccfd4c18d406ecaaa684e07727d90b4/google_cloud_monitoring-2.27.0-py2.py3-none-any.whl", hash = "sha256:d51c006b778d2873955c23144a0a13de839c5ebe619e8fca77e1f3aa2643db35", size = 380280 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-ndb"
|
||||
version = "1.11.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-cloud-datastore" },
|
||||
{ name = "pymemcache" },
|
||||
{ name = "pytz" },
|
||||
{ name = "redis" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/23/34/7caaef0ec6e5da9a72d335128c5991b67210e1c53289be99738ac749ca8b/google-cloud-ndb-1.11.2.tar.gz", hash = "sha256:48048083040430145f42fb9ea05bc5954c47c53b39b9e02c29a1a7478dc142cb", size = 157360 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/73/f4/fafa5ac551646b2aa39e93c80719e6d8c7fe37cb1929e811b5680807cc95/google_cloud_ndb-1.11.2-py3-none-any.whl", hash = "sha256:b08d3c81b18a8c906cb3e13833471fba78accd91dce954630531e6ffa7b49fee", size = 177354 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-storage"
|
||||
version = "1.23.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "google-auth", version = "1.35.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12.4'" },
|
||||
{ name = "google-auth", version = "2.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12.4'" },
|
||||
{ name = "google-cloud-core" },
|
||||
{ name = "google-resumable-media" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c4/6c/7a2c35f85cb965f318b5e5080673df082920ecd2c0399c25800c7b9e9b41/google-cloud-storage-1.23.0.tar.gz", hash = "sha256:c66e876ae9547884fa42566a2ebfec51d280f488d7a058af9611ba90c78bed78", size = 5402234 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/87/78/7cf94b3d0961b1a3036ba351c7fdc04170baa73d20fcb41240da214c83fd/google_cloud_storage-1.23.0-py2.py3-none-any.whl", hash = "sha256:9f59c100d3940e38567c48d54cf1a2e7591a2f38e9693dfc11a242d5e54a1626", size = 72323 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-resumable-media"
|
||||
version = "0.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "six" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/79/70/8d2afddae61b0a0189dbefcdcd024a4030c9c696ca3ea410e43498520ed9/google-resumable-media-0.5.1.tar.gz", hash = "sha256:97155236971970382b738921f978a6f86a7b5a0b0311703d991e065d3cb55773", size = 2117923 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/cc/cd05c633298fcbba5d61b6b8844de598e001954281a004fc1a13c61a5121/google_resumable_media-0.5.1-py2.py3-none-any.whl", hash = "sha256:cdc64378dc9a7a7bf963a8d0c944c99b549dc0c195a9acbf1fcd465f380b9002", size = 38962 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "googleapis-common-protos"
|
||||
version = "1.67.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/31/e1/fbffb85a624f1404133b5bb624834e77e0f549e2b8548146fe18c56e1411/googleapis_common_protos-1.67.0.tar.gz", hash = "sha256:21398025365f138be356d5923e9168737d94d46a72aefee4a6110a1f23463c86", size = 57344 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/89/30/2bd0eb03a7dee7727cd2ec643d1e992979e62d5e7443507381cce0455132/googleapis_common_protos-1.67.0-py2.py3-none-any.whl", hash = "sha256:579de760800d13616f51cf8be00c876f00a9f146d3e6510e19d1f4111758b741", size = 164985 },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
grpc = [
|
||||
{ name = "grpcio" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.1.1"
|
||||
@@ -783,69 +413,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grpc-google-iam-v1"
|
||||
version = "0.14.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "googleapis-common-protos", extra = ["grpc"] },
|
||||
{ name = "grpcio" },
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/47/2f/68e43b0e551974fa7dd18798a5974710586a72dc484ecaa2fc023d961342/grpc_google_iam_v1-0.14.0.tar.gz", hash = "sha256:c66e07aa642e39bb37950f9e7f491f70dad150ac9801263b42b2814307c2df99", size = 18327 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/66/b4/ab54f7fda4af43ca5c094bc1d6341780fd669c44ae18952b5337029b1d98/grpc_google_iam_v1-0.14.0-py2.py3-none-any.whl", hash = "sha256:fb4a084b30099ba3ab07d61d620a0d4429570b13ff53bd37bac75235f98b7da4", size = 27276 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grpcio"
|
||||
version = "1.70.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/69/e1/4b21b5017c33f3600dcc32b802bb48fe44a4d36d6c066f52650c7c2690fa/grpcio-1.70.0.tar.gz", hash = "sha256:8d1584a68d5922330025881e63a6c1b54cc8117291d382e4fa69339b6d914c56", size = 12788932 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/10/e9/f72408bac1f7b05b25e4df569b02d6b200c8e7857193aa9f1df7a3744add/grpcio-1.70.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:95469d1977429f45fe7df441f586521361e235982a0b39e33841549143ae2851", size = 5229736 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/17/e65139ea76dac7bcd8a3f17cbd37e3d1a070c44db3098d0be5e14c5bd6a1/grpcio-1.70.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:ed9718f17fbdb472e33b869c77a16d0b55e166b100ec57b016dc7de9c8d236bf", size = 11432751 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/12/42de6082b4ab14a59d30b2fc7786882fdaa75813a4a4f3d4a8c4acd6ed59/grpcio-1.70.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:374d014f29f9dfdb40510b041792e0e2828a1389281eb590df066e1cc2b404e5", size = 5711439 },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/f8/b5a19524d273cbd119274a387bb72d6fbb74578e13927a473bc34369f079/grpcio-1.70.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2af68a6f5c8f78d56c145161544ad0febbd7479524a59c16b3e25053f39c87f", size = 6330777 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/67/3d6c0ad786238aac7fa93b79246fc452978fbfe9e5f86f70da8e8a2797d0/grpcio-1.70.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7df14b2dcd1102a2ec32f621cc9fab6695effef516efbc6b063ad749867295", size = 5944639 },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/0d/d9f7cbc41c2743cf18236a29b6a582f41bd65572a7144d92b80bc1e68479/grpcio-1.70.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c78b339869f4dbf89881e0b6fbf376313e4f845a42840a7bdf42ee6caed4b11f", size = 6643543 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/24/bdd7e606b3400c14330e33a4698fa3a49e38a28c9e0a831441adbd3380d2/grpcio-1.70.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:58ad9ba575b39edef71f4798fdb5c7b6d02ad36d47949cd381d4392a5c9cbcd3", size = 6199897 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/33/8132eb370087960c82d01b89faeb28f3e58f5619ffe19889f57c58a19c18/grpcio-1.70.0-cp310-cp310-win32.whl", hash = "sha256:2b0d02e4b25a5c1f9b6c7745d4fa06efc9fd6a611af0fb38d3ba956786b95199", size = 3617513 },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/bc/0fce5cfc0ca969df66f5dca6cf8d2258abb88146bf9ab89d8cf48e970137/grpcio-1.70.0-cp310-cp310-win_amd64.whl", hash = "sha256:0de706c0a5bb9d841e353f6343a9defc9fc35ec61d6eb6111802f3aa9fef29e1", size = 4303342 },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/c4/1f67d23d6bcadd2fd61fb460e5969c52b3390b4a4e254b5e04a6d1009e5e/grpcio-1.70.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:17325b0be0c068f35770f944124e8839ea3185d6d54862800fc28cc2ffad205a", size = 5229017 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/bd/cc36811c582d663a740fb45edf9f99ddbd99a10b6ba38267dc925e1e193a/grpcio-1.70.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:dbe41ad140df911e796d4463168e33ef80a24f5d21ef4d1e310553fcd2c4a386", size = 11472027 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/32/8538bb2ace5cd72da7126d1c9804bf80b4fe3be70e53e2d55675c24961a8/grpcio-1.70.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5ea67c72101d687d44d9c56068328da39c9ccba634cabb336075fae2eab0d04b", size = 5707785 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/5c/a45f85f2a0dfe4a6429dee98717e0e8bd7bd3f604315493c39d9679ca065/grpcio-1.70.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb5277db254ab7586769e490b7b22f4ddab3876c490da0a1a9d7c695ccf0bf77", size = 6331599 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/e5/5316b239380b8b2ad30373eb5bb25d9fd36c0375e94a98a0a60ea357d254/grpcio-1.70.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7831a0fc1beeeb7759f737f5acd9fdcda520e955049512d68fda03d91186eea", size = 5940834 },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/33/dbf035bc6d167068b4a9f2929dfe0b03fb763f0f861ecb3bb1709a14cb65/grpcio-1.70.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:27cc75e22c5dba1fbaf5a66c778e36ca9b8ce850bf58a9db887754593080d839", size = 6641191 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/c4/684d877517e5bfd6232d79107e5a1151b835e9f99051faef51fed3359ec4/grpcio-1.70.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d63764963412e22f0491d0d32833d71087288f4e24cbcddbae82476bfa1d81fd", size = 6198744 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/43/92fe5eeaf340650a7020cfb037402c7b9209e7a0f3011ea1626402219034/grpcio-1.70.0-cp311-cp311-win32.whl", hash = "sha256:bb491125103c800ec209d84c9b51f1c60ea456038e4734688004f377cfacc113", size = 3617111 },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/15/b6cf2c9515c028aff9da6984761a3ab484a472b0dc6435fcd07ced42127d/grpcio-1.70.0-cp311-cp311-win_amd64.whl", hash = "sha256:d24035d49e026353eb042bf7b058fb831db3e06d52bee75c5f2f3ab453e71aca", size = 4304604 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/a4/ddbda79dd176211b518f0f3795af78b38727a31ad32bc149d6a7b910a731/grpcio-1.70.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:ef4c14508299b1406c32bdbb9fb7b47612ab979b04cf2b27686ea31882387cff", size = 5198135 },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/5c/60eb8a063ea4cb8d7670af8fac3f2033230fc4b75f62669d67c66ac4e4b0/grpcio-1.70.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:aa47688a65643afd8b166928a1da6247d3f46a2784d301e48ca1cc394d2ffb40", size = 11447529 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/b9/1bf8ab66729f13b44e8f42c9de56417d3ee6ab2929591cfee78dce749b57/grpcio-1.70.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:880bfb43b1bb8905701b926274eafce5c70a105bc6b99e25f62e98ad59cb278e", size = 5664484 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/06/2f377d6906289bee066d96e9bdb91e5e96d605d173df9bb9856095cccb57/grpcio-1.70.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e654c4b17d07eab259d392e12b149c3a134ec52b11ecdc6a515b39aceeec898", size = 6303739 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/50/64c94cfc4db8d9ed07da71427a936b5a2bd2b27c66269b42fbda82c7c7a4/grpcio-1.70.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2394e3381071045a706ee2eeb6e08962dd87e8999b90ac15c55f56fa5a8c9597", size = 5910417 },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/89/8795dfc3db4389c15554eb1765e14cba8b4c88cc80ff828d02f5572965af/grpcio-1.70.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b3c76701428d2df01964bc6479422f20e62fcbc0a37d82ebd58050b86926ef8c", size = 6626797 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/b2/6a97ac91042a2c59d18244c479ee3894e7fb6f8c3a90619bb5a7757fa30c/grpcio-1.70.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac073fe1c4cd856ebcf49e9ed6240f4f84d7a4e6ee95baa5d66ea05d3dd0df7f", size = 6190055 },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/2b/28db55c8c4d156053a8c6f4683e559cd0a6636f55a860f87afba1ac49a51/grpcio-1.70.0-cp312-cp312-win32.whl", hash = "sha256:cd24d2d9d380fbbee7a5ac86afe9787813f285e684b0271599f95a51bce33528", size = 3600214 },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/c3/a7a225645a965029ed432e5b5e9ed959a574e62100afab553eef58be0e37/grpcio-1.70.0-cp312-cp312-win_amd64.whl", hash = "sha256:0495c86a55a04a874c7627fd33e5beaee771917d92c0e6d9d797628ac40e7655", size = 4292538 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grpcio-status"
|
||||
version = "1.48.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "googleapis-common-protos" },
|
||||
{ name = "grpcio" },
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/62/a86443ec8f7bf635fe0b48abe56cd699816bdc0b29d24e0bcb5cada42d4a/grpcio-status-1.48.2.tar.gz", hash = "sha256:53695f45da07437b7c344ee4ef60d370fd2850179f5a28bb26d8e2aa1102ec11", size = 13485 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/14/32/754cd4474790239c7436a7a9490bc0c4a0a2ed604cb9a940151a3b1055b9/grpcio_status-1.48.2-py3-none-any.whl", hash = "sha256:2c33bbdbe20188b2953f46f31af669263b6ee2a9b2d38fa0d36ee091532e21bf", size = 14441 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.14.0"
|
||||
@@ -868,18 +435,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httplib2"
|
||||
version = "0.22.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyparsing" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3d/ad/2371116b22d616c194aa25ec410c9c6c37f23599dcd590502b74db197584/httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81", size = 351116 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/6c/d2fbdaaa5959339d53ba38e94c123e4e84b8fbc4b84beb0e70d7c1608486/httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", size = 96854 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.28.1"
|
||||
@@ -1081,43 +636,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/a8/9f5816342fb3773882e4dbb1c8e2934b49376d7aca840916590a7b6468cb/langsmith-0.3.2-py3-none-any.whl", hash = "sha256:48ff6bc5eda62f4729596bb68d4f96166d2654728ac32970b69b1be874c61925", size = 333022 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mozfile"
|
||||
version = "3.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "six" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/25/f8/a1f0076490d50dbe8bdcf15df97856a4734f459aaf0a4d42c64a11ab7231/mozfile-3.0.0.tar.gz", hash = "sha256:92ca1a786abbdf5e6a7aada62d3a4e28f441ef069c7623223add45268e53c789", size = 7699 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/cd/fe6b0afa57fbf026631de1435682735dae0aa0ffbe3855f62806da31c55b/mozfile-3.0.0-py2.py3-none-any.whl", hash = "sha256:3b0afcda2fa8b802ef657df80a56f21619008f61fcc14b756124028d7b7adf5c", size = 8227 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mozinfo"
|
||||
version = "1.2.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "distro" },
|
||||
{ name = "mozfile" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/23/35/96cccb2244a08247f5c1b5e810d6117d35a30e4a3e29679ed0c7dd2406c6/mozinfo-1.2.3.tar.gz", hash = "sha256:5d2b8a5f1b362692f221e33eb3ff47454a580db1a1384614cdc637b31131b438", size = 6358 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/28/b2/0efcb9aa6d1362aa00b567c8f355f028332a5a533f80c45e5dacd12a5466/mozinfo-1.2.3-py2.py3-none-any.whl", hash = "sha256:90e0cfb377fc2cc3fad023d38c1f6d60a9135400ff5684a04abf79ca5cc3c521", size = 7454 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mozprocess"
|
||||
version = "1.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mozinfo" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3a/72/ecf7be35e24065c5780412627f4269a8bdae71eb42823d1416ea6968db11/mozprocess-1.4.0.tar.gz", hash = "sha256:6dbb6ebb5e01d6bdf1b24202719ad298331885aee088375e7e4372cdf9d5bb98", size = 21642 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/a3/354e9f0c8b629319e6f6334beaf42e38eeb5ddc821c77e9082752b037d3f/mozprocess-1.4.0-py2.py3-none-any.whl", hash = "sha256:9a3b218dab0f1277275be7d89d673cd55b062519043a88a1a1a77eacefdfdf5e", size = 23105 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "6.1.0"
|
||||
@@ -1256,31 +774,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/26/96/deb93f871f401045a684ca08a009382b247d14996d7a94fea6aa43c67b94/numpy-2.2.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60", size = 12822674 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oauth2client"
|
||||
version = "4.1.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "httplib2" },
|
||||
{ name = "pyasn1" },
|
||||
{ name = "pyasn1-modules" },
|
||||
{ name = "rsa" },
|
||||
{ name = "six" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a6/7b/17244b1083e8e604bf154cf9b716aecd6388acd656dd01893d0d244c94d9/oauth2client-4.1.3.tar.gz", hash = "sha256:d486741e451287f69568a4d26d70d9acd73a2bbfa275746c535b4209891cccc6", size = 155910 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/95/a9/4f25a14d23f0786b64875b91784607c2277eff25d48f915e39ff0cff505a/oauth2client-4.1.3-py2.py3-none-any.whl", hash = "sha256:b8a81cc5d60e2d364f0b1b98f958dbd472887acaf1a5b05e21c28c31a2d6d3ac", size = 98206 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oauthlib"
|
||||
version = "3.2.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918", size = 177352 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.60.2"
|
||||
@@ -1422,18 +915,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/41/b6/c5319caea262f4821995dca2107483b94a3345d4607ad797c76cb9c36bcc/propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54", size = 11818 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proto-plus"
|
||||
version = "1.26.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/26/79/a5c6cbb42268cfd3ddc652dc526889044a8798c688a03ff58e5e92b743c8/proto_plus-1.26.0.tar.gz", hash = "sha256:6e93d5f5ca267b54300880fff156b6a3386b3fa3f43b1da62e680fc0c586ef22", size = 56136 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/42/c3/59308ccc07b34980f9d532f7afc718a9f32b40e52cde7a740df8d55632fb/proto_plus-1.26.0-py3-none-any.whl", hash = "sha256:bf2dfaa3da281fc3187d12d224c707cb57214fb2c22ba854eb0c105a3fb2d4d7", size = 50166 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "3.20.3"
|
||||
@@ -1447,42 +928,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/14/619e24a4c70df2901e1f4dbc50a6291eb63a759172558df326347dce1f0d/protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db", size = 162128 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psutil"
|
||||
version = "7.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyasn1"
|
||||
version = "0.6.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ba/e9/01f1a64245b89f039897cb0130016d79f77d52669aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyasn1-modules"
|
||||
version = "0.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyasn1" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1d/67/6afbf0d507f73c32d21084a79946bfcfca5fbc62a72057e9c23797a737c9/pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c", size = 310028 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/77/89/bc88a6711935ba795a679ea6ebee07e128050d6382eaa35a0a47c8032bdc/pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd", size = 181537 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.22"
|
||||
@@ -1580,15 +1025,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/46/93416fdae86d40879714f72956ac14df9c7b76f7d41a4d68aa9f71a0028b/pydantic_settings-2.7.1-py3-none-any.whl", hash = "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd", size = 29718 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pymemcache"
|
||||
version = "4.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d9/b6/4541b664aeaad025dfb8e851dcddf8e25ab22607e674dd2b562ea3e3586f/pymemcache-4.0.0.tar.gz", hash = "sha256:27bf9bd1bbc1e20f83633208620d56de50f14185055e49504f4f5e94e94aff94", size = 70176 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/41/ba/2f7b22d8135b51c4fefb041461f8431e1908778e6539ff5af6eeaaee367a/pymemcache-4.0.0-py2.py3-none-any.whl", hash = "sha256:f507bc20e0dc8d562f8df9d872107a278df049fa496805c1431b926f3ddd0eab", size = 60772 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pymongo"
|
||||
version = "4.11.1"
|
||||
@@ -1627,15 +1063,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/e2/b1747eabad8bf172aa66fae50ed7290c4992b8adbeaddbe31944755dbed4/pymongo-4.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:c71655f4188c70032ba56ac7ead688449e4f86a4ccd8e57201ee283f2f591e1d", size = 882299 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.2.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8b/1a/3544f4f299a47911c2ab3710f534e52fea62a633c96806995da5d25be4b2/pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a", size = 1067694 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/a7/c8a2d361bf89c0d9577c934ebb7421b25dc84bf3a8e3ac0a40aed9acc547/pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", size = 107716 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.3.4"
|
||||
@@ -1662,15 +1089,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2025.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5f/57/df1c9157c8d5a05117e455d66fd7cf6dbc46974f832b1058ed4856785d8a/pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e", size = 319617 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
@@ -1787,19 +1205,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests-oauthlib"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "oauthlib" },
|
||||
{ name = "requests" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests-toolbelt"
|
||||
version = "1.0.0"
|
||||
@@ -1812,18 +1217,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "4.9"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pyasn1" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/aa/65/7d973b89c4d2351d7fb232c2e452547ddfa243e93131e7cfa766da627b52/rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21", size = 29711 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", size = 34315 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.9.3"
|
||||
@@ -1849,15 +1242,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/69/cb/b3fe58a136a27d981911cba2f18e4b29f15010623b79f0f2510fd0d31fd3/ruff-0.9.3-py3-none-win_arm64.whl", hash = "sha256:800d773f6d4d33b0a3c60e2c6ae8f4c202ea2de056365acfa519aa48acf28e0b", size = 10038168 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "75.8.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/92/ec/089608b791d210aec4e7f97488e67ab0d33add3efccb83a056cbafe3a2a6/setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6", size = 1343222 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/69/8a/b9dc7678803429e4a3bc9ba462fa3dd9066824d3c607490235c6a796be5a/setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3", size = 1228782 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.17.0"
|
||||
@@ -2002,15 +1386,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uritemplate"
|
||||
version = "4.1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d2/5a/4742fdba39cd02a56226815abfa72fe0aa81c33bed16ed045647d6000eba/uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0", size = 273898 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/81/c0/7461b49cd25aeece13766f02ee576d1db528f1c37ce69aee300e075b485b/uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e", size = 10356 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.3.0"
|
||||
|
||||
@@ -11,7 +11,7 @@ import argparse
|
||||
from redis import Redis
|
||||
from buttercup.common.datastructures.msg_pb2 import BuildOutput, WeightedHarness
|
||||
import time
|
||||
from clusterfuzz.fuzz import get_fuzz_targets
|
||||
from buttercup.common.clusterfuzz_utils import get_fuzz_targets
|
||||
import os
|
||||
from buttercup.common.logger import setup_package_logger
|
||||
from buttercup.common.maps import BUILD_TYPES
|
||||
|
||||
@@ -7,7 +7,6 @@ authors = [{ name = "Trail of Bits", email = "opensource@trailofbits.com" }]
|
||||
requires-python = ">=3.12,<3.13"
|
||||
dependencies = [
|
||||
"argon2-cffi>=21.0.0",
|
||||
"clusterfuzz>=2.6.0",
|
||||
"common",
|
||||
"fastapi>=0.115.6",
|
||||
"pydantic>=2.10.5",
|
||||
|
||||
@@ -17,7 +17,7 @@ from buttercup.common.datastructures.msg_pb2 import (
|
||||
from buttercup.common.project_yaml import ProjectYaml
|
||||
from buttercup.orchestrator.scheduler.cancellation import Cancellation
|
||||
from buttercup.orchestrator.scheduler.vulnerabilities import Vulnerabilities
|
||||
from clusterfuzz.fuzz import get_fuzz_targets
|
||||
from buttercup.common.clusterfuzz_utils import get_fuzz_targets
|
||||
from buttercup.orchestrator.scheduler.patches import Patches
|
||||
from buttercup.orchestrator.scheduler.bundles import Bundles
|
||||
from buttercup.orchestrator.api_client_factory import create_api_client
|
||||
|
||||
Reference in New Issue
Block a user