Skip to content

BUG: make_request_and_raise_if_error_async drops repeated query parameters from the endpoint URL #3510

Closed

Description

Describe the bug

make_request_and_raise_if_error_async in pyrit/common/net_utility.py keeps only the first value of a query parameter that appears more than once in endpoint_uri. It reads the URL's query with extract_url_parameters, which flattens parse_qs to a dict[str, str] using v[0], merges extra_url_parameters into that dict, and sends the dict to httpx as params. So https://api.example.com/search?tag=a&tag=b is requested as https://api.example.com/search?tag=a, and tag=b is silently dropped.

Repeated keys are a common way to pass a list (?id=1&id=2, ?fields=a&fields=b), so a request to such an endpoint silently asks for something different from what the caller wrote. The function is used by AzureMLChatTarget, PromptShieldTarget, HackAPromptTarget and the remote dataset image downloads, which pass the URL they are given. This happens with and without extra_url_parameters.

Steps/Code to Reproduce

import asyncio

import respx

from pyrit.common.net_utility import make_request_and_raise_if_error_async


@respx.mock
async def main():
    route = respx.get("https://api.example.com/search").respond(200)
    for url, extra in [
        ("https://api.example.com/search?tag=a&tag=b", None),
        ("https://api.example.com/search?tag=a&tag=b&api-version=1", {"api-version": "2"}),
    ]:
        await make_request_and_raise_if_error_async(endpoint_uri=url, method="GET", extra_url_parameters=extra)
        print("endpoint_uri:", url, "extra:", extra)
        print("  sent       :", route.calls.last.request.url)


asyncio.run(main())

Expected Results

Every query parameter in the URL is sent, and extra_url_parameters still replaces the keys it sets:

endpoint_uri: https://api.example.com/search?tag=a&tag=b extra: None
  sent       : https://api.example.com/search?tag=a&tag=b
endpoint_uri: https://api.example.com/search?tag=a&tag=b&api-version=1 extra: {'api-version': '2'}
  sent       : https://api.example.com/search?tag=a&tag=b&api-version=2

Actual Results

Only the first tag value is sent:

endpoint_uri: https://api.example.com/search?tag=a&tag=b extra: None
  sent       : https://api.example.com/search?tag=a
endpoint_uri: https://api.example.com/search?tag=a&tag=b&api-version=1 extra: {'api-version': '2'}
  sent       : https://api.example.com/search?tag=a&api-version=2

Screenshots

N/A

Versions

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

Metadata

Metadata

Assignees

No one assigned

    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