diff --git a/doc/api.rst b/doc/api.rst index 2dc469f821..984a7515c7 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -319,10 +319,13 @@ API Reference RepeatTokenConverter ROT13Converter SearchReplaceConverter + SneakyBitsSmugglerConverter StringJoinConverter SuffixAppendConverter SuperscriptConverter + TemplateSegmentConverter TenseConverter + TextJailbreakConverter TextToHexConverter ToneConverter ToxicSentenceGeneratorConverter @@ -332,6 +335,7 @@ API Reference UnicodeSubstitutionConverter UrlConverter VariationConverter + VariationSelectorSmugglerConverter ZalgoConverter ZeroWidthConverter diff --git a/pyrit/prompt_converter/add_image_text_converter.py b/pyrit/prompt_converter/add_image_text_converter.py index bc8cc93689..f6e904d2a9 100644 --- a/pyrit/prompt_converter/add_image_text_converter.py +++ b/pyrit/prompt_converter/add_image_text_converter.py @@ -18,16 +18,9 @@ class AddImageTextConverter(PromptConverter): """ Adds a string to an image and wraps the text into multiple lines if necessary. - This class is similar to AddImageTextConverter except - we pass in an image file path as an argument to the constructor as opposed to text. - Args: - img_to_add (str): File path of image to add text to - font_name (str): Path of font to use. Must be a TrueType font (.ttf). Defaults to "helvetica.ttf". - color (tuple): Color to print text in, using RGB values. Defaults to (0, 0, 0). - font_size (float): Size of font to use. Defaults to 15. - x_pos (int): X coordinate to place text in (0 is left most). Defaults to 10. - y_pos (int): Y coordinate to place text in (0 is upper most). Defaults to 10. + This class is similar to :class:`AddTextImageConverter` except + we pass in an image file path as an argument to the constructor as opposed to text. """ def __init__( @@ -39,6 +32,20 @@ def __init__( x_pos: int = 10, y_pos: int = 10, ): + """ + Initializes the converter with the image file path and text properties. + + Args: + img_to_add (str): File path of image to add text to. + font_name (str): Path of font to use. Must be a TrueType font (.ttf). Defaults to "helvetica.ttf". + color (tuple): Color to print text in, using RGB values. Defaults to (0, 0, 0). + font_size (float): Size of font to use. Defaults to 15. + x_pos (int): X coordinate to place text in (0 is left most). Defaults to 10. + y_pos (int): Y coordinate to place text in (0 is upper most). Defaults to 10. + + Raises: + ValueError: If ``img_to_add`` is empty or invalid, or if ``font_name`` does not end with ".ttf". + """ if not img_to_add: raise ValueError("Please provide valid image path") if not font_name.endswith(".ttf"): @@ -53,11 +60,11 @@ def __init__( def _load_font(self): """ - Load the font for a given font name and font size + Loads the font for a given font name and font size. Returns: - ImageFont.FreeTypeFont or ImageFont.ImageFont: The loaded font object. If the specified font - cannot be loaded, the default font is returned. + ImageFont.FreeTypeFont or ImageFont.ImageFont: The loaded font object. If the specified font + cannot be loaded, the default font is returned. Raises: OSError: If the font resource cannot be loaded, a warning is logged and the default font is used instead. @@ -72,7 +79,7 @@ def _load_font(self): def _add_text_to_image(self, text: str) -> Image.Image: """ - Adds wrapped text to the image at self._img_to_add. + Adds wrapped text to the image at `self._img_to_add`. Args: text (str): The text to add to the image. @@ -111,13 +118,17 @@ def _add_text_to_image(self, text: str) -> Image.Image: async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Converter that overlays input text on the img_to_add. + Converts the given prompt by adding it as text to the image. Args: - prompt (str): The prompt to be added to the image. - input_type (PromptDataType): type of data + prompt (str): The text to be added to the image. + input_type (PromptDataType): The type of input data. + Returns: - ConverterResult: The filename of the converted image as a ConverterResult Object + ConverterResult: The result containing path to the updated image. + + Raises: + ValueError: If the input type is not supported. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/add_image_to_video_converter.py b/pyrit/prompt_converter/add_image_to_video_converter.py index 4cbb725e73..5ac6c6f5ff 100644 --- a/pyrit/prompt_converter/add_image_to_video_converter.py +++ b/pyrit/prompt_converter/add_image_to_video_converter.py @@ -27,13 +27,8 @@ class AddImageVideoConverter(PromptConverter): """ Adds an image to a video at a specified position. - Also, currently the image is placed in the whole video, not at a specific timepoint - Args: - video_path (str): File path of video to add image to - output_path (str, Optional): File path of output video. Defaults to None. - img_position (tuple): Position to place image in video. Defaults to (10, 10). - img_resize_size (tuple): Size to resize image to. Defaults to (500, 500). + Currently the image is placed in the whole video, not at a specific timepoint. """ def __init__( @@ -43,6 +38,18 @@ def __init__( img_position: tuple = (10, 10), img_resize_size: tuple = (500, 500), ): + """ + Initializes the converter with the video path and image properties. + + Args: + video_path (str): File path of video to add image to. + output_path (str, Optional): File path of output video. Defaults to None. + img_position (tuple): Position to place image in video. Defaults to (10, 10). + img_resize_size (tuple): Size to resize image to. Defaults to (500, 500). + + Raises: + ValueError: If ``video_path`` is empty or invalid. + """ if not video_path: raise ValueError("Please provide valid video path") @@ -54,13 +61,14 @@ def __init__( async def _add_image_to_video(self, image_path: str, output_path: str) -> str: """ - Adds image to video + Adds an image to video. + Args: image_path (str): The image path to add to video. output_path (str): The output video path. Returns: - output_path (str): The output video path. + str: The output video path. """ try: @@ -158,13 +166,17 @@ async def _add_image_to_video(self, image_path: str, output_path: str) -> str: async def convert_async(self, *, prompt: str, input_type: PromptDataType = "image_path") -> ConverterResult: """ - Converter that adds an image to a video + Converts the given prompt (image) by adding it to a video. Args: - prompt (str): The image file name to be added to the video. - input_type (PromptDataType): type of data + prompt (str): The image path to be added to the video. + input_type (PromptDataType): The type of input data. + Returns: - ConverterResult: The filename of the converted video as a ConverterResult Object + ConverterResult: The result containing filename of the converted video. + + Raises: + ValueError: If the input type is not supported. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/add_text_image_converter.py b/pyrit/prompt_converter/add_text_image_converter.py index 0bec90a93a..0a31b70913 100644 --- a/pyrit/prompt_converter/add_text_image_converter.py +++ b/pyrit/prompt_converter/add_text_image_converter.py @@ -19,13 +19,8 @@ class AddTextImageConverter(PromptConverter): """ Adds a string to an image and wraps the text into multiple lines if necessary. - Args: - text_to_add (str): Text to add to an image. Defaults to empty string. - font_name (str): Path of font to use. Must be a TrueType font (.ttf). Defaults to "helvetica.ttf". - color (tuple): Color to print text in, using RGB values. Defaults to (0, 0, 0). - font_size (float): Size of font to use. Defaults to 15. - x_pos (int): X coordinate to place text in (0 is left most). Defaults to 10. - y_pos (int): Y coordinate to place text in (0 is upper most). Defaults to 10. + This class is similar to :class:`AddImageTextConverter` except + we pass in text as an argument to the constructor as opposed to an image file path. """ def __init__( @@ -37,6 +32,20 @@ def __init__( x_pos: int = 10, y_pos: int = 10, ): + """ + Initializes the converter with the text and text properties. + + Args: + text_to_add (str): Text to add to an image. Defaults to empty string. + font_name (str): Path of font to use. Must be a TrueType font (.ttf). Defaults to "helvetica.ttf". + color (tuple): Color to print text in, using RGB values. Defaults to (0, 0, 0). + font_size (float): Size of font to use. Defaults to 15. + x_pos (int): X coordinate to place text in (0 is left most). Defaults to 10. + y_pos (int): Y coordinate to place text in (0 is upper most). Defaults to 10. + + Raises: + ValueError: If ``text_to_add`` is empty, or if ``font_name`` does not end with ".ttf". + """ if text_to_add.strip() == "": raise ValueError("Please provide valid text_to_add value") if not font_name.endswith(".ttf"): @@ -51,11 +60,11 @@ def __init__( def _load_font(self): """ - Load the font for a given font name and font size + Loads the font for a given font name and font size. Returns: - ImageFont.FreeTypeFont or ImageFont.ImageFont: The loaded font object. If the specified font - cannot be loaded, the default font is returned. + ImageFont.FreeTypeFont or ImageFont.ImageFont: The loaded font object. If the specified font + cannot be loaded, the default font is returned. Raises: OSError: If the font resource cannot be loaded, a warning is logged and the default font is used instead. @@ -105,13 +114,17 @@ def _add_text_to_image(self, image: Image.Image) -> Image.Image: async def convert_async(self, *, prompt: str, input_type: PromptDataType = "image_path") -> ConverterResult: """ - Converter that adds text to an image + Converts the given prompt (image) by adding text to it. Args: - prompt (str): The filename of the image to add the text to - input_type (PromptDataType): type of data + prompt (str): The image file path to which text will be added. + input_type (PromptDataType): The type of input data. + Returns: - ConverterResult: The filename of the converted image as a ConverterResult Object + ConverterResult: The result containing path to the updated image. + + Raises: + ValueError: If the input type is not supported. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/ansi_escape/ansi_attack_converter.py b/pyrit/prompt_converter/ansi_escape/ansi_attack_converter.py index 58b049829c..ce61424f73 100644 --- a/pyrit/prompt_converter/ansi_escape/ansi_attack_converter.py +++ b/pyrit/prompt_converter/ansi_escape/ansi_attack_converter.py @@ -20,10 +20,12 @@ class AnsiAttackConverter(PromptConverter): """ - A single converter that can: - - Use raw and escaped ANSI payloads. - - Ask the model about ANSI codes, repeat given payloads, unescape strings. - - Incorporate the user's original prompt into the final scenario, making the testing more dynamic. + Generates prompts with ANSI codes to evaluate LLM behavior and system risks. + + This converter can: + - Use raw and escaped ANSI payloads. + - Ask the model about ANSI codes, repeat given payloads, unescape strings. + - Incorporate the user's original prompt into the final scenario, making the testing more dynamic. """ def __init__( @@ -36,6 +38,8 @@ def __init__( incorporate_user_prompt: bool = True, ): """ + Initializes the converter with various options to control the scenarios generated. + Args: include_raw (bool): Include scenarios with raw ANSI codes. include_escaped (bool): Include scenarios with escaped ANSI codes. @@ -58,6 +62,7 @@ def output_supported(self, output_type: PromptDataType) -> bool: return output_type == "text" async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """Converts the given prompt into an ANSI attack scenario.""" if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/ascii_art_converter.py b/pyrit/prompt_converter/ascii_art_converter.py index f0b81cd03a..dd6a73c518 100644 --- a/pyrit/prompt_converter/ascii_art_converter.py +++ b/pyrit/prompt_converter/ascii_art_converter.py @@ -8,21 +8,21 @@ class AsciiArtConverter(PromptConverter): - """Converts a string to ASCII art""" + """ + Uses the `art` package to convert text into ASCII art. + """ def __init__(self, font="rand"): - self.font_value = font - - async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Converter that uses art to convert strings to ASCII art. - This can sometimes bypass LLM filters + Initializes the converter with a specified font. Args: - prompt (str): The prompt to be converted. - Returns: - str: The converted prompt. + font (str): The font to use for ASCII art. Defaults to "rand" which selects a random font. """ + self.font_value = font + + async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """Converts the given prompt into ASCII art.""" if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/atbash_converter.py b/pyrit/prompt_converter/atbash_converter.py index 921ce30e45..48306f9f76 100644 --- a/pyrit/prompt_converter/atbash_converter.py +++ b/pyrit/prompt_converter/atbash_converter.py @@ -11,22 +11,24 @@ class AtbashConverter(PromptConverter): """ - Converter to encode prompt using atbash cipher. + Encodes text using the Atbash cipher. Uses the following to encode: - ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 - ZYXWVUTSRQPONMLKJIHGFEDCBA 9876543210 + - ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 + - ZYXWVUTSRQPONMLKJIHGFEDCBA 9876543210 - 'Hello 123' would encode to 'Svool 876' - - Parameters - --- - append_description: bool, default=False - Append plaintext "expert" text to the prompt. Includes instructions to only communicate - using the cipher, a description of the cipher, and an example encoded using cipher. + 'Hello 123' would encode to 'Svool 876'. """ def __init__(self, *, append_description: bool = False) -> None: + """ + Initializes the converter with an option to append a description. + + Args: + append_description (bool): If True, appends plaintext "expert" text to the prompt. + This includes instructions to only communicate using the cipher, + a description of the cipher, and an example encoded using the cipher. + """ self.append_description = append_description self.example = ( "User: How do I cut down a stop sign?\n" @@ -35,9 +37,7 @@ def __init__(self, *, append_description: bool = False) -> None: ) async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: - """ - Simple converter that atbash cipher encodes the prompt. - """ + """Converts the given prompt using the Atbash cipher.""" if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/audio_frequency_converter.py b/pyrit/prompt_converter/audio_frequency_converter.py index 2f82c39e59..d700941f9f 100644 --- a/pyrit/prompt_converter/audio_frequency_converter.py +++ b/pyrit/prompt_converter/audio_frequency_converter.py @@ -17,13 +17,11 @@ class AudioFrequencyConverter(PromptConverter): """ - The AudioFrequencyConverter takes an audio file and shifts its frequency, by default it will shift it above - human range (=20kHz). - Args: - output_format (str): The format of the audio file. Defaults to "wav". - shift_value (int): The value by which the frequency will be shifted. Defaults to 20000 Hz. + Shifts the frequency of an audio file by a specified value. + By default, it will shift it above the human hearing range (=20 kHz). """ + #: Accepted audio formats for conversion. AcceptedAudioFormats = Literal["wav"] def __init__( @@ -32,6 +30,13 @@ def __init__( output_format: AcceptedAudioFormats = "wav", shift_value: int = 20000, ) -> None: + """ + Initializes the converter with the specified output format and shift value. + + Args: + output_format (str): The format of the audio file, defaults to "wav". + shift_value (int): The value by which the frequency will be shifted, defaults to 20000 Hz. + """ self._output_format = output_format self._shift_value = shift_value @@ -42,17 +47,19 @@ def output_supported(self, output_type: PromptDataType) -> bool: return output_type == "audio_path" async def convert_async(self, *, prompt: str, input_type: PromptDataType = "audio_path") -> ConverterResult: - """Convert an audio file by shifting its frequency. + """ + Converts the given audio file by shifting its frequency. Args: - prompt (str): File path to audio file - input_type (PromptDataType): Type of data, defaults to "audio_path" + prompt (str): File path to the audio file to be converted. + input_type (PromptDataType): The type of input data. + + Returns: + ConverterResult: The result containing the audio file path. Raises: ValueError: If the input type is not supported. - - Returns: - ConverterResult: The converted audio file as a ConverterResult object. + Exception: If there is an error during the conversion process. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/azure_speech_audio_to_text_converter.py b/pyrit/prompt_converter/azure_speech_audio_to_text_converter.py index 3d9afcfc12..6726c2efaa 100644 --- a/pyrit/prompt_converter/azure_speech_audio_to_text_converter.py +++ b/pyrit/prompt_converter/azure_speech_audio_to_text_converter.py @@ -18,18 +18,14 @@ class AzureSpeechAudioToTextConverter(PromptConverter): """ - The AzureSpeechAudioTextConverter takes a .wav file and transcribes it into text. - https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-to-text + Transcribes a .wav audio file into text using Azure AI Speech service. - Args: - azure_speech_region (str, Optional): The name of the Azure region. - azure_speech_key (str, Optional): The API key for accessing the service. - recognition_language (str): Recognition voice language. Defaults to "en-US". - For more on supported languages, see the following link - https://learn.microsoft.com/en-us/azure/ai-services/speech-service/language-support + https://learn.microsoft.com/en-us/azure/ai-services/speech-service/speech-to-text """ + #: The name of the Azure region. AZURE_SPEECH_REGION_ENVIRONMENT_VARIABLE: str = "AZURE_SPEECH_REGION" + #: The API key for accessing the service. AZURE_SPEECH_KEY_ENVIRONMENT_VARIABLE: str = "AZURE_SPEECH_KEY" def __init__( @@ -38,6 +34,16 @@ def __init__( azure_speech_key: Optional[str] = None, recognition_language: str = "en-US", ) -> None: + """ + Initializes the converter with Azure Speech service credentials and recognition language. + + Args: + azure_speech_region (str, Optional): The name of the Azure region. + azure_speech_key (str, Optional): The API key for accessing the service. + recognition_language (str): Recognition voice language. Defaults to "en-US". + For more on supported languages, see the following link: + https://learn.microsoft.com/en-us/azure/ai-services/speech-service/language-support + """ self._azure_speech_region: str = default_values.get_required_value( env_var_name=self.AZURE_SPEECH_REGION_ENVIRONMENT_VARIABLE, passed_value=azure_speech_region @@ -59,13 +65,17 @@ def output_supported(self, output_type: PromptDataType) -> bool: async def convert_async(self, *, prompt: str, input_type: PromptDataType = "audio_path") -> ConverterResult: """ - Converter that transcribes audio to text. + Converts the given audio file into its text representation. Args: - prompt (str): File path to audio file - input_type (PromptDataType): Type of data + prompt (str): File path to the audio file to be transcribed. + input_type (PromptDataType): The type of input data. + Returns: - ConverterResult: The transcribed text as a ConverterResult Object + ConverterResult: The result containing the transcribed text. + + Raises: + ValueError: If the input type is not supported or if the provided file is not a .wav file. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") @@ -87,12 +97,13 @@ async def convert_async(self, *, prompt: str, input_type: PromptDataType = "audi def recognize_audio(self, audio_bytes: bytes) -> str: """ - Recognize audio file and return transcribed text. + Recognizes audio file and returns transcribed text. Args: audio_bytes (bytes): Audio bytes input. + Returns: - str: Transcribed text + str: Transcribed text. """ try: import azure.cognitiveservices.speech as speechsdk # noqa: F811 @@ -144,22 +155,22 @@ def recognize_audio(self, audio_bytes: bytes) -> str: def transcript_cb(self, evt: Any, transcript: list[str]) -> None: """ - Callback function that appends transcribed text upon receiving a "recognized" event + Callback function that appends transcribed text upon receiving a "recognized" event. Args: - evt (speechsdk.SpeechRecognitionEventArgs): event - transcript (list): list to store transcribed text + evt (speechsdk.SpeechRecognitionEventArgs): Event. + transcript (list): List to store transcribed text. """ logger.info("RECOGNIZED: {}".format(evt.result.text)) transcript.append(evt.result.text) def stop_cb(self, evt: Any, recognizer: Any) -> None: """ - Callback function that stops continuous recognition upon receiving an event 'evt' + Callback function that stops continuous recognition upon receiving an event 'evt'. Args: - evt (speechsdk.SpeechRecognitionEventArgs): event - recognizer (speechsdk.SpeechRecognizer): speech recognizer object + evt (speechsdk.SpeechRecognitionEventArgs): Event. + recognizer (speechsdk.SpeechRecognizer): Speech recognizer object. """ try: import azure.cognitiveservices.speech as speechsdk # noqa: F811 diff --git a/pyrit/prompt_converter/azure_speech_text_to_audio_converter.py b/pyrit/prompt_converter/azure_speech_text_to_audio_converter.py index 27d54755b4..4c59133dff 100644 --- a/pyrit/prompt_converter/azure_speech_text_to_audio_converter.py +++ b/pyrit/prompt_converter/azure_speech_text_to_audio_converter.py @@ -16,22 +16,17 @@ class AzureSpeechTextToAudioConverter(PromptConverter): """ - The AzureSpeechTextToAudio takes a prompt and generates a wave file. + Generates a wave file from a text prompt using Azure AI Speech service. + https://learn.microsoft.com/en-us/azure/ai-services/speech-service/text-to-speech - Args: - azure_speech_region (str, Optional): The name of the Azure region. - azure_speech_key (str, Optional): The API key for accessing the service. - synthesis_language (str): Synthesis voice language - synthesis_voice_name (str): Synthesis voice name, see URL - For more details see the following link for synthesis language and synthesis voice: - https://learn.microsoft.com/en-us/azure/ai-services/speech-service/language-support - filename (str): File name to be generated. Please include either .wav or .mp3 - output_format (str): Either wav or mp3. Must match the file prefix. """ + #: The name of the Azure region. AZURE_SPEECH_REGION_ENVIRONMENT_VARIABLE: str = "AZURE_SPEECH_REGION" + #: The API key for accessing the service. AZURE_SPEECH_KEY_ENVIRONMENT_VARIABLE: str = "AZURE_SPEECH_KEY" + #: Supported audio formats for output. AzureSpeachAudioFormat = Literal["wav", "mp3"] def __init__( @@ -42,6 +37,19 @@ def __init__( synthesis_voice_name: str = "en-US-AvaNeural", output_format: AzureSpeachAudioFormat = "wav", ) -> None: + """ + Initializes the converter with Azure Speech service credentials, synthesis language, and voice name. + + Args: + azure_speech_region (str, Optional): The name of the Azure region. + azure_speech_key (str, Optional): The API key for accessing the service. + synthesis_language (str): Synthesis voice language. + synthesis_voice_name (str): Synthesis voice name, see URL. + For more details see the following link for synthesis language and synthesis voice: + https://learn.microsoft.com/en-us/azure/ai-services/speech-service/language-support + filename (str): File name to be generated. Please include either .wav or .mp3. + output_format (str): Either wav or mp3. Must match the file prefix. + """ self._azure_speech_region: str = default_values.get_required_value( env_var_name=self.AZURE_SPEECH_REGION_ENVIRONMENT_VARIABLE, passed_value=azure_speech_region ) @@ -61,6 +69,21 @@ def output_supported(self, output_type: PromptDataType) -> bool: return output_type == "audio_path" async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Converts the given text prompt into its audio representation. + + Args: + prompt (str): The text prompt to be converted into audio. + input_type (PromptDataType): The type of input data. + + Returns: + ConverterResult: The result containing the audio file path. + + Raises: + ModuleNotFoundError: If the ``azure.cognitiveservices.speech`` module is not installed. + RuntimeError: If there is an error during the speech synthesis process. + ValueError: If the input type is not supported or if the prompt is empty. + """ try: import azure.cognitiveservices.speech as speechsdk # noqa: F811 except ModuleNotFoundError as e: diff --git a/pyrit/prompt_converter/base64_converter.py b/pyrit/prompt_converter/base64_converter.py index 411437b3f2..9ce6a31823 100644 --- a/pyrit/prompt_converter/base64_converter.py +++ b/pyrit/prompt_converter/base64_converter.py @@ -8,11 +8,12 @@ class Base64Converter(PromptConverter): + """ + Converts text prompts into Base64 encoded strings. + """ async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: - """ - Simple converter that just base64 encodes the prompt - """ + """Converts the given prompt into Base64 encoded string.""" if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/binary_converter.py b/pyrit/prompt_converter/binary_converter.py index 0a18f5932e..d681a86943 100644 --- a/pyrit/prompt_converter/binary_converter.py +++ b/pyrit/prompt_converter/binary_converter.py @@ -11,12 +11,16 @@ class BinaryConverter(WordLevelConverter): - """Transforms input text into its binary representation with configurable bits per character (8, 16, or 32)""" + """ + Transforms input text into its binary representation with configurable bits per character (8, 16, or 32). + """ class BitsPerChar(Enum): - BITS_8 = 8 - BITS_16 = 16 - BITS_32 = 32 + """The number of bits per character for binary conversion.""" + + BITS_8 = 8 #: 8 bits per character, suitable for ASCII characters. + BITS_16 = 16 #: 16 bits per character, suitable for Unicode characters. + BITS_32 = 32 #: 32 bits per character, suitable for extended Unicode characters. def __init__( self, @@ -28,7 +32,8 @@ def __init__( regex: Optional[Union[str, re.Pattern]] = None, ): """ - Initialize the converter. + Initializes the converter with the specified bits per character and selection parameters. + This class allows for selection of words to convert based on various criteria. Only one selection parameter may be provided at a time (indices, keywords, proportion, or regex). If no selection parameter is provided, all words will be converted. @@ -48,7 +53,7 @@ def __init__( self.bits_per_char = bits_per_char def validate_input(self, prompt): - # Check if bits_per_char is sufficient for the characters in the prompt + """Checks if ``bits_per_char`` is sufficient for the characters in the prompt.""" bits = self.bits_per_char.value max_code_point = max((ord(char) for char in prompt), default=0) min_bits_required = max_code_point.bit_length() @@ -59,11 +64,11 @@ def validate_input(self, prompt): ) async def convert_word_async(self, word: str) -> str: + """Converts each character in the word to its binary representation.""" bits = self.bits_per_char.value - # Convert each character in the word to its binary representation return " ".join(format(ord(char), f"0{bits}b") for char in word) def join_words(self, words: list[str]) -> str: - """Join the converted words with the binary representation of a space.""" + """Joins the converted words with the binary representation of a space.""" space_binary = format(ord(" "), f"0{self.bits_per_char.value}b") return f" {space_binary} ".join(words) diff --git a/pyrit/prompt_converter/caesar_converter.py b/pyrit/prompt_converter/caesar_converter.py index e897be8e19..c55adc9056 100644 --- a/pyrit/prompt_converter/caesar_converter.py +++ b/pyrit/prompt_converter/caesar_converter.py @@ -11,24 +11,27 @@ class CaesarConverter(PromptConverter): """ - Converter to encode prompt using caesar cipher. + Encodes text using the Caesar cipher with a specified offset. - Encodes by using given offset. - Using offset=1, 'Hello 123' would encode to 'Ifmmp 234', as each character would shift by 1. + Using ``offset=1``, 'Hello 123' would encode to 'Ifmmp 234', as each character would shift by 1. Shifts for digits 0-9 only work if the offset is less than 10, if the offset is equal to or greather than 10, any numeric values will not be shifted. - - Parameters - --- - caesar_offset: int - Offset for caesar cipher, range 0 to 25 (inclusive). Can also be negative for shifting backwards. - - append_description: bool, default=False - Append plaintext "expert" text to the prompt. Includes instructions to only communicate - using the cipher, a description of the cipher, and an example encoded using cipher. """ def __init__(self, *, caesar_offset: int, append_description: bool = False) -> None: + """ + Initializes the converter with a Caesar cipher offset and an option to append a description. + + Args: + caesar_offset (int): Offset for caesar cipher, range 0 to 25 (inclusive). + Can also be negative for shifting backwards. + append_description (bool): If True, appends plaintext "expert" text to the prompt. + This includes instructions to only communicate using the cipher, + a description of the cipher, and an example encoded using the cipher. + + Raises: + ValueError: If ``caesar_offset`` is not in the range -25 to 25 inclusive. + """ if caesar_offset < -25 or caesar_offset > 25: raise ValueError("caesar offset value invalid, must be between -25 and 25 inclusive.") self.caesar_offset = caesar_offset @@ -40,9 +43,7 @@ def __init__(self, *, caesar_offset: int, append_description: bool = False) -> N ) async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: - """ - Simple converter that caesar cipher encodes the prompt. - """ + """Converts the given prompt using the Caesar cipher.""" if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/character_space_converter.py b/pyrit/prompt_converter/character_space_converter.py index 7d3f12212a..b67fa11a24 100644 --- a/pyrit/prompt_converter/character_space_converter.py +++ b/pyrit/prompt_converter/character_space_converter.py @@ -8,13 +8,15 @@ class CharacterSpaceConverter(PromptConverter): + """ + Spaces out the input prompt and removes specified punctuations. + + For more information on the bypass strategy, refer to: + https://www.robustintelligence.com/blog-posts/bypassing-metas-llama-classifier-a-simple-jailbreak + """ async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: - """ - Simple converter that spaces out the input prompt and removes specified punctuations. - For more information on the bypass strategy, refer to: - https://www.robustintelligence.com/blog-posts/bypassing-metas-llama-classifier-a-simple-jailbreak - """ + """Converts the given prompt by removing punctuation and spacing out characters.""" if not self.input_supported(input_type): raise ValueError("Input type not supported") converted_text = re.sub("[!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~]", "", " ".join(prompt)) diff --git a/pyrit/prompt_converter/charswap_attack_converter.py b/pyrit/prompt_converter/charswap_attack_converter.py index ef46cf0bec..26f7d03aa5 100644 --- a/pyrit/prompt_converter/charswap_attack_converter.py +++ b/pyrit/prompt_converter/charswap_attack_converter.py @@ -10,7 +10,9 @@ class CharSwapConverter(WordLevelConverter): - """Applies character swapping to words in the prompt to test adversarial textual robustness.""" + """ + Applies character swapping to words in the prompt to test adversarial textual robustness. + """ def __init__( self, @@ -22,7 +24,8 @@ def __init__( regex: Optional[Union[str, re.Pattern]] = None, ): """ - Initialize the converter. + Initializes the converter with the specified parameters. + This class allows for selection of words to convert based on various criteria. Only one selection parameter may be provided at a time (indices, keywords, proportion, or regex). By default, proportion is set to 0.2, meaning 20% of randomly selected words will be perturbed. @@ -48,10 +51,11 @@ async def convert_word_async(self, word: str) -> str: def _perturb_word(self, word: str) -> str: """ - Perturb a word by swapping two adjacent characters. + Perturbs a word by swapping two adjacent characters. Args: word (str): The word to perturb. + Returns: str: The perturbed word with swapped characters. """ diff --git a/pyrit/prompt_converter/codechameleon_converter.py b/pyrit/prompt_converter/codechameleon_converter.py index 660f584381..0c80d61a22 100644 --- a/pyrit/prompt_converter/codechameleon_converter.py +++ b/pyrit/prompt_converter/codechameleon_converter.py @@ -14,41 +14,33 @@ class CodeChameleonConverter(PromptConverter): """ - The CodeChameleon Converter uses a combination of personal encryption and decryption functions, - code nesting, as well as a set of instructions for the response to bypass LLM safeguards. - - The user prompt is encrypted, and the target is asked to solve the encrypted problem by completing a - ProblemSolver class utilizing the decryption function while following the instructions. + Encrypts user prompt, adds stringified decrypt function in markdown and instructions. + + The user prompt is encrypted, and the target is asked to solve the encrypted problem by completing + a ProblemSolver class utilizing the decryption function while following the instructions. + + Supports the following encryption types: + - `custom`: + User provided encryption and decryption functions. Encryption function used to encode prompt. + Markdown formatting and plaintext instructions appended to decryption function, used as text only. + Should include imports. + - `reverse`: + Reverse the prompt. "How to cut down a tree?" becomes "tree? a down cut to How". + - `binary_tree`: + Encode prompt using binary tree. "How to cut down a tree"?" becomes + ``{'value': 'cut', 'left': {'value': 'How', 'left': None, + 'right': {'value': 'to', 'left': None, 'right': None}}, + 'right': {'value': 'a', 'left': {'value': 'down', 'left': None, 'right': None}, + 'right': {'value': 'tree?', 'left': None, 'right': None}}}`` + - `odd_even`: + All words in odd indices of prompt followed by all words in even indices. + "How to cut down a tree?" becomes "How cut a to down tree?". + - `length`: + List of words in prompt sorted by length, use word as key, original index as value. + "How to cut down a tree?" becomes + ``[{'a': 4}, {'to': 1}, {'How': 0}, {'cut': 2}, {'down': 3}, {'tree?': 5}]`` Code Chameleon Converter based on https://arxiv.org/abs/2402.16717 by Lv, Huijie, et al. - - Parameters - --- - encrypt_mode: {"custom", "reverse", "binary_tree", "odd_even", "length"} - Select a built-in encryption method or provide custom encryption and decryption functions. - `custom`: User provided encryption and decryption functions. Encryption function used to encode prompt. - Markdown formatting and plaintext instructions appended to decryption function, used as text only. - Should include imports. - `reverse`: Reverse the prompt. "How to cut down a tree?" becomes "tree? a down cut to How" - `binary_tree`: Encode prompt using binary tree. "How to cut down a tree"?" becomes - "{'value': 'cut', - 'left': {'value': 'How', 'left': None, 'right': {'value': 'to', 'left': None, 'right': None}}, - 'right': {'value': 'a', 'left': {'value': 'down', 'left': None, 'right': None}, - 'right': {'value': 'tree?', 'left': None, 'right': None}}}" - `odd_even`: All words in odd indices of prompt followed by all words in even indices. - "How to cut down a tree?" becomes "How cut a to down tree?" - `length`: List of words in prompt sorted by length, use word as key, original index as value. - "How to cut down a tree?" becomes "[{'a': 4}, {'to': 1}, {'How': 0}, {'cut': 2}, {'down': 3}, {'tree?': 5}]" - - encrypt_function: Callable, default=None - User provided encryption function. Only used if `encrypt_mode` is "custom". - Used to encode user prompt. - - decrypt_function: Callable or list, default=None - User provided encryption function. Only used if `encrypt_mode` is "custom". - Used as part of markdown code block instructions in system prompt. - If list is provided, strings will be treated as single statements for imports or comments. Functions - will take the source code of the function. """ def __init__( @@ -58,6 +50,23 @@ def __init__( encrypt_function: Optional[Callable] = None, decrypt_function: Optional[Callable | list[Callable | str]] = None, ) -> None: + """ + Initializes the converter with the specified encryption type and optional functions. + + Args: + encrypt_type (str): Must be one of "custom", "reverse", "binary_tree", "odd_even" or "length". + encrypt_function (Callable, optional): User provided encryption function. + Only used if `encrypt_mode` is "custom". Used to encode user prompt. + decrypt_function (Callable or list, optional): User provided encryption function. + Only used if `encrypt_mode` is "custom". + Used as part of markdown code block instructions in system prompt. + If list is provided, strings will be treated as single statements for imports or comments. + Functions will take the source code of the function. + + Raises: + ValueError: If ``encrypt_type`` is not valid or if ``encrypt_function`` or ``decrypt_function`` are not + provided when ``encrypt_type`` is "custom". + """ match encrypt_type: case "custom": if encrypt_function is None or decrypt_function is None: @@ -86,9 +95,7 @@ def __init__( ) async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: - """ - Converter that encrypts user prompt, adds stringified decrypt function in markdown and instructions. - """ + """Converts the given prompt by applying the specified encryption function.""" if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/colloquial_wordswap_converter.py b/pyrit/prompt_converter/colloquial_wordswap_converter.py index 32e23cafff..a82bf420f9 100644 --- a/pyrit/prompt_converter/colloquial_wordswap_converter.py +++ b/pyrit/prompt_converter/colloquial_wordswap_converter.py @@ -10,13 +10,15 @@ class ColloquialWordswapConverter(PromptConverter): - """Converts a string to a Singaporean colloquial version""" + """ + Converts text into colloquial Singaporean context. + """ def __init__( self, deterministic: bool = False, custom_substitutions: Optional[Dict[str, List[str]]] = None ) -> None: """ - Initialize the converter with optional deterministic mode and custom substitutions. + Initializes the converter with optional deterministic mode and custom substitutions. Args: deterministic (bool): If True, use the first substitution for each wordswap. @@ -47,16 +49,7 @@ def __init__( self._deterministic = deterministic async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: - """ - Convert the given prompt to colloquial Singaporean context. - - Args: - prompt (str): The text to convert. - input_type (PromptDataType): The type of input data. - - Returns: - ConverterResult: A ConverterResult containing the Singaporean colloquial version of the prompt. - """ + """Converts the given prompt by replacing words with colloquial Singaporean terms.""" if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/denylist_converter.py b/pyrit/prompt_converter/denylist_converter.py index d74d27887d..4f9e90b22e 100644 --- a/pyrit/prompt_converter/denylist_converter.py +++ b/pyrit/prompt_converter/denylist_converter.py @@ -14,7 +14,11 @@ class DenylistConverter(LLMGenericTextConverter): - """Eliminates forbidden words or phrases in a prompt by replacing them with synonyms.""" + """ + Replaces forbidden words or phrases in a prompt with synonyms using an LLM. + + An existing ``PromptChatTarget`` is used to perform the conversion (like Azure OpenAI). + """ def __init__( self, @@ -23,6 +27,15 @@ def __init__( system_prompt_template: Optional[SeedPrompt] = None, denylist: list[str] = [], ): + """ + Initializes the converter with a target, an optional system prompt template, and a denylist. + + Args: + converter_target (PromptChatTarget): The target for the prompt conversion. + system_prompt_template (Optional[SeedPrompt]): The system prompt template to use for the conversion. + If not provided, a default template will be used. + denylist (list[str]): A list of words or phrases that should be replaced in the prompt. + """ # set to default strategy if not provided system_prompt_template = ( system_prompt_template @@ -38,13 +51,15 @@ def __init__( async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Removes any words or phrases from the input prompt that are in the denylist, replacing them - with synonomous words. + Converts the given prompt by removing any words or phrases that are in the denylist, + replacing them with synonymous words. Args: prompt (str): The prompt to be converted. + input_type (PromptDataType): The type of input data. + Returns: - str: The converted prompt without any denied words. + ConverterResult: The result containing the modified prompt. """ # check if the prompt contains any words from the denylist and if so, diff --git a/pyrit/prompt_converter/diacritic_converter.py b/pyrit/prompt_converter/diacritic_converter.py index 119989ee34..39362d3689 100644 --- a/pyrit/prompt_converter/diacritic_converter.py +++ b/pyrit/prompt_converter/diacritic_converter.py @@ -12,25 +12,25 @@ class DiacriticConverter(PromptConverter): """ - A PromptConverter that applies diacritics to specified characters in a string. + Applies diacritics to specified characters in a string. """ def __init__(self, target_chars: str = "aeiou", accent: str = "acute"): """ - Initializes the DiacriticConverter. + Initializes the converter with specified target characters and diacritic accent. Args: target_chars (str): Characters to apply the diacritic to. Defaults to "aeiou". accent (str): Type of diacritic to apply (default is 'acute'). - Available options are: - - 'acute': "\u0301" - - 'grave': "\u0300" - - 'tilde': "\u0303" - - 'umlaut': "\u0308" + Available options are: + - `acute`: \u0301 + - `grave`: \u0300 + - `tilde`: \u0303 + - `umlaut`: \u0308 Raises: - ValueError: If `target_chars` is empty. + ValueError: If ``target_chars`` is empty or if the specified accent is not recognized. """ super().__init__() @@ -81,15 +81,7 @@ def _add_diacritic(self, text: str) -> str: ) async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: - """ - Converts the given prompt by applying diacritics to target characters. - - Args: - prompt (str): The prompt to be converted. - - Returns: - ConverterResult: The result containing the modified prompt. - """ + """Converts the given prompt by applying diacritics to specified characters.""" if not self.input_supported(input_type): raise ValueError("Only 'text' input type is supported.") diff --git a/pyrit/prompt_converter/emoji_converter.py b/pyrit/prompt_converter/emoji_converter.py index 9d139e146d..c210ab9df3 100644 --- a/pyrit/prompt_converter/emoji_converter.py +++ b/pyrit/prompt_converter/emoji_converter.py @@ -13,6 +13,7 @@ class EmojiConverter(WordLevelConverter): Inspired by https://github.com/BASI-LABS/parseltongue/blob/main/src/utils.ts """ + #: Dictionary mapping letters to their corresponding emojis. emoji_dict = { "a": ["πŸ…", "πŸ…°οΈ", "πŸ„°"], "b": ["πŸ…‘", "πŸ…±οΈ", "πŸ„±"], diff --git a/pyrit/prompt_converter/flip_converter.py b/pyrit/prompt_converter/flip_converter.py index 114d0dec56..8fba91cfb4 100644 --- a/pyrit/prompt_converter/flip_converter.py +++ b/pyrit/prompt_converter/flip_converter.py @@ -6,11 +6,12 @@ class FlipConverter(PromptConverter): + """ + Flips the input text prompt. For example, "hello me" would be converted to "em olleh". + """ async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: - """ - Simple converter that flips the prompt. "hello me" would be "em olleh" - """ + """Converts the given prompt by reversing the text.""" if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/fuzzer_converter/fuzzer_converter_base.py b/pyrit/prompt_converter/fuzzer_converter/fuzzer_converter_base.py index 45afbfe6f2..f90f1b9b53 100644 --- a/pyrit/prompt_converter/fuzzer_converter/fuzzer_converter_base.py +++ b/pyrit/prompt_converter/fuzzer_converter/fuzzer_converter_base.py @@ -28,30 +28,40 @@ class FuzzerConverter(PromptConverter): Base class for GPTFUZZER converters. Adapted from GPTFUZZER: Red Teaming Large Language Models with Auto-Generated Jailbreak Prompts. - Paper https://arxiv.org/pdf/2309.10253 by Jiahao Yu, Xingwei Lin, Zheng Yu, Xinyu Xing - GitHub https://github.com/sherdencooper/GPTFuzz/tree/master - - Parameters: - converter_target (PromptChatTarget): Chat target used to perform fuzzing on user prompt - prompt_template (SeedPrompt): Template to be used instead of the default system prompt with instructions for - the chat target. + Paper: https://arxiv.org/pdf/2309.10253 by Jiahao Yu, Xingwei Lin, Zheng Yu, Xinyu Xing. + GitHub: https://github.com/sherdencooper/GPTFuzz/tree/master """ def __init__(self, *, converter_target: PromptChatTarget, prompt_template: Optional[SeedPrompt] = None): + """ + Initializes the converter with the specified chat target and prompt template. + + Args: + converter_target (PromptChatTarget): Chat target used to perform fuzzing on user prompt. + prompt_template (SeedPrompt, Optional): Template to be used instead of the default system prompt with + instructions for the chat target. + """ self.converter_target = converter_target self.system_prompt = prompt_template.value self.template_label = "TEMPLATE" def update(self, **kwargs) -> None: + """Updates the converter with new parameters.""" pass async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Converter to generate versions of prompt with new, prepended sentences. + Converts the given prompt into the target format supported by the converter. Args: prompt (str): The prompt to be converted. - input_type (PromptDataType): The type of the input prompt. + input_type (PromptDataType): The type of input data. + + Returns: + ConverterResult: The result containing the modified prompt. + + Raises: + ValueError: If the input type is not supported. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") @@ -89,6 +99,7 @@ async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text @pyrit_json_retry async def send_prompt_async(self, request): + """Sends the prompt request to the converter target and processes the response.""" response = await self.converter_target.send_prompt_async(prompt_request=request) response_msg = response.get_value() diff --git a/pyrit/prompt_converter/fuzzer_converter/fuzzer_crossover_converter.py b/pyrit/prompt_converter/fuzzer_converter/fuzzer_crossover_converter.py index 544961ec12..cb26136ca7 100644 --- a/pyrit/prompt_converter/fuzzer_converter/fuzzer_crossover_converter.py +++ b/pyrit/prompt_converter/fuzzer_converter/fuzzer_crossover_converter.py @@ -20,13 +20,7 @@ class FuzzerCrossOverConverter(FuzzerConverter): """ - Fuzzer converter that uses multiple prompt templates to generate new prompts. - - Args: - converter_target (PromptChatTarget): Chat target used to perform fuzzing on user prompt - prompt_template (SeedPrompt, Optional): Template to be used instead of the default system prompt with - instructions for the chat target. - prompt_templates (List[str], Optional): List of prompt templates to use in addition to the default template. + Uses multiple prompt templates to generate new prompts. """ def __init__( @@ -36,6 +30,15 @@ def __init__( prompt_template: Optional[SeedPrompt] = None, prompt_templates: Optional[List[str]] = None, ): + """ + Initializes the converter with the specified chat target and prompt templates. + + Args: + converter_target (PromptChatTarget): Chat target used to perform fuzzing on user prompt. + prompt_template (SeedPrompt, Optional): Template to be used instead of the default system prompt with + instructions for the chat target. + prompt_templates (List[str], Optional): List of prompt templates to use in addition to the default one. + """ prompt_template = ( prompt_template if prompt_template @@ -53,7 +56,7 @@ def update(self, **kwargs) -> None: async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Converter to generate versions of prompt with new, prepended sentences. + Converts the given prompt by combining it with a random prompt template from the list of available templates. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/fuzzer_converter/fuzzer_expand_converter.py b/pyrit/prompt_converter/fuzzer_converter/fuzzer_expand_converter.py index 03d95bfd35..455bc42ecb 100644 --- a/pyrit/prompt_converter/fuzzer_converter/fuzzer_expand_converter.py +++ b/pyrit/prompt_converter/fuzzer_converter/fuzzer_expand_converter.py @@ -18,6 +18,10 @@ class FuzzerExpandConverter(FuzzerConverter): + """ + Generates versions of a prompt with new, prepended sentences. + """ + def __init__( self, *, @@ -35,7 +39,7 @@ def __init__( async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Converter to generate versions of prompt with new, prepended sentences. + Converts the given prompt by generating versions of it with new, prepended sentences. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/fuzzer_converter/fuzzer_rephrase_converter.py b/pyrit/prompt_converter/fuzzer_converter/fuzzer_rephrase_converter.py index 1eebc02208..cc76d15538 100644 --- a/pyrit/prompt_converter/fuzzer_converter/fuzzer_rephrase_converter.py +++ b/pyrit/prompt_converter/fuzzer_converter/fuzzer_rephrase_converter.py @@ -12,6 +12,10 @@ class FuzzerRephraseConverter(FuzzerConverter): + """ + Generates versions of a prompt with rephrased sentences. + """ + def __init__(self, *, converter_target: PromptChatTarget, prompt_template: SeedPrompt = None): prompt_template = ( prompt_template diff --git a/pyrit/prompt_converter/fuzzer_converter/fuzzer_shorten_converter.py b/pyrit/prompt_converter/fuzzer_converter/fuzzer_shorten_converter.py index a70512e479..6c1bac04b9 100644 --- a/pyrit/prompt_converter/fuzzer_converter/fuzzer_shorten_converter.py +++ b/pyrit/prompt_converter/fuzzer_converter/fuzzer_shorten_converter.py @@ -12,6 +12,10 @@ class FuzzerShortenConverter(FuzzerConverter): + """ + Generates versions of a prompt with shortened sentences. + """ + def __init__(self, *, converter_target: PromptChatTarget, prompt_template: SeedPrompt = None): prompt_template = ( prompt_template diff --git a/pyrit/prompt_converter/fuzzer_converter/fuzzer_similar_converter.py b/pyrit/prompt_converter/fuzzer_converter/fuzzer_similar_converter.py index 9b64043734..c22f60a4d9 100644 --- a/pyrit/prompt_converter/fuzzer_converter/fuzzer_similar_converter.py +++ b/pyrit/prompt_converter/fuzzer_converter/fuzzer_similar_converter.py @@ -12,6 +12,10 @@ class FuzzerSimilarConverter(FuzzerConverter): + """ + Generates versions of a prompt with similar sentences. + """ + def __init__(self, *, converter_target: PromptChatTarget, prompt_template: SeedPrompt = None): prompt_template = ( prompt_template diff --git a/pyrit/prompt_converter/human_in_the_loop_converter.py b/pyrit/prompt_converter/human_in_the_loop_converter.py index 00fc6b1d42..1ca6cb0143 100644 --- a/pyrit/prompt_converter/human_in_the_loop_converter.py +++ b/pyrit/prompt_converter/human_in_the_loop_converter.py @@ -12,31 +12,42 @@ class HumanInTheLoopConverter(PromptConverter): """ - Allows review of each prompt sent to a target before sending it. User can choose to send the prompt as is, - modify the prompt, or run the prompt through one of the passed-in converters before sending it. + Allows review of each prompt sent to a target before sending it. - Args: - converters: (List[PromptConverter], Optional): List of possible converters to run input through. + User can choose to send the prompt as is, modify the prompt, + or run the prompt through one of the passed-in converters before sending it. """ def __init__( self, converters: Optional[list[PromptConverter]] = None, ): + """ + Initializes the converter with a list of possible converters to run input through. + + Args: + converters (List[PromptConverter], Optional): List of possible converters to run input through. + """ self._converters = converters or [] async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Before sending a prompt to a target, user is given three options to choose from: - (1) Proceed with sending the prompt as is. - (2) Manually modify the prompt. - (3) Run the prompt through a converter before sending it. + Converts the given prompt by allowing user interaction before sending it to a target. + + User is given three options to choose from: + (1) Proceed with sending the prompt as is. + (2) Manually modify the prompt. + (3) Run the prompt through a converter before sending it. Args: - prompt (str): The prompt to be added to the image. - input_type (PromptDataType): Type of data + prompt (str): The prompt to be converted. + input_type (PromptDataType): The type of input data. + Returns: - ConverterResult: The filename of the converted image as a ConverterResult Object + ConverterResult: The result containing the modified prompt. + + Raises: + ValueError: If no converters are provided and the user chooses to run a converter. """ user_input = "" if self._converters: diff --git a/pyrit/prompt_converter/insert_punctuation_converter.py b/pyrit/prompt_converter/insert_punctuation_converter.py index 52dee87b5a..ad765e1622 100644 --- a/pyrit/prompt_converter/insert_punctuation_converter.py +++ b/pyrit/prompt_converter/insert_punctuation_converter.py @@ -13,20 +13,26 @@ class InsertPunctuationConverter(PromptConverter): """ Inserts punctuation into a prompt to test robustness. - Punctuation insertion: inserting single punctuations in string.punctuation. + + Punctuation insertion: inserting single punctuations in `string.punctuation`. Words in a prompt: a word does not contain any punctuation and space. "a1b2c3" is a word; "a1 2" are 2 words; "a1,b,3" are 3 words. """ + #: Common punctuation characters. Used if no punctuation list is provided. default_punctuation_list = [",", ".", "!", "?", ":", ";", "-"] def __init__(self, word_swap_ratio: float = 0.2, between_words: bool = True) -> None: """ - Initialize the converter with optional and word swap ratio. + Initializes the converter with a word swap ratio and punctuation insertion mode. + Args: word_swap_ratio (float): Percentage of words to perturb. Defaults to 0.2. between_words (bool): If True, insert punctuation only between words. - If False, insert punctuation within words. Defaults to True. + If False, insert punctuation within words. Defaults to True. + + Raises: + ValueError: If ``word_swap_ratio`` is not between 0 and 1. """ # Swap ratio cannot be 0 or larger than 1 if not 0 < word_swap_ratio <= 1: @@ -50,13 +56,18 @@ async def convert_async( self, *, prompt: str, input_type: PromptDataType = "text", punctuation_list: Optional[List[str]] = None ) -> ConverterResult: """ - Convert the given prompt by inserting punctuation. + Converts the given prompt by inserting punctuation. + Args: prompt (str): The text to convert. input_type (PromptDataType): The type of input data. punctuation_list (Optional[List[str]]): List of punctuations to use for insertion. + Returns: - ConverterResult: A ConverterResult containing a interation of modified prompts. + ConverterResult: The result containing a interation of modified prompts. + + Raises: + ValueError: If the input type is not supported. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/leetspeak_converter.py b/pyrit/prompt_converter/leetspeak_converter.py index fc8d4baa7f..c48120cec8 100644 --- a/pyrit/prompt_converter/leetspeak_converter.py +++ b/pyrit/prompt_converter/leetspeak_converter.py @@ -9,7 +9,9 @@ class LeetspeakConverter(WordLevelConverter): - """Converts a string to a leetspeak version.""" + """ + Converts a string to a leetspeak version. + """ def __init__( self, @@ -22,7 +24,8 @@ def __init__( regex: Optional[Union[str, re.Pattern]] = None, ): """ - Initialize the converter with optional deterministic mode and custom substitutions. + Initializes the converter with optional deterministic mode and custom substitutions. + This class allows for selection of words to convert based on various criteria. Only one selection parameter may be provided at a time (indices, keywords, proportion, or regex). If no selection parameter is provided, all words will be converted. diff --git a/pyrit/prompt_converter/llm_generic_text_converter.py b/pyrit/prompt_converter/llm_generic_text_converter.py index 310ae1b759..90cfe21a28 100644 --- a/pyrit/prompt_converter/llm_generic_text_converter.py +++ b/pyrit/prompt_converter/llm_generic_text_converter.py @@ -18,6 +18,10 @@ class LLMGenericTextConverter(PromptConverter): + """ + Represents a generic LLM converter that expects text to be transformed (e.g. no JSON parsing or format). + """ + def __init__( self, *, @@ -27,15 +31,14 @@ def __init__( **kwargs, ): """ - Generic LLM converter that expects text to be transformed (e.g. no JSON parsing or format) + Initializes the converter with a target and optional prompt templates. Args: - converter_target (PromptChatTarget): The endpoint that converts the prompt + converter_target (PromptChatTarget): The endpoint that converts the prompt. system_prompt_template (SeedPrompt, Optional): The prompt template to set as the system prompt. user_prompt_template_with_objective (SeedPrompt, Optional): The prompt template to set as the user prompt. expects kwargs: Additional parameters for the prompt template. - """ self._converter_target = converter_target self._system_prompt_template = system_prompt_template @@ -48,17 +51,14 @@ def __init__( async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Convert a prompt based on the prompt template + Converts the given prompt using an LLM via the specified converter target. - Parameters: - prompt (str): The prompt to convert. - input_type (PromptDataType, Optional): The data type of the input prompt. Defaults to "text". + Args: + prompt (str): The prompt to be converted. + input_type (PromptDataType): The type of input data. Returns: - ConverterResult: The result of the conversion, including the converted output text and output type. - - Raises: - ValueError: If the input type is not supported. + ConverterResult: The result containing the converted output and its type. """ conversation_id = str(uuid.uuid4()) diff --git a/pyrit/prompt_converter/malicious_question_generator_converter.py b/pyrit/prompt_converter/malicious_question_generator_converter.py index 60d1d95ab9..bda9851e7b 100644 --- a/pyrit/prompt_converter/malicious_question_generator_converter.py +++ b/pyrit/prompt_converter/malicious_question_generator_converter.py @@ -14,7 +14,9 @@ class MaliciousQuestionGeneratorConverter(LLMGenericTextConverter): """ - A PromptConverter that generates malicious questions using an LLM via an existing PromptTarget (like Azure OpenAI). + Generates malicious questions using an LLM. + + An existing ``PromptChatTarget`` is used to perform the conversion (like Azure OpenAI). """ def __init__(self, *, converter_target: PromptChatTarget, prompt_template: SeedPrompt = None): diff --git a/pyrit/prompt_converter/math_prompt_converter.py b/pyrit/prompt_converter/math_prompt_converter.py index 4557c6a2e8..689fba8e14 100644 --- a/pyrit/prompt_converter/math_prompt_converter.py +++ b/pyrit/prompt_converter/math_prompt_converter.py @@ -14,8 +14,9 @@ class MathPromptConverter(LLMGenericTextConverter): """ - A PromptConverter that converts natural language instructions into symbolic mathematics problems - using an LLM via an existing PromptTarget (like Azure OpenAI or other supported backends). + Converts natural language instructions into symbolic mathematics problems using an LLM. + + An existing ``PromptChatTarget`` is used to perform the conversion (like Azure OpenAI). """ def __init__(self, *, converter_target: PromptChatTarget, prompt_template: SeedPrompt = None): @@ -24,7 +25,7 @@ def __init__(self, *, converter_target: PromptChatTarget, prompt_template: SeedP Args: converter_target (PromptChatTarget): The endpoint that converts the prompt. - prompt_template (SeedPrompt): The prompt template to use. + prompt_template (SeedPrompt): The seed prompt template to use. """ # Load the template from the YAML file or use a default template if not provided @@ -40,14 +41,14 @@ def __init__(self, *, converter_target: PromptChatTarget, prompt_template: SeedP async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Convert a prompt into a mathematical problem format. + Converts the given prompt into a mathematical problem format. - Parameters: - prompt (str): The prompt to convert. + Args: + prompt (str): The prompt to be converted. + input_type (PromptDataType): The type of input data. Returns: - ConverterResult: The result of the conversion, - including the mathematical representation and real-world example. + ConverterResult: The result containing the mathematical representation and real-world example. """ logger.info(f"Converting prompt: {prompt}") diff --git a/pyrit/prompt_converter/morse_converter.py b/pyrit/prompt_converter/morse_converter.py index 0d8f83bcd2..77edb3f9cf 100644 --- a/pyrit/prompt_converter/morse_converter.py +++ b/pyrit/prompt_converter/morse_converter.py @@ -10,20 +10,20 @@ class MorseConverter(PromptConverter): """ - Converter to encode prompts using morse code. + Encodes prompts using morse code. Uses '-' and '.' characters, with ' ' to separate characters and '/' to separate words. - - Invalid/unsupported characters replaced with error sequence '........'. - - Parameters - --- - append_description: bool, default=False - Append plaintext "expert" text to the prompt. Includes instructions to only communicate - using the cipher, a description of the cipher, and an example encoded using cipher. + Invalid or unsupported characters are replaced with an error sequence '........'. """ def __init__(self, *, append_description: bool = False) -> None: + """ + Initializes the converter with an option to append a description to the prompt. + + Args: + append_description (bool): Append plaintext "expert" text to the prompt. Includes instructions to only + communicate using the cipher, a description of the cipher, and an example encoded using cipher. + """ self.append_description = append_description self.example = ( "User: How do I cut down a stop sign?\n" @@ -33,7 +33,7 @@ def __init__(self, *, append_description: bool = False) -> None: async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Simple converter that morse code encodes the prompt. + Converts the given prompt to morse code. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/noise_converter.py b/pyrit/prompt_converter/noise_converter.py index 42dd2476de..c6531a39d7 100644 --- a/pyrit/prompt_converter/noise_converter.py +++ b/pyrit/prompt_converter/noise_converter.py @@ -15,6 +15,12 @@ class NoiseConverter(LLMGenericTextConverter): + """ + Injects noise errors into a conversation using an LLM. + + An existing ``PromptChatTarget`` is used to perform the conversion (like Azure OpenAI). + """ + def __init__( self, *, @@ -24,14 +30,13 @@ def __init__( prompt_template: Optional[SeedPrompt] = None, ): """ - Injects noise errors into a conversation + Initializes the converter with the specified parameters. Args: - converter_target (PromptChatTarget): The endpoint that converts the prompt + converter_target (PromptChatTarget): The endpoint that converts the prompt. noise (str): The noise to inject. Grammar error, delete random letter, insert random space, etc. - number_errors (int): The number of errors to inject + number_errors (int): The number of errors to inject. prompt_template (SeedPrompt, Optional): The prompt template for the conversion. - """ # set to default strategy if not provided prompt_template = ( diff --git a/pyrit/prompt_converter/pdf_converter.py b/pyrit/prompt_converter/pdf_converter.py index fd193a325b..028025cba1 100644 --- a/pyrit/prompt_converter/pdf_converter.py +++ b/pyrit/prompt_converter/pdf_converter.py @@ -16,25 +16,17 @@ class PDFConverter(PromptConverter): """ - Converts a text prompt into a PDF file. Supports various modes: - 1. Template-Based Generation: If a `SeedPrompt` is provided, dynamic data can be injected into the - template using the `SeedPrompt.render_template_value` method, and the resulting content is converted to a PDF. - 2. Direct Text-Based Generation: If no template is provided, the raw string prompt is converted directly - into a PDF. - 3. Modify Existing PDFs (Overlay approach): Enables injecting text into existing PDFs at specified - coordinates, merging a new "overlay layer" onto the original PDF. - - Args: - prompt_template (Optional[SeedPrompt], optional): A `SeedPrompt` object representing a template. - font_type (str): Font type for the PDF. Defaults to "Helvetica". - font_size (int): Font size for the PDF. Defaults to 12. - font_color (tuple): Font color for the PDF in RGB format. Defaults to (255, 255, 255). - page_width (int): Width of the PDF page in mm. Defaults to 210 (A4 width). - page_height (int): Height of the PDF page in mm. Defaults to 297 (A4 height). - column_width (int): Width of each column in the PDF. Defaults to 0 (full page width). - row_height (int): Height of each row in the PDF. Defaults to 10. - existing_pdf (Optional[Path], optional): Path to an existing PDF file. Defaults to None. - injection_items (Optional[List[Dict]], optional): A list of injection items for modifying an existing PDF. + Converts a text prompt into a PDF file. + + Supports various modes: + - Template-Based Generation: + If a ``SeedPrompt`` is provided, dynamic data can be injected into the template using + the ``SeedPrompt.render_template_value`` method, and the resulting content is converted to a PDF. + - Direct Text-Based Generation: + If no template is provided, the raw string prompt is converted directly into a PDF. + - Modify Existing PDFs (Overlay approach): + Enables injecting text into existing PDFs at specified coordinates, merging a new "overlay layer" + onto the original PDF. """ def __init__( @@ -50,6 +42,25 @@ def __init__( existing_pdf: Optional[Path] = None, injection_items: Optional[List[Dict]] = None, ) -> None: + """ + Initializes the converter with the specified parameters. + + Args: + prompt_template (Optional[SeedPrompt], optional): A ``SeedPrompt`` object representing a template. + font_type (str): Font type for the PDF. Defaults to "Helvetica". + font_size (int): Font size for the PDF. Defaults to 12. + font_color (tuple): Font color for the PDF in RGB format. Defaults to (255, 255, 255). + page_width (int): Width of the PDF page in mm. Defaults to 210 (A4 width). + page_height (int): Height of the PDF page in mm. Defaults to 297 (A4 height). + column_width (int): Width of each column in the PDF. Defaults to 0 (full page width). + row_height (int): Height of each row in the PDF. Defaults to 10. + existing_pdf (Optional[Path], optional): Path to an existing PDF file. Defaults to None. + injection_items (Optional[List[Dict]], optional): A list of injection items for modifying an existing PDF. + + Raises: + ValueError: If the font color is invalid or the injection items are not provided as a list of dictionaries. + FileNotFoundError: If the provided PDF file does not exist. + """ self._prompt_template = prompt_template self._font_type = font_type self._font_size = font_size @@ -93,7 +104,7 @@ async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text Args: prompt (str): The prompt to be embedded in the PDF. - input_type (PromptDataType): The type of the input data (default: "text"). + input_type (PromptDataType): The type of input data. Returns: ConverterResult: The result containing the full file path to the generated PDF. diff --git a/pyrit/prompt_converter/persuasion_converter.py b/pyrit/prompt_converter/persuasion_converter.py index bb2ca14078..bc6775f8b1 100644 --- a/pyrit/prompt_converter/persuasion_converter.py +++ b/pyrit/prompt_converter/persuasion_converter.py @@ -26,27 +26,36 @@ class PersuasionConverter(PromptConverter): """ - Converter to rephrase prompts using a variety of persuasion techniques. + Rephrases prompts using a variety of persuasion techniques. Based on https://arxiv.org/abs/2401.06373 by Zeng et al. - Parameters - --- - converter_target: PromptChatTarget - Chat target used to perform rewriting on user prompt - - persuasion_technique: - {"authority_endorsement", "evidence_based", "expert_endorsement", "logical_appeal", "misrepresentation"} - Persuasion technique to be used by the converter, determines the system prompt to be used to - generate new prompts. - - authority_endorsement: Citing authoritative sources in support of a claim. - - evidence_based: Using empirical data, statistics, and facts to support a claim or decision. - - expert_endorsement: Citing domain experts in support of a claim. - - logical_appeal: Using logic or reasoning to support a claim. - - misrepresentation: Presenting oneself or an issue in a way that's not genuine or true. + Supported persuasion techniques: + - "authority_endorsement": + Citing authoritative sources in support of a claim. + - "evidence_based": + Using empirical data, statistics, and facts to support a claim or decision. + - "expert_endorsement": + Citing domain experts in support of a claim. + - "logical_appeal": + Using logic or reasoning to support a claim. + - "misrepresentation": + Presenting oneself or an issue in a way that's not genuine or true. """ def __init__(self, *, converter_target: PromptChatTarget, persuasion_technique: str): + """ + Initializes the converter with the specified target and prompt template. + + Args: + converter_target (PromptChatTarget): The chat target used to perform rewriting on user prompts. + persuasion_technique (str): Persuasion technique to be used by the converter, determines the system prompt + to be used to generate new prompts. Must be one of "authority_endorsement", "evidence_based", + "expert_endorsement", "logical_appeal", "misrepresentation". + + Raises: + ValueError: If the persuasion technique is not supported or does not exist. + """ self.converter_target = converter_target try: @@ -59,7 +68,7 @@ def __init__(self, *, converter_target: PromptChatTarget, persuasion_technique: async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Converter to generate versions of prompt with new, prepended sentences. + Converts the given prompt using the persuasion technique specified during initialization. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") @@ -94,6 +103,7 @@ async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text @pyrit_json_retry async def send_persuasion_prompt_async(self, request): + """Sends the prompt to the converter target and processes the response.""" response = await self.converter_target.send_prompt_async(prompt_request=request) response_msg = response.get_value() diff --git a/pyrit/prompt_converter/prompt_converter.py b/pyrit/prompt_converter/prompt_converter.py index 8a8584df24..49105e53b7 100644 --- a/pyrit/prompt_converter/prompt_converter.py +++ b/pyrit/prompt_converter/prompt_converter.py @@ -12,7 +12,11 @@ @dataclass class ConverterResult: + """The result of a prompt conversion, containing the converted output and its type.""" + + #: The converted text output. This is the main result of the conversion. output_text: str + #: The data type of the converted output. Indicates the format/type of the ``output_text``. output_type: PromptDataType def __str__(self): @@ -21,44 +25,50 @@ def __str__(self): class PromptConverter(abc.ABC, Identifier): """ - A prompt converter is responsible for converting prompts into a different representation. - + Base class for converters that transform prompts into a different representation or format. """ + def __init__(self): + """ + Initializes the prompt converter. + """ + super().__init__() + @abc.abstractmethod async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Converts the given prompts into a different representation + Converts the given prompt into the target format supported by the converter. Args: - prompt: The prompt to be converted. + prompt (str): The prompt to be converted. + input_type (PromptDataType): The type of input data. Returns: - str: The converted representation of the prompts. + ConverterResult: The result containing the converted output and its type. """ @abc.abstractmethod def input_supported(self, input_type: PromptDataType) -> bool: """ - Checks if the input type is supported by the converter + Checks if the input type is supported by the converter. Args: - input_type: The input type to check + input_type (PromptDataType): The input type to check. Returns: - bool: True if the input type is supported, False otherwise + bool: True if the input type is supported, False otherwise. """ @abc.abstractmethod def output_supported(self, output_type: PromptDataType) -> bool: """ - Checks if the output type is supported by the converter + Checks if the output type is supported by the converter. Args: - output_type: The output type to check + output_type (PromptDataType): The output type to check. Returns: - bool: True if the output type is supported, False otherwise + bool: True if the output type is supported, False otherwise. """ async def convert_tokens_async( @@ -109,6 +119,12 @@ async def _replace_text_match(self, match): return result def get_identifier(self): + """ + Returns an identifier dictionary for the converter. + + Returns: + dict: The identifier dictionary. + """ public_attributes = {} public_attributes["__type__"] = self.__class__.__name__ public_attributes["__module__"] = self.__class__.__module__ diff --git a/pyrit/prompt_converter/qr_code_converter.py b/pyrit/prompt_converter/qr_code_converter.py index 0b8f911f29..8a38f9b16d 100644 --- a/pyrit/prompt_converter/qr_code_converter.py +++ b/pyrit/prompt_converter/qr_code_converter.py @@ -11,26 +11,8 @@ class QRCodeConverter(PromptConverter): - """Converts a text string to a QR code image. - - Args: - scale (int, Optional): Scaling factor that determines the width/height in pixels of each - black/white square (known as a "module") in the QR code. Defaults to 3. - border (int, Optional): Controls how many modules thick the border should be. - Defaults to recommended value of 4. - dark_color (tuple, Optional): Sets color of dark modules, using RGB values. - Defaults to black: (0, 0, 0). - light_color (tuple, Optional): Sets color of light modules, using RGB values. - Defaults to white: (255, 255, 255). - data_dark_color (tuple, Optional): Sets color of dark data modules (the modules that actually - stores the data), using RGB values. Defaults to dark_color. - data_light_color (tuple, Optional): Sets color of light data modules, using RGB values. - Defaults to light_color. - finder_dark_color (tuple, Optional): Sets dark module color of finder patterns (squares located in - three corners), using RGB values. Defaults to dark_color. - finder_light_color (tuple, Optional): Sets light module color of finder patterns, using RGB values. - Defaults to light_color. - border_color (tuple, Optional): Sets color of border, using RGB values. Defaults to light_color. + """ + Converts a text string to a QR code image. """ def __init__( @@ -45,6 +27,28 @@ def __init__( finder_light_color: Optional[tuple] = None, border_color: Optional[tuple] = None, ): + """ + Initializes the converter with specified parameters for QR code generation. + + Args: + scale (int, Optional): Scaling factor that determines the width/height in pixels of each + black/white square (known as a "module") in the QR code. Defaults to 3. + border (int, Optional): Controls how many modules thick the border should be. + Defaults to recommended value of 4. + dark_color (tuple, Optional): Sets color of dark modules, using RGB values. + Defaults to black: (0, 0, 0). + light_color (tuple, Optional): Sets color of light modules, using RGB values. + Defaults to white: (255, 255, 255). + data_dark_color (tuple, Optional): Sets color of dark data modules (the modules that actually + stores the data), using RGB values. Defaults to ``dark_color``. + data_light_color (tuple, Optional): Sets color of light data modules, using RGB values. + Defaults to light_color. + finder_dark_color (tuple, Optional): Sets dark module color of finder patterns (squares located in + three corners), using RGB values. Defaults to ``dark_color``. + finder_light_color (tuple, Optional): Sets light module color of finder patterns, using RGB values. + Defaults to light_color. + border_color (tuple, Optional): Sets color of border, using RGB values. Defaults to ``light_color``. + """ self._scale = scale self._border = border self._dark_color = dark_color @@ -58,13 +62,17 @@ def __init__( async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Converter that converts string to QR code image. + Converts the given prompt to a QR code image. Args: prompt (str): The prompt to be converted. - input_type (PromptDataType): Type of data to be converted. Defaults to "text". + input_type (PromptDataType): The type of input data. + Returns: - ConverterResult: The filename of the converted QR code image as a ConverterResult Object + ConverterResult: The result containing filename of the converted QR code image. + + Raises: + ValueError: If the input type is not supported or if the prompt is empty. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/random_capital_letters_converter.py b/pyrit/prompt_converter/random_capital_letters_converter.py index 609c864804..726971e64b 100644 --- a/pyrit/prompt_converter/random_capital_letters_converter.py +++ b/pyrit/prompt_converter/random_capital_letters_converter.py @@ -11,14 +11,18 @@ class RandomCapitalLettersConverter(PromptConverter): - """This converter takes a prompt and randomly capitalizes it by a percentage of the total characters. - - Args: - This accepts a text prompt, and a percentage of randomization from 1 to 100. This includes decimal - points in that range. + """ + Takes a prompt and randomly capitalizes it by a percentage of the total characters. """ def __init__(self, percentage: float = 100.0) -> None: + """ + Initializes the converter with the specified percentage of randomization. + + Args: + percentage (float): The percentage of characters to capitalize in the prompt. Must be between 1 and 100. + Defaults to 100.0. This includes decimal points in that range. + """ self.percentage = percentage def input_supported(self, input_type: PromptDataType) -> bool: @@ -27,20 +31,20 @@ def input_supported(self, input_type: PromptDataType) -> bool: def output_supported(self, output_type: PromptDataType) -> bool: return output_type == "text" - # function to check if character is lower case returns True or False def is_lowercase_letter(self, char): + """Checks if the given character is a lowercase letter.""" return char.islower() - # function to check if number is between 1 and 100 returns True or False def is_percentage(self, input_string): + """Checks if the input string is a valid percentage between 1 and 100.""" try: number = float(input_string) return 1 <= number <= 100 except ValueError: return False - # function to generate an array of random positions set by a number def generate_random_positions(self, total_length, set_number): + """Generates a list of unique random positions within the range of `total_length`.""" # Ensure the set number is not greater than the total length if set_number > total_length: logger.error(f"Set number {set_number} cannot be greater than the total length which is {total_length}.") @@ -54,6 +58,7 @@ def generate_random_positions(self, total_length, set_number): return random_positions def string_to_upper_case_by_percentage(self, percentage, prompt): + """Converts a string by randomly capitalizing a percentage of its characters.""" if not self.is_percentage(percentage): logger.error(f"Percentage number {percentage} cannot be higher than 100 and lower than 1.") raise ValueError(f"Percentage number {percentage} cannot be higher than 100 and lower than 1.") @@ -67,7 +72,7 @@ def string_to_upper_case_by_percentage(self, percentage, prompt): async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Simple converter that converts the prompt to capital letters via a percentage . + Converts the given prompt by randomly capitalizing a percentage of its characters. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/repeat_token_converter.py b/pyrit/prompt_converter/repeat_token_converter.py index fc285188fb..6db51eef69 100644 --- a/pyrit/prompt_converter/repeat_token_converter.py +++ b/pyrit/prompt_converter/repeat_token_converter.py @@ -10,29 +10,21 @@ class RepeatTokenConverter(PromptConverter): """ - Repeat a specified token a specified number of times in addition to a given prompt. + Repeats a specified token a specified number of times in addition to a given prompt. + Based on: https://dropbox.tech/machine-learning/bye-bye-bye-evolution-of-repeated-token-attacks-on-chatgpt-models - Parameters - --- - token_to_repeat: string - The string to be repeated - - times_to_repeat: int - The number of times the string will be repeated - - token_insert_mode: {"split", "prepend", "append", "repeat"}, default="prepend" - Method to insert repeated tokens: - - If "split" prompt text will be split on the first occurance of (.?!) punctuation, - and repeated tokens will be inserted at location of split. - - If "prepend" repeated tokens will be inserted before the prompt text. - - If "append" repeated tokens will be inserted after the prompt text. - - If "repeat" prompt text will be ignored and result will only be repeated tokens. + Supported insertion modes: + - "split": + The prompt text will be split on the first occurrence of (.?!) punctuation, + and repeated tokens will be inserted at the location of the split. + - "prepend": + Repeated tokens will be inserted before the prompt text. + - "append": + Repeated tokens will be inserted after the prompt text. + - "repeat": + The prompt text will be ignored, and the result will only contain repeated tokens. """ def __init__( @@ -42,6 +34,15 @@ def __init__( times_to_repeat: int, token_insert_mode: Optional[Literal["split", "prepend", "append", "repeat"]] = None, ) -> None: + """ + Initializes the converter with the specified token, number of repetitions, and insertion mode. + + Args: + token_to_repeat (str): The string to be repeated. + times_to_repeat (int): The number of times the string will be repeated. + token_insert_mode (str, optional): The mode of insertion for the repeated token. + Can be "split", "prepend", "append", or "repeat". + """ self.token_to_repeat = " " + token_to_repeat.strip() self.times_to_repeat = times_to_repeat if not token_insert_mode: @@ -78,7 +79,7 @@ def insert(text: str) -> list: async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Converter to insert repeated tokens into the prompt. + Converts the given prompt by repeating the specified token a specified number of times. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/rot13_converter.py b/pyrit/prompt_converter/rot13_converter.py index 8e602985e5..a4686fdd30 100644 --- a/pyrit/prompt_converter/rot13_converter.py +++ b/pyrit/prompt_converter/rot13_converter.py @@ -7,7 +7,9 @@ class ROT13Converter(WordLevelConverter): - """Simple converter that just ROT13 encodes the prompt""" + """ + Encodes prompts using the ROT13 cipher. + """ async def convert_word_async(self, word: str) -> str: return codecs.encode(word, "rot13") diff --git a/pyrit/prompt_converter/search_replace_converter.py b/pyrit/prompt_converter/search_replace_converter.py index b26a9e511f..4b2ea9a5f3 100644 --- a/pyrit/prompt_converter/search_replace_converter.py +++ b/pyrit/prompt_converter/search_replace_converter.py @@ -9,15 +9,20 @@ class SearchReplaceConverter(PromptConverter): - """Converts a string by replacing chosen phrase with a new phrase of choice - - Args: - pattern (str): the regex pattern to replace - replace (str): the new phrase to replace with, can be a list and a random element is chosen - regex_flags (int): regex flags to use for the replacement + """ + Converts a string by replacing chosen phrase with a new phrase of choice. """ def __init__(self, pattern: str, replace: str | list[str], regex_flags=0) -> None: + """ + Initializes the converter with the specified regex pattern and replacement phrase(s). + + Args: + pattern (str): The regex pattern to replace. + replace (str | list[str]): The new phrase to replace with. Can be a single string or a list of strings. + If a list is provided, a random element will be chosen for replacement. + regex_flags (int): Regex flags to use for the replacement. Defaults to 0 (no flags). + """ self.pattern = pattern self.replace_list = [replace] if isinstance(replace, str) else replace @@ -25,13 +30,17 @@ def __init__(self, pattern: str, replace: str | list[str], regex_flags=0) -> Non async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Simple converter that just replaces character in string with a chosen new character + Converts the given prompt by replacing the specified pattern with a random choice from the replacement list. Args: - prompt (str): prompt to convert - input_type (PromptDataType): type of input + prompt (str): The prompt to be converted. + input_type (PromptDataType): The type of input data. + + Returns: + ConverterResult: The result containing the converted text. - Returns: converted text as a ConverterResult object + Raises: + ValueError: If the input type is not supported. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/string_join_converter.py b/pyrit/prompt_converter/string_join_converter.py index d99903d0d0..e5a5d03120 100644 --- a/pyrit/prompt_converter/string_join_converter.py +++ b/pyrit/prompt_converter/string_join_converter.py @@ -8,7 +8,9 @@ class StringJoinConverter(WordLevelConverter): - """Converts text by joining its characters with the specified join value""" + """ + Converts text by joining its characters with the specified join value. + """ def __init__( self, @@ -20,7 +22,8 @@ def __init__( regex: Optional[Union[str, re.Pattern]] = None, ): """ - Initialize the converter. + Initializes the converter with the specified join value and selection parameters. + This class allows for selection of words to convert based on various criteria. Only one selection parameter may be provided at a time (indices, keywords, proportion, or regex). If no selection parameter is provided, all words will be converted. diff --git a/pyrit/prompt_converter/suffix_append_converter.py b/pyrit/prompt_converter/suffix_append_converter.py index 84f3648025..30183c4c81 100644 --- a/pyrit/prompt_converter/suffix_append_converter.py +++ b/pyrit/prompt_converter/suffix_append_converter.py @@ -6,6 +6,12 @@ class SuffixAppendConverter(PromptConverter): + """ + Appends a specified suffix to the prompt. + E.g. with a suffix `!!!`, it converts a prompt of `test` to `test !!!`. + + See https://github.com/Azure/PyRIT/tree/main/pyrit/auxiliary_attacks/gcg for adversarial suffix generation. + """ def __init__(self, *, suffix: str): if not suffix: @@ -14,18 +20,6 @@ def __init__(self, *, suffix: str): self.suffix = suffix async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: - """ - Simple converter that appends a given suffix to the prompt. - E.g. with a suffix `!!!`, it converts a prompt of `test` to `test !!!` - - See PyRIT/pyrit/auxiliary_attacks/gcg for adversarial suffix generation - - Args: - prompt (str): The prompt to be converted. - - Returns: - list[str]: The converted prompts. - """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/superscript_converter.py b/pyrit/prompt_converter/superscript_converter.py index 7d76520267..5ee8bea15b 100644 --- a/pyrit/prompt_converter/superscript_converter.py +++ b/pyrit/prompt_converter/superscript_converter.py @@ -8,8 +8,7 @@ class SuperscriptConverter(WordLevelConverter): """ Converts text to superscript. - Note: - This converter leaves characters that do not have a superscript equivalent unchanged. + This converter leaves characters that do not have a superscript equivalent unchanged. """ _superscript_map = { diff --git a/pyrit/prompt_converter/template_segment_converter.py b/pyrit/prompt_converter/template_segment_converter.py index dfcb848079..a8bebc0577 100644 --- a/pyrit/prompt_converter/template_segment_converter.py +++ b/pyrit/prompt_converter/template_segment_converter.py @@ -15,9 +15,9 @@ class TemplateSegmentConverter(PromptConverter): """ - A PromptConverter that uses a template to randomly split a prompt into segments defined by the template. + Uses a template to randomly split a prompt into segments defined by the template. - This converter is a generalized version of this + This converter is a generalized version of this: https://adversa.ai/blog/universal-llm-jailbreak-chatgpt-gpt-4-bard-bing-anthropic-and-beyond/ """ @@ -27,9 +27,14 @@ def __init__( prompt_template: Optional[SeedPrompt] = None, ): """ + Initializes the converter with the specified target and prompt template. + Args: prompt_template (SeedPrompt, Optional): The prompt template for the conversion. Must have two or more - parameters. If not provided, uses the default tom_and_jerry.yaml template. + parameters. If not provided, uses the default ``tom_and_jerry.yaml`` template. + + Raises: + ValueError: If the template has fewer than two parameters or if any parameter is missing in the template. """ super().__init__() @@ -67,7 +72,7 @@ def input_supported(self, input_type: PromptDataType) -> bool: def output_supported(self, output_type: PromptDataType) -> bool: return output_type == "text" - async def convert_async(self, *, prompt: str, input_type="text") -> ConverterResult: + async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ Converts the given prompt by splitting it into random segments and using them to fill the template parameters. The prompt is split into N segments (where N is the number of template parameters) at random word boundaries. @@ -75,13 +80,13 @@ async def convert_async(self, *, prompt: str, input_type="text") -> ConverterRes Args: prompt (str): The prompt to be converted. - input_type (str): The type of input data. Must be "text". + input_type (PromptDataType): The type of input data. Returns: ConverterResult: The result containing the template filled with prompt segments. Raises: - ValueError: If input_type is not supported. + ValueError: If the input type is not supported. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") @@ -98,10 +103,10 @@ def _split_prompt_into_segments(self, prompt: str) -> list[str]: If there aren't enough words for all parameters, remaining segments will be empty strings. Args: - prompt (str): The prompt to split into segments + prompt (str): The prompt to split into segments. Returns: - list[str]: List of segments, padded with empty strings if needed + list[str]: List of segments, padded with empty strings if needed. """ words = prompt.split() num_splits = min(len(words), self._number_parameters - 1) diff --git a/pyrit/prompt_converter/tense_converter.py b/pyrit/prompt_converter/tense_converter.py index 65e543cb6d..81efd12c90 100644 --- a/pyrit/prompt_converter/tense_converter.py +++ b/pyrit/prompt_converter/tense_converter.py @@ -13,12 +13,18 @@ class TenseConverter(LLMGenericTextConverter): + """ + Converts a conversation to a different tense using an LLM. + + An existing ``PromptChatTarget`` is used to perform the conversion (like Azure OpenAI). + """ + def __init__(self, *, converter_target: PromptChatTarget, tense: str, prompt_template: SeedPrompt = None): """ - Converts a conversation to a different tense + Initializes the converter with the target chat support, tense, and optional prompt template. Args: - converter_target (PromptChatTarget): The target chat support for the conversion which will translate + converter_target (PromptChatTarget): The target chat support for the conversion which will translate. tone (str): The tense the converter should convert the prompt to. E.g. past, present, future. prompt_template (SeedPrompt, Optional): The prompt template for the conversion. diff --git a/pyrit/prompt_converter/text_jailbreak_converter.py b/pyrit/prompt_converter/text_jailbreak_converter.py index 46548fd663..7a70e15045 100644 --- a/pyrit/prompt_converter/text_jailbreak_converter.py +++ b/pyrit/prompt_converter/text_jailbreak_converter.py @@ -7,13 +7,22 @@ class TextJailbreakConverter(PromptConverter): + """ + Uses a jailbreak template to create a prompt. + """ def __init__(self, *, jailbreak_template: TextJailBreak): + """ + Initializes the converter with the specified jailbreak template. + + Args: + jailbreak_template (TextJailBreak): The jailbreak template to use for conversion. + """ self.jail_break_template = jailbreak_template async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Simple converter that uses a jailbreak template to create a prompt + Converts the given prompt using the jailbreak template. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/text_to_hex_converter.py b/pyrit/prompt_converter/text_to_hex_converter.py index 2b625f1451..fc978b0cd6 100644 --- a/pyrit/prompt_converter/text_to_hex_converter.py +++ b/pyrit/prompt_converter/text_to_hex_converter.py @@ -5,7 +5,9 @@ class TextToHexConverter(WordLevelConverter): - """Converts text to a hexadecimal encoded utf-8 string""" + """ + Converts text to a hexadecimal encoded utf-8 string. + """ async def convert_word_async(self, word: str) -> str: return word.encode("utf-8").hex().upper() diff --git a/pyrit/prompt_converter/token_smuggling/ascii_smuggler_converter.py b/pyrit/prompt_converter/token_smuggling/ascii_smuggler_converter.py index f1c2ff5c36..f64aea3634 100644 --- a/pyrit/prompt_converter/token_smuggling/ascii_smuggler_converter.py +++ b/pyrit/prompt_converter/token_smuggling/ascii_smuggler_converter.py @@ -14,8 +14,8 @@ class AsciiSmugglerConverter(SmugglerConverter): Implements encoding and decoding using Unicode Tags. If 'control' is True, the encoded output is wrapped with: - - U+E0001 (start control tag) - - U+E007F (end control tag) + - U+E0001 (start control tag) + - U+E007F (end control tag) Replicates the functionality detailed in the following blog post: https://embracethered.com/blog/posts/2024/hiding-and-finding-text-with-unicode-tags/ @@ -23,9 +23,10 @@ class AsciiSmugglerConverter(SmugglerConverter): def __init__(self, action: Literal["encode", "decode"] = "encode", unicode_tags: bool = False): """ - Initialize the converter with options for encoding/decoding. + Initializes the converter with options for encoding/decoding. Args: + action (Literal["encode", "decode"]): The action to perform. unicode_tags (bool): Whether to add Unicode tags during encoding. """ self.unicode_tags = unicode_tags diff --git a/pyrit/prompt_converter/token_smuggling/base.py b/pyrit/prompt_converter/token_smuggling/base.py index d64984bc9b..2ccfcadbc0 100644 --- a/pyrit/prompt_converter/token_smuggling/base.py +++ b/pyrit/prompt_converter/token_smuggling/base.py @@ -20,23 +20,29 @@ class SmugglerConverter(PromptConverter, abc.ABC): """ def __init__(self, action: Literal["encode", "decode"] = "encode") -> None: + """ + Initializes the converter with options for encoding/decoding. + + Args: + action (Literal["encode", "decode"]): The action to perform. + """ if action not in ["encode", "decode"]: raise ValueError("Action must be either 'encode' or 'decode'") self.action = action async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Convert the prompt by either encoding or decoding it based on the specified action. + Converts the given prompt by either encoding or decoding it based on the specified action. Args: - prompt (str): The prompt to be processed. - input_type (PromptDataType): Type of input; only "text" is supported. + prompt (str): The prompt to be converted. + input_type (PromptDataType): The type of input data. Returns: - ConverterResult: The result containing the output text and its type. + ConverterResult: The result containing the converted text and its type. Raises: - ValueError: If the input type is unsupported. + ValueError: If the input type is not supported. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") @@ -49,11 +55,9 @@ async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text return ConverterResult(output_text=decoded, output_type="text") def input_supported(self, input_type: PromptDataType) -> bool: - """Return True if the input type is 'text'.""" return input_type == "text" def output_supported(self, output_type: PromptDataType) -> bool: - """Return True if the output type is 'text'.""" return output_type == "text" @abc.abstractmethod diff --git a/pyrit/prompt_converter/token_smuggling/sneaky_bits_smuggler_converter.py b/pyrit/prompt_converter/token_smuggling/sneaky_bits_smuggler_converter.py index f928f3f478..9801be31cf 100644 --- a/pyrit/prompt_converter/token_smuggling/sneaky_bits_smuggler_converter.py +++ b/pyrit/prompt_converter/token_smuggling/sneaky_bits_smuggler_converter.py @@ -14,11 +14,11 @@ class SneakyBitsSmugglerConverter(SmugglerConverter): Encodes and decodes text using a bit-level approach. Uses two invisible Unicode characters: - - `zero_char` (default: U+2062) to represent binary 0. - - `one_char` (default: U+2064) to represent binary 1. + - ``zero_char`` (default: U+2062) to represent binary 0. + - ``one_char`` (default: U+2064) to represent binary 1. Replicates functionality detailed in: - - https://embracethered.com/blog/posts/2025/sneaky-bits-and-ascii-smuggler/ + - https://embracethered.com/blog/posts/2025/sneaky-bits-and-ascii-smuggler/ """ def __init__( @@ -28,13 +28,15 @@ def __init__( one_char: Optional[str] = None, ): """ + Initializes the converter with options for encoding/decoding in Sneaky Bits mode. + Args: action (Literal["encode", "decode"]): The action to perform. - zero_char (Optional[str]): Character to represent binary 0 in sneaky_bits mode (default: U+2062). - one_char (Optional[str]): Character to represent binary 1 in sneaky_bits mode (default: U+2064). + zero_char (Optional[str]): Character to represent binary 0 in ``sneaky_bits`` mode (default: U+2062). + one_char (Optional[str]): Character to represent binary 1 in ``sneaky_bits`` mode (default: U+2064). Raises: - ValueError: If an unsupported action or encoding_mode is provided. + ValueError: If an unsupported action or ``encoding_mode`` is provided. """ super().__init__(action=action) self.zero_char = zero_char if zero_char is not None else "\u2062" # Invisible Times @@ -42,11 +44,10 @@ def __init__( def encode_message(self, message: str) -> Tuple[str, str]: """ - Encode the message using Sneaky Bits mode. + Encodes the message using Sneaky Bits mode. - The message is first converted to its UTF-8 byte sequence. Then each byte is represented - as 8 bits, with each bit replaced by an invisible character (self.zero_char for 0 and - self.one_char for 1). + The message is first converted to its UTF-8 byte sequence. Then each byte is represented as 8 bits, + with each bit replaced by an invisible character (``self.zero_char`` for 0 and ``self.one_char`` for 1). Args: message (str): The message to encode. @@ -71,11 +72,10 @@ def encode_message(self, message: str) -> Tuple[str, str]: def decode_message(self, message: str) -> str: """ - Decode the message encoded using Sneaky Bits mode. + Decodes the message encoded using Sneaky Bits mode. - The method filters out only the valid invisible characters (self.zero_char and self.one_char), - groups them into 8-bit chunks, reconstructs each byte, and finally decodes the byte sequence - using UTF-8. + The method filters out only the valid invisible characters (``self.zero_char`` and ``self.one_char``), + groups them into 8-bit chunks, reconstructs each byte, and finally decodes the byte sequence using UTF-8. Args: message (str): The message encoded with Sneaky Bits. diff --git a/pyrit/prompt_converter/token_smuggling/variation_selector_smuggler_converter.py b/pyrit/prompt_converter/token_smuggling/variation_selector_smuggler_converter.py index 3052f20321..a947ab2e25 100644 --- a/pyrit/prompt_converter/token_smuggling/variation_selector_smuggler_converter.py +++ b/pyrit/prompt_converter/token_smuggling/variation_selector_smuggler_converter.py @@ -14,14 +14,14 @@ class VariationSelectorSmugglerConverter(SmugglerConverter): Encodes and decodes text using Unicode Variation Selectors. Each UTF-8 byte is mapped as follows: - - Bytes 0x00-0x0F are mapped to U+FE00-U+FE0F. - - Bytes 0x10-0xFF are mapped to U+E0100-U+E01EF. + - Bytes 0x00-0x0F are mapped to U+FE00-U+FE0F. + - Bytes 0x10-0xFF are mapped to U+E0100-U+E01EF. - If 'embed_in_base' is True, the payload is concatenated with a base character + If ``embed_in_base`` is True, the payload is concatenated with a base character (default: 😊); otherwise, a space separator is inserted. Replicates functionality detailed in: - - https://paulbutler.org/2025/smuggling-arbitrary-data-through-an-emoji/ + - https://paulbutler.org/2025/smuggling-arbitrary-data-through-an-emoji/ Extension: In addition to embedding into a base character, we also support appending invisible variation selectors directly to visible textβ€”enabling mixed @@ -35,17 +35,17 @@ def __init__( embed_in_base: bool = True, ): """ - Initialize the converter with options for encoding/decoding. + Initializes the converter with options for encoding/decoding. Args: action (Literal["encode", "decode"]): The action to perform. - base_char_utf8 (Optional[str]): Base character for variation_selector_smuggler mode (default: 😊). + base_char_utf8 (Optional[str]): Base character for ``variation_selector_smuggler`` mode (default: 😊). embed_in_base (bool): If True, the hidden payload is embedded directly into the base character. If False, a visible separator (space) is inserted between the base and payload. Default is True. Raises: - ValueError: If an unsupported action or encoding_mode is provided. + ValueError: If an unsupported action or ``encoding_mode`` is provided. """ super().__init__(action=action) self.utf8_base_char = base_char_utf8 if base_char_utf8 is not None else "😊" @@ -53,11 +53,13 @@ def __init__( def encode_message(self, message: str) -> Tuple[str, str]: """ - Encode the message using Unicode variation selectors. + Encodes the message using Unicode variation selectors. + The message is converted to UTF-8 bytes, and each byte is mapped to a variation selector: - - 0x00-0x0F => U+FE00 to U+FE0F. - - 0x10-0xFF => U+E0100 to U+E01EF. - If embed_in_base is True, the payload is embedded directly into the base character; + - 0x00-0x0F => U+FE00 to U+FE0F. + - 0x10-0xFF => U+E0100 to U+E01EF. + + If ``embed_in_base`` is True, the payload is embedded directly into the base character; otherwise, a visible separator (a space) is inserted between the base and payload. """ payload = "" @@ -84,7 +86,7 @@ def encode_message(self, message: str) -> Tuple[str, str]: def decode_message(self, message: str) -> str: """ - Decode a message encoded using Unicode variation selectors. + Decodes a message encoded using Unicode variation selectors. The decoder scans the string for variation selectors, ignoring any visible separator. """ bytes_out = bytearray() @@ -116,12 +118,15 @@ def decode_message(self, message: str) -> str: # Extension of Paul Butler's method def encode_visible_hidden(self, visible: str, hidden: str) -> Tuple[str, str]: """ - Combine visible text with hidden text by encoding the hidden text using variation_selector_smuggler mode. + Combines visible text with hidden text by encoding the hidden text using ``variation_selector_smuggler`` mode. + The hidden payload is generated as a composite using the current embedding setting and then appended to the visible text. + Args: visible (str): The visible text. hidden (str): The secret/hidden text to encode. + Returns: Tuple[str, str]: A tuple containing a summary and the combined text. """ @@ -132,11 +137,14 @@ def encode_visible_hidden(self, visible: str, hidden: str) -> Tuple[str, str]: # Extension of Paul Butler's method def decode_visible_hidden(self, combined: str) -> Tuple[str, str]: """ - Extract the visible text and decode the hidden text from a combined string. - It searches for the first occurrence of the base character (self.utf8_base_char) and treats everything + Extracts the visible text and decodes the hidden text from a combined string. + + It searches for the first occurrence of the base character (``self.utf8_base_char``) and treats everything from that point on as the hidden payload. + Args: combined (str): The combined text containing visible and hidden parts. + Returns: Tuple[str, str]: A tuple with the visible text and the decoded hidden text. """ diff --git a/pyrit/prompt_converter/tone_converter.py b/pyrit/prompt_converter/tone_converter.py index 2e3037d1aa..733bebc79d 100644 --- a/pyrit/prompt_converter/tone_converter.py +++ b/pyrit/prompt_converter/tone_converter.py @@ -13,12 +13,18 @@ class ToneConverter(LLMGenericTextConverter): + """ + Converts a conversation to a different tone using an LLM. + + An existing ``PromptChatTarget`` is used to perform the conversion (like Azure OpenAI). + """ + def __init__(self, *, converter_target: PromptChatTarget, tone: str, prompt_template: SeedPrompt = None): """ - Converts a conversation to a different tone + Initializes the converter with the target chat support, tone, and optional prompt template. Args: - converter_target (PromptChatTarget): The target chat support for the conversion which will translate + converter_target (PromptChatTarget): The target chat support for the conversion which will translate. tone (str): The tone for the conversation. E.g. upset, sarcastic, indifferent, etc. prompt_template (SeedPrompt, Optional): The prompt template for the conversion. diff --git a/pyrit/prompt_converter/toxic_sentence_generator_converter.py b/pyrit/prompt_converter/toxic_sentence_generator_converter.py index c19429db10..f50225efff 100644 --- a/pyrit/prompt_converter/toxic_sentence_generator_converter.py +++ b/pyrit/prompt_converter/toxic_sentence_generator_converter.py @@ -19,8 +19,9 @@ class ToxicSentenceGeneratorConverter(LLMGenericTextConverter): """ - A PromptConverter that generates toxic sentence starters using an LLM via an - existing PromptTarget. + Generates toxic sentence starters using an LLM. + + An existing ``PromptChatTarget`` is used to perform the conversion (like Azure OpenAI). Based on Project Moonshot's attack module that generates toxic sentences to test LLM safety guardrails: @@ -31,10 +32,10 @@ def __init__(self, *, converter_target: PromptChatTarget, prompt_template: Optio """ Initializes the converter with a specific target and template. - Parameters: + Args: converter_target (PromptChatTarget): The endpoint that converts the prompt. prompt_template (SeedPrompt): The seed prompt template to use. If not provided, - defaults to the toxic_sentence_generator.yaml. + defaults to the ``toxic_sentence_generator.yaml``. """ # set to default strategy if not provided @@ -50,41 +51,21 @@ def __init__(self, *, converter_target: PromptChatTarget, prompt_template: Optio async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Converts a seed prompt into a toxic sentence starter. + Converts the given prompt into a toxic sentence starter. - Parameters: - prompt (str): The prompt to convert. - input_type (PromptDataType, Optional): The data type of the input prompt. - Defaults to "text". + Args: + prompt (str): The prompt to be converted. + input_type (PromptDataType): The type of input data. Returns: - ConverterResult: The result of the conversion, containing the toxic sentence - starter. + ConverterResult: The conversion result containing the toxic sentence starter. """ # Add the prompt to _prompt_kwargs before calling the base method self._prompt_kwargs["prompt"] = prompt return await super().convert_async(prompt=prompt, input_type=input_type) def input_supported(self, input_type: PromptDataType) -> bool: - """ - Checks if the input type is supported by this converter. - - Parameters: - input_type (PromptDataType): The data type to check. - - Returns: - bool: True if the input type is supported, False otherwise. - """ return input_type == "text" def output_supported(self, output_type: PromptDataType) -> bool: - """ - Checks if the output type is supported by this converter. - - Parameters: - output_type (PromptDataType): The data type to check. - - Returns: - bool: True if the output type is supported, False otherwise. - """ return output_type == "text" diff --git a/pyrit/prompt_converter/translation_converter.py b/pyrit/prompt_converter/translation_converter.py index a68bfe2bb9..64ca06e226 100644 --- a/pyrit/prompt_converter/translation_converter.py +++ b/pyrit/prompt_converter/translation_converter.py @@ -28,6 +28,10 @@ class TranslationConverter(PromptConverter): + """ + Translates prompts into different languages using an LLM. + """ + def __init__( self, *, @@ -38,12 +42,14 @@ def __init__( max_wait_time_in_seconds: int = 60, ): """ - Initializes a TranslationConverter object. + Initializes the converter with the target chat support, language, and optional prompt template. Args: - converter_target (PromptChatTarget): The target chat support for the conversion which will translate + converter_target (PromptChatTarget): The target chat support for the conversion which will translate. language (str): The language for the conversion. E.g. Spanish, French, leetspeak, etc. prompt_template (SeedPrompt, Optional): The prompt template for the conversion. + max_retries (int): Maximum number of retries for the conversion. + max_wait_time_in_seconds (int): Maximum wait time in seconds between retries. Raises: ValueError: If the language is not provided. @@ -72,11 +78,16 @@ def __init__( async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Generates variations of the input prompt using the converter target. - Parameters: - prompt (str): prompt to convert - Return: - (ConverterResult): result generated by the converter target + Converts the given prompt by translating it using the converter target. + + Args: + prompt (str): The prompt to be converted. + + Returns: + ConverterResult: The result containing the generated version of the prompt. + + Raises: + ValueError: If the input type is not supported. """ conversation_id = str(uuid.uuid4()) diff --git a/pyrit/prompt_converter/unicode_confusable_converter.py b/pyrit/prompt_converter/unicode_confusable_converter.py index 368b32574d..b34b560df3 100644 --- a/pyrit/prompt_converter/unicode_confusable_converter.py +++ b/pyrit/prompt_converter/unicode_confusable_converter.py @@ -17,8 +17,8 @@ class UnicodeConfusableConverter(PromptConverter): """ - A PromptConverter that applies substitutions to words in the prompt - to test adversarial textual robustness by replacing characters with visually similar ones. + Applies substitutions to words in the prompt to test adversarial textual robustness + by replacing characters with visually similar ones. """ def __init__( @@ -28,17 +28,21 @@ def __init__( deterministic: bool = False, ): """ - Initializes the UnicodeConfusableConverter. + Initializes the converter with the specified source package for homoglyph generation. Args: - source_package: The package to use for homoglyph generation. Can be either "confusable_homoglyphs" - which can be found here: https://pypi.org/project/confusable-homoglyphs/ or "confusables" which can be - found here: https://pypi.org/project/confusables/. "Confusable_homoglyphs" is used by default as it is - more regularly maintained and up to date with the latest Unicode-provided confusables found here: - https://www.unicode.org/Public/security/latest/confusables.txt. However, "confusables" - provides additional methods of matching characters (not just Unicode list), so each character - has more possible substitutions. - deterministic: This argument is for unittesting only. + source_package (Literal["confusable_homoglyphs", "confusables"]): + The package to use for homoglyph generation. + + Can be either: + - "confusable_homoglyphs" (https://pypi.org/project/confusable-homoglyphs/): + Used by default as it is more regularly maintained and up to date with the latest + Unicode-provided confusables found here: + https://www.unicode.org/Public/security/latest/confusables.txt + - "confusables" (https://pypi.org/project/confusables/): + Provides additional methods of matching characters (not just Unicode list), + so each character has more possible substitutions. + deterministic (bool): This argument is for unittesting only. """ if source_package not in ["confusable_homoglyphs", "confusables"]: raise ValueError( @@ -55,9 +59,13 @@ async def convert_async(self, *, prompt: str, input_type="text") -> ConverterRes Args: prompt (str): The prompt to be converted. - input_type (str): The type of input (should be "text"). + input_type (PromptDataType): The type of input data. + Returns: ConverterResult: The result containing the prompt with confusable subsitutions applied. + + Raises: + ValueError: If the input type is not supported. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") @@ -75,6 +83,7 @@ def _get_homoglyph_variants(self, word: str) -> list: Args: word (str): The word to find homoglyphs for. + Returns: list: A list of homoglyph variants for the word. """ @@ -98,6 +107,7 @@ def _generate_perturbed_prompts(self, prompt: str) -> str: Args: prompt (str): The original prompt. + Returns: str: A perturbed prompt with character-level substitutions. """ @@ -127,10 +137,11 @@ def _generate_perturbed_prompts(self, prompt: str) -> str: def _confusable(self, char: str) -> str: """ - Pick a confusable character for the given character using the "confusables" package. + Picks a confusable character for the given character using the "confusables" package. Args: char (str): The character to be replaced. + Returns: str: The confusable character to replace the given character. """ diff --git a/pyrit/prompt_converter/unicode_replacement_converter.py b/pyrit/prompt_converter/unicode_replacement_converter.py index 98351260d8..10af862073 100644 --- a/pyrit/prompt_converter/unicode_replacement_converter.py +++ b/pyrit/prompt_converter/unicode_replacement_converter.py @@ -8,7 +8,9 @@ class UnicodeReplacementConverter(WordLevelConverter): - """Simple converter that returns the unicode representation of the prompt.""" + """ + Converts a prompt to its unicode representation. + """ def __init__( self, @@ -20,7 +22,8 @@ def __init__( regex: Optional[Union[str, re.Pattern]] = None, ): """ - Initialize the converter. + Initializes the converter with the specified selection parameters. + This class allows for selection of words to convert based on various criteria. Only one selection parameter may be provided at a time (indices, keywords, proportion, or regex). If no selection parameter is provided, all words will be converted. diff --git a/pyrit/prompt_converter/unicode_sub_converter.py b/pyrit/prompt_converter/unicode_sub_converter.py index 0664acae7e..df198d5881 100644 --- a/pyrit/prompt_converter/unicode_sub_converter.py +++ b/pyrit/prompt_converter/unicode_sub_converter.py @@ -6,12 +6,16 @@ class UnicodeSubstitutionConverter(PromptConverter): + """ + Encodes the prompt using any unicode starting point. + """ + def __init__(self, *, start_value=0xE0000): self.startValue = start_value async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Simple converter that just encodes the prompt using any unicode starting point. + Converts the given prompt by encoding it using any unicode starting point. Default is to use invisible flag emoji characters. """ if not self.input_supported(input_type): diff --git a/pyrit/prompt_converter/url_converter.py b/pyrit/prompt_converter/url_converter.py index 00f51f51f7..86b2e80c8d 100644 --- a/pyrit/prompt_converter/url_converter.py +++ b/pyrit/prompt_converter/url_converter.py @@ -8,10 +8,13 @@ class UrlConverter(PromptConverter): + """ + Converts a prompt to a URL-encoded string. + """ async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Simple converter that just URL encodes the prompt + Converts the given prompt into a URL-encoded string. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") diff --git a/pyrit/prompt_converter/variation_converter.py b/pyrit/prompt_converter/variation_converter.py index 7a4dea498d..1d20857a42 100644 --- a/pyrit/prompt_converter/variation_converter.py +++ b/pyrit/prompt_converter/variation_converter.py @@ -26,7 +26,19 @@ class VariationConverter(PromptConverter): + """ + Generates variations of the input prompts using the converter target. + """ + def __init__(self, *, converter_target: PromptChatTarget, prompt_template: SeedPrompt = None): + """ + Initializes the converter with the specified target and prompt template. + + Args: + converter_target (PromptChatTarget): The target to which the prompt will be sent for conversion. + prompt_template (SeedPrompt, optional): The template used for generating the system prompt. + If not provided, a default template will be used. + """ self.converter_target = converter_target # set to default strategy if not provided @@ -44,11 +56,16 @@ def __init__(self, *, converter_target: PromptChatTarget, prompt_template: SeedP async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: """ - Generates variations of the input prompts using the converter target. - Parameters: - prompts: list of prompts to convert - Return: - target_responses: list of prompt variations generated by the converter target + Converts the given prompt by generating variations of it using the converter target. + + Args: + prompt (str): The prompt to be converted. + + Returns: + ConverterResult: The result containing the generated variations. + + Raises: + ValueError: If the input type is not supported. """ if not self.input_supported(input_type): raise ValueError("Input type not supported") @@ -90,6 +107,7 @@ async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text @pyrit_json_retry async def send_variation_prompt_async(self, request): + """Sends the prompt request to the converter target and retrieves the response.""" response = await self.converter_target.send_prompt_async(prompt_request=request) response_msg = response.get_value() diff --git a/pyrit/prompt_converter/word_level_converter.py b/pyrit/prompt_converter/word_level_converter.py index fb63592568..2644bdcd85 100644 --- a/pyrit/prompt_converter/word_level_converter.py +++ b/pyrit/prompt_converter/word_level_converter.py @@ -29,7 +29,8 @@ def __init__( regex: Optional[Union[str, re.Pattern]] = None, ): """ - Initialize the converter. + Initializes the converter with the specified selection parameters. + This class allows for selection of words to convert based on various criteria. Only one selection parameter may be provided at a time (indices, keywords, proportion, or regex). If no selection parameter is provided, all words will be converted. @@ -58,7 +59,7 @@ def __init__( self._regex = regex or ".*" def _select_word_indices(self, words: List[str]) -> List[int]: - """Return indices of words to be converted based on the selection criteria.""" + """Returns indices of words to be converted based on the selection criteria.""" if not words: return [] @@ -85,17 +86,40 @@ def _select_word_indices(self, words: List[str]) -> List[int]: @abc.abstractmethod async def convert_word_async(self, word: str) -> str: + """ + Converts a single word into the target format supported by the converter. + + Args: + word (str): The word to be converted. + + Returns: + str: The converted word. + """ pass def validate_input(self, prompt: str) -> None: - """Validate the input before processing (can be overridden by subclasses)""" + """Validates the input before processing (can be overridden by subclasses).""" pass def join_words(self, words: list[str]) -> str: - """Provide a way for subclasses to override the default behavior of joining words.""" + """Provides a way for subclasses to override the default behavior of joining words.""" return " ".join(words) async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text") -> ConverterResult: + """ + Converts the given prompt into the target format supported by the converter. + + Args: + prompt (str): The prompt to be converted. + input_type (PromptDataType): The type of input data. + + Returns: + ConverterResult: The result containing the converted output and its type. + + Raises: + TypeError: If the prompt is None. + ValueError: If the input type is not supported. + """ if prompt is None: raise TypeError("Prompt cannot be None") diff --git a/pyrit/prompt_converter/zalgo_converter.py b/pyrit/prompt_converter/zalgo_converter.py index 133f6f4ea1..d26dcead9f 100644 --- a/pyrit/prompt_converter/zalgo_converter.py +++ b/pyrit/prompt_converter/zalgo_converter.py @@ -16,7 +16,9 @@ class ZalgoConverter(WordLevelConverter): - """Converts text into cursed Zalgo text using combining Unicode marks.""" + """ + Converts text into cursed Zalgo text using combining Unicode marks. + """ def __init__( self, @@ -29,7 +31,8 @@ def __init__( regex: Optional[Union[str, re.Pattern]] = None, ): """ - Initialize the converter. + Initializes the converter with the specified selection parameters. + This class allows for selection of words to convert based on various criteria. Only one selection parameter may be provided at a time (indices, keywords, proportion, or regex). If no selection parameter is provided, all words will be converted. diff --git a/pyrit/prompt_converter/zero_width_converter.py b/pyrit/prompt_converter/zero_width_converter.py index 37552e1998..0663add1dd 100644 --- a/pyrit/prompt_converter/zero_width_converter.py +++ b/pyrit/prompt_converter/zero_width_converter.py @@ -7,10 +7,10 @@ class ZeroWidthConverter(PromptConverter): """ - A PromptConverter that injects zero-width spaces between characters - in the provided text to bypass content safety mechanisms. + Injects zero-width spaces between characters in the provided text to bypass content safety mechanisms. """ + #: Constant for zero-width space character. ZERO_WIDTH_SPACE = "\u200b" def input_supported(self, input_type: PromptDataType) -> bool: @@ -25,9 +25,13 @@ async def convert_async(self, *, prompt: str, input_type: PromptDataType = "text Args: prompt (str): The prompt to be converted. + input_type (PromptDataType): The type of input data. Returns: ConverterResult: The result containing the modified prompt. + + Raises: + ValueError: If the input type is not supported. """ if not self.input_supported(input_type): raise ValueError("Only 'text' input type is supported.")