Commit graph

49 commits

Author SHA1 Message Date
Johannes Millan
c8e5ed2c15 fix(plugins): harden node execution grants 2026-06-09 14:07:06 +02:00
Johannes Millan
4a99e7e505
fix(security): stored XSS → RCE chain — note image, plugin nodeExecution, CSP (GHSA-78rv-m663-4fph) (#8178)
* fix(ui): prevent stored XSS in enlarge-img directive

The enlarged-image element was built by interpolating the image URL into an innerHTML string, so a crafted synced/imported note.imgUrl could break out of the src attribute and inject an event handler (stored DOM-XSS). Build the <img> with createElement and property assignment instead, so the URL is never parsed as HTML. Adds a regression spec.

Refs: GHSA-78rv-m663-4fph

* fix(plugins): authorize nodeExecution from main-process state

The Electron main process authorized Node execution from the manifest the renderer passes on each IPC call, and window.ea.pluginExecNodeScript is callable by any renderer code — so injected renderer JS could forge {permissions:['nodeExecution']} and run arbitrary Node (CWE-501).

The main process now keeps its own grantedPlugins set, populated via a dedicated PLUGIN_SET_NODE_CONSENT channel. The renderer registers a grant in _fireOnReady (before the first node call, covering every load path including zip upload) and revokes it on teardown. The executor requires set membership.

Defense in depth, not a hard boundary: the registration channel is itself renderer-reachable, so a fully-compromised renderer could register a grant itself. It blocks the disclosed PoC (forged manifest for a never-loaded plugin) and removes per-call manifest trust. A hard boundary needs a main-process consent dialog.

Refs: GHSA-78rv-m663-4fph

* fix(security): add object-src 'none' and document CSP constraints

Adds object-src 'none' (the app embeds no <object>/<embed>) and replaces the stale TODO with an accurate note on why 'unsafe-eval'/'unsafe-inline' cannot be dropped yet: the plugin runtime and the inline bootstrap script use new Function, and the packaged app loads the renderer over file:// (opaque origin), so tightening script-src to 'self' is unreliable.

Refs: GHSA-78rv-m663-4fph
2026-06-08 20:20:17 +02:00
felix bear
00cd665e1a
feat(backup): configure automatic backup file limit #6690 (#8094)
Co-authored-by: cocojojo5213 <cocojojo5213@users.noreply.github.com>
2026-06-08 12:09:49 +02:00
Harbinger
9910d30fca
feat(sync): add OneDrive sync provider with PKCE auth (#7523)
* feat(sync): integrate onedrive with pkce and operation log sync

* fix(sync): add oauth state validation and token refresh concurrency control

* fix(sync): refactor onedrive oauth cleanup and reduce download API calls

- Replace setInterval-based OAuth state pruning with on-demand
  _pruneExpiredOAuthStates() to avoid background timer overhead
- Optimize downloadFile to read ETag from content response headers
  first, falling back to a metadata request only when needed (reduces
  API calls from 2 to 1 in the common case)
- Narrow OneDrive class interface from SyncProviderServiceInterface to
  FileSyncProvider for stronger type guarantees
- Accept optional devPath constructor parameter for non-production
  folder namespacing
- Extract isOneDriveClientIdRequired to a helper function in
  sync-form.const.ts for readability
- Change default OneDrive sync folder name to 'Super Productivity'

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): address PR #7523 review feedback for OneDrive sync

Critical fixes:
- Detect invalid_grant in token refresh responses and clear credentials
- Reset _tokenRefreshInFlightPromise in clearAuthCredentials to prevent
  stale refresh results from overwriting cleared state
- Re-read config before persisting refreshed tokens to avoid race with
  concurrent clearAuthCredentials calls

Important fixes:
- Extract OAuth state management to shared oauth-state.util.ts to fix
  circular dependency between OneDrive provider and callback handler
- Add 401 retry pattern (_is401Retry) matching Dropbox's approach:
  on 401, proactively refresh token and retry, only clear credentials
  if retry fails
- Remove unused auth status translation keys (AUTH_SUCCESS,
  BTN_AUTHENTICATE, BTN_REAUTHENTICATE, REAUTH_SUCCESS,
  STATUS_CONFIGURED, STATUS_NOT_CONFIGURED) and form props
- Remove unused requireAuth parameter from _cfgOrError

Minor fixes:
- Replace path-exposing log messages with structured logging
- Remove unused _formatHttpErrorDetails helper
- Return empty ETag fallback in getFileRev instead of throwing
- Add folder cache invalidation comment for path changes
- Add afterEach to restore global fetch in tests
- Add PKCE defence-in-depth comment in auth code dialog

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): use eslint-disable-next-line for @microsoft.graph.conflictBehavior

