187 Commits

Author SHA1 Message Date
kolaente
85836076be feat(migration/wekan): import attachments from board export
Parse the top-level `attachments` array in WeKan board JSON exports,
group them by card ID, base64-decode the payload, and attach the
resulting files to the generated tasks so they land in Vikunja as
task attachments. Orphaned attachments (cardId with no matching card)
are silently skipped; decode errors are logged and skipped.
2026-04-13 16:04:14 +00:00
kolaente
8db4ba8a26 test(todoist): serve attachment from local test server
The test previously fetched the attachment from https://vikunja.io/testimage.jpg,
which caused flaky failures in CI when the external host was unreachable
(context deadline exceeded). Serve the local testimage.jpg via httptest and
temporarily allow non-routable IPs for the SSRF-safe client so the test is
hermetic and deterministic.
2026-04-09 16:22:56 +00:00
kolaente
33389bb0b3 test(migration): regression test for forged attachment size
Builds an in-memory export zip with a 2 MB payload and a data.json
that claims size: 0, then asserts neither the honest 2 MB row nor
the forged 0-size row ends up in the files table. Covers
GHSA-qh78-rvg3-cv54.
2026-04-09 16:22:56 +00:00
kolaente
abfbcb4cf3 fix(migration): bound per-entry zip cap by configured files.maxsize
The hard-coded 500 MB per-entry cap meant operators who set a tighter
files.maxsize could not actually enforce it on imports. Derive the cap
from files.maxsize with a floor so data.json / filters.json / VERSION
entries can still be read when the configured limit is tiny.

Clamp the uint64->int64 conversion and the LimitReader cap so absurd
configuration values do not overflow into MinInt64 and cause
io.LimitReader to treat every entry as EOF.
2026-04-09 16:22:56 +00:00
kolaente
db7f1445a8 fix(migration): compute attachment size from content during import
Import metadata is attacker-controlled and can forge a small size to
bypass the attachment size limit (GHSA-qh78-rvg3-cv54). Compute the
size from the decoded content instead of trusting a.File.Size.
2026-04-09 16:22:56 +00:00
kolaente
667f229d8c refactor(files): derive attachment size from content in sibling callers
Task/project duplication and the Todoist migration were passing stored
or API-reported sizes into NewAttachment. Derive the size from the
actual buffered content so every caller matches the hardened boundary
behaviour (GHSA-qh78-rvg3-cv54 defence-in-depth).
2026-04-09 16:22:56 +00:00
kolaente
0f3730d045 fix(notifications): escape markdown in user-controlled strings in email lines
Task titles, project titles, team names, doer/assignee names, and API
token titles were interpolated raw into Line(...) calls whose content is
rendered to HTML by goldmark and then sanitized with bluemonday UGCPolicy.
UGCPolicy intentionally allows safe <a href> and <img src> with
http/https URLs, so a title containing Markdown link or image syntax
would survive sanitization as a working phishing link or tracking pixel
in a legitimate Vikunja email.

Introduce notifications.EscapeMarkdown, which prefixes every CommonMark
§2.4 backslash-escapable ASCII punctuation character — including '<' so
autolinks like `<https://evil.com>` are neutralized before reaching
goldmark — with a backslash. Apply it to every user-controlled argument
of every Line(...) call in pkg/models that feeds into an i18n template,
and to the hand-built "* [title](url) (project)" Markdown link in the
overdue-tasks digest notification.

Also escape the migration error string in MigrationFailedNotification,
an additional sink not listed in the advisory (error messages can carry
user-controlled content from the external migration source).

Subject(...), Greeting(...), and CreateConversationalHeader(...) are
left unchanged: Subject is passed directly to the mail library and is
not markdown-rendered, Greeting is rendered via html/template's built-in
HTML escaping without markdown, and the conversational header is
sanitized as raw HTML by bluemonday in mail_render.go.

Fixes GHSA-45q4-x4r9-8fqj.
2026-04-09 15:44:04 +00:00
kolaente
bc0bb556ad feat(migration): flatten project hierarchy for single-project imports 2026-04-07 15:20:06 +00:00
kolaente
3437f98dc3 feat(migration): add skip rows option to CSV import
Allow users to skip the first N data rows when importing CSV files.
This is useful when the CSV contains metadata rows before the actual
task data begins. Adds skip_rows to ImportConfig (backend) and a
number input in the parsing options UI (frontend).
2026-04-07 15:20:06 +00:00
Claude
f555762def feat(migration): add generic CSV import with column mapping
Add a new CSV migration module that allows users to import tasks from
any CSV file with custom column mapping and parsing options.

Backend changes:
- New CSV migrator module with detection, preview, and import endpoints
- Auto-detection of delimiter, quote character, and date format
- Suggested column mappings based on column name patterns
- Transactional import using InsertFromStructure

Frontend changes:
- New CSV migration UI with two-step flow (upload -> mapping -> import)
- Column mapping selectors for all task attributes
- Live preview showing first 5 tasks with current mapping
- Parsing option controls for delimiter and date format

