Skip to content
Merged
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
10 changes: 1 addition & 9 deletions apps/desktop/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2403,7 +2403,7 @@ function registerPluginDiscoveryHandlers(): void {
});

// Install plugin from a remote URL (marketplace download)
ipcMain.handle('plugins:installFromUrl', async (_event, url: string, pluginSlug: string) => {
ipcMain.handle('plugins:installFromUrl', async (_event, url: string, _pluginSlug: string) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Validate downloaded manifest ID against requested plugin

By ignoring pluginSlug in plugins:installFromUrl, the install path is now bound only to manifest.id from the downloaded archive. If the marketplace entry is misconfigured (or serves the wrong bundle), installing plugin A can silently delete/replace plugin B because destDir is computed from manifest.id and existing directories are removed before rename. The previous check prevented this cross-plugin overwrite scenario, so we still need an integrity check tying the requested plugin identity to the downloaded manifest.

Useful? React with 👍 / 👎.

// Safety: only allow https URLs
if (!url.startsWith('https://')) {
return { success: false, error: 'Only HTTPS URLs are allowed' };
Expand Down Expand Up @@ -2502,14 +2502,6 @@ function registerPluginDiscoveryHandlers(): void {
return { success: false, error: 'Invalid manifest: missing id or name' };
}

// Validate manifest.id matches the expected pluginSlug if provided
if (pluginSlug && pluginSlug.length > 0 && manifest.id !== pluginSlug) {
return {
success: false,
error: `Manifest ID "${manifest.id}" does not match expected plugin "${pluginSlug}"`,
};
}

// Validate plugin ID - only allow alphanumeric, hyphens, underscores
if (!/^[a-zA-Z0-9_-]+$/.test(manifest.id)) {
return {
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/renderer/ui/patterns/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export function Modal({
);

const contentRef = useRef<HTMLDivElement | null>(null);
const generatedId = useId();

// Focus the modal container on open
useEffect(() => {
Expand All @@ -56,7 +57,6 @@ export function Modal({

if (!open) return null;

const generatedId = useId();
const titleId = title != null ? generatedId : undefined;

return createPortal(
Expand Down
Loading