From 4cc6fb747064be32076ac6ca458b9a715d7feea1 Mon Sep 17 00:00:00 2001 From: Tristan Isham Date: Thu, 14 Nov 2024 12:14:47 -0500 Subject: [PATCH] reformat to have a more secure execution. Propperly parsed version input. --- cli/config.go | 10 ++++++ cli/meta/exec.go | 53 ------------------------------ cli/run.go | 85 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 76 insertions(+), 72 deletions(-) delete mode 100644 cli/meta/exec.go diff --git a/cli/config.go b/cli/config.go index aa96b98..f3a8b45 100644 --- a/cli/config.go +++ b/cli/config.go @@ -102,6 +102,16 @@ func validVmuAlis(version string) bool { return version == "default" || version == "mach" } +func (z ZVM) zigPath() (string, error) { + zig := filepath.Join(z.baseDir, "bin", "zig") + + if _, err := os.Stat(zig); err != nil { + return "", err + } + + return zig, nil +} + func (z ZVM) getVersion(version string) error { if _, err := os.Stat(filepath.Join(z.baseDir, version)); err != nil { return err diff --git a/cli/meta/exec.go b/cli/meta/exec.go deleted file mode 100644 index e96f460..0000000 --- a/cli/meta/exec.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2022 Tristan Isham. All rights reserved. -// Use of this source code is governed by the MIT -// license that can be found in the LICENSE file. -package meta - -import ( - "fmt" - "os" - "os/exec" - "strings" -) - -// Execute the given Zig command with a specified compiler -func Exec(bin string, cmd []string) error { - // zvm run 0.14.0 build run --help - if bin == "" { - return fmt.Errorf("compiler binary cannot be empty") - } - - zig := exec.Command(bin, cmd...) - zig.Stdin, zig.Stdout, zig.Stderr = os.Stdin, os.Stdout, os.Stderr - - err := zig.Run() - if err != nil { - if err, ok := err.(*exec.ExitError); ok { - os.Exit(err.ExitCode()) - } else { - return fmt.Errorf("Error executing command '%s': %s\n", cmd, err) - } - } - - return nil -} - -// Execute the given Zig command with a specified compiler -// This is a convenience function that will automatically split a -// command and execute it -func ExecString(cmd string) error { - command := strings.Split(cmd, " ") - if len(command) < 1 { - return fmt.Errorf("No command given") - } - - zig := exec.Command(command[0], command[1:]...) - zig.Stdin, zig.Stdout, zig.Stderr = os.Stdin, os.Stdout, os.Stderr - - err := zig.Run() - if err != nil { - return fmt.Errorf("Error executing command '%s': %s\n", cmd, err) - } - - return nil -} diff --git a/cli/run.go b/cli/run.go index 7b8b426..b6512b9 100644 --- a/cli/run.go +++ b/cli/run.go @@ -7,47 +7,94 @@ import ( "errors" "fmt" "os" + "os/exec" "path/filepath" - - "github.com/tristanisham/zvm/cli/meta" + "slices" + "strings" ) // Run the given Zig compiler with the provided arguments -func (z *ZVM) Run(ver string, cmd []string) error { - if err := z.getVersion(ver); err != nil { - if errors.Is(err, os.ErrNotExist) { +func (z *ZVM) Run(version string, cmd []string) error { + + if len(version) == 0 { + zig, err := z.zigPath() + if err != nil { + return fmt.Errorf("%w: no Zig version found", ErrMissingBundlePath) + } + + return z.runZig(zig, cmd) + } + + installedVersions, err := z.GetInstalledVersions() + if err != nil { + return err + } - fmt.Printf("It looks like %s isn't installed. Would you like to install it first? [y/n]\n", ver) + if slices.Contains(installedVersions, version) { + return z.runZig(version, cmd) + } else { + rawVersionStructure, err := z.fetchVersionMap() + if err != nil { + return err + } - if getConfirmation() { - if err = z.Install(ver, false); err != nil { - return err - } + _, err = getTarPath(version, &rawVersionStructure) + if err != nil { + if errors.Is(err, ErrUnsupportedVersion) { + return fmt.Errorf("%s: %q", err, version) } else { - return fmt.Errorf("version %s is not installed", ver) + return err + } + } + + fmt.Printf("It looks like %s isn't installed. Would you like to install it first? [y/n]\n", version) + + if getConfirmation() { + if err = z.Install(version, false); err != nil { + return err } + return z.runZig(version, cmd) + } else { + return fmt.Errorf("version %s is not installed", version) } } - return z.runBin(ver, cmd) } -func (z *ZVM) runBin(ver string, cmd []string) error { - // $ZVM_PATH/$VERSION/zig cmd - bin := filepath.Join(z.baseDir, ver, "zig") +func (z *ZVM) runZig(version string, cmd []string) error { + bin := strings.TrimSpace(filepath.Join(z.baseDir, version, "zig")) - // Skip symlink checks, does this Zig binary exist? - stat, err := os.Stat(bin) - if err != nil { + if stat, err := os.Stat(bin); err != nil { return fmt.Errorf("%w: %s", err, stat.Name()) } // the logging here really muddies up the output of the Zig compiler // and adds a lot of noise. For that reason this function exits with // the zig compilers exit code - if err := meta.Exec(bin, cmd); err != nil { + if err := execute(bin, cmd); err != nil { return err } return nil } + +// Execute the given Zig command with a specified compiler +func execute(bin string, cmd []string) error { + // zvm run 0.14.0 build run --help + if len(bin) == 0 { + return fmt.Errorf("compiler binary cannot be empty") + } + + zig := exec.Command(bin, cmd...) + zig.Stdin, zig.Stdout, zig.Stderr = os.Stdin, os.Stdout, os.Stderr + + if err := zig.Run(); err != nil { + if err2, ok := err.(*exec.ExitError); ok { + os.Exit(err2.ExitCode()) + } else { + return fmt.Errorf("error executing command '%s': %w", cmd, err2) + } + } + + return nil +}