Когда ИИ тестирует лучше тебя: ищу баги в OWASP Juice Shop с помощью Cypress и Workik

от автора

Я — Solution Architect с 19 годами в IT, часто помогаю новичкам в тестировании. Джуны обычно жалуются на вагон теории, отсутствие практики и примеров, где основные алогритмы собраны вместе. 

Я решил исправить эту проблему: взял OWASP Juice Shop — уязвимое веб-приложение, развернул его через Docker на http://135.181.239.135:4000 и протестировал с помощью ИИ.

В статье расскажу про:

  • ИИ для запуска тест-кейсов: автоматизация без глубоких знаний программирования.

  • Обучение ИИ-генерации тест-кейсов: как ИИ помогает придумывать сценарии.

  • Написание баг-репортов с ИИ: создание чётких и технически точных отчётов.

  • Эксплоративное тестирование с ИИ: поиск уязвимостей с подсказками ИИ.

  • ИИ как учитель: фидбэк и обучение тестированию.

  • Подводные камни ИИ для джунов: чего избегать, чтобы не плодить ошибки.

ИИ для запуска тест-кейсов

Притворюсь джуном-тестировщиком, который тестирует OWASP Juice Shop, развёрнутый в Docker. Задача — проверить форму логина, но навыков написания скриптов на Selenium или Playwright нет. Знаю, как проверять UI, кликать кнопки и заполнять формы, но кодировать это — проблема.

Для решения использую ИИ-инструмент Workik, который генерирует тесты на Cypress по текстовому описанию функциональности. Это упрощает автоматизацию: не нужно искать CSS-селекторы или разбираться с DOM вручную.

Шаг 1: Описываем задачу в Workik

Открываю https://workik.com/cypress-test-case-generator и в поле ввода описываю задачу:

Тестирование формы логина в OWASP Juice Shop на http://135.181.239.135:4000/#/login.
Проверить
:

  1. Успешный вход с email test@test.com и паролем password123.

  2. Ошибка при неверном пароле wrongpass.

  3. Ошибка при пустом email.

Жму «Generate», и Workik выдаёт код на Cypress. Вот пример (с техническими деталями):

// cypress/integration/login.spec.js  describe('Тестирование формы логина в OWASP Juice Shop', () => {      const baseUrl = 'http://135.181.239.135:4000/#/login';      beforeEach(() => {          cy.visit(baseUrl);          cy.intercept('POST', '/rest/user/login').as('loginRequest');      });      it('Успешный вход с валидными данными', () => {          cy.get('input[type="email"]').type('test@test.com');          cy.get('input[type="password"]').type('password123');          cy.get('button[type="submit"]').click();          cy.wait('@loginRequest').its('response').should('have.property', 'statusCode', 200);          cy.url().should('eq', 'http://135.181.239.135:4000/#/');          cy.get('.welcome-message').should('have.text', 'Welcome back!');      });      it('Ошибка при неверном пароле', () => {          cy.get('input[type="email"]').type('test@test.com');          cy.get('input[type="password"]').type('wrongpass');          cy.get('button[type="submit"]').click();          cy.wait('@loginRequest').its('response').should('have.property', 'statusCode', 401);          cy.get('.error-message').should('have.text', 'Invalid email or password');      });      it('Ошибка при пустом email', () => {          cy.get('input[type="password"]').type('password123');          cy.get('button[type="submit"]').click();          cy.get('.error-message').should('have.text', 'Email is required');          cy.get('input[type="email"]').should('have.class', 'ng-invalid');      });  });

Технические детали:

  • Используется cy.intercept для перехвата POST-запроса на /rest/user/login, что позволяет проверять HTTP-ответы.

  • Для успешного входа ожидается статус 200 и редирект на главную страницу.

  • Для неверного пароля сервер возвращает 401 Unauthorized, а фронт отображает сообщение об ошибке.

  • При пустом email валидация формы на фронте добавляет класс ng-invalid к полю, а сервер не вызывается.

Шаг 2: Настраиваем окружение и запускаем тесты

Устанавливаю Cypress в проекте:

mkdir juice-shop-tests  cd juice-shop-tests  npm init -y  npm install cypress --save-dev  npx cypress open
Так у меня выглядит эта папка.

Так у меня выглядит эта папка.
А так выглядит запущенный cypress:

А так выглядит запущенный cypress:
Cypress открывает окно, где я выбираю E2E Testing

