Skip to content

FEAT: Show who saved each target and list only your own (part 10) - #4028

Open
varunj-msft wants to merge 332 commits into
microsoft:mainfrom
varunj-msft:varunj-msft/13178-Show-Who-Saved
Open

varunj-msft wants to merge 332 commits into
microsoft:mainfrom
varunj-msft:varunj-msft/13178-Show-Who-Saved

Conversation

@varunj-msft

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

Copy link
Copy Markdown
Contributor

Description

Part 10 of saving Registry entries across restarts. Stacked on part 9 (#4027); part 11 (#4050) builds on it. This PR's own changes: varunj-msft/PyRIT@varunj-msft/13178-Save-From-Registry...varunj-msft/13178-Show-Who-Saved

Design: 4.4 Credentials (Owners). Where every part fits: PR map.

This is one of the changes for saving Registry entries across restarts. It builds on #4027, which saves targets from the Registry. Until the earlier PRs merge, the diff here also shows their changes.

Problem. With sign-in on, everyone's saved targets land in one Targets list, and nothing says who saved which, so users can't find their own. Frederic raised this on the design gist and suggested Mine, Official and All views.

Change.

  • The server already records who saved a target in its saved file. It now keeps that on the running target, both when the target is saved and when it's restored at startup or apply. Restore does the same for converters and scorers, for when they can be saved.
  • Each listed target says who saved it (saved_by, the name) and whether that's the signed-in user asking (saved_by_me). The server compares the object IDs, so no ID leaves it. Capability detection returns both too, so the caption stays after a check.
  • The Targets table shows "Saved by you" or "Saved by " under the Saved label. An "Only targets I saved" checkbox sits under the "Show hidden targets" button. It shows only while you have a saved target, so with sign-in off nothing new appears, and like "Show hidden targets" it isn't kept across reloads.

What stays the same. Anyone can still save and use every target, and nothing here decides who can change or delete one. The saved file format doesn't change. Targets from configuration and Temporary targets have no owner. The round robin member list and the other target pickers are unchanged.

Not covered.

  • Who can change or delete a saved target (its owner or an admin). That comes with editing and deleting.
  • Saving converters and scorers. Those PRs set the owner the same way.
  • An Official view. Targets from configuration already have their own label; marking a saved target as official can come later.

Tests and Documentation

  • tests/unit/registry/test_registry_recipe_restore.py: restore keeps who saved each target, converter and scorer; an entry saved without sign-in has nobody.
  • tests/unit/backend/test_target_service.py: a saved target says who saved it and whether it was the caller, for the saver, another user and nobody signed in, the same before and after a restart; a target saved with sign-in off and a Temporary one name nobody, including the create response.
  • tests/unit/backend/test_mappers.py: the name and the flag for each owner and caller, a blank name, no object ID in the response, and inner targets of a saved round robin naming nobody.
  • tests/unit/backend/test_api_routes.py and test_auth_middleware.py: list and get pass the caller's object ID, and none with sign-in off.
  • TargetTable.test.tsx: the captions; the checkbox narrowing the list, together with the column filters and with hidden targets; no checkbox when nothing is yours; turning off when your last saved target is gone.
  • frontend/e2e/config.spec.ts (mock): the captions, the checkbox, and a fresh load listing everyone again. frontend/e2e/registry.spec.ts (seeded, sign-in off): nobody is named and there's no checkbox.
  • Each of the 18 ways I tried to break this fails at least one of these tests.
  • uv run --frozen --extra all python -m pytest -n 8 --dist=loadfile tests/unit/registry tests/unit/backend tests/unit/prompt_target tests/unit/models tests/unit/setup tests/unit/converter tests/unit/score: 19,612 passed, 9 skipped, 4 xfailed
  • cd frontend && npm run test:coverage: 184 suites, 4,563 tests passed
  • cd frontend && npx playwright test --project=mock e2e/config.spec.ts e2e/registry.spec.ts: 29 passed
  • cd frontend && E2E_SEEDED_MODE=true npx playwright test --project=seeded: 109 passed
  • npx tsc --noEmit, npx eslint on the changed files, and pre-commit (ruff, ty, docs checks): passed
  • doc/gui/0_gui.md (Registry): the "Saved by" caption and the checkbox. pyrit/backend/README.md: the two new fields.

No JupyText changes.

The dialog asks for a registry name and suggests one, saves the target by
default, and creates a Temporary target only when it can't be saved, saying
why before it is created. The Targets table labels each target Saved, From
configuration or Temporary.
# Conflicts:
#	tests/unit/backend/test_runtime_lifecycle.py
# Conflicts:
#	pyrit/backend/routes/targets.py
#	pyrit/backend/services/target_service.py
#	tests/unit/backend/test_api_routes.py
#	tests/unit/backend/test_target_service.py
# Conflicts:
#	pyrit/backend/services/target_service.py
Saving now refuses what restore can't rebuild: a setting left to the server's environment (each target type
declares these, replacing the backend's class-name table) and parameters the create API can't set. A cancelled
save finishes or is undone before the cancellation goes on, rollback deletes only the version this save wrote,
storage failures log a redacted cause chain, and capability detection keeps the target's origin.
The dialog keeps the type's own reasons from the type list and gets the rest from POST /api/targets/saveability,
which runs create's checks and the library's save checks without building or saving anything, so the warning and
the save share one policy. The Entra ID endpoint message shows on the Endpoint URL field after it loses focus.
Saved-entry storage failures now pass their traceback through redact_urls, and code scanning flags the scheme pattern
as slow on a long run of letters. A scheme is short, so bounding it keeps the match linear.

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