VYPR
High severityNVD Advisory· Published Jul 19, 2024· Updated Aug 2, 2024

Custom workspace allow to overwrite plugin entrypoint executable in Woodpecker

CVE-2024-41121

Description

CVE-2024-41121 allows any user in Woodpecker CI/CD server to execute malicious pipelines that can lead to host takeover or secret extraction by overwriting plugin entrypoints, patched in 2.7.0.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2024-41121 allows any user in Woodpecker CI/CD server to execute malicious pipelines that can lead to host takeover or secret extraction by overwriting plugin entrypoints, patched in 2.7.0.

Root

Cause

Woodpecker CI/CD server permitted any authenticated user to create and trigger pipeline runs containing malicious workflow steps. The core issue stems from insufficient validation of workspace base paths and environment variables given to plugins. Specifically, by setting the workspace base to a location like /usr/local/, an attacker could overwrite plugin entrypoint files (e.g., /usr/local/bin/dockerd-entrypoint.sh) in a preceding step, which would then be executed in a privileged container when a plugin such as plugins/docker runs [1][4].

Exploitation

An attacker simply needs to define a pipeline workflow with a step that modifies files within the configurable workspace base path, followed by a step that invokes a privileged plugin whose entrypoint resides within that path. The attacker can also set environment variables like LD_PRELOAD to hijack plugin execution, as the IsPlugin function only checked that Commands and Entrypoint were empty but did not restrict workspace base or environment variables [4]. The attack requires no special privileges beyond the ability to create a workflow in the Woodpecker server.

Impact

Successful exploitation yields two types of impact: (1) host takeover of the agent machine executing the workflow, because the malicious code runs in a privileged container; (2) extraction of secrets that are normally provided to plugins, since the attacker’s overwritten entrypoint can intercept and exfiltrate environment variables [1][4].

Mitigation

Woodpecker version 2.7.0 fixes the vulnerability by introducing a blocklist of environment variables that could alter plugin execution, enforcing that plugins only mount the workspace base in a predefined location, and disallowing arbitrary environment variable settings for plugins [2][3]. Users should upgrade immediately, as no workarounds are available [1].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
go.woodpecker-ci.org/woodpecker/v2Go
< 2.7.02.7.0
go.woodpecker-ci.org/woodpeckerGo
< 2.7.02.7.0

Affected products

3

Patches

2
805c3156f857

