super-productivity/packages
oon arfiandwi 0c07053032
feat(plugin): add PluginAPI.request with manifest allowedHosts allowlist (#8721)
* feat(plugin): add generic request capability to PluginAPI

* feat(plugin): gate PluginAPI.request behind manifest allowedHosts

PluginAPI.request is otherwise reachable to any host the shared SSRF
filter does not block. Add a manifest-declared, host-enforced allowlist
so a plugin's outbound reach is explicit and reviewable at install.

- PluginManifest.allowedHosts: exact hostnames the plugin may reach.
- PluginBridgeService enforces it before the shared HTTP/SSRF layer:
  exact host, case-insensitive, trailing-dot tolerant, port-agnostic;
  userinfo tricks resolved via URL parsing; fail-closed (empty/omitted
  allowedHosts disables request entirely).
- validatePluginManifest rejects malformed allowedHosts at install.

Companion tests: bridge enforcement (reject non-declared host,
fail-closed on undefined/empty, case-insensitive + trailing-dot,
userinfo-trick blocked) and manifest validation.

* style(plugin): fix prettier formatting flagged by CI in request API

The generic-request commit slipped past checkFile on three files; CI
prettier flagged the multi-line request() signature and a long spec
assertion string. Formatting only, no behavior change.

* feat(plugin): require "http" permission for PluginAPI.request

Network egress becomes an explicit, opt-in capability (like nodeExecution):
request now requires "http" in the manifest permissions in addition to a
matching allowedHosts entry. Missing either is fail-closed. Enforced
host-side (before the shared SSRF layer) on both the iframe and
Function-sandbox paths; the "http" permission gets a human-readable line in
the plugin security info.

Tests: fail-closed without the "http" permission.

* feat(plugin): surface plugin network reach in the plugin-management UI

Render the manifest allowedHosts as a chip-set beside permissions/hooks
(with a count in the collapsible title), so a plugin's outbound network
reach is reviewable in-app instead of only in the raw manifest.

Tests: allowedHosts shown in the collapsible title, omitted when none.

* feat(plugin): block redirects on PluginAPI.request (SSRF-via-redirect)

Only the initial request URL is allowlist/SSRF-checked, but HttpClient/XHR
auto-follows 3xx — so a declared host could 302 to a private/metadata IP and
return internal content to the plugin. Execute the request path via fetch with
redirect:"error" so any redirect is refused instead of chased.

- New PluginHttpHelperOpts.blockRedirects (default false); PluginBridge sets it
  for request. Issue-provider HTTP is untouched (still HttpClient).
- fetch executor preserves the HttpClient contract callers depend on: query
  params, timeout via AbortController (covering the body read, not just headers),
  text/json parsing, and non-2xx rejecting with .status + .error.
- Only plain objects/arrays are JSON-serialized; FormData/Blob/URLSearchParams/
  ArrayBuffer(View) pass through, and Content-Type: application/json is added
  only for the JSON case (HttpClient parity).
- Web/desktop only: on native, Capacitor patches fetch and ignores
  redirect/signal, so native falls back to HttpClient (documented limitation).
- Blocks all redirects incl. benign same-host ones; manual per-hop re-validation
  is not possible on the web (cross-origin Location is opaque). Documented.

Tests: redirect refused, .status/.error parity, SSRF still pre-checked,
body pass-through, params + responseType:text, real-timer timeout, and native
falls back to HttpClient.

* fix(plugin): gate allowedHosts UI on the "http" capability

The plugin-management panel showed a "Network access" section (and title count)
whenever allowedHosts was non-empty, regardless of permissions — advertising
reach for a capability the bridge rejects without the "http" permission. Gate the
section, the chip loop, and the title count on a getNetworkReachHosts() helper
(hosts only when permissions includes "http"). Addresses review on #8721.

* docs(plugin): note _requestNoRedirect bypasses NetworkRetryInterceptorService

The fetch path skips Angular HTTP_INTERCEPTORS, so PluginAPI.request GETs lose the
single status-0 retry the HttpClient paths keep. Inherent to redirect:"error"
(XHR/HttpClient cannot block redirects). Flagged in review on #8721.
2026-07-07 11:50:17 +02:00
..
plugin-api feat(plugin): add PluginAPI.request with manifest allowedHosts allowlist (#8721) 2026-07-07 11:50:17 +02:00
plugin-dev fix(caldav): handle numeric event shape in extractSyncValues (#8564) (#8573) 2026-06-24 13:46:40 +02:00
shared-schema Cleanup/remove dead super sync surface (#8526) 2026-06-22 14:34:18 +02:00
super-sync-server chore(deps): bump @electric-sql/pglite from 0.5.2 to 0.5.3 (#8654) 2026-07-01 12:31:42 +02:00
sync-core refactor(sync-core): drop shared-schema vector-clock compat re-export anda app enum (#8441) 2026-06-17 16:21:26 +02:00
sync-providers fix(sync): stop file-based E2EE key loss from leaking plaintext (GHSA-9544) (#8800) 2026-07-06 18:49:52 +02:00
vite-plugin chore(deps): bump esbuild in the npm_and_yarn group across 0 directory (#8451) 2026-06-17 14:41:32 +02:00
build-packages.js fix(plugin): refresh procrastination buster i18n #5102 (#8145) 2026-06-08 20:44:43 +02:00
README.md feat: cleanup and update plugins 2 2025-06-29 06:36:02 +02:00

Super Productivity Packages

This directory contains plugin packages and the plugin API for Super Productivity.

Structure

  • plugin-api/ - TypeScript definitions for the plugin API
  • plugin-dev/ - Plugin development examples and tools
    • api-test-plugin/ - Basic API test plugin
    • procrastination-buster/ - Example SolidJS-based plugin
    • yesterday-tasks-plugin/ - Simple plugin showing yesterday's tasks
    • boilerplate-solid-js/ - Template for creating new SolidJS plugins (not built)
    • sync-md/ - Markdown sync plugin (not built)

Building Packages

All packages are built automatically when running the main build process:

npm run build:packages

This command:

  1. Builds the plugin-api TypeScript definitions
  2. Builds plugins that require compilation (e.g., procrastination-buster)
  3. Copies plugin files to src/assets/ for inclusion in the app

Development

To work on a specific plugin:

cd plugin-dev/[plugin-name]
npm install
npm run dev

Adding a New Plugin

  1. Create a new directory in plugin-dev/
  2. Add the plugin configuration to /packages/build-packages.js
  3. Run npm run build:packages to test the build

Notes

  • The boilerplate-solid-js and sync-md plugins are development templates and are not included in production builds
  • Plugin files are automatically copied to src/assets/ during the build process
  • The build script handles dependency installation automatically