FEAT Wire frontend attack view to backend APIs#1371
FEAT Wire frontend attack view to backend APIs#1371romanlutz wants to merge 117 commits intoAzure:mainfrom
Conversation
|
Can you add screenshots for the frontend |
Backend:
- Replace private CentralMemory._memory_instance access with try/except
around the public get_memory_instance() API in the lifespan handler.
Initialization:
- Extract run_initializers_async() as a public function in
pyrit.setup.initialization so initializer execution can be invoked
without redundantly re-loading env files, resetting defaults, and
re-creating the memory instance.
- FrontendCore.run_initializers_async() now calls the new function
directly instead of re-invoking initialize_pyrit_async.
- Export run_initializers_async from pyrit.setup.
Frontend:
- Extract TargetTable into its own component (TargetTable.tsx).
- Move makeStyles definitions to co-located .styles.ts files for
TargetConfig, TargetTable, and CreateTargetDialog.
- Remove redundant explicit generic in useState<string>('') calls.
- Use FluentUI Field validationMessage/validationState props for
inline field-level validation in CreateTargetDialog.
Tests:
- Update TestRunScenarioAsync patches to mock run_initializers_async
instead of initialize_pyrit_async.
c799aa4 to
7e30544
Compare
There was a problem hiding this comment.
Pull request overview
Wires the PyRIT frontend attack experience to live backend APIs, including target management, attack execution, conversation branching, and richer message rendering.
Changes:
- Added/updated backend attack + target endpoints and DTOs (including conversation summaries and target metadata).
- Refactored initialization flow to shift lifespan init into CLI and consolidate initializer execution.
- Expanded frontend UI (config/history/chat) and added extensive unit + E2E coverage.
Reviewed changes
Copilot reviewed 84 out of 88 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/registry/test_converter_registry.py | Adds unit tests for converter registry singleton/metadata behavior. |
| tests/unit/memory/test_sqlite_memory.py | Adds tests for new conversation stats aggregation in SQLite memory. |
| tests/unit/cli/test_pyrit_backend.py | Adds tests for CLI config-file forwarding and server startup flow. |
| tests/unit/cli/test_frontend_core.py | Updates patch targets and initializer execution expectations after refactor. |
| tests/unit/backend/test_target_service.py | Updates tests for target registry naming changes. |
| tests/unit/backend/test_main.py | Updates lifespan expectations to “warn only” behavior. |
| pyrit/setup/initializers/airt_targets.py | Adds extra kwargs support and new/renamed target presets. |
| pyrit/setup/initializers/airt.py | Switches auth approach to Entra token providers and updates required vars. |
| pyrit/setup/initialization.py | Extracts run_initializers_async to separate initializer execution from memory init. |
| pyrit/setup/init.py | Exposes run_initializers_async as part of setup public API. |
| pyrit/prompt_target/openai/openai_video_target.py | Enforces single-turn conversation constraint for video target. |
| pyrit/prompt_target/openai/openai_target.py | Refactors OpenAI base target inheritance/init to align with PromptTarget. |
| pyrit/prompt_target/openai/openai_response_target.py | Includes target-specific params in identifiers (e.g., extra body params). |
| pyrit/prompt_target/openai/openai_realtime_target.py | Adjusts inheritance to keep chat-target semantics for realtime target. |
| pyrit/prompt_target/openai/openai_image_target.py | Enforces single-turn conversation constraint for image target. |
| pyrit/models/conversation_stats.py | Introduces ConversationStats aggregate model. |
| pyrit/models/attack_result.py | Adds attack_result_id onto domain AttackResult. |
| pyrit/models/init.py | Exports ConversationStats from models package. |
| pyrit/memory/sqlite_memory.py | Adds conversation stats query and refactors some filtering helpers/update behavior. |
| pyrit/memory/memory_models.py | Maps DB primary key into AttackResult.attack_result_id. |
| pyrit/memory/memory_interface.py | Adds conversation stats API and updates attack result insert/update semantics. |
| pyrit/memory/azure_sql_memory.py | Adds Azure SQL implementation for conversation stats and safer update behavior. |
| pyrit/cli/pyrit_backend.py | Refactors CLI to use FrontendCore two-step init and adds --config-file. |
| pyrit/cli/frontend_core.py | Moves deferred imports to module-level and adds run_initializers_async method. |
| pyrit/backend/services/target_service.py | Renames target id field to registry name and updates pagination cursor. |
| pyrit/backend/routes/version.py | Adds database backend info to version response payload. |
| pyrit/backend/routes/targets.py | Renames path params and docs to registry naming scheme. |
| pyrit/backend/routes/attacks.py | Expands attack routes to support conversations and changes identifiers to attack_result_id. |
| pyrit/backend/models/targets.py | DTO rename + adds supports_multiturn_chat. |
| pyrit/backend/models/attacks.py | Adds target metadata nesting, conversation endpoints, and new message request fields. |
| pyrit/backend/models/init.py | Updates exports for renamed message response DTO. |
| pyrit/backend/mappers/target_mappers.py | Maps multiturn capability and renames target id field in DTO mapping. |
| pyrit/backend/mappers/init.py | Renames exported async mapper function. |
| pyrit/backend/main.py | Removes standalone uvicorn runner and changes lifespan to warn when uninitialized. |
| frontend/src/utils/messageMapper.ts | Adds backend DTO ↔ UI Message mapping (attachments, reasoning, errors). |
| frontend/src/types/index.ts | Adds backend DTO type mirrors and expands UI message model. |
| frontend/src/services/api.ts | Adds targets/attacks/labels API clients and query serialization. |
| frontend/src/services/api.test.ts | Expands mocked API service tests for new endpoints. |
| frontend/src/components/Sidebar/Navigation.tsx | Adds navigation views (chat/history/config) and active styling. |
| frontend/src/components/Sidebar/Navigation.test.tsx | Updates navigation tests for new view switching behavior. |
| frontend/src/components/Layout/MainLayout.tsx | Shows DB info in version tooltip and wires navigation callbacks. |
| frontend/src/components/Layout/MainLayout.test.tsx | Updates layout tests for new navigation props and DB tooltip behavior. |
| frontend/src/components/Labels/LabelsBar.test.tsx | Adds unit tests for labels UI behavior and label fetching. |
| frontend/src/components/Config/TargetTable.tsx | Adds target list table UI with active-target selection controls. |
| frontend/src/components/Config/TargetTable.styles.ts | Adds styling for target table and active row highlighting. |
| frontend/src/components/Config/TargetConfig.tsx | Implements target config page with fetch/retry, refresh, and create dialog. |
| frontend/src/components/Config/TargetConfig.test.tsx | Adds tests for config page states and interactions. |
| frontend/src/components/Config/TargetConfig.styles.ts | Adds styling for config page layout and states. |
| frontend/src/components/Config/CreateTargetDialog.tsx | Adds create-target dialog and validation + submit flow. |
| frontend/src/components/Config/CreateTargetDialog.test.tsx | Adds tests for create-target dialog validation and submission. |
| frontend/src/components/Config/CreateTargetDialog.styles.ts | Adds styling for create-target dialog layout. |
| frontend/src/components/Chat/InputBox.tsx | Adds banners/locking states, ref API, and multiturn warnings for active target. |
| frontend/src/components/Chat/InputBox.test.tsx | Adds tests for new input-box behaviors (single-turn, ref attachments, banners). |
| frontend/src/components/Chat/ConversationPanel.tsx | Adds conversation list panel for attacks with promote-to-main and new conversation actions. |
| frontend/src/components/Chat/ConversationPanel.test.tsx | Adds tests for conversation panel rendering and interactions. |
| frontend/src/App.tsx | Introduces multi-view app shell, target selection, attack loading, and global labels. |
| frontend/src/App.test.tsx | Expands app tests for navigation, target selection, and opening historical attacks. |
| frontend/playwright.config.ts | Splits Playwright projects into seeded vs live modes. |
| frontend/package.json | Adds e2e scripts for seeded and live test projects. |
| frontend/eslint.config.js | Adds Node globals for Playwright e2e files. |
| frontend/e2e/config.spec.ts | Adds e2e coverage for config page and config↔chat flow. |
| frontend/e2e/api.spec.ts | Adds e2e API smoke tests (targets/attacks) and improves slow-backend handling. |
| frontend/e2e/accessibility.spec.ts | Updates a11y coverage for new navigation and config table; adjusts expected header text. |
| frontend/dev.py | Improves dev runner process management, adds detach/logs/config-file support. |
| frontend/README.md | Documents seeded vs live e2e modes. |
| .github/workflows/frontend_tests.yml | Runs seeded-only e2e in GitHub Actions. |
| .gitattributes | Adds union merge strategy for squad log/state files. |
| .devcontainer/devcontainer_setup.sh | Makes Playwright install failures non-blocking with clearer messaging. |
| from pyrit.memory.memory_models import AttackResultEntry, PromptMemoryEntry | ||
|
|
||
| return exists().where( | ||
| targeted_harm_categories_subquery = exists().where( |
There was a problem hiding this comment.
Both _get_attack_result_harm_category_condition and _get_attack_result_label_condition used to return an exists().where(...) condition, but now only assign it to a local variable (*_subquery) without returning it. That will make these helpers return None, breaking filtering logic that expects a SQLAlchemy condition. Return the constructed subquery (or revert to the inline return exists().where(...)).
| from pyrit.memory.memory_models import AttackResultEntry, PromptMemoryEntry | ||
|
|
||
| return exists().where( | ||
| labels_subquery = exists().where( |
There was a problem hiding this comment.
Both _get_attack_result_harm_category_condition and _get_attack_result_label_condition used to return an exists().where(...) condition, but now only assign it to a local variable (*_subquery) without returning it. That will make these helpers return None, breaking filtering logic that expects a SQLAlchemy condition. Return the constructed subquery (or revert to the inline return exists().where(...)).
pyrit/backend/routes/attacks.py
Outdated
| tb = traceback.format_exception(type(e), e, e.__traceback__) | ||
| # Include the root cause if chained | ||
| cause = e.__cause__ | ||
| if cause: | ||
| tb += traceback.format_exception(type(cause), cause, cause.__traceback__) | ||
| detail = "".join(tb) | ||
| raise HTTPException( | ||
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | ||
| detail=f"Failed to add message: {str(e)}", | ||
| detail=detail, | ||
| ) from e |
There was a problem hiding this comment.
Returning full stack traces in the HTTP 500 detail leaks internal code structure and potentially sensitive runtime data to clients. Log the traceback server-side (optionally include request IDs), and return a generic error message to the client; if you want trace exposure for development, gate it behind a dev/debug flag.
|
|
||
|
|
||
| class OpenAITarget(PromptChatTarget): | ||
| class OpenAITarget(PromptTarget): |
There was a problem hiding this comment.
Now that OpenAI targets use multiple inheritance elsewhere (e.g., RealtimeTarget(OpenAITarget, PromptChatTarget)), calling PromptTarget.__init__ directly bypasses cooperative initialization and can break MRO-based init chaining. Prefer super().__init__(...) in OpenAITarget.__init__ so all bases in the MRO can initialize correctly.
| PromptTarget.__init__( | ||
| self, |
There was a problem hiding this comment.
Now that OpenAI targets use multiple inheritance elsewhere (e.g., RealtimeTarget(OpenAITarget, PromptChatTarget)), calling PromptTarget.__init__ directly bypasses cooperative initialization and can break MRO-based init chaining. Prefer super().__init__(...) in OpenAITarget.__init__ so all bases in the MRO can initialize correctly.
| PromptTarget.__init__( | |
| self, | |
| super().__init__( |
|
|
||
| useEffect(() => { | ||
| fetchConversations() | ||
| }, [fetchConversations, activeConversationId]) |
There was a problem hiding this comment.
Including activeConversationId in this effect dependency will refetch the full conversations list on every conversation selection, creating unnecessary API traffic and UI churn. Consider fetching on attackResultId changes (and after mutating actions like create/promote), but not on local selection changes.
| }, [fetchConversations, activeConversationId]) | |
| }, [fetchConversations]) |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 84 out of 88 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
pyrit/memory/sqlite_memory.py:1
- This function used to return the
exists().where(...)condition, but now assigns it tolabels_subqueryand never returns it. That will cause callers to appendNoneto SQLAlchemy conditions and break attack filtering by labels. Returnlabels_subquery(or inline it back into a return statement).
# Copyright (c) Microsoft Corporation.
- Add run_initializers_async to pyrit.setup for programmatic initialization - Switch AIRTInitializer to Entra (Azure AD) auth, removing API key requirements - Add --config-file flag to pyrit_backend CLI - Use PyRIT configuration loader in FrontendCore and pyrit_backend - Update AIRTTargetInitializer with new target types Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add conversation_stats model and attack_result extensions - Add get_attack_results with filtering by harm categories, labels, attack type, and converter types to memory interface - Implement SQLite-specific JSON filtering for attack results - Add memory_models field for targeted_harm_categories - Add prompt_metadata support to openai image/video/response targets - Fix missing return statements in SQLite harm_category and label filters Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add attack CRUD routes with conversation management - Add message sending with target dispatch and response handling - Add attack mappers for domain-to-DTO conversion with signed blob URLs - Add attack service with video remix support and piece persistence - Expand target service and routes with registry-based target management - Add version endpoint with database info Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add attack-centric chat UI with multi-conversation support - Add conversation panel with branching and message actions - Add attack history view with filtering - Add labels bar for attack metadata - Add target configuration with create dialog - Add message mapper utilities for backend/frontend translation - Add video playback support with signed blob URLs - Add InputBox with attachment support and auto-expand - Update dev.py with --detach, logs, and process management - Add e2e tests for chat, config, and flow scenarios Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
8f1a532 to
65a4182
Compare
Replace uuid-based test_operation/test_operator with readable examples 'op_trash_panda' and 'roakey' matching frontend defaults. Remove unused uuid imports. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… into romanlutz/frontend_attack_view
- api.ts: use headers.set() instead of index assignment for AxiosHeaders - types/index.ts: fix score_category to string[] | null, prompt_metadata to Record<string, unknown> to match backend models - e2e/api.spec.ts: fix created.type -> created.target_type assertion - version.py: sanitize database path to basename only to avoid leaking full filesystem paths in unauthenticated endpoint Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- e2e/api.spec.ts: update skip comment to be accurate about why create-target may fail (credentials, not schema mismatch) - messageMapper.ts: tighten base64 detection to exclude file paths (Windows/Unix) and URI schemes (file:, blob:, etc.) that could be incorrectly treated as base64 encoded content Navigation.tsx already has aria-label on all buttons (F was stale). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add attack_result_id to createAttack mock response - Add target_conversation_id and target_registry_name to all addMessage test requests matching AddMessageRequest type Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tack_view # Conflicts: # tests/unit/registry/test_converter_registry.py
- Add operator and operation optional fields to ConfigurationLoader - Pass them through FrontendCore to backend app state - Expose default_labels in version endpoint response - Frontend fetches labels from version API on startup and uses them as initial values instead of hardcoded placeholders Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When port 3000 is occupied, Vite picks an alternate port (e.g. 3001) but dev.py always printed 3000. Now reads Vite's stdout to detect the actual port and displays it correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dev.py now polls GET /api/health (up to 2min) before printing the success banner. If the backend process exits during startup (e.g. auth failure), it prints a clear failure message instead. Frontend starts in parallel to save time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Backend stdout is now piped and forwarded via a background thread. This ensures all init output (including errors) appears before the success/failure banner, preventing the confusing interleaved output where 'Servers running!' appeared before init errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
stop_servers now also finds processes by port (netstat/lsof) in addition to command-line pattern matching. This catches orphaned backends that were started outside of dev.py (e.g. via Start-Process) which caused the health check to pass instantly against a stale server. Also force unbuffered Python output for the backend subprocess. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The --database argparse arg defaulted to 'SQLite', which always overrode the memory_db_type from the config file (e.g. azure_sql). Changed default to None so load_with_overrides only applies an override when the user explicitly passes --database on the CLI. Fixed in pyrit_backend, pyrit_scan, and pyrit_shell. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 5 Playwright E2E tests covering error scenarios: - Backend returns 500 on send → error bubble + input preserved - Network error on send → network error message in chat - Health check failure → connection degraded banner - Health recovery → reconnected banner - Create-attack fails → error displayed Also add 'mock' project to playwright.config.ts so untagged tests (route-intercepted, no real backend needed) are discoverable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tack_view # Conflicts: # pyrit/backend/main.py # pyrit/backend/models/attacks.py # pyrit/backend/routes/version.py # pyrit/backend/services/attack_service.py
Lift filter state from AttackHistory to App.tsx so filters survive navigation between views. Add a Reset button (FilterDismissRegular icon) that appears when any filter is active and resets all to defaults. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move exported constants (DEFAULT_HISTORY_FILTERS, DEFAULT_GLOBAL_LABELS) to separate files to avoid react-refresh/only-export-components warnings. Fix unused vars in flows.spec.ts, unused imports in tests, and replace 'as any' casts with proper types in ChatWindow.test.tsx. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…view The target mapper was only extracting explicitly named params (temperature, top_p, etc.) and ignoring others like reasoning_effort, reasoning_summary, max_output_tokens. Now collects all non-extracted params into target_specific_params so the frontend displays them. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add tests at all three levels to prevent regression: - Mapper: extra params collected, None-valued excluded, merge with explicit - API routes: list and get endpoints include target_specific_params in JSON - Service: list and get return target_specific_params from registry objects - Frontend: TargetConfig renders reasoning_effort, reasoning_summary etc. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When branching into a new conversation or copying to a new conversation within the same attack, the conversations panel now opens automatically so users can see and navigate between conversations. Added setIsPanelOpen(true) to handleCopyToNewConversation and handleBranchConversation handlers, plus 2 new tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a new attack is created, capture the active target as attackTarget so the cross-target guard fires if the user switches targets mid- conversation. Previously attackTarget was only set when loading historical attacks, so switching targets during a new attack was not blocked. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a previously generated video is carried over to a new conversation (via copy/branch) and sent with a text prompt, the backend now auto-resolves the video_id from the original piece's metadata and sets it on both the text and video_path pieces. This enables seamless video remixing without requiring the frontend to manage video_id metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The frontend now carries prompt_metadata (including video_id) through the full pipeline: backend response → MessageAttachment → outgoing request. This ensures video_id is properly set on both text and video pieces when remixing, without relying solely on the backend fallback. Changes: - Add metadata field to MessageAttachment type - Add prompt_metadata to BackendMessagePiece and MessagePiece DTOs - Preserve prompt_metadata in pieceToAttachment mapper - Include prompt_metadata in attachmentToMessagePieceRequest - Auto-set video_id on text piece in buildMessagePieces when video attachment with metadata is present - Include prompt_metadata in backend pyrit_messages_to_dto_async mapper Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The video target's _validate_video_remix_pieces was stripping video_path pieces from the message in-place before the normalizer stored the request. This meant the user's sent message only showed the text piece, not the video attachment. The stripping was unnecessary since the target only uses video_id from prompt_metadata for remix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
isMediaDataType() now includes 'binary' so that binary_path pieces (PDFs, Word docs, etc.) are rendered as file attachments rather than being dumped into the text content area. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The backend expects the query parameter 'label' (repeatable key:value) but the frontend type definition used 'labels'. The caller in AttackHistory.tsx was already using 'label' correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…r attacks FastAPI parses ?converter_types= as [''], not []. The service treats [] as 'only attacks with no converters' but [''] would try to match a converter named ''. Now empty strings are stripped at the route level. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
show_logs() used the 'tail' command which doesn't exist on Windows. Now reads last N lines and polls for new content in pure Python. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Aligns with all other timestamp fields in the API which use datetime. Pydantic handles ISO 8601 serialization automatically. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add target management and real attack execution to the PyRIT frontend, replacing the echo stub with live backend communication.
Backend:
CLI:
Frontend:
Tests:
Screenshots:
Landing page

History view

Target config view

Text-to-text


Branching into new conversation after first turn
Text-to-image



Reusing the same image for text+image-to-image
Text-to-video
