Skip to content

Commit 7fed392

Browse files
committed
Disallow certain templates
1 parent 4692ca7 commit 7fed392

File tree

3 files changed

+49
-17
lines changed

3 files changed

+49
-17
lines changed

server/errors.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ var (
8989
errHTTPBadRequestSinceInvalid = &errHTTP{40008, http.StatusBadRequest, "invalid since parameter", "https://ntfy.sh/docs/subscribe/api/#fetch-cached-messages", nil}
9090
errHTTPBadRequestTopicInvalid = &errHTTP{40009, http.StatusBadRequest, "invalid request: topic invalid", "", nil}
9191
errHTTPBadRequestTopicDisallowed = &errHTTP{40010, http.StatusBadRequest, "invalid request: topic name is not allowed", "", nil}
92-
errHTTPBadRequestMessageNotUTF8 = &errHTTP{40011, http.StatusBadRequest, "invalid message: message must be UTF-8 encoded", "", nil}
92+
errHTTPBadRequestMessageNotUTF8 = &errHTTP{40011, http.StatusBadRequest, "invalid request: message must be UTF-8 encoded", "", nil}
9393
errHTTPBadRequestAttachmentURLInvalid = &errHTTP{40013, http.StatusBadRequest, "invalid request: attachment URL is invalid", "https://ntfy.sh/docs/publish/#attachments", nil}
9494
errHTTPBadRequestAttachmentsDisallowed = &errHTTP{40014, http.StatusBadRequest, "invalid request: attachments not allowed", "https://ntfy.sh/docs/config/#attachments", nil}
9595
errHTTPBadRequestAttachmentsExpiryBeforeDelivery = &errHTTP{40015, http.StatusBadRequest, "invalid request: attachment expiry before delayed delivery date", "https://ntfy.sh/docs/publish/#scheduled-delivery", nil}
@@ -113,14 +113,15 @@ var (
113113
errHTTPBadRequestPhoneNumberNotVerified = &errHTTP{40034, http.StatusBadRequest, "invalid request: phone number not verified, or no matching verified numbers found", "https://ntfy.sh/docs/publish/#phone-calls", nil}
114114
errHTTPBadRequestAnonymousCallsNotAllowed = &errHTTP{40035, http.StatusBadRequest, "invalid request: anonymous phone calls are not allowed", "https://ntfy.sh/docs/publish/#phone-calls", nil}
115115
errHTTPBadRequestPhoneNumberVerifyChannelInvalid = &errHTTP{40036, http.StatusBadRequest, "invalid request: verification channel must be 'sms' or 'call'", "https://ntfy.sh/docs/publish/#phone-calls", nil}
116-
errHTTPBadRequestDelayNoCall = &errHTTP{40037, http.StatusBadRequest, "delayed call notifications are not supported", "", nil}
116+
errHTTPBadRequestDelayNoCall = &errHTTP{40037, http.StatusBadRequest, "invalid request: delayed call notifications are not supported", "", nil}
117117
errHTTPBadRequestWebPushSubscriptionInvalid = &errHTTP{40038, http.StatusBadRequest, "invalid request: web push payload malformed", "", nil}
118118
errHTTPBadRequestWebPushEndpointUnknown = &errHTTP{40039, http.StatusBadRequest, "invalid request: web push endpoint unknown", "", nil}
119119
errHTTPBadRequestWebPushTopicCountTooHigh = &errHTTP{40040, http.StatusBadRequest, "invalid request: too many web push topic subscriptions", "", nil}
120-
errHTTPBadRequestTemplatedMessageTooLarge = &errHTTP{40041, http.StatusBadRequest, "invalid request: message or title is too large after replacing template", "", nil}
121-
errHTTPBadRequestTemplatedMessageNotJSON = &errHTTP{40042, http.StatusBadRequest, "invalid request: message body must be JSON if templating is enabled", "", nil}
122-
errHTTPBadRequestTemplateInvalid = &errHTTP{40043, http.StatusBadRequest, "invalid request: could not parse template", "", nil}
123-
errHTTPBadRequestTemplateExecutionFailed = &errHTTP{40044, http.StatusBadRequest, "invalid request: template execution failed", "", nil}
120+
errHTTPBadRequestTemplateMessageTooLarge = &errHTTP{40041, http.StatusBadRequest, "invalid request: message or title is too large after replacing template", "https://ntfy.sh/docs/publish/#message-templating", nil}
121+
errHTTPBadRequestTemplateMessageNotJSON = &errHTTP{40042, http.StatusBadRequest, "invalid request: message body must be JSON if templating is enabled", "https://ntfy.sh/docs/publish/#message-templating", nil}
122+
errHTTPBadRequestTemplateInvalid = &errHTTP{40043, http.StatusBadRequest, "invalid request: could not parse template", "https://ntfy.sh/docs/publish/#message-templating", nil}
123+
errHTTPBadRequestTemplateDisallowedFunctionCalls = &errHTTP{40044, http.StatusBadRequest, "invalid request: template contains disallowed function calls, e.g. template, call, or define", "https://ntfy.sh/docs/publish/#message-templating", nil}
124+
errHTTPBadRequestTemplateExecuteFailed = &errHTTP{40045, http.StatusBadRequest, "invalid request: template execution failed", "https://ntfy.sh/docs/publish/#message-templating", nil}
124125
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", "", nil}
125126
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication", nil}
126127
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication", nil}

server/server.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ const (
136136
templateMaxExecutionTime = 100 * time.Millisecond
137137
)
138138

139+
var (
140+
// templateDisallowedRegex tests a template for disallowed expressions. While not really dangerous, they
141+
// are not useful, and seem potentially troublesome.
142+
templateDisallowedRegex = regexp.MustCompile(`(?m)\{\{-?\s*(call|template|define)\b`)
143+
)
144+
139145
// WebSocket constants
140146
const (
141147
wsWriteWait = 2 * time.Second
@@ -1108,23 +1114,26 @@ func (s *Server) handleBodyAsTemplatedTextMessage(m *message, body *util.PeekedR
11081114
return err
11091115
}
11101116
if len(m.Message) > s.config.MessageSizeLimit {
1111-
return errHTTPBadRequestTemplatedMessageTooLarge
1117+
return errHTTPBadRequestTemplateMessageTooLarge
11121118
}
11131119
return nil
11141120
}
11151121

11161122
func replaceTemplate(tpl string, source string) (string, error) {
1123+
if templateDisallowedRegex.MatchString(tpl) {
1124+
return "", errHTTPBadRequestTemplateDisallowedFunctionCalls
1125+
}
11171126
var data any
11181127
if err := json.Unmarshal([]byte(source), &data); err != nil {
1119-
return "", errHTTPBadRequestTemplatedMessageNotJSON
1128+
return "", errHTTPBadRequestTemplateMessageNotJSON
11201129
}
11211130
t, err := template.New("").Parse(tpl)
11221131
if err != nil {
11231132
return "", errHTTPBadRequestTemplateInvalid
11241133
}
11251134
var buf bytes.Buffer
11261135
if err := t.Execute(util.NewTimeoutWriter(&buf, templateMaxExecutionTime), data); err != nil {
1127-
return "", errHTTPBadRequestTemplateExecutionFailed
1136+
return "", errHTTPBadRequestTemplateExecuteFailed
11281137
}
11291138
return buf.String(), nil
11301139
}

0 commit comments

Comments
 (0)