fix: close bar launch case bypass (#1613)

This commit is contained in:
Kai (Tam Nhu) Tran authored and GitHub committed 2026-06-30 12:33:46 -04:00
1 parent 0e097f3301
commit 3f1dce30bc
3 files changed
+28 -10

No files matched your search

@@ -105,14 +105,21 @@ struct BarServerLauncher: Sendable {
}
private func isUnderCcsDir(_ path: String) -> Bool {
let ccsPath = URL(fileURLWithPath: home)
.appendingPathComponent(".ccs")
.standardizedFileURL
.path
let targetPath = URL(fileURLWithPath: path).standardizedFileURL.path
let ccsPath = pathForComparison(
URL(fileURLWithPath: home)
.appendingPathComponent(".ccs")
)
let targetPath = pathForComparison(URL(fileURLWithPath: path))
return targetPath == ccsPath || targetPath.hasPrefix(ccsPath + "/")
}
private func pathForComparison(_ url: URL) -> String {
url.standardizedFileURL
.resolvingSymlinksInPath()
.path
.lowercased()
}
private func isAbsolutePath(_ path: String) -> Bool {
path.hasPrefix("/")
}
+8 -5
View File
@@ -377,7 +377,9 @@ export function updateSettingsFile(
*/
function normalizePathForComparison(filePath: string): string {
const normalized = path.resolve(path.normalize(filePath));
return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
return process.platform === 'win32' || process.platform === 'darwin'
? normalized.toLowerCase()
: normalized;
}
function isPathWithin(basePath: string, targetPath: string): boolean {
@@ -437,7 +439,8 @@ export function validateFilePath(filePath: string): {
// Block access to sensitive subdirectories
const relativePath = path.relative(ccsDir, normalizedPath);
const pathSegments = relativePath.split(path.sep).filter(Boolean);
if (pathSegments.includes('.git') || pathSegments.includes('node_modules')) {
const comparisonSegments = pathSegments.map((segment) => segment.toLowerCase());
if (comparisonSegments.includes('.git') || comparisonSegments.includes('node_modules')) {
return { valid: false, readonly: false, error: 'Access to this path is not allowed' };
}
@@ -445,9 +448,9 @@ export function validateFilePath(filePath: string): {
// It must only be written by trusted bar install/launch code paths, not the
// generic dashboard file API.
if (
pathSegments.length === 2 &&
pathSegments[0] === 'bar' &&
pathSegments[1] === 'launch.json'
comparisonSegments.length === 2 &&
comparisonSegments[0] === 'bar' &&
comparisonSegments[1] === 'launch.json'
) {
return { valid: false, readonly: false, error: 'Access to this path is not allowed' };
}
@@ -50,6 +50,14 @@ describe('validateFilePath', () => {
expect(result.readonly).toBe(false);
});
test('rejects case variants of the macOS bar launch descriptor', () => {
const filePath = path.join(tempDir, '.ccs', 'BAR', 'LAUNCH.JSON');
const result = validateFilePath(filePath);
expect(result.valid).toBe(false);
expect(result.readonly).toBe(false);
});
test('still allows other writes inside the bar directory', () => {
const filePath = path.join(tempDir, '.ccs', 'bar', 'serve.log');
const result = validateFilePath(filePath);