EventManager: disable commands by default

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2024-11-10 11:52:31 +01:00
parent 3dd412f6e3
commit b524da11e9
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
4 changed files with 47 additions and 17 deletions

View file

@ -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,
@ -5231,6 +5243,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{

View file

@ -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)
} }

View file

@ -1840,6 +1840,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
@ -2374,13 +2378,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{
{ {
@ -24027,6 +24042,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())))

View file

@ -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">