Replace spread+computed-property workaround with a plain property plus
an explicit eslint-disable-next-line comment. Clearer intent: the
naming violation is a Graph API requirement, not accidental.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): pre-save OneDrive form config in reauth() to prevent MissingCredentialsSPError

Re-auth was failing silently on Linux because getAuthHelper() reads
clientId from IndexedDB (privateCfg), but the form values were never
written before calling configuredAuthForSyncProviderIfNecessary().

- Extract shared BTN_REAUTHENTICATE and REAUTH_SUCCESS translation keys
- Add OneDrive to OAUTH_SYNC_PROVIDERS
- Pre-save form config in reauth() matching the existing save() flow

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): adapt TooManyRequestsAPIError to new constructor signature after rebase

* refactor(sync): migrate OneDrive provider to packages/sync-providers

Move OneDrive core logic into the shared sync-providers package,
matching the pattern used by Dropbox, WebDAV, and other providers.
The app-side code is now a thin adapter with a createOneDriveProvider()
factory function.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): re-throw MissingRefreshTokenAPIError from _requestOAuthToken catch block

The catch block in _requestOAuthToken was swallowing the
MissingRefreshTokenAPIError thrown after clearing credentials on
invalid_grant, causing it to fall through to a generic HttpNotOkAPIError
instead. Now re-throws MissingRefreshTokenAPIError explicitly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): address PR #7523 second review critical and important items

