Commit Graph

1093 Commits

Author SHA1 Message Date
kolaente ac76bce5cd fix: use recursive CTE in accessibleProjectIDsSubquery for inherited project permissions
Resolves #2490. Users with team access on a parent project were not seeing
subtask relations for tasks in child projects because getUserProjectsStatement
does not walk the project hierarchy. The fix wraps the base query in a
recursive CTE that traverses child projects via parent_project_id.
2026-04-05 12:20:35 +00:00
kolaente 6299bea794 fix(mail): guard log calls in GetMailDomain and fix hostname-dependent tests
GetMailDomain called log.Warningf which panics when the logger is not
initialized (e.g. in unit tests). Add log.IsInitialized() guard.

Also fix TestGetThreadID tests that hardcoded "vikunja" as the expected
fallback domain - on CI the os.Hostname() fallback produces a different
value. Tests now dynamically compute the expected domain.
2026-04-03 18:30:39 +00:00
kolaente 5249366aa3 refactor(models): use shared GetMailDomain in getThreadID 2026-04-03 18:30:39 +00:00
kolaente a628c99006 test: assert position existence instead of conditional skip
Replace if-exists guard with require.True assertion so a
missing position correctly fails the test.
2026-04-03 17:26:55 +00:00
kolaente ce3e56f192 refactor: use nested map for position conflict tracking
Replace struct key map[viewPos]bool with nested
map[int64]map[float64]bool for cleaner lookups.
2026-04-03 17:26:55 +00:00
kolaente 104c8eadae fix: use InDelta for float comparison in tests 2026-04-03 17:26:55 +00:00
kolaente 0c3d01099f fix: detect and resolve position conflicts during task creation
Fixes #2495, fixes #2400

When tasks are created, setTaskInBucketInViews() assigns positions
without checking for conflicts. If two tasks get the same position value,
their order scrambles on reload. This adds a conflict check after
bulk-inserting positions, reusing the existing resolution logic from the
update path.
2026-04-03 17:26:55 +00:00
kolaente c6e79926f0 fix: add position conflict resolution for batch-inserted positions
Add resolvePositionConflictsAfterInsert() which checks newly inserted
task positions for duplicate position values within the same view and
resolves them using existing conflict resolution logic.
2026-04-03 17:26:55 +00:00
kolaente 88c2f0a289 fix(project): remove non-existent columns from UpdateProject column list
The UpdateProject function referenced done_bucket_id and default_bucket_id
in its column update list, but these columns belong to the project_views
table, not the projects table. This caused SQL errors when archiving or
updating a project on MySQL/PostgreSQL.

Also adds a test for archiving a non-archived project.

Fixes #2459
2026-04-03 16:59:05 +00:00
kolaente e7b693c788 fix(tasks): include tasks with deleted parents in subtask-expanded queries
When expand[]=subtasks is used, the LEFT JOIN on task_relations filters
out same-project subtasks. If a parent task was deleted, the JOIN
produces NULL for parent_tasks.id/project_id, and since NULL != value
evaluates to NULL (not TRUE) in SQL, these tasks were incorrectly
excluded.

