7.9 KiB
keyhunter scan --verify command end-to-end
- --verify-timeout and --verify-workers user-facing flags
- VERIFY column + metadata line in table output (backward compatible)
affects:
- cmd/scan.go RunE now collects findings, verifies, then persists
- pkg/output/table.go now renders verify column conditionally
tech-stack:
added: []
patterns:
- "Collect-then-verify-then-persist batch pattern (verification is not per-finding streaming)"
- "Provider+masked-key tuple for back-assigning Result to Finding index"
- "Conditional table columns driven by anyVerified flag for backward compat"
key-files:
created:
- cmd/scan_test.go
- pkg/output/table_test.go
- .planning/phases/05-verification-engine/05-05-SUMMARY.md
modified:
- cmd/scan.go
- pkg/output/table.go
decisions:
- "Verification runs in batch after scan completes, not streamed per-finding — matches plan requirement and keeps consent prompt off the hot path."
- "Result→Finding back-assignment via provider+KeyMasked tuple (VerifyAll preserves neither order nor identity)."
- "Scoped scan_test.go to flag-registration only (per plan escape hatch) — full RunE integration test would require stdin injection refactor that's out of scope."
- "Table column conditionally added based on anyVerified to preserve pre-Phase-5 output format exactly when --verify is not used."
- "Metadata keys rendered in sorted order for deterministic test assertions and stable display."
metrics:
duration: "~12min"
completed: "2026-04-05T12:54:59Z"
tasks: 2
commits: 4
requirements: [VRFY-01, VRFY-04, VRFY-05]
Phase 5 Plan 05: Scan --verify Integration Summary
Objective
Wire Plans 05-02/03/04 together into the scan command: gate --verify behind consent, run the HTTPVerifier over collected findings, persist verify results, and render a new VERIFY column in the output table. Add --verify-timeout and --verify-workers flags.
What Was Built
Task 1: Scan command wiring (cmd/scan.go)
- Added
flagVerifyTimeout time.Duration(default 10s) andflagVerifyWorkers int(default 10) package-level vars. - Registered
--verify-timeoutand--verify-workersflags ininit(). - Imported
github.com/salvacybersec/keyhunter/pkg/verify. - Refactored
RunEscan loop:- Collect — drain
eng.Scanchannel into[]engine.Findingwithout persisting. - Verify — when
--verify && len(findings) > 0, callverify.EnsureConsent(db, os.Stdin, os.Stderr). If declined, print notice to stderr and skip. If granted, createverify.NewHTTPVerifier(flagVerifyTimeout), callVerifyAll(ctx, findings, reg, flagVerifyWorkers), and back-assign eachResultto itsFindingvia aprovider|maskedlookup index (populatingVerified,VerifyStatus,VerifyHTTPCode,VerifyMetadata,VerifyError). - Persist — loop over enriched findings once, build
storage.Findingwith verify_* fields, calldb.SaveFinding.
- Collect — drain
- Output rendering unchanged at this layer (Task 2 handles the column).
Task 2: Output table verify column (pkg/output/table.go)
- Added five lipgloss styles:
styleVerifyLive(green),styleVerifyDead(red),styleVerifyRate(yellow),styleVerifyErr(red),styleVerifyUnk(gray). PrintFindingsnow scans findings once to computeanyVerified.- When
anyVerifiedis true, the header gains aVERIFYcolumn and each row printsverifySymbol(f)(✓ live / ✗ dead / ⚠ rate / ! err / ? unk). When false, the layout matches the exact pre-Phase-5 output (backward compatible —TestPrintFindings_NoVerification_Unchangedenforces this). - When
len(f.VerifyMetadata) > 0, an indented secondary line↳ key: value, key: valueis rendered with keys sorted alphabetically for deterministic output. - New
verifySymbol(f engine.Finding) stringhelper mapsVerifyStatus→ colored glyph.
Tests
cmd/scan_test.go (new)
TestScan_VerifyFlags_Registered/verify-timeout_flag_exists_with_10s_defaultTestScan_VerifyFlags_Registered/verify-workers_flag_exists_with_default_10TestScan_VerifyFlags_Registered/verify_flag_still_present
All three pass. Per plan escape hatch, behavioral integration tests (declined/granted consent end-to-end) were deferred because the current scanCmd.RunE reads os.Stdin directly without injection — refactoring it to accept an injected reader would ripple outside Plan 05-05's scope. Behavior is exercised indirectly via pkg/verify/consent_test.go + pkg/verify/verifier_test.go from Wave 1.
pkg/output/table_test.go (new)
TestPrintFindings_NoVerification_Unchanged— asserts VERIFY header absent when no findings are verified (backward compat lock).TestPrintFindings_LiveVerification_ShowsCheck— verified live finding produces VERIFY header and "live" text in output.TestPrintFindings_Metadata_Rendered— metadata pairs render on indented line, sorted alphabetically (org before tier).
Uses os.Pipe + os.Stdout swap to capture stdout, strips ANSI escape sequences via regex before assertions. All three pass.
Verification Results
$ go build ./...
(clean)
$ go test ./...
ok github.com/salvacybersec/keyhunter/cmd 0.005s
ok github.com/salvacybersec/keyhunter/pkg/engine 0.242s
ok github.com/salvacybersec/keyhunter/pkg/engine/sources (cached)
ok github.com/salvacybersec/keyhunter/pkg/legal (cached)
ok github.com/salvacybersec/keyhunter/pkg/output 0.004s
ok github.com/salvacybersec/keyhunter/pkg/providers 0.944s
ok github.com/salvacybersec/keyhunter/pkg/storage (cached)
ok github.com/salvacybersec/keyhunter/pkg/verify 0.393s
$ go run . scan --help | grep verify
--verify actively verify found keys (opt-in, Phase 5)
--verify-timeout duration per-key verification HTTP timeout (default 10s) (default 10s)
--verify-workers int parallel workers for key verification (default 10) (default 10)
Commits
| Hash | Message |
|---|---|
d537078 |
test(05-05): add failing test for --verify-timeout/--verify-workers flags |
6fc0abe |
feat(05-05): wire --verify into scan pipeline with consent gate |
edba8fb |
test(05-05): add failing tests for VERIFY column and metadata rendering |
cc9dabe |
feat(05-05): render VERIFY column and metadata line in output table |
Deviations from Plan
None — plan executed as written. Test scope was explicitly scoped down to flag-registration per the plan's own escape hatch ("Prefer the lightweight flag-registration test to avoid pulling the full scan path into tests").
Success Criteria Check
- VRFY-01: consent prompt gates --verify on first use (via
verify.EnsureConsentcall before verifier) - VRFY-04: metadata displayed under finding when extracted (indented
↳line) - VRFY-05:
--verify-timeoutand--verify-workersflags work (threaded throughNewHTTPVerifier+VerifyAllworkers arg) - Unverified scans unchanged from Phase 4 behavior (backward-compat test enforces identical header format)
go build ./...cleango test ./... -vgreen across all modified packagesgo run . scan --helpshows--verify,--verify-timeout,--verify-workers
Known Stubs
None. All wiring is live; no placeholders in the verified code paths.
Self-Check: PASSED
All 5 key files exist on disk. All 4 commit hashes are present in git history.