-
Notifications
You must be signed in to change notification settings - Fork 0
feat(cli): compact single-dash flag model #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -28,6 +28,7 @@ func main() { | |||||
| func run() int { | ||||||
| normalizeLegacyArgs() | ||||||
| normalizeCommandArgs() | ||||||
| normalizeCompactFlags() | ||||||
|
|
||||||
| var ( | ||||||
| scanTargets []string | ||||||
|
|
@@ -63,38 +64,38 @@ func run() int { | |||||
| guide bool | ||||||
| ) | ||||||
|
|
||||||
| pflag.StringArrayVarP(&scanTargets, "scan", "s", nil, "Scan target(s)") | ||||||
| pflag.BoolVarP(&status, "status", "S", false, "Show scan status") | ||||||
| pflag.BoolVarP(&results, "results", "R", false, "Show results") | ||||||
| pflag.BoolVarP(&listTools, "list-tools", "L", false, "List external tool availability") | ||||||
| pflag.BoolVarP(&export, "export", "E", false, "Export results to JSON") | ||||||
| pflag.BoolVarP(&configCmd, "config", "C", false, "Show config paths") | ||||||
| pflag.BoolVarP(&pipeline, "pipeline", "P", false, "Show pipeline path (v2 native pipeline is built-in)") | ||||||
| pflag.BoolVar(&serve, "serve", false, "Start web dashboard server") | ||||||
| pflag.StringArrayVar(&scanTargets, "scn", nil, "Scan target(s)") | ||||||
| pflag.BoolVar(&status, "sts", false, "Show scan status") | ||||||
| pflag.BoolVar(&results, "res", false, "Show results") | ||||||
| pflag.BoolVar(&listTools, "lst", false, "List external tool availability") | ||||||
| pflag.BoolVar(&export, "exp", false, "Export results to JSON") | ||||||
| pflag.BoolVar(&configCmd, "cfg", false, "Show config paths") | ||||||
| pflag.BoolVar(&pipeline, "pip", false, "Show pipeline path (v2 native pipeline is built-in)") | ||||||
| pflag.BoolVar(&serve, "srv", false, "Start web dashboard server") | ||||||
|
|
||||||
| pflag.StringVarP(&filePath, "file", "F", "", "Read targets from file") | ||||||
| pflag.BoolVar(&useStdin, "stdin", false, "Read targets from stdin") | ||||||
| pflag.StringVarP(&domain, "domain", "d", "", "Filter by domain") | ||||||
| pflag.StringVar(&scanID, "id", "", "Fetch specific scan ID") | ||||||
| pflag.StringVarP(&what, "what", "w", "all", "Result view: all|subdomains|live|ports|urls|js|vulns") | ||||||
| pflag.StringVarP(&mode, "mode", "m", "wide", "Mode: wide|narrow|fast|deep|osint") | ||||||
| pflag.BoolVarP(&fast, "fast", "f", false, "Shortcut for mode fast") | ||||||
| pflag.BoolVarP(&narrow, "narrow", "n", false, "Shortcut for mode narrow") | ||||||
| pflag.IntVar(&rate, "rate", 150, "Request rate hint") | ||||||
| pflag.IntVar(&threads, "threads", 30, "Worker threads") | ||||||
| pflag.IntVar(&limit, "limit", 50, "Output limit") | ||||||
| pflag.StringVarP(&output, "output", "o", "", "Output file") | ||||||
| pflag.BoolVarP(&quiet, "quiet", "q", false, "Quiet output") | ||||||
| pflag.BoolVar(&showVersion, "version", false, "Show version") | ||||||
| pflag.StringVar(&serveAddr, "addr", "127.0.0.1:8088", "Dashboard bind address") | ||||||
| pflag.StringVar(&storagePath, "storage", "", "Storage root directory (default: ./storage)") | ||||||
| pflag.StringVar(&stages, "stages", "all", "Comma-separated stages: subdomains,http,ports,urls,vulns") | ||||||
| pflag.StringArrayVar(&setAPI, "set-api", nil, "Set API key as name=value (repeatable). Use empty value to unset.") | ||||||
| pflag.BoolVar(&showAPI, "show-api", false, "Show configured API keys (masked)") | ||||||
| pflag.BoolVar(&setup, "setup", false, "Show setup screen with tool installation status") | ||||||
| pflag.BoolVar(&installTools, "install-tools", false, "Install missing supported tools (Linux)") | ||||||
| pflag.StringVar(&profile, "profile", "balanced", "Workflow profile: passive|balanced|aggressive") | ||||||
| pflag.BoolVar(&guide, "guide", false, "Show first-principles workflow guide") | ||||||
| pflag.StringVar(&filePath, "fil", "", "Read targets from file") | ||||||
| pflag.BoolVar(&useStdin, "inp", false, "Read targets from stdin") | ||||||
| pflag.StringVar(&domain, "dom", "", "Filter by domain") | ||||||
| pflag.StringVar(&scanID, "sid", "", "Fetch specific scan ID") | ||||||
| pflag.StringVar(&what, "wht", "all", "Result view: all|subdomains|live|ports|urls|js|vulns") | ||||||
| pflag.StringVar(&mode, "mod", "wide", "Mode: wide|narrow|fast|deep|osint") | ||||||
| pflag.BoolVar(&fast, "fst", false, "Shortcut for mode fast") | ||||||
| pflag.BoolVar(&narrow, "nrw", false, "Shortcut for mode narrow") | ||||||
| pflag.IntVar(&rate, "rte", 150, "Request rate hint") | ||||||
| pflag.IntVar(&threads, "thr", 30, "Worker threads") | ||||||
| pflag.IntVar(&limit, "lim", 50, "Output limit") | ||||||
| pflag.StringVar(&output, "out", "", "Output file") | ||||||
| pflag.BoolVar(&quiet, "qut", false, "Quiet output") | ||||||
| pflag.BoolVar(&showVersion, "ver", false, "Show version") | ||||||
| pflag.StringVar(&serveAddr, "adr", "127.0.0.1:8088", "Dashboard bind address") | ||||||
| pflag.StringVar(&storagePath, "str", "", "Storage root directory (default: ./storage)") | ||||||
| pflag.StringVar(&stages, "stg", "all", "Comma-separated stages: subdomains,http,ports,urls,vulns") | ||||||
| pflag.StringArrayVar(&setAPI, "sak", nil, "Set API key as name=value (repeatable). Use empty value to unset.") | ||||||
| pflag.BoolVar(&showAPI, "shk", false, "Show configured API keys (masked)") | ||||||
| pflag.BoolVar(&setup, "stp", false, "Show setup screen with tool installation status") | ||||||
| pflag.BoolVar(&installTools, "ins", false, "Install missing supported tools (Linux)") | ||||||
| pflag.StringVar(&profile, "prf", "balanced", "Workflow profile: passive|balanced|aggressive") | ||||||
| pflag.BoolVar(&guide, "gud", false, "Show first-principles workflow guide") | ||||||
| pflag.Parse() | ||||||
|
|
||||||
| if showVersion { | ||||||
|
|
@@ -277,38 +278,38 @@ func printHelp() { | |||||
| Usage: | ||||||
| macaron scan example.com | ||||||
| macaron status | ||||||
| macaron results -d example.com -w live | ||||||
| macaron serve --addr 127.0.0.1:8088 | ||||||
| macaron results -dom example.com -wht live | ||||||
| macaron serve -adr 127.0.0.1:8088 | ||||||
| macaron setup | ||||||
|
|
||||||
| Core flags: | ||||||
| -s, --scan TARGET Scan one or more targets | ||||||
| -F, --file FILE Read targets from file | ||||||
| --stdin Read targets from stdin | ||||||
| -m, --mode MODE wide|narrow|fast|deep|osint | ||||||
| -S, --status Show scan summaries | ||||||
| -R, --results Show scan details | ||||||
| -E, --export Export JSON | ||||||
| -L, --list-tools Show tool availability | ||||||
| --storage DIR Use custom storage root (default ./storage) | ||||||
| --stages LIST Choose stages: subdomains,http,ports,urls,vulns | ||||||
| --set-api k=v Save API keys to storage config.yaml | ||||||
| --show-api Show masked API keys | ||||||
| --setup Show setup screen with tool status | ||||||
| --install-tools Install missing supported tools (Linux) | ||||||
| --profile NAME passive|balanced|aggressive | ||||||
| --guide Show first-principles workflow guide | ||||||
| --serve Start browser dashboard | ||||||
| --version Show version`) | ||||||
| -scn TARGET Scan one or more targets | ||||||
| -fil FILE Read targets from file | ||||||
| -inp Read targets from stdin | ||||||
| -mod MODE wide|narrow|fast|deep|osint | ||||||
| -sts Show scan summaries | ||||||
| -res Show scan details | ||||||
| -exp Export JSON | ||||||
| -lst Show tool availability | ||||||
| -str DIR Use custom storage root (default ./storage) | ||||||
| -stg LIST Choose stages: subdomains,http,ports,urls,vulns | ||||||
| -sak k=v Save API keys to storage config.yaml | ||||||
| -shk Show masked API keys | ||||||
| -stp Show setup screen with tool status | ||||||
| -ins Install missing supported tools (Linux) | ||||||
| -prf NAME passive|balanced|aggressive | ||||||
| -gud Show first-principles workflow guide | ||||||
| -srv Start browser dashboard | ||||||
| -ver Show version`) | ||||||
| } | ||||||
|
|
||||||
| func normalizeLegacyArgs() { | ||||||
| for i, arg := range os.Args { | ||||||
| if arg == "-setup" { | ||||||
| os.Args[i] = "--setup" | ||||||
| os.Args[i] = "-stp" | ||||||
| } | ||||||
| if arg == "-install-tools" { | ||||||
| os.Args[i] = "--install-tools" | ||||||
| os.Args[i] = "-ins" | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
@@ -327,29 +328,95 @@ func normalizeCommandArgs() { | |||||
| args = append(args, tok) | ||||||
| continue | ||||||
| } | ||||||
| args = append(args, "--scan", tok) | ||||||
| args = append(args, "--scn", tok) | ||||||
| } | ||||||
| if len(args) == 1 { | ||||||
| args = append(args, "--scan") | ||||||
| args = append(args, "--scn") | ||||||
| } | ||||||
| os.Args = args | ||||||
| case "status": | ||||||
| os.Args = append([]string{os.Args[0], "--status"}, rest...) | ||||||
| os.Args = append([]string{os.Args[0], "--sts"}, rest...) | ||||||
| case "results": | ||||||
| os.Args = append([]string{os.Args[0], "--results"}, rest...) | ||||||
| os.Args = append([]string{os.Args[0], "--res"}, rest...) | ||||||
| case "serve": | ||||||
| os.Args = append([]string{os.Args[0], "--serve"}, rest...) | ||||||
| os.Args = append([]string{os.Args[0], "--srv"}, rest...) | ||||||
| case "setup": | ||||||
| os.Args = append([]string{os.Args[0], "--setup"}, rest...) | ||||||
| os.Args = append([]string{os.Args[0], "--stp"}, rest...) | ||||||
| case "export": | ||||||
| os.Args = append([]string{os.Args[0], "--export"}, rest...) | ||||||
| os.Args = append([]string{os.Args[0], "--exp"}, rest...) | ||||||
| case "config": | ||||||
| os.Args = append([]string{os.Args[0], "--config"}, rest...) | ||||||
| os.Args = append([]string{os.Args[0], "--cfg"}, rest...) | ||||||
| case "guide": | ||||||
| os.Args = append([]string{os.Args[0], "--guide"}, rest...) | ||||||
| os.Args = append([]string{os.Args[0], "--gud"}, rest...) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| func normalizeCompactFlags() { | ||||||
| flagMap := map[string]string{ | ||||||
| "scan": "scn", "s": "scn", "scn": "scn", | ||||||
| "status": "sts", "S": "sts", "sts": "sts", | ||||||
| "results": "res", "R": "res", "res": "res", | ||||||
| "list-tools": "lst", "L": "lst", "lst": "lst", | ||||||
| "export": "exp", "E": "exp", "exp": "exp", | ||||||
| "config": "cfg", "C": "cfg", "cfg": "cfg", | ||||||
| "pipeline": "pip", "P": "pip", "pip": "pip", | ||||||
| "serve": "srv", "srv": "srv", | ||||||
| "file": "fil", "F": "fil", "fil": "fil", | ||||||
| "stdin": "inp", "inp": "inp", | ||||||
| "domain": "dom", "d": "dom", "dom": "dom", | ||||||
| "id": "sid", "sid": "sid", | ||||||
| "what": "wht", "w": "wht", "wht": "wht", | ||||||
| "mode": "mod", "m": "mod", "mod": "mod", | ||||||
| "fast": "fst", "f": "fst", "fst": "fst", | ||||||
| "narrow": "nrw", "n": "nrw", "nrw": "nrw", | ||||||
| "rate": "rte", "rte": "rte", | ||||||
| "threads": "thr", "thr": "thr", | ||||||
| "limit": "lim", "lim": "lim", | ||||||
| "output": "out", "o": "out", "out": "out", | ||||||
| "quiet": "qut", "q": "qut", "qut": "qut", | ||||||
| "version": "ver", "ver": "ver", | ||||||
| "addr": "adr", "adr": "adr", | ||||||
| "storage": "str", "str": "str", | ||||||
| "stages": "stg", "stg": "stg", | ||||||
| "set-api": "sak", "sak": "sak", | ||||||
| "show-api": "shk", "shk": "shk", | ||||||
| "setup": "stp", "stp": "stp", | ||||||
| "install-tools": "ins", "ins": "ins", | ||||||
| "profile": "prf", "prf": "prf", | ||||||
| "guide": "gud", "gud": "gud", | ||||||
| } | ||||||
| args := make([]string, 0, len(os.Args)) | ||||||
| args = append(args, os.Args[0]) | ||||||
| for _, arg := range os.Args[1:] { | ||||||
| if strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") && len(arg) > 2 { | ||||||
|
||||||
| if strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") && len(arg) > 2 { | |
| if strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") && len(arg) >= 2 { |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,17 +15,50 @@ func withArgs(args []string, fn func()) { | |
| func TestNormalizeLegacySetup(t *testing.T) { | ||
| withArgs([]string{"macaron", "-setup"}, func() { | ||
| normalizeLegacyArgs() | ||
| if osArgs()[1] != "--setup" { | ||
| t.Fatalf("expected --setup, got %s", osArgs()[1]) | ||
| if osArgs()[1] != "-stp" { | ||
| t.Fatalf("expected -stp, got %s", osArgs()[1]) | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| func TestNormalizeCommandScan(t *testing.T) { | ||
| withArgs([]string{"macaron", "scan", "example.com", "--fast"}, func() { | ||
| normalizeCompactFlags() | ||
| normalizeCommandArgs() | ||
| args := osArgs() | ||
| want := []string{"macaron", "--scan", "example.com", "--fast"} | ||
| want := []string{"macaron", "--scn", "example.com", "--fst"} | ||
| if len(args) != len(want) { | ||
| t.Fatalf("unexpected len: %#v", args) | ||
| } | ||
| for i := range want { | ||
| if args[i] != want[i] { | ||
| t.Fatalf("idx %d: got %q want %q", i, args[i], want[i]) | ||
| } | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| func TestNormalizeLongToCompact(t *testing.T) { | ||
| withArgs([]string{"macaron", "--scan", "example.com", "--threads", "20"}, func() { | ||
| normalizeCompactFlags() | ||
| args := osArgs() | ||
| want := []string{"macaron", "--scn", "example.com", "--thr", "20"} | ||
| if len(args) != len(want) { | ||
| t.Fatalf("unexpected len: %#v", args) | ||
| } | ||
| for i := range want { | ||
| if args[i] != want[i] { | ||
| t.Fatalf("idx %d: got %q want %q", i, args[i], want[i]) | ||
| } | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| func TestNormalizeSingleDashCompact(t *testing.T) { | ||
| withArgs([]string{"macaron", "-stp", "-ver"}, func() { | ||
| normalizeCompactFlags() | ||
| args := osArgs() | ||
| want := []string{"macaron", "--stp", "--ver"} | ||
|
Comment on lines
24
to
+61
|
||
| if len(args) != len(want) { | ||
| t.Fatalf("unexpected len: %#v", args) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
normalizeLegacyArgsrewrites-setup/-install-toolsto-stp/-ins(single-dash), which still requires a laternormalizeCompactFlagspass to become parseable bypflag(otherwise-stpis treated as a shorthand cluster). Consider rewriting legacy args directly to the canonical long form (--stp/--ins) so parsing does not depend on normalization order or future refactors.