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
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.
* 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>
* 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