FEAT: Add --output console | json flag in scenario-results CLI command - #2508
Open
Justin Song (jsong468) wants to merge 1 commit into
Open
FEAT: Add --output console | json flag in scenario-results CLI command#2508Justin Song (jsong468) wants to merge 1 commit into
--output console | json flag in scenario-results CLI command#2508Justin Song (jsong468) wants to merge 1 commit into
Conversation
| """ | ||
|
|
||
| #: Human-readable console text (the default). | ||
| CONSOLE = "console" |
Contributor
There was a problem hiding this comment.
should we not have anything new here and just match the output module?
| print(payload.model_dump_json(indent=2)) | ||
|
|
||
|
|
||
| async def print_results_console_async(*, result: ScenarioResult, payload: ResultsPayload) -> None: |
Contributor
There was a problem hiding this comment.
hmmm, is there a reason why we're not using pyrit.output? This seems to dupe a lot of code here.
| class ScenarioOverviewPayload(BaseModel): | ||
| """ | ||
| The ``overview`` view: scenario-level aggregates and per-group success rates. | ||
|
|
Contributor
There was a problem hiding this comment.
Similarly, I don't love this code being in cli module
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.
This is phase 4 of the goal of letting users dig into individual attack results after running scenarios in pyrit_scan or pyrit_shell. See gist here. This PR adds a machine-readable output format to the
scenario-resultscommand in bothpyrit_scanandpyrit_shell, so a run can be inspected as JSON, not just human-readable console text.JSON

attacksviewJSON

conversationsviewJSON

overviewview (see "Less-obvious decisions / Open to feedback" section)Description
--output {console,json}flag onscenario-results(both front-ends).console(default) keeps today's behavior;jsonserializes the view's typed payload viamodel_dump_json(). No backend changes.--viewbuilds one typed payload;--outputonly decides how to serialize it, so every view works with every format without an N×M explosion of renderers:overview→ScenarioOverviewPayload(aggregates + per-group rates)attacks→AttacksTablePayloadconversations→ConversationsPayloadfull→FullPayload { attacks, conversations }(one well-formed JSON document rather than two concatenated ones)build_results_payload_asyncreturns the right payload per view, hiding the sync (overview/attacks, embedded data) vs. async (conversations/full, per-attack fetch) split behind a single call, so each front-end collapses to "build the payload, then switch on format."apply_view_limit_policymessages now print tostderr, so--output jsonkeeps stdout a clean, parseable document (e.g.pyrit_scan ... --output json > out.jsoncaptures only JSON while the note still shows on screen).shownsibling field added to the attacks/conversations payloads next to the pre-limittotal. Console shows "Showing N of M"; JSON consumers get the same signal as data (shown < total⇒ truncated). It is a computed field (len(...)) so it can't drift from what was actually rendered.Less-obvious decisions / Open to feedback
ScenarioResultthe CLI already holds, so reusing it gives cross-surface consistency (CLI matches the notebook/library output) with no memory coupling. (Conversations can't reuse the framework conversation printer because it's coupled toCentralMemory+Messagedomain objects the thin REST client doesn't have — hence CLI-owned transcript renderers there.)PrettyScenarioResultMemoryPrinterat all in CLI and just add a new renderer for the payload, similar toprint_attacks_tableorprint_conversations(comes at cost of extra code and repeated logic). This question can also be revisited if consumers need changes.ScenarioOverviewPayloadmirror all the information presented by thePrettyScenarioResultMemoryPrinter(including retrieving scorer metrics, parsing target/scorer identifier information) while still using the framework printer for theconsoleoverview view and enhance the unit test to enforce consistency (comes at cost of extra code, maintenance to keep the two views in line, repeated logic)Next Phase:
--output htmland file-destination flag (--output-file) are deferred to phase 5; JSON goes to stdout and relies on shell redirection for now (i.e.,pyrit_scan scenario-results 5cdb4b76-b1a6-4fc4-9cfb-d0cc85bb8424 --view attacks --output json >output.json)Tests and Documentation
test_results.py):OutputFormatvalues +parse_output_format(valid/invalid);--outputregistered with defaultconsole;build_overview_payloadaggregates + per-group rates; the characterization test locking overview aggregates to the framework printer's rendered output;build_results_payload_asyncreturns the correct payload per view and composesFullPayloadunder one shared limit;showntracks--limittruncation whiletotalstays pre-limit.test_output.py):print_results_jsonemits a single parseable document equal tomodel_dump_json(including the computedshown);print_results_console_asyncdispatches each payload to the right renderer (overview → framework printer, attacks → table, conversations → transcripts, full → both).test_pyrit_scan.py,test_pyrit_shell.py):--output jsonper view emits parseable JSON on stdout with exit 0; the console path is unchanged; limit-policy notes land on stderr; existing error paths preserved.--outputhelp text notes JSON is machine-readable with informational notes on stderr so stdout stays parseable.tests/unit/clisuite green; ruff (incl. import sorting) and the async-suffix build check clean.