Repository navigation
FEAT: Export Operation findings to Word and Markdown - #3493
Open
Adrian Gavrila (adrian-gavrila) wants to merge 3 commits into
Open
Adrian Gavrila (adrian-gavrila) wants to merge 3 commits into
Adrian Gavrila (adrian-gavrila) wants to merge 3 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Follow-up to #3066 and #3275. An Operation can hold findings with linked attack evidence, but putting them into a report meant copying each one by hand. Export findings on the Operation page downloads the findings you select as one Word or Markdown document, ready to paste into a larger report.
What changed
copyrit-findings-synthetic-evaluation-2026-10-22-1258.docx.POST /api/operations/{id}/findings/exporttakesfinding_ids,format(docxby default, ormarkdown) anddashboard_base_url, and returns the document as an attachment. Empty or duplicate IDs, unknown fields, and a base URL that isn't plain HTTP(S) or has credentials, a query or a fragment return 422. A missing Operation, or a finding that is missing or belongs to another Operation, returns 404. If rendering fails, the response is a 500 with no partial file. The export only reads; there's no schema change or migration.�in both formats, and every line-break convention becomes a line break.ManualScorer), the rationale, and From operation when the attack belongs to a different Operation. A deleted attack keeps its entry under its attack ID and reads Evidence unavailable. Messages, attachments and media are never included. Links are built from the dashboard's own address, so they point at the deployment the export came from.OperationService.list_findings_async. The order of IDs in the request doesn't change it.downloadFilehelper. The Axios response interceptor reads error bodies from blob responses before its compatibility and 401 checks, so a failed download shows the server's message. The Operation header buttons (Edit, Delete, Export findings, New finding) have icons.Markdown export of the same seven synthetic findings
How it fits together
flowchart LR A["Operation page:<br/>Export findings"] --> B["Dialog pages every finding;<br/>user picks findings and format"] B -->|"POST finding_ids, format,<br/>dashboard_base_url"| C["Export route"] C --> D["finding_export: selected findings and<br/>evidence through OperationService"] D --> E["Small report model"] E --> F["Word renderer"] E --> G["Markdown renderer"] F --> H["Attachment"] G --> H H --> I["Browser saves the file"]Design choices and limits
pyrit/output. The output printers returnstr, and a.docxis a ZIP file. A GUI download didn't need that contract widened.attackConversationRoutePath. That function's JSDoc now says exported documents link there, and the seeded test opens one.Content-Dispositionuses the same rule in UTC with a-utcsuffix, for API clients and for cross-origin deployments that don't expose the header.\&. Evidence is read finding by finding; tests cover 101 findings and 101 evidence items on one finding.Follow-ups
_project_finding_fields. The renderers already take any list of labelled fields; that work also needs to decide how a field picks its Word column.Tests and Documentation
Backend tests cover the HTTP contract (attachment headers, Unicode filenames, 422, 404 and 500), the report built from SQLite (selection across the 100-row page, dashboard order, 101 evidence items, human and automated scores, undetermined and missing scores, deleted attacks, Other severity and harm type, cross-Operation evidence), and both renderers on whole documents (table layout, bullets, escaping and fencing, hostile text, links under a path prefix, control characters, Word document properties). Frontend tests cover the dialog (paging, selection, stale responses, focus, errors), blob error decoding, the download helper and the filename rule. A seeded Playwright test downloads both formats, checks the dialog at 390px (including that the warning fits its bar), and opens an exported link. Results on
a0beb5543, based on0f58aed71:pytest -n 4 --dist=loadfile --cov=pyrit --cov-fail-under=78 tests/unit: 31,218 passed, 57 skipped, 96.23% coverage. This ran on0c62fda60;a0beb5543changes only frontend files.diff-cover coverage.xml --compare-branch=0f58aed71 --fail-under=90: 99% of 329 changed lines. The one uncovered line is the_percent_encodefallback infinding_export.py, which no generated link reaches.pre-commit run --all-files: passed.npm run test:coverage: 3,654 passed.npm run lint,npx tsc --noEmit,npm run buildandnpx playwright test --list: passed.npx playwright test --project=seeded: 104 passed on0c62fda60. Ona0beb5543,operations.spec.tsandoperations-acceptance.spec.tspassed (15 tests).npx playwright test --project=mock: 180 passed, 22 failed, all in target registry, accessibility, routing and onboarding specs that this PR doesn't touch. Withmain's frontend,config.spec.tsfails 15 tests on this Windows machine, the same count as with this branch.OperationService.Docs:
doc/gui/0_gui.mdadds an "Exporting findings" section under Operations. No notebooks changed, so JupyText wasn't needed.