mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
fix(api): name the private-origin refusal on webhook and ntfy routes
The save routes evaluate the same policy the senders do. A target on a private network the operator has not listed is a 422 with meta.errorCode = private_origin_not_approved and a message that names NOTIFICATION_PRIVATE_ORIGINS, instead of the generic "Invalid data"; a listed private address saves although the plain public floor would have refused it. The settings schemas take the target predicate as a parameter so the validator module never reads the environment; the exported constants keep the public floor for the OpenAPI contract. The test routes forward the sender's refusal as the same 422 and code instead of a bare 500, so the card can say why the test failed rather than sending the operator to the wide-event log. A delivery fault is still a 500. Both test routes and the dispatcher reach the same sender function, and a structural test holds them there. The OpenAPI descriptions name the code on all four routes; the test button's error map gains the code in every locale. Refs #947
This commit is contained in:
1 parent
6c75c4f39f
commit
984a75d036
17 files changed
+614
-56
No files matched your search
+32
-11
@@ -11969,7 +11969,8 @@ paths:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"422":
|
||||
description: Body matched neither accepted shape, or `enabled` is true while the stored config has no server URL and
|
||||
topic.
|
||||
topic; or the server URL is on a private network the operator has not listed in
|
||||
`NOTIFICATION_PRIVATE_ORIGINS` (`meta.errorCode` = `private_origin_not_approved`). Nothing was stored.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -12094,7 +12095,9 @@ paths:
|
||||
"413": *a16
|
||||
"415": *a17
|
||||
"422":
|
||||
description: Body matched neither accepted shape, or `enabled` is true while no webhook URL is stored.
|
||||
description: Body matched neither accepted shape, or `enabled` is true while no webhook URL is stored; or the URL is on
|
||||
a private network the operator has not listed in `NOTIFICATION_PRIVATE_ORIGINS` (`meta.errorCode` =
|
||||
`private_origin_not_approved`). Nothing was stored.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -12311,7 +12314,9 @@ paths:
|
||||
summary: Send a test message to the saved ntfy topic
|
||||
description: Publishes one message to the account's configured ntfy server and topic. Takes no body. Rate-limited 5 per
|
||||
5 minutes per user. Refuses with 400 when no ntfy channel is saved or when the saved config is missing a server
|
||||
URL or topic; a publish failure is a 500.
|
||||
URL or topic; a publish failure is a 500. A server on a private network is refused with 422 and `meta.errorCode`
|
||||
= `private_origin_not_approved` unless the operator listed its exact origin in `NOTIFICATION_PRIVATE_ORIGINS`;
|
||||
the test and the scheduled delivery take the same decision.
|
||||
responses:
|
||||
"200":
|
||||
description: The provider accepted the message. Not proof it was delivered.
|
||||
@@ -12320,7 +12325,13 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/NtfyTestEnvelope"
|
||||
"401": *a1
|
||||
"422": *a4
|
||||
"422":
|
||||
description: The saved server is on a private network the operator has not approved (`meta.errorCode` =
|
||||
`private_origin_not_approved`). The resolved address is never echoed.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"429": *a5
|
||||
/api/settings/telegram/test:
|
||||
post:
|
||||
@@ -12353,7 +12364,9 @@ paths:
|
||||
description: Posts one payload to the account's configured webhook URL. Takes no body. Rate-limited 5 per 5 minutes per
|
||||
user. Refuses with 400 when no webhook channel is saved or the saved config has no URL; a delivery failure is a
|
||||
500. The URL was checked against the SSRF floor when it was saved, so a test cannot be used to reach an internal
|
||||
host.
|
||||
host. A URL on a private network is refused with 422 and `meta.errorCode` = `private_origin_not_approved` unless
|
||||
the operator listed its exact origin in `NOTIFICATION_PRIVATE_ORIGINS`; the test and the scheduled delivery take
|
||||
the same decision.
|
||||
responses:
|
||||
"200":
|
||||
description: The provider accepted the message. Not proof it was delivered.
|
||||
@@ -12362,7 +12375,13 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/WebhookTestEnvelope"
|
||||
"401": *a1
|
||||
"422": *a4
|
||||
"422":
|
||||
description: The saved URL is on a private network the operator has not approved (`meta.errorCode` =
|
||||
`private_origin_not_approved`). The resolved address is never echoed.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"429": *a5
|
||||
/api/settings/data:
|
||||
delete:
|
||||
@@ -25600,9 +25619,10 @@ components:
|
||||
required:
|
||||
- enabled
|
||||
description: "ntfy channel config. `serverUrl` must pass the SSRF floor — a URL resolving into a private range is
|
||||
refused at input time. An EMPTY OR OMITTED `authToken` preserves the stored one rather than clearing it: the GET
|
||||
never returns the secret, so a client round-tripping the form would otherwise wipe it on every unrelated save.
|
||||
Sending a non-empty value replaces it. There is no way to clear a stored auth token through this endpoint."
|
||||
refused at input time unless the operator listed its exact origin in `NOTIFICATION_PRIVATE_ORIGINS`. An EMPTY OR
|
||||
OMITTED `authToken` preserves the stored one rather than clearing it: the GET never returns the secret, so a
|
||||
client round-tripping the form would otherwise wipe it on every unrelated save. Sending a non-empty value
|
||||
replaces it. There is no way to clear a stored auth token through this endpoint."
|
||||
TelegramSettingsPutRequest:
|
||||
anyOf:
|
||||
- $ref: "#/components/schemas/NotificationChannelEnabledRequest"
|
||||
@@ -25660,8 +25680,9 @@ components:
|
||||
- enabled
|
||||
description: "Generic-webhook channel config — one channel covering Gotify, Discord, Slack, a Matrix bridge, Home
|
||||
Assistant, or any relay accepting an inbound JSON POST. `url` must pass the SSRF floor at input time and is
|
||||
re-checked at dispatch time. An EMPTY OR OMITTED `headerValue` preserves the stored one, for the same reason as
|
||||
ntfy's `authToken`: the GET never returns it. There is no way to clear a stored header value through this
|
||||
re-checked at dispatch time; a private origin passes both only when the operator listed it in
|
||||
`NOTIFICATION_PRIVATE_ORIGINS`. An EMPTY OR OMITTED `headerValue` preserves the stored one, for the same reason
|
||||
as ntfy's `authToken`: the GET never returns it. There is no way to clear a stored header value through this
|
||||
endpoint."
|
||||
DeleteAccountRequest:
|
||||
type: object
|
||||
|
||||
Reference in new issue
Block a user