fix(core-tools): resolve defensive path resolution for at-reference files and fix macOS tests (#28053)

Co-authored-by: David Pierce <davidapierce@google.com>
This commit is contained in:
luisfelipe-alt
2026-06-30 19:45:32 +00:00
committed by GitHub
parent ae0a3aa7b9
commit b5fc06ee33
21 changed files with 1196 additions and 92 deletions
@@ -20,7 +20,10 @@ describe('pathCorrector', () => {
let mockConfig: Config;
beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'path-corrector-test-'));
const rawTempDir = fs.mkdtempSync(
path.join(os.tmpdir(), 'path-corrector-test-'),
);
tempDir = fs.realpathSync(rawTempDir);
rootDir = path.join(tempDir, 'root');
otherWorkspaceDir = path.join(tempDir, 'other');
fs.mkdirSync(rootDir, { recursive: true });
+9 -3
View File
@@ -8,6 +8,7 @@ import * as fs from 'node:fs';
import * as path from 'node:path';
import type { Config } from '../config/config.js';
import { bfsFileSearchSync } from './bfsFileSearch.js';
import { resolveDefensiveToolPath } from './paths.js';
type SuccessfulPathCorrection = {
success: true;
@@ -34,8 +35,13 @@ export function correctPath(
filePath: string,
config: Config,
): PathCorrectionResult {
const sanitizedPath = resolveDefensiveToolPath(
filePath,
config.getTargetDir(),
);
// Check for direct path relative to the primary target directory.
const directPath = path.join(config.getTargetDir(), filePath);
const directPath = path.join(config.getTargetDir(), sanitizedPath);
if (fs.existsSync(directPath)) {
return { success: true, correctedPath: directPath };
}
@@ -43,8 +49,8 @@ export function correctPath(
// If not found directly, search across all workspace directories for ambiguous matches.
const workspaceContext = config.getWorkspaceContext();
const searchPaths = workspaceContext.getDirectories();
const basename = path.basename(filePath);
const normalizedTarget = filePath.replace(/\\/g, '/');
const basename = path.basename(sanitizedPath);
const normalizedTarget = sanitizedPath.replace(/\\/g, '/');
// Normalize path for matching and check if it ends with the provided relative path
const foundFiles = searchPaths
+17
View File
@@ -20,6 +20,7 @@ import {
toAbsolutePath,
toPathKey,
isTrustedSystemPath,
resolveDefensiveToolPath,
} from './paths.js';
vi.mock('node:fs', async (importOriginal) => {
@@ -918,4 +919,20 @@ describe('normalizePath', () => {
});
});
});
describe('resolveDefensiveToolPath', () => {
it('should sanitize paths by stripping null bytes', () => {
const targetDir = '/workspace';
const filePathWithNull = 'src/index.ts\0.exe';
const result = resolveDefensiveToolPath(filePathWithNull, targetDir);
expect(result).toBe('src/index.ts.exe');
});
it('should sanitize @ prefixed paths by stripping null bytes', () => {
const targetDir = '/workspace';
const filePathWithNull = '@/components/Button.tsx\0';
const result = resolveDefensiveToolPath(filePathWithNull, targetDir);
expect(result).toBe('components/Button.tsx');
});
});
});
+51
View File
@@ -572,3 +572,54 @@ export function isTrustedSystemPath(filePath: string): boolean {
);
}
}
/**
* Defensively resolves and sanitizes a file path generated by the LLM,
* stripping user-facing reference prefixes if necessary.
*/
export function resolveDefensiveToolPath(
filePath: string,
targetDir: string,
): string {
const cleanPath = filePath.replace(/\0/g, '');
try {
const literalPath = path.resolve(targetDir, cleanPath);
// If the file literally exists on disk as-is, return the resolved literal path immediately
if (fs.existsSync(literalPath)) {
return cleanPath;
}
// If the model supplied a leading @ prefix and the literal path doesn't exist:
if (cleanPath.startsWith('@') && cleanPath.length > 1) {
if (cleanPath.startsWith('@/') || cleanPath.startsWith('@\\')) {
const stripped = cleanPath.substring(1).replace(/^[\\/]+/, '');
return stripped.length > 0 ? stripped : cleanPath;
}
const strippedPath = cleanPath.substring(1).replace(/^[\\/]+/, '');
// Check if a literal directory/file starting with '@' exists for the first segment.
// If it does, we should preserve the '@' prefix.
const parts = strippedPath.split(/[\\/]/);
const firstSegment = parts[0];
if (firstSegment) {
const literalFirstSegment = path.resolve(targetDir, '@' + firstSegment);
if (fs.existsSync(literalFirstSegment)) {
return cleanPath;
}
// Otherwise, strip the '@' prefix to resolve to the standard directory name,
// preventing the accidental creation of literal '@'-prefixed directories (e.g. '@src', '@policies')
// when creating new files or directories.
return strippedPath;
}
}
} catch {
// Fallback to original path if any filesystem or resolution error occurs
}
// Fallback: return the original path
return cleanPath;
}