Drop swagger-ui, document telemetry, add opt-outs (#7524)#7757
Conversation
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
Review Summary by QodoDrop swagger-ui telemetry, add privacy opt-outs, document outbound calls
WalkthroughsDescription• Drops swagger-ui-express dependency (injects Scarf telemetry pixel) • Replaces /api-docs with vendored RapiDoc 9.3.4 (MIT, no outbound calls) • Adds privacy.updateCheck and privacy.pluginCatalog opt-outs (default true) • Publishes PRIVACY.md documenting both outbound calls and disabling each • Updates admin UI to show banner when plugin catalog is disabled Diagramflowchart LR
A["swagger-ui-express<br/>+ Scarf pixel"] -->|removed| B["RapiDoc 9.3.4<br/>vendored"]
C["UpdateCheck.ts<br/>hourly info.json"] -->|gated by| D["privacy.updateCheck"]
E["installer.ts<br/>plugins.json fetch"] -->|gated by| F["privacy.pluginCatalog"]
D -->|default true| G["PRIVACY.md<br/>opt-out guide"]
F -->|default true| G
H["admin plugins page"] -->|disabled signal| I["info banner"]
File Changes1. src/node/utils/Settings.ts
|
Code Review by Qodo
1.
|
Three-deliverable plan: vendor RapiDoc to replace swagger-ui-express (Scarf-injecting), add privacy.updateCheck and privacy.pluginCatalog opt-outs for our two outbound calls, and ship PRIVACY.md as a public stance doc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Twelve TDD-flavoured tasks: privacy settings shape, UpdateCheck + installer opt-outs (each with a failing-test-first cycle), admin backend/UI plumbing, dependency drop, vendored RapiDoc, PRIVACY.md, final verification matrix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds privacy.updateCheck and privacy.pluginCatalog, both defaulting to true so behavior is unchanged until operators opt out. Refs #7524 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
check() and getLatestVersion() now early-return when the setting is off. Logs once on first skip. The admin "update available" panel already tolerates an undefined latestVersion. Refs #7524 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extracts the gate into pluginCatalogGuard.ts so it can be unit-tested under vitest without dragging in the CJS require() chain from installer.ts. getAvailablePlugins() now throws the tagged disabled error before any fetch. Refs #7524 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Short-circuits the four catalog-driven socket events. The install/ uninstall events are untouched so operators can still install by plugin name even when the catalog is disabled. Refs #7524 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Was hardcoding static.etherpad.org and ignoring opt-out. Now exits 0 cleanly when privacy.pluginCatalog=false. Refs #7524 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refs #7524 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops the swagger-ui-express dep (third-party Scarf telemetry pixel, see swagger-api/swagger-ui#10573) and serves /api-docs with a static HTML shell that mounts <rapi-doc>. /api-docs.json is unchanged. The vendored RapiDoc asset is added in the next commit so the tree is broken for one diff hunk — pair this with the rapidoc-min.js commit during review. Refs #7524 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pinned bundle with checksum in VERSION. Replaces swagger-ui-dist which shipped a Scarf telemetry pixel. Disables RapiDoc's bundled Google Fonts request via load-fonts="false" plus explicit regular-font/mono-font system stacks — RapiDoc's CSS @font-face rules would otherwise fetch Open Sans from fonts.gstatic.com at render time. Also fixes the /api-docs route's res.sendFile to use an absolute path resolved via settings.root (the previous {root: 'src/static'} was resolved from CWD which is already src/, producing src/src/static). Refs #7524 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Subscribes to results:catalogDisabled and renders a localized info banner on the plugins page. install/uninstall still function via CLI. Refs #7524 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Publishes Etherpad's stance on telemetry: two documented, opt-out outbound calls; no third-party analytics; no install-time phone-homes in our deps. Refs #7524 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Qodo flagged that checkUpdates emitted the unresolved Promise (missing
await) and emitted {} for updatable on the error path, both breaking
the admin UI's expected string[] shape. Pre-existing bug surfaced when
the surrounding block was edited for the privacy.pluginCatalog gate.
Refs #7524
3a3118a to
3539dcb
Compare
| @@ -0,0 +1,3 @@ | |||
| rapidoc 9.3.4 - vendored 2026-05-15 | |||
There was a problem hiding this comment.
Why rapidoc? It is an unmaintained api doc. Yours is not even the latest one https://github.com/rapi-doc/RapiDoc/releases/tag/v9.3.4 . there is scalar: https://github.com/scalar/scalar
There was a problem hiding this comment.
Good catch — switched to Scalar in 14a0350. RapiDoc is indeed effectively unmaintained.
Scalar 1.57.2 (MIT, actively developed) is now the vendored standalone bundle. Privacy posture is preserved by configuring the embed:
withDefaultFonts: false— nofonts.scalar.comwoff2 fetchagent.disabled: true— noapi.scalar.com/vector/*calls (search/curated/embeddings)mcp.disabled: true— no MCP integration UIshowDeveloperTools: 'never',hideClientButton: true,telemetry: false
Verified with headless Chromium against /api-docs: page mounts Scalar, renders the Etherpad OpenAPI document, and makes zero requests to any host outside localhost.
Per @SamTV12345's review on #7757: RapiDoc has been effectively unmaintained for a while. Scalar (https://github.com/scalar/scalar) is MIT-licensed, actively developed, and ships a self-contained standalone bundle that works the same way for our purposes. Privacy posture is preserved by configuring the embed: - withDefaultFonts: false (no fonts.scalar.com woff2 fetch) - telemetry: false (defensive) - agent.disabled: true (no api.scalar.com/vector/* calls) - mcp.disabled: true (no MCP integration) - showDeveloperTools: 'never' - hideClientButton: true Verified with headless Chromium: page loads /api-docs, mounts Scalar, renders the Etherpad OpenAPI document, and makes zero requests to any host other than localhost. Vendor: - src/static/vendor/scalar/standalone.js (@scalar/api-reference 1.57.2) - src/static/vendor/scalar/VERSION (sha256 pinned) - src/static/vendor/scalar/LICENSE (MIT) Removed: - src/static/vendor/rapidoc/* Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Conflicts: - src/static/js/pluginfw/installer.ts: keep both sides (assertPluginCatalogEnabled from PR + checkEngineCompatibility/InstallerTaskQueue from develop) - pnpm-lock.yaml: regenerated via pnpm install --lockfile-only Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
swagger-ui-express(Scarf telemetry pixel — swagger-api/swagger-ui#10573)/api-docsnow served by vendored RapiDoc 9.3.4 (MIT, ~647KB), withload-fonts="false"+ explicit system-font stacks so no Google Fonts request eitherprivacy.updateCheckandprivacy.pluginCatalogopt-outs (defaulttrue; behaviour unchanged for existing operators)PRIVACY.mddocumenting both outbound calls and how to disable eachbin/plugins/stalePlugins.tsreadssettings.updateServerand honours the new flagCloses #7524.
How the opt-outs behave
privacy.updateChecktrueUpdateCheck.check()andgetLatestVersion()early-return; one-time log line on first skip; admin "update available" panel simply omits the lineprivacy.pluginCatalogtruegetAvailablePlugins()throws a tagged disabled error; admin socket handlers emitresults:catalogDisabled; admin plugin page renders an info banner pointing topnpm run plugins i ep_<name>; install/uninstall by name still workVendored asset audit
grep -E "scarf|google-analytics|googletagmanager|sentry|datadog|segment\.|mixpanel|amplitude|navigator\.sendBeacon" src/static/vendor/rapidoc/rapidoc-min.js→ no matchesgrep -oE "https?://..."→ sort -u):fonts.gstatic.com/...woff2→ embedded in@font-face; disabled viaload-fonts="false"+ explicitregular-font/mono-fontattributes + CSS overridegithub.com/...,marked.js.org→ comment-only linkssrc/static/vendor/rapidoc/VERSIONTest plan
pnpm test:vitest→ 603 / 603 (3 new specs:privacy/settings-defaults,privacy/updateCheck-optout,privacy/installer-optout)pnpm exec tsc --noEmitclean insrc/andadmin//api-docsrenders RapiDoc; DevTools Network shows no third-party hosts (Google Fonts disabled at attribute level)privacy.updateCheck: falselogs "Update check disabled..." on boot; noinfo.jsonrequestprivacy.pluginCatalog: false→ noplugins.jsonrequest on idle serverprivacy.pluginCatalog: false, log into/admin/pluginsand confirm the localised info banner renders (admin_plugins.catalog_disabled)grep -rIn "swagger-ui-express" src admin bin→ no resultsNotes for reviewers
src/static/js/pluginfw/pluginCatalogGuard.ts(small standalone helper). This is a deliberate split so the gate is unit-testable under vitest without dragging ininstaller.ts's CJSrequire('./plugins')chain.src/node/types/SwaggerUIResource.tsis a TypeScript-only type used byopenapi.ts; kept as-is. The unrelated comment atopenapi.ts:810mentioningswagger-node-expressis historical and kept.@scarf/scarfis already listed underignoredBuiltDependenciesinpnpm-workspace.yaml, so even if a future transitive dep pulls Scarf in, its install-time pixel is suppressed. PRIVACY.md notes this.🤖 Generated with Claude Code