diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 246b54b..f8da2bf 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -59,12 +59,12 @@ Requirements for initial release. Each maps to roadmap phases. ### Key Management -- [ ] **KEYS-01**: keyhunter keys list — show all found keys (masked by default, --unmask for full) -- [ ] **KEYS-02**: keyhunter keys show — single key full detail (always unmasked) -- [ ] **KEYS-03**: keyhunter keys export --format=json|csv — export all keys with full values -- [ ] **KEYS-04**: keyhunter keys copy — copy full key to clipboard -- [ ] **KEYS-05**: keyhunter keys verify — verify specific key and show full detail -- [ ] **KEYS-06**: keyhunter keys delete — remove key from database +- [x] **KEYS-01**: keyhunter keys list — show all found keys (masked by default, --unmask for full) +- [x] **KEYS-02**: keyhunter keys show — single key full detail (always unmasked) +- [x] **KEYS-03**: keyhunter keys export --format=json|csv — export all keys with full values +- [x] **KEYS-04**: keyhunter keys copy — copy full key to clipboard +- [x] **KEYS-05**: keyhunter keys verify — verify specific key and show full detail +- [x] **KEYS-06**: keyhunter keys delete — remove key from database ### External Tool Import diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 5288443..0f9234b 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -146,7 +146,7 @@ Plans: - [x] 06-02-PLAN.md — JSONFormatter + CSVFormatter (full Finding fields, Unmask option) - [x] 06-03-PLAN.md — SARIF 2.1.0 formatter with custom structs (rule dedup, level mapping) - [x] 06-04-PLAN.md — pkg/storage/queries.go: Filters, ListFindingsFiltered, GetFinding, DeleteFinding -- [ ] 06-05-PLAN.md — cmd/keys.go command tree: list/show/export/copy/delete/verify (KEYS-01..06) +- [x] 06-05-PLAN.md — cmd/keys.go command tree: list/show/export/copy/delete/verify (KEYS-01..06) - [ ] 06-06-PLAN.md — scan --output registry dispatch + exit codes 0/1/2 (OUT-05, OUT-06) ### Phase 7: Import Adapters & CI/CD Integration diff --git a/.planning/STATE.md b/.planning/STATE.md index 78f112b..960958f 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,14 +3,14 @@ gsd_state_version: 1.0 milestone: v1.0 milestone_name: milestone status: executing -stopped_at: Completed 06-03-PLAN.md -last_updated: "2026-04-05T20:34:48.003Z" +stopped_at: Completed 06-05-PLAN.md +last_updated: "2026-04-05T20:40:28.139Z" last_activity: 2026-04-05 progress: total_phases: 18 completed_phases: 5 total_plans: 34 - completed_plans: 32 + completed_plans: 33 percent: 20 --- @@ -26,7 +26,7 @@ See: .planning/PROJECT.md (updated 2026-04-04) ## Current Position Phase: 06 (output-reporting) — EXECUTING -Plan: 2 of 6 +Plan: 3 of 6 Status: Ready to execute Last activity: 2026-04-05 @@ -76,6 +76,7 @@ Progress: [██░░░░░░░░] 20% | Phase 05 P05 | 12min | 2 tasks | 5 files | | Phase 06 P01 | 8m | 2 tasks | 7 files | | Phase 06 P03 | ~6m | 1 tasks | 2 files | +| Phase 06-output-reporting P05 | 4min | 2 tasks | 3 files | ## Accumulated Context @@ -106,6 +107,7 @@ Recent decisions affecting current work: - [Phase 05]: Verification runs in batch mode after scan completes (collect -> verify -> persist) with Result->Finding back-assignment via provider+masked-key tuple - [Phase 06]: Registry pattern for output formatters; TableFormatter strips ANSI when writer is not a TTY via zero-value lipgloss.Style - [Phase 06]: SARIF 2.1.0 via hand-rolled structs (no library) per CLAUDE.md +- [Phase 06-output-reporting]: keys export rejects SARIF (scan-only); keys show always unmasked; keys verify updates findings inline via db.SQL().Exec ### Pending Todos @@ -120,6 +122,6 @@ None yet. ## Session Continuity -Last session: 2026-04-05T20:32:29.674Z -Stopped at: Completed 06-03-PLAN.md +Last session: 2026-04-05T20:40:28.135Z +Stopped at: Completed 06-05-PLAN.md Resume file: None diff --git a/.planning/phases/06-output-reporting/06-05-SUMMARY.md b/.planning/phases/06-output-reporting/06-05-SUMMARY.md new file mode 100644 index 0000000..3d8cc67 --- /dev/null +++ b/.planning/phases/06-output-reporting/06-05-SUMMARY.md @@ -0,0 +1,122 @@ +--- +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*