diff --git a/service/internal/api/api_test.go b/service/internal/api/api_test.go index e00394c..502f79d 100644 --- a/service/internal/api/api_test.go +++ b/service/internal/api/api_test.go @@ -385,7 +385,7 @@ func testWithEntity(t *testing.T, binding *executor.ActionBinding, rr *Dashboard assert.Equal(t, expectedCanExec, actionResult.CanExec, message) } -// buildExecWithoutLogsTestConfig returns config for GHSA-jm28: user "runner" may exec but not read logs. +// buildExecWithoutLogsTestConfig returns config for GHSA-jm28-2wcr-qf3h: user "runner" may exec but not read logs. func buildExecWithoutLogsTestConfig(t *testing.T) (*config.Config, *authpublic.AuthenticatedUser) { t.Helper() cfg := config.DefaultConfig() diff --git a/service/internal/executor/arguments.go b/service/internal/executor/arguments.go index 8eeae9b..6810e6c 100644 --- a/service/internal/executor/arguments.go +++ b/service/internal/executor/arguments.go @@ -315,10 +315,6 @@ func typeSafetyCheckDatetime(value string) error { } func anchorCustomRegexPattern(pattern string) string { - if strings.HasPrefix(pattern, "^") && strings.HasSuffix(pattern, "$") { - return pattern - } - return "^(?:" + pattern + ")$" } diff --git a/service/internal/executor/arguments_test.go b/service/internal/executor/arguments_test.go index d820cba..d6caffe 100644 --- a/service/internal/executor/arguments_test.go +++ b/service/internal/executor/arguments_test.go @@ -596,6 +596,13 @@ func TestTypeSafetyCheckRegex(t *testing.T) { value: "example.com; id", hasError: true, }, + { + name: "reject alternation bypass when pattern looks anchored", + field: "host", + pattern: "regex:^safe$|bad", + value: "xxxbad", + hasError: true, + }, } for _, tt := range tests { diff --git a/service/internal/executor/executor.go b/service/internal/executor/executor.go index 35cc44d..24848a0 100644 --- a/service/internal/executor/executor.go +++ b/service/internal/executor/executor.go @@ -1238,8 +1238,22 @@ func stepExecAfter(req *ExecutionRequest) bool { return true } -func buildShellAfterCommand(ctx context.Context, req *ExecutionRequest, stdout, stderr *bytes.Buffer) (*exec.Cmd, map[string]string, error) { +func shellAfterCompletedAction(req *ExecutionRequest) (*config.Action, bool) { + if req == nil { + return nil, false + } + if !hasBindingAndAction(req) { + return nil, false + } if req.Binding.Action.ShellAfterCompleted == "" { + return nil, false + } + return req.Binding.Action, true +} + +func buildShellAfterCommand(ctx context.Context, req *ExecutionRequest, stdout, stderr *bytes.Buffer) (*exec.Cmd, map[string]string, error) { + action, ok := shellAfterCompletedAction(req) + if !ok { return nil, nil, nil } @@ -1248,14 +1262,14 @@ func buildShellAfterCommand(ctx context.Context, req *ExecutionRequest, stdout, return nil, nil, err } - finalParsedCommand, err := tpl.ParseTemplateWithActionContext(req.Binding.Action.ShellAfterCompleted, req.Binding.Entity, args) + finalParsedCommand, err := tpl.ParseTemplateWithActionContext(action.ShellAfterCompleted, req.Binding.Entity, args) if err != nil { msg := "Could not prepare shellAfterCompleted command: " + err.Error() + "\n" req.mutateLogEntry(func(entry *InternalLogEntry) { entry.Output += msg }) log.Warn(msg) - return nil, nil, nil + return nil, nil, err } cmd := wrapCommandInShell(ctx, finalParsedCommand)