From 02b18828bef1f52df7e3b328db08ce286f22fc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davor=20Raci=C4=87?= Date: Wed, 25 Feb 2026 10:47:57 +0100 Subject: [PATCH] fix(opencode): address code review findings for cleanup and schema docs - Add project boundary guard to removeEmptyParents() using path.resolve and startsWith check to prevent traversal outside projectDir (Augment) - Fix JSDoc: "Recursively remove" -> "Walk up ancestor directories" - Add user-visible migration log message when processing legacy_targets - Document legacy_targets field in Installer Config Schema comment block in platform-codes.yaml (CodeRabbit + Augment) Co-Authored-By: Claude Sonnet 4.6 --- tools/cli/installers/lib/ide/_config-driven.js | 8 ++++++-- tools/cli/installers/lib/ide/platform-codes.yaml | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/cli/installers/lib/ide/_config-driven.js b/tools/cli/installers/lib/ide/_config-driven.js index 3aa86613d..6e8c7c8a0 100644 --- a/tools/cli/installers/lib/ide/_config-driven.js +++ b/tools/cli/installers/lib/ide/_config-driven.js @@ -455,6 +455,7 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}} async cleanup(projectDir, options = {}) { // Migrate legacy target directories (e.g. .opencode/agent → .opencode/agents) if (this.installerConfig?.legacy_targets) { + if (!options.silent) await prompts.log.message(' Migrating legacy directories...'); for (const legacyDir of this.installerConfig.legacy_targets) { await this.cleanupTarget(projectDir, legacyDir, options); await this.removeEmptyParents(projectDir, legacyDir); @@ -540,17 +541,20 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}} } } /** - * Recursively remove empty directories walking up from dir toward projectDir + * Walk up ancestor directories from relativeDir toward projectDir, removing each if empty * Stops at projectDir boundary — never removes projectDir itself * @param {string} projectDir - Project root (boundary) * @param {string} relativeDir - Relative directory to start from */ async removeEmptyParents(projectDir, relativeDir) { + const resolvedProject = path.resolve(projectDir); let current = relativeDir; let last = null; while (current && current !== '.' && current !== last) { last = current; - const fullPath = path.join(projectDir, current); + const fullPath = path.resolve(projectDir, current); + // Boundary guard: never traverse outside projectDir + if (!fullPath.startsWith(resolvedProject + path.sep) && fullPath !== resolvedProject) break; try { if (!(await fs.pathExists(fullPath))) { current = path.dirname(current); diff --git a/tools/cli/installers/lib/ide/platform-codes.yaml b/tools/cli/installers/lib/ide/platform-codes.yaml index fe5b42a7a..2d9e8c129 100644 --- a/tools/cli/installers/lib/ide/platform-codes.yaml +++ b/tools/cli/installers/lib/ide/platform-codes.yaml @@ -194,6 +194,8 @@ platforms: # template_type: string # Default template type to use # header_template: string (optional) # Override for header/frontmatter template # body_template: string (optional) # Override for body/content template +# legacy_targets: array (optional) # Old target dirs to clean up on reinstall (migration) +# - string # Relative path, e.g. .opencode/agent # targets: array (optional) # For multi-target installations # - target_dir: string # template_type: string