mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(plugins): validate example panel reply dispatch (#31877)
* fix(plugins): validate example panel reply dispatch * style(plugins): brace panel reply admission guard
This commit is contained in:
@@ -51,14 +51,14 @@
|
||||
<script>
|
||||
'use strict'
|
||||
var seq = 0
|
||||
var pending = {}
|
||||
var pending = new Map()
|
||||
var statusEl = document.getElementById('status')
|
||||
var termsEl = document.getElementById('terms')
|
||||
|
||||
function call(action, params) {
|
||||
return new Promise(function (resolve) {
|
||||
var requestId = 'req-' + ++seq
|
||||
pending[requestId] = resolve
|
||||
pending.set(requestId, resolve)
|
||||
// The panel frame is an opaque origin, so '*' is the only usable
|
||||
// targetOrigin; the host verifies the sending window instead.
|
||||
window.parent.postMessage(
|
||||
@@ -70,10 +70,12 @@
|
||||
|
||||
window.addEventListener('message', function (event) {
|
||||
var data = event.data
|
||||
if (!data || data.type !== 'orca-panel-action-result') return
|
||||
var resolve = pending[data.requestId]
|
||||
if (event.source !== window.parent || !data || data.type !== 'orca-panel-action-result') {
|
||||
return
|
||||
}
|
||||
var resolve = pending.get(data.requestId)
|
||||
if (!resolve) return
|
||||
delete pending[data.requestId]
|
||||
pending.delete(data.requestId)
|
||||
resolve(data)
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { createContext, runInContext } from 'node:vm'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
describe('hello-orca panel replies', () => {
|
||||
it('only resolves an outstanding request once, from the parent window', async () => {
|
||||
const parent = { postMessage: vi.fn() }
|
||||
let receive: (event: { source: unknown; data: unknown }) => void = () => {
|
||||
throw new Error('Panel did not register its message listener')
|
||||
}
|
||||
const context = createContext({
|
||||
window: {
|
||||
parent,
|
||||
addEventListener: (name: string, listener: typeof receive) => {
|
||||
if (name === 'message') {
|
||||
receive = listener
|
||||
}
|
||||
}
|
||||
},
|
||||
document: { getElementById: () => ({ addEventListener: vi.fn() }) }
|
||||
})
|
||||
const html = readFileSync(
|
||||
new URL('../../../examples/plugins/hello-orca/panel.html', import.meta.url),
|
||||
'utf8'
|
||||
)
|
||||
const script = html.match(/<script>([\s\S]*?)<\/script>/)?.[1]
|
||||
if (!script) {
|
||||
throw new Error('Panel script missing')
|
||||
}
|
||||
runInContext(script, context)
|
||||
const resolved = vi.fn()
|
||||
const request = runInContext("call('workspace.readContext')", context).then(resolved)
|
||||
const reply = {
|
||||
type: 'orca-panel-action-result',
|
||||
requestId: 'req-1',
|
||||
ok: true,
|
||||
value: null
|
||||
}
|
||||
|
||||
for (const requestId of ['__proto__', 'constructor', '__defineGetter__', 'unknown']) {
|
||||
expect(() => receive({ source: parent, data: { ...reply, requestId } })).not.toThrow()
|
||||
}
|
||||
receive({ source: {}, data: reply })
|
||||
await Promise.resolve()
|
||||
expect(resolved).not.toHaveBeenCalled()
|
||||
receive({ source: parent, data: reply })
|
||||
await request
|
||||
expect(resolved).toHaveBeenCalledExactlyOnceWith(reply)
|
||||
receive({ source: parent, data: reply })
|
||||
expect(runInContext('pending.size', context)).toBe(0)
|
||||
expect(parent.postMessage).toHaveBeenCalledExactlyOnceWith(
|
||||
{
|
||||
type: 'orca-panel-action',
|
||||
requestId: 'req-1',
|
||||
action: 'workspace.readContext',
|
||||
params: undefined
|
||||
},
|
||||
'*'
|
||||
)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user