def set_bucket_size(self, info: RequestInfo): bucket_size = self.bucket_size_cache[info.netloc]['body_param_bucket'].get('size') if bucket_size: info.body_param_bucket = bucket_size - (len(info.request.body) if info.request.body else 0) else: info.body_param_bucket = None
def set_bucket_size(self, info: RequestInfo): bucket_size = self.bucket_size_cache[info.netloc].get('bucket') if bucket_size: info.cookie_bucket = bucket_size - len(info.request.headers.get('Cookie', '')) else: info.cookie_bucket = None
def set_bucket_size(self, info: RequestInfo): """ Устанавивает для запроса в `info` общее число хидеров """ bucket_size = self.bucket_size_cache[info.netloc]['header_bucket'].get('size') if bucket_size: info.header_bucket = bucket_size - len(info.request.headers.keys()) else: info.header_bucket = None
def set_bucket_size(self, info: RequestInfo): bucket_size = self.bucket_size_cache[ info.netloc]['url_param_bucket'].get('size') if bucket_size: info.url_param_bucket = bucket_size - len( urlparse(info.origin_url).query) else: info.url_param_bucket = None
def set_bucket_size(self, info: RequestInfo): bucket_size = self.bucket_size_cache[info.netloc]['body_param_bucket'].get('size') if bucket_size: # Событие, возможное только в крайних случаях if bucket_size < self.min_json_param_chunk: bucket_size = self.min_json_param_chunk info.body_param_bucket = bucket_size - (len(info.request.body) if info.request.body else 0) else: info.body_param_bucket = None
def check_content_length_reason(reasons: list, info: RequestInfo, response: Response): # Если изменилась длина контента if info.response.headers.get('Content-Length', 0) != response.headers.get( 'Content-Length', 0): # Если оригинальный ответ - html документ if info.response_html_tags_count > 0: # То дополнительно проверяем число тэгов html запроса new_html_tags_count = info.count_html_tags(response.text) if new_html_tags_count != info.response_html_tags_count: reasons.append({ 'reason': DIFF_HTML_TAGS_COUNT, 'value': f'{new_html_tags_count} ({info.response_html_tags_count})' }) else: orig_content_length = info.response.headers.get( 'Content-Length', 0) content_length = response.headers.get('Content-Length', 0) reasons.append({ 'reason': DIFF_CONTENT_LENGTH, 'value': f'{content_length} ({orig_content_length})' })
def find_secrets(self, info: RequestInfo, words: List[str]): """ Проверяет изменения в ответе для заданного списка параметров `words` в URL-строке :param info: :param words: Названия заголовков :return: dict([(`param`, `reasons`)]) - если найдено конкретное слово int - если со словами требуется провести манипуляции """ # Добавляем параметры в URL-строку request = info.copy_request() params = { k: v for k, v in zip(words, [info.url_param_value] * len(words)) } param_type = ParamType.URL self.add_url_params(request, params) response = self.do_request(request) # Если не удалось получить ответ на запрос, то убираем слова из очереди if response is None: self.logger.error( f'[{info.origin_url}] Ошибка при выполнении запроса, ' 'порция удалена из учереди') return DISCARD_WORDS reasons = self.get_url_param_reasons(info, response) # Если есть изменения if reasons: # Если найден конкретный заголовок, то возвращаем его вместе с причинами if len(words) == 1: self.logger.success( f'Найден {param_type}-параметр "{words[0]}" к {info.origin_url}' ) self.logger.debug( f'{param_type}-параметр "{words[0]}": reasons={reasons}') return { words[0]: { 'url': info.origin_url, 'reasons': reasons, 'type': param_type, 'response': response } } # Иначе где-то среди слов есть искомые else: return SPLIT_WORDS # Иначе отбросить else: return DISCARD_WORDS
def find_secrets(self, info: RequestInfo, words: List[str]): """ :param info: :param words: :return: """ request = info.copy_request() headers = {k: v for k, v in zip(words, [info.header_value] * len(words))} param_type = ParamType.HEADER self.add_headers(request, headers) response = self.do_request(request) # Если не удалось получить ответ на запрос, то убираем слова из очереди if response is None: self.logger.error( f'[{info.origin_url}] Ошибка при выполнении запроса, ' 'порция удалена из учереди') return DISCARD_WORDS reasons = self.check_response(info, response) # Если есть изменения if reasons: # Если найден конкретный заголовок, то возвращаем его вместе с причинами if len(words) == 1: self.logger.success(f'Найден {param_type}-параметр "{words[0]}" к {info.origin_url}') self.logger.debug(f'{param_type}-параметр "{words[0]}": reasons={reasons}') return {words[0]: {'url': info.origin_url, 'reasons': reasons, 'type': param_type, 'response': response}} # Иначе где-то среди слов есть искомые else: return SPLIT_WORDS # Иначе отбросить else: return DISCARD_WORDS
sys.exit(1) if len(not_prepared_requests): logger.warning( f'Список не подготовленных запросов: {not_prepared_requests}') logger.info(f'Подготовлено запросов: {len(prepared_requests)}') # Добавляем заголовки в запросы, переданные через командную строку if args.additional_headers: logger.info('Добавление заголовков -H к запросам') for request in prepared_requests: RequestHelper.add_headers(request, args.additional_headers) # Преобразуем список PreparedRequest в список RequestInfo requests_list = [RequestInfo(request) for request in prepared_requests] # Получаем эталонный ответ от сервера для каждого из запросов logger.info( 'Получение эталонного ответа от сервера для каждого из запросов') RequestHelper.set_origin_responses(requests_list, args.threads, args.retry, args.timeout, args.delay, args.proxy, args.allow_redirects, logger) # Фильтруем запросы, на которые не удалость получить ответы requests_list = RequestHelper.filter_requests( requests_list, lambda x: x.response is None, 'Следующие запросы не получили изначальный ответ от сервера', 'Не удалось получить изначальные ответы на все запросы', logger) if requests_list is None: exit()
def get_optimal_bucket(self, info: RequestInfo, min_chunk: int, add_random: Callable, additional_size: Callable, logger: Logger) -> Union[int, None]: """ Ищет оптимальный размер порции параметров соотношение (Длина порции) / (время ответа) :param info: :return: """ left, cur, right = 1024, 2048, 4096 left_border = 0 right_border = math.inf counter = 5 optimal_size = None optimal_rate = 0 # Ограничение на число циклов while counter: counter -= 1 # Если левая граница обнулилась if left == 0: break # Если диапазон неделим, то прекратить цикл if right - cur < 2 or cur - left < 2: break # Подготавливаем запросы _requests = [info.copy_request() for _ in range(3)] for request, length in zip(_requests, [left, cur, right]): add_random(request, length) # Отправляем jobs = [gevent.spawn(self.do_request, request) for request in _requests] gevent.joinall(jobs) responses = [job.value for job in jobs] # Получаем результаты results = [] # results = [response.status_code == info.response.status_code if response is not None else response # for response in responses] for response in responses: if not response: results.append(None) # Если совпадают коды ответа elif response.status_code == info.response.status_code: results.append(True) # Если Payload Too Large/URI Too Long/Request Header Fields Too Large elif response.status_code in {413, 414, 431}: results.append(False) # Если код ответа на отрезке [500, 599], а оригинальный код не в этом отрезке elif 500 <= response.status_code < 600 and not 500 <= info.response.status_code < 600: results.append(False) # Если код ответа на отрезке [400, 499], а оригинальный код не в этом отрезке elif 400 <= response.status_code < 500 and not 400 <= info.response.status_code < 500: results.append(False) else: logger.debug(f'Необработанный случай: act_status_code={response.status_code}, orig_status_cod={info.response.status_code}') results.append(True) # Если все запросы не получили ответа от сервера, то сдвигаемся влево if not any(results): right_border = left right = right_border cur = right >> 1 left = cur >> 1 continue # Иначе выбираем среди ответов оптимальный rates = [] for response, size, result in zip([response for response in responses], [left, cur, right], results): # Рассматриваем только те случаи, когда мы не вышли за границы elapsed = response.elapsed.total_seconds() if (response is not None and result == True) else math.inf rate = round(size / elapsed, 1) rates.append(rate) if rate > optimal_rate and result: optimal_rate = rate optimal_size = size # Cмотрим, в какую сторону развивается динамика max_rate = max(rates) # Если все запросы не превысили границу, то двигаемся в сторону динамики if all(results): # Если динамика увеличивается слева if rates[0] == max_rate: right_border = right # То смещаемся влево right = left - 1 cur = right >> 1 left = cur >> 1 # Если левый указатель меньше левой границы if left < left_border: # То пересчитываем указатели в пределах границ left, cur, right = self.shift_bounds(left_border, right_border) # Если динамика увеличивается справа elif rates[2] == max_rate: left_border = left # То смещаемся вправо left = right + 1 cur = left << 1 right = cur << 1 # Если правый указатель вышел за пределы правой границы if right > right_border: # То пересчитываем указатели в пределах границ left, cur, right = self.shift_bounds(left_border, right_border) # Иначе рассматриваем окрестности центра else: left_border = left if left > left_border else left_border right_border = right if right < right_border else right_border left = (left + cur) // 2 right = (cur + right) // 2 # Если результаты [True, False|None, False|None] elif results[0] == True and all([not r for r in results[1:]]): right_border = cur if cur < right_border else right_border # То сдвигаемся влево right = left - 1 cur = right >> 1 left = cur >> 1 # Если результаты [True, True, False|None] elif results[2] in {None, False} and all([r for r in results[:2]]): right_border = right if right < right_border else right_border # То смотрим на динамику слева и посередине # Если динамика увеличивается слева if rates[0] == max_rate: # То сдвигаемся влево right = left - 1 # Сдвигаем рассматриваемую правую границу на 1 от ранее рассматриваемой левой cur = right >> 1 left = cur >> 1 # Если левый указатель меньше левой границы if left < left_border: # То пересчитываем указатели в пределах границ left, cur, right = self.shift_bounds(left_border, right_border) # Иначе копаем в пределах cur else: right = round((cur + right) / 2) left = (left + cur) // 2 else: # Сдвигаемся влево right = left - 1 # Сдвигаем рассматриваемую правую границу на 1 от ранее рассматриваемой левой cur = right >> 1 left = cur >> 1 # Если по итогу оптимальный размер меньше минимально требуемого, то вернуть минимально требуемый требуемый if optimal_size is not None: if optimal_size < min_chunk < right_border: return min_chunk + additional_size(info) return optimal_size + additional_size(info) return optimal_size