Skip to content

FEAT: Save converter copies (part 18) - #4069

Open
varunj-msft wants to merge 363 commits into
microsoft:mainfrom
varunj-msft:varunj-msft/13178-Save-Converter-Copies
Open

varunj-msft wants to merge 363 commits into
microsoft:mainfrom
varunj-msft:varunj-msft/13178-Save-Converter-Copies

Conversation

@varunj-msft

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

Copy link
Copy Markdown
Contributor

Description

Part 18 of saving Registry entries across restarts. A side branch stacked on part 13 (#4052), with part 17 (#3984) merged in as the first commit; nothing builds on it. This PR's own changes: varunj-msft/PyRIT@c1f28b9...varunj-msft/13178-Save-Converter-Copies

Design: 4.7 Editing and deleting, 4.5 Entries that use other entries. Where every part fits: PR map.

This is one of the changes for saving Registry entries across restarts. It needs #3984, which keeps a copy's endpoint and credentials the same as its source's. Until the earlier PRs merge, the diff here also shows their changes.

Save to registry on a converter's changed settings, in chat or in a technique edit, made a Temporary copy that was gone after a restart. Copies can now be saved, and a saved copy is rebuilt from the converter it copies at every start.

  • A converter created with source and "persist": true is saved as a recipe that names the converter it copies and holds only the changed settings. At every start it's rebuilt from that converter as it is then, after it, so it keeps where that converter sends requests and how it signs in.
  • Only the changes are checked: they can't hold a credential or name a Temporary entry (typed 400). A reference the changes clear is saved as the entry its default names, as for a new converter, so a Temporary default is refused too. A copy of a Temporary converter can't be saved, since that converter is gone after a restart. A copy can't change its source's connection settings at all, saved or not, and that refusal now comes before the credential and Temporary-entry checks on the changes.
  • Restore checks a copy's saved changes the same way, waits for the converter it copies, and keeps who saved it. A copy whose converter is gone, or is now a different class, fails to restore and says why.
  • The Save to registry dialog saves by default, follows the saved-name rule, and offers Create as Temporary with the reason when the copy can't be saved. A taken name shows on the name field.
  • The backend README and the GUI doc cover saved copies.

What stays the same: a copy made with Create as Temporary is Temporary, as before, and a copy still can't change its source's connection settings (#3984). Target copies are unchanged.

Screenshots: Save to registry for a copy of a saved converter, the saved copy in the Converters table, and a copy of a Temporary converter offered as Temporary with the reason.
copies_1_save_copy_dialog
copies_2_saved_copy_row
copies_3_copy_of_temporary_offers_temporary

Not covered:

# Conflicts:
#	pyrit/registry/registry_recipe_restore.py
# Conflicts:
#	pyrit/registry/registry_recipe_restore.py
# Conflicts:
#	tests/unit/registry/test_instance_registry.py
…entials

The Azure Speech converters and the Azure Content Safety scorer read this
server's key when theirs is empty, or else sign in with its Azure identity, and
no value turns that off. They now declare that key as a server fallback, so a
saved one is refused when it's saved or restored, as saved targets already are.
The audio scorers transcribe with this server's Speech settings, and the local
refusal classifier scorer can load its model from a folder on this server or
download it with this server's Hugging Face sign-in, so those can't be saved.

A class that declares no auth modes now supports only api_key in the saved-entry
check, as the saveability check already assumed, so a recipe can't pass by
adding an auth_mode the class doesn't take.
Only targets can be saved through the API so far. The steps that turn off the
server values an entry would read, refuse what it still can't hold or a
Temporary entry it names, write its recipe, register it, and delete the recipe
again if registering fails move from the target service into a shared module,
along with the route helpers for the owner and the not-saveable problem. The
origin enum becomes RegistryEntryOrigin, read by registry_entry_origin, so
converters and scorers can use the same pieces. Targets behave as before.
POST /api/converters and POST /api/scorers take "persist": true, as
POST /api/targets does: the entry is built the way restore builds it, saved
under registry_recipes_source with the signed-in user, and restored at every
start. An entry that would use a Temporary target is refused, as is one that
would use this server's credentials. A converter that would keep an uploaded
file, or a copy made with source, can't be saved yet.

Each listed converter and scorer carries its origin, and the converters and
scorers the startup scripts register are marked as coming from configuration.
A saved converter can't be removed through the API yet, since it would come
back from its file at the next start.
The converter dialog only made Temporary converters, wherever it opened: the
Converters tab, the converter panel in chat, and New converter in the technique
editor. It now saves the converter by default under a suggested name such as
base64_converter, which keeps clear of the names the startup initializers use.
A converter type that can't be saved, a converter with an uploaded file, one
that uses a Temporary target, or a save the server refused is offered as
Temporary instead, with the reason. A taken name shows on the name field.
GET /api/converters/types says why a type can't be saved.

The Converters and Scorers tables label each entry Saved, From configuration,
or Temporary, and a saved converter's remove button is disabled with the
reason. Save to registry on a converter's changed settings says the copy is
Temporary. The origin badge and its labels move to shared names.
# Conflicts:
#	pyrit/backend/services/runtime_lifecycle.py
#	tests/unit/backend/test_runtime_lifecycle.py
# Conflicts:
#	pyrit/setup/initializers/tools.py
#	tests/unit/backend/test_scenario_run_service.py
#	tests/unit/backend/test_scenario_suite_execution_service.py
#	tests/unit/registry/test_attack_technique_registry.py
#	tests/unit/registry/test_instance_registry.py
#	tests/unit/registry/test_registry_recipe_restore.py
The runtime status keeps its short summary. The restore log now also lists each field's full location
and message, escaped, without the stored values, and with URL credentials removed. Credentials are
removed before a location is shortened, so the summary can't keep part of one either.
# Conflicts:
#	pyrit/registry/registry_recipe_storage.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