Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/devtools-kit/src/_types/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export interface ClientFunctions {
refresh: (event: ClientUpdateEvent) => void
callHook: (hook: string, ...args: any[]) => Promise<void>
navigateTo: (path: string) => void
openUrl: (url: string) => void

onTerminalData: (_: { id: string, data: string }) => void
onTerminalExit: (_: { id: string, code?: number }) => void
Expand Down
1 change: 1 addition & 0 deletions packages/devtools/client/pages/settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const editorOptions = [
['Sublime Text', 'sublime'],
['Atom', 'atom'],
['Windsurf', 'windsurf'],
['Google Antigravity', 'antigravity'],
]

const scaleOptions = [
Expand Down
3 changes: 3 additions & 0 deletions packages/devtools/client/setup/client-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ export function setupClientRPC() {
if (router.currentRoute.value?.path !== path)
router.push(path)
},
async openUrl(url: string) {
window.open(url, '_blank')
},
Comment on lines +43 to +45
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

openUrl broadcasts to all clients with no URL validation — arbitrary-URL injection risk.

openUrl is now a general-purpose client RPC callable by any server-side code holding ctx.rpc. A malicious or compromised Nuxt module can call ctx.rpc.broadcast.openUrl('https://attacker.example/phish'), causing every connected DevTools client to open the attacker URL silently in a new tab. There is no restriction at the client handler to antigravity.google or even to the https: scheme (a javascript: URL, while blocked by most browsers in window.open, is a valid string today).

At minimum, validate the protocol before calling window.open:

🛡️ Proposed fix
     async openUrl(url: string) {
-      window.open(url, '_blank')
+      if (!url.startsWith('https://'))
+        return
+      window.open(url, '_blank', 'noopener,noreferrer')
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async openUrl(url: string) {
window.open(url, '_blank')
},
async openUrl(url: string) {
if (!url.startsWith('https://'))
return
window.open(url, '_blank', 'noopener,noreferrer')
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/devtools/client/setup/client-rpc.ts` around lines 43 - 45, The
openUrl client RPC (function openUrl) currently calls window.open(url, '_blank')
with no validation, allowing arbitrary-URL injection when broadcast; fix by
validating and sanitizing the incoming URL before opening: parse the url string
(new URL(url) in a try/catch), ensure the protocol is either "https:" (and
optionally "http:" if acceptable), and optionally check against a small
whitelist of allowed hostnames or same-origin rules; if validation fails, do not
call window.open and instead call console.warn or log the rejected URL; update
the openUrl handler to perform this validation and only call window.open for
validated URLs.

} satisfies ClientFunctions)

// Re-register client functions now that they're populated
Expand Down
12 changes: 6 additions & 6 deletions packages/devtools/src/server-rpc/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,8 @@ const ABSOLUTE_PATH_RE = /^[a-z]:|^\//i
const FILE_LINE_COL_RE = /^(.*?)(:[:\d]*)$/
const NUXT_WELCOME_RE = /<NuxtWelcome\s*\/>/

export function setupGeneralRPC({
nuxt,
options,
refresh,
openInEditorHooks,
}: NuxtDevtoolsServerContext) {
export function setupGeneralRPC(ctx: NuxtDevtoolsServerContext) {
const { nuxt, options, refresh, openInEditorHooks } = ctx
const components: Component[] = []
const imports: Import[] = []
const importPresets: Import[] = []
Expand Down Expand Up @@ -223,6 +219,10 @@ export function setupGeneralRPC({
let editor = getOptions()?.behavior.openInEditor ?? undefined
if (editor === 'auto')
editor = undefined
if (editor === 'antigravity') {
ctx.rpc.broadcast.openUrl(`https://antigravity.google/open?file=${path}${suffix}`)
return true
}
Comment on lines +222 to +225
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

File path is not URL-encoded — paths with spaces or special characters will generate broken URLs.

path is an absolute OS path (e.g., /home/john/my projects/src/App.vue) and suffix contains colons (e.g., :10:5). Neither is encoded before being spliced into the query string, so:

  • A space in any directory name produces an invalid URL (space is not a legal query-string character).
  • Characters like #, &, and ? in a path fragment or break the query string.
  • Windows paths with backslashes pass through unescaped.
🐛 Proposed fix
-          ctx.rpc.broadcast.openUrl(`https://antigravity.google/open?file=${path}${suffix}`)
+          ctx.rpc.broadcast.openUrl(`https://antigravity.google/open?file=${encodeURIComponent(path + suffix)}`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (editor === 'antigravity') {
ctx.rpc.broadcast.openUrl(`https://antigravity.google/open?file=${path}${suffix}`)
return true
}
if (editor === 'antigravity') {
ctx.rpc.broadcast.openUrl(`https://antigravity.google/open?file=${encodeURIComponent(path + suffix)}`)
return true
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/devtools/src/server-rpc/general.ts` around lines 222 - 225, The URL
built in the antigravity branch uses raw path and suffix which breaks for
spaces, backslashes and special characters; update the branch where editor ===
'antigravity' to URL-encode both path and suffix before calling
ctx.rpc.broadcast.openUrl (e.g., normalize Windows backslashes to forward
slashes on path, then apply encodeURIComponent to the path and to suffix) and
use those encoded values when constructing the query string so openUrl receives
a safe, valid URL.

await import('launch-editor').then(r => (r.default || r)(path + suffix, editor))
return true
}
Expand Down
Loading