From b3d3b3ce79ffe12eab9db26fe58b8dbf053ff7d6 Mon Sep 17 00:00:00 2001 From: Mateusz Mierzwinski Date: Tue, 12 Nov 2024 15:23:09 -0800 Subject: [PATCH] Adding gosec support --- CHANGELOG | 12 ++++ autobuild.yaml | 2 + cmd/autobuild-go/main.go | 7 +++ internal/builder/gobuilder.go | 50 ++++++++++++++-- internal/gopkginstaller/gopkginstaller.go | 70 +++++++++++++++++++++++ 5 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 internal/gopkginstaller/gopkginstaller.go diff --git a/CHANGELOG b/CHANGELOG index 0f6b67d..497566f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,15 @@ +[0.0.4] 2024-11-12 - Enabling Go Security (gosec) + +Added: +- GoSec support (https://github.com/securego/gosec) added. Can be used when added `gosec` stage. + +Changed: +- No changes in this release + +Removed: +- No removals in this release + + [0.0.3] 2024-11-11 - Enabling versioning injection Added: diff --git a/autobuild.yaml b/autobuild.yaml index 7bad0f9..32cd21b 100644 --- a/autobuild.yaml +++ b/autobuild.yaml @@ -39,6 +39,7 @@ profiles: - arm64 - amd64 stages: + - gosec - test - build - hash @@ -49,6 +50,7 @@ profiles: - arm64 - amd64 stages: + - gosec - test - build diff --git a/cmd/autobuild-go/main.go b/cmd/autobuild-go/main.go index ec52700..7b00f43 100644 --- a/cmd/autobuild-go/main.go +++ b/cmd/autobuild-go/main.go @@ -5,6 +5,7 @@ import ( "autobuild-go/internal/colors" "autobuild-go/internal/config" "autobuild-go/internal/golanginstaller" + "autobuild-go/internal/gopkginstaller" "autobuild-go/internal/models" "autobuild-go/internal/processors" "fmt" @@ -83,6 +84,12 @@ func main() { os.Exit(1) } + colors.HorizontalLine("Extra tools and packages") + gopkgInstaller := gopkginstaller.New(installer.GoToolchainDir(), map[string]string{ + "gosec": "github.com/securego/gosec/v2/cmd/gosec@latest", + }) + gopkgInstaller.Install() + colors.HorizontalLine("Testing & building Go projects") projectDestChan := make(chan models.Project, 5) diff --git a/internal/builder/gobuilder.go b/internal/builder/gobuilder.go index f8889e1..77bcd8e 100644 --- a/internal/builder/gobuilder.go +++ b/internal/builder/gobuilder.go @@ -13,6 +13,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "sync" "time" @@ -54,6 +55,13 @@ func (g *GoBuilder) Build(projectsSource chan models.Project) { } } + if _, ok := g.stages["gosec"]; ok { + if err := g.gosecExec(project); err != nil { + colors.ErrLog("Error: %v", err) + return + } + } + if _, ok := g.stages["build"]; ok { if err := g.buildExec(project); err != nil { colors.ErrLog("Error building app %s: %v", project.AppName, err) @@ -89,7 +97,7 @@ func (g *GoBuilder) testExec(project models.Project) error { // Execute the command if err := cmd.Run(); err != nil { // If there's an error, return the captured stdout and stderr as part of the error - persistLog(outBuf, errBuf, project.BuildDir, project.AppName) + persistLog("test-", outBuf, errBuf, project.BuildDir, project.AppName) return fmt.Errorf("error testing %s: %v. Logs created", project.AppName, err) } colors.Success("Successfully tested application "+colors.Blue+"`%s`"+colors.Reset+" in "+colors.Yellow+"%.1f"+colors.Reset+" seconds", project.AppName, time.Since(tn).Seconds()) @@ -97,6 +105,36 @@ func (g *GoBuilder) testExec(project models.Project) error { return nil } +func (g *GoBuilder) gosecExec(project models.Project) error { + tn := time.Now() + colors.Icon(colors.Yellow, "\u226b", "Go security check of "+colors.Blue+"%s"+colors.Reset+" app", project.AppName) + + suffix := "" + if runtime.GOOS == "windows" { + suffix = ".exe" + } + + // Prepare the build command: go build -o outputPath project.AppMainSrcDir + cmd := exec.Command(filepath.Join(g.goPathPath, "bin", "gosec"+suffix), "./...") + cmd.Dir = project.RootDir + cmd.Env = g.defaultEnv + + // Capture output + var outBuf, errBuf bytes.Buffer + cmd.Stdout = &outBuf + cmd.Stderr = &errBuf + + // Execute the command + if err := cmd.Run(); err != nil { + // If there's an error, return the captured stdout and stderr as part of the error + persistLog("gosec-", outBuf, errBuf, project.BuildDir, project.AppName) + return fmt.Errorf("security error %s: %v. Logs created", project.AppName, err) + } + colors.Success("Successfully checked application "+colors.Blue+"`%s`"+colors.Reset+" in "+colors.Yellow+"%.1f"+colors.Reset+" seconds", project.AppName, time.Since(tn).Seconds()) + + return nil +} + func (g *GoBuilder) sumExec(project models.Project) error { for _, target := range g.targets { outputName := fmt.Sprintf("%s-%s-%s%s", project.AppName, target.GOOS, target.GOARCH, target.EXECSUFFIX) @@ -135,19 +173,19 @@ func (g *GoBuilder) sumExec(project models.Project) error { return nil } -func persistLog(buf bytes.Buffer, buf2 bytes.Buffer, dir string, name string) { - outFileLog := filepath.Join(dir, fmt.Sprintf("build-%s.log", name)) - outErrLog := filepath.Join(dir, fmt.Sprintf("error-%s.log", name)) +func persistLog(prefix string, buf bytes.Buffer, buf2 bytes.Buffer, dir string, name string) { + outFileLog := filepath.Join(dir, fmt.Sprintf("%sbuild-%s.log", prefix, name)) + outErrLog := filepath.Join(dir, fmt.Sprintf("%serror-%s.log", prefix, name)) for k, v := range map[string]*bytes.Buffer{ outFileLog: &buf, outErrLog: &buf2, } { if err := os.WriteFile(k, v.Bytes(), os.ModePerm); err != nil { - fmt.Printf("Error writing build log: %v", err) + fmt.Printf("Error writing log: %v", err) continue } - colors.ErrLog("Error building app "+colors.Red+"%s"+colors.Reset+"! Log stored in %s", name, k) + colors.ErrLog("Error "+colors.Red+"%s"+colors.Reset+"! Log stored in %s", prefix+name, k) } } diff --git a/internal/gopkginstaller/gopkginstaller.go b/internal/gopkginstaller/gopkginstaller.go new file mode 100644 index 0000000..6d2ae22 --- /dev/null +++ b/internal/gopkginstaller/gopkginstaller.go @@ -0,0 +1,70 @@ +package gopkginstaller + +import ( + "autobuild-go/internal/colors" + "bytes" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" +) + +type pkgInstaller struct { + packages map[string]string + goexecPath string + gorootPath string + gopathPath string +} + +func (p *pkgInstaller) Install() error { + suffix := "" + if runtime.GOOS == "windows" { + suffix = ".exe" + } + for k, v := range p.packages { + fmt.Printf("\t%s>>%sChecking package %s%s%s... ", colors.Green, colors.Reset, colors.Blue, k, colors.Reset) + + if _, err := os.Lstat(filepath.Join(p.gopathPath, "bin", fmt.Sprintf("%s%s", k, suffix))); err != nil { + if p.installPkg(v) != nil { + fmt.Println(colors.Red, "[fail]", colors.Reset) + continue + } + fmt.Println(colors.Green, "[ok]", colors.Reset) + continue + } + fmt.Println(colors.Green, "[ok]", colors.Reset) + } + return nil +} + +func (p *pkgInstaller) installPkg(v string) error { + envVariables := os.Environ() + envVariables = append(envVariables, "GOPATH="+p.gopathPath, "GOROOT="+p.gorootPath) + envVariables = append(envVariables, "PATH="+fmt.Sprintf("%s%s%s", filepath.Join(p.gopathPath, "bin"), string(os.PathListSeparator), os.Getenv("PATH"))) + + execCmd := exec.Command(p.goexecPath, "install", v) + execCmd.Env = envVariables + + execBuff := new(bytes.Buffer) + execCmd.Stdout = execBuff + execCmd.Stderr = execBuff + + if err := execCmd.Run(); err != nil { + return err + } + return nil +} + +func New(toolchainDir string, packages map[string]string) *pkgInstaller { + goExec := "go" + if runtime.GOOS == "windows" { + goExec = "go.exe" + } + return &pkgInstaller{ + packages: packages, + goexecPath: filepath.Join(toolchainDir, "go", "bin", goExec), + gorootPath: filepath.Join(toolchainDir, "go"), + gopathPath: filepath.Join(toolchainDir, "gopath"), + } +}