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
19 changes: 15 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,23 @@ main ← Production releases (semantic-release runs here)
| `feature/*` | New features | `develop` |
| `fix/*` | Bug fixes | `develop` |

### Branch Protection Rules (Critical)
### Branch Protection Rules (MANDATORY)

- **NEVER commit directly to `develop` or `main`** — always create a feature/fix branch first
- **All work goes through PRs** — even small fixes. This ensures CI runs and reviews happen
- **PR flow:** `feature/*` → `develop` → `main`
- **Claude Code must also follow this** — create a branch, commit there, push, then create PR to develop
- **All work goes through PRs** — even small fixes, even single-line changes
- **PR flow:** `feature/*` or `fix/*` → `develop` (via PR) → `main` (via PR)
- **Branch naming:** `feature/short-description` for new features, `fix/short-description` for bug fixes

**Claude Code MUST follow this workflow:**

1. `git checkout develop && git pull origin develop`
2. `git checkout -b fix/description-here` (or `feature/`)
3. Make changes, commit on the branch
4. `git push -u origin fix/description-here`
5. `gh pr create --base develop --head fix/description-here`
6. After merge: `git checkout develop && git pull && git branch -d fix/description-here`

**NEVER do:** `git commit` on develop, `git push origin develop`, `gh pr create --base main --head develop` (unless releasing)

### Release Process (Automated)

Expand Down
14 changes: 13 additions & 1 deletion 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) => {
// Safety: only allow https URLs
if (!url.startsWith('https://')) {
return { success: false, error: 'Only HTTPS URLs are allowed' };
Expand Down Expand Up @@ -2502,6 +2502,18 @@ function registerPluginDiscoveryHandlers(): void {
return { success: false, error: 'Invalid manifest: missing id or name' };
}

// Cross-plugin overwrite protection: if we requested plugin A but the
// archive contains plugin B, block when it would overwrite an existing plugin
if (pluginSlug && manifest.id !== pluginSlug) {
const wouldOverwrite = join(paths.plugins, manifest.id);
if (existsSync(wouldOverwrite)) {
return {
success: false,
error: `Archive contains "${manifest.id}" but "${pluginSlug}" was requested. Refusing to overwrite existing plugin.`,
};
}
}

// Validate plugin ID - only allow alphanumeric, hyphens, underscores
if (!/^[a-zA-Z0-9_-]+$/.test(manifest.id)) {
return {
Expand Down
Loading