4.3 KiB
4.3 KiB
Phase 6: Output, Reporting & Key Management - Context
Gathered: 2026-04-05 Status: Ready for planning Mode: Auto-generated
## Phase BoundaryTwo related capabilities:
- Scan output formats: table (default, colored, masked), JSON, SARIF 2.1.0, CSV.
--unmaskreveals full keys. Proper exit codes for CI/CD. - Key management commands:
keyhunter keys list/show/export/copy/deletefor lifecycle operations on stored keys.
Output Formats (OUT-01..06)
- Extend pkg/output/ with:
json.go,sarif.go,csv.go, keepingtable.gofrom Phase 1/5 - Format registry:
output.Formatterinterface withFormat(findings []Finding, w io.Writer) error - Selection:
--output=table|json|sarif|csvflag on scan command (default: table) - Masking:
MaskKey()is default;--unmaskpasses a flag down to the formatter - Colors: lipgloss for table format only (already in use), strip colors when stdout isn't a TTY (detect via
isatty)
Exit Codes (OUT-06)
- 0: no findings
- 1: findings detected
- 2: scan error
- Set via
os.Exit(code)at end of scan command. Usecobra.Command.SilenceUsage = trueon error to avoid duplicate usage message.
SARIF 2.1.0
- Implementation: custom structs (no library per CLAUDE.md — ~200 lines). schema at https://docs.oasis-open.org/sarif/sarif/v2.1.0/
- Fields:
$schema,version: "2.1.0",runs[].tool.driver.{name, version, rules[]},runs[].results[]withruleId, level, message, locations[{physicalLocation.{artifactLocation.uri, region.{startLine, startColumn, endLine, endColumn}}}] - Rule: one rule per provider (ruleId = provider name)
- Level mapping: confidence high → error, medium → warning, low → note
Key Management (KEYS-01..06)
- New command tree:
keyhunter keys <subcommand>in cmd/keys.go (replace existing stub)list— table of all stored findings, masked by default,--unmaskfor full,--provider=openaifilter,--verifiedfiltershow <id>— full detail for one finding (unmasked), including verify metadataexport --format=json|csv [--output=file]— dump findings to file/stdoutcopy <id>— copy plaintext key to clipboard via atotto/clipboard (already imported)delete <id>— remove from findings table, with--yesto skip confirm
- ID: use SQLite rowid (integer). Display in list output.
Findings DB Query Layer
- New
pkg/storage/queries.go—ListFindings(filters Filters) ([]Finding, error),GetFinding(id int) (*Finding, error),DeleteFinding(id int) error - Filter struct:
Provider string, Verified *bool, Since time.Time - Pagination:
Limit, Offsetfields (default unlimited for v1)
Output Package Layout
pkg/output/
formatter.go — Formatter interface + Registry
table.go — existing, refactor to implement Formatter
json.go — NEW — JSON output (uses encoding/json)
sarif.go — NEW — SARIF 2.1.0 output
csv.go — NEW — CSV output (uses encoding/csv)
colors.go — NEW — isatty detection, color stripping
<code_context>
Existing Code Insights
Reusable Assets
- pkg/output/table.go — existing, extend to Formatter interface
- pkg/storage/findings.go — existing CRUD
- pkg/engine/finding.go — extended with verify fields in Phase 5
- pkg/engine/finding.go MaskKey() — already defined
- cmd/stubs.go —
keysis currently a stub, replace with real command tree
Dependencies to add
github.com/mattn/go-isatty— for TTY detection
</code_context>
## Specific Ideas- JSON output should include full Finding struct (all fields)
- CSV header row: id,provider,confidence,key_masked,source,line,detected_at,verified,verify_status
- SARIF should include the scan binary version (extract from cmd/root.go version constant)
- Keys export to file should use atomic write (write to temp then rename)
- Delete should show confirmation: "Delete finding #42 (openai, sk-proj-***abc)? [y/N]"
- Per-finding tags/labels — not in scope
- Bulk operations (delete all from provider X) — add a --provider filter, but no bulk delete
- Export to cloud storage (S3, GCS) — out of scope
- HTML report format — defer to dashboard phase