@@ -3,6 +3,57 @@ export type CommandRestriction = {
33 reason : string
44}
55
6+ /**
7+ * Parses a command string into arguments, respecting quoted strings and escaped quotes.
8+ * Matches valkey-cli behavior: 'GET "my key"' → ['GET', 'my key']
9+ */
10+ export const parseCommandArgs = ( command : string ) : string [ ] => {
11+ const args : string [ ] = [ ]
12+ const input = command . trim ( )
13+ let i = 0
14+
15+ while ( i < input . length ) {
16+ // Skip whitespace
17+ while ( i < input . length && / \s / . test ( input [ i ] ) ) i ++
18+ if ( i >= input . length ) break
19+
20+ let arg = ""
21+ if ( input [ i ] === "\"" ) {
22+ // Double-quoted string
23+ i ++ // skip opening quote
24+ while ( i < input . length && input [ i ] !== "\"" ) {
25+ if ( input [ i ] === "\\" && i + 1 < input . length ) {
26+ i ++ // skip backslash
27+ }
28+ arg += input [ i ]
29+ i ++
30+ }
31+ i ++ // skip closing quote
32+ } else if ( input [ i ] === "'" ) {
33+ // Single-quoted string
34+ i ++ // skip opening quote
35+ while ( i < input . length && input [ i ] !== "'" ) {
36+ if ( input [ i ] === "\\" && i + 1 < input . length ) {
37+ i ++ // skip backslash
38+ }
39+ arg += input [ i ]
40+ i ++
41+ }
42+ i ++ // skip closing quote
43+ } else {
44+ // Unquoted token
45+ while ( i < input . length && ! / [ \s " ' ] / . test ( input [ i ] ) ) {
46+ arg += input [ i ]
47+ i ++
48+ }
49+ }
50+
51+ args . push ( arg )
52+ }
53+
54+ return args
55+ }
56+
657// these commands are blocked and cannot be executed because they can cause server problems
758export const BLOCKED_COMMANDS : CommandRestriction [ ] = [
859 { pattern : [ "SHUTDOWN" ] , reason : "SHUTDOWN stops the server and cannot be undone remotely." } ,
@@ -21,18 +72,18 @@ export const CONFIRM_COMMANDS: CommandRestriction[] = [
2172 { pattern : [ "CLUSTER" , "RESET" ] , reason : "CLUSTER RESET resets the cluster state and may cause data loss." } ,
2273]
2374
24- export function matchesRestriction ( command : string , restriction : CommandRestriction ) : boolean {
25- const parts = command . trim ( ) . toUpperCase ( ) . split ( / \s + / )
75+ export function matchesRestriction ( parsedArgs : string [ ] , restriction : CommandRestriction ) : boolean {
76+ const parts = parsedArgs . map ( ( p ) => p . toUpperCase ( ) )
2677 return (
2778 restriction . pattern . length <= parts . length &&
2879 restriction . pattern . every ( ( token , i ) => parts [ i ] === token )
2980 )
3081}
3182
32- export function findBlockedCommand ( command : string ) : CommandRestriction | undefined {
33- return BLOCKED_COMMANDS . find ( ( r ) => matchesRestriction ( command , r ) )
83+ export function findBlockedCommand ( parsedArgs : string [ ] ) : CommandRestriction | undefined {
84+ return BLOCKED_COMMANDS . find ( ( r ) => matchesRestriction ( parsedArgs , r ) )
3485}
3586
36- export function findConfirmCommand ( command : string ) : CommandRestriction | undefined {
37- return CONFIRM_COMMANDS . find ( ( r ) => matchesRestriction ( command , r ) )
87+ export function findConfirmCommand ( parsedArgs : string [ ] ) : CommandRestriction | undefined {
88+ return CONFIRM_COMMANDS . find ( ( r ) => matchesRestriction ( parsedArgs , r ) )
3889}
0 commit comments