Commit Graph

1061 Commits

Author SHA1 Message Date
kolaente
50c3eebd23 test: add failing test for cross-project task relation info disclosure 2026-03-23 16:26:37 +00:00
kolaente
212968cec4 chore(lint): suppress additional gosec false positives
Add #nosec comments for G703/G704 findings in db, doctor, webhooks,
gravatar, unsplash, and migration helper code.
2026-03-23 16:40:07 +01:00
kolaente
2053426062 chore(lint): suppress known gosec false positives
Add config-level exclusions for G117 (secret-named struct fields),
G101 in test files, G702/G704 in magefile, and goheader in plugins.
Add inline #nosec comments for specific G703/G704 false positives
in export, dump/restore, migration, and avatar code.
2026-03-23 16:23:15 +01:00
kolaente
d0606eadea fix: check child project's own IsArchived flag in CheckIsArchived
CheckIsArchived() previously skipped checking a child project's own
IsArchived flag when ParentProjectID > 0, immediately recursing to
only check the parent. This allowed write operations on individually
archived child projects whose parent was not archived.

Now the function loads the project from the database first, checks its
own IsArchived flag, and only then recurses to check parent projects.
2026-03-23 14:13:53 +00:00
kolaente
8409bdb120 refactor(user): export IsErrUserStatusError for use across packages
Make isErrUserStatusError public and replace all verbose
!IsErrAccountDisabled(err) && !IsErrAccountLocked(err) checks
with the shorter IsErrUserStatusError(err) call.
2026-03-23 12:06:16 +00:00
kolaente
ea4ba18def fix(user): handle status errors across the codebase, remove redundant checks 2026-03-23 12:06:16 +00:00
kolaente
a66bda2f51 test: register totp fixture in test setup 2026-03-20 12:22:27 +00:00
kolaente
49419619bd fix: only enforce task_id check when TaskID is provided
Internal callers (reactions) look up comments by ID without knowing
the task. The IDOR protection is still effective because ReadOne
always has TaskID set from the URL parameter.
2026-03-20 11:41:28 +00:00
kolaente
bc6d843ed4 fix: verify comment belongs to task in URL to prevent IDOR
Add task_id check to getTaskCommentSimple so that a comment can only
be loaded if it actually belongs to the task specified in the URL.
Previously, any valid comment ID could be read through any accessible
task endpoint.

Ref: GHSA-mr3j-p26x-72x4
2026-03-20 11:41:28 +00:00
kolaente
be0aaa7060 fix: adapt image preview DoS protection to new FileStorage interface
File.File is now io.ReadCloser (no Seek). Buffer the file bytes
once via io.ReadAll, then use bytes.NewReader for both DecodeConfig
and Decode. Test updated to use io.NopCloser instead of afero.
2026-03-20 11:34:41 +00:00
kolaente
af61d0f1a0 fix: reject images exceeding 50M pixels before decode 2026-03-20 11:34:41 +00:00
kolaente
f7592e2cfd test: add failing test for image preview with oversized dimensions 2026-03-20 11:34:41 +00:00
kolaente
89923ebe70 fix: update test expectations for new disabled user fixture
- TestListUsers expects 17 users (was 16)
- TestCleanupOldTokens expects 3 old tokens deleted (was 2)
2026-03-20 11:23:21 +00:00
kolaente
17eccd848f test: add FileStat assertion to validate storage path in attachment test 2026-03-20 10:59:44 +01:00
kolaente
0e1f44e57e refactor: replace afero with FileStorage interface
Replace the github.com/spf13/afero dependency with a purpose-built
FileStorage interface (Open, Write, Stat, Remove, MkdirAll) with three
implementations: localStorage (with basePath), s3Storage (with key
prefix), and memStorage (for tests).

Each implementation owns its base path — callers pass only file IDs.
Delete s3fs.go, change File.File from afero.File to io.ReadCloser,
and fix duplication flows to buffer content for seeking.
2026-03-20 10:59:44 +01:00
kolaente
8d9bc3e65e feat(webhooks): add built-in SSRF protection using daenney/ssrf
Block webhook requests to non-globally-routable IP addresses by default.
Uses net.Dialer.Control hook to validate resolved IPs against IANA
Special Purpose Registries after DNS resolution, preventing DNS rebinding.

