中文翻译暂不可用,显示俄语原文。

Реализовать fuzzing для агента

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать fuzzing для агента

1. Цель задачи

Научиться проектировать и выполнять фаззинг-тестирование LLM-агента, генерируя граничные, пустые, чрезмерно длинные и структурно некорректные запросы. Цель — выявить нештатное поведение (краши, зависания, неадекватные ответы, утечки контекста) до попадания в production.

Ключевой результат Рабочий фаззер, который за 1 час прогона находит не менее 3 различных уязвимостей/ошибок в целевом агенте (либо доказывает их отсутствие при покрытии тестовых сценариев).

2. Исходные данные

Перед началом необходимо иметь:

Что нужноОткуда взять
Целевой агент (API или локальный класс)Собственный пет-проект / тестовый агент из учебного курса
Документация агента (список команд, параметры)README, OpenAPI spec, docstrings
Токен / ключ API (если внешний сервис)Личный аккаунт разработчика (тестовый лимит)
Таймауты и квоты для тестовой средыНастройки окружения (рекомендуется staging)

Если нет реального агента — симулируем:

  1. Напишите простого агента на Python (например, на базе LangChain или простой цепочки if-else), который принимает текстовый запрос и возвращает ответ. Агент должен обрабатывать команды calc, translate, weather (имитация).
  2. Намеренно добавьте 2-3 уязвимости: превышение длины входной строки (буфер), некорректное распознавание пустого запроса (краш), рекурсивный вызов при определённом шаблоне.
  3. Запустите его как локальный Flask/FastAPI сервер на порту 8000.

3. Технологический стек

КомпонентИнструментыНазначение
ЯзыкPython 3.10+Реализация фаззера
Фреймворк тестированияpytest, hypothesisГенерация стратегий fuzzing
HTTP-клиентrequests, aiohttpОтправка запросов к агенту
Генерация данныхFaker, custom strategiesГраничные, пустые, длинные строки
Мониторингtime, loggingФиксация таймингов и ошибок
Отчётjson, markdownСохранение результатов
Агент (тестируемый)Flask/FastAPI / LangChainЦелевая система

4. Этапы выполнения

Этап 1: Подготовка окружения и тестируемого агента (30-45 минут)

Действия

  1. Установите зависимости:
    pip install hypothesis pytest requests faker
    
  2. Разверните тестового агента (если используете симуляцию):
    • Создайте файл agent_server.py с простым агентом, обрабатывающим команды:
      from flask import Flask, request, jsonify
      app = Flask(__name__)
      
      def handle_calc(expr):
          try:
              return str(eval(expr))   # уязвимость: eval!
          except:
              return "Error"
      
      def handle_translate(text):
          if len(text) > 1000: raise ValueError("Too long")
          return "translated: " + text
      
      @app.route("/agent", methods=["POST"])
      def agent():
          data = request.json
          cmd = data.get("cmd", "")
          arg = data.get("arg", "")
          if cmd == "" or arg is None:
              return jsonify({"error": "empty"}), 400
          if cmd == "calc":
              return jsonify({"result": handle_calc(arg)})
          if cmd == "translate":
              return jsonify({"result": handle_translate(arg)})
          # ... другие команды
      
  3. Запустите сервер: python agent_server.py
  4. Убедитесь, что агент отвечает на простые запросы (например, {"cmd":"calc", "arg":"2+2"}).

Ожидаемый результат этапа Работающий локальный сервер агента, готовый к фаззингу.

Этап 2: Проектирование стратегий fuzzing (45-60 минут)

Действия

  1. Определите контракт агента (формат входа, возможные поля, типы).
    • Поля: cmd (строка), arg (строка), опционально params (объект).
    • Допустимые команды: calc, translate, weather.
  2. Выделите граничные случаи для каждой команды:
    • Пустые строки: "", None.
    • Очень длинные строки: >10 000 символов.
    • Строки с управляющими символами: \x00, \n, \r, \t.
    • Невалидные JSON (если агент ожидает JSON, отправьте бинарные данные).
    • Спецсимволы: SQL-инъекции, XSS-попытки.
    • Команды, не существующие в агенте.
    • Огромное количество вложенных объектов (если принимается JSON).
  3. Запишите сценарии в виде Hypothesis strategies:
    from hypothesis import given, strategies as st
    
    # Стратегия для поля cmd
    cmd_strategy = st.text(
        min_size=0, max_size=100,
        alphabet=st.characters(whitelist_categories=('L', 'N', 'P'), blacklist_characters='\x00')
    ) | st.none() | st.just('\x00' * 10)
    
    # Стратегия для поля arg
    arg_strategy = st.text(min_size=0, max_size=20000)
    # ...
    

