package userscanner import ( "context" "os/exec" "strings" "github.com/anotherhadi/iknowyou/internal/tools" ) const ( name = "user-scanner" description = "🕵️‍♂️ (2-in-1) Email & Username OSINT suite. Analyzes 195+ scan vectors (95+ email / 100+ username) for security research, investigations, and digital footprinting." link = "https://github.com/kaifcodec/user-scanner" icon = "" ) type Config struct { AllowLoud bool `yaml:"allow_loud" iky:"desc=Enable scanning sites that may send emails/notifications (password resets, etc.);default=false"` OnlyFound bool `yaml:"only_found" iky:"desc=Only show sites where the username/email was found;default=true"` } type Runner struct { cfg Config } func New() tools.ToolRunner { cfg := Config{} tools.ApplyDefaults(&cfg) return &Runner{cfg: cfg} } func (r *Runner) Name() string { return name } func (r *Runner) Description() string { return description } func (r *Runner) Link() string { return link } func (r *Runner) Icon() string { return icon } func (r *Runner) InputTypes() []tools.InputType { return []tools.InputType{ tools.InputTypeEmail, tools.InputTypeUsername, } } func (r *Runner) ConfigPtr() interface{} { return &r.cfg } func (r *Runner) ConfigFields() []tools.ConfigField { return tools.ReflectConfigFields(r.cfg) } func (r *Runner) Available() (bool, string) { if _, err := exec.LookPath("user-scanner"); err != nil { return false, "user-scanner binary not found in PATH" } return true, "" } func (r *Runner) Dependencies() []string { return []string{"user-scanner"} } func (r *Runner) Run(ctx context.Context, target string, inputType tools.InputType, out chan<- tools.Event) error { defer close(out) args := make([]string, 0, 6) switch inputType { case tools.InputTypeEmail: args = append(args, "-e", target) default: args = append(args, "-u", target) } if r.cfg.AllowLoud { args = append(args, "--allow-loud") } if r.cfg.OnlyFound { args = append(args, "--only-found") } cmd := exec.CommandContext(ctx, "user-scanner", args...) output, err := tools.RunWithPTY(ctx, cmd) // Removing banner output = tools.RemoveFirstLines(output, 8) // count = output = tools.CropAfterExclude(output, "[i] Scan complete.") count := 0 if err != nil && ctx.Err() != nil { out <- tools.Event{Tool: name, Type: tools.EventTypeError, Payload: "scan cancelled"} } else if output != "" { out <- tools.Event{Tool: name, Type: tools.EventTypeOutput, Payload: output} count = strings.Count(output, "[✔]") } out <- tools.Event{Tool: name, Type: tools.EventTypeCount, Payload: count} out <- tools.Event{Tool: name, Type: tools.EventTypeDone} return nil }