mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 08:17:11 +00:00
fix(validation): add Windows reserved name validation and version format edge cases
- Fixed version regex to accept -N suffix (e.g., 6.6.80-0) - Added .trim() to version input for whitespace handling - Added Windows reserved device names (CON, PRN, AUX, NUL, COM1-9, LPT1-9) validation - Updated binary-service, variant-service, and validation-service with checks
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
* Extracted from api-command.ts for reuse and testability.
|
||||
*/
|
||||
|
||||
import { isReservedName } from '../../config/reserved-names';
|
||||
import { isReservedName, isWindowsReservedName } from '../../config/reserved-names';
|
||||
|
||||
/**
|
||||
* Validate API profile name
|
||||
@@ -24,6 +24,9 @@ export function validateApiName(name: string): string | null {
|
||||
if (isReservedName(name)) {
|
||||
return `'${name}' is a reserved name`;
|
||||
}
|
||||
if (isWindowsReservedName(name)) {
|
||||
return `'${name}' is a Windows reserved device name and cannot be used`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,10 +84,10 @@ export async function checkLatestVersion(): Promise<LatestVersionResult> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate version format
|
||||
* Validate version format (supports X.Y.Z or X.Y.Z-N suffix)
|
||||
*/
|
||||
export function isValidVersionFormat(version: string): boolean {
|
||||
return /^\d+\.\d+\.\d+$/.test(version);
|
||||
return /^\d+\.\d+\.\d+(-\d+)?$/.test(version);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@ import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { CLIProxyProfileName } from '../../auth/profile-detector';
|
||||
import { CLIProxyProvider } from '../types';
|
||||
import { isReservedName } from '../../config/reserved-names';
|
||||
import { isReservedName, isWindowsReservedName } from '../../config/reserved-names';
|
||||
import { isUnifiedMode } from '../../config/unified-config-loader';
|
||||
import { deleteConfigForPort } from '../config-generator';
|
||||
import { deleteSessionLockForPort } from '../session-tracker';
|
||||
@@ -58,6 +58,9 @@ export function validateProfileName(name: string): string | null {
|
||||
if (isReservedName(name)) {
|
||||
return `'${name}' is a reserved name`;
|
||||
}
|
||||
if (isWindowsReservedName(name)) {
|
||||
return `'${name}' is a Windows reserved device name and cannot be used`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -619,13 +619,15 @@ export async function handleCliproxyCommand(args: string[]): Promise<void> {
|
||||
|
||||
const installIdx = args.indexOf('--install');
|
||||
if (installIdx !== -1) {
|
||||
const version = args[installIdx + 1];
|
||||
let version = args[installIdx + 1];
|
||||
if (!version || version.startsWith('-')) {
|
||||
console.error(fail('Missing version argument for --install'));
|
||||
console.error(' Usage: ccs cliproxy --install <version>');
|
||||
console.error(' Example: ccs cliproxy --install 6.5.53');
|
||||
console.error(' Example: ccs cliproxy --install 6.6.80-0');
|
||||
process.exit(1);
|
||||
}
|
||||
// Strip leading 'v' prefix and whitespace (user may type " v6.6.80-0 ")
|
||||
version = version.trim().replace(/^v/, '');
|
||||
await handleInstallVersion(version, verbose);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,35 @@ export const RESERVED_PROFILE_NAMES = [
|
||||
|
||||
export type ReservedProfileName = (typeof RESERVED_PROFILE_NAMES)[number];
|
||||
|
||||
/**
|
||||
* Windows reserved device names - cannot be used as filenames on Windows.
|
||||
* Case-insensitive on Windows filesystem.
|
||||
*/
|
||||
export const WINDOWS_RESERVED_NAMES = [
|
||||
'CON',
|
||||
'PRN',
|
||||
'AUX',
|
||||
'NUL',
|
||||
'COM1',
|
||||
'COM2',
|
||||
'COM3',
|
||||
'COM4',
|
||||
'COM5',
|
||||
'COM6',
|
||||
'COM7',
|
||||
'COM8',
|
||||
'COM9',
|
||||
'LPT1',
|
||||
'LPT2',
|
||||
'LPT3',
|
||||
'LPT4',
|
||||
'LPT5',
|
||||
'LPT6',
|
||||
'LPT7',
|
||||
'LPT8',
|
||||
'LPT9',
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Check if a name is reserved and cannot be used for user profiles.
|
||||
* @param name - The profile name to check
|
||||
@@ -28,6 +57,18 @@ export function isReservedName(name: string): boolean {
|
||||
return RESERVED_PROFILE_NAMES.includes(name.toLowerCase() as ReservedProfileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a name is a Windows reserved device name.
|
||||
* These cause filesystem errors on Windows systems.
|
||||
* @param name - The name to check
|
||||
* @returns true if the name is a Windows reserved name
|
||||
*/
|
||||
export function isWindowsReservedName(name: string): boolean {
|
||||
return WINDOWS_RESERVED_NAMES.includes(
|
||||
name.toUpperCase() as (typeof WINDOWS_RESERVED_NAMES)[number]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a profile name and throw if reserved.
|
||||
* @param name - The profile name to validate
|
||||
|
||||
Reference in New Issue
Block a user