Files
2026-04-05 16:00:42 +03:00

4.3 KiB

Phase 6: Output, Reporting & Key Management - Context

Gathered: 2026-04-05 Status: Ready for planning Mode: Auto-generated

## Phase Boundary

Two related capabilities:

  1. Scan output formats: table (default, colored, masked), JSON, SARIF 2.1.0, CSV. --unmask reveals full keys. Proper exit codes for CI/CD.
  2. Key management commands: keyhunter keys list/show/export/copy/delete for lifecycle operations on stored keys.
## Implementation Decisions

Output Formats (OUT-01..06)

  • Extend pkg/output/ with: json.go, sarif.go, csv.go, keeping table.go from Phase 1/5
  • Format registry: output.Formatter interface with Format(findings []Finding, w io.Writer) error
  • Selection: --output=table|json|sarif|csv flag on scan command (default: table)
  • Masking: MaskKey() is default; --unmask passes 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. Use cobra.Command.SilenceUsage = true on 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[] with ruleId, 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, --unmask for full, --provider=openai filter, --verified filter
    • show <id> — full detail for one finding (unmasked), including verify metadata
    • export --format=json|csv [--output=file] — dump findings to file/stdout
    • copy <id> — copy plaintext key to clipboard via atotto/clipboard (already imported)
    • delete <id> — remove from findings table, with --yes to skip confirm
  • ID: use SQLite rowid (integer). Display in list output.

Findings DB Query Layer

  • New pkg/storage/queries.goListFindings(filters Filters) ([]Finding, error), GetFinding(id int) (*Finding, error), DeleteFinding(id int) error
  • Filter struct: Provider string, Verified *bool, Since time.Time
  • Pagination: Limit, Offset fields (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 — keys is 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]"
## Deferred Ideas
  • 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