feat(core): implement global SPEC and fix provider routing failover#68
Open
jbmendonca wants to merge 14 commits intoEvolutionAPI:mainfrom
Open
feat(core): implement global SPEC and fix provider routing failover#68jbmendonca wants to merge 14 commits intoEvolutionAPI:mainfrom
jbmendonca wants to merge 14 commits intoEvolutionAPI:mainfrom
Conversation
…volutionAPI#52) Vault audit §2.S1 CRITICAL: /api/shares/<token>/view had zero rate limiting. Add flask-limiter (in-memory, single-process MVP) with: - 60 req/min/IP on view_share (Vault §2.S1) - Global default 600 req/min on all other routes (non-blocking baseline) - Referrer-Policy, Cache-Control no-store, Pragma, HSTS, X-Content-Type-Options headers on every public share response (Vault §2.S2) The Limiter singleton lives in rate_limit.py to break the circular-import chain between app.py (which imports route blueprints) and the blueprints that need @limiter.limit() decorators. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…ortals (EvolutionAPI#53) * feat(security): rate-limit public share endpoint + security headers Vault audit §2.S1 CRITICAL: /api/shares/<token>/view had zero rate limiting. Add flask-limiter (in-memory, single-process MVP) with: - 60 req/min/IP on view_share (Vault §2.S1) - Global default 600 req/min on all other routes (non-blocking baseline) - Referrer-Policy, Cache-Control no-store, Pragma, HSTS, X-Content-Type-Options headers on every public share response (Vault §2.S2) The Limiter singleton lives in rate_limit.py to break the circular-import chain between app.py (which imports route blueprints) and the blueprints that need @limiter.limit() decorators. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(plugins): B2.0 public_pages capability — read-only token-bound portals Add the public_pages plugin capability (B2.0 scope, read-only): plugin_schema.py: - Add Capability.public_pages and Capability.safe_uninstall enum values - Add PluginPublicPageTokenSource + PluginPublicPage Pydantic models (bundle must be under ui/public/, revoked_when disallowed in v1 to prevent SQL injection) - Extend ReadonlyQuery with public_via + bind_token_param fields - Add 4 PluginManifest model validators: capability required, table slug-prefix, unique ids/route_prefixes, readonly_data references valid page routes/plugin_public_pages.py (new): - GET /p/<slug>/<route_prefix>/<token> — serve portal bundle (60 req/min/IP) - GET /p/<slug>/<route_prefix>/<token>/data — serve token-bound readonly query (120/min) - GET /p/<slug>/public-assets/<path> — serve ui/public/ static assets - Token validation via parametric SQL (identifiers validated at install by schema) - Module-level _PLUGIN_PUBLIC_PREFIXES cache for install/uninstall lifecycle - Vault §B2.S2: Referrer-Policy, Cache-Control no-store, HSTS, X-Content-Type-Options on all responses - CSP: default-src 'self' on portal bundles - Rate limiting via rate_limit.limiter (imported from PR EvolutionAPI#52) app.py: - Import and register plugin_public_pages_bp - /p/... paths already bypass auth_middleware (non-/api/ paths are passthrough) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…vation, sandboxed hook (EvolutionAPI#54) - plugin_schema.py: PluginSafeUninstall, PluginPreUninstallHook, PluginUserConfirmation models + validators (block_uninstall enforcement, preserved_tables slug-prefix check, safe_uninstall_enabled_requires_confirmation, no _orphan_* refs in readonly SQL) - routes/plugins.py: uninstall gate (admin-only, confirmation_phrase, exported_at check, zip_password); sandboxed pre-hook subprocess (no secrets, read-only DB copy, 600s timeout); cascade-DELETE filtering for preserved_host_entities; orphan table rename (_orphan_{slug}_{table}); EVONEXUS_ALLOW_FORCE_UNINSTALL=1 escape hatch with audit; reinstall SHA256 check against plugin_orphans before orphan recovery - app.py: plugin_orphans table migration (id, slug, tablename, orphaned_at, orphaned_by_user_id, original_plugin_version, original_sha256, original_publisher_url, recovered_at, UNIQUE(slug, tablename)) - PluginUninstall.tsx: 3-step wizard (regulatory reason+checkbox → ZIP password → typed phrase); force-uninstall orange banner; integrated in PluginDetail.tsx - docs/plugin-contract.md: full plugin.yaml contract for public_pages + safe_uninstall Vault §B3 mitigations: S1 block_uninstall gate, S2 admin enforcement, S3 sandboxed hook, S4 no _orphan_* SQL refs, S5 AES-256 ZIP password, S6 force-uninstall audit trail. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…er auto-injection (EvolutionAPI#55) Two related Wave 2.1.x extensions to the plugin contract that close the last two gaps blocking endpoint-level RBAC for plugin authors (gap inventory in evonexus-plugin-nutri Step 3 RBAC decision). Changes - PluginWritableResource.requires_role: Optional[List[str]] — when set, the POST/PUT/DELETE handler returns 403 if current_user.role is not in the list. 'admin' role always passes (super-user override). Backwards compatible: resources without the field accept any authenticated user (legacy default). Validator enforces kebab-case role names (^[a-z][a-z0-9-]*$). - routes.plugins.writable_data: enforces requires_role at the endpoint, with a 403 message naming the required roles and the actor's current role. - routes.plugins.readonly_data: auto-injects :current_user_id and :current_user_role as bind params on every readonly query. Plugins can reference them directly in SQL for server-enforced scoping without an app-layer wrapper. The two parameter names are reserved — client requests carrying them in the query string get 400 (no identity spoofing). Tests - tests/backend/test_plugins_rbac_and_scoping.py — 13 cases covering Pydantic acceptance/rejection, 403/200 paths for writable, scoping/spoofing for readonly, backwards compat for resources without requires_role and queries without :current_user_id refs. Compat - Existing plugins (PM Essentials) continue to work unchanged — the new field defaults to None and the auto-injected bind params are silently ignored if the SQL doesn't reference them. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ser, raw bundle for clients (EvolutionAPI#56) The portal_page handler at /p/{slug}/{route_prefix}/{token} previously served the plugin's JS bundle with mimetype application/javascript regardless of the caller. Browsers navigating to a portal URL saw the JS source instead of a rendered page. Discovered during evonexus-plugin-nutri Step 5. Changes - portal_page: when the Accept header includes text/html and NOT application/javascript, render a minimal HTML shell that loads the bundle as <script type="module" src="/p/{slug}/public-assets/{file}"> and instantiates the plugin's declared custom_element_name. Token reaches the element via data-token attribute (no need for the bundle to re-parse window.location). - _serve_html_shell: new helper. Defensive: validates bundle path is inside ui/public/ and custom_element_name matches alphanum-dash before emitting. Sets X-Content-Type-Options: nosniff + a tight CSP (default-src 'self', frame-ancestors 'none', no external scripts). - Programmatic clients (curl, fetch with Accept: application/javascript) keep getting the raw bundle — backwards compatible. - Bundle is fetched from the existing /p/{slug}/public-assets/{path} route (no token), which is safe because the bundle contains no patient data — data lives behind the token-gated /data endpoint. Tests - tests/backend/test_plugin_public_pages_html_shell.py — 9 cases: HTML accept renders shell, JS accept returns bundle, default Accept (*/*) returns bundle, invalid token returns 404 even with HTML accept, CSP + X-Content-Type-Options present, custom element name appears exactly once, XSS-safe (single <script> tag, token only inside data-token), legacy programmatic fetches unchanged. Compat - Existing public-page consumers using fetch() with Accept: application/json or default see no behaviour change. Plugins that already shipped a bundle start rendering correctly in browsers without any plugin update. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…volutionAPI#57) Bumps version to 0.33.0 so the plugin nutri (which requires this version) can install. Bundles the five plugin-contract PRs (EvolutionAPI#52→EvolutionAPI#56) merged today into a single release. Plus a UX fix on the install wizard so 409s say why they conflicted. The fix - lib/api.ts buildError now falls back to data.conflicts[0] when the standard error/message fields are absent. The plugin preview endpoint returns {conflicts: string[], manifest, ...} on 409 — without this fix the wizard showed only "409 CONFLICT" with the actual reason hidden. - PluginInstallModal: conflicts type was Record<string, unknown>, backend always returned string[]; the JSON.keys() coercion produced index strings. Now typed as string[] and rendered as a list. Tested - Frontend tsc --noEmit clean - Plugin nutri 200 pytest still pass after the 11 `# nosec B603` markers added to subprocess.run calls (false positives from regex security scan — all calls use list args, no shell=True) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… routing - Base: upstream main (v0.33.0) with all official updates - Added: TKR security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy) - Added: CORS CSRF token support (X-CSRF-Token headers) - Added: Provider failover chain (getProviderCandidates, resolveProviderChain) - Added: Platform observability, cache, queue, and metrics modules - Added: Runtime config, structured logging, request/session security - Added: Frontend: CommandPalette, ThemeContext, Observability page, SecurityTab - Preserved: upstream OpenRouter Smart Router native integration - Adapted: Provider-config.js enhanced with routing and default_model support
v0.33.0 — plugin contract bundle (PRs EvolutionAPI#52→EvolutionAPI#56) + install error surfacing
- Add /api/onboarding/workspace-status endpoint to detect configured workspace - Auto-skip onboarding in create_user() when workspace is ready - Fix false positive: check providers.json file exists (not _read_config fallback) - Auto-skip in OnboardingRouter for pre-existing users with state=null - Add 'Use existing workspace' button to Welcome screen - Add translation keys for workspace-ready banner
There was a problem hiding this comment.
Sorry @jbmendonca, your pull request is larger than the review limit of 150000 diff characters
Contributor
Author
|
Adding to this PR: A critical stability fix for the Terminal WebSocket proxy. Werkzeug 3.1 dropped native WSGI WebSocket support, causing \lask-sock's \websocket=True\ routes to be ignored behind Traefik unless \wsgi.websocket\ is explicitly set. This resulted in the 200 OK / 404 Node.js fallback error. Added a WSGI Middleware (\ProxyFixWSGI) to intercept \HTTP_UPGRADE\ and inject the flag, plus a documentation file \docs/WEBSOCKET_PROXY_FIX.md\ explaining the architecture. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implementação da SPEC global do projeto e correção do roteador inteligente do OpenRouter (fallback/failover).