Fix(sandbox): repair Worker→Host message routing and add storage backend#1991
Fix(sandbox): repair Worker→Host message routing and add storage backend#1991kaizhou-lab merged 2 commits intomainfrom
Conversation
- Add 'api-call' case in SandboxHost.handleMessage() so Worker→Host RPC (e.g. aion.storage.get/set/delete) is routed to apiHandlers instead of being silently dropped (Bug #4) - Forward 'event' messages to extensionEventBus (ext:* prefix) and onUIMessage callback (ui-message), fixing silent discard (Bug #5) - Add 30s timeout to callMainThread() in sandboxWorker to prevent indefinite hangs when Host fails to respond - Introduce ExtensionStorage: JSON-file-based per-extension KV store that backs the aion.storage API (not yet wired — pending ChannelPlugin migration to SandboxHost) - Add TODO markers in ChannelPluginResolver and lifecycle.ts for future migration from main-process eval('require') to Worker Thread sandbox - Add extension market research docs (security model, architecture, sandbox architecture diagrams, gap analysis)
Code Review:Fix(sandbox): repair Worker→Host message routing and add storage backend (#1991)变更概述本 PR 修复了 SandboxHost 消息路由中的两个关键 bug:Worker 发出的 方案评估结论:✅ 方案合理 PR 正确地在 问题清单🟡 MEDIUM — ExtensionStorage 的 set/delete 直接变异缓存对象文件: 问题代码: async set(extensionName: string, key: string, value: unknown): Promise<void> {
const data = this.loadData(extensionName);
data[key] = value;
this.saveData(extensionName, data);
}
async delete(extensionName: string, key: string): Promise<void> {
const data = this.loadData(extensionName);
delete data[key];
this.saveData(extensionName, data);
}问题说明: 修复建议: async set(extensionName: string, key: string, value: unknown): Promise<void> {
const data = this.loadData(extensionName);
const updated = { ...data, [key]: value };
this.saveData(extensionName, updated);
}
async delete(extensionName: string, key: string): Promise<void> {
const data = this.loadData(extensionName);
const { [key]: _, ...rest } = data;
this.saveData(extensionName, rest);
}🟡 MEDIUM — ExtensionStorage 文件路径未校验 extensionName文件: 问题代码: private getFilePath(extensionName: string): string {
return path.join(this.storageDir, `${extensionName}.json`);
}问题说明:如果 修复建议: import { isPathWithinDirectory } from './pathSafety';
private getFilePath(extensionName: string): string {
const filePath = path.join(this.storageDir, `${extensionName}.json`);
if (!isPathWithinDirectory(filePath, this.storageDir)) {
throw new Error(`[ExtensionStorage] Invalid extension name: "${extensionName}"`);
}
return filePath;
}🔵 LOW — loadData 异常时静默丢弃错误文件: 问题代码: } catch {
// File doesn't exist or is corrupted — start fresh
const empty: Record<string, unknown> = {};
this.cache.set(extensionName, empty);
return empty;
}问题说明:当 JSON 文件损坏时,catch 块静默重置数据而不记录任何日志。文件不存在(ENOENT)不需要日志,但 JSON 解析失败意味着数据丢失,应记录警告以帮助排查问题。 修复建议: } catch (error) {
// ENOENT is expected for new extensions; log corruption for debugging
if (error instanceof Error && !error.message.includes('ENOENT')) {
console.warn(`[ExtensionStorage] Corrupted data for "${extensionName}", starting fresh:`, error.message);
}
const empty: Record<string, unknown> = {};
this.cache.set(extensionName, empty);
return empty;
}汇总
结论本报告由本地 CONCLUSION: CONDITIONAL |
- Use immutable patterns in ExtensionStorage set/delete methods - Add path traversal validation in getFilePath using isPathWithinDirectory Review follow-up for #1991
PR Fix 验证报告原始 PR: #1991
总结: ✅ 已修复 2 个 | ❌ 未能修复 0 个
|
Summary
Closes #1990
SandboxHost.handleMessage()to handleapi-callmessages from Worker, routing them to a genericapiHandlersmap and sendingapi-responseback — previously these were silently dropped, causingaion.storage.*calls to hang forevercase 'event'to forwardext:*events toextensionEventBusandui-messageto anonUIMessagecallback — previously all extension events were silently discardedcallMainThread()insandboxWorker.tsso Worker→Host calls fail with a clear error instead of hanging indefinitelyExtensionStorage: JSON-file-based per-extension KV store backing theaion.storageAPI (not yet wired — pending ChannelPlugin/Lifecycle migration to SandboxHost)ChannelPluginResolverandlifecycle.tsfor future migration from main-processeval('require')to Worker Thread sandboxTest plan
bunx tsc --noEmit— zero errorsbun run lint— zero errorsbun run test— 2226 passed, 0 failedSandboxHostmessage routing (api-call, event, api-response) andExtensionStorage(get/set/delete, isolation, corruption recovery, createApiHandlers)