CVE-2024-52309
Description
SFTPGo is a full-featured and highly configurable SFTP, HTTP/S, FTP/S and WebDAV server - S3, Google Cloud Storage, Azure Blob. One powerful feature of SFTPGo is the ability to have the EventManager execute scripts or run applications in response to certain events. This feature is very common in all software similar to SFTPGo and is generally unrestricted. However, any SFTPGo administrator with permission to run a script has access to the underlying OS/container with the same permissions as the user running SFTPGo. This is unexpected for some SFTPGo administrators who think that there is a clear distinction between accessing the system shell and accessing the SFTPGo WebAdmin UI. To avoid this confusion, running system commands is disabled by default in 2.6.3, and an allow list has been added so that system administrators configuring SFTPGo must explicitly define which commands are allowed to be configured from the WebAdmin UI.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/drakkan/sftpgo/v2Go | >= 2.4.0, < 2.6.3 | 2.6.3 |
sftpgoGo | >= 2.4.0, < 2.6.3 | 2.6.3 |
Affected products
1Patches
2b524da11e946EventManager: disable commands by default
4 files changed · +47 −17
internal/common/protocol_test.go+21 −4 modified@@ -3928,6 +3928,11 @@ func TestEventRule(t *testing.T) { err = os.WriteFile(uploadScriptPath, getUploadScriptContent(movedPath, "", 0), 0755) assert.NoError(t, err) + dataprovider.EnabledActionCommands = []string{uploadScriptPath} + defer func() { + dataprovider.EnabledActionCommands = nil + }() + action1.Type = dataprovider.ActionTypeCommand action1.Options = dataprovider.BaseEventActionOptions{ 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) assert.NoError(t, err) action2, _, err := httpdtest.AddEventAction(a2, http.StatusCreated) @@ -4312,8 +4321,8 @@ func TestEventRuleDisabledCommand(t *testing.T) { } rule, _, err := httpdtest.AddEventRule(r, http.StatusCreated) assert.NoError(t, err) - // restrit command execution - dataprovider.EnabledActionCommands = []string{"/bin/ls"} + // restrict command execution + dataprovider.EnabledActionCommands = nil lastReceivedEmail.reset() // 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)) lastReceivedEmail.reset() - dataprovider.EnabledActionCommands = nil - _, err = httpdtest.RemoveFolder(folder, http.StatusOK) assert.NoError(t, err) @@ -4368,6 +4375,11 @@ func TestEventRuleProviderEvents(t *testing.T) { err = os.WriteFile(saveObjectScriptPath, getSaveProviderObjectScriptContent(outPath, 0), 0755) assert.NoError(t, err) + dataprovider.EnabledActionCommands = []string{saveObjectScriptPath} + defer func() { + dataprovider.EnabledActionCommands = nil + }() + a1 := dataprovider.BaseEventAction{ Name: "a1", Type: dataprovider.ActionTypeCommand, @@ -5231,6 +5243,11 @@ func TestEventActionCommandEnvVars(t *testing.T) { envName := "MY_ENV" uploadScriptPath := filepath.Join(os.TempDir(), "upload.sh") + dataprovider.EnabledActionCommands = []string{uploadScriptPath} + defer func() { + dataprovider.EnabledActionCommands = nil + }() + err := os.WriteFile(uploadScriptPath, getUploadScriptEnvContent(envName), 0755) assert.NoError(t, err) a1 := dataprovider.BaseEventAction{
internal/dataprovider/eventrule.go+1 −4 modified@@ -59,7 +59,7 @@ var ( ActionTypeDataRetentionCheck, ActionTypePasswordExpirationCheck, ActionTypeUserExpirationCheck, ActionTypeUserInactivityCheck, ActionTypeIDPAccountCheck, ActionTypeRotateLogs} // 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 ) @@ -455,9 +455,6 @@ func (c *EventActionHTTPConfig) GetHTTPClient() *http.Client { // IsActionCommandAllowed returns true if the specified command is allowed func IsActionCommandAllowed(cmd string) bool { - if len(EnabledActionCommands) == 0 { - return true - } return slices.Contains(EnabledActionCommands, cmd) }
internal/httpd/httpd_test.go+19 −0 modified@@ -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) assert.NoError(t, err) // invalid type @@ -2374,13 +2378,24 @@ func TestEventActionValidation(t *testing.T) { assert.NoError(t, err) assert.Contains(t, string(resp), "command is required") action.Options.CmdConfig.Cmd = "relative" + dataprovider.EnabledActionCommands = []string{action.Options.CmdConfig.Cmd} + defer func() { + dataprovider.EnabledActionCommands = nil + }() + _, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest) assert.NoError(t, err) assert.Contains(t, string(resp), "invalid command, it must be an absolute path") action.Options.CmdConfig.Cmd = filepath.Join(os.TempDir(), "cmd") _, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest) 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") + action.Options.CmdConfig.Timeout = 30 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)) req, err = http.NewRequest(http.MethodPost, path.Join(webAdminEventActionPath, action.Name), bytes.NewBuffer([]byte(form.Encode())))
templates/webadmin/eventaction.html+6 −9 modified@@ -44,6 +44,11 @@ <h3 data-i18n="{{.Title}}" class="card-title section-title"></h3> <div class="col-md-9"> <select id="idType" name="type" class="form-select" data-control="i18n-select2" data-hide-search="true"> {{- 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> {{- end}} </select> @@ -400,21 +405,13 @@ <h3 data-i18n="actions.multipart_body" class="card-title section-title-inner">Mu <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"> - <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}} <option value="{{.}}" {{if eq $.Action.Options.CmdConfig.Cmd . }}selected{{end}}>{{.}}</option> {{- end}} </select> </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}} <div class="form-group row action-type action-cmd mt-10">
88b1850b5806EventManager: allow to define the allowed system commands
10 files changed · +259 −17
internal/common/common.go+24 −1 modified@@ -239,6 +239,9 @@ func Initialize(c Configuration, isShared int) error { if err := c.initializeProxyProtocol(); err != nil { return err } + if err := c.EventManager.validate(); err != nil { + return err + } vfs.SetTempPath(c.TempPath) dataprovider.SetTempPath(c.TempPath) vfs.SetAllowSelfConnections(c.AllowSelfConnections) @@ -247,6 +250,7 @@ func Initialize(c Configuration, isShared int) error { vfs.SetResumeMaxSize(c.ResumeMaxSize) vfs.SetUploadMode(c.UploadMode) dataprovider.SetAllowSelfConnections(c.AllowSelfConnections) + dataprovider.EnabledActionCommands = c.EventManager.EnabledCommands transfersChecker = getTransfersChecker(isShared) return nil } @@ -512,6 +516,23 @@ type ConnectionTransfer struct { DLSize int64 `json:"-"` } +// EventManagerConfig defines the configuration for the EventManager +type EventManagerConfig struct { + // EnabledCommands defines the system commands that can be executed via EventManager, + // an empty list means that any command is allowed to be executed. + // Commands must be set as an absolute path + EnabledCommands []string `json:"enabled_commands" mapstructure:"enabled_commands"` +} + +func (c *EventManagerConfig) validate() error { + for _, c := range c.EnabledCommands { + if !filepath.IsAbs(c) { + return fmt.Errorf("invalid command %q: it must be an absolute path", c) + } + } + return nil +} + // MetadataConfig defines how to handle metadata for cloud storage backends type MetadataConfig struct { // If not zero the metadata will be read before downloads and will be @@ -621,7 +642,9 @@ type Configuration struct { // server's local time, otherwise UTC will be used. TZ string `json:"tz" mapstructure:"tz"` // Metadata configuration - Metadata MetadataConfig `json:"metadata" mapstructure:"metadata"` + Metadata MetadataConfig `json:"metadata" mapstructure:"metadata"` + // EventManager configuration + EventManager EventManagerConfig `json:"event_manager" mapstructure:"event_manager"` idleTimeoutAsDuration time.Duration idleLoginTimeout time.Duration defender Defender
internal/common/common_test.go+27 −0 modified@@ -217,6 +217,33 @@ func TestConnections(t *testing.T) { Connections.RUnlock() } +func TestEventManagerCommandsInitialization(t *testing.T) { + configCopy := Config + + c := Configuration{ + EventManager: EventManagerConfig{ + EnabledCommands: []string{"ls"}, // not an absolute path + }, + } + err := Initialize(c, 0) + assert.ErrorContains(t, err, "invalid command") + + var commands []string + if runtime.GOOS == osWindows { + commands = []string{"C:\\command"} + } else { + commands = []string{"/bin/ls"} + } + + c.EventManager.EnabledCommands = commands + err = Initialize(c, 0) + assert.NoError(t, err) + assert.Equal(t, commands, dataprovider.EnabledActionCommands) + + dataprovider.EnabledActionCommands = configCopy.EventManager.EnabledCommands + Config = configCopy +} + func TestInitializationProxyErrors(t *testing.T) { configCopy := Config
internal/common/eventmanager.go+3 −0 modified@@ -1484,6 +1484,9 @@ func executeHTTPRuleAction(c dataprovider.EventActionHTTPConfig, params *EventPa } func executeCommandRuleAction(c dataprovider.EventActionCommandConfig, params *EventParams) error { + if !dataprovider.IsActionCommandAllowed(c.Cmd) { + return fmt.Errorf("command %q is not allowed", c.Cmd) + } addObjectData := false if params.Object != nil { for _, k := range c.EnvVars {
internal/common/protocol_test.go+142 −0 modified@@ -4208,6 +4208,148 @@ func TestEventRuleStatues(t *testing.T) { require.NoError(t, err) } +func TestEventRuleDisabledCommand(t *testing.T) { + if runtime.GOOS == osWindows { + t.Skip("this test is not available on Windows") + } + smtpCfg := smtp.Config{ + Host: "127.0.0.1", + Port: 2525, + From: "notification@example.com", + TemplatesPath: "templates", + } + err := smtpCfg.Initialize(configDir, true) + require.NoError(t, err) + + saveObjectScriptPath := filepath.Join(os.TempDir(), "provider.sh") + outPath := filepath.Join(os.TempDir(), "provider_out.json") + err = os.WriteFile(saveObjectScriptPath, getSaveProviderObjectScriptContent(outPath, 0), 0755) + assert.NoError(t, err) + + a1 := dataprovider.BaseEventAction{ + Name: "a1", + Type: dataprovider.ActionTypeCommand, + Options: dataprovider.BaseEventActionOptions{ + CmdConfig: dataprovider.EventActionCommandConfig{ + Cmd: saveObjectScriptPath, + Timeout: 10, + EnvVars: []dataprovider.KeyValue{ + { + Key: "SFTPGO_OBJECT_DATA", + Value: "{{ObjectData}}", + }, + }, + }, + }, + } + a2 := dataprovider.BaseEventAction{ + Name: "a2", + Type: dataprovider.ActionTypeEmail, + Options: dataprovider.BaseEventActionOptions{ + EmailConfig: dataprovider.EventActionEmailConfig{ + Recipients: []string{"test3@example.com"}, + Subject: `New "{{Event}}" from "{{Name}}"`, + Body: "Object name: {{ObjectName}} object type: {{ObjectType}} Data: {{ObjectData}}", + }, + }, + } + + a3 := dataprovider.BaseEventAction{ + Name: "a3", + Type: dataprovider.ActionTypeEmail, + Options: dataprovider.BaseEventActionOptions{ + EmailConfig: dataprovider.EventActionEmailConfig{ + Recipients: []string{"failure@example.com"}, + Subject: `Failed "{{Event}}" from "{{Name}}"`, + Body: "Object name: {{ObjectName}} object type: {{ObjectType}}, IP: {{IP}}", + }, + }, + } + action1, _, err := httpdtest.AddEventAction(a1, http.StatusCreated) + assert.NoError(t, err) + action2, _, err := httpdtest.AddEventAction(a2, http.StatusCreated) + assert.NoError(t, err) + action3, _, err := httpdtest.AddEventAction(a3, http.StatusCreated) + assert.NoError(t, err) + + r := dataprovider.EventRule{ + Name: "rule", + Status: 1, + Trigger: dataprovider.EventTriggerProviderEvent, + Conditions: dataprovider.EventConditions{ + ProviderEvents: []string{"add"}, + Options: dataprovider.ConditionOptions{ + ProviderObjects: []string{"folder"}, + }, + }, + Actions: []dataprovider.EventAction{ + { + BaseEventAction: dataprovider.BaseEventAction{ + Name: action1.Name, + }, + Order: 1, + Options: dataprovider.EventActionOptions{ + StopOnFailure: true, + }, + }, + { + BaseEventAction: dataprovider.BaseEventAction{ + Name: action2.Name, + }, + Order: 2, + }, + { + BaseEventAction: dataprovider.BaseEventAction{ + Name: action3.Name, + }, + Order: 3, + Options: dataprovider.EventActionOptions{ + IsFailureAction: true, + StopOnFailure: true, + }, + }, + }, + } + rule, _, err := httpdtest.AddEventRule(r, http.StatusCreated) + assert.NoError(t, err) + // restrit command execution + dataprovider.EnabledActionCommands = []string{"/bin/ls"} + + lastReceivedEmail.reset() + // create a folder to trigger the rule + folder := vfs.BaseVirtualFolder{ + Name: "ftest failed command", + MappedPath: filepath.Join(os.TempDir(), "p"), + } + folder, _, err = httpdtest.AddFolder(folder, http.StatusCreated) + assert.NoError(t, err) + + assert.NoFileExists(t, outPath) + assert.Eventually(t, func() bool { + return lastReceivedEmail.get().From != "" + }, 3000*time.Millisecond, 100*time.Millisecond) + email := lastReceivedEmail.get() + assert.Len(t, email.To, 1) + assert.True(t, slices.Contains(email.To, "failure@example.com")) + assert.Contains(t, email.Data, `Subject: Failed "add" from "admin"`) + assert.Contains(t, email.Data, fmt.Sprintf("Object name: %s object type: folder", folder.Name)) + lastReceivedEmail.reset() + + dataprovider.EnabledActionCommands = nil + + _, err = httpdtest.RemoveFolder(folder, http.StatusOK) + assert.NoError(t, err) + + _, err = httpdtest.RemoveEventRule(rule, http.StatusOK) + assert.NoError(t, err) + _, err = httpdtest.RemoveEventAction(action1, http.StatusOK) + assert.NoError(t, err) + _, err = httpdtest.RemoveEventAction(action2, http.StatusOK) + assert.NoError(t, err) + _, err = httpdtest.RemoveEventAction(action3, http.StatusOK) + assert.NoError(t, err) +} + func TestEventRuleProviderEvents(t *testing.T) { if runtime.GOOS == osWindows { t.Skip("this test is not available on Windows")
internal/config/config.go+4 −0 modified@@ -242,6 +242,9 @@ func Init() { Metadata: common.MetadataConfig{ Read: 0, }, + EventManager: common.EventManagerConfig{ + EnabledCommands: []string{}, + }, }, ACME: acme.Configuration{ Email: "", @@ -2032,6 +2035,7 @@ func setViperDefaults() { viper.SetDefault("common.server_version", globalConf.Common.ServerVersion) viper.SetDefault("common.tz", globalConf.Common.TZ) viper.SetDefault("common.metadata.read", globalConf.Common.Metadata.Read) + viper.SetDefault("common.event_manager.enabled_commands", globalConf.Common.EventManager.EnabledCommands) viper.SetDefault("acme.email", globalConf.ACME.Email) viper.SetDefault("acme.key_type", globalConf.ACME.KeyType) viper.SetDefault("acme.certs_path", globalConf.ACME.CertsPath)
internal/dataprovider/eventrule.go+14 −0 modified@@ -58,6 +58,9 @@ var ( ActionTypeBackup, ActionTypeUserQuotaReset, ActionTypeFolderQuotaReset, ActionTypeTransferQuotaReset, ActionTypeDataRetentionCheck, ActionTypePasswordExpirationCheck, ActionTypeUserExpirationCheck, ActionTypeUserInactivityCheck, ActionTypeIDPAccountCheck, ActionTypeRotateLogs} + // EnabledActionCommands defines the system commands that can be executed via EventManager, + // an empty list means that any command is allowed to be executed. + EnabledActionCommands []string ) func isActionTypeValid(action int) bool { @@ -450,6 +453,14 @@ func (c *EventActionHTTPConfig) GetHTTPClient() *http.Client { return client } +// IsActionCommandAllowed returns true if the specified command is allowed +func IsActionCommandAllowed(cmd string) bool { + if len(EnabledActionCommands) == 0 { + return true + } + return slices.Contains(EnabledActionCommands, cmd) +} + // EventActionCommandConfig defines the configuration for a command event target type EventActionCommandConfig struct { Cmd string `json:"cmd,omitempty"` @@ -462,6 +473,9 @@ func (c *EventActionCommandConfig) validate() error { if c.Cmd == "" { return util.NewI18nError(util.NewValidationError("command is required"), util.I18nErrorCommandRequired) } + if !IsActionCommandAllowed(c.Cmd) { + return util.NewValidationError(fmt.Sprintf("command %q is not allowed", c.Cmd)) + } if !filepath.IsAbs(c.Cmd) { return util.NewI18nError( util.NewValidationError("invalid command, it must be an absolute path"),
internal/httpd/httpd_test.go+11 −0 modified@@ -2395,6 +2395,17 @@ func TestEventActionValidation(t *testing.T) { _, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest) assert.NoError(t, err) assert.Contains(t, string(resp), "invalid command args") + action.Options.CmdConfig.Args = nil + // restrict commands + if runtime.GOOS == osWindows { + dataprovider.EnabledActionCommands = []string{"C:\\cmd.exe"} + } else { + dataprovider.EnabledActionCommands = []string{"/bin/sh"} + } + _, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest) + assert.NoError(t, err) + assert.Contains(t, string(resp), "is not allowed") + dataprovider.EnabledActionCommands = nil action.Type = dataprovider.ActionTypeEmail _, resp, err = httpdtest.AddEventAction(action, http.StatusBadRequest)
internal/httpd/webadmin.go+17 −15 modified@@ -297,13 +297,14 @@ type rolePage struct { type eventActionPage struct { basePage - Action dataprovider.BaseEventAction - ActionTypes []dataprovider.EnumMapping - FsActions []dataprovider.EnumMapping - HTTPMethods []string - RedactedSecret string - Error *util.I18nError - Mode genericPageMode + Action dataprovider.BaseEventAction + ActionTypes []dataprovider.EnumMapping + FsActions []dataprovider.EnumMapping + HTTPMethods []string + EnabledCommands []string + RedactedSecret string + Error *util.I18nError + Mode genericPageMode } type eventRulePage struct { @@ -1088,14 +1089,15 @@ func (s *httpdServer) renderEventActionPage(w http.ResponseWriter, r *http.Reque } data := eventActionPage{ - basePage: s.getBasePageData(title, currentURL, w, r), - Action: action, - ActionTypes: dataprovider.EventActionTypes, - FsActions: dataprovider.FsActionTypes, - HTTPMethods: dataprovider.SupportedHTTPActionMethods, - RedactedSecret: redactedSecret, - Error: getI18nError(err), - Mode: mode, + basePage: s.getBasePageData(title, currentURL, w, r), + Action: action, + ActionTypes: dataprovider.EventActionTypes, + FsActions: dataprovider.FsActionTypes, + HTTPMethods: dataprovider.SupportedHTTPActionMethods, + EnabledCommands: dataprovider.EnabledActionCommands, + RedactedSecret: redactedSecret, + Error: getI18nError(err), + Mode: mode, } renderAdminTemplate(w, templateEventAction, data) }
sftpgo.json+4 −1 modified@@ -62,7 +62,10 @@ "entries_soft_limit": 100, "entries_hard_limit": 150 } - ] + ], + "event_manager": { + "enabled_commands": [] + } }, "acme": { "domains": [],
templates/webadmin/eventaction.html+13 −0 modified@@ -396,13 +396,26 @@ <h3 data-i18n="actions.multipart_body" class="card-title section-title-inner">Mu </div> </div> + {{ if .EnabledCommands}} + <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"> + <select id="idCmdPath" name="cmd_path" class="form-select" data-control="i18n-select2" data-hide-search="true"> + {{- range .EnabledCommands}} + <option value="{{.}}" {{if eq $.Action.Options.CmdConfig.Cmd . }}selected{{end}}>{{.}}</option> + {{- end}} + </select> + </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}} <div class="form-group row action-type action-cmd mt-10"> <label for="idCommandArgs" data-i18n="actions.command_args" class="col-md-3 col-form-label">Arguments</label>
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-49cc-xrjf-9qf7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-52309ghsaADVISORY
- github.com/drakkan/sftpgo/commit/88b1850b5806eee81150873d4e565144b21021fbnvdWEB
- github.com/drakkan/sftpgo/commit/b524da11e9466d05fe03304713ee1c61bb276ec4nvdWEB
- github.com/drakkan/sftpgo/security/advisories/GHSA-49cc-xrjf-9qf7nvdWEB
- pkg.go.dev/vuln/GO-2024-3283ghsaWEB
News mentions
0No linked articles in our index yet.