mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-06 08:13:49 +00:00
fix: close bar launch case bypass (#1613)
This commit is contained in:
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("/")
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user