Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 122 additions & 12 deletions doc/code/targets/10_3_websocket_copilot_target.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
{
"cell_type": "markdown",
"id": "0",
"metadata": {},
"metadata": {
"lines_to_next_cell": 0
},
"source": [
"# WebSocket Copilot Target\n",
"\n",
Expand All @@ -13,13 +15,15 @@
"- `COPILOT_USERNAME` and `COPILOT_PASSWORD` environment variables\n",
"- Playwright installed: `pip install playwright && playwright install chromium`\n",
"\n",
"Some environments are not suited for automated authentication (e.g. they have security policies with retrieving tokens or have MFA). See the [Alternative Authentication](#alternative-authentication-with-manualcopilotauthenticator) section below."
"Some environments are not suited for automated authentication (e.g. they have security policies with retrieving tokens or have MFA). For interactive authentication compatible with MFA and Conditional Access, see [Browser Session Authentication](#browser-session-authentication). To provide a token manually, see [Alternative Authentication](#alternative-authentication-with-manualcopilotauthenticator)."
]
},
{
"cell_type": "markdown",
"id": "1",
"metadata": {},
"metadata": {
"lines_to_next_cell": 0
},
"source": [
"## Basic Usage with `PromptSendingAttack`\n",
"\n",
Expand All @@ -30,7 +34,9 @@
"cell_type": "code",
"execution_count": null,
"id": "2",
"metadata": {},
"metadata": {
"lines_to_next_cell": 0
},
"outputs": [
{
"name": "stdout",
Expand Down Expand Up @@ -92,7 +98,9 @@
"cell_type": "code",
"execution_count": null,
"id": "4",
"metadata": {},
"metadata": {
"lines_to_next_cell": 0
},
"outputs": [
{
"name": "stdout",
Expand Down Expand Up @@ -166,7 +174,6 @@
"source": [
"from pyrit.executor.attack import MultiPromptSendingAttack\n",
"from pyrit.models import Message\n",
"from pyrit.output import output_attack_async\n",
"from pyrit.prompt_target import WebSocketCopilotTarget\n",
"from pyrit.setup import IN_MEMORY, initialize_pyrit_async\n",
"\n",
Expand Down Expand Up @@ -195,6 +202,105 @@
{
"cell_type": "markdown",
"id": "5",
"metadata": {
"lines_to_next_cell": 0
},
"source": [
"## Browser Session Authentication\n",
"\n",
"`BrowserSessionCopilotAuthenticator` captures a token from a persistent Edge session.\n",
"Complete account selection when prompted. The browser remains minimized for token\n",
"renewal and closes when the context exits.\n",
"\n",
"Captured tokens remain in memory. Supply a different `profile_path` for each persona.\n",
"\n",
"Install the optional dependency before using this authenticator:\n",
"\n",
"```bash\n",
"pip install \"pyrit[playwright]\"\n",
"```\n",
"\n",
"The authenticator uses a locally installed Microsoft Edge browser."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6",
"metadata": {
"lines_to_next_cell": 0
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\u001b[33m════════════════════════════════════════════════════════════════════════════════════════════════════\u001b[0m\n",
"\u001b[1m\u001b[33m ❓ ATTACK RESULT: UNDETERMINED ❓ \u001b[0m\n",
"\u001b[33m════════════════════════════════════════════════════════════════════════════════════════════════════\u001b[0m\n",
"\n",
"\u001b[1m\u001b[44m\u001b[37m Attack Summary \u001b[0m\n",
"\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
"\u001b[1m 📋 Basic Information\u001b[0m\n",
"\u001b[36m • Objective: What is your favorite color?\u001b[0m\n",
"\u001b[36m • Attack Type: PromptSendingAttack\u001b[0m\n",
"\u001b[36m • Conversation ID: 4adc3983-5751-41d8-bc26-6cd76ea48b60\u001b[0m\n",
"\n",
"\u001b[1m ⚡ Execution Metrics\u001b[0m\n",
"\u001b[32m • Turns Executed: 1\u001b[0m\n",
"\u001b[32m • Execution Time: 17.48s\u001b[0m\n",
"\n",
"\u001b[1m 🎯 Outcome\u001b[0m\n",
"\u001b[33m • Status: ❓ UNDETERMINED\u001b[0m\n",
"\u001b[37m • Reason: No objective scorer configured\u001b[0m\n",
"\n",
"\u001b[1m\u001b[44m\u001b[37m Conversation History with Objective Target \u001b[0m\n",
"\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
"\n",
"\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
"\u001b[1m\u001b[34m🔹 Turn 1 - USER\u001b[0m\n",
"\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
"\u001b[34m What is your favorite color?\u001b[0m\n",
"\n",
"\u001b[33m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
"\u001b[1m\u001b[33m🔸 ASSISTANT\u001b[0m\n",
"\u001b[33m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
"\u001b[33m I don't have personal preferences, so I don't actually have a favorite color.\u001b[0m\n",
"\u001b[33m \u001b[0m\n",
"\u001b[33m If I had to pick one for fun, I'd choose **blue** because it's commonly associated with\u001b[0m\n",
"\u001b[33m technology, reliability, and calmness. 💙\u001b[0m\n",
"\u001b[33m \u001b[0m\n",
"\u001b[33m What's your favorite color?\u001b[0m\n",
"\n",
"\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
"\n",
"\u001b[2m\u001b[37m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n",
"\u001b[2m\u001b[37m Report generated at: 2026-08-27 16:34:36 UTC \u001b[0m\n"
]
}
],
"source": [
"from pyrit.auth import BrowserSessionCopilotAuthenticator\n",
"from pyrit.executor.attack import PromptSendingAttack\n",
"from pyrit.output import output_attack_async\n",
"from pyrit.prompt_target import WebSocketCopilotTarget\n",
"from pyrit.setup import IN_MEMORY, initialize_pyrit_async\n",
"\n",
"await initialize_pyrit_async(memory_db_type=IN_MEMORY, silent=True)\n",
"\n",
"objective = \"What is your favorite color?\"\n",
"\n",
"async with BrowserSessionCopilotAuthenticator() as auth:\n",
" target = WebSocketCopilotTarget(authenticator=auth)\n",
" attack = PromptSendingAttack(objective_target=target)\n",
" result = await attack.execute_async(objective=objective)\n",
" await output_attack_async(result)"
]
},
{
"cell_type": "markdown",
"id": "7",
"metadata": {},
"source": [
"## Alternative Authentication with `ManualCopilotAuthenticator`\n",
Expand All @@ -208,7 +314,7 @@
"3. Go to the Network tab.\n",
"4. Filter by \"Socket\" connections or search for \"Chathub\".\n",
"5. Start typing in the chat to initiate a WebSocket connection.\n",
"6. Look for the latest WebSocket connection to `substrate.office.com/m365Copilot/Chathub`.\n",
"6. Look for the latest WebSocket connection to `substrate.svc.cloud.microsoft/m365Copilot/Chathub`.\n",
"7. You may find the `access_token` in the request URL or in the request payload.\n",
"\n",
"You can either pass the token directly or set the `COPILOT_ACCESS_TOKEN` environment variable."
Expand All @@ -217,8 +323,10 @@
{
"cell_type": "code",
"execution_count": null,
"id": "6",
"metadata": {},
"id": "8",
"metadata": {
"lines_to_next_cell": 0
},
"outputs": [
{
"name": "stdout",
Expand Down Expand Up @@ -273,7 +381,7 @@
},
{
"cell_type": "markdown",
"id": "7",
"id": "9",
"metadata": {},
"source": [
"## Multimodal Support (Text and Images)\n",
Expand All @@ -286,7 +394,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "8",
"id": "10",
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -360,7 +468,6 @@
"\n",
"from pyrit.executor.attack import PromptSendingAttack\n",
"from pyrit.models import Message, MessagePiece\n",
"from pyrit.output import output_attack_async\n",
"from pyrit.prompt_target import WebSocketCopilotTarget\n",
"from pyrit.setup import IN_MEMORY, initialize_pyrit_async\n",
"\n",
Expand Down Expand Up @@ -400,6 +507,9 @@
}
],
"metadata": {
"jupytext": {
"main_language": "python"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
Expand Down
37 changes: 35 additions & 2 deletions doc/code/targets/10_3_websocket_copilot_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# - `COPILOT_USERNAME` and `COPILOT_PASSWORD` environment variables
# - Playwright installed: `pip install playwright && playwright install chromium`
#
# Some environments are not suited for automated authentication (e.g. they have security policies with retrieving tokens or have MFA). See the [Alternative Authentication](#alternative-authentication-with-manualcopilotauthenticator) section below.
# Some environments are not suited for automated authentication (e.g. they have security policies with retrieving tokens or have MFA). For interactive authentication compatible with MFA and Conditional Access, see [Browser Session Authentication](#browser-session-authentication). To provide a token manually, see [Alternative Authentication](#alternative-authentication-with-manualcopilotauthenticator).

# %% [markdown]
# ## Basic Usage with `PromptSendingAttack`
Expand Down Expand Up @@ -77,6 +77,39 @@

await output_attack_async(result)

# %% [markdown]
# ## Browser Session Authentication
#
# `BrowserSessionCopilotAuthenticator` captures a token from a persistent Edge session.
# Complete account selection when prompted. The browser remains minimized for token
# renewal and closes when the context exits.

# Captured tokens remain in memory. Supply a different `profile_path` for each persona.
#
# Install the optional dependency before using this authenticator:
#
# ```bash
# pip install "pyrit[playwright]"
# ```
#
# The authenticator uses a locally installed Microsoft Edge browser.
# %%
from pyrit.auth import BrowserSessionCopilotAuthenticator
from pyrit.executor.attack import PromptSendingAttack
from pyrit.output import output_attack_async
from pyrit.prompt_target import WebSocketCopilotTarget
from pyrit.setup import IN_MEMORY, initialize_pyrit_async

await initialize_pyrit_async(memory_db_type=IN_MEMORY, silent=True)

objective = "What is your favorite color?"

async with BrowserSessionCopilotAuthenticator() as auth:
target = WebSocketCopilotTarget(authenticator=auth)
attack = PromptSendingAttack(objective_target=target)
result = await attack.execute_async(objective=objective)
await output_attack_async(result)

# %% [markdown]
# ## Alternative Authentication with `ManualCopilotAuthenticator`
#
Expand All @@ -89,7 +122,7 @@
# 3. Go to the Network tab.
# 4. Filter by "Socket" connections or search for "Chathub".
# 5. Start typing in the chat to initiate a WebSocket connection.
# 6. Look for the latest WebSocket connection to `substrate.office.com/m365Copilot/Chathub`.
# 6. Look for the latest WebSocket connection to `substrate.svc.cloud.microsoft/m365Copilot/Chathub`.
# 7. You may find the `access_token` in the request URL or in the request payload.
#
# You can either pass the token directly or set the `COPILOT_ACCESS_TOKEN` environment variable.
Expand Down
4 changes: 4 additions & 0 deletions pyrit/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
is_azure_openai_endpoint,
)
from pyrit.auth.azure_storage_auth import AzureStorageAuth
from pyrit.auth.browser_session_copilot_authenticator import (
BrowserSessionCopilotAuthenticator,
)
from pyrit.auth.copilot_authenticator import CopilotAuthenticator
from pyrit.auth.manual_copilot_authenticator import ManualCopilotAuthenticator
from pyrit.auth.openai_auth import resolve_openai_auth
Expand All @@ -34,6 +37,7 @@
"Authenticator": "pyrit.auth.authenticator",
"AzureAuth": "pyrit.auth.azure_auth",
"AzureStorageAuth": "pyrit.auth.azure_storage_auth",
"BrowserSessionCopilotAuthenticator": "pyrit.auth.browser_session_copilot_authenticator",
"CopilotAuthenticator": "pyrit.auth.copilot_authenticator",
"ManualCopilotAuthenticator": "pyrit.auth.manual_copilot_authenticator",
"resolve_openai_auth": "pyrit.auth.openai_auth",
Expand Down
Loading