mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-02 18:18:43 +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.
|
* 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
|
* Validate API profile name
|
||||||
@@ -24,6 +24,9 @@ export function validateApiName(name: string): string | null {
|
|||||||
if (isReservedName(name)) {
|
if (isReservedName(name)) {
|
||||||
return `'${name}' is a reserved name`;
|
return `'${name}' is a reserved name`;
|
||||||
}
|
}
|
||||||
|
if (isWindowsReservedName(name)) {
|
||||||
|
return `'${name}' is a Windows reserved device name and cannot be used`;
|
||||||
|
}
|
||||||
return null;
|
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 {
|
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 * as path from 'path';
|
||||||
import { CLIProxyProfileName } from '../../auth/profile-detector';
|
import { CLIProxyProfileName } from '../../auth/profile-detector';
|
||||||
import { CLIProxyProvider } from '../types';
|
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 { isUnifiedMode } from '../../config/unified-config-loader';
|
||||||
import { deleteConfigForPort } from '../config-generator';
|
import { deleteConfigForPort } from '../config-generator';
|
||||||
import { deleteSessionLockForPort } from '../session-tracker';
|
import { deleteSessionLockForPort } from '../session-tracker';
|
||||||
@@ -58,6 +58,9 @@ export function validateProfileName(name: string): string | null {
|
|||||||
if (isReservedName(name)) {
|
if (isReservedName(name)) {
|
||||||
return `'${name}' is a reserved name`;
|
return `'${name}' is a reserved name`;
|
||||||
}
|
}
|
||||||
|
if (isWindowsReservedName(name)) {
|
||||||
|
return `'${name}' is a Windows reserved device name and cannot be used`;
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -619,13 +619,15 @@ export async function handleCliproxyCommand(args: string[]): Promise<void> {
|
|||||||
|
|
||||||
const installIdx = args.indexOf('--install');
|
const installIdx = args.indexOf('--install');
|
||||||
if (installIdx !== -1) {
|
if (installIdx !== -1) {
|
||||||
const version = args[installIdx + 1];
|
let version = args[installIdx + 1];
|
||||||
if (!version || version.startsWith('-')) {
|
if (!version || version.startsWith('-')) {
|
||||||
console.error(fail('Missing version argument for --install'));
|
console.error(fail('Missing version argument for --install'));
|
||||||
console.error(' Usage: ccs cliproxy --install <version>');
|
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);
|
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);
|
await handleInstallVersion(version, verbose);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,35 @@ export const RESERVED_PROFILE_NAMES = [
|
|||||||
|
|
||||||
export type ReservedProfileName = (typeof RESERVED_PROFILE_NAMES)[number];
|
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.
|
* Check if a name is reserved and cannot be used for user profiles.
|
||||||
* @param name - The profile name to check
|
* @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);
|
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.
|
* Validate a profile name and throw if reserved.
|
||||||
* @param name - The profile name to validate
|
* @param name - The profile name to validate
|
||||||
|
|||||||
Reference in New Issue
Block a user