docs(05-02): complete legal disclaimer & consent prompt plan
This commit is contained in:
@@ -41,12 +41,12 @@ Requirements for initial release. Each maps to roadmap phases.
|
|||||||
|
|
||||||
### Verification
|
### Verification
|
||||||
|
|
||||||
- [ ] **VRFY-01**: Active key verification via lightweight API calls when --verify flag is set
|
- [x] **VRFY-01**: Active key verification via lightweight API calls when --verify flag is set
|
||||||
- [x] **VRFY-02**: Verification is opt-in only (off by default) with consent prompt on first use
|
- [x] **VRFY-02**: Verification is opt-in only (off by default) with consent prompt on first use
|
||||||
- [x] **VRFY-03**: Each provider YAML defines verify endpoint, method, headers, success/failure codes
|
- [x] **VRFY-03**: Each provider YAML defines verify endpoint, method, headers, success/failure codes
|
||||||
- [ ] **VRFY-04**: Verification extracts additional metadata (org, rate limit, permissions) when available
|
- [ ] **VRFY-04**: Verification extracts additional metadata (org, rate limit, permissions) when available
|
||||||
- [ ] **VRFY-05**: Configurable verification timeout (default 10s, --verify-timeout flag)
|
- [ ] **VRFY-05**: Configurable verification timeout (default 10s, --verify-timeout flag)
|
||||||
- [ ] **VRFY-06**: Legal disclaimer and documentation ships with verification feature
|
- [x] **VRFY-06**: Legal disclaimer and documentation ships with verification feature
|
||||||
|
|
||||||
### Output & Reporting
|
### Output & Reporting
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ Plans:
|
|||||||
|
|
||||||
Plans:
|
Plans:
|
||||||
- [x] 05-01-PLAN.md — Wave 0: extend VerifySpec schema, Finding struct, storage schema; add gjson dep
|
- [x] 05-01-PLAN.md — Wave 0: extend VerifySpec schema, Finding struct, storage schema; add gjson dep
|
||||||
- [ ] 05-02-PLAN.md — LEGAL.md + pkg/legal embed + consent prompt + keyhunter legal command
|
- [x] 05-02-PLAN.md — LEGAL.md + pkg/legal embed + consent prompt + keyhunter legal command
|
||||||
- [ ] 05-03-PLAN.md — pkg/verify HTTPVerifier: template sub, gjson metadata extraction, ants pool
|
- [ ] 05-03-PLAN.md — pkg/verify HTTPVerifier: template sub, gjson metadata extraction, ants pool
|
||||||
- [x] 05-04-PLAN.md — Update 12 Tier 1 provider YAMLs with extended verify specs + guardrail test
|
- [x] 05-04-PLAN.md — Update 12 Tier 1 provider YAMLs with extended verify specs + guardrail test
|
||||||
- [ ] 05-05-PLAN.md — cmd/scan.go --verify wiring + --verify-timeout/workers flags + output verify column
|
- [ ] 05-05-PLAN.md — cmd/scan.go --verify wiring + --verify-timeout/workers flags + output verify column
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ gsd_state_version: 1.0
|
|||||||
milestone: v1.0
|
milestone: v1.0
|
||||||
milestone_name: milestone
|
milestone_name: milestone
|
||||||
status: executing
|
status: executing
|
||||||
stopped_at: Completed 05-04-PLAN.md
|
stopped_at: Completed 05-02-PLAN.md
|
||||||
last_updated: "2026-04-05T12:48:10.887Z"
|
last_updated: "2026-04-05T12:48:48.980Z"
|
||||||
last_activity: 2026-04-05
|
last_activity: 2026-04-05
|
||||||
progress:
|
progress:
|
||||||
total_phases: 18
|
total_phases: 18
|
||||||
completed_phases: 4
|
completed_phases: 4
|
||||||
total_plans: 28
|
total_plans: 28
|
||||||
completed_plans: 25
|
completed_plans: 26
|
||||||
percent: 20
|
percent: 20
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ See: .planning/PROJECT.md (updated 2026-04-04)
|
|||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 05 (verification-engine) — EXECUTING
|
Phase: 05 (verification-engine) — EXECUTING
|
||||||
Plan: 3 of 5
|
Plan: 4 of 5
|
||||||
Status: Ready to execute
|
Status: Ready to execute
|
||||||
Last activity: 2026-04-05
|
Last activity: 2026-04-05
|
||||||
|
|
||||||
@@ -71,6 +71,7 @@ Progress: [██░░░░░░░░] 20%
|
|||||||
| Phase 04 P05 | 3min | 1 tasks | 2 files |
|
| Phase 04 P05 | 3min | 1 tasks | 2 files |
|
||||||
| Phase 05 P01 | 3m43s | 2 tasks | 10 files |
|
| Phase 05 P01 | 3m43s | 2 tasks | 10 files |
|
||||||
| Phase 05 P04 | 10m | 2 tasks | 25 files |
|
| Phase 05 P04 | 10m | 2 tasks | 25 files |
|
||||||
|
| Phase 05-verification-engine P02 | 7m | 2 tasks | 9 files |
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
|
|
||||||
@@ -95,6 +96,8 @@ Recent decisions affecting current work:
|
|||||||
- [Phase 04]: Introduced selectSource dispatcher with sourceFlags struct for testable CLI source routing
|
- [Phase 04]: Introduced selectSource dispatcher with sourceFlags struct for testable CLI source routing
|
||||||
- [Phase 05]: Keep legacy VerifySpec ValidStatus/InvalidStatus alongside canonical SuccessCodes/FailureCodes; Effective*() helpers pick canonical-first with fallback
|
- [Phase 05]: Keep legacy VerifySpec ValidStatus/InvalidStatus alongside canonical SuccessCodes/FailureCodes; Effective*() helpers pick canonical-first with fallback
|
||||||
- [Phase 05]: Store Finding.VerifyMetadata as JSON TEXT column; legacy DBs migrated in-place via PRAGMA table_info + conditional ALTER TABLE in storage.Open()
|
- [Phase 05]: Store Finding.VerifyMetadata as JSON TEXT column; legacy DBs migrated in-place via PRAGMA table_info + conditional ALTER TABLE in storage.Open()
|
||||||
|
- [Phase 05-verification-engine]: LEGAL.md dual-location mirror (root + pkg/legal/) required because go:embed cannot traverse parents — mirrors Phase 1 providers pattern
|
||||||
|
- [Phase 05-verification-engine]: verify.consent setting: granted is sticky across runs; declined is not — users who initially refuse can change mind without manual reset
|
||||||
|
|
||||||
### Pending Todos
|
### Pending Todos
|
||||||
|
|
||||||
@@ -109,6 +112,6 @@ None yet.
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-04-05T12:48:10.879Z
|
Last session: 2026-04-05T12:48:48.976Z
|
||||||
Stopped at: Completed 05-04-PLAN.md
|
Stopped at: Completed 05-02-PLAN.md
|
||||||
Resume file: None
|
Resume file: None
|
||||||
|
|||||||
151
.planning/phases/05-verification-engine/05-02-SUMMARY.md
Normal file
151
.planning/phases/05-verification-engine/05-02-SUMMARY.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
---
|
||||||
|
phase: 05-verification-engine
|
||||||
|
plan: 02
|
||||||
|
subsystem: verification-consent
|
||||||
|
tags: [legal, consent, embed, cli, verify]
|
||||||
|
requirements: [VRFY-01, VRFY-06]
|
||||||
|
dependency-graph:
|
||||||
|
requires:
|
||||||
|
- "pkg/storage settings table GetSetting/SetSetting (Phase 1)"
|
||||||
|
- "cmd/root.go cobra command tree (Phase 1)"
|
||||||
|
provides:
|
||||||
|
- "pkg/legal.Text() — compile-time embedded LEGAL.md"
|
||||||
|
- "pkg/verify.EnsureConsent(db, in, out) — consent gate for --verify"
|
||||||
|
- "keyhunter legal subcommand"
|
||||||
|
- "verify.consent setting key contract"
|
||||||
|
affects:
|
||||||
|
- "cmd/root.go (new subcommand registration)"
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "go:embed dual-location mirror (root LEGAL.md + pkg/legal/LEGAL.md), same pattern as providers/ vs pkg/providers/definitions/"
|
||||||
|
- "Sticky-granted, transient-declined consent semantics (granted persists, declined re-prompts)"
|
||||||
|
- "io.Reader/io.Writer injection for testable interactive prompts"
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- "LEGAL.md"
|
||||||
|
- "pkg/legal/LEGAL.md"
|
||||||
|
- "pkg/legal/legal.go"
|
||||||
|
- "pkg/legal/legal_test.go"
|
||||||
|
- "cmd/legal.go"
|
||||||
|
- "pkg/verify/consent.go"
|
||||||
|
- "pkg/verify/consent_test.go"
|
||||||
|
- ".planning/phases/05-verification-engine/05-02-SUMMARY.md"
|
||||||
|
modified:
|
||||||
|
- "cmd/root.go"
|
||||||
|
decisions:
|
||||||
|
- "LEGAL.md duplicated at repo root and pkg/legal/ because go:embed cannot traverse parents"
|
||||||
|
- "verify.consent setting: 'granted' is sticky, 'declined' is not — users who initially refuse can change their mind on the next run without manual reset"
|
||||||
|
- "EnsureConsent takes io.Reader/io.Writer rather than reading os.Stdin/os.Stdout directly, so tests can drive it with strings.NewReader + bytes.Buffer"
|
||||||
|
- "Consent prompt text explicitly mentions CFAA, Computer Misuse Act, GDPR, provider ToS and directs users to `keyhunter legal` for the full document"
|
||||||
|
metrics:
|
||||||
|
duration: "~7m"
|
||||||
|
completed_date: "2026-04-05"
|
||||||
|
tasks_completed: 2
|
||||||
|
files_created: 8
|
||||||
|
files_modified: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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
|
||||||
|
//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
|
||||||
|
```go
|
||||||
|
func EnsureConsent(db *storage.DB, in io.Reader, out io.Writer) (bool, error)
|
||||||
|
```
|
||||||
|
Behavior:
|
||||||
|
1. Reads `verify.consent` from the settings table.
|
||||||
|
2. If previously `"granted"` → returns `(true, nil)` immediately, writes nothing.
|
||||||
|
3. Otherwise writes a multi-line warning to `out` referencing "legal implications", CFAA, Computer Misuse Act, GDPR, provider ToS, and pointing the user to `keyhunter legal`.
|
||||||
|
4. Reads one line from `in`, lowercases and trims.
|
||||||
|
5. If the answer is `"yes"` → persists `"granted"` and returns `(true, nil)`.
|
||||||
|
6. Any other input (including `"y"`, `"no"`, empty) → persists `"declined"` and returns `(false, nil)`.
|
||||||
|
7. 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 output
|
||||||
|
- `TestEnsureConsent_TypeYes` — prompts, persists granted, prompt text contains "legal implications" and "keyhunter legal"
|
||||||
|
- `TestEnsureConsent_TypeYesUppercase` — case-insensitive match
|
||||||
|
- `TestEnsureConsent_TypeNo` — persists declined, returns false
|
||||||
|
- `TestEnsureConsent_Empty` — empty input treated as decline
|
||||||
|
- `TestEnsureConsent_DeclinedNotSticky` — seed declined + type yes → re-prompted and now granted
|
||||||
|
|
||||||
|
All 6 pass.
|
||||||
|
|
||||||
|
## TDD Trace (Task 2)
|
||||||
|
|
||||||
|
- **RED** (`e5f7214`) — wrote `consent_test.go` referencing `ConsentSettingKey`, `ConsentGranted`, `ConsentDeclined`, and `EnsureConsent` before the implementation existed. Build failed with "undefined:" errors for all 5 symbols. ✅ confirmed failing.
|
||||||
|
- **GREEN** (`d4c1403`) — created `consent.go` with 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 in `TestEnsureConsent_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 legal` prints the embedded LEGAL.md to stdout (`grep -c CFAA` returns 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.EnsureConsent` at the top of the scan/verify entry point once this plan lands
|
||||||
|
- **05-04 (verify CLI flag integration)** — has `ConsentSettingKey`, `ConsentGranted`, `ConsentDeclined` constants 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
|
||||||
Reference in New Issue
Block a user