Skip to content

[BREAKING] FIX: Keep a private copy's endpoint and credentials the same as its source's - #3984

Open
varunj-msft wants to merge 3 commits into
microsoft:mainfrom
varunj-msft:varunj-msft/13178-Copy-Keeps-Connection
Open

varunj-msft wants to merge 3 commits into
microsoft:mainfrom
varunj-msft:varunj-msft/13178-Copy-Keeps-Connection

Conversation

@varunj-msft

Copy link
Copy Markdown
Contributor

Description

Problem. A private copy is built from a registered target or converter through the API: source on POST /api/targets and POST /api/converters, converter specs in previews, and converter settings in a technique edit. Today a copy can set its own endpoint, auth_mode, api_key or headers, or for the Azure Speech converters its region, key or resource ID, while still using the source's credential. So a key set up for one endpoint can be used somewhere else.

Change.

  • Each class names the parameters that choose where its requests go and how they sign in, in COPY_LOCKED_PARAMETER_NAMES: endpoint, auth_mode, api_key, headers and httpx_client_kwargs on OpenAITarget (so every OpenAI target), and azure_speech_region, azure_speech_key and azure_speech_resource_id on both Speech converters. The parameter catalog marks them with copy_locked.
  • recreate_instance refuses them for API input with any value, even the source's own, so a copy can't be used to check a guess at the stored key. The 400 names the fields.
  • The converter settings dialog (a copy's temporary settings) no longer shows locked fields and says the connection settings come from the source. It also leaves them out of settings carried over from before this change, which would otherwise be refused.

What stays the same. Settings like temperature, model, voice, and the target a converter uses can still change on a copy, since each of those keeps every key with its own endpoint. Chat's target copies only change temperature, so they work as before. Creating a target or converter, the create dialog, and copies built in Python (external_input=False) are unchanged.

Breaking. An API call that sets one of these fields on a copy now gets a 400. A saved run whose technique edit set one of these fields on a converter copy can't be resumed or run again, and gets the same 400. To use a different endpoint or credential, create a new target or converter.

#3809 landed first, so this PR also updates its test: clearing api_key or headers on a copy of a no-key target is now refused instead of being ignored.

Tests and Documentation

  • tests/unit/registry/test_converter_registry.py: a copy of either Speech converter can't set the region, key or resource ID, including the source's own region, and can still change the voice; a trusted Python copy can still change the region.
  • tests/unit/backend/test_temporary_components.py: a target copy can't set endpoint (a different one or the source's own), api_key, auth_mode or headers, and a copy of a no-key target can't clear its key or headers.
  • tests/unit/backend/test_converter_service.py: the same refusal through converter create (registered and not) and preview, nothing gets registered, and a copy's editable settings leave out the locked ones. The preview's conversion is mocked.
  • tests/unit/backend/test_technique_edit_service.py: the same refusal for converter settings in a technique edit.
  • tests/unit/registry/test_resolution.py, tests/unit/models/test_parameter.py: copy_locked comes from the class's list and is serialized.
  • frontend/src/components/Registry/CreateConverterDialog.test.tsx: editing a copy hides the locked field and sends only the changed voice, settings carried over from before lose the locked field, and creating a converter still shows it.
  • Removing the check makes every new refusal test fail; removing each dialog filter fails its test.
  • doc/code/registry/0_registry.md: copies keep these settings, and how a class declares them.
  • pytest -n 4 --dist=loadfile tests/unit/backend tests/unit/registry tests/unit/models tests/unit/setup tests/unit/converter/test_azure_speech_converter.py tests/unit/converter/test_azure_speech_text_converter.py tests/unit/prompt_target/target/test_openai_chat_target.py tests/unit/prompt_target/target/test_openai_target_auth.py: 8,142 passed, 9 skipped, 4 xfailed.
  • npx jest src/components/Registry/CreateConverterDialog.test.tsx: 92 passed. npx tsc --noEmit: passed. ESLint on the changed frontend files: passed. pre-commit (ruff, ty) on the changed files: passed.

No JupyText changes.

A copy built from a registered target or converter through the API could set
its own endpoint, auth mode, key or headers, or for the Azure Speech converters
its region, key or resource ID, while keeping the source's credential. Those
settings are now locked on copies: classes declare them, the registry refuses
them for API input with any value, and the converter settings dialog no longer
offers them or carries them over from earlier settings. Settings like
temperature or the voice can still change.
Copilot AI balanced review requested due to automatic review settings November 5, 2026 15:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues were identified in the changes restricting connection-setting overrides for API-created private copies.

No open issues 🎉

What changed in this PR

This PR prevents API-created private copies of OpenAI targets and Azure Speech converters from overriding connection settings, keeping source credentials tied to their configured destinations. New component creation and trusted Python copies remain unchanged.

Changes:

  • Adds copy_locked parameter metadata and rejects locked overrides from external input, including unchanged or null values.
  • Hides locked fields in temporary converter settings and removes legacy overrides before submission.
  • Adds regression coverage and documents the breaking API behavior, including the updated no-key copy behavior from #3809.
File Description
tests/​unit/​registry/​test_resolution.py Tests class-declared copy-lock metadata.
tests/​unit/​registry/​test_converter_registry.py Tests Speech copy restrictions and permitted overrides.
tests/​unit/​models/​test_parameter.py Checks serialization of copy_locked.
tests/​unit/​backend/​test_temporary_components.py Tests target connection locks, including no-key copies.
tests/​unit/​backend/​test_technique_edit_service.py Tests locked converter overrides in technique edits.
tests/​unit/​backend/​test_converter_service.py Tests restrictions across creation, preview, and editable settings.
pyrit/​registry/​resolution.py Derives copy-lock metadata from component declarations.
pyrit/​registry/​registry.py Rejects externally supplied locked overrides.
pyrit/​prompt_target/​openai/​openai_target.py Declares locked OpenAI connection parameters.
pyrit/​prompt_target/​common/​prompt_target.py Defines the target copy-lock declaration.
pyrit/​models/​parameter.py Adds the serialized copy_locked field.
pyrit/​converter/​converter.py Defines the converter copy-lock declaration.
pyrit/​converter/​azure_speech_text_to_audio_converter.py Locks Speech region, key, and resource ID.
pyrit/​converter/​azure_speech_audio_to_text_converter.py Locks Speech region, key, and resource ID.
pyrit/​backend/​services/​converter_service.py Excludes locked parameters from editable instance settings.
frontend/​src/​types/​index.ts Adds frontend typing for copy_locked.
frontend/​src/​components/​Registry/​CreateConverterDialog.tsx Hides locked settings and filters legacy overrides.
frontend/​src/​components/​Registry/​CreateConverterDialog.test.tsx Tests copy filtering and unchanged creation controls.
doc/​code/​registry/​0_registry.md Documents connection locks and component declarations.

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants