fix(cli): redteam --format text --output writes JSON content to .txt file - #255
Open
nikhilpatidar wants to merge 1 commit into
Open
fix(cli): redteam --format text --output writes JSON content to .txt file#255nikhilpatidar wants to merge 1 commit into
nikhilpatidar wants to merge 1 commit into
Conversation
The --output dispatch in nuguard/cli/commands/redteam.py only had explicit branches for markdown and sarif. The default 'text' format fell through to the JSON else-block, so 'nuguard redteam --output foo.txt' wrote a JSON document with a .txt extension. Root cause: missing branch for fmt == 'text' in the --output dispatch loop. Fix: extract _render_findings_text() helper that supports both colored stdout (colour=True) and plain on-disk (colour=False) output, and add an explicit 'text' branch to the dispatch that writes via the helper with colour=False. The stdout path now also uses the helper for consistency. Tests: 8 regression tests in tests/cli/test_redteam_format_output.py pin the contract (plain text vs ANSI colored, severity ordering, empty findings, dispatch loop).
KanishkThamman
approved these changes
Aug 11, 2026
KanishkThamman
left a comment
Collaborator
There was a problem hiding this comment.
Fix is correct and narrowly scoped — verified the old dispatch fell through to the JSON else-branch for text, now doesn't. One nit inline on test coverage.
| # --------------------------------------------------------------------------- | ||
|
|
||
|
|
||
| def test_dispatch_text_format_writes_plain_text_file(tmp_path: Path) -> None: |
Collaborator
There was a problem hiding this comment.
Nit: this re-implements the dispatch loop inline rather than invoking the real redteam() CLI command via CliRunner (see tests/benchmark/test_redteam_cli.py for the pattern). It won't catch a regression if the real dispatch order changes again — worth a follow-up end-to-end test.
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.
PR Type
Fixes #254
What
nuguard redteam --output foo.txt(default format) writing JSON content to a.txtfile.textbranch to the--outputdispatch loop innuguard/cli/commands/redteam.py._render_findings_text()helper used by both the stdout path (withcolour=True) and the file-write path (withcolour=False).tests/cli/test_redteam_format_output.pythat pin the dispatch contract.Why
The
--outputdispatch loop only had explicit branches formarkdownandsarif. The defaulttextformat fell through to the JSON else-block, sonuguard redteam --output foo.txtwrote a JSON document with a.txtextension. This broke downstream tooling that processes the saved report by format-extension (e.g. a CD pipeline that renames--output reporttoreport.txtfor archiving, or a log shipper that parses line-oriented text). The discrepancy between stdout and the saved file is also surprising for users — they see plain text on the terminal and JSON in the file.Root Cause
Missing
if fmt == "text":branch in the--outputdispatch. The dispatch'selsebranch JSON-serialised the findings, which was reached for bothjsonandtext(the default).How
--output foo.txtproduces a JSON document starting with{.markdownandsarifhad explicit branches.policy.pyandvalidate.pydispatch loops, which correctly handletextexplicitly — onlyredteam.pyhad the bug._render_findings_text(findings, meta, scan_outcome, *, colour=False) -> strso the same logic produces both stdout (coloured) and on-disk (plain) text reports.if fmt == "text":branch in the--outputdispatch that calls the helper withcolour=False(no ANSI escapes in a file)._print_findingsto call the helper withcolour=Truefor stdout parity.textandjsonformats.Test Steps
main:nuguard redteam --output foo.txtwrites JSON starting with{.nuguard redteam --output foo.txtwrites plain text starting withNuGuard Red-Team.nuguard redteamstill emits ANSI-coloured text to stdout.uv run pytest tests/cli/test_redteam_format_output.py -v— 8 tests pass.uv run pytest tests/cli/ -q— 78 tests pass.uv run pytest tests/cli/ tests/sbom/ -q— 260 tests pass, 1 skipped.uv run ruff check nuguard/cli/commands/redteam.py— clean.uv run mypy nuguard/cli/commands/redteam.py— clean.Checks
make testpasses (uv run pytest tests/cli/ tests/sbom/ -q— 260 passed, 1 skipped)make lintpasses (ruff check+mypyonnuguard/cli/commands/redteam.py)make fmtapplied, no difftests/cli/test_redteam_format_output.py(8 new tests covering the bug)Other Notes
bug/redteam-text-format-output, branched frommainper thebug/*→mainpolicy in CONTRIBUTING.md.behaviorcommand has a documented intentional fallback where--format text --output foowrites markdown inside the file (with a comment explaining the preserved behaviour). That is out of scope for this bug.