Skip to content

feat(ui-builder): complete v3 plugin sweep - #148

Merged
olliethedev merged 2 commits into
v3from
feat/ui-builder-phase2-sweep
Aug 20, 2026
Merged

feat(ui-builder): complete v3 plugin sweep#148
olliethedev merged 2 commits into
v3from
feat/ui-builder-phase2-sweep

Conversation

@olliethedev

@olliethedev olliethedev commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • migrate UI Builder hooks and SSR loaders to the shared resource/query-key layer while preserving CMS cache-key compatibility
  • add route/action permissions, provider notifications, localization overrides, lifecycle errors, and resource-form server validation handling
  • keep the third-party UI Builder source unchanged and make the BTST registry depend on its upstream shadcn registry instead of embedding a local copy
  • document the new public hook, query-key export, permissions, localization, and registry ownership boundary

Verification

  • pnpm build
  • pnpm typecheck
  • pnpm knip
  • pnpm --filter @btst/stack test — 476 tests passed
  • focused UI Builder/SSR suite — 16 tests passed
  • pnpm --filter @btst/stack test-registry
  • cd docs && pnpm build
  • UI Builder Playwright smoke suite in freshly generated Next.js, React Router, and TanStack consumers — 24 tests passed
  • protected upstream paths have no diff from v3:
    • packages/ui/src/components/ui-builder/**
    • packages/ui/src/lib/ui-builder/**

Notes

The pre-existing workspace change to pnpm-lock.yaml is intentionally excluded.


Note

Cursor Bugbot is generating a summary for commit 7c663c0. Configure here.

@vercel

vercel Bot commented Aug 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
better-stack-docs Ready Ready Preview Aug 20, 2026 2:15pm
better-stack-playground Ready Ready Preview Aug 20, 2026 2:15pm

Request Review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security Review — feat(ui-builder): complete v3 plugin sweep

Result: No high-confidence vulnerabilities found. The PR is primarily a refactor (query-key extraction, hook simplification, i18n/notify plumbing) plus a meaningful security improvement. Analysis by area follows.


✅ Security improvement: client-side permission gating added

Previously, the Edit and Delete actions in the page list had no CanAccess guard — any authenticated user could see and invoke them regardless of role. This PR wraps each action correctly:

// page-list-page.internal.tsx (new)
<CanAccess resource="ui-builder:page" action="update" params={{ id: page.id }}>
  <DropdownMenuItem >Edit</DropdownMenuItem>
</CanAccess>
<CanAccess resource="ui-builder:page" action="delete" params={{ id: page.id }}>
  <DropdownMenuItem >Delete</DropdownMenuItem>
</CanAccess>

Similarly, PageListPage and PageBuilderPage now pass a permission prop to ComposedRoute, enforcing a read/create/update check at the route boundary. This is additive hardening.


⚠️ Low-confidence observations (not blocking, context-dependent)

1. Error messages surface to the admin UI

DefaultErrorComponent (and DefaultErrorComponent in page-renderer.tsx) renders raw error messages:

{(error instanceof Error ? error.message : undefined) || unexpectedError}

This is a React text node — no XSS risk. However, if the backend ever returns error messages containing internal details (stack traces, DB schema hints), those would appear in the admin UI. Since this panel is admin-only and the pattern existed before the PR, this is low severity, but the server should ensure error responses are appropriately sanitized before reaching the client.

2. bySlug hook passes the slug as a raw query parameter without hook-level validation

// query-keys.ts
bySlug: {
  query: (slug: string) => ({ slug, limit: 1 }),

The public useUIBuilderPageBySlug(slug) hook accepts an arbitrary string. If a consumer passes a value sourced from URL params without sanitizing it first (e.g. useUIBuilderPageBySlug(params.slug)), the raw value is forwarded as a query parameter. The server must validate and reject unexpected values. The hook could defensively validate the slug, but this is a consumer-side concern.

3. External registry URL is pinned to a branch HEAD (pre-existing)

// build-registry.ts (pre-existing entry, not introduced by this PR)
"https://raw.githubusercontent.com/olliethedev/ui-builder/refs/heads/main/registry/block-registry.json"

This URL resolves to the live main branch, not a tagged release or commit hash. A supply-chain compromise of that upstream repo would be pulled in on the next registry rebuild. This risk is not introduced by this PR — the URL was already present and the new EXTERNAL_ONLY_REGISTRY_COMPONENTS logic correctly routes to it rather than embedding local copies. It is flagged here for awareness. Pinning to a commit SHA or version tag would harden this.


No findings in

  • Injection (SQL / command / path traversal): typeSlug is hardcoded to UI_BUILDER_TYPE_SLUG; no user-controlled values flow into URL path segments. slug in create body is constrained client-side by ^[a-z0-9-]+$ before submission.
  • Secrets / token leakage: No credentials logged or embedded. headers forwarded from plugin overrides follows the established pattern.
  • SSRF / open redirect: Post-create redirect uses basePath (host-controlled) + server-returned page.id, not user input.
  • Unsafe deserialization: layers and variables are JSON.stringify-ed client-side before transmission; the server is responsible for schema validation on ingest.
  • Dependency / supply-chain (new): No new third-party packages added. sonner is removed as a dependency from this plugin (replaced by useNotify), which is a positive reduction in surface area.
  • Auth bypass: All client-side permission gates (CanAccess, ComposedRoute permission) are UI convenience — the authoritative enforcement remains server-side as expected.
Open in Web View Automation 

Sent by Cursor Automation: Find vulnerabilities

@github-actions

Copy link
Copy Markdown
Contributor

Shadcn registry validated — no registry changes detected.

@vercel
vercel Bot temporarily deployed to Preview – better-stack-docs August 20, 2026 14:10 Inactive
@olliethedev
olliethedev merged commit 9f18db6 into v3 Aug 20, 2026
11 of 12 checks passed
@olliethedev
olliethedev deleted the feat/ui-builder-phase2-sweep branch August 20, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant