mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-06-10 11:54:27 +02:00
Fix host cleanup, volume allowlist, cache upload, and action host edge cases (#970)
## Summary - prevent host-mode execution from deleting caller-owned workdirs - harden `valid_volumes` checks against `..` and symlink escapes - return immediately after artifact cache upload write failures - default implicit remote action clone hosts to `GitHubInstance`/`github.com` Authored with assistance from OpenAI Codex GPT-5. --------- Co-authored-by: silverwind <me@silverwind.io> Reviewed-on: https://gitea.com/gitea/runner/pulls/970 Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
This commit is contained in:
@@ -220,12 +220,12 @@ func (rc *RunContext) startHostEnvironment() common.Executor {
|
||||
}
|
||||
toolCache := filepath.Join(cacheDir, "tool_cache")
|
||||
rc.JobContainer = &container.HostEnvironment{
|
||||
Path: path,
|
||||
TmpDir: runnerTmp,
|
||||
ToolCache: toolCache,
|
||||
Workdir: rc.Config.Workdir,
|
||||
BindWorkdir: rc.Config.BindWorkdir,
|
||||
ActPath: actPath,
|
||||
Path: path,
|
||||
TmpDir: runnerTmp,
|
||||
ToolCache: toolCache,
|
||||
Workdir: rc.Config.Workdir,
|
||||
CleanWorkdir: rc.Config.CleanWorkdir,
|
||||
ActPath: actPath,
|
||||
CleanUp: func() {
|
||||
os.RemoveAll(miscpath)
|
||||
},
|
||||
|
||||
@@ -73,6 +73,7 @@ type Config struct {
|
||||
EventJSON string // the content of JSON file to use for event.json in containers, overrides EventPath
|
||||
ContainerNamePrefix string // the prefix of container name
|
||||
ContainerMaxLifetime time.Duration // the max lifetime of job containers
|
||||
CleanWorkdir bool // remove host executor workdir on teardown
|
||||
DefaultActionInstance string // the default actions web site
|
||||
PlatformPicker func(labels []string) string // platform picker, it will take precedence over Platforms if isn't nil
|
||||
JobLoggerLevel *log.Level // the level of job logger
|
||||
@@ -91,6 +92,17 @@ func (c Config) GetToken() string {
|
||||
return token
|
||||
}
|
||||
|
||||
// DefaultActionURL returns the host used for implicit remote actions.
|
||||
func (c Config) DefaultActionURL() string {
|
||||
if c.DefaultActionInstance != "" {
|
||||
return c.DefaultActionInstance
|
||||
}
|
||||
if c.GitHubInstance != "" {
|
||||
return c.GitHubInstance
|
||||
}
|
||||
return "github.com"
|
||||
}
|
||||
|
||||
type caller struct {
|
||||
runContext *RunContext
|
||||
|
||||
|
||||
@@ -113,9 +113,10 @@ func (sar *stepActionRemote) prepareActionExecutor() common.Executor {
|
||||
}
|
||||
|
||||
actionDir := fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), sar.Step.UsesHash())
|
||||
token := getGitCloneToken(sar.getRunContext().Config, sar.remoteAction.CloneURL(sar.RunContext.Config.DefaultActionInstance))
|
||||
defaultActionURL := sar.RunContext.Config.DefaultActionURL()
|
||||
token := getGitCloneToken(sar.getRunContext().Config, sar.remoteAction.CloneURL(defaultActionURL))
|
||||
gitClone := stepActionRemoteNewCloneExecutor(git.NewGitCloneExecutorInput{
|
||||
URL: sar.remoteAction.CloneURL(sar.RunContext.Config.DefaultActionInstance),
|
||||
URL: sar.remoteAction.CloneURL(defaultActionURL),
|
||||
Ref: sar.remoteAction.Ref,
|
||||
Dir: actionDir,
|
||||
Token: token,
|
||||
@@ -274,7 +275,7 @@ func (sar *stepActionRemote) cloneSkipTLS() bool {
|
||||
if sar.remoteAction.URL == "" {
|
||||
// Empty URL means the default action instance should be used
|
||||
// Return true if the URL of the Gitea instance is the same as the URL of the default action instance
|
||||
return sar.RunContext.Config.DefaultActionInstance == sar.RunContext.Config.GitHubInstance
|
||||
return sar.RunContext.Config.DefaultActionURL() == sar.RunContext.Config.GitHubInstance
|
||||
}
|
||||
// Return true if the URL of the remote action is the same as the URL of the Gitea instance
|
||||
return sar.remoteAction.URL == sar.RunContext.Config.GitHubInstance
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.yaml.in/yaml/v4"
|
||||
)
|
||||
|
||||
@@ -434,6 +435,57 @@ func TestStepActionRemotePreThroughActionToken(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepActionRemoteUsesGitHubInstanceWhenDefaultActionInstanceEmpty(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
var actualURL string
|
||||
sarm := &stepActionRemoteMocks{}
|
||||
|
||||
origStepAtionRemoteNewCloneExecutor := stepActionRemoteNewCloneExecutor
|
||||
stepActionRemoteNewCloneExecutor = func(input git.NewGitCloneExecutorInput) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
actualURL = input.URL
|
||||
return nil
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
stepActionRemoteNewCloneExecutor = origStepAtionRemoteNewCloneExecutor
|
||||
}()
|
||||
|
||||
sar := &stepActionRemote{
|
||||
Step: &model.Step{
|
||||
Uses: "actions/setup-go@v4",
|
||||
},
|
||||
RunContext: &RunContext{
|
||||
Config: &Config{
|
||||
GitHubInstance: "gitea.example",
|
||||
DefaultActionInstance: "",
|
||||
ActionCacheDir: t.TempDir(),
|
||||
},
|
||||
Run: &model.Run{
|
||||
JobID: "1",
|
||||
Workflow: &model.Workflow{
|
||||
Jobs: map[string]*model.Job{
|
||||
"1": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
readAction: sarm.readAction,
|
||||
}
|
||||
|
||||
suffixMatcher := func(suffix string) any {
|
||||
return mock.MatchedBy(func(actionDir string) bool {
|
||||
return strings.HasSuffix(actionDir, suffix)
|
||||
})
|
||||
}
|
||||
sarm.On("readAction", sar.Step, suffixMatcher(sar.Step.UsesHash()), "", mock.Anything, mock.Anything).Return(&model.Action{}, nil)
|
||||
|
||||
require.NoError(t, sar.prepareActionExecutor()(ctx))
|
||||
assert.Equal(t, "https://gitea.example/actions/setup-go", actualURL)
|
||||
sarm.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestStepActionRemotePost(t *testing.T) {
|
||||
table := []struct {
|
||||
name string
|
||||
|
||||
Reference in New Issue
Block a user