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
25 changes: 17 additions & 8 deletions src/core/config/CustomModesManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -782,12 +782,11 @@ export class CustomModesManager {
const filePath = path.join(modeRulesDir, entry.name)
const content = await fs.readFile(filePath, "utf-8")
if (content.trim()) {
// Calculate relative path based on mode source
const relativePath = isGlobalMode
? path.relative(baseDir, filePath)
: path.relative(path.join(baseDir, ".roo"), filePath)
// Calculate relative path from within the rules directory
// This excludes the rules-{slug} folder from the path
const relativePath = path.relative(modeRulesDir, filePath)
// Normalize path to use forward slashes for cross-platform compatibility
const normalizedRelativePath = relativePath.toPosix()
const normalizedRelativePath = relativePath.replace(/\\/g, '/')
rulesFiles.push({ relativePath: normalizedRelativePath, content: content.trim() })
}
}
Expand Down Expand Up @@ -883,11 +882,21 @@ export class CustomModesManager {
continue // Skip this file but continue with others
}

const targetPath = path.join(baseDir, normalizedRelativePath)
// Check if path starts with a rules-* folder (old export format)
let cleanedRelativePath = normalizedRelativePath
const rulesMatch = normalizedRelativePath.match(/^rules-[^\/\\]+[\/\\]/)
if (rulesMatch) {
// Strip the entire rules-* folder reference for backwards compatibility
cleanedRelativePath = normalizedRelativePath.substring(rulesMatch[0].length)
logger.info(`Detected old export format, stripping ${rulesMatch[0]} from path`)
}

// Use the rules folder path instead of base directory
const targetPath = path.join(rulesFolderPath, cleanedRelativePath)
const normalizedTargetPath = path.normalize(targetPath)
const expectedBasePath = path.normalize(baseDir)
const expectedBasePath = path.normalize(rulesFolderPath)

// Ensure the resolved path stays within the base directory
// Ensure the resolved path stays within the rules folder
if (!normalizedTargetPath.startsWith(expectedBasePath)) {
logger.error(`Path traversal attempt detected: ${ruleFile.relativePath}`)
continue // Skip this file but continue with others
Expand Down
Loading