Ожидаемый результат этапа Документированный набор стратегий (в коде или таблице) с указанием ожидаемого поведения.

Этап 3: Реализация фаззера (1-1.5 часа)

Действия

  1. Напишите основной скрипт fuzzer.py
    • Используйте Hypothesis с декоратором @given для генерации запросов.
    • Отправляйте POST-запросы к http://localhost:8000/agent.
    • Ловите исключения (connection error, timeout, HTTP errors).
    • Фиксируйте:
      • Время ответа.
      • Статус код.
      • Наличие краша (если сервер упал).
      • Размер ответа.
      • Необычные строки в ответе (например, "Error", "Traceback").
  2. Добавьте фильтрацию ложных срабатываний
    • Пропускайте ожидаемые HTTP 400/500 (агент их допускает).
    • Фиксируйте только неожиданные: 200 вместо 400, краш процесса, бесконечное выполнение.
  3. Реализуйте защиту от долгих запросов
    • Установите таймаут 5 секунд.
    • Если запрос не завершился — логируйте TIMEOUT.
  4. Пример структуры
    import requests
    from hypothesis import given, settings, strategies as st
    import logging
    
    logging.basicConfig(filename='fuzz.log', level=logging.INFO)
    
    @settings(max_examples=200, deadline=5000)
    @given(
        cmd=st.text(min_size=0, max_size=200),
        arg=st.text(min_size=0, max_size=5000)
    )
    def test_agent(cmd, arg):
        payload = {"cmd": cmd, "arg": arg}
        try:
            resp = requests.post("http://localhost:8000/agent", json=payload, timeout=3)
            # Анализ ответа
            if resp.status_code == 200 and resp.elapsed.total_seconds() > 2:
                logging.warning(f"Slow response: {cmd=} {arg=}")
            if resp.status_code == 500:
                logging.error(f"Server error: {cmd=} {arg=}")
            if not resp.headers.get('Content-Type'):
                logging.warning(f"No content-type: {cmd=} {arg=}")
        except requests.exceptions.ConnectionError:
            logging.critical(f"Agent crashed! {cmd=} {arg=}")
    

Ожидаемый результат этапа Рабочий скрипт fuzzer.py, который можно запустить и наблюдать логи.

Этап 4: Запуск фаззера и анализ результатов (30-60 минут)

Действия

  1. Запустите фаззер pytest fuzzer.py -v --hypothesis-show-statistics
  2. Наблюдайте за логами в реальном времени смотрите fuzz.log.
  3. При обнаружении краша агента
    • Восстановите агента (перезапустите сервер).
    • Запишите конкретный входной запрос, вызвавший краш.
    • Классифицируйте проблему (null pointer, переполнение буфера, бесконечная рекурсия).
  4. Соберите статистику
    • Количество выполненных тестов.
    • Количество ошибок (категории: CRASH, TIMEOUT, UNEXPECTED_200, etc.).
    • Минимальное воспроизводящее значение (shrunk example).
  5. Повторите с увеличенным числом примеров (--max-examples=1000) для лучшего покрытия.

Ожидаемый результат этапа Список найденных уязвимостей (минимум 3) с воспроизводимыми примерами.

Этап 5: Оформление отчёта (30 минут)

Действия

  1. Создайте файл fuzzing_report.md со следующей структурой:
    • Цель и методы (кратко).
    • Конфигурация фаззера (гипотезы, количество примеров).
    • Результаты по категориям
      • Краши агента (с примерами запросов).
      • Неожиданные таймауты.
      • Ответы с некорректным HTTP-статусом.
      • Другие аномалии.
    • Критичность каждой уязвимости (P0-P3).
    • Воспроизводимые примеры (JSON-запросы).
    • Рекомендации по исправлению.
  2. Приложите лог-файл (fuzz.log) как доказательство.