Cypress открывает окно, где я выбираю E2E Testing
Дальше соглашаюсь со стандартной конфигурацией и открываю Chrome:

Дальше соглашаюсь со стандартной конфигурацией и открываю Chrome:
Окно выбора создания файла конфигурации

Окно выбора создания файла конфигурации

Создаю новый файл конфигурации login.spec.cy.js, вставляю код от Workik и запускаю тесты. Cypress открывает браузер, выполняет действия и проверяет результаты. Если тест падает, в логах видно:

  • URL запроса (/rest/user/login).

  • HTTP-статус (например, 401 для неверного пароля).

  • Тело ответа (например, {«error»: «Invalid email or password»}).

Пример ошибки: Тест на неверный пароль падает, если сервер возвращает не Invalid email or password, а Wrong credentials. Лог Cypress показывает:

GET /rest/user/login 401

Response: {«error»: «Wrong credentials»}

Исправляю ожидание:

cy.get(‘.error-message’).should(‘have.text’, ‘Wrong credentials’);

Шаг 3: Учимся на результатах

Добавляю тест на SQL-инъекцию через Workik, указав: «Проверить SQL-инъекцию с email ‘ OR ‘1’=’1 используя cypress. Получаю:

describe('SQL Injection Test on Login', () => {     it('should attempt SQL injection with email', () => {         // Переход к странице логина         cy.visit('http://135.181.239.135:4000/#/login');          // Ввод вредоносного email         cy.get('input[name="email"]').type("' OR '1'='1");          // Ввод случайного пароля         cy.get('input[name="password"]').type('any_password');          // Нажимаем на кнопку логина         cy.get('button').contains('Login').click();          // Ожидаем и проверяем наличие сообщения об успешном входе         cy.url().should('not.include', '/login'); // Проверка, что мы не на странице логина          // Проверка, что сообщение об успешном логине или изменение в интерфейсе происходит         cy.contains('Logged in').should('exist');     }); });

Технический анализ:

  • Сервер корректно экранирует ввод, возвращая 401 вместо 200, что исключает возможность обхода авторизации.

  • Отсутствие SQL-ошибок в ответе (SQL syntax error) подтверждает защиту от инъекций.

  • Лог Cypress показывает, что запрос ушёл с экранированным телом: email=%27%20OR%20%271%27%3D%271.

Шаг 4: Интеграция в CI/CD

Добавляю тесты в GitHub Actions для автоматического прогона:

name: Cypress Tests  on:   push:     branches:       - main  # Укажите вашу основную ветку  jobs:   cypress-run:     runs-on: ubuntu-latest      steps:       # 1. Шаг: Проверка исходного кода       - name: Checkout code         uses: actions/checkout@v3        # 2. Шаг: Настройка Node.js       - name: Set up Node.js         uses: actions/setup-node@v3         with:           node-version: '16'        # 3. Шаг: Установка зависимостей       - name: Install dependencies         run: npm install        # 4. Шаг: Запуск тестов Cypress в headless-режиме       - name: Run Cypress tests         run: npx cypress run --browser chrome --headless         # Настройка сохранения артефактов         continue-on-error: true # Позволяет процессу завершаться, даже если тесты не проходят        # 5. Шаг: Сохранение артефактов (логи, скриншоты, видео)       - name: Upload Cypress artifacts         uses: actions/upload-artifact@v3         with:           name: cypress-artifacts           path: |             cypress/videos             cypress/screenshots             cypress/results

Технические детали:

  • Cypress запускается в headless-режиме с Chrome.

  • Логи и артефакты (скриншоты, видео) сохраняются в GitHub Actions для анализа.

Плюсы:

  • Workik генерирует код, совместимый с CI/CD.

  • Логи HTTP-запросов помогают дебагать серверные ошибки.

Подводные камни:

  • Селекторы (например, .error-message) могут ломаться при редизайне фронта.

  • Бесплатная версия Workik ограничивает количество генераций.

Учимся через ИИ-генерацию тест-кейсов

Форма логина кажется простой, но тестировщик должен проверять краевые случаи: пустые поля, SQL-инъекции, длинные строки. Workik помогает генерировать такие сценарии.

Шаг 1: Описываем задачу

В Workik пишу:

Workik выдаёт код:

describe('Login Form Tests', () => {     beforeEach(() => {         cy.visit('http://135.181.239.135:4000/#/login');  // Переход к странице логина     });      it('should successfully log in with valid credentials', () => {         cy.get('input[name="email"]').type('test@test.com');  // Ввод email         cy.get('input[name="password"]').type('password123');  // Ввод пароля         cy.get('button').contains('Login').click();  // Нажимаем вход          cy.url().should('not.include', '/login');  // Проверяем, что не на странице логина         cy.contains('Logged in').should('exist');  // Проверка на наличие сообщения о входе     });      it('should display error for incorrect password', () => {         cy.get('input[name="email"]').type('test@test.com');  // Ввод email         cy.get('input[name="password"]').type('wrongpass');  // Неверный пароль         cy.get('button').contains('Login').click();  // Нажимаем вход          cy.contains('Invalid email or password').should('exist');  // Проверка на наличие ошибки     });      it('should display error for empty email', () => {         cy.get('input[name="email"]').type('');  // Пустой email         cy.get('input[name="password"]').type('password123');  // Ввод пароля         cy.get('button').contains('Login').click();  // Нажимаем вход          cy.contains('Invalid email or password').should('exist');  // Проверка на наличие ошибки     });      it('should handle SQL injection attempt', () => {         cy.get('input[name="email"]').type("' OR '1'='1");  // SQL-инъекция         cy.get('input[name="password"]').type('any_password');  // Неверный пароль         cy.get('button').contains('Login').click();  // Нажимаем вход          cy.contains('Invalid email or password').should('exist');  // Проверка на наличие ошибки     });      it('should display error for long email', () => {         const longEmail = 'a'.repeat(101) + '@test.com';  // Длинный email (>100 символов)         cy.get('input[name="email"]').type(longEmail);  // Ввод длинного email         cy.get('input[name="password"]').type('password123');  // Ввод пароля         cy.get('button').contains('Login').click();  // Нажимаем вход          cy.contains('Invalid email or password').should('exist');  // Проверка на наличие ошибки     }); });

Технический анализ:

  • Тест на длинный email падает: сервер возвращает 500 вместо ожидаемого 400 Bad Request.

  • Лог Cypress: POST /rest/user/login 500, тело ответа: {«error»: «Uncaught exception in user validation»}.

  • Это указывает на отсутствие серверной валидации длины строки, что приводит к неперехваченному исключению.

Шаг 2: Запускаем и анализируем

Запускаю тесты (npx cypress open). Тест с длинным email падает. Лог:

POST /rest/user/login 500

Response: {«error»: «Uncaught exception in user validation»}

Исправляю ожидание:

cy.get(‘#errorMessage’).should(‘contain’, ‘Internal Server Error’);

Технический вывод:

  • Сервер не валидирует длину email, что вызывает исключение в бэкенде (вероятно, в Node.js/Express).

  • Рекомендация: добавить валидацию на уровне middleware (например, express-validator).

Шаг 3: Учимся думать как тестировщик

Workik подсказывает неочевидные случаи, вроде SQL-инъекций. Анализ логов учит:

  • Проверять HTTP-статусы (200, 401, 500).

  • Искать несоответствия между фронтом и бэкендом.

Упрощаем баг-репорты с ИИ

Найти баг — полдела. Нужно описать его так, чтобы разработчики поняли проблему. Пример: тест на длинный email выявил ошибку 500.

Шаг 1: Ловим баг

Тест:

it('Длинный email', () => {     const longEmail = 'a'.repeat(100) + '@test.com';     cy.get('#email').type(longEmail);     cy.get('#password').type('password123');     cy.get('#loginButton').click();     cy.wait('@loginRequest').its('response.statusCode').should('eq', 500); }); 

Лог:

  • POST /rest/user/login 500

  • Тело ответа: {«error»: «Uncaught exception in user validation»}

  • Скриншот: сообщение «Internal Server Error».

Шаг 2: Собираем баг-репорт

Баг-репорт:

Название: Ошибка 500 при отправке длинного email в форме логина
Шаги воспроизведения:

  1. Открыть http://135.181.239.135:4000/#/login.

  2. В поле email ввести строку длиной >100 символов (например, a…a@test.com).

  3. В поле пароля ввести password123.

  4. Нажать «Log in».

Ожидаемое поведение:

  • Сервер возвращает 400 Bad Request с сообщением Email too long.

  • Фронт отображает ошибку валидации.

Фактическое поведение:

  • Сервер возвращает 500 Internal Server Error.

  • Тело ответа: {«error»: «Uncaught exception in user validation»}.

  • Фронт показывает «Internal Server Error».

Логи:

  • POST /rest/user/login 500

  • Запрос: {«email»: «a…a@test.com«, «password»: «password123»}

  • Ответ: {«error»: «Uncaught exception in user validation»}

Скриншоты: [Скриншот из cypress/screenshots]
Окружение:

Технический анализ:

  • Ошибка указывает на отсутствие валидации длины email в бэкенде.

  • Возможная причина: необработанное исключение в функции валидации (например, TypeError при работе со строкой).

  • Рекомендация: добавить проверку длины email в middleware или модели пользователя.

Шаг 3: Упрощаем с ИИ

Инструменты вроде BugBug анализируют логи и предлагают текст репорта. Пример:

Test failed: Expected 400, got 500

Suggested report:

— Steps: Send POST /rest/user/login with email >100 chars

— Expected: 400 Bad Request

— Actual: 500 Internal Server Error

— Logs: {«error»: «Uncaught exception»}

Эксплоративное тестирование с ИИ

Эксплоративное тестирование — это исследование приложения без строгих тест-кейсов. В Juice Shop полно уязвимостей (XSS, SQL-инъекции), и ИИ помогает их найти.

Шаг 1: Получаем подсказки

В Workik пишу:

Исследовать OWASP Juice Shop на http://135.181.239.135:4000. Найти уязвимости в форме логина, корзине, поиске. Подсказать сценарии.

Workik предлагает:

Форма логина:

  • SQL-инъекция: Ввести ‘ OR ‘1’=’1.

  • XSS: Ввести <script>alert(‘hack’)</script>.

  • Переполнение буфера: Длинный email (>1000 символов).

Корзина:

  • Манипуляция количеством: Изменить поле quantity через DevTools.

  • CSRF: Проверить отсутствие токена в запросах.

Поиск:

  • XSS: Ввести <script>alert(‘boom’)</script>.

  • SQL-инъекция: Ввести %’ OR ‘1’=’1.

Шаг 2: Исследуем

Проверяю XSS в форме логина:

Шаги:

  1. В поле email ввожу <script>alert(‘hack’)</script>.

  2. Жму «Log in».

Результат:

  • Появляется alert с текстом hack.

  • Лог: скрипт выполняется без экранирования.

Баг-репорт:

Название: XSS-уязвимость в форме логина
Шаги:

  1. Открыть http://135.181.239.135:4000/#/login.

  2. В поле email ввести <script>alert(‘hack’)</script>.

  3. Нажать «Log in».

Ожидаемое поведение:

  • Ввод экранируется, alert не выполняется.

  • Сервер возвращает 400 или 401.

Фактическое поведение:

  • Выполняется JavaScript (alert).

  • Ответ сервера: 200 OK (ввод не валидируется).

Логи:

  • POST /rest/user/login 200

  • Тело запроса: {«email»: «<script>alert(‘hack’)</script>», «password»: «password123»}

Технический анализ:

  • Отсутствует экранирование ввода на сервере (например, нет sanitize-html).

  • Рекомендация: добавить Content Security Policy (CSP) и экранирование.

ИИ как учитель

ИИ учит через фидбэк. Например, тест на длинный email падает. Лог:

POST /rest/user/login 500

{«error»: «Uncaught exception»}

Workik подсказывает: «Проверь серверные логи». Я нахожу в логах Juice Shop:

TypeError: Cannot read property ‘length’ of undefined

    at validateEmail (/app/server.js:123)

Урок:

  • Проблема в функции validateEmail, которая не проверяет тип входных данных.

  • Учусь анализировать стек вызовов.

Подводные камни

  • Слепая вера: Workik может упустить XSS в поиске, если не указать конкретно.

  • Ограничения бесплатной версии: Нет анализа серверных логов.

  • Контекст: ИИ не знает о скрытых API-уязвимостях Juice Shop.


А если вам нужен ИИ-помощник для сотрудника — познакомьтесь ближе с продуктами Minervasoft. Ассистент с генеративным ИИ Minerva Copilot встраивается в любую систему компании и подсказывает ответы из корпоративной базы знаний в зависимости от контекста. Быстро, качественно и со ссылками на статьи.

Кроме того, благодаря технологии DataHub система управления знаниями Minerva Knowledge становится корпоративным «мозгом» для GenAI. Платформа объединяет любые источники информации в компании, в том числе внутренние системы, базы знаний и другие хранилища данных.

Узнать подробнее


ссылка на оригинал статьи https://habr.com/ru/articles/916222/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *