Disallow HEAD/GET requests with body
parent
12f85cceb1
commit
b805d49cfd
|
@ -2,6 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,6 +23,15 @@ func (e errHTTP) JSON() string {
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrapErrHTTP(err *errHTTP, message string, args ...interface{}) *errHTTP {
|
||||||
|
return &errHTTP{
|
||||||
|
Code: err.Code,
|
||||||
|
HTTPCode: err.HTTPCode,
|
||||||
|
Message: fmt.Sprintf("%s, %s", err.Message, fmt.Sprintf(message, args...)),
|
||||||
|
Link: err.Link,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errHTTPBadRequestEmailDisabled = &errHTTP{40001, http.StatusBadRequest, "e-mail notifications are not enabled", "https://ntfy.sh/docs/config/#e-mail-notifications"}
|
errHTTPBadRequestEmailDisabled = &errHTTP{40001, http.StatusBadRequest, "e-mail notifications are not enabled", "https://ntfy.sh/docs/config/#e-mail-notifications"}
|
||||||
errHTTPBadRequestDelayNoCache = &errHTTP{40002, http.StatusBadRequest, "cannot disable cache for delayed message", ""}
|
errHTTPBadRequestDelayNoCache = &errHTTP{40002, http.StatusBadRequest, "cannot disable cache for delayed message", ""}
|
||||||
|
@ -39,7 +49,7 @@ var (
|
||||||
errHTTPBadRequestAttachmentsExpiryBeforeDelivery = &errHTTP{40015, http.StatusBadRequest, "invalid request: attachment expiry before delayed delivery date", "https://ntfy.sh/docs/publish/#scheduled-delivery"}
|
errHTTPBadRequestAttachmentsExpiryBeforeDelivery = &errHTTP{40015, http.StatusBadRequest, "invalid request: attachment expiry before delayed delivery date", "https://ntfy.sh/docs/publish/#scheduled-delivery"}
|
||||||
errHTTPBadRequestWebSocketsUpgradeHeaderMissing = &errHTTP{40016, http.StatusBadRequest, "invalid request: client not using the websocket protocol", "https://ntfy.sh/docs/subscribe/api/#websockets"}
|
errHTTPBadRequestWebSocketsUpgradeHeaderMissing = &errHTTP{40016, http.StatusBadRequest, "invalid request: client not using the websocket protocol", "https://ntfy.sh/docs/subscribe/api/#websockets"}
|
||||||
errHTTPBadRequestJSONInvalid = &errHTTP{40017, http.StatusBadRequest, "invalid request: request body must be message JSON", "https://ntfy.sh/docs/publish/#publish-as-json"}
|
errHTTPBadRequestJSONInvalid = &errHTTP{40017, http.StatusBadRequest, "invalid request: request body must be message JSON", "https://ntfy.sh/docs/publish/#publish-as-json"}
|
||||||
errHTTPBadRequestActionsInvalid = &errHTTP{40018, http.StatusBadRequest, "invalid request: actions not valid", "https://ntfy.sh/docs/publish/#action-buttons"}
|
errHTTPBadRequestActionsInvalid = &errHTTP{40018, http.StatusBadRequest, "invalid request: actions invalid", "https://ntfy.sh/docs/publish/#action-buttons"}
|
||||||
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", ""}
|
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", ""}
|
||||||
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication"}
|
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication"}
|
||||||
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication"}
|
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication"}
|
||||||
|
|
|
@ -539,7 +539,7 @@ func (s *Server) parsePublishParams(r *http.Request, v *visitor, m *message) (ca
|
||||||
if actionsStr != "" {
|
if actionsStr != "" {
|
||||||
m.Actions, err = parseActions(actionsStr)
|
m.Actions, err = parseActions(actionsStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, false, "", false, errHTTPBadRequestActionsInvalid
|
return false, false, "", false, err // wrapped errHTTPBadRequestActionsInvalid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unifiedpush = readBoolParam(r, false, "x-unifiedpush", "unifiedpush", "up") // see GET too!
|
unifiedpush = readBoolParam(r, false, "x-unifiedpush", "unifiedpush", "up") // see GET too!
|
||||||
|
|
|
@ -2,7 +2,6 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"heckel.io/ntfy/util"
|
"heckel.io/ntfy/util"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -61,22 +60,26 @@ func parseActions(s string) (actions []*action, err error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add ID field
|
// Add ID field, ensure correct uppercase/lowercase
|
||||||
for i := range actions {
|
for i := range actions {
|
||||||
actions[i].ID = util.RandomString(actionIDLength)
|
actions[i].ID = util.RandomString(actionIDLength)
|
||||||
|
actions[i].Action = strings.ToLower(actions[i].Action)
|
||||||
|
actions[i].Method = strings.ToUpper(actions[i].Method)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate
|
// Validate
|
||||||
if len(actions) > actionsMax {
|
if len(actions) > actionsMax {
|
||||||
return nil, fmt.Errorf("too many actions, only %d allowed", actionsMax)
|
return nil, wrapErrHTTP(errHTTPBadRequestActionsInvalid, "only %d actions allowed", actionsMax)
|
||||||
}
|
}
|
||||||
for _, action := range actions {
|
for _, action := range actions {
|
||||||
if !util.InStringList([]string{"view", "broadcast", "http"}, action.Action) {
|
if !util.InStringList([]string{"view", "broadcast", "http"}, action.Action) {
|
||||||
return nil, fmt.Errorf("cannot parse actions: action '%s' unknown", action.Action)
|
return nil, wrapErrHTTP(errHTTPBadRequestActionsInvalid, "action '%s' unknown", action.Action)
|
||||||
} else if action.Label == "" {
|
} else if action.Label == "" {
|
||||||
return nil, fmt.Errorf("cannot parse actions: label must be set")
|
return nil, wrapErrHTTP(errHTTPBadRequestActionsInvalid, "parameter 'label' is required")
|
||||||
} else if util.InStringList([]string{"view", "http"}, action.Action) && action.URL == "" {
|
} else if util.InStringList([]string{"view", "http"}, action.Action) && action.URL == "" {
|
||||||
return nil, fmt.Errorf("parameter 'url' is required for action '%s'", action.Action)
|
return nil, wrapErrHTTP(errHTTPBadRequestActionsInvalid, "parameter 'url' is required for action '%s'", action.Action)
|
||||||
|
} else if action.Action == "http" && util.InStringList([]string{"GET", "HEAD"}, action.Method) && action.Body != "" {
|
||||||
|
return nil, wrapErrHTTP(errHTTPBadRequestActionsInvalid, "parameter 'body' cannot be set if method is %s", action.Method)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +104,7 @@ func parseActionsFromSimple(s string) ([]*action, error) {
|
||||||
}
|
}
|
||||||
parts := util.SplitNoEmpty(rawAction, ",")
|
parts := util.SplitNoEmpty(rawAction, ",")
|
||||||
if len(parts) < 3 {
|
if len(parts) < 3 {
|
||||||
return nil, fmt.Errorf("cannot parse action: action requires at least keys 'action', 'label' and one parameter: %s", rawAction)
|
return nil, wrapErrHTTP(errHTTPBadRequestActionsInvalid, "action requires at least keys 'action', 'label' and one parameter: %s", rawAction)
|
||||||
}
|
}
|
||||||
for i, part := range parts {
|
for i, part := range parts {
|
||||||
key, value := util.SplitKV(part, "=")
|
key, value := util.SplitKV(part, "=")
|
||||||
|
@ -131,10 +134,10 @@ func parseActionsFromSimple(s string) ([]*action, error) {
|
||||||
case "body":
|
case "body":
|
||||||
newAction.Body = value
|
newAction.Body = value
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("cannot parse action: key '%s' not supported, please use JSON format instead", part)
|
return nil, wrapErrHTTP(errHTTPBadRequestActionsInvalid, "key '%s' unknown", key)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("cannot parse action: unknown phrase '%s'", part)
|
return nil, wrapErrHTTP(errHTTPBadRequestActionsInvalid, "unknown term '%s'", part)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actions = append(actions, newAction)
|
actions = append(actions, newAction)
|
||||||
|
|
Loading…
Reference in New Issue