Repository navigation
fix(hub-ui-onboard): confirm installs in the terminal - #445
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
👁️🗨️ Review this pull request with grouped, summarized diffs at: raw result💭 analyzed by {
"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"
}
} |
This branch was successfully deployed
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.
Summary
The security review of nuxt/nuxt#36581 found that
@devframes/hub-ui-onboardlet any device that can reach a network-bound dev server run the install, which changespackage.jsonand the lockfile. The only guard was a same-origin check, and a non-browser request passes it when it sends noOriginand noSec-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:
POSTwaits.createOnboarding()calls the newonConfirmoption with{ packages, command, request }. A return oftruestarts the install (202). Any other answer refuses it (403), and the panel says that the dev server did not allow the install.onConfirm, the default printsDF9005and reads oney/Nline from stdin. When stdin is not a TTY (CI, a detached container, a piped task runner), nobody can answer, so it refuses at once withDF9006.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.onConfirmlets an integration ask in its own way, for example throughconsola.promptin a Nuxt module.messagesgets two keys,confirmingandrefused.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 buildandvitest runpass (155 files, 1736 tests). The tsnapi snapshot shows only additions:onConfirm,OnboardingConfirmation, and the two message keys.onConfirmreturnsfalsegives403with no install.truegives202and the install runs. Two clicks share one question. The default refuses without a TTY.confirmInTerminalreadsy,Yes, an empty line andncorrectly, and leaves stdin flowing when another reader was attached first.script):ygives202and the install runs,ngives403, and< /dev/nullgivesDF9006and403. The process exits after each answer.vitedev server under a PTY:yanswers the prompt, and Vite shortcuts (h) still work after it.@nuxt/devtools-onboardas 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.