add input validation to cli

This commit is contained in:
olebeck 2023-05-05 19:52:18 +02:00
parent f2c45d379d
commit 405bb64850
8 changed files with 126 additions and 44 deletions

1
go.mod
View File

@ -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 (

2
go.sum
View File

@ -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=

View File

@ -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,

View File

@ -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 {

View File

@ -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)

View File

@ -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 {

View File

@ -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
}

View File

@ -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