case 4727 - safe security headers on every response
Every response carries X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN and Referrer-Policy: strict-origin, so self-hosted installs get them too (the hosted nginx sends the same values). F35 (4727 step 6). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -193,7 +193,7 @@ John's "next release" cases for AyaNova (exported 2026-10-24). Each is its own s
|
||||
| 4724 | SMTP: certificate checked, password never returned | F30, F31. **Done** (certificate checked, opt-out config setting `AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE` with manual page; password masked as `********` and kept on save; test `SmtpPasswordHidden`; self-signed listener proof; changelog lines for both) |
|
||||
| 4725 | Sessions: password change ends sessions, exact-path token exemption, default SuperUser refused when licensed | F7, F8, F9. **Done** (sessions end on reset, administrator password change and login disabled; exact route exemption, inactive users refused; default-password SuperUser limited to changing it on a purchased licence, option A; test `Sessions`; John's click-through with a temporary licence override; manual and changelog) |
|
||||
| 4726 | Login throttling/lockout (raven and sockeye) | F6. **Done** (10 failures per login or 50 per address in 15 minutes, then 429 for the rest of the window; same in Sockeye, whose login is public; test `LoginLockout`; per-address limit proved with a forwarded documentation address) |
|
||||
| 4727 | Low severity hardening | F10, F11, F12, F22, F23, F25, F29, F35, F36, F38; the AyaNova half of F39 (DB ID not shown anonymously). In steps: 1 logo (F23) **done**; 2 API root page (F11, F39) **done**; 3 `server-info` (F29) **done**; 4 template export (F22) **done**, job owner check accepted; 5 `OverrideUserId` and export download (F25) **done**; 6 safe headers (F35); 7 remove `dist/docker` (F38) |
|
||||
| 4727 | Low severity hardening | F10, F11, F12, F22, F23, F25, F29, F35, F36, F38; the AyaNova half of F39 (DB ID not shown anonymously). In steps: 1 logo (F23) **done**; 2 API root page (F11, F39) **done**; 3 `server-info` (F29) **done**; 4 template export (F22) **done**, job owner check accepted; 5 `OverrideUserId` and export download (F25) **done**; 6 safe headers (F35) **done**; 7 remove `dist/docker` (F38) |
|
||||
| 4681 | Sockeye: licence/trial request hardening (tagged sockeye, rockfish) | F40. **Done** (sockeye `cd3ebed`: rate limits, email check, cryptographic codes; rvf unchanged) |
|
||||
|
||||
**From the port and earlier triage**
|
||||
@@ -251,7 +251,7 @@ Found during the port, out of scope. One line each.
|
||||
|
||||
- `NotifyController.SendNotifySmtpDirectMessage`, User branch: the missing-address and inactive messages say "Vendor" instead of "User" (found in case 4723)
|
||||
- SMTP connection security mapping (`Mailer.cs`): "StartTls" connects with `useSsl: true`, which MailKit treats as TLS from the start (port 465), not STARTTLS (587); "None" uses STARTTLS when offered, which can be stripped. Correcting it could break customers using "StartTls" on port 465 (found in case 4724)
|
||||
- Integration test `DownloadNames` logs in again as the shared `Sales` user to get a download key, which replaces the token the other tests use for Sales while running in parallel (one session per user). Tests that need a download key should create their own user, as `TemplateExportRights` does (found in case 4727)
|
||||
- Integration test `DownloadNames` logs in again as the shared `Sales` user to get a download key, which replaces the token the other tests use for Sales while running in parallel (one session per user). Tests that need a download key should create their own user, as `TemplateExportRights` does (found in case 4727). Also fragile under parallel runs: `DataListOperations.WorkOrderList_PaginationOffsetAndLimitWork` fetches two pages of work orders and checks they do not overlap, while `WorkOrderCrud` creates and deletes work orders (failed once in case 4727)
|
||||
- From F6 (case 4726), split off as separate changes: a password strength rule (not empty is the only rule today; seeded trial users use their login as password) is a new, visible feature; `limit_req` on `/api/v8/auth` in the hosted nginx configuration belongs in the `sockeye-client` setup script and affects every hosted droplet. Both are second layers behind the login lockout
|
||||
- Race in the notification system: a record saved while a matching notification subscription is being deleted fails with 500 (`anotifyevent_notifysubscriptionid_fkey` violation), and the user's save fails with it. Seen once in the integration suite (`CustomerCrud.CRUD`, run in parallel with subscription tests) during case 4724
|
||||
- **Case 4731 (John, priority 1, after 8.3.0):** a global notification setting "allow email to any address from the API and report scripts" for `notify/direct-smtp`; off for new installs, on for upgraded databases; when off, only a record's saved address. Follows from F3 / case 4723
|
||||
|
||||
@@ -227,7 +227,7 @@ take the next free number; a fixed or accepted finding keeps its ID and changes
|
||||
| F12 | Low | confirmed | Password hashing: PBKDF2-SHA512 with 10,000 iterations |
|
||||
| F25 | Low | **fixed in case 4727** (upload before rights check accepted) | Attachment and export gaps: upload stored before the rights check, "attached by" can be set to anyone, exports not tied to their user |
|
||||
| F29 | Low | **fixed in case 4727** | `server-info` gives every login, customer contacts included, the server DB ID and full licence details |
|
||||
| F35 | Low | confirmed | No security headers from the server itself (self-hosted), no CSP anywhere, CORS any origin, public Swagger UI, download keys in URLs |
|
||||
| F35 | Low | **fixed in case 4727** (CSP a later case; CORS, Swagger accepted) | No security headers from the server itself (self-hosted), no CSP anywhere, CORS any origin, public Swagger UI, download keys in URLs |
|
||||
| F38 | Low | confirmed (code) | Linux hardening: no systemd sandboxing, service user can replace program files, desktop runs as root, unused Docker files with a fixed database password |
|
||||
| F39 | Low | confirmed (code) | Licence fetch is one-shot by DB ID, and the DB ID is shown anonymously while unlicensed: someone else can take a new key first |
|
||||
| F40 | Low | **fixed in case 4681** (Sockeye) | Trial request routes: no rate limit, non-cryptographic verification codes, a fixed bot-check value |
|
||||
@@ -737,6 +737,7 @@ client's own scripts; limit CORS to the server's own origin unless API users nee
|
||||
consider Swagger UI for logged-in users only.
|
||||
|
||||
**John, 2026-10-24:** the safe headers (`nosniff`, `X-Frame-Options`, `Referrer-Policy`) in case 4727. Content-Security-Policy for the client: its own later case, it needs testing of the whole client. CORS: **accepted** as is (the token is a header, not a cookie; limiting origins could break browser-based API users). Swagger at `/api-docs`: **accepted** public (it is the API documentation).
|
||||
**Case 4727, step 6 (2026-10-24):** every response carries `X-Content-Type-Options: nosniff`, `X-Frame-Options: SAMEORIGIN` and `Referrer-Policy: strict-origin` (the hosted nginx values, so the duplicates behind nginx agree); test `ApiRoute.SafeHeadersOnEveryResponse`. Still open from F35: the client Content-Security-Policy (later case); download keys in URLs stay (accepted with F7's fix).
|
||||
|
||||
### F36 Listening on all interfaces, no firewall (pass 10, from code)
|
||||
|
||||
|
||||
@@ -338,6 +338,16 @@ namespace AyaNova
|
||||
// await next.Invoke();
|
||||
// });
|
||||
|
||||
//case 4727 (F35): safe headers on every response, so self-hosted installs get them too (hosted droplets
|
||||
//also get them from nginx: same values, so the duplicates agree)
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
context.Response.Headers["X-Content-Type-Options"] = "nosniff";
|
||||
context.Response.Headers["X-Frame-Options"] = "SAMEORIGIN";
|
||||
context.Response.Headers["Referrer-Policy"] = "strict-origin";
|
||||
await next.Invoke();
|
||||
});
|
||||
|
||||
|
||||
#region STATIC FILES
|
||||
_newLog.LogDebug("Static files");
|
||||
|
||||
Reference in New Issue
Block a user