From 234d96aaa67fac8f574d571f1245a47296459ffa Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Fri, 23 Oct 2026 20:48:44 -0700 Subject: [PATCH] Security review: John's answers on F36, F37 and F9 Hosted droplets have a restrictive cloud firewall (F36 re-rated low); standalone PostgreSQL trust accepted (F37); default SuperUser password is deliberate for trials, but the licensed-mode change is enforced by the client only (F9 note). Co-Authored-By: Claude Opus 5.5 --- devdocs/modernization/SECURITY-REVIEW.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/devdocs/modernization/SECURITY-REVIEW.md b/devdocs/modernization/SECURITY-REVIEW.md index 3ff9570a..16b3bc91 100644 --- a/devdocs/modernization/SECURITY-REVIEW.md +++ b/devdocs/modernization/SECURITY-REVIEW.md @@ -209,9 +209,9 @@ Status: *candidate* (seen in code, not yet confirmed), *confirmed*, *fixed in ca | F32 | Medium | confirmed (code) | A real-looking SMTP account and password are in the DEBUG defaults in `raven` and `sockeye` source | | F34 | Medium | confirmed | Shipped client libraries behind on security fixes: DOMPurify 2.3.8 (wiki sanitiser), Vuetify 2.6.6 | | F6 | Medium | confirmed | No lockout or rate limit on login or the 2FA step; no password rules | -| F9 | Medium | confirmed | Default SuperUser password, and an anonymous route that says whether it is still set | -| F36 | Medium | confirmed (code) | AyaNova listens on all interfaces on plain HTTP port 7575 in every template, hosted droplets included, and the setup script adds no firewall | -| F37 | Medium | confirmed (code) | Standalone PostgreSQL accepts any local connection without a password (`trust`) | +| F9 | Medium | confirmed | Default SuperUser password (by design for trials), and an anonymous route that says whether it is still set | +| F36 | Low | confirmed (code) | AyaNova listens on all interfaces on plain HTTP port 7575 in every template; hosted droplets are protected by a cloud firewall | +| F37 | Low | accepted (John) | Standalone PostgreSQL accepts any local connection without a password (`trust`) | | F2 | Medium | confirmed (code) | Windows installers give all local users modify rights on the program and data folders, `config.json` included | | F7 | Low | confirmed | Password change or reset does not end existing sessions, and makes the download key non-expiring | | F8 | Low | confirmed | Latest-token check skipped for any URL ending in `/auth` or `notify/hello` | @@ -326,7 +326,7 @@ A new database has SuperUser `superuser` / `l3tm3in` (`PrimeData.cs`). Anonymous `GET notify/hello` returns `sudf: true` while that is still the password, and the login form then fills it in. Anyone can therefore check an internet-facing server for it in one request and log in as SuperUser. Each call also runs a PBKDF2 hash, a cheap way to load the server. -Whether hosted droplets set their own SuperUser password at setup: to check in pass 10. **Pass 10:** they do not. The setup script generates the database password and JWT secret but not a SuperUser password, so every new hosted droplet starts internet-facing with `l3tm3in` until someone changes it. Fix in the script: generate one, set it once with `AYANOVA_SET_SUPERUSER_PW`, and write it to `/root/ayanova-secrets.txt` with the others. +Whether hosted droplets set their own SuperUser password at setup: to check in pass 10. **Pass 10:** they do not. The setup script generates the database password and JWT secret but not a SuperUser password, so every new hosted droplet starts internet-facing with `l3tm3in` until someone changes it. Fix in the script: generate one, set it once with `AYANOVA_SET_SUPERUSER_PW`, and write it to `/root/ayanova-secrets.txt` with the others. **John, 2026-10-23:** the published password is deliberate while a server is in trial mode, for easy evaluation; once licensed for production it must be changed on first login. Checked: that rule is enforced by the client only (`api/initialize.js` sends the user to the password page when `knownPassword` and the licence status is ActivePurchased); the server still accepts `superuser`/`l3tm3in` on a licensed server (API logins, or a user who navigates away) and `notify/hello` keeps reporting it. Fix that keeps the trial design: on a purchased licence, have the server refuse a login with the default SuperUser credentials (or allow it only to change the password), and stop returning `sudf` outside trial mode. Fix options: only return `sudf` from local or LAN addresses, or force a password change on first SuperUser login; at least warn in the UI and the manual. @@ -717,8 +717,9 @@ consider Swagger UI for logged-in users only. Every `config.json` template (`lan-install-config.json`, `linux-server`, `linux-desktop`, the standalone launcher's) sets `AYANOVA_USE_URLS` to `http://*:7575`. On hosted droplets nginx proxies HTTPS to `127.0.0.1:7575`, but Kestrel also listens on the public address, and the setup -script configures no host firewall. Unless a DigitalOcean cloud firewall blocks it (**John to -check**), anyone can reach AyaNova directly on port 7575 over plain HTTP, without TLS and +script configures no host firewall. Unless a DigitalOcean cloud firewall blocks it (**John, 2026-10-23: every droplet has a restrictive +cloud firewall**, the same settings confirmed for the Sockeye migration, so hosted droplets are +not exposed; re-rated Low), anyone can reach AyaNova directly on port 7575 over plain HTTP, without TLS and without nginx's headers, and logins sent there travel in clear. The standalone (single-PC) install also accepts connections from the whole network. Fix: hosted and Linux server templates bind `http://127.0.0.1:7575` when behind a proxy; the setup script enables `ufw` @@ -733,7 +734,9 @@ database user, the `postgres` superuser included, without a password. Standalone single-PC (mostly evaluation) install, so the exposure is local only; with F2 the database files are writable by every local user anyway. Fix: `scram-sha-256` with a password generated at install and written to the launcher's `config.json`; or accept it for evaluation installs -(John's decision). +(John's decision). **John, 2026-10-23: accepted** for standalone unless the fix is very short and +low-risk; the scram change needs a generated password in the installer, the launcher config and a +prepared database, so it is not that short: accepted. ### F38 Linux hardening (pass 10, from code) @@ -833,4 +836,4 @@ roles that should not see the schedule (pass 7). | 2026-10-23 | 7 Staff roles | Went through the remaining logged-in routes without a role check, the restricted-role filtering in `WorkOrderBiz`, and user and contact administration in `UserBiz`. Tested on the local dev server, read-only: **F27 confirmed** (`SubContractorRestricted` through `report/data`). F28 from code (live test not done). New: F29. The rest of the list found OK (see "Pass 7: routes ... found OK") | | 2026-10-23 | 8 Email and notifications | Code reading only, nothing sent. Read `NotifyController` (direct SMTP and message), `Mailer`, `GlobalOpsNotificationSettings` controller, biz and model, `NotifyEventHelper`, `CoreJobNotify`, `CoreJobCustomerNotify`, `NotifySubscriptionBiz`. F3 and F4 confirmed by code; F17 confirmed and re-rated Medium (names and links, no reports). New: F30, F31, F32. **Action for John: rotate the FastMail app password in F32** | | 2026-10-23 | 9 Client, headers, CORS, Swagger | Code reading of every `v-html` use in `raven-client` and its data source; `npm audit` read-only against the lockfile (no install, no change), triaged to shipped packages; server and nginx headers, CORS, Swagger. New: F33, F34 (needs John's decision on two front-end packages), F35; F14 extended (`unit/customerunit`) | -| 2026-10-23 | 10 Deployment | Code reading: `lan.iss`, `standalone.iss`, the four `config.json` templates, the standalone PostgreSQL configuration, the hosted setup script in `sockeye-client` (secrets, permissions, systemd unit, nginx, certbot), `dist/docker`. F2 confirmed and extended (`config.json` readable by all users); F9 extended (hosted droplets start with the default SuperUser password). New: F36, F37, F38. **John to check:** whether a DigitalOcean cloud firewall blocks port 7575 on hosted droplets | +| 2026-10-23 | 10 Deployment | Code reading: `lan.iss`, `standalone.iss`, the four `config.json` templates, the standalone PostgreSQL configuration, the hosted setup script in `sockeye-client` (secrets, permissions, systemd unit, nginx, certbot), `dist/docker`. F2 confirmed and extended (`config.json` readable by all users); F9 extended (hosted droplets start with the default SuperUser password). New: F36, F37, F38. John then confirmed hosted droplets have a restrictive cloud firewall (F36 re-rated Low), accepted F37, and explained the trial default password (F9 note added) |