The CSV migrator creates a parent "Imported from CSV" project with
child projects based on the project column if provided, or a default
"Tasks" project for tasks without a specified project.
2026-04-07 15:20:06 +00:00
kolaente
e40877cca1 fix(migration): delete all default buckets when migration provides its own
Previously only the "To-Do" default bucket was deleted, leaving "Doing"
and "Done" as duplicates alongside migration-provided buckets. Now all
default-created buckets are removed when migration data already provides
bucket assignments for all tasks.
2026-04-07 12:05:47 +00:00
kolaente
ccf1468884 fix(migration): correct TickTick swagger annotation to PUT 2026-04-07 12:05:47 +00:00
kolaente
56ce73738d test(migration): add WeKan migration tests and fixture
Add comprehensive tests for the WeKan conversion function including
edge cases (empty board, orphan cards, color mapping, multiple
checklists, unsupported fields) and a realistic JSON fixture file.
2026-04-07 12:05:47 +00:00
kolaente
4cc7715951 feat(migration): add WeKan board JSON import
Add a file-based migration importer that reads WeKan board JSON exports
and creates Vikunja projects with kanban buckets, tasks, labels,
checklists, and comments.

WeKan lists become kanban buckets. Checklists are converted to HTML
task lists in the description. Card descriptions and comments are
converted from markdown to HTML using goldmark. Label colors are
mapped from WeKan's CSS color names to their actual hex values.
2026-04-07 12:05:47 +00:00
kolaente
112e486314 test: add test for deeply nested TickTick task ordering 2026-03-26 15:08:12 +00:00
kolaente
9b1c52e9e3 fix: sort TickTick tasks so parents come before children
TickTick CSV exports don't guarantee parent tasks appear before their
subtasks. When a child row came first, the shared migration pipeline
tried to create a title-less placeholder for the missing parent, which
failed with 'Task title cannot be empty'.

Resolves go-vikunja/vikunja#2487
2026-03-26 15:08:12 +00:00
kolaente
c49636430f test: add failing test for TickTick child-before-parent CSV order 2026-03-26 15:08:12 +00:00
kolaente
cc22acdf3e chore(lint): suppress gosec false positives on SSRF-safe HTTP client calls 2026-03-23 16:34:22 +00:00
kolaente
73edbb6d46 fix: prevent SSRF via Microsoft Todo migration pagination links 2026-03-23 16:34:22 +00:00
kolaente
9329774223 fix: prevent SSRF via migration file attachment URLs (GHSA-g66v-54v9-52pr) 2026-03-23 16:34:22 +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
3dd2ba4aa4 feat: register Vikunja tables with db package at init 2026-03-04 15:37:54 +01:00
kolaente
6ed684d708 fix(events): dispatch pending events in migration and export handlers
Refs #2315
2026-03-03 12:46:34 +01:00
kolaente
a7e4a4f4af fix(migration): support space-separated date format in TickTick importer
Fixes https://github.com/go-vikunja/vikunja/issues/2324
2026-03-02 08:35:35 +01:00
kolaente
39da47e435 fix: detect and fail on oversized zip entries instead of silent truncation
Replace io.LimitReader with a new readZipEntry helper that reads one extra
byte to detect when content exceeds maxZipEntrySize (500MB). This prevents
silent data corruption where partial file bytes would be stored as if the
upload succeeded.

The import now fails with ErrFileTooLarge instead of accepting truncated
content for attachments and background blobs.
2026-02-25 13:01:00 +01:00
kolaente
9d19a04550 fix(migration): use checked type assertion for background file id 2026-02-25 13:01:00 +01:00
kolaente
fc5ab844de fix(migration): limit zip entry read size to prevent decompression bombs 2026-02-25 13:01:00 +01:00
kolaente
6815cdbda4 fix(migration): reject zip entries with path traversal in vikunja-file import 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
kolaente
c9c250fb1c fix: add missing Commit() to write callers
After NewSession() auto-begins a transaction, callers that perform
writes must explicitly call Commit() for changes to persist. Without
this, writes are silently rolled back when Close() is called.

Affected callers:
- user deletion notification cron
- caldav token generation/deletion
- token cleanup cron
- mark-all-notifications-read endpoint
- saved filter view cron
- project background delete
- typesense reindex
- export cleanup cron
- task last-updated listener
- saved filter view listener
- SSO team cleanup cron
- migration status start/finish
- background set/remove handlers
- orphaned task position cleanup
- file creation
2026-02-25 11:03:02 +01:00
kolaente
249b651692 fix: treat archived TickTick tasks as done during import
TickTick uses status "2" (Archived) for completed tasks that were
subsequently archived. The import only checked for status "1"
(Completed), causing archived tasks to be imported as open despite
having a completion timestamp.

