mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-10-06 10:22:13 +00:00
c3788d943f
Since /tmp is writable by everybody, a user could precreate /tmp/woodpecker with 777 permissions, allowing them to modify the pipeline while it is being run, or preventing the pipeline from running. And since os.MkdirAll error code wasn't checked, the same attacker could have precreated the directory where the pipeline is executed to mess with the run, allowing code execution under the UID of the agent (who has access to the toke, to communicate with the server, which mean a attacker could inject a fake agent, steal credentials, etc)
117 lines
2.9 KiB
Go
117 lines
2.9 KiB
Go
package local
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
|
"github.com/woodpecker-ci/woodpecker/server"
|
|
)
|
|
|
|
type local struct {
|
|
cmd *exec.Cmd
|
|
output io.ReadCloser
|
|
workingdir string
|
|
}
|
|
|
|
// make sure local implements Engine
|
|
var _ types.Engine = &local{}
|
|
|
|
// New returns a new local Engine.
|
|
func New() types.Engine {
|
|
return &local{}
|
|
}
|
|
|
|
func (e *local) Name() string {
|
|
return "local"
|
|
}
|
|
|
|
func (e *local) IsAvailable() bool {
|
|
return true
|
|
}
|
|
|
|
func (e *local) Load() error {
|
|
dir, err := ioutil.TempDir("", "woodpecker-local-*")
|
|
e.workingdir = dir
|
|
return err
|
|
}
|
|
|
|
// Setup the pipeline environment.
|
|
func (e *local) Setup(ctx context.Context, proc *types.Config) error {
|
|
return nil
|
|
}
|
|
|
|
// Exec the pipeline step.
|
|
func (e *local) Exec(ctx context.Context, proc *types.Step) error {
|
|
// Get environment variables
|
|
Command := []string{}
|
|
for a, b := range proc.Environment {
|
|
if a != "HOME" && a != "SHELL" { // Don't override $HOME and $SHELL
|
|
Command = append(Command, a+"="+b)
|
|
}
|
|
}
|
|
|
|
// Get default clone image
|
|
defaultCloneImage := "docker.io/woodpeckerci/plugin-git:latest"
|
|
if len(server.Config.Pipeline.DefaultCloneImage) > 0 {
|
|
defaultCloneImage = server.Config.Pipeline.DefaultCloneImage
|
|
}
|
|
|
|
if proc.Image == defaultCloneImage {
|
|
// Default clone step
|
|
Command = append(Command, "CI_WORKSPACE="+e.workingdir+"/"+proc.Environment["CI_REPO"])
|
|
Command = append(Command, "plugin-git")
|
|
} else {
|
|
// Use "image name" as run command
|
|
Command = append(Command, proc.Image[18:len(proc.Image)-7])
|
|
Command = append(Command, "-c")
|
|
|
|
// Decode script and delete initial lines
|
|
// Deleting the initial lines removes netrc support but adds compatibility for more shells like fish
|
|
Script, _ := base64.RawStdEncoding.DecodeString(proc.Environment["CI_SCRIPT"])
|
|
Command = append(Command, string(Script)[strings.Index(string(Script), "\n\n")+2:])
|
|
}
|
|
|
|
// Prepare command
|
|
e.cmd = exec.CommandContext(ctx, "/bin/env", Command...)
|
|
|
|
// Prepare working directory
|
|
if proc.Image == defaultCloneImage {
|
|
e.cmd.Dir = e.workingdir + "/" + proc.Environment["CI_REPO_OWNER"]
|
|
} else {
|
|
e.cmd.Dir = e.workingdir + "/" + proc.Environment["CI_REPO"]
|
|
}
|
|
err := os.MkdirAll(e.cmd.Dir, 0o700)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Get output and redirect Stderr to Stdout
|
|
e.output, _ = e.cmd.StdoutPipe()
|
|
e.cmd.Stderr = e.cmd.Stdout
|
|
|
|
return e.cmd.Start()
|
|
}
|
|
|
|
// Wait for the pipeline step to complete and returns
|
|
// the completion results.
|
|
func (e *local) Wait(context.Context, *types.Step) (*types.State, error) {
|
|
return &types.State{
|
|
Exited: true,
|
|
}, e.cmd.Wait()
|
|
}
|
|
|
|
// Tail the pipeline step logs.
|
|
func (e *local) Tail(context.Context, *types.Step) (io.ReadCloser, error) {
|
|
return e.output, nil
|
|
}
|
|
|
|
// Destroy the pipeline environment.
|
|
func (e *local) Destroy(context.Context, *types.Config) error {
|
|
os.RemoveAll(e.cmd.Dir)
|
|
return nil
|
|
}
|