Configurable via webhooks.allownonroutableips (default: false).
2026-03-19 15:18:06 +01:00
kolaente
d5dbf04bd0 test(webhooks): add SSRF protection tests 2026-03-19 15:18:06 +01:00
Tink
d11f097eee fix(tasks): support both expand and expand[] query parameter formats (#2415)
The `expand` query parameter only supported the `expand[]=foo` array
format, but the swagger docs described it as a plain string parameter.
This adds support for both formats (`expand=foo` and `expand[]=foo`),
matching the existing pattern used by `sort_by` and `order_by`
parameters.

Closes #2408

---------

Co-authored-by: kolaente <k@knt.li>
2026-03-19 09:18:11 +00:00
kolaente
e19bea8e3a fix: register bulk label route correctly for API token permissions
The tasks_labels_bulk route was not recognized as a CRUD route by
isStandardCRUDRoute, causing it to be processed as a non-CRUD route
and registered in the wrong apiTokenRoutes group. API tokens with
tasks_labels permissions could not access the bulk endpoint, resulting
in a 401 error.

Fixes https://github.com/go-vikunja/vikunja/issues/2375
2026-03-10 23:58:44 +01:00
kolaente
554593cdb6 test: add failing test for bulk label API token route registration 2026-03-10 23:58:44 +01:00
kolaente
79a612aa5d fix: send account deletion notification before deleting user row
When deleting a user via CLI (`vikunja user delete <id> -n`), the user
row was deleted first, then `notifications.Notify` was called. But
`Notify` calls `User.ShouldNotify()` which queries the database to check
the user's status — and since the row was already deleted within the same
transaction, it returned `ErrUserDoesNotExist`.

Move the notification call before the `DELETE` so the user row still
exists when `ShouldNotify` checks it.

Closes go-vikunja/vikunja#2335
2026-03-10 23:44:53 +01:00
kolaente
dbbc80aea6 feat: extend WebhookListener for user-level webhooks
Add User field to reminder and overdue events so the webhook listener
can look up user-level webhooks. Add conditional user filtering to
reminder and overdue cron jobs - when only email is enabled, filter to
email-enabled users; when webhooks are enabled, fetch all users so
events can be dispatched. Dispatch TasksOverdueEvent and
TaskReminderFiredEvent for webhook consumption.
2026-03-08 19:45:53 +01:00
kolaente
d4577c660f feat: add user_id to webhooks and user-directed event infrastructure
Add user_id column to webhooks table (nullable, for user-level webhooks
vs project-level). Extend webhook model, permissions, and listener to
support user-level webhooks that fire for user-directed events like
task reminders and overdue task notifications.

Add TasksOverdueEvent for dispatching overdue notifications via webhooks.
Update webhook permissions to handle both user-level and project-level
ownership. Add webhook test fixture and register webhooks table in test
fixture loader.
2026-03-08 19:45:53 +01:00
kolaente
aacf650ec2 test: add tests for conversational email system
Add tests for conversational header generation, HTML rendering
with p-tag wrapping, plain-text conversion, footer handling,
and conditional action links. Update mention test fixtures
with Project field.
2026-03-08 16:03:47 +01:00
kolaente
b3572c5932 feat: convert notifications to conversational email style
Convert task comment, mention, assignment, and reminder notifications
to use the conversational email format. Add Project field to notification
structs, include task identifiers in subjects and headers, add doer
avatars, notification settings links in footers, and From headers.
2026-03-08 16:03:47 +01:00
kolaente
c7c63e8ead test: add result count assertions for ParadeDB search tests
Address review feedback: assert exact result counts when ParadeDB is
active. fuzzy(1, prefix=true) broadens matches via edit distance,
returning 6 projects for "TEST10", 14 tasks for "number #17", and
12 projects for "Test1".
2026-03-05 13:57:05 +01:00
kolaente
b69705e64b test: fix lint and adjust project search test for ParadeDB fuzzy matching
- Use require.NotEmpty instead of require.Greater for testifylint
- Skip exclusion assertions in web project search test when ParadeDB is
  active, since fuzzy(1, prefix=true) on "Test1" also matches Test2, Test3
2026-03-05 13:57:05 +01:00
kolaente
6268c48f15 test: adjust ParadeDB search tests for fuzzy prefix match broadening
ParadeDB fuzzy(1, prefix=true) returns more results than ILIKE due to
edit-distance tolerance on tokenized terms. Adjust assertions to check
containment rather than exact result sets when ParadeDB is active.
2026-03-05 13:57:05 +01:00
kolaente
3568aaacee test: add task #48 to expected results in feature tests
The new fixture task #48 (Landingpages update, project 1) needs to
appear in all feature test expected result sets that list project 1
tasks. Also bumps the expected next index in TestTask_Create.
2026-03-05 13:57:05 +01:00
kolaente
7288483879 fix: handle deleted user in saved filter view event listener
Use a separate error variable for the user lookup in
UpdateTaskInSavedFilterViews so that ErrUserDoesNotExist does not
pollute the named return `err`. The `:=` inside the for loop shadowed
the outer `err`, leaving it set to the stale user-not-found error,
which caused the handler to be poisoned.

Closes #2359
2026-03-04 22:05:01 +01:00
kolaente
54c7c4aef2 refactor: move ListUsers tests from pkg/user to pkg/models
The ListUsers function now references team_members and teams tables
via a subquery for external team discoverability. The pkg/user test
environment only syncs user tables, so these tests need to run in
pkg/models which has the full schema and all fixtures.

Also adds new tests for the external team discoverability bypass
directly in the models package alongside the moved tests.
2026-03-04 20:32:11 +01:00
kolaente
9c23e19644 fix: preserve cover image when duplicating task
Track old-to-new attachment ID mapping during duplication and
re-set CoverImageAttachmentID on the new task if the original
had a cover image configured.
2026-03-04 17:20:26 +01:00
kolaente
7aad96b199 fix: close source file handle when duplicating attachments
Save the source file handle before calling NewAttachment (which
overwrites attachment.File) and use defer to ensure it gets closed.
This prevents file descriptor leaks on both success and error paths.
2026-03-04 17:20:26 +01:00
kolaente
692357a648 refactor: use TaskRelation.Create for copy relation
Use the TaskRelation Create method instead of raw inserts. This
handles the bidirectional relation automatically and dispatches
the appropriate event.
2026-03-04 17:20:26 +01:00
kolaente
e07eeed211 refactor: batch label inserts during task duplication
Collect all LabelTask records in the loop and insert them in a single
database call instead of inserting one at a time.
2026-03-04 17:20:26 +01:00
kolaente
6da0f68562 fix: remove debug log statements from task duplicate
Remove all log.Debugf calls from the Create method as they were
leftover development aids and should not be in production code.
2026-03-04 17:20:26 +01:00
kolaente
d8f3a96b06 feat: add task duplicate backend model and tests 2026-03-04 17:20:26 +01:00
kolaente
3dd2ba4aa4 feat: register Vikunja tables with db package at init 2026-03-04 15:37:54 +01:00
kolaente
18f16878a8 fix: prevent nil pointer panic in mention notification listeners
When a task bucket is updated without changing buckets (early return in
updateTaskBucket), b.Task remains nil. The TaskUpdatedEvent was then
dispatched with a nil Task, causing a nil pointer dereference in
HandleTaskUpdatedMentions when accessing event.Task.Description.

This adds:
- A nil guard in TaskBucket.Update to skip event dispatch when b.Task is nil
- Nil checks in HandleTaskUpdatedMentions, HandleTaskCreateMentions,
  HandleTaskCommentEditMentions, and UpdateTaskInSavedFilterViews
- Tests verifying the handlers gracefully handle nil task events

Closes #2351
2026-03-04 10:29:16 +01:00
kolaente
f516bbe560 test: update event assertions to work with deferred dispatch
Tests that call model methods directly now call events.DispatchPending
before asserting event dispatch.

Refs #2315
2026-03-03 12:46:34 +01:00
kolaente
1f363dbd43 fix(events): defer event dispatch for user creation and task positions
Refs #2315
2026-03-03 12:46:34 +01:00
kolaente
8afbdf2deb fix(events): defer event dispatch for team operations
Convert events.Dispatch to events.DispatchOnCommit in team and team
member CRUD. Also removes premature s.Commit() from TeamMember.Delete
since the handler manages the transaction lifecycle.

Refs #2315
2026-03-03 12:46:34 +01:00
kolaente
dd7f7de518 fix(events): defer event dispatch for project operations
Convert events.Dispatch to events.DispatchOnCommit in project CRUD,
project-user sharing, and project-team sharing.

Refs #2315
2026-03-03 12:46:34 +01:00
kolaente
fe459c9297 fix(events): defer event dispatch for task sub-entities
Convert events.Dispatch to events.DispatchOnCommit in task assignees,
comments, attachments, relations, and kanban bucket operations.

Refs #2315
2026-03-03 12:46:34 +01:00
kolaente
3eb289262f fix(events): defer task event dispatch until after transaction commit
Convert events.Dispatch to events.DispatchOnCommit in Task.Create,
updateSingleTask, Task.Delete, and triggerTaskUpdatedEventForTaskID.
Events are now dispatched by the handler after s.Commit(), ensuring
webhook listeners see committed data.

Fixes #2315
2026-03-03 12:46:34 +01:00
Weijie Zhao
3ca4913fcb fix: use MinPositionSpacing threshold in calculateNewPositionForTask (#2320)
calculateNewPositionForTask only checked for lowestPosition == 0 before
triggering a full position recalculation. Extremely small position
values (e.g. 3.16e-285) passed this check, causing new tasks to get
meaningless positions via the index * 2^16 fallback, breaking sort
order.

Use the same < MinPositionSpacing threshold that
createPositionsForTasksInView and the position update handler already
use.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-02 09:14:38 +01:00
kolaente
23d84e7811 fix(views): assign default position when creating new project views
When creating a new view without specifying a position, it defaulted to
0, causing it to always sort before all other views. Apply
calculateDefaultPosition to assign a unique position based on the view
ID, consistent with how projects, tasks, and buckets handle this.

Fixes go-vikunja/vikunja#2319
2026-03-02 08:35:35 +01:00
kolaente
3d5ad73d4f fix(filter): recover from datemath panic on malformed date filter values
The go-datemath lexer panics with "scanner internal error" when given
certain malformed inputs like "no" (it starts recognizing "now" but
hits EOF). Wrap datemath.Parse in a recover so the panic becomes a
regular error, allowing the fallback date parser to handle it gracefully.

Closes go-vikunja/vikunja#2307
2026-02-26 16:09:13 +01:00
kolaente
f3ac0574c0 fix(auth): use checked type assertions for all JWT claims 2026-02-25 13:01:00 +01:00
kolaente
b6155d525c feat(cli): reorganize repair commands under unified 'vikunja repair' parent (#2300)
Consolidate four scattered repair/maintenance CLI commands into a unified `vikunja repair` parent command with subcommands.
2026-02-25 11:50:09 +00:00