Repository navigation
cli: the --pre-commit-mode not-examined notice says contents unreadable for every cause, including a budget cut-short and a parse failure #817
Description
Summary
In --pre-commit-mode, the one-line stderr notice for files that were not fully examined reads NOT examined (contents unreadable) for every cause, including the two where the tool read the file perfectly well: a validator budget or deadline that cut coverage short, and a file it could not parse. A developer whose commit was let through (rc 0 without --fail-on-incomplete) is told the file was unreadable when the truth is that a validator ran out of time, which points them at the wrong fix (permissions, encoding) instead of the right one (the budget, or --fail-on-incomplete). The entries behind the line already carry the cause; only the wording ignores it. Left in place by #813 because cmd/main.go was outside its files.
Reproduction
Measured on main @ c403e27 (v2.5.5), synthetic input (517-17-1007 is an SSN-shaped test value):
printf 'Ticket 4471: caller verified SSN 517-17-1007, reply to j7.example@example.org\n' > one.txt
ferret-scan --file one.txt --pre-commit-mode --checks SSN,EMAIL --validator-budget all=1ns
# ferret-scan: 1 file NOT examined (contents unreadable) — findings may be missing. Re-run without --pre-commit for detail.
# rc=0
printf '%%PDF-1.7\n517-17-1007\n' > broken.pdf
ferret-scan --file broken.pdf --pre-commit-mode --checks SSN
# ferret-scan: 1 file NOT examined (contents unreadable) — findings may be missing. Re-run without --pre-commit for detail.Both files were read. The first was cut short by the budget; the second cannot be parsed as a PDF. The full (non-pre-commit) report classifies them correctly as coverage cut short and cannot parse.
Root cause
cmd/main.go:2733–2742: the notice is a single fmt.Fprintf with the literal (contents unreadable) and the entry count. The unscannedEntries it counts come from collectUnscanned (cmd/unscanned_report.go:385), whose entries carry Cause unscannedCause with eight values (causeUnreadable, causeUnparseable, causeNoText, causeCutShort, causeNotFollowed, causeTooLarge, causeNotRegular, causeRefusedTraversal) and a String() label each (:104–125); the full report uses them. The notice was written when the only channel feeding it was the unreadable one, and the wording stayed when the other channels were added.
Resolution
Keep the one-line contract (pre-commit output is read mid-commit) but state the cause: group the entries by Cause and print the labels, e.g. ferret-scan: 1 file NOT examined (coverage cut short) — findings may be missing. Re-run without --pre-commit for detail., or with several causes 2 files NOT examined (1 coverage cut short, 1 cannot parse). When any entry is causeCutShort and --fail-on-incomplete is not set, add the hint that the flag makes this exit 3, since that is the gate decision the developer needs. Reuse unscannedCause.String() so the pre-commit line and the full report cannot disagree.
Acceptance criteria
- The two commands above print
(coverage cut short)and(cannot parse)respectively; a chmod-000 file still prints(contents unreadable). - A mixed directory prints the per-cause counts on one line.
- A test in
cmd/pins the three wordings and fails on main (today's string has no cause). - No change to exit codes, to the full report, or to machine formats.
Activity