diff --git a/cmd/cli/commands/login/login.go b/cmd/cli/commands/login/login.go index 294c26b..69ccbf3 100644 --- a/cmd/cli/commands/login/login.go +++ b/cmd/cli/commands/login/login.go @@ -1,60 +1,60 @@ -package login - -import ( - "cloudsave/cmd/cli/tools/prompt/credentials" - "cloudsave/pkg/remote/client" - "context" - "flag" - "fmt" - "os" - - "github.com/google/subcommands" -) - -type ( - LoginCmd struct { - } -) - -func (*LoginCmd) Name() string { return "login" } -func (*LoginCmd) Synopsis() string { return "save IN PLAIN TEXT your credentials" } -func (*LoginCmd) Usage() string { - return `Usage: cloudsave login - -Warning: this command saves the login into a plain text json file - -Options: -` -} - -func (p *LoginCmd) SetFlags(f *flag.FlagSet) { -} - -func (p *LoginCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { - if f.NArg() != 1 { - fmt.Fprintf(os.Stderr, "error: this command take 1 argument") - return subcommands.ExitUsageError - } - - server := f.Arg(0) - - username, password, err := credentials.Read(server) - if err != nil { - fmt.Fprintf(os.Stderr, "error: failed to read std output: %s", err) - return subcommands.ExitFailure - } - - cli := client.New(server, username, password) - if _, err := cli.Version(); err != nil { - fmt.Fprintf(os.Stderr, "error: failed to login: %s", err) - return subcommands.ExitFailure - } - - if err := credentials.Login(username, password, server); err != nil { - fmt.Fprintf(os.Stderr, "error: failed to save login: %s", err) - return subcommands.ExitFailure - } - - fmt.Println("login information saved!") - return subcommands.ExitSuccess -} +package login + +import ( + "cloudsave/cmd/cli/tools/prompt/credentials" + "cloudsave/pkg/remote/client" + "context" + "flag" + "fmt" + "os" + + "github.com/google/subcommands" +) + +type ( + LoginCmd struct { + } +) + +func (*LoginCmd) Name() string { return "login" } +func (*LoginCmd) Synopsis() string { return "save IN PLAIN TEXT your credentials" } +func (*LoginCmd) Usage() string { + return `Usage: cloudsave login + +Warning: this command saves the login into a plain text json file + +Options: +` +} + +func (p *LoginCmd) SetFlags(f *flag.FlagSet) { +} + +func (p *LoginCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + if f.NArg() != 1 { + fmt.Fprintf(os.Stderr, "error: this command take 1 argument") + return subcommands.ExitUsageError + } + + server := f.Arg(0) + + username, password, err := credentials.Read(server) + if err != nil { + fmt.Fprintf(os.Stderr, "error: failed to read std output: %s", err) + return subcommands.ExitFailure + } + + cli := client.New(server, username, password) + if _, err := cli.Version(); err != nil { + fmt.Fprintf(os.Stderr, "error: failed to login: %s", err) + return subcommands.ExitFailure + } + + if err := credentials.Login(username, password, server); err != nil { + fmt.Fprintf(os.Stderr, "error: failed to save login: %s", err) + return subcommands.ExitFailure + } + + fmt.Println("login information saved!") + return subcommands.ExitSuccess +} diff --git a/cmd/cli/commands/logout/logout.go b/cmd/cli/commands/logout/logout.go index 9ae7d42..b0adc76 100644 --- a/cmd/cli/commands/logout/logout.go +++ b/cmd/cli/commands/logout/logout.go @@ -1,43 +1,43 @@ -package logout - -import ( - "cloudsave/cmd/cli/tools/prompt/credentials" - "context" - "flag" - "fmt" - "os" - - "github.com/google/subcommands" -) - -type ( - LogoutCmd struct { - } -) - -func (*LogoutCmd) Name() string { return "logout" } -func (*LogoutCmd) Synopsis() string { return "logout from a server" } -func (*LogoutCmd) Usage() string { - return `Usage: cloudsave logout - -Options: -` -} - -func (p *LogoutCmd) SetFlags(f *flag.FlagSet) { -} - -func (p *LogoutCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { - if f.NArg() != 1 { - fmt.Fprintf(os.Stderr, "error: this command take 1 argument") - return subcommands.ExitUsageError - } - - if err := credentials.Logout(f.Arg(0)); err != nil { - fmt.Fprintf(os.Stderr, "error: failed to logout: %s", err) - return subcommands.ExitFailure - } - - fmt.Println("bye!") - return subcommands.ExitSuccess -} +package logout + +import ( + "cloudsave/cmd/cli/tools/prompt/credentials" + "context" + "flag" + "fmt" + "os" + + "github.com/google/subcommands" +) + +type ( + LogoutCmd struct { + } +) + +func (*LogoutCmd) Name() string { return "logout" } +func (*LogoutCmd) Synopsis() string { return "logout from a server" } +func (*LogoutCmd) Usage() string { + return `Usage: cloudsave logout + +Options: +` +} + +func (p *LogoutCmd) SetFlags(f *flag.FlagSet) { +} + +func (p *LogoutCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + if f.NArg() != 1 { + fmt.Fprintf(os.Stderr, "error: this command take 1 argument") + return subcommands.ExitUsageError + } + + if err := credentials.Logout(f.Arg(0)); err != nil { + fmt.Fprintf(os.Stderr, "error: failed to logout: %s", err) + return subcommands.ExitFailure + } + + fmt.Println("bye!") + return subcommands.ExitSuccess +} diff --git a/cmd/cli/commands/show/show.go b/cmd/cli/commands/show/show.go index 4086c92..5f770b5 100644 --- a/cmd/cli/commands/show/show.go +++ b/cmd/cli/commands/show/show.go @@ -1,51 +1,51 @@ -package show - -import ( - "cloudsave/pkg/data" - "context" - "flag" - "fmt" - "os" - - "github.com/google/subcommands" -) - -type ( - ShowCmd struct { - Service *data.Service - } -) - -func (*ShowCmd) Name() string { return "show" } -func (*ShowCmd) Synopsis() string { return "show metadata about game" } -func (*ShowCmd) Usage() string { - return `Usage: cloudsave show - -Show metdata about a game -` -} - -func (p *ShowCmd) SetFlags(f *flag.FlagSet) { -} - -func (p *ShowCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { - if f.NArg() != 1 { - fmt.Fprintln(os.Stderr, "error: missing game ID") - return subcommands.ExitUsageError - } - - gameID := f.Arg(0) - g, err := p.Service.One(gameID) - if err != nil { - fmt.Fprintf(os.Stderr, "error: failed to apply: %s", err) - return subcommands.ExitFailure - } - - fmt.Println(g.Name) - fmt.Println("------") - fmt.Println("Version: ", g.Version) - fmt.Println("Path: ", g.Path) - fmt.Println("MD5: ", g.MD5) - - return subcommands.ExitSuccess -} +package show + +import ( + "cloudsave/pkg/data" + "context" + "flag" + "fmt" + "os" + + "github.com/google/subcommands" +) + +type ( + ShowCmd struct { + Service *data.Service + } +) + +func (*ShowCmd) Name() string { return "show" } +func (*ShowCmd) Synopsis() string { return "show metadata about game" } +func (*ShowCmd) Usage() string { + return `Usage: cloudsave show + +Show metdata about a game +` +} + +func (p *ShowCmd) SetFlags(f *flag.FlagSet) { +} + +func (p *ShowCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + if f.NArg() != 1 { + fmt.Fprintln(os.Stderr, "error: missing game ID") + return subcommands.ExitUsageError + } + + gameID := f.Arg(0) + g, err := p.Service.One(gameID) + if err != nil { + fmt.Fprintf(os.Stderr, "error: failed to apply: %s", err) + return subcommands.ExitFailure + } + + fmt.Println(g.Name) + fmt.Println("------") + fmt.Println("Version: ", g.Version) + fmt.Println("Path: ", g.Path) + fmt.Println("MD5: ", g.MD5) + + return subcommands.ExitSuccess +} diff --git a/cmd/cli/tools/prompt/credentials/credentials.go b/cmd/cli/tools/prompt/credentials/credentials.go index 2e43ac9..d41aa85 100644 --- a/cmd/cli/tools/prompt/credentials/credentials.go +++ b/cmd/cli/tools/prompt/credentials/credentials.go @@ -89,7 +89,7 @@ func save(store map[string]credential) error { Store: store, } - f, err := os.OpenFile(filepath.Join(datastorePath, "credential.json"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + f, err := os.OpenFile(filepath.Clean(filepath.Join(datastorePath, "credential.json")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { return fmt.Errorf("failed to open datastore: %w", err) } @@ -104,7 +104,7 @@ func save(store map[string]credential) error { } func load() (map[string]credential, error) { - f, err := os.OpenFile(filepath.Join(datastorePath, "credential.json"), os.O_RDONLY, 0) + f, err := os.OpenFile(filepath.Clean(filepath.Join(datastorePath, "credential.json")), os.O_RDONLY, 0) if err != nil { if errors.Is(err, os.ErrNotExist) { return make(map[string]credential), nil diff --git a/cmd/server/security/htpasswd/htpasswd.go b/cmd/server/security/htpasswd/htpasswd.go index 075ac2c..5187c15 100644 --- a/cmd/server/security/htpasswd/htpasswd.go +++ b/cmd/server/security/htpasswd/htpasswd.go @@ -2,6 +2,7 @@ package htpasswd import ( "os" + "path/filepath" "strings" ) @@ -12,7 +13,7 @@ type ( ) func Open(path string) (File, error) { - c, err := os.ReadFile(path) + c, err := os.ReadFile(filepath.Clean(path)) if err != nil { return File{}, err } @@ -26,7 +27,7 @@ func Open(path string) (File, error) { if len(kv) != 2 { continue } - f.data[kv[0]] = kv[1] + f.data[kv[0]] = kv[1] } return f, nil diff --git a/cmd/web/server/config/config.go b/cmd/web/server/config/config.go index 87616bc..cecc924 100644 --- a/cmd/web/server/config/config.go +++ b/cmd/web/server/config/config.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" ) type ( @@ -22,7 +23,7 @@ type ( ) func Load(path string) (Configuration, error) { - f, err := os.OpenFile(path, os.O_RDONLY, 0) + f, err := os.OpenFile(filepath.Clean(path), os.O_RDONLY, 0) if err != nil { return Configuration{}, fmt.Errorf("failed to open configuration file: %w", err) } diff --git a/pkg/data/data.go b/pkg/data/data.go index 779d90e..bc25c8a 100644 --- a/pkg/data/data.go +++ b/pkg/data/data.go @@ -400,7 +400,7 @@ func (l Service) apply(src, dst string) error { return fmt.Errorf("failed to remove old save: %w", err) } - f, err := os.OpenFile(src, os.O_RDONLY, 0) + f, err := os.OpenFile(filepath.Clean(src), os.O_RDONLY, 0) if err != nil { return fmt.Errorf("failed to open archive: %w", err) } diff --git a/pkg/remote/client/client.go b/pkg/remote/client/client.go index abf89b7..087c7c3 100644 --- a/pkg/remote/client/client.go +++ b/pkg/remote/client/client.go @@ -14,6 +14,7 @@ import ( "net/http" "net/url" "os" + "path/filepath" "strconv" "time" @@ -221,12 +222,12 @@ func (c *Client) Pull(gameID, archivePath string) error { req.SetBasicAuth(c.username, c.password) - f, err := os.OpenFile(archivePath+".part", os.O_CREATE|os.O_WRONLY, 0600) + f, err := os.OpenFile(filepath.Clean(archivePath+".part"), os.O_CREATE|os.O_WRONLY, 0600) if err != nil { return fmt.Errorf("failed to open file: %w", err) } defer func() { - if err := os.Rename(archivePath+".part", archivePath); err != nil { + if err := os.Rename(filepath.Clean(archivePath+".part"), archivePath); err != nil { panic(err) } }() @@ -276,7 +277,7 @@ func (c *Client) PullBackup(gameID, uuid, archivePath string) error { req.SetBasicAuth(c.username, c.password) - f, err := os.OpenFile(archivePath+".part", os.O_CREATE|os.O_WRONLY, 0600) + f, err := os.OpenFile(filepath.Clean(archivePath+".part"), os.O_CREATE|os.O_WRONLY, 0600) if err != nil { return fmt.Errorf("failed to open file: %w", err) } @@ -422,7 +423,7 @@ func (c *Client) get(url string) (obj.HTTPObject, error) { } func (c *Client) push(u, archivePath string, m repository.Metadata) error { - f, err := os.OpenFile(archivePath, os.O_RDONLY, 0) + f, err := os.OpenFile(filepath.Clean(archivePath), os.O_RDONLY, 0) if err != nil { return fmt.Errorf("failed to open file: %w", err) } diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index 8960af8..c205c18 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -39,7 +39,7 @@ func init() { } func One(gameID string) (Remote, error) { - content, err := os.ReadFile(filepath.Join(datastorepath, gameID, "remote.json")) + content, err := os.ReadFile(filepath.Clean(filepath.Join(datastorepath, gameID, "remote.json"))) if err != nil { if errors.Is(err, os.ErrNotExist) { return Remote{}, ErrNoRemote @@ -62,7 +62,7 @@ func Set(gameID, url string) error { URL: url, } - f, err := os.OpenFile(filepath.Join(datastorepath, gameID, "remote.json"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + f, err := os.OpenFile(filepath.Join(filepath.Join(datastorepath, gameID, "remote.json")), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return err } diff --git a/pkg/repository/repository.go b/pkg/repository/repository.go index ff0f899..5e638f9 100644 --- a/pkg/repository/repository.go +++ b/pkg/repository/repository.go @@ -182,7 +182,7 @@ func (l *LazyRepository) WriteBlob(ID Identifier) (io.Writer, error) { path := l.DataPath(ID) slog.Debug("loading write buffer...", "id", ID) - dst, err := os.OpenFile(filepath.Join(path, "data.tar.gz"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + dst, err := os.OpenFile(filepath.Clean(filepath.Join(path, "data.tar.gz")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { return nil, fmt.Errorf("failed to open destination file: %w", err) } @@ -195,7 +195,7 @@ func (l *LazyRepository) WriteMetadata(id GameIdentifier, m Metadata) error { path := l.DataPath(id) slog.Debug("writing metadata", "id", id, "metadata", m) - dst, err := os.OpenFile(filepath.Join(path, "metadata.json"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + dst, err := os.OpenFile(filepath.Clean(filepath.Join(path, "metadata.json")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { return fmt.Errorf("failed to open destination file: %w", err) } @@ -213,7 +213,7 @@ func (l *LazyRepository) Metadata(id GameIdentifier) (Metadata, error) { path := l.DataPath(id) slog.Debug("loading metadata", "id", id) - src, err := os.OpenFile(filepath.Join(path, "metadata.json"), os.O_RDONLY, 0) + src, err := os.OpenFile(filepath.Clean(filepath.Join(path, "metadata.json")), os.O_RDONLY, 0) if err != nil { if errors.Is(err, os.ErrNotExist) { return Metadata{}, ErrNotFound @@ -272,7 +272,7 @@ func (l *LazyRepository) Backup(id BackupIdentifier) (Backup, error) { func (l *LazyRepository) LastScan(id GameIdentifier) (time.Time, error) { path := l.DataPath(id) - data, err := os.ReadFile(filepath.Join(path, ".last_run")) + data, err := os.ReadFile(filepath.Clean(filepath.Join(path, ".last_run"))) if err != nil { if errors.Is(err, os.ErrNotExist) { return time.Time{}, nil @@ -292,7 +292,7 @@ func (l *LazyRepository) ResetLastScan(id GameIdentifier) error { path := l.DataPath(id) slog.Debug("resetting last scan datetime for", "id", id) - f, err := os.OpenFile(filepath.Join(path, ".last_run"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + f, err := os.OpenFile(filepath.Clean(filepath.Join(path, ".last_run")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { return fmt.Errorf("failed to open file: %w", err) } @@ -311,7 +311,7 @@ func (l *LazyRepository) ReadBlob(id Identifier) (io.ReadSeekCloser, error) { path := l.DataPath(id) slog.Debug("loading read buffer...", "id", id) - dst, err := os.OpenFile(filepath.Join(path, "data.tar.gz"), os.O_RDONLY, 0) + dst, err := os.OpenFile(filepath.Clean(filepath.Join(path, "data.tar.gz")), os.O_RDONLY, 0) if err != nil { if errors.Is(err, os.ErrNotExist) { return nil, fmt.Errorf("failed to open blob: %w", ErrNotFound) @@ -325,7 +325,7 @@ func (l *LazyRepository) ReadBlob(id Identifier) (io.ReadSeekCloser, error) { func (l *LazyRepository) SetRemote(id GameIdentifier, url string) error { path := l.DataPath(id) - src, err := os.OpenFile(filepath.Join(path, "remote.json"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + src, err := os.OpenFile(filepath.Clean(filepath.Join(path, "remote.json")), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { return fmt.Errorf("failed to open remote description: %w", err) } @@ -345,7 +345,7 @@ func (l *LazyRepository) SetRemote(id GameIdentifier, url string) error { func (l *LazyRepository) Remote(id GameIdentifier) (*Remote, error) { path := l.DataPath(id) - src, err := os.OpenFile(filepath.Join(path, "remote.json"), os.O_RDONLY, 0) + src, err := os.OpenFile(filepath.Clean(filepath.Join(path, "remote.json")), os.O_RDONLY, 0) if err != nil { if errors.Is(err, os.ErrNotExist) { return nil, nil diff --git a/pkg/tools/archive/archive.go b/pkg/tools/archive/archive.go index fb4584a..a8f8a04 100644 --- a/pkg/tools/archive/archive.go +++ b/pkg/tools/archive/archive.go @@ -44,7 +44,7 @@ func Untar(file io.Reader, path string) error { } // the target location where the dir/file should be created - target := filepath.Join(path, header.Name) + target := filepath.Clean(filepath.Join(path, filepath.Clean(header.Name))) // the following switch could also be done using fi.Mode(), not sure if there // a benefit of using one vs. the other. @@ -122,7 +122,7 @@ func Tar(file io.Writer, root string) error { return nil } - file, err := os.Open(path) + file, err := os.Open(filepath.Clean(path)) if err != nil { return fmt.Errorf("failed to open file: %w", err) } diff --git a/pkg/tools/hash/hash.go b/pkg/tools/hash/hash.go index c4df4ee..a607dbf 100644 --- a/pkg/tools/hash/hash.go +++ b/pkg/tools/hash/hash.go @@ -5,16 +5,16 @@ import ( "encoding/hex" "io" "os" + "path/filepath" ) func FileMD5(fp string) (string, error) { - f, err := os.OpenFile(fp, os.O_RDONLY, 0) + f, err := os.OpenFile(filepath.Clean(fp), os.O_RDONLY, 0) if err != nil { return "", err } defer f.Close() - hasher := md5.New() if _, err := io.Copy(hasher, f); err != nil { return "", err