3430 Commits

Author SHA1 Message Date
Christopher Broderick
5c2808dec2 Merge pull request #1457 from beemines/feat/copy-followup
feat: copy follow-ups to related tickets
2026-10-06 12:44:09 +01:00
Christopher Broderick
ae6ec10ffd Merge branch 'main' into feat/copy-followup 2026-10-06 12:39:50 +01:00
Bee mines
4bf67418dd docs: remove trailing blank line 2026-10-06 17:41:05 +08:00
Bee mines
e8fff88387 docs: use setting directive for follow-up copy option 2026-10-06 17:40:40 +08:00
Bee mines
8f6f71372c feat: make follow-up copying opt-in per deployment 2026-10-06 15:53:45 +08:00
dependabot[bot]
8d98cc6244 chore(deps): bump the frontend-minor-patch group with 2 updates
Bumps the frontend-minor-patch group with 2 updates: [datatables.net](https://github.com/DataTables/DataTablesSrc) and [datatables.net-bs5](https://github.com/DataTables/DataTablesSrc).


Updates `datatables.net` from 3.1.2 to 3.1.3
- [Release notes](https://github.com/DataTables/DataTablesSrc/releases)
- [Commits](https://github.com/DataTables/DataTablesSrc/compare/3.1.2...3.1.3)

Updates `datatables.net-bs5` from 3.1.2 to 3.1.3
- [Release notes](https://github.com/DataTables/DataTablesSrc/releases)
- [Commits](https://github.com/DataTables/DataTablesSrc/compare/3.1.2...3.1.3)

---
updated-dependencies:
- dependency-name: datatables.net
  dependency-version: 3.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-minor-patch
- dependency-name: datatables.net-bs5
  dependency-version: 3.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-05 09:00:04 -10:00
dependabot[bot]
7828494471 chore(deps): bump the frontend-minor-patch group with 5 updates
Bumps the frontend-minor-patch group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [datatables.net](https://github.com/DataTables/DataTablesSrc) | `3.0.4` | `3.1.2` |
| [datatables.net-bs5](https://github.com/DataTables/DataTablesSrc) | `3.0.4` | `3.1.2` |
| [datatables.net-buttons](https://github.com/DataTables/Buttons) | `4.0.3` | `4.1.2` |
| [datatables.net-buttons-bs5](https://github.com/DataTables/Buttons) | `4.0.3` | `4.1.2` |
| [datatables.net-buttons-dt](https://github.com/DataTables/Buttons) | `4.0.3` | `4.1.2` |


Updates `datatables.net` from 3.0.4 to 3.1.2
- [Release notes](https://github.com/DataTables/DataTablesSrc/releases)
- [Commits](https://github.com/DataTables/DataTablesSrc/compare/3.0.4...3.1.2)

Updates `datatables.net-bs5` from 3.0.4 to 3.1.2
- [Release notes](https://github.com/DataTables/DataTablesSrc/releases)
- [Commits](https://github.com/DataTables/DataTablesSrc/compare/3.0.4...3.1.2)

Updates `datatables.net-buttons` from 4.0.3 to 4.1.2
- [Commits](https://github.com/DataTables/Buttons/compare/4.0.3...4.1.2)

Updates `datatables.net-buttons-bs5` from 4.0.3 to 4.1.2
- [Commits](https://github.com/DataTables/Buttons/compare/4.0.3...4.1.2)

Updates `datatables.net-buttons-dt` from 4.0.3 to 4.1.2
- [Commits](https://github.com/DataTables/Buttons/compare/4.0.3...4.1.2)

---
updated-dependencies:
- dependency-name: datatables.net
  dependency-version: 3.1.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-minor-patch
- dependency-name: datatables.net-bs5
  dependency-version: 3.1.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-minor-patch
- dependency-name: datatables.net-buttons
  dependency-version: 4.1.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-minor-patch
- dependency-name: datatables.net-buttons-bs5
  dependency-version: 4.1.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-minor-patch
- dependency-name: datatables.net-buttons-dt
  dependency-version: 4.1.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-28 09:33:05 -10:00
Benbb96
ff0b160c2b fix: enforce allowed URL schemes on rendered markdown
Fixes GHSA-rrx8-xf85-mqwg, reported by @hhesenjan.

get_markdown() checked ALLOWED_URL_SCHEMES with a regular expression on its
input that only knew inline links. Reference links and images were never
checked, and an HTML entity in the scheme hid it from the expression, so a
forbidden scheme could reach an href or src.

The rendered HTML now goes through nh3 with url_schemes set to
ALLOWED_URL_SCHEMES, which covers every markdown syntax. The input check is
kept, so inline links still lose their scheme rather than being dropped.
Link and image titles and the fenced code language class are allowed
through. <br /> is now serialised as <br>, and tests/test_markdown.py is
updated for it.
2026-09-28 14:29:38 +02:00
Benbb96
981f52c7f1 fix: stop running urlize over rendered markdown
Fixes GHSA-f4gw-75qw-v2hf, reported by @hhesenjan.

The templates passed the output of get_markdown() through urlizetrunc.
urlize is not HTML aware and rewrote the inside of attribute values, so a
crafted ticket could break out of an attribute and add an event handler to
the staff ticket page. It fires on Django 5.2 and earlier; 6.0 hardened
urlize.

urlizetrunc is removed from the description, the resolution and the
follow-up comments. Markdown already turns URLs into links. The urlize calls
in public_view_ticket.html run over plain text and are unchanged.

The tests parse the page and assert that no element carries an on*
attribute. They only fail without the fix on Django 5.2.
2026-09-28 14:29:05 +02:00
Bee mines
ab8bc74c14 refactor: reuse the follow-up editor for copying 2026-09-28 19:48:47 +08:00
Christopher Broderick
68bbd57663 Merge pull request #1458 from DavidVadnais/fallback_for_respond_layout
fallback to tabs if typo in ticket_respond_layout
2026-09-27 14:04:20 +01:00
Benbb96
0e8d0f1189 test: pin the user tickets match as case sensitive
Nothing verifies either address, so one differing only in case identifies a
different mailbox as far as this endpoint can tell. Switching the lookup to
__iexact would read as a usability fix and would widen the collision it was
fixed for, so the exact match is deliberate and now tested.
2026-09-27 14:55:56 +02:00
Benbb96
aedda361e8 fix: do not treat a missing email address as an identity
Fixes GHSA-pc66-g337-vfmf, reported by @mansurmavlankulov.

UserTicketViewSet answers "the tickets submitted by the current user" by
matching Ticket.submitter_email against User.email, behind IsAuthenticated
alone. Neither field is verified and both can be empty, so an account with no
address was the submitter of every ticket with no address, and read them in
full through the API.

The model default for submitter_email is NULL, which does not collide, but
Django's EmailField form field returns "" for empty input and the ModelForm
stores that in the nullable column. Every ticket submitted through a form
without an address is therefore exposed, which is the ordinary path rather than
an edge case.

An empty address now returns nothing rather than matching, and empty submitter
addresses are excluded whatever the caller's address is, so neither side of the
comparison can be the sentinel.

This is not the per-queue gap fixed in GHSA-g9r9-54m2-m6g5. That one was about
the queue permission model not being consulted by three other viewsets, needed
a staff account and per-queue mode enabled. This one needs neither, and the
queryset was already scoped, just on a key that identifies nobody.

tests/test_user_tickets_api.py covers both directions, and pins the premise
separately: a ticket submitted through the form with the field left blank
stores "" rather than NULL, which is what makes the collision reachable.
2026-09-27 14:55:56 +02:00
DavidVadnais
e7f1ae6f8b fallback to tabs if typo in ticket_respond_layout 2026-09-26 13:08:29 -10:00
Bee mines
5c9ec4abbd feat: copy follow-ups to related tickets 2026-09-26 17:24:26 +08:00
Christopher Broderick
a3d61c598a Merge pull request #1456 from DavidVadnais/add-migration-for-codespell
codespell commit afd98bd changed a model description without makemigr…
2026-09-24 21:37:33 +01:00
DavidVadnais
76a97396ed codespell commit afd98bd changed a model description without makemigrations run 2026-09-24 09:47:42 -10:00
DavidVadnais
184003f7eb UserSetting: followup page layout 2026-09-23 11:24:42 -10:00
DavidVadnais
f5f005e001 Followup: hover how long ago sent to get date 2026-09-23 09:01:23 -10:00
DavidVadnais
90f013bb0a ticket.html: shows the recipient list below the public checkbox 2026-09-22 11:31:56 -10:00
DavidVadnais
1fd20bb1ea Backlink only once 2026-09-21 11:03:46 -10:00
DavidVadnais
0e7a972120 Create HELPDESK_ENABLE_BACKLINKS setting 2026-09-21 11:03:46 -10:00
DavidVadnais
581669b5ba Create followup backlink capability 2026-09-21 11:03:46 -10:00
DavidVadnais
68f911bce3 adds three CSS rules that style the DataTables column visibility dropdown 2026-09-21 10:31:19 -10:00
David Vadnais
4084493a49 Merge pull request #1439 from gurupratap-matharu/enhancement/improve-email-ignore-templates
Enhancement/improve email ignore templates
2026-09-20 19:48:51 -10:00
Gurupratap Matharu
d874e19075 Use legend instead of generic label for multiple checkboxes 2026-09-21 11:04:16 +05:30
Gurupratap Matharu
362a0bf65a Rename id to item_id in url arguments for email ignore delete 2026-09-21 11:04:16 +05:30
David Vadnais
cfcb5fc36c Merge branch 'main' into enhancement/improve-email-ignore-templates 2026-09-19 08:28:13 -10:00
David Vadnais
7cb9175fbd Merge pull request #1442 from ezra-varady/fix-sorting
fix column sorting on undisplayed columns
2026-09-19 08:21:11 -10:00
Ezra Varady
777d2e4908 fix column sorting on undisplayed columns as well as some misc dark mode css 2026-09-18 16:09:47 -10:00
Gurupratap Matharu
3a6ad4d0b6 Remove redundant decorators to check superuser status 2026-09-18 11:30:42 +05:30
Gurupratap Matharu
42d5400079 Add missing breadcrumbs and format buttons 2026-09-18 11:30:25 +05:30
Gurupratap Matharu
4c49a2047d Unit tests for email ignore list-add-delete views 2026-09-18 11:30:05 +05:30
Christopher Broderick
8b7ec81931 Merge pull request #1437 from DavidVadnais/fix-issue-#1436
ticket.html: move sorting followups inline with tabs
v2.5.0
2026-09-17 22:51:43 +01:00
DavidVadnais
d057bd1573 ticket.html: move sorting followups inline with tabs 2026-09-17 11:44:04 -10:00
David Vadnais
b7f916ba2f Merge pull request #1435 from gurupratap-matharu/feature/password-reset
Feature/password reset
2026-09-17 11:36:55 -10:00
Gurupratap Matharu
5b2dc0b9bf Use widgets for EmailIgnoreForm and format fieldset label 2026-09-17 12:39:48 +05:30
Gurupratap Matharu
8ea58d5469 Display a simple list of links for better UX 2026-09-17 12:38:50 +05:30
Gurupratap Matharu
1425a65f0e Show success message on create - delete action 2026-09-17 12:38:00 +05:30
Gurupratap Matharu
36b9490c84 Beautify email ignore list - add- del templates 2026-09-17 12:37:38 +05:30
Gurupratap Matharu
eaba54d699 Use bootstrap5 template tag and remove Js hack 2026-09-17 10:59:23 +05:30
Christopher Broderick
ee8de7dd1f Merge branch 'main' into feature/password-reset 2026-09-16 11:14:17 +01:00
Gurupratap Matharu
545eecb08f Add new settings to docs for clarity 2026-09-16 12:00:16 +05:30
Gurupratap Matharu
6a24cfc9d4 Fix login url and show forgot password link based on setting value 2026-09-16 12:00:01 +05:30
Gurupratap Matharu
91760e7a53 Introduce new settings to enable password reset 2026-09-16 11:59:12 +05:30
Benbb96
f54705ed0a fix: stop leaking and rendering pre-set reply bodies in raw_details
Fixes GHSA-6pjw-93c8-87fj, reported by @VidhanThakur17.

The /raw/preset/ endpoint looked a PreSetReply up by primary key alone and
returned its body with the default text/html content type. Any staff member
could therefore read a reply attached to a queue they have no access to, and
a reply body holding markup was executed in the browser of whoever displayed
it, even though the only caller feeds the response into a textarea.

The body is now served as text/plain with nosniff so it cannot be interpreted
as a document, and a non numeric id, which used to raise a ValueError out of
the lookup, is a 404 like any other unknown id.

The lookup is restricted to replies that are either global or attached to one
of the queues accessible_queues() returns, so it follows can_access_queue()
rather than the wider set the queue pickers are offered. get_queues() would
have kept every queue accepting public submissions readable, which is the
distinction the public submission test pins down. A reply attached to several
readable queues is matched once, hence the distinct().

With per-queue permissions off, or for a superuser, the filter keeps every
reply, so the default configuration is unchanged.
2026-09-15 10:39:33 +02:00
Benbb96
e512303497 fix: hash the password before the first insert on /api/users/
ModelSerializer.create() passes validated_data to objects.create() with the
password still in it, so the INSERT carried the plain text and set_password()
only overwrote it in a second statement. The first row is already in the
write-ahead log, the binary log, the replicas and any query log by then, and
nothing that comes after removes it there.

Popping the password out, building the instance and hashing before the single
save keeps the plain text out of every statement. The regression test reads
back the SQL the request executed and looks for it.
2026-09-15 10:38:50 +02:00
Benbb96
fd2591991f fix: require add_user permission and validate passwords on /api/users/
Creating an account through the API was guarded by IsAdminUser alone, which
tests is_staff and nothing else. is_staff is the flag admitting an account to
the admin site, not the right to create users, so the endpoint accepted
requests that /admin/auth/user/add/ refuses from the same account. Adding
DjangoModelPermissions brings the model permission back into the check. It
reads the permission off the view queryset, so a project with its own
AUTH_USER_MODEL is covered too, and IsAdminUser stays alongside it so holding
the permission is not on its own a way in.

UserSerializer set the password with set_password(), which hashes whatever it
is handed. Every AUTH_PASSWORD_VALIDATOR a deployment configured therefore
applied everywhere a password is set except here. The serializer now runs
validate_password() against an unsaved user carrying the rest of the submitted
fields, so validators comparing a password to its own account have the username
and email to work from, and a rejected password comes back as a 400 on the
password field.

Deployments calling this endpoint with a staff-only account will start getting
403 and need the permission granted.

Reported by @VidhanThakur17.
2026-09-15 10:38:50 +02:00
Christopher Broderick
ff5f075f47 Merge pull request #1431 from gurupratap-matharu/hotfix/followup-card-colors
Hotfix/followup card colors
2026-09-15 09:36:39 +01:00
Christopher Broderick
b392a92a50 Merge branch 'main' into hotfix/followup-card-colors 2026-09-15 09:23:44 +01:00