mirror of
https://github.com/OliveTin/OliveTin
synced 2025-10-30 04:47:03 +00:00
Merge branches 'release/2k' and 'release/2k' of github.com:OliveTin/OliveTin into release/2k
This commit is contained in:
@@ -11,6 +11,7 @@ type Action struct {
|
||||
Title string
|
||||
Icon string
|
||||
Shell string
|
||||
Exec []string
|
||||
ShellAfterCompleted string
|
||||
Timeout int
|
||||
Acls []string
|
||||
|
||||
@@ -294,3 +294,68 @@ func mangleInvalidDatetimeValues(req *ExecutionRequest, arg *config.ActionArgume
|
||||
req.Arguments[arg.Name] = timestamp.Format("2006-01-02T15:04:05")
|
||||
}
|
||||
}
|
||||
|
||||
func parseActionExec(values map[string]string, action *config.Action, entityPrefix string) ([]string, error) {
|
||||
for _, arg := range action.Arguments {
|
||||
argName := arg.Name
|
||||
argValue := values[argName]
|
||||
|
||||
err := typecheckActionArgument(&arg, argValue, action)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"name": argName,
|
||||
"value": argValue,
|
||||
}).Debugf("Arg assigned")
|
||||
}
|
||||
|
||||
parsedArgs := make([]string, len(action.Exec))
|
||||
for i, arg := range action.Exec {
|
||||
parsedArg, err := parseCommandForReplacements(arg, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parsedArg = sv.ReplaceEntityVars(entityPrefix, parsedArg)
|
||||
parsedArgs[i] = parsedArg
|
||||
}
|
||||
|
||||
redactedArgs := redactExecArgs(parsedArgs, action.Arguments, values)
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"actionTitle": action.Title,
|
||||
"cmd": redactedArgs,
|
||||
}).Infof("Action parse args - After (Exec)")
|
||||
|
||||
return parsedArgs, nil
|
||||
}
|
||||
|
||||
//gocyclo:ignore
|
||||
func redactExecArgs(execArgs []string, arguments []config.ActionArgument, argumentValues map[string]string) []string {
|
||||
redacted := make([]string, len(execArgs))
|
||||
for i, arg := range execArgs {
|
||||
redacted[i] = redactShellCommand(arg, arguments, argumentValues)
|
||||
}
|
||||
return redacted
|
||||
}
|
||||
|
||||
func checkShellArgumentSafety(action *config.Action) error {
|
||||
if action.Shell == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
unsafeTypes := []string{"url", "email", "raw_string_multiline", "very_dangerous_raw_string"}
|
||||
|
||||
for _, arg := range action.Arguments {
|
||||
for _, unsafeType := range unsafeTypes {
|
||||
if arg.Type == unsafeType {
|
||||
return fmt.Errorf("unsafe argument type '%s' cannot be used with Shell execution. Use 'exec' instead. See https://docs.olivetin.app/action_execution/shellvsexec.html", arg.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -67,6 +68,8 @@ type ExecutionRequest struct {
|
||||
|
||||
logEntry *InternalLogEntry
|
||||
finalParsedCommand string
|
||||
execArgs []string
|
||||
useDirectExec bool
|
||||
executor *Executor
|
||||
}
|
||||
|
||||
@@ -408,7 +411,21 @@ func stepParseArgs(req *ExecutionRequest) bool {
|
||||
|
||||
mangleInvalidArgumentValues(req)
|
||||
|
||||
req.finalParsedCommand, err = parseActionArguments(req.Arguments, req.Action, req.EntityPrefix)
|
||||
if len(req.Action.Exec) > 0 {
|
||||
req.useDirectExec = true
|
||||
req.execArgs, err = parseActionExec(req.Arguments, req.Action, req.EntityPrefix)
|
||||
} else {
|
||||
req.useDirectExec = false
|
||||
|
||||
err = checkShellArgumentSafety(req.Action)
|
||||
if err != nil {
|
||||
req.logEntry.Output = err.Error()
|
||||
log.Warn(err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
req.finalParsedCommand, err = parseActionArguments(req.Arguments, req.Action, req.EntityPrefix)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
req.logEntry.Output = err.Error()
|
||||
@@ -548,7 +565,13 @@ func stepExec(req *ExecutionRequest) bool {
|
||||
|
||||
streamer := &OutputStreamer{Req: req}
|
||||
|
||||
cmd := wrapCommandInShell(ctx, req.finalParsedCommand)
|
||||
var cmd *exec.Cmd
|
||||
if req.useDirectExec {
|
||||
cmd = wrapCommandDirect(ctx, req.execArgs)
|
||||
} else {
|
||||
cmd = wrapCommandInShell(ctx, req.finalParsedCommand)
|
||||
}
|
||||
|
||||
cmd.Stdout = streamer
|
||||
cmd.Stderr = streamer
|
||||
cmd.Env = buildEnv(req.Arguments)
|
||||
|
||||
@@ -21,5 +21,17 @@ func wrapCommandInShell(ctx context.Context, finalParsedCommand string) *exec.Cm
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
|
||||
return cmd
|
||||
|
||||
}
|
||||
|
||||
func wrapCommandDirect(ctx context.Context, execArgs []string) *exec.Cmd {
|
||||
if len(execArgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, execArgs[0], execArgs[1:]...)
|
||||
|
||||
// This is to ensure that the process group is killed when the parent process is killed.
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -22,3 +22,11 @@ func wrapCommandInShell(ctx context.Context, finalParsedCommand string) *exec.Cm
|
||||
return exec.CommandContext(ctx, "cmd", "/u", "/C", finalParsedCommand)
|
||||
}
|
||||
}
|
||||
|
||||
func wrapCommandDirect(ctx context.Context, execArgs []string) *exec.Cmd {
|
||||
if len(execArgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return exec.CommandContext(ctx, execArgs[0], execArgs[1:]...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user