docs(09): create phase plan
This commit is contained in:
185
.planning/phases/09-osint-infrastructure/09-05-PLAN.md
Normal file
185
.planning/phases/09-osint-infrastructure/09-05-PLAN.md
Normal file
@@ -0,0 +1,185 @@
|
||||
---
|
||||
phase: 09-osint-infrastructure
|
||||
plan: 05
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["09-01", "09-02", "09-03", "09-04"]
|
||||
files_modified:
|
||||
- cmd/recon.go
|
||||
- cmd/stubs.go
|
||||
- cmd/root.go
|
||||
autonomous: true
|
||||
requirements: [RECON-INFRA-08]
|
||||
must_haves:
|
||||
truths:
|
||||
- "`keyhunter recon full` runs Engine.SweepAll + Dedup and prints a masked findings table"
|
||||
- "`keyhunter recon list` prints the registered source names one per line"
|
||||
- "--stealth, --respect-robots (default true), --query flags exist on `recon full`"
|
||||
- "ExampleSource is registered at init() so Phase 9 ships a demonstrable pipeline"
|
||||
- "The stub reconCmd in cmd/stubs.go is removed; cmd/recon.go owns the command tree"
|
||||
artifacts:
|
||||
- path: "cmd/recon.go"
|
||||
provides: "reconCmd with subcommands `full` and `list`, flag wiring, source registration"
|
||||
contains: "var reconCmd"
|
||||
- path: "cmd/stubs.go"
|
||||
provides: "reconCmd stub removed; other stubs unchanged"
|
||||
key_links:
|
||||
- from: "cmd/recon.go"
|
||||
to: "pkg/recon.Engine"
|
||||
via: "NewEngine + Register(ExampleSource{}) + SweepAll"
|
||||
pattern: "recon\\.NewEngine"
|
||||
- from: "cmd/recon.go"
|
||||
to: "pkg/recon.Dedup"
|
||||
via: "Dedup applied to SweepAll results before printing"
|
||||
pattern: "recon\\.Dedup"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Wire the recon package into the Cobra CLI with `keyhunter recon full` and `keyhunter recon list`. Remove the stub reconCmd from cmd/stubs.go. Register ExampleSource at init() so `recon full` produces visible output end-to-end on a fresh clone.
|
||||
|
||||
Purpose: Satisfies RECON-INFRA-08 "Recon full command — parallel sweep across all sources with deduplication". Completes the phase's user-facing entrypoint.
|
||||
Output: cmd/recon.go (new), cmd/stubs.go (stub removed), cmd/root.go (registration unchanged or updated)
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/phases/09-osint-infrastructure/09-CONTEXT.md
|
||||
@cmd/stubs.go
|
||||
@.planning/phases/09-osint-infrastructure/09-01-SUMMARY.md
|
||||
@.planning/phases/09-osint-infrastructure/09-03-SUMMARY.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Remove reconCmd stub from cmd/stubs.go</name>
|
||||
<files>cmd/stubs.go</files>
|
||||
<action>
|
||||
Delete the `var reconCmd = &cobra.Command{...}` block from cmd/stubs.go. Leave verifyCmd, serveCmd, scheduleCmd untouched. The real reconCmd will be declared in cmd/recon.go (Task 2).
|
||||
|
||||
Verify cmd/root.go still references `reconCmd` — it will resolve to the new declaration in cmd/recon.go (same package `cmd`).
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd /home/salva/Documents/apikey && ! grep -q 'var reconCmd' cmd/stubs.go</automated>
|
||||
</verify>
|
||||
<done>cmd/stubs.go no longer declares reconCmd; file still compiles with other stubs.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Create cmd/recon.go with full and list subcommands</name>
|
||||
<files>cmd/recon.go</files>
|
||||
<action>
|
||||
Create cmd/recon.go declaring `var reconCmd` plus subcommands `reconFullCmd` and `reconListCmd`. Flag wiring:
|
||||
- `--stealth` bool, default false
|
||||
- `--respect-robots` bool, default true
|
||||
- `--query` string, default "" (empty -> sources use their own default keywords)
|
||||
|
||||
```go
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/salvacybersec/keyhunter/pkg/recon"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
reconStealth bool
|
||||
reconRespectRobots bool
|
||||
reconQuery string
|
||||
)
|
||||
|
||||
var reconCmd = &cobra.Command{
|
||||
Use: "recon",
|
||||
Short: "Run OSINT recon across internet sources",
|
||||
Long: "Run OSINT recon sweeps across registered sources. Phase 9 ships with an ExampleSource stub; real sources land in Phases 10-16.",
|
||||
}
|
||||
|
||||
var reconFullCmd = &cobra.Command{
|
||||
Use: "full",
|
||||
Short: "Sweep all enabled sources in parallel and deduplicate findings",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
eng := buildReconEngine()
|
||||
cfg := recon.Config{
|
||||
Stealth: reconStealth,
|
||||
RespectRobots: reconRespectRobots,
|
||||
Query: reconQuery,
|
||||
}
|
||||
ctx := context.Background()
|
||||
all, err := eng.SweepAll(ctx, cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("recon sweep: %w", err)
|
||||
}
|
||||
deduped := recon.Dedup(all)
|
||||
fmt.Printf("recon: swept %d sources, %d findings (%d after dedup)\n", len(eng.List()), len(all), len(deduped))
|
||||
for _, f := range deduped {
|
||||
fmt.Printf(" [%s] %s %s %s\n", f.SourceType, f.ProviderName, f.KeyMasked, f.Source)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var reconListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List registered recon sources",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
eng := buildReconEngine()
|
||||
for _, name := range eng.List() {
|
||||
fmt.Println(name)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// buildReconEngine constructs the recon Engine with all sources registered.
|
||||
// Phase 9 ships ExampleSource only; Phases 10-16 will add real sources here
|
||||
// (or via a registration side-effect in their packages).
|
||||
func buildReconEngine() *recon.Engine {
|
||||
e := recon.NewEngine()
|
||||
e.Register(recon.ExampleSource{})
|
||||
return e
|
||||
}
|
||||
|
||||
func init() {
|
||||
reconFullCmd.Flags().BoolVar(&reconStealth, "stealth", false, "enable UA rotation and jitter delays")
|
||||
reconFullCmd.Flags().BoolVar(&reconRespectRobots, "respect-robots", true, "respect robots.txt for web-scraping sources")
|
||||
reconFullCmd.Flags().StringVar(&reconQuery, "query", "", "override query sent to each source")
|
||||
reconCmd.AddCommand(reconFullCmd)
|
||||
reconCmd.AddCommand(reconListCmd)
|
||||
}
|
||||
```
|
||||
|
||||
Do NOT modify cmd/root.go unless `rootCmd.AddCommand(reconCmd)` is missing. (It currently exists because the stub was registered there.)
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd /home/salva/Documents/apikey && go build ./... && go run . recon list | grep -q '^example$'</automated>
|
||||
</verify>
|
||||
<done>`keyhunter recon list` prints "example". `keyhunter recon full` prints 2 findings from ExampleSource with "recon: swept 1 sources, 2 findings (2 after dedup)".</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- `go build ./...` succeeds
|
||||
- `go run . recon list` prints `example`
|
||||
- `go run . recon full` prints "recon: swept 1 sources, 2 findings (2 after dedup)" and two lines with [recon:example]
|
||||
- `go run . recon full --stealth --query=test` runs without error
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- reconCmd owned by cmd/recon.go, stub removed from cmd/stubs.go
|
||||
- `recon full` and `recon list` subcommands work end-to-end
|
||||
- Dedup wired through the SweepAll result
|
||||
- Flags --stealth, --respect-robots (default true), --query all parse
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/09-osint-infrastructure/09-05-SUMMARY.md`
|
||||
</output>
|
||||
</content>
|
||||
Reference in New Issue
Block a user