Skip to content
Closed
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 apps/desktop/src/main/database/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export { DatabaseAdapter } from "@prompthub/db";
export type { Database } from "@prompthub/db";
export { SCHEMA_TABLES, SCHEMA_INDEXES, SCHEMA } from "@prompthub/db";
export { PromptDB } from "@prompthub/db";
export { PromptRelationDB } from "@prompthub/db";
export { FolderDB } from "@prompthub/db";
export { SkillDB } from "@prompthub/db";
export { RuleDB } from "@prompthub/db";
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/src/main/ipc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ const REBINDABLE_DB_CHANNELS = [
IPC_CHANNELS.PROMPT_INSERT_DIRECT,
IPC_CHANNELS.PROMPT_SYNC_WORKSPACE,
IPC_CHANNELS.PROMPT_MIGRATE_IDB_BATCH,
IPC_CHANNELS.PROMPT_MOVE,
IPC_CHANNELS.PROMPT_RELATION_CREATE,
IPC_CHANNELS.PROMPT_RELATION_LIST,
IPC_CHANNELS.PROMPT_RELATION_UPDATE,
IPC_CHANNELS.PROMPT_RELATION_DELETE,
IPC_CHANNELS.VERSION_GET_ALL,
IPC_CHANNELS.VERSION_CREATE,
IPC_CHANNELS.VERSION_ROLLBACK,
Expand Down
31 changes: 31 additions & 0 deletions apps/desktop/src/main/ipc/prompt.ipc.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { ipcMain } from 'electron';
import { IPC_CHANNELS } from '@prompthub/shared/constants';
import { PromptDB } from '../database/prompt';
import { PromptRelationDB } from '../database';
import { FolderDB } from '../database/folder';
import type Database from '../database/sqlite';
import type {
CreatePromptRelationDTO,
CreatePromptDTO,
Folder,
Prompt,
PromptRelationQuery,
PromptVersion,
SearchQuery,
UpdatePromptRelationDTO,
UpdatePromptDTO,
} from '@prompthub/shared/types';
import { syncPromptWorkspaceFromDatabase } from "../services/prompt-workspace";
Expand All @@ -18,6 +22,7 @@ import { syncPromptWorkspaceFromDatabase } from "../services/prompt-workspace";
* 注册 Prompt 相关 IPC 处理器
*/
export function registerPromptIPC(db: PromptDB, folderDb: FolderDB, rawDb: Database.Database): void {
const relationDb = new PromptRelationDB(rawDb);
const syncWorkspace = () => {
syncPromptWorkspaceFromDatabase(db, folderDb);
};
Expand Down Expand Up @@ -275,4 +280,30 @@ export function registerPromptIPC(db: PromptDB, folderDb: FolderDB, rawDb: Datab
syncWorkspace();
return true;
});

ipcMain.handle(IPC_CHANNELS.PROMPT_RELATION_CREATE, async (_, data: CreatePromptRelationDTO) => {
const relation = relationDb.create(data);
syncWorkspace();
return relation;
});

ipcMain.handle(IPC_CHANNELS.PROMPT_RELATION_LIST, async (_, query?: PromptRelationQuery) => {
return relationDb.list(query);
});

ipcMain.handle(IPC_CHANNELS.PROMPT_RELATION_UPDATE, async (_, id: string, data: UpdatePromptRelationDTO) => {
const relation = relationDb.update(id, data);
if (relation) {
syncWorkspace();
}
return relation;
});

ipcMain.handle(IPC_CHANNELS.PROMPT_RELATION_DELETE, async (_, id: string) => {
const deleted = relationDb.delete(id);
if (deleted) {
syncWorkspace();
}
return deleted;
});
Comment on lines +284 to +308

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Relation ipc lacks validation 📘 Rule violation ⛨ Security

8.2

The new prompt relation IPC handlers accept unvalidated inputs and do not translate failures into
structured error responses. Malformed payloads or thrown DB errors can propagate unpredictably
across the main-process IPC boundary.
Agent Prompt
## Issue description
IPC handlers for prompt relations do not validate payloads/ids and do not return structured error objects on failure.

## Issue Context
Per IPC boundary requirements, handlers must reject malformed inputs and propagate structured errors without risking main-process instability.

## Fix Focus Areas
- apps/desktop/src/main/ipc/prompt.ipc.ts[284-308]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +284 to +308

Copy link
Copy Markdown

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

请在关系 IPC 处理器中补齐入参校验与结构化错误返回。

Line 284-308 的 4 个新增 handler 直接信任 renderer 传入数据,且未做统一 try/catch。建议先校验 id/query/data 的运行时类型,再统一返回结构化错误对象,避免把 malformed payload 直接下沉到 DB 并产生非结构化拒绝。

建议修复(示例)
+  const toIpcError = (error: unknown) => ({
+    ok: false as const,
+    error: { message: error instanceof Error ? error.message : "Unknown error" },
+  });
+
+  const assertRelationId = (value: unknown): string => {
+    if (typeof value !== "string" || value.trim().length === 0) {
+      throw new Error("Relation id is required");
+    }
+    return value;
+  };
+
   ipcMain.handle(IPC_CHANNELS.PROMPT_RELATION_CREATE, async (_, data: CreatePromptRelationDTO) => {
-    const relation = relationDb.create(data);
-    syncWorkspace();
-    return relation;
+    try {
+      // TODO: 替换为仓库统一的 runtime DTO 校验函数
+      const relation = relationDb.create(data);
+      syncWorkspace();
+      return { ok: true as const, data: relation };
+    } catch (error) {
+      return toIpcError(error);
+    }
   });

As per coding guidelines, “apps/desktop/src/main/ipc/**: All IPC handlers must validate input types and reject malformed payloads before processing” and “IPC handlers must catch errors and return structured error responses, never crash the main process silently”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/main/ipc/prompt.ipc.ts` around lines 284 - 308, The four
relation IPC handlers (PROMPT_RELATION_CREATE, PROMPT_RELATION_LIST,
PROMPT_RELATION_UPDATE, PROMPT_RELATION_DELETE) lack input validation and error
handling. For each handler, add runtime type validation for the input parameters
(validate that id is a non-empty string, query is a valid object if provided,
and data matches the expected DTO structure), wrap the handler logic in a
try/catch block, and return a structured error response object (instead of
throwing or crashing) when validation fails or database operations throw errors.
This prevents malformed payloads from reaching the database layer and ensures
consistent error responses to the renderer process.

Source: Coding guidelines

}
11 changes: 11 additions & 0 deletions apps/desktop/src/preload/api/prompt.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { ipcRenderer } from "electron";
import { IPC_CHANNELS } from "@prompthub/shared/constants/ipc-channels";
import type {
CreatePromptRelationDTO,
CreatePromptDTO,
Folder,
Prompt,
PromptRelationQuery,
PromptVersion,
SearchQuery,
UpdatePromptRelationDTO,
UpdatePromptDTO,
} from "@prompthub/shared/types";

Expand Down Expand Up @@ -42,4 +45,12 @@ export const promptApi = {
}) => ipcRenderer.invoke(IPC_CHANNELS.PROMPT_MIGRATE_IDB_BATCH, payload),
move: (promptId: string, newParentId: string | null, newOrder: number) =>
ipcRenderer.invoke(IPC_CHANNELS.PROMPT_MOVE, promptId, newParentId, newOrder),
createRelation: (data: CreatePromptRelationDTO) =>
ipcRenderer.invoke(IPC_CHANNELS.PROMPT_RELATION_CREATE, data),
listRelations: (query?: PromptRelationQuery) =>
ipcRenderer.invoke(IPC_CHANNELS.PROMPT_RELATION_LIST, query),
updateRelation: (id: string, data: UpdatePromptRelationDTO) =>
ipcRenderer.invoke(IPC_CHANNELS.PROMPT_RELATION_UPDATE, id, data),
deleteRelation: (id: string) =>
ipcRenderer.invoke(IPC_CHANNELS.PROMPT_RELATION_DELETE, id),
};
4 changes: 4 additions & 0 deletions apps/desktop/src/renderer/components/layout/MainContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ export function MainContent() {
function PromptSkillMainContent() {
const { t, i18n } = useTranslation();
const prompts = usePromptStore((state) => state.prompts);
const relations = usePromptStore((state) => state.relations);
const selectedId = usePromptStore((state) => state.selectedId);
const selectedIds = usePromptStore((state) => state.selectedIds);
const lastSelectedId = usePromptStore((state) => state.lastSelectedId);
Expand All @@ -360,6 +361,7 @@ function PromptSkillMainContent() {
const togglePinned = usePromptStore((state) => state.togglePinned);
const deletePrompt = usePromptStore((state) => state.deletePrompt);
const updatePrompt = usePromptStore((state) => state.updatePrompt);
const createRelation = usePromptStore((state) => state.createRelation);
const searchQuery = usePromptStore((state) => state.searchQuery);
const filterTags = usePromptStore((state) => state.filterTags);
const toggleFilterTag = usePromptStore((state) => state.toggleFilterTag);
Expand Down Expand Up @@ -1989,13 +1991,15 @@ function PromptSkillMainContent() {
<Suspense fallback={loadingFallback}>
<PromptListView
prompts={visiblePrompts}
relations={relations}
selectedId={selectedId}
selectedIds={selectedIds}
onSelect={(id) => selectPrompt(id)}
onToggleFavorite={toggleFavorite}
onCopy={handleCopyPrompt}
onContextMenu={handleContextMenu}
onMovePrompt={movePrompt}
onCreateRelation={createRelation}
/>
</Suspense>
)}
Expand Down
Loading
Loading