Playwright Target:#

This notebook demonstrates how to interact with the Playwright Target in PyRIT.

The PlaywrightTarget class allows you to interact with web applications using Playwright. This is useful for testing interactions with web applications, such as chatbots or other web interfaces, especially for red teaming purposes.

Example Setup#

Before you begin, ensure you have the correct version of PyRIT installed and any necessary secrets configured as described here.

To run the Flask app, you also must download and run Ollama, making sure the flask is using a correct model.

Additionally, you need to install playwright by executing playwright install.

Example: Interacting with a Web Application using PlaywrightTarget#

In this example, we’ll interact with a simple web application running locally at http://127.0.0.1:5000. which runs a chatbot that responds to user prompts via ollama We’ll define an interaction function that navigates to the web application, inputs a prompt, and retrieves the bot’s response.

Start the Flask App#

Before we can interact with the web application, we need to start the Flask app that serves the chatbot, this will be done in a subprocess

import subprocess
import os
import sys
import time
from urllib.request import urlopen
from urllib.error import URLError


def start_flask_app():
    # Get the notebook's directory
    notebook_dir = os.getcwd()

    # Construct the path to app.py relative to the notebook directory
    app_path = os.path.join(notebook_dir, 'playwright_demo', 'app.py')

    # Ensure that app.py exists
    if not os.path.isfile(app_path):
        raise FileNotFoundError(f"Cannot find app.py at {app_path}")

        # Start the Flask app
    print(f"Starting Flask app from {app_path}...")
    flask_process = subprocess.Popen(
        [sys.executable, app_path],
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL
    )


    # Wait for the app to start by checking if the server is up
    server_url = 'http://127.0.0.1:5000'
    server_ready = False
    while not server_ready:
        try:
            response = urlopen(server_url)
            if response.status == 200:
                server_ready = True
        except URLError:
            time.sleep(0.5)  # Wait a bit before checking again
    print("Flask app is running and ready!")
    return flask_process



# Start the Flask app
flask_process = start_flask_app()
Starting Flask app from d:\git\PyRIT-internal\PyRIT\doc\code\targets\playwright_demo\app.py...
Flask app is running and ready!

The flask app should now be running locally:

image-2.png

Interaction Function#

This is playwright script that interacts with the chatbot web application.

from pyrit.common import default_values
from playwright.async_api import Page, async_playwright
from pyrit.prompt_target import PlaywrightTarget
from pyrit.orchestrator import PromptSendingOrchestrator
from pyrit.models import PromptRequestPiece

# Load environment variables (optional)
default_values.load_environment_files()


# Define the interaction function
async def interact_with_my_app(page: Page, request_piece: PromptRequestPiece) -> str:
    # Define selectors
    input_selector = '#message-input'
    send_button_selector = '#send-button'
    bot_message_selector = '.bot-message'

    # Count existing messages
    initial_messages = await page.query_selector_all(bot_message_selector)
    initial_message_count = len(initial_messages)

    # Wait for the page to be ready
    await page.wait_for_selector(input_selector)

    # Send the prompt text
    prompt_text = request_piece.converted_value
    await page.fill(input_selector, prompt_text)
    await page.click(send_button_selector)

    # Wait for new messages (bot messages)
    await page.wait_for_function(
        f"document.querySelectorAll('{bot_message_selector}').length > {initial_message_count}"
    )

    # Extract the bot's response text
    bot_message_element = await page.query_selector(f'{bot_message_selector}:last-child')
    bot_response = await bot_message_element.text_content()
    return bot_response.strip()

Using PlaywrightTarget with the Interaction Function and Scorer#

Now, we can use the PlaywrightTarget by passing the interaction function we defined. We’ll use the PromptSendingOrchestrator to send prompts to the target and collects responses.

import asyncio
import sys

if sys.platform == "win32":
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

# Using PlaywrightTarget with the interaction function and scorer
async def main(page: Page):
    target = PlaywrightTarget(
        interaction_func=interact_with_my_app,
        page=page
    )

    with PromptSendingOrchestrator(
            objective_target=target,
    ) as orchestrator:
        all_prompts = [
            "Tell me a joke about computer programming.",
        ]

        await orchestrator.send_prompts_async(prompt_list=all_prompts)
        await orchestrator.print_conversations_async()  # type: ignore


async def run():
    async with async_playwright() as playwright:
        browser = await playwright.chromium.launch(headless=False)
        context = await browser.new_context()
        page: Page = await context.new_page()
        await page.goto('http://127.0.0.1:5000')
        await main(page)
        await context.close()
        await browser.close()

# Note in Windows this doesn't run in jupyter notebooks due to playwright limitations
# https://github.com/microsoft/playwright-python/issues/480
# await run()

if __name__ == "__main__":
    asyncio.run(run())

Terminate the Flask App#

# Terminate the Flask app when done
flask_process.terminate()
flask_process.wait()  # Ensure the process has terminated
print("Flask app has been terminated.")
Flask app has been terminated.