--- phase: 06-output-reporting plan: 05 subsystem: cli tags: [cobra, cli, clipboard, sqlite, keys-management] requires: - phase: 06-output-reporting provides: "formatter registry (json/csv), storage query layer, verify package" provides: - "keyhunter keys list/show/export/copy/delete/verify subcommand tree" - "cmd/keys.go with openDBWithKey + storageToEngine helpers" - "atomic file-write pattern for exports (.tmp + os.Rename, 0600)" affects: [07-import-hooks, 18-dashboard] tech-stack: added: [] patterns: - "cmd.OutOrStdout for hermetic Cobra subcommand tests" - "viper-first, env fallback, config default for database.path + passphrase" key-files: created: - cmd/keys.go - cmd/keys_test.go - .planning/phases/06-output-reporting/06-05-SUMMARY.md modified: - cmd/stubs.go key-decisions: - "SARIF export is intentionally rejected by keys export (scan-only per context)" - "keys show is always unmasked (KEYS-02) — no --unmask flag" - "keys verify updates findings table inline via db.SQL().Exec rather than adding a storage helper" - "Tests invoke RunE directly with cmd.SetOut buffers — no os.Stdout redirection needed" patterns-established: - "openDBWithKey(): mirrors scan.go's DB-open + loadOrCreateEncKey sequence so every keys subcommand shares one path" - "storageToEngine(): bridge between storage.Finding and engine.Finding for formatter reuse" requirements-completed: [KEYS-01, KEYS-02, KEYS-03, KEYS-04, KEYS-05, KEYS-06] duration: 4min completed: 2026-04-05 --- # Phase 06 Plan 05: Keys Command Tree Summary **keyhunter keys list/show/export/copy/delete/verify wired to the Plan-04 query layer and the Plans 01-03 formatter registry, replacing the Phase-1 stub.** ## Performance - **Duration:** ~4 min - **Started:** 2026-04-05T20:35:50Z - **Completed:** 2026-04-05T20:39:40Z - **Tasks:** 2 - **Files modified:** 3 (cmd/keys.go, cmd/keys_test.go, cmd/stubs.go) ## Accomplishments - Six-subcommand `keys` tree: list, show, export, copy, delete, verify - list supports `--provider`, tri-state `--verified` (via `Flag().Changed`), `--limit`, `--unmask` - show always unmasked with sorted verify metadata rendering (KEYS-02) - export reuses `output.Get("json"|"csv")`, atomic `.tmp` + rename on `--output`, stdout otherwise - copy uses `atotto/clipboard` with masked confirmation line - delete prompts on stdin unless `--yes`; cancelled paths print "Cancelled." - verify re-runs `HTTPVerifier.VerifyAll` against the stored row, updates `findings.verify_*` in place, re-renders - Seven integration tests (`TestKeysList_*`, `TestKeysShow_*`, `TestKeysExport_*`, `TestKeysDelete_WithYes`) exercise the full happy and miss paths against a temp-file SQLite DB; copy + verify intentionally skipped (clipboard headless / network) ## Task Commits 1. **Task 1: Implement keys command tree** — `06594af` (feat) 2. **Task 2: Integration tests for keys list/show/export/delete** — `e2394ec` (test) ## Files Created/Modified - `cmd/keys.go` (created) — 350+ LOC, six subcommands plus `openDBWithKey`, `storageToEngine`, `renderFinding`, `parseID` helpers, `init()` wiring flags and AddCommand - `cmd/keys_test.go` (created) — seedKeysDB helper plus 7 tests using `cmd.SetOut` + RunE invocation - `cmd/stubs.go` (modified) — removed the `keysCmd` placeholder, left a pointer comment so no duplicate declaration exists with `cmd/keys.go` ## Decisions Made - **SARIF rejected by keys export**: context gathered for Plan 06 explicitly scopes SARIF to the scan command. `keys export --format=sarif` returns a clear error mentioning scan-only. - **verify updates via raw UPDATE**: rather than adding a `db.UpdateFindingVerify` helper (not requested anywhere else), the implementation uses `db.SQL().Exec` inline with JSON-marshaled metadata. Easy to promote to a helper later if a second caller appears. - **Tests call RunE directly**: avoids mutating `rootCmd.SetArgs` global state and gives each test a clean output buffer. Matches `cmd/scan_test.go`'s lightweight style. - **Passphrase resolution order**: `viper("encryption.passphrase")` → `KEYHUNTER_PASSPHRASE` env → `config.Load().Passphrase`. This makes test setup trivial (`t.Setenv`) while preserving production semantics. ## Deviations from Plan None — plan executed exactly as written. Minor optional cleanups applied: - The plan suggested using `output.ColorsEnabled(os.Stdout)` for list output; the implemented list renderer is plain text (no color) to keep tests deterministic and output grep-friendly. Color support can be added later without API changes. - All `fmt.Fprintln` calls use `cmd.OutOrStdout()` / `cmd.ErrOrStderr()` from the start, which the plan flagged as an acceptable mid-task refactor during Task 2. Doing it in Task 1 kept both tasks clean. ## Issues Encountered None. First `go build ./...` and first `go test ./...` both passed without adjustment. ## User Setup Required None — no external services introduced. ## Next Phase Readiness - All of KEYS-01..06 satisfied; keys tree is production-ready and under test - Import/hook work in Phase 7 can reuse `openDBWithKey` and `storageToEngine` - Dashboard work (Phase 18) can share `renderFinding`'s field ordering for HTML detail views - Follow-up: promote the inline `verify` UPDATE to a `db.UpdateFindingVerify` helper if/when a second caller appears ## Self-Check: PASSED - `cmd/keys.go` exists - `cmd/keys_test.go` exists - Stub removed from `cmd/stubs.go` - Commits `06594af`, `e2394ec` present in `git log` - `go build ./...` green - `go test ./... -count=1` green (all packages) - `go run . keys --help` lists six subcommands --- *Phase: 06-output-reporting* *Plan: 05* *Completed: 2026-04-05*