mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 23:20:24 +00:00
EventManager: disable commands by default
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
d0f348a46a
commit
f07c9a7e01
4 changed files with 47 additions and 17 deletions
|
@ -3928,6 +3928,11 @@ func TestEventRule(t *testing.T) {
|
||||||
err = os.WriteFile(uploadScriptPath, getUploadScriptContent(movedPath, "", 0), 0755)
|
err = os.WriteFile(uploadScriptPath, getUploadScriptContent(movedPath, "", 0), 0755)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
dataprovider.EnabledActionCommands = []string{uploadScriptPath}
|
||||||
|
defer func() {
|
||||||
|
dataprovider.EnabledActionCommands = nil
|
||||||
|
}()
|
||||||
|
|
||||||
action1.Type = dataprovider.ActionTypeCommand
|
action1.Type = dataprovider.ActionTypeCommand
|
||||||
action1.Options = dataprovider.BaseEventActionOptions{
|
action1.Options = dataprovider.BaseEventActionOptions{
|
||||||
CmdConfig: dataprovider.EventActionCommandConfig{
|
CmdConfig: dataprovider.EventActionCommandConfig{
|
||||||
|
@ -4265,6 +4270,10 @@ func TestEventRuleDisabledCommand(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
_, _, err = httpdtest.AddEventAction(a1, http.StatusBadRequest)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// Enable the command to allow saving
|
||||||
|
dataprovider.EnabledActionCommands = []string{a1.Options.CmdConfig.Cmd}
|
||||||
action1, _, err := httpdtest.AddEventAction(a1, http.StatusCreated)
|
action1, _, err := httpdtest.AddEventAction(a1, http.StatusCreated)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
action2, _, err := httpdtest.AddEventAction(a2, http.StatusCreated)
|
action2, _, err := httpdtest.AddEventAction(a2, http.StatusCreated)
|
||||||
|
@ -4312,8 +4321,8 @@ func TestEventRuleDisabledCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
rule, _, err := httpdtest.AddEventRule(r, http.StatusCreated)
|
rule, _, err := httpdtest.AddEventRule(r, http.StatusCreated)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// restrit command execution
|
// restrict command execution
|
||||||
dataprovider.EnabledActionCommands = []string{"/bin/ls"}
|
dataprovider.EnabledActionCommands = nil
|
||||||
|
|
||||||
lastReceivedEmail.reset()
|
lastReceivedEmail.reset()
|
||||||
// create a folder to trigger the rule
|
// create a folder to trigger the rule
|
||||||
|
@ -4335,8 +4344,6 @@ func TestEventRuleDisabledCommand(t *testing.T) {
|
||||||
assert.Contains(t, email.Data, fmt.Sprintf("Object name: %s object type: folder", folder.Name))
|
assert.Contains(t, email.Data, fmt.Sprintf("Object name: %s object type: folder", folder.Name))
|
||||||
lastReceivedEmail.reset()
|
lastReceivedEmail.reset()
|
||||||
|
|
||||||
dataprovider.EnabledActionCommands = nil
|
|
||||||
|
|
||||||
_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
|
_, err = httpdtest.RemoveFolder(folder, http.StatusOK)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
@ -4368,6 +4375,11 @@ func TestEventRuleProviderEvents(t *testing.T) {
|
||||||
err = os.WriteFile(saveObjectScriptPath, getSaveProviderObjectScriptContent(outPath, 0), 0755)
|
err = os.WriteFile(saveObjectScriptPath, getSaveProviderObjectScriptContent(outPath, 0), 0755)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
dataprovider.EnabledActionCommands = []string{saveObjectScriptPath}
|
||||||
|
defer func() {
|
||||||
|
dataprovider.EnabledActionCommands = nil
|
||||||
|
}()
|
||||||
|
|
||||||
a1 := dataprovider.BaseEventAction{
|
a1 := dataprovider.BaseEventAction{
|
||||||
Name: "a1",
|
Name: "a1",
|
||||||
Type: dataprovider.ActionTypeCommand,
|
Type: dataprovider.ActionTypeCommand,
|
||||||
|
@ -5200,6 +5212,11 @@ func TestEventActionCommandEnvVars(t *testing.T) {
|
||||||
envName := "MY_ENV"
|
envName := "MY_ENV"
|
||||||
uploadScriptPath := filepath.Join(os.TempDir(), "upload.sh")
|
uploadScriptPath := filepath.Join(os.TempDir(), "upload.sh")
|
||||||
|
|
||||||
|
dataprovider.EnabledActionCommands = []string{uploadScriptPath}
|
||||||
|
defer func() {
|
||||||
|
dataprovider.EnabledActionCommands = nil
|
||||||
|
}()
|
||||||
|
|
||||||
err := os.WriteFile(uploadScriptPath, getUploadScriptEnvContent(envName), 0755)
|
err := os.WriteFile(uploadScriptPath, getUploadScriptEnvContent(envName), 0755)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
a1 := dataprovider.BaseEventAction{
|
a1 := dataprovider.BaseEventAction{
|
||||||
|
|
|
@ -59,7 +59,7 @@ var (
|
||||||
ActionTypeDataRetentionCheck, ActionTypePasswordExpirationCheck, ActionTypeUserExpirationCheck,
|
ActionTypeDataRetentionCheck, ActionTypePasswordExpirationCheck, ActionTypeUserExpirationCheck,
|
||||||
ActionTypeUserInactivityCheck, ActionTypeIDPAccountCheck, ActionTypeRotateLogs}
|
ActionTypeUserInactivityCheck, ActionTypeIDPAccountCheck, ActionTypeRotateLogs}
|
||||||
// EnabledActionCommands defines the system commands that can be executed via EventManager,
|
// EnabledActionCommands defines the system commands that can be executed via EventManager,
|
||||||
// an empty list means that any command is allowed to be executed.
|
// an empty list means that no command is allowed to be executed.
|
||||||
EnabledActionCommands []string
|
EnabledActionCommands []string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -455,9 +455,6 @@ func (c *EventActionHTTPConfig) GetHTTPClient() *http.Client {
|
||||||
|
|
||||||
// IsActionCommandAllowed returns true if the specified command is allowed
|
// IsActionCommandAllowed returns true if the specified command is allowed
|
||||||
func IsActionCommandAllowed(cmd string) bool {
|
func IsActionCommandAllowed(cmd string) bool {
|
||||||
if len(EnabledActionCommands) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return slices.Contains(EnabledActionCommands, cmd)
|
return slices.Contains(EnabledActionCommands, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1828,6 +1828,10 @@ func TestBasicActionRulesHandling(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
dataprovider.EnabledActionCommands = []string{a.Options.CmdConfig.Cmd}
|
||||||
|
defer func() {
|
||||||
|
dataprovider.EnabledActionCommands = nil
|
||||||
|
}()
|
||||||
_, _, err = httpdtest.UpdateEventAction(a, http.StatusOK)
|
_, _, err = httpdtest.UpdateEventAction(a, http.StatusOK)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// invalid type
|
// invalid type
|
||||||
|
@ -2362,13 +2366,24 @@ func TestEventActionValidation(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, string(resp), "command is required")
|
assert.Contains(t, string(resp), "command is required")
|
||||||
action.Options.CmdConfig.Cmd = "relative"
|
action.Options.CmdConfig.Cmd = "relative"
|
||||||
|
dataprovider.EnabledActionCommands = []string{action.Options.CmdConfig.Cmd}
|
||||||
|
defer func() {
|
||||||
|
dataprovider.EnabledActionCommands = nil
|
||||||
|
}()
|
||||||
|
|
||||||
_, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
|
_, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, string(resp), "invalid command, it must be an absolute path")
|
assert.Contains(t, string(resp), "invalid command, it must be an absolute path")
|
||||||
action.Options.CmdConfig.Cmd = filepath.Join(os.TempDir(), "cmd")
|
action.Options.CmdConfig.Cmd = filepath.Join(os.TempDir(), "cmd")
|
||||||
_, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
|
_, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, string(resp), "is not allowed")
|
||||||
|
|
||||||
|
dataprovider.EnabledActionCommands = []string{action.Options.CmdConfig.Cmd}
|
||||||
|
_, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
|
||||||
|
assert.NoError(t, err)
|
||||||
assert.Contains(t, string(resp), "invalid command action timeout")
|
assert.Contains(t, string(resp), "invalid command action timeout")
|
||||||
|
|
||||||
action.Options.CmdConfig.Timeout = 30
|
action.Options.CmdConfig.Timeout = 30
|
||||||
action.Options.CmdConfig.EnvVars = []dataprovider.KeyValue{
|
action.Options.CmdConfig.EnvVars = []dataprovider.KeyValue{
|
||||||
{
|
{
|
||||||
|
@ -23318,6 +23333,10 @@ func TestWebEventAction(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
dataprovider.EnabledActionCommands = []string{action.Options.CmdConfig.Cmd}
|
||||||
|
defer func() {
|
||||||
|
dataprovider.EnabledActionCommands = nil
|
||||||
|
}()
|
||||||
form.Set("type", fmt.Sprintf("%d", action.Type))
|
form.Set("type", fmt.Sprintf("%d", action.Type))
|
||||||
req, err = http.NewRequest(http.MethodPost, path.Join(webAdminEventActionPath, action.Name),
|
req, err = http.NewRequest(http.MethodPost, path.Join(webAdminEventActionPath, action.Name),
|
||||||
bytes.NewBuffer([]byte(form.Encode())))
|
bytes.NewBuffer([]byte(form.Encode())))
|
||||||
|
|
|
@ -44,6 +44,11 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<select id="idType" name="type" class="form-select" data-control="i18n-select2" data-hide-search="true">
|
<select id="idType" name="type" class="form-select" data-control="i18n-select2" data-hide-search="true">
|
||||||
{{- range .ActionTypes}}
|
{{- range .ActionTypes}}
|
||||||
|
{{- if eq .Value 2}}
|
||||||
|
{{- if not $.EnabledCommands}}
|
||||||
|
{{- continue}}
|
||||||
|
{{- end}}
|
||||||
|
{{- end}}
|
||||||
<option value="{{.Value}}" {{if eq $.Action.Type .Value }}selected{{end}} data-i18n="{{.Name}}"></option>
|
<option value="{{.Value}}" {{if eq $.Action.Type .Value }}selected{{end}} data-i18n="{{.Name}}"></option>
|
||||||
{{- end}}
|
{{- end}}
|
||||||
</select>
|
</select>
|
||||||
|
@ -400,21 +405,13 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
||||||
<div class="form-group row action-type action-cmd mt-10">
|
<div class="form-group row action-type action-cmd mt-10">
|
||||||
<label for="idCmdPath" data-i18n="actions.types.command" class="col-md-3 col-form-label">Command</label>
|
<label for="idCmdPath" data-i18n="actions.types.command" class="col-md-3 col-form-label">Command</label>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<select id="idCmdPath" name="cmd_path" class="form-select" data-control="i18n-select2" data-hide-search="true">
|
<select id="idCmdPath" name="cmd_path" class="form-select" data-control="i18n-select2" data-hide-search="false">
|
||||||
{{- range .EnabledCommands}}
|
{{- range .EnabledCommands}}
|
||||||
<option value="{{.}}" {{if eq $.Action.Options.CmdConfig.Cmd . }}selected{{end}}>{{.}}</option>
|
<option value="{{.}}" {{if eq $.Action.Options.CmdConfig.Cmd . }}selected{{end}}>{{.}}</option>
|
||||||
{{- end}}
|
{{- end}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{- else}}
|
|
||||||
<div class="form-group row action-type action-cmd mt-10">
|
|
||||||
<label for="idCmdPath" data-i18n="actions.types.command" class="col-md-3 col-form-label">Command</label>
|
|
||||||
<div class="col-md-9">
|
|
||||||
<input id="idCmdPath" type="text" class="form-control" name="cmd_path" value="{{.Action.Options.CmdConfig.Cmd}}" aria-describedby="idCmdPathHelp" />
|
|
||||||
<div id="idCmdPathHelp" class="form-text" data-i18n="actions.command_help"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
|
||||||
<div class="form-group row action-type action-cmd mt-10">
|
<div class="form-group row action-type action-cmd mt-10">
|
||||||
|
|
Loading…
Reference in a new issue