🎉 Release 2.7.0 (#3793)

https://github.com/woodpecker-ci/woodpeckerwoodpecker-botJul 18, 2024via osv
1 file changed · +90 0
  • CHANGELOG.md+90 0 modified
    @@ -1,5 +1,95 @@
     # Changelog
     
    +## [2.7.0](https://github.com/woodpecker-ci/woodpecker/releases/tag/v2.7.0) - 2024-07-18
    +
    +### ❤️ Thanks to all contributors! ❤️
    +
    +@6543, @anbraten, @dvjn, @hhamalai, @lafriks, @pat-s, @qwerty287, @smainz, @tongjicoder, @zc-devs
    +
    +### 🔒 Security
    +
    +- Add blocklist of environment variables who could alter execution of plugins [[#3934](https://github.com/woodpecker-ci/woodpecker/pull/3934)]
    +- Make sure plugins only mount the workspace base in a predefinde location [[#3933](https://github.com/woodpecker-ci/woodpecker/pull/3933)]
    +- Disallow to set arbitrary environments for plugins [[#3909](https://github.com/woodpecker-ci/woodpecker/pull/3909)]
    +- Use proper oauth state [[#3847](https://github.com/woodpecker-ci/woodpecker/pull/3847)]
    +- Enhance token checking [[#3842](https://github.com/woodpecker-ci/woodpecker/pull/3842)]
    +- Bump github.com/hashicorp/go-retryablehttp v0.7.5 -> v0.7.7 [[#3834](https://github.com/woodpecker-ci/woodpecker/pull/3834)]
    +
    +### ✨ Features
    +
    +- Gracefully shutdown server [[#3896](https://github.com/woodpecker-ci/woodpecker/pull/3896)]
    +- Gracefully shutdown agent [[#3895](https://github.com/woodpecker-ci/woodpecker/pull/3895)]
    +- Convert urls in logs to links  [[#3904](https://github.com/woodpecker-ci/woodpecker/pull/3904)]
    +- Allow login using multiple forges [[#3822](https://github.com/woodpecker-ci/woodpecker/pull/3822)]
    +- Global and organization registries [[#1672](https://github.com/woodpecker-ci/woodpecker/pull/1672)]
    +- Cli get repo from git remote [[#3830](https://github.com/woodpecker-ci/woodpecker/pull/3830)]
    +- Add api for forges [[#3733](https://github.com/woodpecker-ci/woodpecker/pull/3733)]
    +
    +### 📈 Enhancement
    +
    +- Cli fix pipeline logs [[#3913](https://github.com/woodpecker-ci/woodpecker/pull/3913)]
    +- Migrate to github.com/urfave/cli/v3 [[#2951](https://github.com/woodpecker-ci/woodpecker/pull/2951)]
    +- Allow to change the working directory also for plugins and services [[#3914](https://github.com/woodpecker-ci/woodpecker/pull/3914)]
    +- Remove `unplugin-icons` [[#3809](https://github.com/woodpecker-ci/woodpecker/pull/3809)]
    +- Release windows binaries as zip file [[#3906](https://github.com/woodpecker-ci/woodpecker/pull/3906)]
    +- Convert to openapi 3.0 [[#3897](https://github.com/woodpecker-ci/woodpecker/pull/3897)]
    +- Enhance pipeline list [[#3898](https://github.com/woodpecker-ci/woodpecker/pull/3898)]
    +- Add user registries UI [[#3888](https://github.com/woodpecker-ci/woodpecker/pull/3888)]
    +- Sort users by login [[#3891](https://github.com/woodpecker-ci/woodpecker/pull/3891)]
    +- Exclude dummy backend in production [[#3877](https://github.com/woodpecker-ci/woodpecker/pull/3877)]
    +- Fix deploy task env [[#3878](https://github.com/woodpecker-ci/woodpecker/pull/3878)]
    +- Get default branch and show message in pipeline list [[#3867](https://github.com/woodpecker-ci/woodpecker/pull/3867)]
    +- Add timestamp for last work done by agent [[#3844](https://github.com/woodpecker-ci/woodpecker/pull/3844)]
    +- Adjust logger types [[#3859](https://github.com/woodpecker-ci/woodpecker/pull/3859)]
    +- Cleanup state reporting [[#3850](https://github.com/woodpecker-ci/woodpecker/pull/3850)]
    +- Unify DB tables/columns [[#3806](https://github.com/woodpecker-ci/woodpecker/pull/3806)]
    +- Let webhook pass on pipeline parsing error [[#3829](https://github.com/woodpecker-ci/woodpecker/pull/3829)]
    +- Exclude mocks from release build [[#3831](https://github.com/woodpecker-ci/woodpecker/pull/3831)]
    +- K8s secrets reference from step [[#3655](https://github.com/woodpecker-ci/woodpecker/pull/3655)]
    +
    +### 🐛 Bug Fixes
    +
    +- Handle empty repositories in gitea when listing PRs [[#3925](https://github.com/woodpecker-ci/woodpecker/pull/3925)]
    +- Update alpine package dep for docker images [[#3917](https://github.com/woodpecker-ci/woodpecker/pull/3917)]
    +- Don't report error if agent was terminated gracefully [[#3894](https://github.com/woodpecker-ci/woodpecker/pull/3894)]
    +- Let agents continuously report their health [[#3893](https://github.com/woodpecker-ci/woodpecker/pull/3893)]
    +- Ignore warnings for cli exec [[#3868](https://github.com/woodpecker-ci/woodpecker/pull/3868)]
    +- Correct favicon states [[#3832](https://github.com/woodpecker-ci/woodpecker/pull/3832)]
    +- Cleanup of the login flow and tests [[#3810](https://github.com/woodpecker-ci/woodpecker/pull/3810)]
    +- Fix newlines in logs [[#3808](https://github.com/woodpecker-ci/woodpecker/pull/3808)]
    +- Fix authentication error handling [[#3807](https://github.com/woodpecker-ci/woodpecker/pull/3807)]
    +
    +### 📚 Documentation
    +
    +- Streamline docs for new users [[#3803](https://github.com/woodpecker-ci/woodpecker/pull/3803)]
    +- Add mastodon verification [[#3843](https://github.com/woodpecker-ci/woodpecker/pull/3843)]
    +- chore(deps): update docs npm deps non-major [[#3837](https://github.com/woodpecker-ci/woodpecker/pull/3837)]
    +- fix(deps): update docs npm deps non-major [[#3824](https://github.com/woodpecker-ci/woodpecker/pull/3824)]
    +- Add openSUSE package [[#3800](https://github.com/woodpecker-ci/woodpecker/pull/3800)]
    +- chore(deps): update docs npm deps non-major [[#3798](https://github.com/woodpecker-ci/woodpecker/pull/3798)]
    +- Add "Docker Tags" Plugin [[#3796](https://github.com/woodpecker-ci/woodpecker/pull/3796)]
    +- chore(deps): update dependency marked to v13 [[#3792](https://github.com/woodpecker-ci/woodpecker/pull/3792)]
    +- chore: fix some comments [[#3788](https://github.com/woodpecker-ci/woodpecker/pull/3788)]
    +
    +### Misc
    +
    +- chore(deps): update web npm deps non-major [[#3930](https://github.com/woodpecker-ci/woodpecker/pull/3930)]
    +- chore(deps): update dependency vitest to v2 [[#3905](https://github.com/woodpecker-ci/woodpecker/pull/3905)]
    +- fix(deps): update module github.com/google/go-github/v62 to v63 [[#3910](https://github.com/woodpecker-ci/woodpecker/pull/3910)]
    +- chore(deps): update docker.io/woodpeckerci/plugin-docker-buildx docker tag to v4.1.0 [[#3908](https://github.com/woodpecker-ci/woodpecker/pull/3908)]
    +- Update plugin-git and add renovate trigger [[#3901](https://github.com/woodpecker-ci/woodpecker/pull/3901)]
    +- chore(deps): update docker.io/mstruebing/editorconfig-checker docker tag to v3.0.3 [[#3903](https://github.com/woodpecker-ci/woodpecker/pull/3903)]
    +- fix(deps): update golang-packages [[#3875](https://github.com/woodpecker-ci/woodpecker/pull/3875)]
    +- chore(deps): lock file maintenance [[#3876](https://github.com/woodpecker-ci/woodpecker/pull/3876)]
    +- [pre-commit.ci] pre-commit autoupdate [[#3862](https://github.com/woodpecker-ci/woodpecker/pull/3862)]
    +- Add dummy backend [[#3820](https://github.com/woodpecker-ci/woodpecker/pull/3820)]
    +- chore(deps): update dependency replace-in-file to v8 [[#3852](https://github.com/woodpecker-ci/woodpecker/pull/3852)]
    +- Update forgejo sdk [[#3840](https://github.com/woodpecker-ci/woodpecker/pull/3840)]
    +- chore(deps): lock file maintenance [[#3838](https://github.com/woodpecker-ci/woodpecker/pull/3838)]
    +- Allow to set dist dir using env var [[#3814](https://github.com/woodpecker-ci/woodpecker/pull/3814)]
    +- chore(deps): lock file maintenance [[#3805](https://github.com/woodpecker-ci/woodpecker/pull/3805)]
    +- chore(deps): update docker.io/lycheeverse/lychee docker tag to v0.15.1 [[#3797](https://github.com/woodpecker-ci/woodpecker/pull/3797)]
    +
     ## [2.6.0](https://github.com/woodpecker-ci/woodpecker/releases/tag/v2.6.0) - 2024-06-13
     
     ### ❤️ Thanks to all contributors! ❤️
    
764329ed1dbc

Make sure plugins only mount the workspace base in a predefinde location (#3933)

10 files changed · +75 24
  • docs/docs/20-usage/20-workflow-syntax.md+7 1 modified
    @@ -523,7 +523,9 @@ For more details check the [services docs](./60-services.md).
     
     ## `workspace`
     
    -The workspace defines the shared volume and working directory shared by all workflow steps. The default workspace matches the pattern `/woodpecker/src/github.com/octocat/hello-world`, based on your repository URL.
    +The workspace defines the shared volume and working directory shared by all workflow steps.
    +The default workspace base is `/woodpecker` and the path is extended with the repository URL (`src/{url-without-schema}`).
    +So an example would be `/woodpecker/src/github.com/octocat/hello-world`.
     
     The workspace can be customized using the workspace block in the YAML file:
     
    @@ -540,6 +542,10 @@ The workspace can be customized using the workspace block in the YAML file:
            - go test
     ```
     
    +:::note
    +Plugins will always have the workspace base at `/woodpecker`
    +:::
    +
     The base attribute defines a shared base volume available to all steps. This ensures your source code, dependencies and compiled binaries are persisted and shared between steps.
     
     ```diff
    
  • docs/docs/20-usage/51-plugins/51-overview.md+5 0 modified
    @@ -47,6 +47,11 @@ steps:
     ## Plugin Isolation
     
     Plugins are just pipeline steps. They share the build workspace, mounted as a volume, and therefore have access to your source tree.
    +While normal steps are all about arbitrary code execution, plugins should only allow the functions intended by the plugin author.
    +
    +So there are a few limitations, like the workspace base is always mounted at `/woodpecker`, but the working directory is dynamically adjusted acordingly. So as user of a plugin you should not have to care about this.
    +
    +Also instead of using environment variables the plugin should only care about one prefixed with `PLUGIN_` witch are the internaml representation of the **settings** ([read more](./20-creating-plugins.md)).
     
     ## Finding Plugins
     
    
  • docs/versioned_docs/version-2.7/20-usage/20-workflow-syntax.md+7 1 modified
    @@ -523,7 +523,9 @@ For more details check the [services docs](./60-services.md).
     
     ## `workspace`
     
    -The workspace defines the shared volume and working directory shared by all workflow steps. The default workspace matches the pattern `/woodpecker/src/github.com/octocat/hello-world`, based on your repository URL.
    +The workspace defines the shared volume and working directory shared by all workflow steps.
    +The default workspace base is `/woodpecker` and the path is extended with the repository URL (`src/{url-without-schema}`).
    +So an example would be `/woodpecker/src/github.com/octocat/hello-world`.
     
     The workspace can be customized using the workspace block in the YAML file:
     
    @@ -540,6 +542,10 @@ The workspace can be customized using the workspace block in the YAML file:
            - go test
     ```
     
    +:::note
    +Plugins will always have the workspace base at `/woodpecker`
    +:::
    +
     The base attribute defines a shared base volume available to all steps. This ensures your source code, dependencies and compiled binaries are persisted and shared between steps.
     
     ```diff
    
  • docs/versioned_docs/version-2.7/20-usage/51-plugins/51-overview.md+5 0 modified
    @@ -47,6 +47,11 @@ steps:
     ## Plugin Isolation
     
     Plugins are just pipeline steps. They share the build workspace, mounted as a volume, and therefore have access to your source tree.
    +While normal steps are all about arbitrary code execution, plugins should only allow the functions intended by the plugin author.
    +
    +So there are a few limitations, like the workspace base is always mounted at `/woodpecker`, but the working directory is dynamically adjusted acordingly. So as user of a plugin you should not have to care about this.
    +
    +Also instead of using environment variables the plugin should only care about one prefixed with `PLUGIN_` witch are the internaml representation of the **settings** ([read more](./20-creating-plugins.md)).
     
     ## Finding Plugins
     
    
  • pipeline/frontend/yaml/compiler/compiler.go+5 4 modified
    @@ -16,6 +16,7 @@ package compiler
     
     import (
     	"fmt"
    +	"path"
     
     	backend_types "go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types"
     	"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/metadata"
    @@ -98,8 +99,8 @@ type Compiler struct {
     	networks          []string
     	env               map[string]string
     	cloneEnv          map[string]string
    -	base              string
    -	path              string
    +	workspaceBase     string
    +	workspacePath     string
     	metadata          metadata.Metadata
     	registries        []Registry
     	secrets           map[string]Secret
    @@ -156,10 +157,10 @@ func (c *Compiler) Compile(conf *yaml_types.Workflow) (*backend_types.Config, er
     	// overrides the default workspace paths when specified
     	// in the YAML file.
     	if len(conf.Workspace.Base) != 0 {
    -		c.base = conf.Workspace.Base
    +		c.workspaceBase = path.Clean(conf.Workspace.Base)
     	}
     	if len(conf.Workspace.Path) != 0 {
    -		c.path = conf.Workspace.Path
    +		c.workspacePath = path.Clean(conf.Workspace.Path)
     	}
     
     	cloneImage := constant.DefaultCloneImage
    
  • pipeline/frontend/yaml/compiler/compiler_test.go+20 9 modified
    @@ -61,13 +61,14 @@ func TestSecretAvailable(t *testing.T) {
     }
     
     func TestCompilerCompile(t *testing.T) {
    +	repoURL := "https://github.com/octocat/hello-world"
     	compiler := New(
     		WithMetadata(metadata.Metadata{
     			Repo: metadata.Repo{
     				Owner:    "octacat",
     				Name:     "hello-world",
     				Private:  true,
    -				ForgeURL: "https://github.com/octocat/hello-world",
    +				ForgeURL: repoURL,
     				CloneURL: "https://github.com/octocat/hello-world.git",
     			},
     		}),
    @@ -76,6 +77,8 @@ func TestCompilerCompile(t *testing.T) {
     			"COLORED": "true",
     		}),
     		WithPrefix("test"),
    +		// we use "/test" as custom workspace base to ensure the enforcement of the pluginWorkspaceBase is applied
    +		WithWorkspaceFromURL("/test", repoURL),
     	)
     
     	defaultNetworks := []*backend_types.Network{{
    @@ -92,7 +95,8 @@ func TestCompilerCompile(t *testing.T) {
     			Image:      constant.DefaultCloneImage,
     			OnSuccess:  true,
     			Failure:    "fail",
    -			Volumes:    []string{defaultVolumes[0].Name + ":"},
    +			Volumes:    []string{defaultVolumes[0].Name + ":/woodpecker"},
    +			WorkingDir: "/woodpecker/src/github.com/octocat/hello-world",
     			Networks:   []backend_types.Conn{{Name: "test_default", Aliases: []string{"clone"}}},
     			ExtraHosts: []backend_types.HostAlias{},
     		}},
    @@ -137,7 +141,8 @@ func TestCompilerCompile(t *testing.T) {
     						Image:      "dummy_img",
     						OnSuccess:  true,
     						Failure:    "fail",
    -						Volumes:    []string{defaultVolumes[0].Name + ":"},
    +						Volumes:    []string{defaultVolumes[0].Name + ":/woodpecker"},
    +						WorkingDir: "/woodpecker/src/github.com/octocat/hello-world",
     						Networks:   []backend_types.Conn{{Name: "test_default", Aliases: []string{"dummy"}}},
     						ExtraHosts: []backend_types.HostAlias{},
     					}},
    @@ -172,7 +177,8 @@ func TestCompilerCompile(t *testing.T) {
     						Commands:   []string{"env"},
     						OnSuccess:  true,
     						Failure:    "fail",
    -						Volumes:    []string{defaultVolumes[0].Name + ":"},
    +						Volumes:    []string{defaultVolumes[0].Name + ":/test"},
    +						WorkingDir: "/test/src/github.com/octocat/hello-world",
     						Networks:   []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo env"}}},
     						ExtraHosts: []backend_types.HostAlias{},
     					}},
    @@ -184,7 +190,8 @@ func TestCompilerCompile(t *testing.T) {
     						Commands:   []string{"echo 1"},
     						OnSuccess:  true,
     						Failure:    "fail",
    -						Volumes:    []string{defaultVolumes[0].Name + ":"},
    +						Volumes:    []string{defaultVolumes[0].Name + ":/test"},
    +						WorkingDir: "/test/src/github.com/octocat/hello-world",
     						Networks:   []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 1"}}},
     						ExtraHosts: []backend_types.HostAlias{},
     					}, {
    @@ -194,7 +201,8 @@ func TestCompilerCompile(t *testing.T) {
     						Commands:   []string{"echo 2"},
     						OnSuccess:  true,
     						Failure:    "fail",
    -						Volumes:    []string{defaultVolumes[0].Name + ":"},
    +						Volumes:    []string{defaultVolumes[0].Name + ":/test"},
    +						WorkingDir: "/test/src/github.com/octocat/hello-world",
     						Networks:   []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 2"}}},
     						ExtraHosts: []backend_types.HostAlias{},
     					}},
    @@ -228,7 +236,8 @@ func TestCompilerCompile(t *testing.T) {
     						Commands:   []string{"env"},
     						OnSuccess:  true,
     						Failure:    "fail",
    -						Volumes:    []string{defaultVolumes[0].Name + ":"},
    +						Volumes:    []string{defaultVolumes[0].Name + ":/test"},
    +						WorkingDir: "/test/src/github.com/octocat/hello-world",
     						Networks:   []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo env"}}},
     						ExtraHosts: []backend_types.HostAlias{},
     					}, {
    @@ -238,7 +247,8 @@ func TestCompilerCompile(t *testing.T) {
     						Commands:   []string{"echo 2"},
     						OnSuccess:  true,
     						Failure:    "fail",
    -						Volumes:    []string{defaultVolumes[0].Name + ":"},
    +						Volumes:    []string{defaultVolumes[0].Name + ":/test"},
    +						WorkingDir: "/test/src/github.com/octocat/hello-world",
     						Networks:   []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo 2"}}},
     						ExtraHosts: []backend_types.HostAlias{},
     					}},
    @@ -250,7 +260,8 @@ func TestCompilerCompile(t *testing.T) {
     						Commands:   []string{"echo 1"},
     						OnSuccess:  true,
     						Failure:    "fail",
    -						Volumes:    []string{defaultVolumes[0].Name + ":"},
    +						Volumes:    []string{defaultVolumes[0].Name + ":/test"},
    +						WorkingDir: "/test/src/github.com/octocat/hello-world",
     						Networks:   []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo 1"}}},
     						ExtraHosts: []backend_types.HostAlias{},
     					}},
    
  • pipeline/frontend/yaml/compiler/convert.go+21 4 modified
    @@ -30,18 +30,31 @@ import (
     	"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/utils"
     )
     
    +const (
    +	// The pluginWorkspaceBase should not be changed, only if you are sure what you do.
    +	pluginWorkspaceBase = "/woodpecker"
    +	// DefaultWorkspaceBase is set if not altered by the user.
    +	DefaultWorkspaceBase = pluginWorkspaceBase
    +)
    +
     func (c *Compiler) createProcess(container *yaml_types.Container, stepType backend_types.StepType) (*backend_types.Step, error) {
     	var (
     		uuid = ulid.Make()
     
     		detached   bool
     		workingDir string
     
    -		workspace   = fmt.Sprintf("%s_default:%s", c.prefix, c.base)
     		privileged  = container.Privileged
     		networkMode = container.NetworkMode
     	)
     
    +	workspaceBase := c.workspaceBase
    +	if container.IsPlugin() {
    +		// plugins have a predefined workspace base to not tamper with entrypoint executables
    +		workspaceBase = pluginWorkspaceBase
    +	}
    +	workspaceVolume := fmt.Sprintf("%s_default:%s", c.prefix, workspaceBase)
    +
     	networks := []backend_types.Conn{
     		{
     			Name:    fmt.Sprintf("%s_default", c.prefix),
    @@ -66,7 +79,7 @@ func (c *Compiler) createProcess(container *yaml_types.Container, stepType backe
     
     	var volumes []string
     	if !c.local {
    -		volumes = append(volumes, workspace)
    +		volumes = append(volumes, workspaceVolume)
     	}
     	volumes = append(volumes, c.volumes...)
     	for _, volume := range container.Volumes.Volumes {
    @@ -77,7 +90,7 @@ func (c *Compiler) createProcess(container *yaml_types.Container, stepType backe
     	environment := map[string]string{}
     	maps.Copy(environment, c.env)
     
    -	environment["CI_WORKSPACE"] = path.Join(c.base, c.path)
    +	environment["CI_WORKSPACE"] = path.Join(workspaceBase, c.workspacePath)
     
     	if stepType == backend_types.StepTypeService || container.Detached {
     		detached = true
    @@ -219,7 +232,11 @@ func (c *Compiler) stepWorkingDir(container *yaml_types.Container) string {
     	if path.IsAbs(container.Directory) {
     		return container.Directory
     	}
    -	return path.Join(c.base, c.path, container.Directory)
    +	base := c.workspaceBase
    +	if container.IsPlugin() {
    +		base = pluginWorkspaceBase
    +	}
    +	return path.Join(base, c.workspacePath, container.Directory)
     }
     
     func convertPort(portDef string) (backend_types.Port, error) {
    
  • pipeline/frontend/yaml/compiler/option.go+2 2 modified
    @@ -97,8 +97,8 @@ func WithNetrc(username, password, machine string) Option {
     // plugin steps in the pipeline.
     func WithWorkspace(base, path string) Option {
     	return func(compiler *Compiler) {
    -		compiler.base = base
    -		compiler.path = path
    +		compiler.workspaceBase = base
    +		compiler.workspacePath = path
     	}
     }
     
    
  • pipeline/frontend/yaml/compiler/option_test.go+2 2 modified
    @@ -29,8 +29,8 @@ func TestWithWorkspace(t *testing.T) {
     			"src/github.com/octocat/hello-world",
     		),
     	)
    -	assert.Equal(t, "/pipeline", compiler.base)
    -	assert.Equal(t, "src/github.com/octocat/hello-world", compiler.path)
    +	assert.Equal(t, "/pipeline", compiler.workspaceBase)
    +	assert.Equal(t, "src/github.com/octocat/hello-world", compiler.workspacePath)
     }
     
     func TestWithEscalated(t *testing.T) {
    
  • server/pipeline/stepbuilder/stepBuilder.go+1 1 modified
    @@ -291,7 +291,7 @@ func (b *StepBuilder) toInternalRepresentation(parsed *yaml_types.Workflow, envi
     			),
     		),
     		compiler.WithProxy(b.ProxyOpts),
    -		compiler.WithWorkspaceFromURL("/woodpecker", b.Repo.ForgeURL),
    +		compiler.WithWorkspaceFromURL(compiler.DefaultWorkspaceBase, b.Repo.ForgeURL),
     		compiler.WithMetadata(metadata),
     		compiler.WithTrusted(b.Repo.IsTrusted),
     		compiler.WithNetrcOnlyTrusted(b.Repo.NetrcOnlyTrusted),
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.