Skip to content

Commit 977f30f

Browse files
authored
Merge pull request #80 from RedisInsight/feature/codeql-fe-fixes
#RI-2088 - codeql fe fixes
2 parents fd40f79 + 15579fa commit 977f30f

File tree

17 files changed

+171
-93
lines changed

17 files changed

+171
-93
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/* ]
16+
branches: [ main, latest, release/*, codeql ]
1717
pull_request:
1818
# The branches below must be a subset of the branches above
1919
branches: [ main ]

configs/webpack.config.main.prod.babel.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import path from 'path';
22
import webpack from 'webpack';
33
import { merge } from 'webpack-merge';
4-
import TerserPlugin from 'terser-webpack-plugin';
54
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
65
import baseConfig from './webpack.config.base';
76
import DeleteSourceMaps from '../scripts/DeleteSourceMaps';

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
}

redisinsight/main.dev.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,10 @@ export const createWindow = async (splash: BrowserWindow | null = null) => {
185185
webPreferences: {
186186
nodeIntegration: true,
187187
nodeIntegrationInWorker: true,
188-
webSecurity: false,
188+
webSecurity: true,
189189
contextIsolation: false,
190190
spellcheck: true,
191-
allowRunningInsecureContent: true,
192-
enableRemoteModule: true,
191+
allowRunningInsecureContent: false,
193192
scrollBounce: true,
194193
},
195194
});

redisinsight/ui/src/components/ContentEditable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ export const parseMultilineContentEditableChangeHtml = (text: string = '') =>
2828
export const parseContentEditableHtml = (text: string = '') =>
2929
text
3030
.replace(/&nbsp;/gi, ' ')
31-
.replace(/&amp;/gi, '&')
3231
.replace(/&lt;/gi, '<')
3332
.replace(/&gt;/gi, '>')
33+
.replace(/&amp;/gi, '&')
3434

3535
const onPaste = (e: React.ClipboardEvent) => {
3636
e.preventDefault()

redisinsight/ui/src/components/query-card/QueryCardCliPlugin/QueryCardCliPlugin.tsx

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ const QueryCardCliPlugin = (props: Props) => {
4545
const generatedIframeNameRef = useRef<string>('')
4646
const { theme } = useContext(ThemeContext)
4747

48-
const dispatch = useDispatch()
49-
5048
const sendMessageToPlugin = (data = {}) => {
5149
const event: any = document.createEvent('Event')
5250
event.initEvent('message', false, false)
@@ -63,21 +61,6 @@ const QueryCardCliPlugin = (props: Props) => {
6361
})
6462
}
6563

66-
const sendRedisCommand = (command: string, requestId: string) => {
67-
dispatch(
68-
sendPluginCommandAction({
69-
command,
70-
onSuccessAction: (response) => {
71-
sendMessageToPlugin({
72-
event: 'executeRedisCommand',
73-
requestId,
74-
data: response
75-
})
76-
}
77-
})
78-
)
79-
}
80-
8164
useEffect(() => {
8265
if (currentView === null) return
8366
pluginApi.onEvent(generatedIframeNameRef.current, PluginEvents.heightChanged, (height: string) => {

0 commit comments

Comments
 (0)