Add builder.IsNull{"parent_tasks.id"} to the OR condition so tasks
whose parent was deleted are still included in results.
2026-04-02 16:30:23 +00:00
kolaente 6b225bb0ba test: add tests for API token expiry notifications and cron 2026-03-30 12:28:15 +00:00
kolaente f308584033 feat: add cron job for API token expiry notifications 2026-03-30 12:28:15 +00:00
kolaente 8ea0dd1610 feat: add API token expiry notification types 2026-03-30 12:28:15 +00:00
kolaente 9884d933fc refactor: extract shared API token validation into ValidateTokenAndGetOwner 2026-03-30 12:09:53 +00:00
kolaente ebec91b356 feat: add HasCaldavAccess method to APIToken 2026-03-30 12:09:53 +00:00
kolaente b0b7c52b15 feat: register caldav permission group for API tokens 2026-03-30 12:09:53 +00:00
j-hugo 23415c57aa
docs: correct task comment endpoint description and title (#2498) 2026-03-29 00:43:58 +01:00
kolaente 71282dcffd feat: add OAuth 2.0 authorization code model and migration
Add the OAuthCode model for storing short-lived authorization codes
with PKCE challenges. Codes are hashed (SHA-256) before storage and
are single-use with a 10-minute expiry. Add the database migration
and OAuth-specific error types.
2026-03-27 23:05:04 +00:00
kolaente 13be01de9f test: update expected results for archived project propagation
Adjust test assertions to reflect that projects inheriting archived
state from parents are now correctly filtered out of ReadAll results,
task collections, and search results across all database backends.
2026-03-25 09:06:33 +00:00
kolaente e3045dfd00 fix: propagate is_archived from parent to child projects in ReadAll CTE
Replace the Go-side propagateArchivedState function with in-CTE
propagation. The recursive SELECT uses (ap.is_archived OR p.is_archived)
to inherit archived state from parent projects. The outer query uses
GROUP BY with MAX(CAST(is_archived AS int)) to handle projects
accessible via both direct permissions and parent traversal. When
getArchived=false, a HAVING clause filters out archived projects.

The is_archived filter is removed from getUserProjectsStatement so
archived parents enter the CTE and propagation works correctly.
2026-03-25 09:06:33 +00:00
kolaente 5cd5dc409b fix: require admin access to list link shares
Previously, any user with read access to a project could list all link
shares including their hashes via GET /projects/{id}/shares. This allowed
read-only collaborators to obtain write or admin link share hashes and
escalate their privileges. Now ReadAll requires admin access to the
project.
2026-03-23 20:39:31 +00:00
kolaente c1418c1619 test: update user count assertions for new locked user fixture
Adjust TestListUsers assertions from 17 to 18 users to account for
the newly added locked user fixture (user18).
2026-03-23 16:37:26 +00:00
kolaente 75c9b753a8 fix: strip BasicAuth credentials from project webhook API responses 2026-03-23 16:35:47 +00:00
kolaente 9efe1fadba fix: block link share users from listing link shares in ReadAll
Link share authenticated users could call ReadAll on link shares,
which leaked hash credentials for other shares on the same project.
This allowed permission escalation from read-only to write/admin.

Add a check at the top of ReadAll() that rejects link-share-authenticated
callers, mirroring the pattern in CanRead() and canDoLinkShare().
Update tests to expect 403 Forbidden for all link share permission levels.

Fixes GHSA-8hp8-9fhr-pfm9
2026-03-23 16:34:40 +00:00
kolaente 848a4e7f07 test: remove redundant webhook SSRF tests
The SSRF protection is now tested at the shared utility level in
pkg/utils/httpclient_test.go. The webhook-specific SSRF tests were
duplicating the same checks since getWebhookHTTPClient() delegates
to NewSSRFSafeHTTPClient().
2026-03-23 16:34:22 +00:00
kolaente d4d88c0f59 test: use new outgoingrequests config keys in SSRF tests 2026-03-23 16:34:22 +00:00
kolaente e5a1c05771 refactor: use shared SSRF-safe HTTP client in webhook code 2026-03-23 16:34:22 +00:00
kolaente 654d2c7042 fix: prevent link share IDOR by validating project_id in Delete and ReadOne 2026-03-23 16:34:07 +00:00
kolaente b8edc8f17f fix: prevent attachment IDOR by validating task_id in ReadOne (GHSA-jfmm-mjcp-8wq2) 2026-03-23 16:34:07 +00:00
kolaente 833f2aec00 refactor: use accessibleProjectIDsSubquery in addBucketsToTasks 2026-03-23 16:26:37 +00:00
kolaente 67a47787fa fix: filter related tasks by project access to prevent cross-project info disclosure 2026-03-23 16:26:37 +00:00
kolaente e2683bb2bc refactor: add accessibleProjectIDsSubquery helper for project-level authz filtering 2026-03-23 16:26:37 +00:00
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