Skip to content

Commit 359c15b

Browse files
Merge pull request #105 from RedisInsight/feature/codeql-be-fixes
Codeql BE
2 parents 7dd4d4a + 7cb8336 commit 359c15b

File tree

6 files changed

+135
-40
lines changed

6 files changed

+135
-40
lines changed

.github/workflows/codeql-analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ name: "CodeQL"
1313

1414
on:
1515
push:
16-
branches: [ main, latest, release/*, feature/codeql-fe-fixes ]
16+
branches: [ main, latest, release/*, feature/codeql-fe-fixes, feature/codeql-be-fixes ]
1717
pull_request:
1818
# The branches below must be a subset of the branches above
1919
branches: [ main ]

redisinsight/api/src/modules/browser/dto/keys.dto.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export class UpdateKeyTtlDto {
201201
@ApiProperty({
202202
type: Number,
203203
description:
204-
'Set a timeout on key in seconds. After the timeout has expired, the key will automatically be deleted.'
204+
'Set a timeout on key in seconds. After the timeout has expired, the key will automatically be deleted. '
205205
+ 'If the property has value of -1, then the key timeout will be removed.',
206206
maximum: MAX_TTL_NUMBER,
207207
})
@@ -216,7 +216,7 @@ export class KeyTtlResponse {
216216
type: Number,
217217
description:
218218
'The remaining time to live of a key that has a timeout. '
219-
+ 'If value equals -2 then the key does not exist or has deleted.'
219+
+ 'If value equals -2 then the key does not exist or has deleted. '
220220
+ 'If value equals -1 then the key has no associated expire (No limit).',
221221
maximum: MAX_TTL_NUMBER,
222222
})

redisinsight/api/src/modules/browser/services/keys-business/scanner/strategies/cluster.strategy.ts

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,11 @@ import {
1212
GetKeysWithDetailsResponse,
1313
RedisDataType,
1414
} from 'src/modules/browser/dto';
15-
import ERROR_MESSAGES from 'src/constants/error-messages';
15+
import { parseClusterCursor } from 'src/modules/browser/utils/clusterCursor';
1616
import { ISettingsProvider } from 'src/modules/core/models/settings-provider.interface';
1717
import { AbstractStrategy } from './abstract.strategy';
1818
import { IGetNodeKeysResult } from '../scanner.interface';
1919

20-
const NODES_SEPARATOR = '||';
21-
const CURSOR_SEPARATOR = '@';
22-
// Correct format 172.17.0.1:7001@-1||172.17.0.1:7002@33
23-
const CLUSTER_CURSOR_REGEX = /^(([a-z0-9.])+:[0-9]+(@-?\d+))+((\|\|)?([a-z0-9.])+:[0-9]+(@-?\d+))*$/;
2420
const REDIS_SCAN_CONFIG = config.get('redis_scan');
2521

2622
export class ClusterStrategy extends AbstractStrategy {
@@ -100,7 +96,7 @@ export class ClusterStrategy extends AbstractStrategy {
10096
initialCursor: string,
10197
): Promise<IGetNodeKeysResult[]> {
10298
if (Number.isNaN(toNumber(initialCursor))) {
103-
return this.getNodesFromClusterCursor(initialCursor);
99+
return parseClusterCursor(initialCursor);
104100
}
105101

106102
const clusterNodes = await this.redisManager.getNodes(
@@ -118,35 +114,6 @@ export class ClusterStrategy extends AbstractStrategy {
118114
}));
119115
}
120116

121-
/**
122-
* Parses composed custom cursor from FE and returns nodes
123-
* Format: 172.17.0.1:7001@22||172.17.0.1:7002@33
124-
*/
125-
private getNodesFromClusterCursor(cursor: string): IGetNodeKeysResult[] {
126-
const isCorrectFormat = CLUSTER_CURSOR_REGEX.test(cursor);
127-
if (!isCorrectFormat) {
128-
throw new Error(ERROR_MESSAGES.INCORRECT_CLUSTER_CURSOR_FORMAT);
129-
}
130-
const nodeStrings = cursor.split(NODES_SEPARATOR);
131-
const nodes = [];
132-
133-
nodeStrings.forEach((item: string) => {
134-
const [address, nextCursor] = item.split(CURSOR_SEPARATOR);
135-
const [host, port] = address.split(':');
136-
if (parseInt(nextCursor, 10) >= 0) {
137-
nodes.push({
138-
total: 0,
139-
scanned: 0,
140-
host,
141-
port: parseInt(port, 10),
142-
cursor: parseInt(nextCursor, 10),
143-
keys: [],
144-
});
145-
}
146-
});
147-
return nodes;
148-
}
149-
150117
private async calculateNodesTotalKeys(
151118
clientOptions,
152119
nodes: IGetNodeKeysResult[],
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import ERROR_MESSAGES from 'src/constants/error-messages';
2+
import { isClusterCursorValid, parseClusterCursor } from './clusterCursor';
3+
4+
const isClusterCursorValidTests = [
5+
{ input: '172.17.0.1:7001@22||172.17.0.1:7002@33', expected: true },
6+
{ input: '172.17.0.1:7001@-1||172.17.0.1:7002@-1', expected: true },
7+
{
8+
input: '172.17.0.1:7001@10'
9+
+ '||172.17.0.1:7002@10'
10+
+ '||172.17.0.1:7003@10'
11+
+ '||172.17.0.1:7004@10'
12+
+ '||172.17.0.1:7005@10'
13+
+ '||172.17.0.1:7006@10',
14+
expected: true,
15+
},
16+
{ input: '172.17.0.1:7001@-1', expected: true },
17+
{ input: 'domain.com:7001@-1', expected: true },
18+
{ input: '172.17.0.1:7001@1228822', expected: true },
19+
{ input: '172.17.0.1:7001@', expected: false },
20+
{ input: '172.17.0.1:7001@text', expected: false },
21+
{ input: '172,17,0,1:7001@-1', expected: false },
22+
{ input: 'plain text', expected: false },
23+
{ input: 'text@text||text@text', expected: false },
24+
{ input: 'text@text', expected: false },
25+
{ input: '', expected: false },
26+
];
27+
28+
describe('isClusterCursorValid', () => {
29+
it.each(isClusterCursorValidTests)('%j', ({ input, expected }) => {
30+
expect(isClusterCursorValid(input)).toBe(expected);
31+
});
32+
});
33+
34+
const defaultNodeScanResult = {
35+
total: 0, scanned: 0, host: '172.17.0.1', port: 0, cursor: 0, keys: [],
36+
};
37+
const parsingError = new Error(ERROR_MESSAGES.INCORRECT_CLUSTER_CURSOR_FORMAT);
38+
const parseClusterCursorTests = [
39+
{
40+
input: '172.17.0.1:7001@22||172.17.0.1:7002@33',
41+
expected: [
42+
{ ...defaultNodeScanResult, port: 7001, cursor: 22 },
43+
{ ...defaultNodeScanResult, port: 7002, cursor: 33 },
44+
],
45+
},
46+
{
47+
input: '172.17.0.1:7001@-1'
48+
+ '||172.17.0.1:7002@10'
49+
+ '||172.17.0.1:7003@-1'
50+
+ '||172.17.0.1:7004@10'
51+
+ '||172.17.0.1:7005@-1'
52+
+ '||172.17.0.1:7006@10',
53+
expected: [
54+
{ ...defaultNodeScanResult, port: 7002, cursor: 10 },
55+
{ ...defaultNodeScanResult, port: 7004, cursor: 10 },
56+
{ ...defaultNodeScanResult, port: 7006, cursor: 10 },
57+
],
58+
},
59+
{
60+
input: '172.17.0.1:7001@-1||172.17.0.1:7002@-1',
61+
expected: [],
62+
},
63+
{ input: '172.17.0.1:7001@', expected: parsingError },
64+
{ input: '172.17.0.1:7001@text', expected: parsingError },
65+
{ input: '172,17,0,1:7001@-1', expected: parsingError },
66+
{ input: 'plain text', expected: parsingError },
67+
{ input: 'text@text||text@text', expected: parsingError },
68+
{ input: 'text@text', expected: parsingError },
69+
{ input: '', expected: parsingError },
70+
{ input: '', expected: parsingError },
71+
];
72+
73+
describe('parseClusterCursor', () => {
74+
it.each(parseClusterCursorTests)('%j', ({ input, expected }) => {
75+
if (expected instanceof Error) {
76+
try {
77+
parseClusterCursor(input);
78+
} catch (e) {
79+
expect(e.message).toEqual(expected.message);
80+
}
81+
} else {
82+
expect(parseClusterCursor(input)).toEqual(expected);
83+
}
84+
});
85+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import ERROR_MESSAGES from 'src/constants/error-messages';
2+
import { IGetNodeKeysResult } from 'src/modules/browser/services/keys-business/scanner/scanner.interface';
3+
4+
const NODES_SEPARATOR = '||';
5+
const CURSOR_SEPARATOR = '@';
6+
// Correct format 172.17.0.1:7001@-1||172.17.0.1:7002@33
7+
const CLUSTER_CURSOR_REGEX = /^(([a-z0-9.])+:[0-9]+(@-?\d+)(?:\|{2}(?!$)|$))+$/;
8+
9+
export const isClusterCursorValid = (cursor) => CLUSTER_CURSOR_REGEX.test(cursor);
10+
11+
/**
12+
* Parses composed custom cursor from FE and returns nodes
13+
* Format: 172.17.0.1:7001@22||172.17.0.1:7002@33
14+
*/
15+
export const parseClusterCursor = (cursor: string): IGetNodeKeysResult[] => {
16+
if (!isClusterCursorValid(cursor)) {
17+
throw new Error(ERROR_MESSAGES.INCORRECT_CLUSTER_CURSOR_FORMAT);
18+
}
19+
const nodeStrings = cursor.split(NODES_SEPARATOR);
20+
const nodes = [];
21+
22+
nodeStrings.forEach((item: string) => {
23+
const [address, nextCursor] = item.split(CURSOR_SEPARATOR);
24+
const [host, port] = address.split(':');
25+
26+
// ignore nodes with cursor -1 (fully scanned)
27+
if (parseInt(nextCursor, 10) >= 0) {
28+
nodes.push({
29+
total: 0,
30+
scanned: 0,
31+
host,
32+
port: parseInt(port, 10),
33+
cursor: parseInt(nextCursor, 10),
34+
keys: [],
35+
});
36+
}
37+
});
38+
return nodes;
39+
};

redisinsight/api/src/utils/hosting-provider-helper.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { HostingProvider } from 'src/modules/core/models/database-instance.entity';
22
import { IP_ADDRESS_REGEX, PRIVATE_IP_ADDRESS_REGEX } from 'src/constants';
33

4+
// Ignore LGTM [js/incomplete-url-substring-sanitization] alert.
5+
// Because we do not bind potentially dangerous logic to this.
6+
// We define a hosting provider for telemetry only.
47
export const getHostingProvider = (host: string): HostingProvider => {
58
// Tries to detect the hosting provider from the hostname.
69
if (host === '0.0.0.0' || host === 'localhost') {
@@ -9,12 +12,13 @@ export const getHostingProvider = (host: string): HostingProvider => {
912
if (IP_ADDRESS_REGEX.test(host) && PRIVATE_IP_ADDRESS_REGEX.test(host)) {
1013
return HostingProvider.LOCALHOST;
1114
}
12-
if (host.endsWith('rlrcp.com') || host.endsWith('redislabs.com')) {
15+
if (host.endsWith('rlrcp.com') || host.endsWith('redislabs.com')) { // lgtm[js/incomplete-url-substring-sanitization]
1316
return HostingProvider.RE_CLOUD;
1417
}
15-
if (host.endsWith('cache.amazonaws.com')) {
18+
if (host.endsWith('cache.amazonaws.com')) { // lgtm[js/incomplete-url-substring-sanitization]
1619
return HostingProvider.AWS;
1720
}
21+
// lgtm[js/incomplete-url-substring-sanitization]
1822
if (host.endsWith('cache.windows.net')) {
1923
return HostingProvider.AZURE;
2024
}

0 commit comments

Comments
 (0)