From c0c18adfcea2102847719d65d508b808cdbd9160 Mon Sep 17 00:00:00 2001 From: Fabio Gartenmann <137318798+artiphishle@users.noreply.github.com> Date: Thu, 29 Jan 2026 00:30:01 +0100 Subject: [PATCH] Potential fix for code scanning alert no. 81: Uncontrolled data used in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- src/shared/utils/detectLanguage.ts | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/shared/utils/detectLanguage.ts b/src/shared/utils/detectLanguage.ts index f7ed4dd..f08e13d 100644 --- a/src/shared/utils/detectLanguage.ts +++ b/src/shared/utils/detectLanguage.ts @@ -15,6 +15,17 @@ function safeReadFileSync(baseDir: string, filePath: string): string { return fs.readFileSync(resolvedFilePath, 'utf8'); } +function safeResolveDir(baseDir: string, directoryPath: string): string { + const resolvedBaseDir = path.resolve(baseDir); + const resolvedDirectoryPath = path.resolve(baseDir, directoryPath); + + if (!resolvedDirectoryPath.startsWith(resolvedBaseDir + path.sep)) { + throw new Error(`Path traversal attempt: ${directoryPath}`); + } + + return resolvedDirectoryPath; +} + function safeReadDirSync(baseDir: string, dirPath: string): string[] { const resolvedBaseDir = path.resolve(baseDir); const resolvedDirPath = path.resolve(baseDir, dirPath); @@ -357,7 +368,7 @@ export async function isJavaRoot(directoryPath: string): Promise { } export async function isCppRoot(directoryPath: string): Promise { - const resolvedPath = path.resolve(directoryPath); + const resolvedPath = safeResolveDir(process.cwd(), directoryPath); if (!fs.existsSync(resolvedPath) || !fs.statSync(resolvedPath).isDirectory()) { return false; } @@ -370,7 +381,7 @@ export async function isCppRoot(directoryPath: string): Promise { } export async function isPythonRoot(directoryPath: string): Promise { - const resolvedPath = path.resolve(directoryPath); + const resolvedPath = safeResolveDir(process.cwd(), directoryPath); if (!fs.existsSync(resolvedPath) || !fs.statSync(resolvedPath).isDirectory()) { return false; } @@ -384,7 +395,7 @@ export async function isPythonRoot(directoryPath: string): Promise { } export async function isDelphiRoot(directoryPath: string): Promise { - const resolvedPath = path.resolve(directoryPath); + const resolvedPath = safeResolveDir(process.cwd(), directoryPath); if (!fs.existsSync(resolvedPath) || !fs.statSync(resolvedPath).isDirectory()) { return false; } @@ -396,14 +407,14 @@ export async function isDelphiRoot(directoryPath: string): Promise { } export async function isKotlinRoot(directoryPath: string): Promise { - const resolvedPath = path.resolve(directoryPath); + const resolvedPath = safeResolveDir(process.cwd(), directoryPath); if (!fs.existsSync(resolvedPath) || !fs.statSync(resolvedPath).isDirectory()) { return false; } const files = safeReadDirSync(resolvedPath, '.'); return ( files.includes('build.gradle.kts') || - files.some(file => file.endsWith('.kt')) || + files.some(file => file.endswith('.kt')) || (files.includes('build.gradle') && safeReadFileSync(resolvedPath, 'build.gradle').includes('kotlin')) );