Ожидаемый результат этапа Готовый отчёт с тремя и более задокументированными уязвимостями или заключением об отсутствии таковых при заданном покрытии.

5. Критерии приемки (Definition of Done)

  • Фаззер написан на Python с использованием Hypothesis.
  • Фаззер способен генерировать граничные, пустые, длинные запросы (не менее 5 различных стратегий).
  • Фаззер корректно обрабатывает сетевые ошибки и таймауты.
  • За 200-500 примеров найдено хотя бы 3 нештатных поведения (краш, зависание, неожиданный 200).
  • Каждый найденный дефект воспроизводится отдельно (простой тестовый скрипт).
  • Написан отчёт в формате markdown с примерами запросов и рекомендациями.
  • Результаты воспроизводимы на staging-окружении (или локальном).
  • Код фаззера покрыт юнит-тестами (хотя бы базовая проверка стратегий).

6. Ожидаемый результат

  • Основной артефакт файл fuzzer.py — скрипт на Python с реализацией фаззинг-тестов.
  • Второстепенные
    • fuzzing_report.md — отчёт с описанием найденных уязвимостей.
    • fuzz.log — лог выполнения.
    • (Опционально) Дополнение к CI-пайплайну для автоматического запуска фаззера при каждом релизе агента.

7. Возможные сложности и их решение

СложностьРешение
Агент долго отвечает на большие запросыУстановить таймаут 3-5 секунд; логировать таймауты как отдельную категорию.
Rate limiting (агент возвращает 429)Настроить hypothesis max_examples и добавить небольшую задержку между запросами (time.sleep(0.1)).
Ложные срабатывания (ожидаемые ошибки)Фильтровать ожидаемые коды (400, 404) и логировать только неожиданные (200 вместо 400, краш).
Сложность воспроизведения (хитрая комбинация)Использовать встроенный в Hypothesis механизм shrink: после нахождения ошибки он автоматически уменьшит входные данные до минимального набора.
Остановка фаззера из-за критической ошибкиЗапускать фаззер в режиме --hypothesis-verbosity=debug и обернуть каждый тестовый случай в try-except, с восстановлением агента (скрипт рестарта).

8. Бюджет времени (оценка)

ЭтапВремя
1. Подготовка окружения и тестируемого агента30-45 мин
2. Проектирование стратегий fuzzing45-60 мин
3. Реализация фаззера1-1.5 часа
4. Запуск фаззера и анализ результатов30-60 мин
5. Оформление отчёта30 мин
Итого~3.5 - 5 часов

Примечание При первом выполнении задачи возможен запас в 30-60 минут на изучение Hypothesis и отладку.

9. Связанные вопросы из базы знаний

ВопросТема
12Что такое fuzzing и зачем он нужен в тестировании агентов?
45Как генерировать краевые случаи с помощью Hypothesis?
78Разница между фаззингом на основе мутаций и генерации.
112Как обрабатывать таймауты в асинхронных тестах?
156Интеграция фаззинга в CI/CD пайплайн.
203Примеры уязвимостей LLM-агентов (injection, buffer overflow).
287Метрики покрытия для фаззинг-тестов.
315Как симулировать атаки на агента (prompt injection) через фаззинг?
402Использование Atheris для фаззинга Python-библиотек.
567Анализ логов фаззинга и классификация ошибок.

10. Чек-лист самопроверки

  • Я установил Hypothesis, pytest, requests и Faker.
  • У меня есть рабочий тестовый агент (собственный или симулированный с известными уязвимостями).
  • Я определил минимум 5 различных стратегий генерации запросов (пустые, длинные, невалидные JSON, спецсимволы, несуществующие команды).
  • Я написал и запустил фаззер, который за 500 примеров нашёл хотя бы 3 аномалии.
  • Я воспроизвёл каждую найденную проблему отдельным простым тестом (curl или Python-запрос).
  • Я написал отчёт в формате markdown и приложил лог.
  • Я убедился, что фаззер не генерирует слишком много ложных срабатываний (отфильтровал ожидаемые коды).
  • Я проверил, что фаззер корректно логирует таймауты и краши сервера.
  • Я сохранил все файлы (fuzzer.py, report, log) в репозиторий проекта.