Closes go-vikunja/vikunja#2278
2026-02-23 14:52:20 +01:00
John Starich
591a646f84 refactor: remove environment variable requirements for go test 2026-02-17 18:01:05 +01:00
Micah
31da3c4533 fix(migration): make migration from Microsoft Todo work for those with previously migrated wunderlist accounts (#2126) 2026-02-17 16:54:03 +01:00
kolaente
dbd74491c4 fix(files): update all callers to provide seekable readers for S3 uploads
Update all code paths that pass file content to the storage layer to
provide io.ReadSeeker instead of io.Reader:

- Avatar upload: use bytes.NewReader instead of bytes.Buffer
- Background upload handler: use bytes.NewReader instead of bytes.Buffer
- Unsplash background: buffer response body into bytes.NewReader
- Dump restore: buffer zip entry into bytes.NewReader
- Migration structure: pass bytes.NewReader directly instead of wrapping
  in io.NopCloser
- Task attachment: change NewAttachment parameter from io.ReadCloser to
  io.ReadSeeker
2026-02-08 15:31:25 +01:00
renovate[bot]
9a61453e86 fix(deps): update module github.com/labstack/echo/v4 to v5 (#2131)
Closes https://github.com/go-vikunja/vikunja/pull/2133

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kolaente <k@knt.li>
2026-01-24 20:38:32 +01:00
kolaente
731b7c3001 fix: avoid mutating global http.DefaultClient in webhook proxy (#2145)
Fixes a bug where the webhook HTTP client was mutating `http.DefaultClient` (the global singleton), causing ALL HTTP requests in the application to use the webhook proxy. This broke OIDC authentication and other external HTTP calls when webhook proxy was configured.

Fixes #2144
2026-01-24 13:58:47 +01:00
kolaente
e085fcaef2 feat(migration/todoist): migrate from Sync API v9 to API v1 (#2072)
Migrates the Todoist migration module from the deprecated Sync API v9 to the new unified Todoist API v1.
2026-01-09 22:50:27 +01:00
kolaente
39b4568bc5 refactor: centralize HTTP error handling (#2062)
This changes the error handling to a centralized HTTP error handler in `pkg/routes/error_handler.go` that converts all error types to proper HTTP responses. This simplifies the overall error handling because http handler now only need to return the error instead of calling HandleHTTPError as previously.
It also removes the duplication between handling errors with and without Sentry.

🐰 Hop along, dear errors, no more wrapping today!
We've centralized handlers in a shiny new way,
From scattered to unified, the code flows so clean,
ValidationHTTPError marshals JSON supreme!
Direct propagation hops forward with glee,
A refactor so grand—what a sight to see! 🎉
2026-01-08 10:02:59 +00:00
kolaente
a4aad79f53 fix: TickTick import (#1871)
This change fixes a few issues with the TickTick import:

1. BOM (Byte Order Mark) Handling: Added stripBOM() function to properly handle UTF-8 BOM at the beginning of CSV files
2. Multi-line Status Section: Updated header detection to handle the multi-line status description in real TickTick exports
3. CSV Parser Configuration: Made the CSV parser more lenient with variable field counts and quote handling
4. Test Infrastructure: Added missing logger initialization for tests
5. Field Mapping: Fixed the core issue where CSV fields weren't being mapped to struct fields correctly

The main problem was in the newLineSkipDecoder function where:
- Header detection calculated line skip count on BOM-stripped content
- CSV decoder was also stripping BOM and applying the same skip count
- This caused inconsistent positioning and empty field mapping

Rewrote the decoder to use a scanner-based approach with consistent BOM handling.

Resolves https://github.com/go-vikunja/vikunja/issues/1870
2025-11-25 22:32:39 +00:00
kolaente
8862b6f69d fix(migration): return proper error message when request fails
Related to https://github.com/go-vikunja/vikunja/issues/1788
2025-11-12 20:25:17 +01:00
kolaente
9efc0baf50 fix(migration): add retry to migration request helper
Resolves https://github.com/go-vikunja/vikunja/issues/1788
2025-11-12 20:10:32 +01:00
kolaente
ca83ad1f98 feat: move to slog for logging 2025-07-21 18:15:39 +02:00
Dominik Pschenitschni
342bbd6192 fix: correct comments 2025-07-02 17:46:21 +02:00
kolaente
17b8d20e7b fix(migration): reset buckets before creating related tasks so that they are actually created (#1015) 2025-06-25 14:03:44 +02:00
kolaente
da95463bb2 fix(migration): detect header lines in csv file when importing from TickTick (#937) 2025-06-13 07:45:54 +00:00
Dominik Pschenitschni
296577a875 fix: correct license header references (#882)
See originals:
- https://www.gnu.org/licenses/agpl-3.0.txt
- https://www.gnu.org/licenses/gpl-3.0.txt
2025-06-10 12:18:38 +02:00
renovate[bot]
4a75f7d4da chore(deps): update golangci/golangci-lint-action action to v7 (#462)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kolaente <k@knt.li>
2025-04-02 09:28:56 +02:00
kolaente
13d4e0e00d chore(utils): remove deprecated MakeRandomString function 2025-03-24 16:52:46 +01:00
kolaente
4d41a1bd9b feat(kanban): create To-Do, Doing, Done buckets when creating a new kanban view 2025-03-21 19:16:52 +01:00