From 405bb648500169b71fa2e3f7531c555629de5003 Mon Sep 17 00:00:00 2001 From: olebeck <31539311+olebeck@users.noreply.github.com> Date: Fri, 5 May 2023 19:52:18 +0200 Subject: [PATCH] add input validation to cli --- go.mod | 1 + go.sum | 2 + handlers/second-user/provider.go | 3 - ui/gui.go | 14 +---- ui/gui/pages/packs/packs.go | 6 +- ui/gui/pages/skins/skins.go | 32 +++++----- utils/input.go | 103 ++++++++++++++++++++++++++++--- utils/iui.go | 9 ++- 8 files changed, 126 insertions(+), 44 deletions(-) diff --git a/go.mod b/go.mod index f2f0aee..9fabcf0 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( golang.org/x/text v0.9.0 gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v3 v3.0.1 + golang.org/x/term v0.7.0 ) require ( diff --git a/go.sum b/go.sum index 78dcb41..b6caa9a 100644 --- a/go.sum +++ b/go.sum @@ -184,6 +184,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/handlers/second-user/provider.go b/handlers/second-user/provider.go index ce6e179..d3fb6f0 100644 --- a/handlers/second-user/provider.go +++ b/handlers/second-user/provider.go @@ -1,8 +1,6 @@ package seconduser import ( - "sync" - "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/world" "github.com/google/uuid" @@ -14,7 +12,6 @@ type provider struct { func (p *provider) Settings() *world.Settings { return &world.Settings{ - Mutex: sync.Mutex{}, Name: "world", Spawn: cube.Pos{0, 0, 0}, DefaultGameMode: world.GameModeCreative, diff --git a/ui/gui.go b/ui/gui.go index 4ae007d..2935086 100644 --- a/ui/gui.go +++ b/ui/gui.go @@ -1,4 +1,4 @@ -//go:build gui || android +//go:build gui package ui @@ -32,7 +32,6 @@ type GUI struct { } func (g *GUI) Init() bool { - utils.SetCurrentUI(g) return true } @@ -54,7 +53,7 @@ func (g *GUI) Start(ctx context.Context, cancel context.CancelFunc) (err error) g.cancel = cancel w := app.NewWindow( - app.Title("Bedrocktool"), + app.Title("Bedrocktool " + utils.Version), ) th := material.NewTheme(gofont.Collection()) @@ -71,7 +70,6 @@ func (g *GUI) Start(ctx context.Context, cancel context.CancelFunc) (err error) } g.router = pages.NewRouter(ctx, w.Invalidate, th) - g.router.Register("Settings", settings.New(&g.router)) g.router.Register("worlds", worlds.New(&g.router)) g.router.Register("skins", skins.New(&g.router)) @@ -80,17 +78,11 @@ func (g *GUI) Start(ctx context.Context, cancel context.CancelFunc) (err error) g.router.SwitchTo("Settings") - go func() { - err = g.run(w) - }() - go func() { app.Main() }() - <-ctx.Done() - - return err + return g.run(w) } func (g *GUI) run(w *app.Window) error { diff --git a/ui/gui/pages/packs/packs.go b/ui/gui/pages/packs/packs.go index 4497f10..72d641b 100644 --- a/ui/gui/pages/packs/packs.go +++ b/ui/gui/pages/packs/packs.go @@ -196,14 +196,12 @@ func (p *Page) layoutFinished(gtx C, th *material.Theme) D { } func (p *Page) Layout(gtx C, th *material.Theme) D { - margin := layout.Inset{ + return layout.Inset{ Top: unit.Dp(25), Bottom: unit.Dp(25), Right: unit.Dp(35), Left: unit.Dp(35), - } - - return margin.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + }.Layout(gtx, func(gtx layout.Context) layout.Dimensions { switch p.State { case messages.UIStateConnecting: return layout.Center.Layout(gtx, material.Label(th, 100, "Connecting").Layout) diff --git a/ui/gui/pages/skins/skins.go b/ui/gui/pages/skins/skins.go index 2f52b43..e2e65bb 100644 --- a/ui/gui/pages/skins/skins.go +++ b/ui/gui/pages/skins/skins.go @@ -55,27 +55,24 @@ func (p *Page) NavItem() component.NavItem { } func (p *Page) Layout(gtx C, th *material.Theme) D { - margin := layout.Inset{ + return layout.Inset{ Top: unit.Dp(25), Bottom: unit.Dp(25), Right: unit.Dp(35), Left: unit.Dp(35), - } - - switch p.State { - case messages.UIStateConnect: - // display login page - return margin.Layout(gtx, material.Label(th, 100, "connect Client").Layout) - case messages.UIStateConnecting: - // display connecting to server - return margin.Layout(gtx, material.Label(th, 100, "Connecting").Layout) - case messages.UIStateMain: - // show the main ui - return margin.Layout(gtx, func(gtx C) D { + }.Layout(gtx, func(gtx C) D { + switch p.State { + case messages.UIStateConnect: + // display login page + return material.Label(th, 100, "connect Client").Layout(gtx) + case messages.UIStateConnecting: + // display connecting to server + return material.Label(th, 100, "Connecting").Layout(gtx) + case messages.UIStateMain: + // show the main ui return layout.Flex{ Axis: layout.Vertical, }.Layout(gtx, - layout.Rigid(material.Label(th, 20, "Skin Basic UI").Layout), layout.Flexed(1, func(gtx C) D { p.l.Lock() defer p.l.Unlock() @@ -89,10 +86,9 @@ func (p *Page) Layout(gtx C, th *material.Theme) D { }) }), ) - }) - } - - return layout.Flex{}.Layout(gtx) + } + return D{} + }) } func (p *Page) Handler(data interface{}) messages.MessageResponse { diff --git a/utils/input.go b/utils/input.go index 0967b10..9699edd 100644 --- a/utils/input.go +++ b/utils/input.go @@ -1,26 +1,84 @@ package utils import ( - "bufio" "context" "fmt" "net" "os" "regexp" "strings" + "sync/atomic" "github.com/bedrock-tool/bedrocktool/locale" "github.com/sirupsen/logrus" + "golang.org/x/term" ) -func UserInput(ctx context.Context, q string) (string, bool) { +func UserInput(ctx context.Context, q string, validator func(string) bool) (string, bool) { c := make(chan string) + oldState, _ := term.MakeRaw(int(os.Stdin.Fd())) + defer term.Restore(int(os.Stdin.Fd()), oldState) go func() { fmt.Print(q) - reader := bufio.NewReader(os.Stdin) - answer, _ := reader.ReadString('\n') - r, _ := regexp.Compile(`[\n\r]`) - answer = string(r.ReplaceAll([]byte(answer), []byte(""))) + + var answerb []byte + var b [1]byte + + var valid bool + var validatorRunning atomic.Bool + var validatorQueued atomic.Bool + + for { + os.Stdin.Read(b[:]) + + done := false + switch b[0] { + case 0x3: + c <- "" + return + case 0xA: + fallthrough + case 0xD: + done = true + case 0x8: + fallthrough + case 0x7F: + if len(answerb) > 0 { + answerb = answerb[:len(answerb)-1] + } + default: + if b[0] >= 0x20 { + answerb = append(answerb, b[0]) + } + } + + if done { + break + } + + fmt.Printf("\r%s%s\033[K", q, string(answerb)) + + if validator != nil { + validatorQueued.Store(true) + if validatorRunning.CompareAndSwap(false, true) { + go func() { + for validatorQueued.Load() { + validatorQueued.Store(false) + valid = validator(string(answerb)) + validatorRunning.Store(false) + var st = "❌" + if valid { + st = "✅" + } + fmt.Printf("\r%s%s %s\033[K\033[%dD", q, string(answerb), st, 4) + } + }() + } + } + } + + print("\n\r") + answer := string(answerb) c <- answer }() @@ -28,6 +86,9 @@ func UserInput(ctx context.Context, q string) (string, bool) { case <-ctx.Done(): return "", true case a := <-c: + if a == "" { + return a, true + } return a, false } } @@ -60,7 +121,7 @@ func ServerInput(ctx context.Context, server string) (string, string, error) { // no arg provided, interactive input if server == "" { var cancelled bool - server, cancelled = UserInput(ctx, locale.Loc("enter_server", nil)) + server, cancelled = UserInput(ctx, locale.Loc("enter_server", nil), ValidateServerInput) if cancelled { return "", "", context.Canceled } @@ -94,3 +155,31 @@ func ServerInput(ctx context.Context, server string) (string, string, error) { } return server, serverGetHostname(server), nil } + +func ValidateServerInput(server string) bool { + if pcapRegex.MatchString(server) { + return true + } + + if realmRegex.MatchString(server) { + return true // todo + } + + host, _, err := net.SplitHostPort(server) + if err != nil { + if strings.Contains(err.Error(), "missing port in address") { + host = server + } + } + + ip := net.ParseIP(host) + if ip != nil { + return true + } + + ips, err := net.LookupIP(host) + if len(ips) > 0 { + return true + } + return false +} diff --git a/utils/iui.go b/utils/iui.go index 5a243e6..7420713 100644 --- a/utils/iui.go +++ b/utils/iui.go @@ -63,7 +63,14 @@ func (c *InteractiveCLI) Start(ctx context.Context, cancel context.CancelFunc) e } fmt.Println(locale.Loc("use_to_run_command", nil)) - cmd, cancelled := UserInput(ctx, locale.Loc("input_command", nil)) + cmd, cancelled := UserInput(ctx, locale.Loc("input_command", nil), func(s string) bool { + for k := range ValidCMDs { + if s == k { + return true + } + } + return false + }) if cancelled { cancel() return nil