Skip to content

BUG: AzureBlobStorageTarget returns a URL with // when the container URL ends with a slash #3495

Open

Description

Describe the bug

AzureBlobStorageTarget._send_prompt_to_target_async builds the returned URL by appending to the configured container URL:

blob_url = self._container_url + "/" + file_name

When container_url (or AZURE_STORAGE_ACCOUNT_CONTAINER_URL) ends with /, for example https://acct.blob.core.windows.net/xpia/, the URL gets // before the file name. The upload goes through _parse_url, which ignores the empty segment, so the blob is uploaded as doc.txt. The returned URL .../xpia//doc.txt names a blob /doc.txt, which does not exist, so anything that uses the returned URL (for example the XPIA workflows) points at a missing blob.

With a folder prefix and a trailing slash (.../xpia/folder/), _parse_url returns the prefix folder/, so the blob itself is uploaded as folder//doc.txt instead of folder/doc.txt.

Steps/Code to Reproduce

import asyncio
from unittest.mock import AsyncMock, MagicMock, patch

from pyrit.setup import IN_MEMORY, initialize_pyrit_async


async def main():
    await initialize_pyrit_async(memory_db_type=IN_MEMORY)

    from pyrit.models import Message, MessagePiece
    from pyrit.prompt_target import AzureBlobStorageTarget

    for container_url in ["https://acct.blob.core.windows.net/xpia/", "https://acct.blob.core.windows.net/xpia/folder/"]:
        target = AzureBlobStorageTarget(container_url=container_url, sas_token="sv=test")
        client = AsyncMock()
        client.get_blob_client = MagicMock(return_value=AsyncMock(exists=AsyncMock(return_value=False)))
        piece = MessagePiece(role="user", original_value="payload", prompt_metadata={"file_name": "doc.txt"})

        with patch("azure.storage.blob.aio.ContainerClient.from_container_url", return_value=client):
            response = await target.send_prompt_async(message=Message(message_pieces=[piece]))

        print("container_url:", container_url)
        print("  uploaded blob:", client.get_blob_client.call_args.kwargs["blob"])
        print("  returned URL :", response[0].get_value())


asyncio.run(main())

Expected Results

The trailing slash makes no difference:

container_url: https://acct.blob.core.windows.net/xpia/
  uploaded blob: doc.txt
  returned URL : https://acct.blob.core.windows.net/xpia/doc.txt
container_url: https://acct.blob.core.windows.net/xpia/folder/
  uploaded blob: folder/doc.txt
  returned URL : https://acct.blob.core.windows.net/xpia/folder/doc.txt

Actual Results

The returned URL does not match the uploaded blob, and the prefixed blob gets an empty path segment:

container_url: https://acct.blob.core.windows.net/xpia/
  uploaded blob: doc.txt
  returned URL : https://acct.blob.core.windows.net/xpia//doc.txt
container_url: https://acct.blob.core.windows.net/xpia/folder/
  uploaded blob: folder//doc.txt
  returned URL : https://acct.blob.core.windows.net/xpia/folder//doc.txt

Screenshots

N/A

Versions

  • OS: Windows 11
  • Python version: 3.11.9
  • PyRIT version: installed from main (eeb9ba9) in editable mode
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions