Images longer than MaxSide (65535) pixels on either side are now rejected
before decoding, like images over the pixel limit.
Attachments with such images are still stored; only their preview is not
generated. Project backgrounds and avatars reject them with the existing
image too large error.
- Reject a config that maps a column more than once, already when the
import is started or previewed.
- Import repeated labels of a row once and reject rows with more than
MaxLabelsPerTask (100) labels.
- Count items while converting rows so a file over the import item limit
fails before the whole structure is built.
- Walk mappings sorted by column so a row only visits the cells it has.
Every VALARM repeats the task title and the output was built with string
concatenation, so rendering cost grew quadratically with the number of
reminders. Build it with a strings.Builder instead.
Tasks may now have at most MaxRemindersPerTask (100) reminders and a
title of at most MaxTaskTitleLength (1000) characters, checked through
v1, v2 and CalDAV. On update the reminder limit only applies when the
count grows and the title limit only when the title changes, so existing
tasks stay editable.
Rowspan/colspan and short-row padding make both converters materialize
far more cells than the input contains. HTML to markdown now renders
tables past MaxTableCells as plain rows, so stored descriptions still
read fine. Markdown to HTML rejects such input, which the v2 API reports
as invalid data.
The business day offsets in the date values of a task or time entry
filter may now add up to at most MaxDatemathBusinessDays (10000). Larger
filters are rejected with the invalid filter value error before any value
is evaluated. The frontend date math parser applies the same limit.
An empty find_action result looked identical to "not authorized", so
agents guessing a wrong resource prefix (task_relations instead of
tasks_relations) concluded their token lacked permission.
Refs #4342
Implement the approved last_purged_at decision with an advancing purge generation so same-second purges invalidate old tokens and full resync tokens remain usable.
Checking the old password directly skipped the failed-attempt counter that login and the email-change prompt use. Extract CheckUserPasswordCountingFailures from CheckUserCredentials and use it for the password change too.
Mirrors the TOTP lockout: issue a password reset token, send a mail, set
the account to locked and revoke its sessions. Counts every wrong password
checked by CheckUserCredentials, including the password and email change
re-auth prompts, so a stolen session can no longer guess the password
indefinitely.
The 3-attempt warning mail no longer resets the counter, otherwise it
could never reach the lockout threshold.
legal.links contains the deprecated imprint and privacy urls first, then the
configured links sorted by key. imprint_url and privacy_policy_url stay in the
response for older clients and are marked deprecated.
Address review decision: the post-commit avatar sync still blocked the
LDAP/OIDC login response for the download and up to 30s of decode
queueing. It now runs in a goroutine on a context that outlives the
request, with panics recovered and failures logged.
Address review decision: SaveAvatar removed the old file's blob from
storage inside the transaction, so a later rollback left the user on an
avatar whose content was gone. The files row is still deleted in the
session; the blob and its previews go once the session commits.
Address review decision: the provider avatar was decoded inside the login
transaction after the user row was written, holding it while queued for
decode capacity, and a later rollback (TOTP, disabled user) discarded the
work. Logins now commit first; the avatar is decoded without a session
and saved in its own short transaction.
Address review decision: a background upload queued for decode capacity
held its transaction for up to 30s. The permission check now runs in a
short read session, the image is decoded and encoded without a session,
and a fresh write session re-checks permission and stores the result.
Address review decision: a decode queued for capacity held the request's
transaction for up to 30s, so on SQLite a concurrent write made the
upload fail with database is locked. Uploads and avatar reads now decode
and encode first; only the encoded PNG enters the transaction.
Address pr-swarm finding: StoreUploadedAvatar ran its own header check
without the read limit, and anything it let through that Decode then
rejected (truncated data, header past 12 MiB) answered 500.
Project background uploads (and migrated backgrounds) are decoded through
imageutils.Decode, which also covers the blur hash computed from the
decoded image. A busy server answers 503. Drops the unused CreateBlurHash,
which decoded without any bound.
Uploaded avatars are decoded through imageutils.Decode on upload and when
resizing per size, so a busy server answers 503 instead of decoding in
parallel. The old avatar file is removed only after the new one decoded,
since a storage delete is not rolled back with the session.
Colliding non-CRUD routes got a method suffix in registration order, per
version, so one key granted different actions on v1 and v2: a read-only
projects.background scope deleted backgrounds through v2, and bucket
create/update keys swapped meaning (GHSA-j5v7-qcwp-5g32). Keys now derive
from the full v1+v2 route set and carry the action (read/create/update/
delete) once a subkey has more than one. Marking all notifications as
read gets its own key on both versions instead of overwriting update.