Skip to content

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

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

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

Conversation

@varunj-msft

@varunj-msft varunj-msft commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Description

Part 17 of saving Registry entries across restarts. It sits on main on its own and can merge any time; parts 18 (#4069) and 19 (#4070) build on it.

Design: 4.4 Credentials. Where every part fits: PR map.

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. A copy could 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 could be used somewhere else. Converter copies send requests today. Target copies with these overrides are only built for a dry run today, so for targets this is a deliberate rule rather than a fix for a current leak: a copy keeps its source's connection. The personal target copies in #4024 will send requests, and the rule is in place before they do.

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

@behnam-o Behnam (behnam-o) self-assigned this Nov 9, 2026
@varunj-msft varunj-msft changed the title [BREAKING] FIX: Keep a private copy's endpoint and credentials the same as its source's [BREAKING] FIX: Keep a private copy's endpoint and credentials the same as its source's (part 17) Nov 9, 2026

@behnam-o Behnam (behnam-o) 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.

The mechanism is clean: components declare the settings a copy can't change, the registry enforces them for API input, and the UI only hides them. I traced every API path that builds a copy: target create from a source, converter create and preview, and technique edits. All of them enforce the lock, and the current lists cover every copyable class that holds credentials.

I don't think the risk in the description is reachable today, though:

So this reads as a deliberate rule ("a copy always uses its source's connection") rather than a fix for an existing leak. Could the description and the [BREAKING] rationale say that?

Comment thread pyrit/converter/converter.py Resolved
Comment thread doc/code/registry/0_registry.md Resolved
# Conflicts:
#	tests/unit/backend/test_temporary_components.py

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.

3 participants