Commit graph

5 commits

Author SHA1 Message Date
Johannes Millan
08d13fd7d5
fix(local-rest-api): validate PATCH/create field value types (#8738)
PATCH /tasks/:id picked allowed keys but never checked value types, so a
wrong-typed value (e.g. tagIds:123, timeEstimate:"abc") was written into
the store and the synced op-log, corrupting state locally and tripping
typia-as-corrupt on other devices when the op replays. Both PATCH and
create now typia.validate the values and return 400 before dispatching.

Also harden the Electron server: reset isListening eagerly and call
closeAllConnections() on stop so a keep-alive socket can't leave the
server stuck and no-op a later re-enable, reject requests with 503 while
disabled, and log an actionable message on EADDRINUSE.

Closes #8732
Refs #7484
2026-07-03 18:20:33 +02:00
Johannes Millan
4d8605baa3 fix(security): harden XSS sinks and add Origin check (#7413)
Address Snyk findings raised in discussion #7413:

- error-handler: inject error title, additionalLog, stackTrace, and
  meta into the global error alert via textContent/setAttribute instead
  of innerHTML interpolation. Also fixes a latent <h2> close-tag typo.
- break-reminder-overlay: use textContent (not innerHTML) for the
  reminder message; switch to decodeURIComponent to match a producer-
  side encodeURIComponent change (also fixes a latent parse bug for
  messages containing & or =).
- local-rest-api: reject any request that arrives with a web Origin
  header. Closes the simple-POST CSRF gap (text/plain bodies are not
  preflighted by CORS) on top of the existing Host allowlist.

The Snyk findings on Google OAuth CLIENT_SECRET (Desktop client per
RFC 8252) and proxy-agent rejectUnauthorized (opt-in for self-hosted
WebDAV with self-signed certs) are documented false positives and
intentional, respectively; both already carry explanatory comments.
2026-05-06 21:37:06 +02:00
Nicholas Dias
7a51d4b06e
feat: allow dev override for local REST API (#7440) 2026-05-01 16:54:26 +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