7.9 KiB
phase, plan, subsystem, tags, requirements, dependency-graph, tech-stack, key-files, decisions, metrics
| phase | plan | subsystem | tags | requirements | dependency-graph | tech-stack | key-files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 05-verification-engine | 02 | verification-consent |
|
|
|
|
|
|
|
Phase 5 Plan 2: Legal Disclaimer & Consent Prompt Summary
One-liner: Shipped LEGAL.md embedded in the binary via go:embed, added a keyhunter legal subcommand, and implemented verify.EnsureConsent with sticky-granted / transient-declined semantics backed by the SQLite settings table.
What Was Built
LEGAL.md (repo root + pkg/legal mirror)
A 109-line user-facing disclaimer covering:
- Purpose — what the verification feature does at a high level
- What --verify Does — lightweight single HTTP call per key, no mutations, masked output
- Legal Considerations — CFAA (18 U.S.C. § 1030), UK Computer Misuse Act 1990, EU Directive 2013/40/EU, provider ToS
- Responsible Use — own keys, authorized engagements, own CI/CD only
- Responsible Disclosure — security.txt, never publish, reasonable rotation window
- Disclaimer — AS IS, no warranty, user bears sole responsibility
- Consent Record — explains the first-run prompt and where the decision is persisted
The root copy is what users read; pkg/legal/LEGAL.md is a byte-identical mirror required by //go:embed (Go embed directives cannot traverse ..). This mirrors the Phase 1 decision to keep providers/*.yaml user-visible with a pkg/providers/definitions/*.yaml embed mirror.
pkg/legal package
//go:embed LEGAL.md
var legalMarkdown string
func Text() string { return legalMarkdown }
Two tests: TestText_NonEmpty (>500 bytes) and TestText_ContainsKeyPhrases (contains "CFAA", "Responsible Use", "Disclaimer"). Both pass.
keyhunter legal command
cmd/legal.go registers a trivial legalCmd that prints legal.Text() to stdout. Wired in cmd/root.go alongside the existing scanCmd, providersCmd, configCmd. go run . legal | grep CFAA returns a match.
pkg/verify.EnsureConsent
func EnsureConsent(db *storage.DB, in io.Reader, out io.Writer) (bool, error)
Behavior:
- Reads
verify.consentfrom the settings table. - If previously
"granted"→ returns(true, nil)immediately, writes nothing. - Otherwise writes a multi-line warning to
outreferencing "legal implications", CFAA, Computer Misuse Act, GDPR, provider ToS, and pointing the user tokeyhunter legal. - Reads one line from
in, lowercases and trims. - If the answer is
"yes"→ persists"granted"and returns(true, nil). - Any other input (including
"y","no", empty) → persists"declined"and returns(false, nil). - Declined is not sticky — the next call re-prompts (found && value != "granted" falls through to the prompt branch).
Six test cases cover the full decision matrix:
TestEnsureConsent_GrantedPrevious— sticky short-circuit with no prompt outputTestEnsureConsent_TypeYes— prompts, persists granted, prompt text contains "legal implications" and "keyhunter legal"TestEnsureConsent_TypeYesUppercase— case-insensitive matchTestEnsureConsent_TypeNo— persists declined, returns falseTestEnsureConsent_Empty— empty input treated as declineTestEnsureConsent_DeclinedNotSticky— seed declined + type yes → re-prompted and now granted
All 6 pass.
TDD Trace (Task 2)
- RED (
e5f7214) — wroteconsent_test.goreferencingConsentSettingKey,ConsentGranted,ConsentDeclined, andEnsureConsentbefore the implementation existed. Build failed with "undefined:" errors for all 5 symbols. ✅ confirmed failing. - GREEN (
d4c1403) — createdconsent.gowith the constants and function. One iteration needed: the initial prompt wrapping split "legal implications" across two lines ("legal\nimplications"), which broke the literal substring assertion inTestEnsureConsent_TypeYes. Rewrapped the prompt text to keep the phrase contiguous on one line. All 6 tests green. - REFACTOR — none needed; implementation matched the plan's reference code modulo the prompt rewrap.
Verification
$ go build ./...
(clean)
$ go test ./pkg/legal/... ./pkg/verify/...
ok github.com/salvacybersec/keyhunter/pkg/legal 0.002s
ok github.com/salvacybersec/keyhunter/pkg/verify 0.331s
$ go run . legal | grep -c CFAA
1
Must-haves truths verified:
- ✅
keyhunter legalprints the embedded LEGAL.md to stdout (grep -c CFAAreturns 1) - ✅ First invocation prompts; "yes" (case-insensitive) proceeds; anything else aborts (6 tests)
- ✅ Consent decision stored in settings and not re-prompted when granted (
TestEnsureConsent_GrantedPrevious) - ✅ LEGAL.md ships via
go:embed(grep -q "go:embed LEGAL.md" pkg/legal/legal.go— present)
Commits
| Hash | Type | Summary |
|---|---|---|
260e342 |
feat | Add LEGAL.md, pkg/legal embed, and keyhunter legal command |
e5f7214 |
test | RED: failing consent tests |
d4c1403 |
feat | GREEN: implement EnsureConsent |
Deviations from Plan
None — plan executed as written. One minor in-task iteration: the prompt text in the reference code happened to wrap "legal implications" across two Fprintln calls, which broke the test assertion on literal substrings. Rewrapped the prompt lines to keep "legal implications" contiguous. Functionally identical; no new behavior.
Out-of-Scope Note
During verification I observed an uncommitted modification to pkg/verify/verifier.go unrelated to this plan — it contains an in-progress HTTPVerifier implementation from what appears to be Plan 05-03 work (the git log shows 3ceccd9 test(05-03): add failing tests for HTTPVerifier single-key verification). I deliberately did not touch that file; it will be committed by plan 05-03 executor.
Self-Check: PASSED
All 8 claimed files exist on disk. All 3 claimed commits exist in git log.
Dependencies Satisfied For Next Plans
- 05-03 (HTTP verifier) — can call
verify.EnsureConsentat the top of the scan/verify entry point once this plan lands - 05-04 (verify CLI flag integration) — has
ConsentSettingKey,ConsentGranted,ConsentDeclinedconstants to key off - Any future plan that needs to surface legal disclaimers or track user acknowledgements — can follow the same
pkg/legal+ dual-LEGAL.md embed pattern