예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
    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
예제 #4
0
    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
예제 #5
0
    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
예제 #6
0
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})'
            })
예제 #7
0
    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
예제 #8
0
    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
예제 #9
0
        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()
예제 #10
0
    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