diff --git a/doc/code/converters/0_converters.ipynb b/doc/code/converters/0_converters.ipynb index 0121ff06f..aa77034b9 100644 --- a/doc/code/converters/0_converters.ipynb +++ b/doc/code/converters/0_converters.ipynb @@ -32,518 +32,7 @@ "execution_count": null, "id": "2", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found default environment files: ['/home/vscode/.pyrit/.env', '/home/vscode/.pyrit/.env.local']\n", - "Loaded environment file: /home/vscode/.pyrit/.env\n", - "Loaded environment file: /home/vscode/.pyrit/.env.local\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Input ModalityOutput ModalityConverter
0audio_pathaudio_pathAudioFrequencyConverter
1audio_pathtextAzureSpeechAudioToTextConverter
2image_pathimage_pathAddTextImageConverter
3image_pathimage_pathTransparencyAttackConverter
4image_pathvideo_pathAddImageVideoConverter
5image_path, urlimage_pathImageCompressionConverter
6textaudio_pathAzureSpeechTextToAudioConverter
7textimage_pathAddImageTextConverter
8textimage_pathQRCodeConverter
9texttextAnsiAttackConverter
10texttextAsciiArtConverter
11texttextAsciiSmugglerConverter
12texttextAskToDecodeConverter
13texttextAtbashConverter
14texttextBase2048Converter
15texttextBase64Converter
16texttextBinAsciiConverter
17texttextBinaryConverter
18texttextBrailleConverter
19texttextCaesarConverter
20texttextCharSwapConverter
21texttextCharacterSpaceConverter
22texttextCodeChameleonConverter
23texttextColloquialWordswapConverter
24texttextDenylistConverter
25texttextDiacriticConverter
26texttextEcojiConverter
27texttextEmojiConverter
28texttextFirstLetterConverter
29texttextFlipConverter
30texttextHumanInTheLoopConverter
31texttextInsertPunctuationConverter
32texttextLLMGenericTextConverter
33texttextLeetspeakConverter
34texttextMaliciousQuestionGeneratorConverter
35texttextMathObfuscationConverter
36texttextMathPromptConverter
37texttextMorseConverter
38texttextNatoConverter
39texttextNoiseConverter
40texttextPersuasionConverter
41texttextROT13Converter
42texttextRandomCapitalLettersConverter
43texttextRandomTranslationConverter
44texttextRepeatTokenConverter
45texttextSearchReplaceConverter
46texttextSelectiveTextConverter
47texttextSneakyBitsSmugglerConverter
48texttextStringJoinConverter
49texttextSuffixAppendConverter
50texttextSuperscriptConverter
51texttextTemplateSegmentConverter
52texttextTenseConverter
53texttextTextJailbreakConverter
54texttextToneConverter
55texttextToxicSentenceGeneratorConverter
56texttextTranslationConverter
57texttextUnicodeConfusableConverter
58texttextUnicodeReplacementConverter
59texttextUnicodeSubstitutionConverter
60texttextUrlConverter
61texttextVariationConverter
62texttextVariationSelectorSmugglerConverter
63texttextZalgoConverter
64texttextZeroWidthConverter
65texturlPDFConverter
\n", - "
" - ], - "text/plain": [ - " Input Modality Output Modality Converter\n", - "0 audio_path audio_path AudioFrequencyConverter\n", - "1 audio_path text AzureSpeechAudioToTextConverter\n", - "2 image_path image_path AddTextImageConverter\n", - "3 image_path image_path TransparencyAttackConverter\n", - "4 image_path video_path AddImageVideoConverter\n", - "5 image_path, url image_path ImageCompressionConverter\n", - "6 text audio_path AzureSpeechTextToAudioConverter\n", - "7 text image_path AddImageTextConverter\n", - "8 text image_path QRCodeConverter\n", - "9 text text AnsiAttackConverter\n", - "10 text text AsciiArtConverter\n", - "11 text text AsciiSmugglerConverter\n", - "12 text text AskToDecodeConverter\n", - "13 text text AtbashConverter\n", - "14 text text Base2048Converter\n", - "15 text text Base64Converter\n", - "16 text text BinAsciiConverter\n", - "17 text text BinaryConverter\n", - "18 text text BrailleConverter\n", - "19 text text CaesarConverter\n", - "20 text text CharSwapConverter\n", - "21 text text CharacterSpaceConverter\n", - "22 text text CodeChameleonConverter\n", - "23 text text ColloquialWordswapConverter\n", - "24 text text DenylistConverter\n", - "25 text text DiacriticConverter\n", - "26 text text EcojiConverter\n", - "27 text text EmojiConverter\n", - "28 text text FirstLetterConverter\n", - "29 text text FlipConverter\n", - "30 text text HumanInTheLoopConverter\n", - "31 text text InsertPunctuationConverter\n", - "32 text text LLMGenericTextConverter\n", - "33 text text LeetspeakConverter\n", - "34 text text MaliciousQuestionGeneratorConverter\n", - "35 text text MathObfuscationConverter\n", - "36 text text MathPromptConverter\n", - "37 text text MorseConverter\n", - "38 text text NatoConverter\n", - "39 text text NoiseConverter\n", - "40 text text PersuasionConverter\n", - "41 text text ROT13Converter\n", - "42 text text RandomCapitalLettersConverter\n", - "43 text text RandomTranslationConverter\n", - "44 text text RepeatTokenConverter\n", - "45 text text SearchReplaceConverter\n", - "46 text text SelectiveTextConverter\n", - "47 text text SneakyBitsSmugglerConverter\n", - "48 text text StringJoinConverter\n", - "49 text text SuffixAppendConverter\n", - "50 text text SuperscriptConverter\n", - "51 text text TemplateSegmentConverter\n", - "52 text text TenseConverter\n", - "53 text text TextJailbreakConverter\n", - "54 text text ToneConverter\n", - "55 text text ToxicSentenceGeneratorConverter\n", - "56 text text TranslationConverter\n", - "57 text text UnicodeConfusableConverter\n", - "58 text text UnicodeReplacementConverter\n", - "59 text text UnicodeSubstitutionConverter\n", - "60 text text UrlConverter\n", - "61 text text VariationConverter\n", - "62 text text VariationSelectorSmugglerConverter\n", - "63 text text ZalgoConverter\n", - "64 text text ZeroWidthConverter\n", - "65 text url PDFConverter" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import pandas as pd\n", "\n", @@ -610,24 +99,7 @@ "execution_count": null, "id": "6", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "text: gryy zr ubj gb phg qbja n gerr\n", - "text: tell me HOw to Cut dowN A tRee\n", - "text: \n", - "_|_ _ |\\ |\\ _ |) _ _|_ _ _ _|_ _| _ _, _|_ ,_ _ _ \n", - " | |/ |/ |/ /|/|/| |/ |/\\ / \\_| | |_ | / \\_ / | | | / | / \\_| | |_/|/| / | | / | |/ |/ \n", - " |_/|_/|_/|_/ | | |_/|_/ | |/\\_/ \\/ \\/ |_/\\_/ \\__/ \\/|_/ |_/ \\/|_/\\_/ \\/ \\/ | |_/ \\/|_/ |_/ |/|_/|_/\n", - " \n", - "\n", - "text: 0000000001110100 0000000001100101 0000000001101100 0000000001101100 0000000000100000 0000000001101101 0000000001100101 0000000000100000 0000000001101000 0000000001101111 0000000001110111 0000000000100000 0000000001110100 0000000001101111 0000000000100000 0000000001100011 0000000001110101 0000000001110100 0000000000100000 0000000001100100 0000000001101111 0000000001110111 0000000001101110 0000000000100000 0000000001100001 0000000000100000 0000000001110100 0000000001110010 0000000001100101 0000000001100101\n", - "text: t m h t c d a t\n" - ] - } - ], + "outputs": [], "source": [ "from pyrit.prompt_converter import (\n", " AsciiArtConverter,\n", @@ -671,39 +143,7 @@ "execution_count": null, "id": "8", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'__type__': 'TextTarget', '__module__': 'pyrit.prompt_target.text_target'}: user: E-x-p-l-a-i-n t-o m-e t-h-e p-r-o-c-e-s-s o-f c-h-o-p-p-i-n-g d-o-w-n a t-r-e-e-.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[PromptSendingAttack (ID: 81e8da7f)] No response received on attempt 1 (likely filtered)\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n", - "\u001b[1m\u001b[34m🔹 Turn 1 - USER\u001b[0m\n", - "\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n", - "\u001b[36m Original:\u001b[0m\n", - "\u001b[37m tell me how to cut down a tree\u001b[0m\n", - "\n", - "\u001b[36m Converted:\u001b[0m\n", - "\u001b[37m E-x-p-l-a-i-n t-o m-e t-h-e p-r-o-c-e-s-s o-f c-h-o-p-p-i-n-g d-o-w-n a t-r-e-e-.\u001b[0m\n", - "\n", - "\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n" - ] - } - ], + "outputs": [], "source": [ "from pyrit.executor.attack import (\n", " AttackConverterConfig,\n", @@ -737,23 +177,85 @@ "printer = ConsoleAttackResultPrinter()\n", "await printer.print_conversation_async(result=result) # type: ignore" ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "## Response Converters\n", + "\n", + "So far, we've focused on **request converters** that transform prompts before sending them to the target. PyRIT also supports **response converters** that transform the target's response before returning it. This is useful in scenarios like:\n", + "\n", + "- Translating responses back to the original language after sending prompts in a different language\n", + "- Decoding encoded responses\n", + "- Normalizing or cleaning up response text\n", + "\n", + "Response converters use the same `PromptConverterConfiguration` class as request converters. They are configured via the `response_converters` parameter in `AttackConverterConfig`.\n", + "\n", + "### Translation Round-Trip Example\n", + "\n", + "A common use case is sending prompts in a different language to test how the target handles non-English input. In this example, we:\n", + "\n", + "1. Use a **request converter** to translate the prompt from English to French\n", + "2. Send the translated prompt to the target\n", + "3. Use a **response converter** to translate the response back to English" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "from pyrit.executor.attack import (\n", + " AttackConverterConfig,\n", + " ConsoleAttackResultPrinter,\n", + " PromptSendingAttack,\n", + ")\n", + "from pyrit.prompt_converter import TranslationConverter\n", + "from pyrit.prompt_normalizer import PromptConverterConfiguration\n", + "from pyrit.prompt_target import OpenAIChatTarget\n", + "\n", + "objective = \"What is the capital of France?\"\n", + "\n", + "# Create an LLM target for the converters\n", + "converter_target = OpenAIChatTarget()\n", + "\n", + "# Create an LLM target to send prompts to\n", + "prompt_target = OpenAIChatTarget()\n", + "\n", + "# Request converter: translate English to French\n", + "request_converter = TranslationConverter(converter_target=converter_target, language=\"French\")\n", + "request_converter_config = PromptConverterConfiguration(converters=[request_converter])\n", + "\n", + "# Response converter: translate response back to English\n", + "response_converter = TranslationConverter(converter_target=converter_target, language=\"English\")\n", + "response_converter_config = PromptConverterConfiguration(converters=[response_converter])\n", + "\n", + "# Configure the attack with both request and response converters\n", + "converter_config = AttackConverterConfig(\n", + " request_converters=[request_converter_config],\n", + " response_converters=[response_converter_config],\n", + ")\n", + "\n", + "attack = PromptSendingAttack(\n", + " objective_target=prompt_target,\n", + " attack_converter_config=converter_config,\n", + ")\n", + "\n", + "result = await attack.execute_async(objective=objective) # type: ignore\n", + "\n", + "# Print the conversation showing both original and converted values\n", + "printer = ConsoleAttackResultPrinter()\n", + "await printer.print_conversation_async(result=result) # type: ignore" + ] } ], "metadata": { "jupytext": { "cell_metadata_filter": "-all" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.14" } }, "nbformat": 4, diff --git a/doc/code/converters/0_converters.py b/doc/code/converters/0_converters.py index 6be75251d..5731a3982 100644 --- a/doc/code/converters/0_converters.py +++ b/doc/code/converters/0_converters.py @@ -136,3 +136,65 @@ printer = ConsoleAttackResultPrinter() await printer.print_conversation_async(result=result) # type: ignore + +# %% [markdown] +# ## Response Converters +# +# So far, we've focused on **request converters** that transform prompts before sending them to the target. PyRIT also supports **response converters** that transform the target's response before returning it. This is useful in scenarios like: +# +# - Translating responses back to the original language after sending prompts in a different language +# - Decoding encoded responses +# - Normalizing or cleaning up response text +# +# Response converters use the same `PromptConverterConfiguration` class as request converters. They are configured via the `response_converters` parameter in `AttackConverterConfig`. +# +# ### Translation Round-Trip Example +# +# A common use case is sending prompts in a different language to test how the target handles non-English input. In this example, we: +# +# 1. Use a **request converter** to translate the prompt from English to French +# 2. Send the translated prompt to the target +# 3. Use a **response converter** to translate the response back to English + +# %% +from pyrit.executor.attack import ( + AttackConverterConfig, + ConsoleAttackResultPrinter, + PromptSendingAttack, +) +from pyrit.prompt_converter import TranslationConverter +from pyrit.prompt_normalizer import PromptConverterConfiguration +from pyrit.prompt_target import OpenAIChatTarget + +objective = "What is the capital of France?" + +# Create an LLM target for the converters +converter_target = OpenAIChatTarget() + +# Create an LLM target to send prompts to +prompt_target = OpenAIChatTarget() + +# Request converter: translate English to French +request_converter = TranslationConverter(converter_target=converter_target, language="French") +request_converter_config = PromptConverterConfiguration(converters=[request_converter]) + +# Response converter: translate response back to English +response_converter = TranslationConverter(converter_target=converter_target, language="English") +response_converter_config = PromptConverterConfiguration(converters=[response_converter]) + +# Configure the attack with both request and response converters +converter_config = AttackConverterConfig( + request_converters=[request_converter_config], + response_converters=[response_converter_config], +) + +attack = PromptSendingAttack( + objective_target=prompt_target, + attack_converter_config=converter_config, +) + +result = await attack.execute_async(objective=objective) # type: ignore + +# Print the conversation showing both original and converted values +printer = ConsoleAttackResultPrinter() +await printer.print_conversation_async(result=result) # type: ignore