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 <noreply@anthropic.com>
This commit is contained in:
Davor Racić 2026-02-25 10:47:57 +01:00
parent 0b219d09e8
commit 02b18828be
2 changed files with 8 additions and 2 deletions

View File

@ -455,6 +455,7 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
async cleanup(projectDir, options = {}) { async cleanup(projectDir, options = {}) {
// Migrate legacy target directories (e.g. .opencode/agent → .opencode/agents) // Migrate legacy target directories (e.g. .opencode/agent → .opencode/agents)
if (this.installerConfig?.legacy_targets) { if (this.installerConfig?.legacy_targets) {
if (!options.silent) await prompts.log.message(' Migrating legacy directories...');
for (const legacyDir of this.installerConfig.legacy_targets) { for (const legacyDir of this.installerConfig.legacy_targets) {
await this.cleanupTarget(projectDir, legacyDir, options); await this.cleanupTarget(projectDir, legacyDir, options);
await this.removeEmptyParents(projectDir, legacyDir); 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 * Stops at projectDir boundary never removes projectDir itself
* @param {string} projectDir - Project root (boundary) * @param {string} projectDir - Project root (boundary)
* @param {string} relativeDir - Relative directory to start from * @param {string} relativeDir - Relative directory to start from
*/ */
async removeEmptyParents(projectDir, relativeDir) { async removeEmptyParents(projectDir, relativeDir) {
const resolvedProject = path.resolve(projectDir);
let current = relativeDir; let current = relativeDir;
let last = null; let last = null;
while (current && current !== '.' && current !== last) { while (current && current !== '.' && current !== last) {
last = current; 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 { try {
if (!(await fs.pathExists(fullPath))) { if (!(await fs.pathExists(fullPath))) {
current = path.dirname(current); current = path.dirname(current);

View File

@ -194,6 +194,8 @@ platforms:
# template_type: string # Default template type to use # template_type: string # Default template type to use
# header_template: string (optional) # Override for header/frontmatter template # header_template: string (optional) # Override for header/frontmatter template
# body_template: string (optional) # Override for body/content 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 # targets: array (optional) # For multi-target installations
# - target_dir: string # - target_dir: string
# template_type: string # template_type: string