Commit Graph

6985 Commits

Author SHA1 Message Date
a6447ecb37 case 4632 - reports render under IIS: the report page's API calls are relayed by the server
Inside IIS (in-process) AyaNova has no port of its own, so the report page
could not reach the API: user reports called http://127.0.0.1:<request port>
(reset on https), notification reports 127.0.0.1:7575 (refused). Under IIS the
page's requests under the API URL are now intercepted and made by the server
(util/ReportApiRelay.cs) to the local address and port a user's request
arrived on, with its host name and scheme; notification reports use the
latest signed-in request, else the override. Outside IIS nothing changes.
The notification token check requires a request from this machine, no longer
host 127.0.0.1. Manual (IIS install, override page) and changelog updated.
Deferred: emailed PDF content type application/pdf/pdf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:04:24 -07:00
ce5fb9e11c Release checklist: download every published installer back and check its version
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 14:44:44 -07:00
e9b79242fd case 4579 - Windows installers: program folder no longer writable by all users
Both installers drop users-modify from the program folder (and local-postgres
in the single-user installer) and run icacls to remove the Users group's own
grants (by SID) from the program folder after copying files, so upgrades over
8.2.4 lose them too. The data folder keeps users-modify and config.json stays
readable by local users (the running account is not known at install time);
both documented in the single-user and LAN install pages. Changelog updated.
F2 fixed (write access), the rest accepted. Deferred: report on a missing
record fails with a generic error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 14:39:00 -07:00
c04de33af2 case 4732 - trusted reverse proxies setting
New config setting AYANOVA_TRUSTED_PROXIES (IP addresses or CIDR networks)
for reverse proxies on another computer: their X-Forwarded-For is used, a
chain of trusted proxies is followed, invalid entries are warned at boot.
X-Forwarded-For from a proxy that is not trusted logs one warning per server
start naming the proxy's address. Without the setting nothing changes (only
a loopback proxy is trusted). Shown in server info; manual page, Linux install
note, changelog. F6 follow-up recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 12:58:01 -07:00
344106ae27 dev-raven.bat: client dev server starts in the ayanova folder
Since c2f2f922 the script opens raven-client's root in VS Code, and the
window running appdev.bat inherited that folder, so npm run serve could
not find package.json. Start that window in raven-client\ayanova.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 09:55:43 -07:00
b99cbf126b Release 8.3.0 cases: 4579 plan recorded (awaiting approval); Sockeye deploy note for 4726
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 22:34:54 -07:00
d084a461ea Security review: summary status of F10, F12, F36, F39 brought up to date
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 22:31:41 -07:00
07d2b07fab case 4727 - remove the unused Docker option
dist/docker (a runtime dockerfile and a compose file that published
PostgreSQL with a fixed password) was from early development and never
offered. Removed with its commented-out lines in build-release.bat and
the BUILD.md mention; the build-environment Dockerfiles for the docs and
the client stay. F38 (4727 step 7, the last step).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 22:30:31 -07:00
1ccbd7745c 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>
2026-10-24 22:01:38 -07:00
cce07f3e6b case 4727 - attached-by override for admins only, export download by its owner only
OverrideUserId on attachment upload is used only from SuperUser or a user
with change rights on User, otherwise ignored. Export files carry the
user's id in their name and export/download serves only the key user's
own export files. F25 (4727 step 5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 21:39:50 -07:00
b0e3933a9f case 4727 - report template export only to users who may use or edit it
report/export/{id} by download key gives a template only to a user whose
roles include one of the template's roles, or who may edit templates (a
user with no roles got any template). Job owner check accepted (no owner
recorded; random job ids). F22 (4727 step 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 21:30:47 -07:00
973b64a682 case 4727 - DB ID and licence to staff only
server-info returns the DB ID and licence, and global-biz-setting/client
the DB ID and licensee, only to Service and NotService users, as the About
form already shows them; customer contacts and subcontractors get empty
values. F29 fixed, AyaNova half of F39 done (4727 step 3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 21:19:38 -07:00
fa7affc91c case 4727 - API root page shows no licence details or DB ID
The anonymous page at /api/v8/ shows the version and its links only: the
licence block is removed and the support link no longer carries the DB
ID and licensee. F11 fixed (4727 step 2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 21:05:49 -07:00
8dc0ffd481 case 4727 - John's decisions on F10, F12, F22, F25, F35, F36, F38
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:57:27 -07:00
289058bad2 case 4727 - logo uploads checked by content, change rights, served sandboxed
Logo upload and delete need change rights on Global (was read rights,
which BizAdminRestricted has). The image type is taken from the bytes:
PNG, JPEG, GIF, WebP or SVG, anything else refused; the claimed type is
not stored. Downloads send nosniff and a sandbox Content-Security-Policy,
so an SVG opened by its URL runs no script. F23 fixed (4727 step 1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:56:56 -07:00
eb1deff2ba case 4693 - form field lists published only when fully built
FormFieldReferenceList and FormFieldKeys built their static lists lazily
and published them before filling them, so requests arriving during the
first build after a restart saw a half-filled list ("is not valid").
Both are now built in a local and published when complete (volatile).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:28:11 -07:00
d8be701bcd case 4726 - login lockout per login name and per address
Failed logins are counted in memory (util/LoginThrottle.cs): 10 per login
name or 50 per client address within 15 minutes, then attempts are refused
with 429 for the rest of the window without checking the password. A
correct password clears the login's count; loopback is not counted per
address. Each lockout is logged. F6 fixed (2FA part in case 4710).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 20:12:26 -07:00
80f81aea0f case 4725 - sessions end on password reset and login disabled, exact login route, default SuperUser limited when licensed
A password set by someone else (reset code, administrator) or a disabled
login replaces the user's session token and download key; changing one's
own password keeps the session. PasswordResetCodeExpire is cleared (was
DlKeyExpire). The token middleware exempts only the exact login and
notify/hello routes and refuses inactive users and users who may not log
in. With a purchased licence, notify/hello no longer offers the default
SuperUser login and a SuperUser with the default password may only change
it (SuperUserDefaultPasswordRoutes). F7, F8 and F9 fixed. Manual and
changelog updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:54:03 -07:00
4141217c04 case 4724 - SMTP certificate checked, password never returned
Mailer checks the SMTP server's certificate (trust and name; no revocation
check). New config setting AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE accepts any
certificate (boot warning, shown in server info, manual page).
The notification settings GET returns ******** instead of the SMTP
password; a PUT with ******** or empty keeps the stored password.
F30 and F31 fixed. Changelog and manual updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:09:02 -07:00
94a6f3b653 case 4731 noted in Deferred: setting for direct email to any address
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 18:36:48 -07:00
f3ba5156c7 case 4723 - direct email and direct notification for staff only, record rights
notify/direct-smtp: customer contacts refused; an address taken from a
Customer, HeadOffice, Vendor or User record needs read rights on that type
(a contact's user record: User or Customer rights). Staff may still give any
address (report scripts use it). The delivery-on check runs after these.
notify/direct-message: customer contacts and subcontractors refused, as the
client already hides it from them. F3 and F4 fixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 18:30:08 -07:00
52d66bcf10 case 4722 - scheduled work order widget checked; performance changelog line to confirm
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 18:10:59 -07:00
7204b9ff6d case 4722 - F34 fixed, case done
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 18:06:47 -07:00
9fe52514b9 case 4721 - F33 fixed, case done
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 17:19:56 -07:00
823bd5029a Release 8.3.0 cases: 4632 and 4719 moved to the end, to be investigated on an IIS + TLS test setup
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 16:32:36 -07:00
d254affc8b case 4720 - restricted roles get filtered work order items in report data, rendering and export
Opening a work order filters it for restricted users (TechRestricted,
SubContractor, SubContractorRestricted): only items they are scheduled on, and
on those only their own labour, travel and scheduling, and for some roles no
parts, units, expenses etc. Report data, rendering and export went through
WorkOrderGetPartialAsync, which filtered nothing for child types (security
review F27). Two causes:
- the partial fetch now applies the same filtering: the item only if the user
  is scheduled on it (checked in the database), then its details filtered; the
  existing per-item filtering moved unchanged into RemoveRestrictedUserItemDetails
  so both fetches share it;
- BizObjectFactory created WorkOrderBiz with UserType.Service for everyone, so
  in these paths a subcontractor was never treated as one; it now passes the
  user's real type (only the subcontractor flags depend on it).

Checked: report data for a restricted subcontractor on work order 1 went from
the whole item with parts to none; scheduled on an item, it gets the item
without parts or other users' labour. Full suite: 111 passed, 5 skipped, the
same 2 known failures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 16:27:59 -07:00
ee59efff48 case 4713 - customer contacts hold and act with customer roles only
Users who may edit contacts but not users (Service, Sales, Accounting) could
give a contact login any role, BizAdmin and OpsAdmin included, and roles are
what every check uses (security review F28). Now:
- saving a Customer or HeadOffice user with any role other than Customer or
  CustomerRestricted is refused (error on Roles);
- UserBiz.EffectiveRoles masks a contact's roles to the customer roles wherever
  they are applied: the token middleware, download-key routes, and the
  notification subscriber check, so contacts that already have staff roles
  stored act without them.

Checked: Service creating a contact with BizAdmin refused, with Customer works;
a contact with OpsAdmin stored in the database gets 403 downloading a log file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 15:59:35 -07:00
6edb57c749 case 4712 - John's click-through as customer contacts passed; contact attachments checked
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 14:50:12 -07:00
87fdc12dce case 4712 - notification subscriptions are the caller's own; object events only to subscribers who may read the record type
Step 6 of 4712 (security review F17). A subscription kept the UserId sent by
the client, and object events (created, modified, deleted, age) were matched by
record type only, so a user could get record names of types it may not read,
delivered to any address. Now a subscription is created for the caller and an
update cannot move it to another user, unless the caller administers users; and
object events skip subscribers without read rights on the record type.

Checked: Service's subscription asked for SuperUser is saved as its own; with
"customer created" subscriptions, Inventory (no read on customers) got 0 events,
Service 1. Full suite: 109 passed, 5 skipped, the same 2 known failures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 14:39:40 -07:00
351eb2d851 case 4712 - service schedule needs work order, quote or PM rights
Step 5 of 4712 (security review F16, staff side; contacts are already refused by
step 1). schedule/svc and schedule/user had no role check, so any login got the
whole service schedule (work order numbers, customer names, technicians). They
now need read rights on work orders, quotes or PMs, the test the client uses to
show the Schedule menu, so no role that sees the schedule loses it; only
Inventory and OpsAdmin roles (no menu) are refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 14:25:23 -07:00
443d382394 case 4712 - customer contacts' pick lists limited to their own records
Step 4 of 4712 (security review F15). Pick lists checked only the Select role,
which customer contacts have for most types, and took the columns from the
request, so a contact could list every customer with phones and emails. For
contacts now: the customer list holds only their own customer (head office
contacts: their head office's customers); the unit list only their own
customer's units (head office contacts: the chosen customer, which must be
theirs; none until one is chosen); unit models as before; any other list 403.
pick-list/single names only those records. New CustomerPickList variant for a
single customer. Staff unchanged.

Checked: the contact's customer list went from 72 customers to its own one;
units only its own; vendors and users 403.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 14:14:43 -07:00
692fececc7 case 4716 - unit pick-list customer variant parsed as a number
UnitPickList.GetVariantCriteria put the value of a "customerid:<value>"
variant from the request into the query unparsed, reachable by any login
(security review F43, found during case 4712). The value is now parsed as a
long; anything else means no restriction, as 0 already did. The client's
format ("customerid: 5") still works.

Checked: new test fails on the old code, passes now; pick-list tests 10/10.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 14:08:01 -07:00
1e32d59b8f Security review: F43, unit pick-list variant put into SQL unparsed (found during case 4712)
Also corrects the pass 6 note that all pick-list variants were parsed or matched
to fixed strings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 14:03:12 -07:00
d75c3eecea case 4712 - customer contacts work only with their own service requests and units
Step 3 of 4712 (security review F14). The Customer role has change rights on
service requests and nothing checked whose they were. For customer contacts:
another customer's request is not found (read, update, delete); creating or
moving a request to a customer the contact does not own is refused (403); a
contact's request is always requested by that contact. unit/customerunit adds
units only to the contact's own customer (or one of its head office's).

Checked: another customer's request 404 (was 200), create for another customer
403, own request created and deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 14:02:19 -07:00
bd4494abac case 4712 - customer contacts reach only their own customer's work orders
Step 2 of 4712 (security review F13). The customer work-order rights checked
only tag-based feature switches, never which customer a work order belongs to.
New UserBiz.CustomerContactOwnsCustomerAsync (own customer, or for a head
office contact a customer of its head office), used by:
- workorder/customer/{id}: another customer's work order is not found (404);
- customer work order report (-100): exactly one work order, which must be the
  contact's (the renderer used every id it was given);
- work order attachment list and download for contacts; other attachment types
  are refused to contacts (the customer screens show them only on work orders).

Checked: other customer's work order 404 (was 200); report on another's or on
two work orders not found; own customer report still accepted (202).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 13:52:31 -07:00
2b1adaa496 case 4712 - customer contacts limited to the routes the customer screens use
Customer contacts had the Customer role's Select and read rights on most record
types and could call any route whose staff role check they passed, including
several with no role check (security review F13 to F17). Step 1 of 4712: the
token middleware now answers 403 (same body as other 403s) to Customer and
HeadOffice users for any route not in CustomerContactRoutes, the routes the
customer screens use (read from raven-client). Anonymous routes are always
allowed. New routes stay closed to contacts unless added on purpose.
Ownership of the records these routes return follows in the next steps.

Checked: full integration suite 103 passed, 5 skipped, the same 2 known
failures as the baseline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 13:44:02 -07:00
7b40caec8b case 4681 - done in sockeye: F40 fixed; repeatable fetch and fingerprint noted as a later case
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 12:55:10 -07:00
445f9b0d0a case 4717 - KPI interval accepted only as a known date unit
Six KPIs put the request's interval criterion into the query as
DATE_TRUNC('<interval>', ...) unchecked, reachable by staff down to
TechRestricted (security review F26). New KPIFetcher.DateTruncUnitOrNull
accepts only PostgreSQL's date_trunc unit names (any case); the seven KPIs that
read interval refuse anything else with "Invalid 'interval' criteria", the same
form as their existing missing-criteria errors. The client only sends day or
month. (WorkOrderByStatusList reads interval without using it; checked too.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 12:36:52 -07:00
b1944b7ca3 case 4716 - pick-list search text passed as SQL parameters
PickListSqlBuilder put the search text and the tag search text into the SQL
inside quotes with no escaping, reachable by any login (security review F18);
an apostrophe in a search also made the query fail. The builder now writes
@plq / @pltag and PickListFetcher adds them as Npgsql parameters (same %text%
match; wildcards typed by the user still apply).

Changelog: searching selection lists for text with an apostrophe now works.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 12:28:43 -07:00
e58ab2c100 case 4715 - download file names checked
report/download and export/download (any download key), the log-file routes and
backup download built a path from the file name in the URL with Path.Combine
and no check, so on Windows a name with a folder part or an absolute path could
reach files outside the folder (security review F21). New helper
FileUtil.FullPathInFolderOrNull accepts only a bare file name that resolves
directly inside the folder; TemporaryFileExists and BackupFileExists use it
(the download routes already call them first), and so do both log routes.
A refused name gets the same 404 as a missing file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 12:20:25 -07:00
705a54f9d8 case 4718 - report and translation import need create rights
report/upload and translation/upload had no role check, so any login, customer
contacts included, could import a report template (whose JavaScript runs in the
server's browser at render time) or a translation (security review F19, F24).
Both now require the same right as creating one, checked before the upload is
read. The import screens are already limited to those roles.

Checked locally: customer contact and Service 403, BizAdmin 202.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 12:10:00 -07:00
a843bd3132 case 4711 - JWT signing key generated by the server; AYANOVA_JWT_SECRET removed
The LAN/IIS and Linux templates shipped a placeholder secret printed in the
manual, and installs that kept it signed logins with a public key; any setting
also invited short or reused values (security review F1). The setting is now
ignored (one warning at boot if still present): the server generates a key once
(48 random bytes) into jwt-signing.key in the data folder (beside the app if
there is none; owner-only on Linux) and reuses it, so sign-ins also survive a
restart where the server used to make a new key each boot (standalone, Linux
desktop). If the file cannot be used, a temporary key is used for that run.

Templates and dev launch configs drop the setting; manual updated (key page
rewritten, setting steps removed from install pages); changelog line.

Checked locally: token forged with the old placeholder 401; key file created
and reused after restart (old token 200); auth and 2FA tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 12:01:08 -07:00
5bf1f2700a case 4710 - done in raven and sockeye: F5 and F41 fixed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 11:36:50 -07:00
141bdeb89c case 4710 - 2FA step requires the temp token from the password step
The 2FA step matched the user by temp token alone, and the token was left
empty after a successful 2FA login (null right after turning 2FA on), so an
empty or missing token skipped the password step (security review F5).
Now: an empty or missing temp token is refused before the lookup; the token is
cleared to null after success; after 5 wrong codes the token is cleared, so the
password must be entered again (counted in memory per user).

Changelog: general security notice for self-hosted upgrades, and the one
behaviour change users can see. SECURITY-REVIEW.md: F5 fixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 11:28:17 -07:00
27e187bac6 Release 8.3.0 cases: John's edits of 2026-10-24
4713 takes F28; 4698 folded into 4711; F2 moved into 4579 (now priority 00);
4665 moved to the release after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 10:42:32 -07:00
229fe208a7 Release 8.3.0 cases: Sockeye case 4681 takes F40 and the Sockeye half of F39
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 10:17:04 -07:00
67205ccf41 Release 8.3.0 case list, and security findings mapped to cases
John's "next release" cases (exported 2026-10-24) listed in NET10-PORT.md;
SECURITY-REVIEW.md maps each finding to its case. F28 has no case yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 10:09:59 -07:00
ba60825167 Security review: finding IDs are permanent
Cases will refer to finding numbers, so they are never renumbered or reused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 06:00:25 -07:00
8a54c020c5 Security review: John's decisions on F41, F42 and F32
2FA turned off on the affected accounts until the F5/F41 fix; F42 key not
rotated now, moving it out of source is a later case; FastMail password
rotation after the old Sockeye server is retired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 21:15:16 -07:00
d6a7e024f0 Security review pass 11: Sockeye public routes; all passes done
From code: the 2FA flaw (F5) is also in Sockeye and the business server (F41,
critical; mitigation: turn 2FA off until fixed); the licence-signing private
keys are hardcoded in Sockeye's source (F42); one-shot licence fetch by DB ID
(F39); trial request routes without rate limits (F40). Review only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 20:51:03 -07:00