- Replace _is401Retry instance field with per-call parameter to prevent
  concurrent 401 races (Critical #1)
- Fix _requestOAuthToken catch block to re-throw MissingRefreshTokenAPIError
  instead of swallowing it (Critical #2)
- Fix _parseOAuthCallback defaulting unknown provider to 'unknown' instead
  of 'dropbox' to prevent misrouting (Critical #3 + Important #6)
- Change conflictBehavior from 'replace' to 'fail' to prevent data loss
  if a file exists with the same name as the folder (Critical #4)
- Await in-flight token refresh promise in clearAuthCredentials before
  clearing, to close the refresh-during-clear race (Important #5)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): address PR #7523 review important and minor items

- Extract duplicate OneDrive pre-auth save block into
  _persistOneDriveFormCfgBeforeAuth() method (Important #7)
- Add GET probe in _ensureSyncFolderExists to skip per-segment
  creation when folder already exists (Important #9)
- Redact sensitive params (code, code_verifier, refresh_token,
  access_token) from token endpoint and Graph error bodies
  (Important #11)
- Remove targetPath from _mapAndThrow error messages to prevent
  logging user content (Important #12)
- Add OAuth state validation in manual paste flow for OneDrive
  (Important #13)
- Fix unknown provider routing in _parseOAuthCallback (Important #14)
- Guard Electron IPC listener against post-destroy emissions
  (Important #15)
- Remove dead authStatus Formly field from sync-form.const.ts (Minor)
- Simplify Headers builder in _ensureSyncFolderExists (Minor)
- Fix spec afterEach to restore original fetch instead of undefined
  (Minor)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): fix clearAuthCredentials race and add unit tests for OneDrive

Fix a race condition in clearAuthCredentials where _tokenRefreshInFlightPromise
was nulled AFTER awaiting, causing the invalid_grant handler's clearAuthCredentials
to wait on itself. Now captures the promise and nulls the field before awaiting.

Add 5 new unit tests covering critical code paths:
- 401 token refresh and retry
- 401 retry failure with credential clearing
- 400 invalid_grant credential clearing
- 412 precondition failed mapping
- Concurrent token refresh deduplication

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): address PR #7523 third review items

- Simplify clearAuthCredentials: null _tokenRefreshInFlightPromise without
  awaiting to avoid deadlock when called from inside the refresh IIFE
- Add superproductivity:// scheme validation in Electron OAuth listener
- Fix invalid_grant test to assert MissingRefreshTokenAPIError
- Fix folder cache test to match GET-probe optimization
- Set setComplete default in beforeEach

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(sync): fix OAuthCallbackHandler spec to expect 'unknown' provider

The _parseOAuthCallback now returns 'unknown' instead of 'dropbox' for
URLs without an explicit provider path segment.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): address PR #7523 fourth review items

- Handle generic 401 from token endpoint (clear creds + MissingRefreshTokenAPIError)
- Remove user-supplied paths from error constructors (rule 9 compliance)
- Soften clearAuthCredentials comment to reflect actual TOCTOU guarantee
- Tighten Electron scheme gate to match native path prefix
- Add scheme info to Electron rejection log
- Fix folder-cache test to assert GET probe count
- Remove redundant per-test setComplete stub

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): address PR #7523 fifth review items

- Scope invalid_grant/401 credential clearing to refresh_token grant only
  (bad auth code exchange no longer wipes existing credentials)
- Redact OAuth code/state from Electron protocol handler logs
- Add @sp/sync-providers/onedrive TS path alias to tsconfig files
- Default undefined sync config fields instead of overwriting with undefined
- Handle OneDrive in provider-switch branch (preserve encryptKey)
- Update sync-config test expectations to match default values

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): address PR #7523 sixth review items

- Use If-None-Match: * for null-rev uploads to prevent concurrent overwrite
- Guard in-flight refresh against stale credentials (match refreshToken/clientId/tenantId)
- Gate OneDrive provider option behind Electron/Native (web CORS unsupported)
- Don't clear credentials on transient token endpoint errors (429/5xx)
- Preserve null syncProvider instead of defaulting to WebDAV
- Hydrate full OneDrive privateCfg on provider switch
- Remove duplicate syncInterval/isManualSyncOnly Formly controls
- Revert non-English locale changes (zh.json, zh-tw.json)
- Redact URL fragments (#) in Electron protocol handler logs
- listFiles rethrows non-404 errors instead of swallowing all
- Update 401 test expectations for new rethrow behavior

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): guard credential clearing against stale concurrent refresh

Prevent a stale in-flight refresh from wiping newer credentials after
a concurrent re-auth. The refresh IIFE now throws a plain Error (not
MissingRefreshTokenAPIError) when it detects config drift, and all
clearAuthCredentials() call sites now verify the stored config still
matches before clearing. Also fix listFiles() to handle 404 from
_requestJson (which throws HttpNotOkAPIError, not RemoteFileNotFoundAPIError).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): address PR #7523 eighth review items

- Replace If-None-Match:* with @microsoft.graph.conflictBehavior=fail query param for upload create-only semantics
- Add identity change detection in _persistOneDriveFormCfgBeforeAuth to clear tokens when useCustomApp/clientId/tenantId change
- Extract _clearIfConfigMatches helper to guard credential clearing against stale concurrent refresh
- Restrict folder probe fallthrough to 404 only, propagate other errors
- Restore locale files from upstream master
- Centralize IS_ONEDRIVE_SUPPORTED in onedrive-auth-mode.const.ts, use in factory and form config

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): address PR #7523 ninth review items

- Paginate listFiles via @odata.nextLink to avoid silent data loss
- Fix logger misuse (log -> normal with proper string message)
- Replace as any with OneDrivePrivateCfg in dialog-sync-cfg
- Throw NoRevAPIError when uploadFile response lacks eTag
- Omit undefined optional booleans from global config to prevent overwrite
- Move OneDrive dynamic import inside IS_ONEDRIVE_SUPPORTED gate
- Require state param for OneDrive full-URL paste (CSRF)
- Add id_token to sensitive keys for body redaction

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sync): add OneDrive type alias to electron tsconfig paths

The @sp/sync-providers/onedrive path alias was missing from
electron/tsconfig.electron.json, causing the Electron build to fail
with "Cannot find module '@sp/sync-providers/onedrive'". The alias was
already present in tsconfig.base.json but electron uses its own paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 15:31:06 +02:00
Johannes Millan
79133f9046 feat(electron): suffix version string per distribution target
Append a short channel marker to the (display-only) version string so bug
reports and the config footer reveal which build a user runs, e.g.:

  win nsis W   portable P   MS Store MS
  mac dmg  D   App Store MAS
  linux AppImage AI   snap SN   flatpak FP   deb/rpm L
  android Play A   F-Droid AF   ios I   web WB

Desktop channels are detected in the Electron process (process.platform,
process.mas/windowsStore, APPIMAGE/SNAP/FLATPAK_ID) via a shared helper
reused by the tray-GUID logic and exposed sync over the preload bridge
(no IPC, getAppVersionStr stays synchronous). Mobile/web are detected in
the frontend. deb vs rpm is not distinguishable at runtime, so both
report L. No caller semver-compares this string.
2026-05-19 16:42:45 +02:00
Het Savani
a3560a5615
feat(settings): add image picker for background image selection (#7564)
* feat(settings): add image picker for background image selection

* fix(settings): use electron file paths for background image selection

* fix(settings): validate and normalize local background image paths

* fix(theme): validate local background image types before reading files
2026-05-13 12:58:07 +02:00
Johannes Millan
258a6c8949 feat(task-widget): make settings per-instance and fix Mac drag/resize
OS behavior (transparency, native drag/resize) differs enough between
platforms that syncing one shared value across devices makes no sense.
Move taskWidget settings out of the synced GlobalConfigState into a
localStorage-backed TaskWidgetSettingsService and a dedicated
UPDATE_TASK_WIDGET_SETTINGS IPC channel.

On macOS, transparent + frameless BrowserWindows do not support native
edge resize or window drag (Electron docs: "Transparent windows are not
resizable"). Use transparent: false on Mac and apply user-set opacity
via BrowserWindow.setOpacity() so native drag/resize keep working.

GlobalConfigFormSectionKey is split from GlobalConfigSectionKey so that
'taskWidget' cannot leak into updateGlobalConfigSection action payloads
(which would create phantom GLOBAL_CONFIG_UPDATE_SECTION ops).

No data migration: users previously configuring the widget will see
defaults and reconfigure once.
2026-04-29 21:16:12 +02:00
Johannes Millan
99b7dee74a fix(backup): move shared timestamp util into electron/ for snap packaging
electron/backup.ts imported getBackupTimestamp from src/app/util/, which
tsc compiled alongside the source. electron-builder only packages
electron/** and the Angular renderer bundle, so the compiled util was
missing from app.asar and the main process crashed on launch with
"Cannot find module '../src/app/util/get-backup-timestamp'".

Moved the util to electron/shared-with-frontend/ (existing convention
for code shared between main and renderer), updated all importers.

Fixes #7270
Refs #7315, #7323, #7265, #7320
2026-04-22 16:14:56 +02:00
Johannes Millan
01e30b9c7e
Feat/to me it looks like there are lots of 60dd04 (#7280)
* fix(issue): prevent crash from orphan issueProviderId (#7135)

The Jira image-headers effect in task-detail-panel subscribed to
selectIssueProviderById without an error handler, so a task with an
issueProviderId pointing at a deleted provider (e.g. after sync
convergence where taskIdsToUnlink didn't cover all local tasks)
propagated the selector throw to Zone.js as a crash dialog. Wrap the
inner selector observable in catchError that logs and falls back to
of(null); the downstream jiraCfg?.isEnabled guard handles the fallback.

Also drop IssueLog.log(issueProviderKey, issueProvider) from the
throwing variant of the selector: providers may carry credentials
(host, token, apiKey) and IssueLog history is exportable.

* fix(focus-mode): sync tray countdown with in-app timer during breaks

Tray title was rebuilt from a cached currentFocusSessionTime that only
refreshed when CURRENT_TASK_UPDATED fired. addTimeSpent is gated on an
active current task, so during focus-mode breaks or task-less focus
sessions the cache froze while the in-app timer kept ticking.

Add the tick action to taskChangeElectron$ so the cache refreshes every
second whenever the focus timer is running.

Fixes #7278

* fix(ci): restore GitHub Actions SHA pins undone by 0e9218bd68

Commit 0e9218bd68 silently reverted PR #7212 (github-actions-minor group
bump) along with its stated sync/client-id work. This restores the 15
workflow files to their pre-revert state.

Actions restored to newer pinned SHAs:
- actions/upload-artifact v7.0.0 -> v7.0.1
- step-security/harden-runner v2.16.1 -> v2.17.0
- softprops/action-gh-release v2.6.1 -> v3.0.0
- signpath/github-action-submit-signing-request v2.0 -> v2.1
- anthropics/claude-code-action v1.0.89 -> v1.0.93
- docker/build-push-action v7.0.0 -> v7.1.0
- easingthemes/ssh-deploy v5.1.1 -> v6.0.3

* fix: restore i18n, UI, and docs work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted the following work alongside its stated
sync/client-id changes. Files where later master commits (fec7b25f23, etc.)
already re-applied the reverted work are intentionally left untouched.

Restored:
- #7232 docs/long-term-plans/location-based-reminders.md (513 lines)
- #7199 Romanian i18n phase 3 (ro.json + ro-md.json, ~1168 lines)
- #7049 Polish translation improvements
- #7143 planner component styling (4 scss files)
- #7211 add-task-bar preserve time estimate when typing title
- #7208 task.reducer roll-up estimates for added subtasks
- #6767 focus-mode pomodoro reset button
- #7205 plugin-dev github-issue-provider TOKEN description
- #7231 mobile-bottom-nav FAB fix
- a4fe03272 iOS keyboard accessory bar (global-theme + dialog-fullscreen-markdown)
- 309670db3 ShortSyntaxEffects undefined guard
- 667a7986f Dropbox PKCE auth comment/behavior

* fix(electron): restore electron + e2e work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted the following electron/e2e work.
Files already re-fixed by later master commits are left as-is:
- e2e/tests/sync/supersync-archive-conflict.spec.ts (de33234976 + 191d129ff3)

Restored:
- e8a3e156eb fix(electron): Linux autostart IDB backing-store recovery
  (re-adds electron/clear-stale-idb-locks.ts + start-app.ts wiring)
- 5ce78a5b63 fix(electron): macOS Cmd+Q / Dock > Quit hang
  (setIsQuiting + before-quit delegate to close-handler)
- 46e0fa2d01 fix(sync): FILE_SYNC_LIST_FILES IPC contract
  (electronAPI.d.ts + local-file-sync + preload + ipc-events)
- ea1ef16307 fix(android): session-only SAF permissions on OEM devices
- 8865dc0a50 test(e2e): supersync parallel-worker stampede guard
  (SUPERSYNC_SERVER_HEALTHY env-var fallback + goto retry loop)
- af7c7687e2 test(e2e): block WS-triggered downloads in non-WS specs
- 265b44db5d test(e2e): premature waitForURL on daily-summary
  (this is literally the fix the bad commit's message claimed to add)

Conflict resolutions:
- e2e/utils/supersync-helpers.ts: kept the refined getDoneTaskElement
  checks from d64014d086 (later than c558bcab5e) while restoring the
  goto retry loop from 8865dc0a50.
- electron/start-app.ts: unioned imports (setIsQuiting +
  clearStaleLevelDbLocks from theirs, fs from ours).

* fix(sync): restore sync-core work undone by 0e9218bd68

Commit 0e9218bd68 silently reverted parts of several sync fixes. Most
sync-core reverts have already been re-addressed differently on master
by later commits (1f5184f6e7, 05cd875dd6, 09f5ced2c9, 7df43358ab,
d9158d6adb, 32dbc95ed9, 8c3b08e016, f89fe1ebc3) — those files are
intentionally left untouched to avoid reverting master's newer work.

This PR restores only the pieces that are genuinely still missing:

- e8a3e156eb fix(electron): IDB backing-store autoreload (in-app piece)
  operation-log-hydrator.service.ts + .spec.ts (the electron/clear-
  stale-idb-locks.ts piece was restored in the prior commit)

Plus three low-risk documentation/cleanup restorations:
- operation-sync.util.ts — add "Nextcloud" to isFileBasedProvider JSDoc
- dropbox.ts — restore improved _getRedirectUri JSDoc (667a7986fb)
- dialog-get-and-enter-auth-code.component.ts — restore isNativePlatform
  comment explaining why manual code entry flow is used (667a7986fb)
- file-adapter.interface.ts — remove stray "// NEW" comment (46e0fa2d01)

Intentionally NOT restored (master's newer work covers or supersedes):
- sync-trigger.service.ts / sync.effects.ts (05cd875dd6)
- sync-wrapper.service.ts (1f5184f6e7)
- sync-errors.ts (1f5184f6e7 re-added LegacySyncFormatDetectedError)
- file-based-sync-adapter.service.ts + spec (1f5184f6e7 + d9158d6adb)
- file-based-sync.types.ts (1f5184f6e7)
- operation-log.const.ts (32dbc95ed9 bumped IDB_OPEN_RETRIES to 5)
- dialog-sync-initial-cfg.component.ts (f89fe1ebc3)
2026-04-20 12:04:38 +02:00
Johannes Millan
0e9218bd68 fix(sync): handle data validation errors and improve client ID management
- Separate JsonParseError and SyncDataCorruptedError handlers in sync wrapper
- Handle corrupted remote data gracefully instead of throwing
- Add getOrGenerateClientId() as unified entry point, eliminating dual injection
  of ClientIdService + CLIENT_ID_PROVIDER in snapshot-upload, file-based-encryption,
  and sync-hydration services
- Use crypto.getRandomValues() instead of Math.random() for client ID generation
- Warn user when stored client ID is invalid and must be regenerated
- Fix flaky e2e supersync tests (premature waitForURL match on daily-summary URL)
2026-04-16 17:41:39 +02:00
Johannes Millan
46e0fa2d01 fix(sync): add FILE_SYNC_LIST_FILES electron IPC handler and fix preload args
Add missing IPC handler for file listing used by local-file sync, and fix
preload.ts passing bare dirPath string instead of the expected { dirPath }
object — causing IPC handler to destructure undefined.
2026-04-14 22:09:02 +02:00
Johannes Millan
1ee9d7f566 refactor: rename overlay-indicator to task-widget
Rename the "overlay indicator" feature to "task widget" across the
entire codebase for clearer naming. Includes file renames, config key
migration, IPC event rename, and translation key updates.

Migration handles old persisted data from both the `overlayIndicator`
config key and the deprecated `misc.isOverlayIndicatorEnabled` field.
The `taskWidget` type is made optional in GlobalConfigState to prevent
Typia auto-fix from overwriting migrated values during upgrade.
2026-03-30 22:00:47 +02:00
Johannes Millan
4b0550be35
fix(security): harden Local REST API against DNS rebinding and field injection (#6996)
* fix(security): harden Local REST API against DNS rebinding and field injection

- Add Host header validation to block DNS rebinding attacks from
  malicious websites targeting the localhost API
- Add IS_ELECTRON guard in handler init() for defense-in-depth
- Add allowlist of task fields settable via API (title, notes, isDone,
  timeEstimate, timeSpent, projectId, tagIds, dueDay, dueWithTime,
  plannedAt) to prevent state corruption via injected internal fields
- Validate source query parameter instead of blind cast
- Add tests verifying disallowed fields are stripped from POST and PATCH

https://claude.ai/code/session_01YJNc4gUCkHHrAAhXJBEaop

* fix: address review findings from daily code review

Security hardening:
- Block cloud metadata endpoints (169.254.169.254, metadata.google.internal)
  even when allowPrivateNetwork is true (SSRF protection)
- Add HTTP method allowlist to PluginHttp request() to prevent abuse
- Add rate limiting (max 50 concurrent) to Local REST API

Bug fixes:
- Fix hardcoded 'ICAL' issueProviderKey in calendar effects; use event's
  actual provider key with ICAL fallback
- Fix race condition in TimeBlockDeleteSidecarService: push IDs instead
  of replacing to prevent data loss on rapid bulk deletes
- Backfill missing issueProviderKey on cached calendar events from
  localStorage (backward compatibility)
- Fix btoa(String.fromCharCode(...spread)) overflow for long strings
  by using loop-based approach

Minor:
- Cap HiddenCalendarEventsService to 500 entries max (prevent
  unbounded localStorage growth)
- Fix misleading "deep clone" comment to "shallow clone"

https://claude.ai/code/session_01YJNc4gUCkHHrAAhXJBEaop

* test: fix unit test failures from review hardening changes

- Replace IS_ELECTRON guard with window.ea?.onLocalRestApiRequest check
  in handler init() so tests can mock the API without navigator.userAgent
- Update plugin-http spec to expect "cloud metadata endpoints" error
  message (was "private/local") for 169.254.169.254 and metadata.google
- Fix allowlist test expectation: title is an allowed field and passes
  through pickAllowedFields as expected

https://claude.ai/code/session_01YJNc4gUCkHHrAAhXJBEaop

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-29 11:59:27 +02:00
Onur Neşvat
959004ee18
feat(electron): add Local REST API for desktop automation (#6981)
* feat(electron): add Local REST API for desktop automation

Add a local HTTP server (port 3876) that allows external scripts and
tools to interact with a running Super Productivity desktop app.

Features:
- Task CRUD operations (create, read, update, delete)
- Task control (start, stop, set current)
- Task archive/restore operations
- Query filters for tasks (by title, project, tag, done status)
- Read-only project and tag listing

The API is disabled by default and can be enabled in Settings > Misc.
Only accepts connections from localhost (127.0.0.1) for security.

* fix: resolve lint errors for local REST API

* fix: use lazy injection for LocalRestApiHandlerService

Fixes StartupService tests by using Injector.get() instead of direct
injection, preventing LocalRestApiHandlerService from being instantiated
when StartupService tests don't have NgRx providers.

* fix: resolve markdown lint errors in API docs

* fix: add security warning to Local REST API hint
2026-03-28 13:35:04 +01:00
Johannes Millan
25051d2899 fix(plugins): use loopback redirect and system browser for Google OAuth
Google's Desktop OAuth client requires loopback redirect URIs
(http://127.0.0.1:<port>) and blocks embedded webviews. This replaces
the custom URI scheme + BrowserWindow approach with a temporary loopback
HTTP server and shell.openExternal for the system browser.

- Add PLUGIN_OAUTH_PREPARE IPC to start loopback server and return port
- Open auth URL in system browser instead of Electron BrowserWindow
- Make getRedirectUri() async to support IPC port retrieval
- Validate OAuth config before starting loopback server to prevent leaks
- Force 200 status on OPTIONS preflight responses in CORS bypass
2026-03-26 17:43:04 +01:00
Johannes Millan
02bc3e88e3 feat(two-way-sync): add plugin OAuth, two-way field sync, and remote issue deletion
Add OAuth support for plugins with PKCE, token persistence in local-only
IndexedDB, and Electron/web redirect handling. Extend two-way sync with
dueDay, dueWithTime, and timeEstimate field mappings including mutually
exclusive field clearing. Support remote issue deletion on task delete
and remote deletion detection during polling.

Add Google Calendar plugin (disabled from bundled builds pending legal
review) as a development reference for the plugin OAuth and two-way
sync APIs.

Additional fixes:
- Restore accidentally deleted focus-mode translation keys
- Remove full Task[] from deleteTasks action to prevent op-log bloat
- Add dueDay/deadlineDay format validation guards (#6908)
- Fix Android reminder alarm cancel intent matching
- Validate dueDay format to prevent false overdue from corrupted data
- Fix PKCE test expectation after utility consolidation
2026-03-22 13:02:41 +01:00
Johannes Millan
78ef3c39d5 fix(electron): always show app border unless in fullscreen
Previously the 1px border only appeared with the custom title bar
enabled. Now it shows for all Electron windows and is hidden during
fullscreen via IPC-driven body class toggling.
2026-03-10 18:57:49 +01:00
Onur Neşvat
d65621272d
feat: add today's tasks and active timer to tray context menu (#6599)
* feat: add today's tasks and active timer to tray context menu

* Fix test: add missing onSwitchTask mock to window.ea
2026-02-23 15:42:07 +01:00
Pue-Tsuâ
9a9e31f36a
Adding Ctrl-V pasting function from clipboard. (#5998)
* docs(clipboard): Added the plan to guide AI implementation.

* feat(clipboard): Changed requirement to reflect the fact that some functions can't be done in browsers.

* feat(clipboard): Added Ctrl-v pasting functionality from clipboard.

- Storing image in IndexedDB in browser, and storing image in userdata path in Electron.
- Added image resizing in markdown.

* docs(clipboard): Removed requirement file since it's implemented.

* feat(clipboard): Added clipboard images management in settings.

* feat(tests): Enhance focus mode and inline markdown tests with mock store and actions

* feat(clipboard): Add electron-only annotation to findImageFile function

* feat(clipboard): Prevent memory leaks by revoking cached blob URLs on image deletion

* feat(clipboard): Improve paste handling by tracking current placeholder and cleaning up old progress

* Merge branch 'master' into feat/clipboard-files

* feat(marked-options): Refactor renderer methods and add hooks for image sizing syntax

* feat(clipboard): Added feature that pasted image will be added to task attachment.

* revert: Revert the change of lock file from last merge.

* revert: Removed unwanted changes.

* refactor(clipboard): Cleaned up code that seems not necessary.

* feat(clipboard): Handle storage quota exceeded error and update messages

* feat(clipboard): Implement validated IPC handlers for clipboard image operations

* feat(clipboard): Add error handling for clipboard image URL resolution

* feat(clipboard): Refactor file operations to use async fs promises

* feat(clipboard): Integrate ClipboardPasteHandlerService for improved paste handling

* feat(clipboard): Rename resolveUrl to resolveIndexedDbUrl for clarity and update references

* feat(clipboard): Add defaultPath option to showOpenDialog for improved user experience

* feat(clipboard): Implement getDefaultClipboardImagesPath utility for consistent image path retrieval

* feat(clipboard): Refactor MIME type handling to use centralized mapping for improved maintainability

* feat(clipboard): Add computed property to toggle markdown parsing based on formatting settings

* revert: Removed unwanted changes.

* revert: Removed unwanted chagnes.

* revert: Removed unwanted chagnes.

* fix(clipboard-images-cfg): remove debug log from selectImagePath method

* refactor(paste-handler): update currentPlaceholder to use getter/setter methods

* fix(docs): correct formatting and improve clarity in multiple wiki pages

* fix: update dialog handling in initLocalFileSyncAdapter for type safety

* revert: Revert space change.

* fix(inline-markdown): ensure model is set to an empty string instead of undefined

* fix(tests): update clipboard images section locator to use collapsible title
2026-02-04 15:46:51 +01:00
Johannes Millan
6ca7223dc6 refactor: add markdown renderer tests and remove dead IPC code
- Add 18 unit tests for marked-options-factory covering:
  - Image renderer (sizing syntax, lazy loading, dimensions)
  - Paragraph renderer (headings h1./h2. syntax)
  - parseImageDimensionsFromTitle helper
  - preprocessMarkdown hook
- Export parseImageDimensionsFromTitle and preprocessMarkdown for testability
- Remove unused IPC.TOGGLE_FULLSCREEN event and handler
  (F11 fullscreen is handled directly in main-window.ts)
2026-01-26 20:55:29 +01:00
Benjamin Hooper
5a2b675383
feat(ui): Desktop Full Screen Mode (F11) #5851 (#6183) 2026-01-26 13:22:32 +01:00
Johannes Millan
1e826698aa fix: linting and compilation errors after file system IPC changes 2025-12-12 20:46:05 +01:00
johannesjo
eefbc7135a fix(electron): sync title bar button colors with app theme
Update window control buttons (minimize, maximize, close) to match
the app's dark/light theme setting instead of always using white.

Fixes #5676
2025-12-07 09:52:20 +01:00
Johannes Millan
c5625de6f1 feat(customWindowTitleBar): add sexy custom window title bar <3 2025-12-02 13:30:36 +01:00
Johannes Millan
9574dd4f19 feat(share): outline multi platform share functionality 2025-10-22 15:35:24 +02:00
Johannes Millan
34ac3dd757 refactor: improve typing 2 2025-08-12 15:27:48 +02:00
Johannes Millan
62c2649320 fix: file saving not working on Ubuntu snap #4901 2025-08-04 21:27:25 +02:00
Johannes Millan
9efa649857 feat: add task toggle start action to protocol handler 2025-08-01 18:46:28 +02:00
Johannes Millan
fb7f925e92 fix: error when using global add task shortcut
Closes #4859
2025-07-28 20:36:49 +02:00
Johannes Millan
2fcd310ea1 Merge branch 'feat/overlay-indicator'
* feat/overlay-indicator:
  feat(overlayIndicator): implement basic overlay

# Conflicts:
#	electron/shared-with-frontend/ipc-events.const.ts
2025-06-27 22:37:14 +02:00
Johannes Millan
d4d81bf511 feat(plugin-api): create foundational plugin API package
- Add @super-productivity/plugin-api package with TypeScript definitions
- Define core plugin interfaces, types, and manifest structure
- Add plugin hooks system for event-driven architecture
- Create plugin API type definitions and constants
- Add documentation and development guidelines
2025-06-27 18:13:19 +02:00
Johannes Millan
1c7eaad5bb feat(overlayIndicator): implement basic overlay 2025-06-27 14:22:27 +02:00
Johannes Millan
d7def289f5 feat(pfapi): make removing files work for local file sync 2025-04-12 13:56:47 +02:00
Johannes Millan
e3eb0742ec feat: improve emoji support and add info #3558 2025-01-03 17:44:21 +01:00
Johannes Millan
866df0c493 feat: add folder picker when there is no valid selected #3473 2024-09-16 20:56:10 +02:00
Johannes Millan
552b25074d feat: check if directory exists #3473 2024-09-16 20:42:49 +02:00
Johannes Millan
cf1c38bf3b refactor: use same backup path everywhere #2910 2023-12-29 14:10:22 +01:00
Johannes Millan
c6d76c6a17 feat: allow for usage of system dark mode 2023-12-20 13:07:50 +01:00
Johannes Millan
a32281e07f feat(electronSecurity): make mac os dark moode stuff work 2023-12-17 13:48:09 +01:00
Johannes Millan
f946607194 feat(electronSecurity): make open path and open external work 2023-12-17 13:41:05 +01:00
Johannes Millan
c6abc8f965 feat(electronSecurity): wrap all send handlers 2023-12-16 14:19:13 +01:00
Johannes Millan
a0a2f44807 refactor(electronSecurity): make typing work in preload for ipc events 2023-12-16 12:53:07 +01:00
Johannes Millan
eaeb833e96 feat(electronSecurity): make apple theme switch work again 2023-12-16 12:20:17 +01:00
Johannes Millan
145de2fba6 feat(electronSecurity): make dev tools and reload work again 2023-12-15 18:35:10 +01:00
Johannes Millan
f9d74f5762 feat(electronSecurity): make app relaunch work 2023-12-15 17:28:06 +01:00
Johannes Millan
849c232d42 feat(electronSecurity): make app.getPath work 2023-12-15 17:23:17 +01:00
Johannes Millan
85046b7e8e feat(focusMode): make session done sound louder 2023-11-15 15:35:15 +01:00
Johannes Millan
4b92a3f9ee feat(fullScreenBlocker): make basic blocker work 2022-10-16 16:00:21 +02:00
Johannes Millan
7ee50dc485 refactor: moved shared stuff to its own folder 2022-03-12 11:59:52 +01:00