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>
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>
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>