Skip to content

Commit a9d52ba

Browse files
author
arthosofteq
authored
Merge pull request #1432 from RedisInsight/be/bugfix/RI-3789-fix-cli-command-parsing
#RI-3789 fix cli command parsing
2 parents e7163b0 + 9f939ad commit a9d52ba

File tree

2 files changed

+70
-63
lines changed

2 files changed

+70
-63
lines changed

redisinsight/api/src/utils/cli-helper.spec.ts

Lines changed: 54 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/quotes */
12
import { randomBytes } from 'crypto';
23
import ERROR_MESSAGES from 'src/constants/error-messages';
34
import { CommandParsingError, RedirectionParsingError } from 'src/modules/cli/constants/errors';
@@ -20,64 +21,59 @@ import {
2021

2122
describe('Cli helper', () => {
2223
describe('splitCliCommandLine', () => {
23-
it('should correctly split simple command with args', () => {
24-
const input = 'memory usage key';
25-
26-
const output = splitCliCommandLine(input);
27-
28-
expect(output).toEqual(['memory', 'usage', 'key']);
29-
});
30-
it('should correctly split command with special symbols in the args in the double quotes', () => {
31-
const input = 'set test "—"';
32-
33-
const output = splitCliCommandLine(input);
34-
const buffer = Buffer.from('e28094', 'hex');
35-
expect(output).toEqual(['set', 'test', buffer]);
36-
});
37-
// todo: enable after review splitCliCommandLine functionality
38-
xit('should correctly split command with special symbols in the args in the single quotes', () => {
39-
const input = "set test '—'";
40-
41-
const output = splitCliCommandLine(input);
42-
43-
const buffer = Buffer.from('e28094', 'hex');
44-
expect(output).toEqual(['set', 'test', buffer]);
45-
});
46-
it('should correctly split simple command without args', () => {
47-
const input = 'info';
48-
49-
const output = splitCliCommandLine(input);
50-
51-
expect(output).toEqual(['info']);
52-
});
53-
it('should correctly split command with double quotes', () => {
54-
const input = 'get "key name"';
55-
56-
const output = splitCliCommandLine(input);
57-
expect(output).toEqual(['get', Buffer.from('key name')]);
58-
});
59-
it('should correctly split command with single quotes', () => {
60-
const input = "get 'key name'";
61-
62-
const output = splitCliCommandLine(input);
63-
64-
expect(output).toEqual(['get', 'key name']);
65-
});
66-
it('should correctly handle special character', () => {
67-
const input = 'set key "\\a\\b\\t\\n\\r"';
68-
const output = splitCliCommandLine(input);
69-
70-
expect(output).toEqual([
71-
'set',
72-
'key',
73-
Buffer.alloc(5, String.fromCharCode(7, 8, 9, 10, 13)),
74-
]);
75-
});
76-
it('should correctly handle hexadecimal', () => {
77-
const input = 'set key "\\xac\\xed"';
78-
const output = splitCliCommandLine(input);
79-
80-
expect(output).toEqual(['set', 'key', Buffer.from([172, 237])]);
24+
[
25+
{
26+
input: 'memory usage key',
27+
output: ['memory', 'usage', 'key'],
28+
},
29+
{
30+
input: 'set test "—"',
31+
output: ['set', 'test', '—'],
32+
},
33+
{
34+
input: "set test '—'",
35+
output: ['set', 'test', '—'],
36+
},
37+
{
38+
input: 'info',
39+
output: ['info'],
40+
},
41+
{
42+
input: 'get "key name"',
43+
output: ['get', 'key name'],
44+
},
45+
{
46+
input: `get "key ' name"`,
47+
output: ['get', `key ' name`],
48+
},
49+
{
50+
input: `get "key \\" name"`,
51+
output: ['get', `key " name`],
52+
},
53+
{
54+
input: "get 'key name'",
55+
output: ['get', 'key name'],
56+
},
57+
{
58+
input: `s"et" ~\\'\\nk"k "ey' 1`,
59+
output: ['set', `~\\\\nk"k "ey`, '1'],
60+
},
61+
{
62+
input: 'set key "\\a\\b\\t\\n\\r"',
63+
output: ['set', 'key', `\u0007\u0008\u0009\n\r`],
64+
},
65+
{
66+
input: 'set key "\\xac\\xed"',
67+
output: ['set', 'key', Buffer.from([172, 237])],
68+
},
69+
{
70+
input: `ACL SETUSER t on nopass ~'\\x00' &* +@all`,
71+
output: ['ACL', 'SETUSER', 't', 'on', 'nopass', '~\\x00', '&*', '+@all'],
72+
},
73+
].forEach((tc) => {
74+
it(`should return ${JSON.stringify(tc.output)} for command ${tc.input}`, async () => {
75+
expect(splitCliCommandLine(tc.input)).toEqual(tc.output);
76+
});
8177
});
8278
it('should throw [CLI_INVALID_QUOTES_CLOSING] error for command with double quotes', () => {
8379
const input = 'get "key"a';

redisinsight/api/src/utils/cli-helper.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,25 @@ function getSpecChar(str: string): string {
6363
return char;
6464
}
6565

66+
export const convertToStringIfPossible = (data: any) => {
67+
if (data instanceof Buffer) {
68+
const str = data.toString();
69+
if (Buffer.compare(data, Buffer.from(str)) === 0) {
70+
return str;
71+
}
72+
}
73+
74+
return data;
75+
};
76+
6677
// todo: review/rewrite this function. Pay attention on handling data inside '' vs ""
6778
// todo: rethink implementation. set key {value} where {value} is string ~500KB take ~15s
6879
export const splitCliCommandLine = (line: string): string[] => {
6980
// Splits a command line into a list of arguments.
7081
// Ported from sdssplitargs() function in sds.c from Redis source code.
7182
// This is the function redis-cli uses to parse command lines.
7283
let i = 0;
73-
let currentArg = null;
84+
let currentArg: any = '';
7485
const args = [];
7586
while (i < line.length) {
7687
/* skip blanks */
@@ -141,19 +152,19 @@ export const splitCliCommandLine = (line: string): string[] => {
141152
} else if ([' ', '\n', '\r', '\t', '\0'].includes(line[i])) {
142153
done = true;
143154
} else if (line[i] === '"') {
144-
currentArg = Buffer.alloc(0);
155+
currentArg = Buffer.from(currentArg);
145156
inq = true;
146157
} else if (line[i] === "'") {
147-
currentArg = '';
148158
insq = true;
149159
} else {
150160
currentArg = `${currentArg || ''}${line[i]}`;
151161
}
152162
if (i < line.length) i += 1;
153163
}
154-
args.push(currentArg);
155-
currentArg = null;
164+
args.push(convertToStringIfPossible(currentArg));
165+
currentArg = '';
156166
}
167+
157168
return args;
158169
};
159170

0 commit comments

Comments
 (0)