Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions editors/vscode/client/PathValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,37 @@
* Even though we are not using `shell: true`, it's a good practice to validate the input.
*/
export function validateSafeBinaryPath(binary: string): boolean {
// Check for path traversal
if (binary.includes('..')) {
// Check for path traversal (including Windows variants)
if (binary.includes('..') || binary.includes('.\\')) {
return false;
}

// Check for malicious characters or patterns
// These characters are never expected in a binary path.
// If any of these characters are present, we consider the path unsafe.
const maliciousPatterns = ['$', '&', ';', '|', '`', '>', '<', '!'];
const maliciousPatterns = [
// linux/macOS
'$',
'&',
';',
'|',
'`',
'>',
'<',
'!',
// windows
'%',
'^',
];
for (const pattern of maliciousPatterns) {
if (binary.includes(pattern)) {
return false;
}
}

// Check if the filename contains `oxc_language_server`
// Malicious projects might try to point to a different binary.
if (!binary.includes('oxc_language_server')) {
if (!binary.replaceAll('\\', '/').toLowerCase().split('/').pop()?.includes('oxc_language_server')) {
return false;
}

Expand Down
14 changes: 14 additions & 0 deletions editors/vscode/tests/PathValidator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,19 @@ suite('validateSafeBinaryPath', () => {
strictEqual(validateSafeBinaryPath('/opt/oxc_language_server'), true);
});

test('should accept case variations of oxc_language_server', () => {
strictEqual(validateSafeBinaryPath('OXC_LANGUAGE_SERVER'), true);
strictEqual(validateSafeBinaryPath('OXC_LANGUAGE_SERVER.exe'), true);
strictEqual(validateSafeBinaryPath('/usr/local/bin/OXC_LANGUAGE_SERVER'), true);
strictEqual(validateSafeBinaryPath('C:\\Program Files\\OXC_LANGUAGE_SERVER.exe'), true);
});

test('should reject paths with directory traversal', () => {
strictEqual(validateSafeBinaryPath('../oxc_language_server'), false);
strictEqual(validateSafeBinaryPath('../../oxc_language_server'), false);
strictEqual(validateSafeBinaryPath('/usr/local/../bin/oxc_language_server'), false);
strictEqual(validateSafeBinaryPath('..\\oxc_language_server'), false);
strictEqual(validateSafeBinaryPath('.\\oxc_language_server'), false);
});

test('should reject paths with malicious characters', () => {
Expand All @@ -24,6 +32,10 @@ suite('validateSafeBinaryPath', () => {
strictEqual(validateSafeBinaryPath('oxc_language_server<input.txt'), false);
strictEqual(validateSafeBinaryPath('oxc_language_server`whoami`'), false);
strictEqual(validateSafeBinaryPath('oxc_language_server!'), false);

// windows specific
strictEqual(validateSafeBinaryPath('oxc_language_server^&pause'), false);
strictEqual(validateSafeBinaryPath('oxc_language_server & del /f *'), false);
});

test('should reject paths not containing oxc_language_server', () => {
Expand All @@ -32,5 +44,7 @@ suite('validateSafeBinaryPath', () => {
strictEqual(validateSafeBinaryPath(''), false);
strictEqual(validateSafeBinaryPath('oxc_language'), false);
strictEqual(validateSafeBinaryPath('language_server'), false);
strictEqual(validateSafeBinaryPath('/oxc_language_server/malicious'), false);
strictEqual(validateSafeBinaryPath('C:\\oxc_language_server\\evil.exe'), false);
});
});
Loading