feat(08-06): add dorks run/add/delete with injectable executor

- Add run subcommand dispatching via dorks.Runner (github live,
  other sources wrapped into friendly ErrSourceNotImplemented)
- Add add subcommand with source/category validation and embedded
  ID collision guard
- Add delete subcommand that refuses embedded dork ids
- Expose newGitHubExecutor as package var for test injection
- cmd/dorks_test.go covers list filtering, add persistence + list
  merge marker, invalid source rejection, embedded collision,
  embedded delete refusal, custom delete, shodan not-implemented
  path, GitHub missing-token auth hint, fake executor run, yaml
  export merge, and info for both origins

Completes DORK-03 (list/run/add/export/info/delete) and DORK-04
(--source/--category filtering).
This commit is contained in:
salvacybersec
2026-04-06 00:27:41 +03:00
parent b7934ce169
commit c281c96040

231
cmd/dorks_test.go Normal file
View File

@@ -0,0 +1,231 @@
package cmd
import (
"bytes"
"context"
"path/filepath"
"strings"
"testing"
"github.com/salvacybersec/keyhunter/pkg/dorks"
"github.com/salvacybersec/keyhunter/pkg/storage"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// setupDorksTest isolates viper state, points database.path at a tempdir
// DB (auto-migrated by storage.Open), and returns the DB path plus a
// t.Cleanup-registered flag reset.
func setupDorksTest(t *testing.T) string {
t.Helper()
dir := t.TempDir()
dbPath := filepath.Join(dir, "dorks.db")
viper.Reset()
viper.Set("database.path", dbPath)
resetDorksFlags()
// Touch the DB once so schema migrations run deterministically even
// when the subcommand under test doesn't reach the DB code path.
db, err := storage.Open(dbPath)
require.NoError(t, err)
require.NoError(t, db.Close())
t.Cleanup(func() {
resetDorksFlags()
viper.Reset()
})
return dbPath
}
// execDorks runs rootCmd with the given args and returns captured stdout
// (which Cobra wires for both Out and Err when SetOut is used) alongside
// the terminal error from Execute.
func execDorks(t *testing.T, args ...string) (string, error) {
t.Helper()
var buf bytes.Buffer
rootCmd.SetOut(&buf)
rootCmd.SetErr(&buf)
rootCmd.SetArgs(args)
err := rootCmd.Execute()
return buf.String(), err
}
// fakeExecutor is an in-memory dorks.Executor used to exercise `dorks run`
// without touching the real GitHub API.
type fakeExecutor struct {
source string
matches []dorks.Match
err error
called int
}
func (f *fakeExecutor) Source() string { return f.source }
func (f *fakeExecutor) Execute(ctx context.Context, d dorks.Dork, limit int) ([]dorks.Match, error) {
f.called++
if f.err != nil {
return nil, f.err
}
out := make([]dorks.Match, len(f.matches))
for i, m := range f.matches {
m.DorkID = d.ID
out[i] = m
}
return out, nil
}
func TestDorksList_FilterBySourceAndCategory(t *testing.T) {
setupDorksTest(t)
out, err := execDorks(t, "dorks", "list", "--source=github", "--category=frontier")
require.NoError(t, err)
assert.Contains(t, out, "openai-github-envfile")
assert.Contains(t, out, "anthropic-github-envfile")
// google-ai dorks are frontier github too; shodan must be filtered out.
assert.NotContains(t, out, "shodan-")
}
func TestDorksAdd_PersistsCustomDorkAndShowsInList(t *testing.T) {
setupDorksTest(t)
_, err := execDorks(t, "dorks", "add",
"--source=github", "--category=frontier",
"--id=my-custom-dork", "--name=Custom", "--query=foo extension:env",
"--description=test dork")
require.NoError(t, err)
resetDorksFlags()
out, err := execDorks(t, "dorks", "list", "--source=github")
require.NoError(t, err)
assert.Contains(t, out, "*my-custom-dork")
}
func TestDorksAdd_RejectsInvalidSource(t *testing.T) {
setupDorksTest(t)
_, err := execDorks(t, "dorks", "add",
"--source=notreal", "--category=frontier",
"--id=x", "--query=foo")
require.Error(t, err)
assert.Contains(t, err.Error(), "not one of")
}
func TestDorksAdd_RejectsEmbeddedIDCollision(t *testing.T) {
setupDorksTest(t)
_, err := execDorks(t, "dorks", "add",
"--source=github", "--category=frontier",
"--id=openai-github-envfile", "--query=foo")
require.Error(t, err)
assert.Contains(t, err.Error(), "collides with an embedded dork")
}
func TestDorksDelete_EmbeddedRefused(t *testing.T) {
setupDorksTest(t)
_, err := execDorks(t, "dorks", "delete", "openai-github-envfile")
require.Error(t, err)
assert.Contains(t, err.Error(), "embedded dorks cannot be deleted")
}
func TestDorksDelete_RemovesCustomDork(t *testing.T) {
setupDorksTest(t)
_, err := execDorks(t, "dorks", "add",
"--source=github", "--category=frontier",
"--id=to-delete", "--query=bar")
require.NoError(t, err)
resetDorksFlags()
out, err := execDorks(t, "dorks", "delete", "to-delete")
require.NoError(t, err)
assert.Contains(t, out, "Deleted custom dork")
resetDorksFlags()
out, err = execDorks(t, "dorks", "list", "--source=github")
require.NoError(t, err)
assert.NotContains(t, out, "*to-delete")
}
func TestDorksRun_ShodanReturnsNotImplemented(t *testing.T) {
setupDorksTest(t)
// Use a shodan dork id if one exists; if none, add one via add first.
// Shodan live execution is not wired in Phase 8.
_, err := execDorks(t, "dorks", "add",
"--source=shodan", "--category=infrastructure",
"--id=shodan-test", "--query=foo")
require.NoError(t, err)
resetDorksFlags()
_, err = execDorks(t, "dorks", "run", "--source=shodan", "--id=shodan-test")
require.Error(t, err)
assert.Contains(t, err.Error(), "not yet implemented")
assert.Contains(t, err.Error(), "Phase 9-16")
}
func TestDorksRun_GitHubMissingTokenReturnsAuthHint(t *testing.T) {
setupDorksTest(t)
// No GITHUB_TOKEN set, no dorks.github.token — executor should yield
// ErrMissingAuth wrapped with a setup hint.
viper.Set("dorks.github.token", "")
_, err := execDorks(t, "dorks", "run",
"--source=github", "--id=openai-github-envfile", "--limit=1")
require.Error(t, err)
assert.Contains(t, err.Error(), "GITHUB_TOKEN")
}
func TestDorksRun_GitHubWithInjectedFakeExecutor(t *testing.T) {
setupDorksTest(t)
fake := &fakeExecutor{
source: "github",
matches: []dorks.Match{
{Source: "github", URL: "https://example.com/x", Path: "repo/x.env", Snippet: "sk-proj-XXXX"},
},
}
original := newGitHubExecutor
newGitHubExecutor = func() dorks.Executor { return fake }
t.Cleanup(func() { newGitHubExecutor = original })
out, err := execDorks(t, "dorks", "run",
"--source=github", "--id=openai-github-envfile", "--limit=1")
require.NoError(t, err)
assert.Equal(t, 1, fake.called)
assert.Contains(t, out, "openai-github-envfile")
assert.Contains(t, out, "https://example.com/x")
}
func TestDorksExport_YAMLContainsEmbeddedAndCustom(t *testing.T) {
setupDorksTest(t)
_, err := execDorks(t, "dorks", "add",
"--source=github", "--category=frontier",
"--id=export-custom", "--query=baz")
require.NoError(t, err)
resetDorksFlags()
out, err := execDorks(t, "dorks", "export", "--format=yaml")
require.NoError(t, err)
assert.Contains(t, out, "openai-github-envfile")
assert.Contains(t, out, "export-custom")
// yaml decoded output should contain `id:` keys.
assert.True(t, strings.Contains(out, "id:"))
}
func TestDorksInfo_EmbeddedAndCustom(t *testing.T) {
setupDorksTest(t)
out, err := execDorks(t, "dorks", "info", "openai-github-envfile")
require.NoError(t, err)
assert.Contains(t, out, "Origin: embedded")
assert.Contains(t, out, "Query:")
resetDorksFlags()
_, err = execDorks(t, "dorks", "add",
"--source=github", "--category=frontier",
"--id=info-custom", "--query=qux")
require.NoError(t, err)
resetDorksFlags()
out, err = execDorks(t, "dorks", "info", "info-custom")
require.NoError(t, err)
assert.Contains(t, out, "Origin: custom")
}