Skip to content

fix(hub-ui-onboard): confirm installs in the terminal - #445

Merged
antfu merged 3 commits into
mainfrom
fix/onboard-require-terminal-code
Oct 14, 2026
Merged

antfu merged 3 commits into
mainfrom
fix/onboard-require-terminal-code

Conversation

@antfubot

@antfubot antfubot commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

The security review of nuxt/nuxt#36581 found that @devframes/hub-ui-onboard let any device that can reach a network-bound dev server run the install, which changes package.json and the lockfile. The only guard was a same-origin check, and a non-browser request passes it when it sends no Origin and no Sec-Fetch-Site. The full Nuxt DevTools asked for a developer-held token before such actions.

Each install from the panel now needs the developer's permission:

  1. The user clicks Install. The panel shows "Confirm in your terminal..." and its POST waits.
  2. createOnboarding() calls the new onConfirm option with { packages, command, request }. A return of true starts the install (202). Any other answer refuses it (403), and the panel says that the dev server did not allow the install.
  3. Without onConfirm, the default prints DF9005 and reads one y/N line from stdin. When stdin is not a TTY (CI, a detached container, a piped task runner), nobody can answer, so it refuses at once with DF9006.

Only one question is open at a time. Clicks while it is open share its answer and start one install. The same-origin check still runs before any question. Disable entirely does not ask, because it writes only node_modules/.devframe/hub-ui-onboard.json.

onConfirm lets an integration ask in its own way, for example through consola.prompt in a Nuxt module. messages gets two keys, confirming and refused.

[DF9005] A browser asked to run `npm i -D @nuxt/devtools`. Type y and press Enter to allow it.
├▶ fix: If you did not click Install, press Enter to refuse. Another device can reach this dev server, so bind it to localhost.
╰▶ see: https://devfra.me/errors/df9005

The first commit used a 6-digit code typed into the panel. The second commit replaces it with this flow.

refs nuxt/nuxt#36581

Verification

  • pnpm lint, pnpm knip, pnpm typecheck, pnpm build and vitest run pass (155 files, 1736 tests). The tsnapi snapshot shows only additions: onConfirm, OnboardingConfirmation, and the two message keys.
  • New tests cover these cases. onConfirm returns false gives 403 with no install. true gives 202 and the install runs. Two clicks share one question. The default refuses without a TTY. confirmInTerminal reads y, Yes, an empty line and n correctly, and leaves stdin flowing when another reader was attached first.
  • Manual check under a real PTY (script): y gives 202 and the install runs, n gives 403, and < /dev/null gives DF9006 and 403. The process exits after each answer.
  • Manual check with a real vite dev server under a PTY: y answers the prompt, and Vite shortcuts (h) still work after it.
  • The panel was checked in the package playground. It shows the waiting label, then the refused note.
  • Not verified: the Nuxt integration in perf(nuxt): use @nuxt/devtools-onboard as a minimal devtools shim nuxt/nuxt#36581. nuxi forks the dev server with an inherited stdin, so the default prompt is expected to work there.

This PR was created with the help of an AI agent.

@vercel

vercel Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
devframe Ready Ready Preview Oct 14, 2026 5:18am UTC

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

👁️‍🗨️ Review this pull request with grouped, summarized diffs at:
👉 https://pulls.review/gh/devframes/devframe/445?from=github-actions[bot]

raw result

💭 analyzed by vercel-ai-gateway/anthropic/claude-sonnet-5
🕰️ 2026-10-14 05:16 UTC
🔗 head 401ebed
🤖 automated by pulls.review

