Harden send_mail API and TLS configuration

This commit is contained in:
lovasoa
2026-07-11 00:25:06 +02:00
parent 77797c909e
commit 9659fedede
11 changed files with 247 additions and 244 deletions
@@ -2,8 +2,7 @@ INSERT INTO sqlpage_functions (
"name",
"introduced_in_version",
"icon",
"description_md",
"return_type"
"description_md"
)
VALUES (
'send_mail',
@@ -11,7 +10,7 @@ VALUES (
'mail',
'Sends an email using the SMTP server configured with `SMTP_HOST`.
`SMTP_HOST` must contain only a host name or `host:port`; URL schemes and paths are rejected. When no port is specified, SQLPage uses port 25.
`SMTP_HOST` contains the relay host name. Set `SMTP_PORT` when the relay does not use the default for the selected encryption mode: 587 for `starttls`, 465 for `tls`, or 25 for `none`.
`SMTP_TLS_MODE` defaults to `starttls`, which requires a STARTTLS upgrade before sending email or credentials. Set it to `tls` for implicit TLS, commonly used on port 465. Plaintext mode (`none`) is allowed only without credentials and should be used only for trusted local SMTP servers.
@@ -19,23 +18,23 @@ If your SMTP server requires authentication, configure `SMTP_USERNAME` and `SMTP
The function accepts a single JSON object argument. The required properties are:
- `recipient`: email address to send to, optionally including a display name such as `"Jane Doe <jane@example.com>"`.
- `to`: email address to send to, optionally including a display name such as `"Jane Doe <jane@example.com>"`.
- `subject`: email subject.
- `body`: plain text email body.
Optional properties:
- `sender`: sender address. Defaults to `SQLPage <sqlpage@localhost>`.
- `from`: sender address. It may be omitted when `SMTP_FROM` configures a default sender.
- `reply_to`: reply-to address.
After the SMTP server accepts the message, the function returns its JSON argument unchanged. It returns `NULL` when passed `NULL`, and raises an error if the message cannot be sent.
The function returns `NULL` after the SMTP relay accepts the message and raises an error if the message cannot be sent. The argument is required; passing `NULL` is an error.
### Example
```sql
set message = json_object(
''recipient'', ''admin@example.com'',
''sender'', ''contact@example.com'',
''to'', ''admin@example.com'',
''from'', ''contact@example.com'',
''subject'', ''New contact form message'',
''body'', ''Hello from SQLPage!''
);
@@ -50,7 +49,7 @@ select ''email'' as name, ''email'' as type, true as required;
select ''message'' as name, ''textarea'' as type, true as required;
set mail = json_object(
''recipient'', ''admin@example.com'',
''to'', ''admin@example.com'',
''reply_to'', $email,
''subject'', ''Website contact form'',
''body'', $message
@@ -58,8 +57,7 @@ set mail = json_object(
select sqlpage.send_mail($mail)
where $message is not null;
```
',
'JSON'
'
);
INSERT INTO sqlpage_function_parameters (
@@ -73,6 +71,6 @@ VALUES (
'send_mail',
1,
'message',
'A JSON object containing the email to send. Required properties are `recipient`, `subject`, and `body`. Optional properties are `sender` and `reply_to`.',
'A JSON object containing the email to send. Required properties are `to`, `subject`, and `body`. Optional properties are `from` (required unless `SMTP_FROM` is configured) and `reply_to`. Unknown properties are rejected to catch misspellings.',
'JSON'
);
+5 -5
View File
@@ -10,7 +10,7 @@ docker compose up
Open http://localhost:8080 to send an email, then inspect it in the Mailpit inbox at http://localhost:8025.
The SMTP server is configured in [`docker-compose.yml`](./docker-compose.yml) with `SMTP_HOST=mailpit:1025` and `SMTP_TLS_MODE=none`. Plaintext mode is intended only for trusted local SMTP servers such as Mailpit.
The SMTP server is configured in [`docker-compose.yml`](./docker-compose.yml) with `SMTP_HOST=mailpit`, `SMTP_PORT=1025`, and `SMTP_TLS_MODE=none`. Plaintext mode is intended only for trusted local SMTP servers such as Mailpit.
For a remote SMTP relay, keep the default `SMTP_TLS_MODE=starttls`, or set it to `tls` when the relay requires implicit TLS. Configure `SMTP_USERNAME` and `SMTP_PASSWORD` when authentication is required; SQLPage rejects credentials in plaintext mode.
@@ -18,14 +18,14 @@ The form handler sends the message with a single function call:
```sql
set message = json_object(
'recipient', :recipient,
'sender', :sender,
'to', :recipient,
'from', :sender,
'subject', :subject,
'body', :body
);
set sent_message = sqlpage.send_mail($message);
set _ = sqlpage.send_mail($message);
```
After the SMTP server accepts the email, `sqlpage.send_mail` returns the message JSON unchanged. It raises an error when delivery to the SMTP server fails.
`sqlpage.send_mail` returns `NULL` after the SMTP relay accepts the message. It raises an error when the relay rejects the message or cannot be reached, so statements after the call run only on success.
Do not expose an unrestricted form like this publicly. In production, authenticate users, restrict recipients, validate input, and add rate limiting to prevent abuse.
+2 -1
View File
@@ -4,7 +4,8 @@ services:
ports:
- "8080:8080"
environment:
SMTP_HOST: mailpit:1025
SMTP_HOST: mailpit
SMTP_PORT: 1025
SMTP_TLS_MODE: none
volumes:
- .:/var/www
+3 -4
View File
@@ -1,6 +1,6 @@
set message = json_object(
'recipient', :recipient,
'sender', :sender,
'to', :recipient,
'from', :sender,
'subject', :subject,
'body', :body
);
@@ -9,8 +9,7 @@ set sent_message = sqlpage.send_mail($message);
select
'alert' as component,
'success' as color,
'Email sent successfully' as title
where $sent_message is not null;
'Email sent successfully' as title;
select 'button' as component;
select 'Send another email' as title, 'index.sql' as link;