mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 14:15:56 +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 {
|
private func isUnderCcsDir(_ path: String) -> Bool {
|
||||||
let ccsPath = URL(fileURLWithPath: home)
|
let ccsPath = pathForComparison(
|
||||||
.appendingPathComponent(".ccs")
|
URL(fileURLWithPath: home)
|
||||||
.standardizedFileURL
|
.appendingPathComponent(".ccs")
|
||||||
.path
|
)
|
||||||
let targetPath = URL(fileURLWithPath: path).standardizedFileURL.path
|
let targetPath = pathForComparison(URL(fileURLWithPath: path))
|
||||||
return targetPath == ccsPath || targetPath.hasPrefix(ccsPath + "/")
|
return targetPath == ccsPath || targetPath.hasPrefix(ccsPath + "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func pathForComparison(_ url: URL) -> String {
|
||||||
|
url.standardizedFileURL
|
||||||
|
.resolvingSymlinksInPath()
|
||||||
|
.path
|
||||||
|
.lowercased()
|
||||||
|
}
|
||||||
|
|
||||||
private func isAbsolutePath(_ path: String) -> Bool {
|
private func isAbsolutePath(_ path: String) -> Bool {
|
||||||
path.hasPrefix("/")
|
path.hasPrefix("/")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -377,7 +377,9 @@ export function updateSettingsFile(
|
|||||||
*/
|
*/
|
||||||
function normalizePathForComparison(filePath: string): string {
|
function normalizePathForComparison(filePath: string): string {
|
||||||
const normalized = path.resolve(path.normalize(filePath));
|
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 {
|
function isPathWithin(basePath: string, targetPath: string): boolean {
|
||||||
@@ -437,7 +439,8 @@ export function validateFilePath(filePath: string): {
|
|||||||
// Block access to sensitive subdirectories
|
// Block access to sensitive subdirectories
|
||||||
const relativePath = path.relative(ccsDir, normalizedPath);
|
const relativePath = path.relative(ccsDir, normalizedPath);
|
||||||
const pathSegments = relativePath.split(path.sep).filter(Boolean);
|
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' };
|
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
|
// It must only be written by trusted bar install/launch code paths, not the
|
||||||
// generic dashboard file API.
|
// generic dashboard file API.
|
||||||
if (
|
if (
|
||||||
pathSegments.length === 2 &&
|
comparisonSegments.length === 2 &&
|
||||||
pathSegments[0] === 'bar' &&
|
comparisonSegments[0] === 'bar' &&
|
||||||
pathSegments[1] === 'launch.json'
|
comparisonSegments[1] === 'launch.json'
|
||||||
) {
|
) {
|
||||||
return { valid: false, readonly: false, error: 'Access to this path is not allowed' };
|
return { valid: false, readonly: false, error: 'Access to this path is not allowed' };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,14 @@ describe('validateFilePath', () => {
|
|||||||
expect(result.readonly).toBe(false);
|
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', () => {
|
test('still allows other writes inside the bar directory', () => {
|
||||||
const filePath = path.join(tempDir, '.ccs', 'bar', 'serve.log');
|
const filePath = path.join(tempDir, '.ccs', 'bar', 'serve.log');
|
||||||
const result = validateFilePath(filePath);
|
const result = validateFilePath(filePath);
|
||||||
|
|||||||
Reference in new issue
Block a user