{
  "headSha": "401ebed4fd55fc503ebc6f93263478c59a135737",
  "result": {
    "overallSummary": "This PR closes a security gap in `@devframes/hub-ui-onboard` where any device reaching a network-bound dev server could trigger a package install via a weak same-origin check. It adds an `onConfirm` hook that must approve every panel-triggered install, defaulting to a `y/N` terminal prompt that auto-refuses when stdin isn't a TTY, with matching client UI, tests, playground support, and documentation (including two new error codes, DF9005/DF9006).",
    "groups": [
      {
        "key": "confirm-flow",
        "label": "Terminal confirmation flow",
        "summary": "Adds an `onConfirm` hook that every panel-triggered install must pass, with a default that asks a `y/N` question in the terminal and refuses when stdin is not a TTY, closing a gap where any network-reachable device could trigger an install through the same-origin-only check.",
        "category": "security",
        "filePaths": [
          "packages/hub-ui-onboard/src/index.ts",
          "packages/hub-ui-onboard/src/terminal-confirm.ts",
          "packages/hub-ui-onboard/src/types.ts",
          "packages/hub-ui-onboard/src/diagnostics.ts"
        ],
        "critical": true,
        "fileNotes": [
          {
            "path": "packages/hub-ui-onboard/src/index.ts",
            "text": "`startInstall` dedupes concurrent requests into a single in-flight confirmation (`asking`), so multiple clicks share one answer and start at most one install.",
            "critical": true
          },
          {
            "path": "packages/hub-ui-onboard/src/terminal-confirm.ts",
            "text": "Reads a single line from stdin without disturbing another consumer (e.g. Vite's keypress shortcuts) if one was already attached and flowing.",
            "critical": true
          }
        ]
      },
      {
        "key": "client-ui",
        "label": "Panel install UX",
        "summary": "Updates the panel's install flow to show a \"Confirm in your terminal...\" state while waiting on the server, and a refusal message if the server denies the install.",
        "category": "ui",
        "filePaths": [
          "packages/hub-ui-onboard/src/client/index.ts"
        ],
        "critical": true
      },
      {
        "key": "tests",
        "label": "Confirmation tests",
        "summary": "Covers the new confirm flow: allow/deny outcomes, shared in-flight questions, no-TTY refusal, and stdin line parsing without stealing the stream from other readers.",
        "category": "tests",
        "filePaths": [
          "packages/hub-ui-onboard/test/onboarding.test.ts",
          "packages/hub-ui-onboard/test/terminal-confirm.test.ts"
        ]
      },
      {
        "key": "playground",
        "label": "Playground demo updates",
        "summary": "Extends the local playground's mock `__onboard` routes and UI to simulate the new confirm/refuse install outcome.",
        "category": "examples",
        "filePaths": [
          "packages/hub-ui-onboard/playground/index.html",
          "packages/hub-ui-onboard/playground/vite.config.ts"
        ]
      },
      {
        "key": "docs",
        "label": "Docs and error reference",
        "summary": "Documents the new confirmation step, the `onConfirm` option, and the new DF9005/DF9006 error codes.",
        "category": "docs",
        "filePaths": [
          "docs/content/1.guide/23.hub-ui-onboard.md",
          "docs/content/6.errors/DF9005.md",
          "docs/content/6.errors/DF9006.md",
          "docs/content/8.references/6.hub-api.md",
          "examples/hub-onboard-vite/README.md"
        ]
      },
      {
        "key": "snapshot",
        "label": "Type snapshot update",
        "summary": "Updates the generated public API type snapshot to reflect the new `onConfirm` option, `OnboardingConfirmation` type, and message keys.",
        "category": "tests",
        "filePaths": [
          "tests/__snapshots__/tsnapi/@devframes/hub-ui-onboard/index.snapshot.d.ts"
        ]
      }
    ],
    "schemaVersion": 1,
    "source": "llm",
    "generatedAt": "2026-10-14T05:16:28.859Z",
    "model": "vercel-ai-gateway/anthropic/claude-sonnet-5",
    "locale": "en"
  }
}

@antfubot antfubot changed the title fix(hub-ui-onboard): require a terminal code to install or disable fix(hub-ui-onboard): confirm installs in the terminal Oct 14, 2026

This branch was successfully deployed

1 active deployment
Preview — 401ebed4 Deployed Oct 14, 2026 by vercel[bot]
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.

2 participants