mirror of
https://github.com/anotherhadi/spilltea.git
synced 2026-08-21 19:05:48 +02:00
9d0ef598a7
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
182 lines
5.6 KiB
Go
182 lines
5.6 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime/debug"
|
|
|
|
tea "charm.land/bubbletea/v2"
|
|
spilltea "github.com/anotherhadi/spilltea"
|
|
"github.com/anotherhadi/spilltea/internal/config"
|
|
"github.com/anotherhadi/spilltea/internal/icons"
|
|
"github.com/anotherhadi/spilltea/internal/intercept"
|
|
"github.com/anotherhadi/spilltea/internal/keys"
|
|
"github.com/anotherhadi/spilltea/internal/style"
|
|
appUI "github.com/anotherhadi/spilltea/internal/ui/app"
|
|
homeUI "github.com/anotherhadi/spilltea/internal/ui/home"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// Version is overwritten at build time by goreleaser/ldflag with the current version tag, or "dev" if not set.
|
|
var version = "dev"
|
|
|
|
func init() {
|
|
if version != "dev" {
|
|
return
|
|
}
|
|
if info, ok := debug.ReadBuildInfo(); ok && info.Main.Version != "" && info.Main.Version != "(devel)" {
|
|
version = info.Main.Version
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
var (
|
|
flagConfig string
|
|
flagPluginsDir string
|
|
flagHost string
|
|
flagPort int
|
|
flagUpstreamProxy string
|
|
flagProject string
|
|
flagSslInsecure bool
|
|
flagAddDefaultPlugins bool
|
|
flagAddDefaultConfig bool
|
|
)
|
|
|
|
rootCmd := &cobra.Command{
|
|
Use: "spilltea",
|
|
Short: "A minimal, terminal-based HTTP(S) proxy for pentesters and CTF players.",
|
|
Version: version,
|
|
SilenceUsage: true,
|
|
SilenceErrors: true,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if flagAddDefaultPlugins {
|
|
home, _ := os.UserHomeDir()
|
|
cfgPath := filepath.Join(home, ".config", "spilltea", "config.yaml")
|
|
if flagConfig != "" {
|
|
cfgPath = flagConfig
|
|
}
|
|
if err := config.Load(cfgPath); err != nil {
|
|
return fmt.Errorf("config: %w", err)
|
|
}
|
|
dir := config.ExpandPath(config.Global.App.PluginsDir)
|
|
if flagPluginsDir != "" {
|
|
dir = flagPluginsDir
|
|
}
|
|
n, err := spilltea.InstallDefaultPlugins(dir)
|
|
if err != nil {
|
|
return fmt.Errorf("add-default-plugins: %w", err)
|
|
}
|
|
fmt.Printf("added %d plugin(s) to %s\n", n, dir)
|
|
return nil
|
|
}
|
|
|
|
if flagAddDefaultConfig {
|
|
home, _ := os.UserHomeDir()
|
|
cfgPath := filepath.Join(home, ".config", "spilltea", "config.yaml")
|
|
if flagConfig != "" {
|
|
cfgPath = flagConfig
|
|
}
|
|
if err := config.WriteDefaultConfig(cfgPath); err != nil {
|
|
return fmt.Errorf("add-default-config: %w", err)
|
|
}
|
|
fmt.Printf("default config written to %s\n", cfgPath)
|
|
return nil
|
|
}
|
|
|
|
if flagProject != "" && !homeUI.IsValidProjectName(flagProject) {
|
|
return fmt.Errorf("project: invalid name %q (only lowercase letters, digits, - and _ are allowed)", flagProject)
|
|
}
|
|
|
|
home, _ := os.UserHomeDir()
|
|
cfgPath := filepath.Join(home, ".config", "spilltea", "config.yaml")
|
|
if flagConfig != "" {
|
|
cfgPath = flagConfig
|
|
}
|
|
|
|
if err := config.Load(cfgPath); err != nil {
|
|
return fmt.Errorf("config: %w", err)
|
|
}
|
|
config.Global.Version = version
|
|
|
|
sslInsecureChanged := cmd.Flags().Changed("ssl-insecure")
|
|
applyCLI := func(c *config.Config) {
|
|
if flagPluginsDir != "" {
|
|
c.App.PluginsDir = flagPluginsDir
|
|
}
|
|
if flagHost != "" {
|
|
c.App.Host = flagHost
|
|
}
|
|
if flagPort != 0 {
|
|
c.App.Port = flagPort
|
|
}
|
|
if flagUpstreamProxy != "" {
|
|
c.App.UpstreamProxy = flagUpstreamProxy
|
|
}
|
|
if sslInsecureChanged {
|
|
c.App.SslInsecure = flagSslInsecure
|
|
}
|
|
}
|
|
applyCLI(config.Global)
|
|
config.SetCLIOverrides(applyCLI)
|
|
|
|
style.Init()
|
|
icons.Init(config.Global)
|
|
keys.Init(config.Global)
|
|
|
|
projectDir := config.ExpandPath(config.Global.App.ProjectDir)
|
|
|
|
if flagProject != "" {
|
|
project, err := homeUI.OpenProject(projectDir, flagProject)
|
|
if err != nil {
|
|
return fmt.Errorf("project: %w", err)
|
|
}
|
|
if err := config.MergeProjectConfig(filepath.Dir(project.Path)); err != nil {
|
|
return fmt.Errorf("project config: %w", err)
|
|
}
|
|
icons.Init(config.Global)
|
|
keys.Init(config.Global)
|
|
broker := intercept.NewBroker()
|
|
m := appUI.New(broker, project.Name, project.Path)
|
|
final, err := tea.NewProgram(m).Run()
|
|
if err != nil {
|
|
return fmt.Errorf("tui: %w", err)
|
|
}
|
|
if app, ok := final.(appUI.Model); ok {
|
|
if ferr := app.FatalErr(); ferr != nil {
|
|
return ferr
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
root := rootModel{home: homeUI.New(projectDir)}
|
|
final, err := tea.NewProgram(root).Run()
|
|
if err != nil {
|
|
return fmt.Errorf("tui: %w", err)
|
|
}
|
|
if r, ok := final.(rootModel); ok {
|
|
if ferr := r.FatalErr(); ferr != nil {
|
|
return ferr
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
rootCmd.Flags().StringVarP(&flagConfig, "config", "c", "", "path to config file")
|
|
rootCmd.Flags().StringVar(&flagPluginsDir, "plugins-dir", "", "path to plugins dir (overrides config)")
|
|
rootCmd.Flags().StringVar(&flagHost, "host", "", "proxy host (overrides config)")
|
|
rootCmd.Flags().IntVarP(&flagPort, "port", "p", 0, "proxy port (overrides config)")
|
|
rootCmd.Flags().StringVar(&flagUpstreamProxy, "upstream-proxy", "", "upstream proxy URL, e.g. http://user:pass@host:8888 (overrides config)")
|
|
rootCmd.Flags().BoolVar(&flagSslInsecure, "ssl-insecure", false, "skip TLS certificate verification (overrides config)")
|
|
rootCmd.Flags().StringVarP(&flagProject, "project", "P", "", `project name to open directly, or "tmp" for a temporary session`)
|
|
rootCmd.Flags().BoolVar(&flagAddDefaultPlugins, "add-default-plugins", false, "copy built-in example plugins into the plugins dir and exit")
|
|
rootCmd.Flags().BoolVar(&flagAddDefaultConfig, "add-default-config", false, "copy the default config file to the config path and exit")
|
|
|
|
if err := rootCmd.Execute(); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|