214 Commits
Author SHA1 Message Date
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>kolaente
972eab94a2 fix(deps): update module github.com/yuin/goldmark to v2 (#3627)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/yuin/goldmark](https://redirect.github.com/yuin/goldmark)
| `v1.8.2` → `v2.0.0` |
![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fyuin%2fgoldmark/v2.0.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fyuin%2fgoldmark/v1.8.2/v2.0.0?slim=true)
|

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/438) for more information.

---

### Release Notes

<details>
<summary>yuin/goldmark (github.com/yuin/goldmark)</summary>

###
[`v2.0.0`](https://redirect.github.com/yuin/goldmark/releases/tag/v2.0.0)

[Compare
Source](https://redirect.github.com/yuin/goldmark/compare/v1.8.5...v2.0.0)

- initial official release of v2

###
[`v1.8.5`](https://redirect.github.com/yuin/goldmark/releases/tag/v1.8.5)

[Compare
Source](https://redirect.github.com/yuin/goldmark/compare/v1.8.4...v1.8.5)

- fix:
[#&#8203;568](https://redirect.github.com/yuin/goldmark/issues/568)

###
[`v1.8.4`](https://redirect.github.com/yuin/goldmark/releases/tag/v1.8.4)

[Compare
Source](https://redirect.github.com/yuin/goldmark/compare/v1.8.3...v1.8.4)

fix: disable svg in data:image urls

###
[`v1.8.3`](https://redirect.github.com/yuin/goldmark/releases/tag/v1.8.3)

[Compare
Source](https://redirect.github.com/yuin/goldmark/compare/v1.8.2...v1.8.3)

**Full Changelog**:
<https://github.com/yuin/goldmark/compare/v1.8.2...v1.8.3>

- fix:
[#&#8203;556](https://redirect.github.com/yuin/goldmark/issues/556)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/go-vikunja/vikunja).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC40Ni4wIiwidXBkYXRlZEluVmVyIjoiNDQuNDYuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: kolaente <k@knt.li>
2026-08-27 20:34:29 +00:00
kolaente 103a5d4635 refactor(projects): dedupe effective parent lookup, tidy error ordering and comments
Also adds a negative control to the structure-import archive test.
2026-08-20 11:58:06 +02:00
kolaente 5732a435a5 fix(migration): cascade archived state to descendants on structure import
Exports written before archiving cascaded carry unflagged children under
archived parents; with the column now authoritative nothing heals that.
2026-08-20 11:58:06 +02:00
kolaente 691e5c9e61 feat(migration): add planka migrator routes on v2
Credentials migrators are verified synchronously before the async migration is
queued (CredentialsChecker). Routes live on v2 only; the listener learns the
migrator via a route-free RegisterMigratorForEvents, which the oauth v2
registrar uses as well. Shared status/migrate registration moved to
migration_shared.go.
2026-08-20 08:45:42 +02:00
kolaente 0358a820df feat(migration): convert planka data to vikunja structure
Everything lands under a "Migrated from Planka" root project; projects with
several boards become a parent with one child per board. Lists become kanban
buckets (trash skipped), closed/archived cards are done, labels keep their
Planka colours, checklists, custom fields and link attachments are rendered
into the description, comments are prefixed with the author unless it is the
importing user. No assignees, memberships or other users' data.
2026-08-20 08:45:42 +02:00
kolaente e4d9aad870 feat(migration): fetch planka projects and boards
Pages archived cards and comments (Planka requires both cursor fields, pages
until empty with a hard cap, partial results are kept), reads base custom
field definitions from the projects payload and rejects Planka v1 payloads.
2026-08-20 08:45:42 +02:00
kolaente 86da987f8a feat(migration): add planka api client
Authenticates with an API key, a JWT or username + password (Planka checks
Bearer before X-Api-Key, so only one header is ever sent; downloads use the
accessToken cookie). The synchronous credential check has one 15s deadline and
no retries. Redirects to another host or to plain http are refused for the
api, followed without credentials for attachment downloads. Response bodies
are capped, url userinfo is stripped, pending login steps (totp, terms) and
non-planka endpoints are reported as distinct client errors (142xx).
2026-08-20 08:45:42 +02:00
kolaente 4655496e2e feat(migration): client-aware get and size-limited download helpers, bounded json decode
Migrators that talk to a user-supplied host need a client with a redirect
policy, a cap on response bodies and no retries on permanent errors
(utils.ErrDoNotRetry). Diagnostic body reads in the shared helpers are capped.
2026-08-20 08:45:42 +02:00
kolaente 3b00c8ed7e fix(migration): keep imported tasks done when they are placed in an imported bucket
Creating a done task puts it in the view's default done bucket; moving it into
the imported bucket flipped it back to open. The imported state (incl. done_at)
is restored in bulk after the task loop.
2026-08-20 08:45:42 +02:00
kolaente 716220a21e fix(migration): don't panic on events for unregistered migrators 2026-08-20 08:45:42 +02:00
kolaente bd1f95bb96 fix(migration): map exported assignees to the importing user instead of failing
Vikunja-file exports carry task assignees with user ids from the source
instance. On import those ids were looked up on the target instance and
permission-checked, which fails with "User does not have access to the
project" whenever the id belongs to someone else (e.g. importing a
self-hosted export into Vikunja Cloud). Since ba980b1b8 that error aborts
the whole import.

Match assignees against the importing user by email, then username, and
drop everyone else – foreign user ids have no meaning on this instance.

Fixes #3476
2026-08-19 15:10:01 +02:00
kolaente 937ee313b1 fix(migration): don't fail todoist migration on undownloadable attachments
Todoist returns opaque identifiers instead of urls in file_url for
attachments it does not host itself (mail attachments for example).
Passing those to the http client failed with "unsupported protocol
scheme" and aborted the entire migration.

Skip attachments without an http(s) url and log-and-continue when a
single download fails instead of failing the whole migration.
2026-08-04 21:21:05 +02:00
kolaente ba980b1b8e fix(migration): abort import on unexpected task creation error
Non-empty-title errors from task creation fell through silently and the
loop kept using a task with ID 0, attaching relations and labels to id
0. Return the error instead; empty titles keep being skipped.
2026-08-02 16:21:50 +02:00
Mauandkolaente eb31ee3780 fix(migration/csv): cap CSV import buffer to the configured upload limit
DetectCSVStructure, PreviewImport, and MigrateWithConfig each did
make([]byte, size) with size taken directly from the caller (an
uploaded file's declared size) with no upper bound, letting a
malicious or corrupt size value force an arbitrarily large
allocation. Cap it to the server's existing configured max upload
size before allocating.
2026-07-29 22:53:46 +02:00
kolaente a6edc67876 fix(migration): prevent stack overflow on ticktick parentId cycles
sortParentsBeforeChildren marked a task as placed only after recursing
into its parent, so a parentId cycle in an uploaded TickTick export made
place() recurse forever. That is a Go stack overflow, a runtime fatal
error the recover middleware cannot catch, so a two-line CSV from any
authenticated user took down the whole process.

Track a tri-state per task and mark it before recursing, which breaks
the cycle. Acyclic input is unaffected.
2026-07-29 09:22:22 +02:00
kolaenteandkolaente 0101dc9386 fix(migration): seed task positions for exports without order information
Tasks imported without a position are inserted in front of the lowest one by
halving it. That reaches the minimum spacing every few dozen inserts, which
recalculates every position in the view and makes a large import O(n²) - a
10k task TickTick export with an empty Order column never finished.

Numbering the tasks up front skips the halving entirely and keeps the order
the export was written in.

Closes #3297
2026-07-26 01:13:08 +00:00
kolaente 781ffac198 fix(security): require Admin to detach a project from its parent (GHSA-44v6-7fxq-vgf4) 2026-07-19 18:59:34 +02:00
kolaente b31d606b88 fix(kanban): prevent cross-tenant bucket relocation via project_view_id mass-assignment (GHSA-569v-q83c-3j3g) 2026-07-19 18:59:34 +02:00
kolaenteandkolaente 330b94c3c4 feat(migration): import recurring tasks from todoist 2026-06-26 13:32:08 +00:00
kolaenteandkolaente a881246e80 refactor(migration): extract file/CSV migrate orchestration into shared funcs
Pull the StartMigration -> Migrate -> FinishMigration orchestration out of
the v1 echo handlers into handler.RunFileMigration and csv.RunMigration so
the v2 API can reuse the exact same business logic. v1 is refactored onto
them and stays byte-identical on the wire.

Also tag the CSV detect/preview/config DTOs with doc:/enum: so they carry
descriptions in the v2 OpenAPI schema (ignored by v1 swaggo/xorm).
2026-06-12 08:51:19 +00:00
kolaenteandkolaente 9da51f5096 refactor(events): pass context to DispatchPending directly
Every DispatchPending caller either has the request context in scope or
is genuinely request-less, so passing it as a parameter replaces the
stored-context mechanism on the pending queue and satisfies
contextcheck. Also fixes lint findings in the audit package.
2026-06-12 08:56:08 +00:00
kolaenteandkolaente 9c3c1047ac feat(api/v2): port OAuth migrators (Todoist, Trello, Microsoft To-Do)
Add /api/v2 auth/status/migrate endpoints for the three OAuth-based
migrators. One generic helper registers all three ops per migrator
behind its static config gate, so there's no copy-pasted block per
migrator.

The migrate kick-off orchestration (already-running guard + event
dispatch) is extracted into migrationHandler.StartMigration so v1 and
v2 share it; v1's wire output is unchanged. The guard now surfaces as a
typed migration.ErrMigrationAlreadyRunning (412) so v2 can translate it
through the standard error bridge.
2026-06-11 18:35:55 +00:00
Tink botandkolaente fd10300597 fix(migration): don't drop TickTick tasks sharing a malformed id
Collapsing unparseable taskIds to 0 meant sortParentsBeforeChildren,
which tracked placement by TaskID, treated every zero-id task after the
first as already placed and silently dropped it. Track placement by task
identity instead so duplicate or zero ids never conflate distinct tasks.
2026-06-01 10:09:58 +00:00
Tink botandkolaente ebb89ba4f3 fix(migration): tolerate non-numeric values in TickTick CSV exports
TickTick exports could contain non-numeric values in columns Vikunja
parses as integers (Priority, taskId, parentId). gocsv's strconv.ParseInt
then failed, aborting the entire import and surfacing as an internal
server error reported to Sentry (e.g. parsing "p1": invalid syntax).

Numeric ID columns now fall back to 0 for unparseable values instead of
failing the import. The Priority column, which was previously parsed but
never carried over to the imported task, is now mapped onto the task and
accepts both the plain numeric form (0, 1, 3, 5) and the "pN" form
(p1, p2, p3).

Closes #2822
2026-06-01 10:09:58 +00:00
Tink botandkolaente fa6e1f8e49 fix(migration): reuse existing labels on re-import
Seed the dedup map at the start of insertFromStructure with the importing
user's existing labels, keyed by title + normalized hex color. Previously
the map was empty on each run, so importing the same CSV (or any other
migration format) twice would create a second copy of every label.

Scoped to the user's own labels so imports don't silently link to other
users' labels visible via shared projects.

Fixes #2742
2026-05-19 09:09:59 +00:00
Tink botandkolaente 6b14307896 test(trello): drop redundant BackgroundImage assignment in getTestBoard 2026-05-15 15:16:11 +00:00
Tink botandkolaente fc373ae963 test(trello): serve testimage from local server instead of vikunja.io
Mirrors the Todoist migration test setup so TestConvertTrelloToVikunja
no longer depends on https://vikunja.io/testimage.jpg being reachable.
2026-05-15 15:16:11 +00:00
kolaenteandkolaente 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
kolaenteandkolaente 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
kolaenteandkolaente 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
kolaenteandkolaente 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
kolaenteandkolaente 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
kolaenteandkolaente 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
kolaenteandkolaente 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
kolaenteandkolaente bc0bb556ad feat(migration): flatten project hierarchy for single-project imports 2026-04-07 15:20:06 +00:00
kolaenteandkolaente 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
Claudeandkolaente 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
kolaenteandkolaente 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
kolaenteandkolaente ccf1468884 fix(migration): correct TickTick swagger annotation to PUT 2026-04-07 12:05:47 +00:00
kolaenteandkolaente 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
kolaenteandkolaente 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
kolaenteandkolaente 112e486314 test: add test for deeply nested TickTick task ordering 2026-03-26 15:08:12 +00:00
kolaenteandkolaente 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
kolaenteandkolaente c49636430f test: add failing test for TickTick child-before-parent CSV order 2026-03-26 15:08:12 +00:00
kolaenteandkolaente cc22acdf3e chore(lint): suppress gosec false positives on SSRF-safe HTTP client calls 2026-03-23 16:34:22 +00:00
kolaenteandkolaente 73edbb6d46 fix: prevent SSRF via Microsoft Todo migration pagination links 2026-03-23 16:34:22 +00:00
kolaenteandkolaente 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