Skip to content

Commit 0e286bd

Browse files
authored
fix: Restrict link parsing so it requires protocol (#1254)
Fixes #1252 Urls require `https://` or `http://`.
1 parent 33c9673 commit 0e286bd

6 files changed

Lines changed: 59 additions & 46 deletions

File tree

packages/console/src/monaco/MonacoUtils.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,17 +158,17 @@ describe('provideLinks', () => {
158158
getLineCount: jest.fn(() => 2),
159159
getLineContent: jest.fn(lineNumber =>
160160
lineNumber === 1
161-
? 'google.com http://www.example.com/'
161+
? 'https://google.com http://www.example.com/'
162162
: 'mail@gmail.com'
163163
),
164164
} as unknown) as monaco.editor.ITextModel;
165165

166166
const expectedValue = {
167167
links: [
168-
{ url: 'http://google.com', range: new monaco.Range(1, 1, 1, 11) },
168+
{ url: 'https://google.com', range: new monaco.Range(1, 1, 1, 19) },
169169
{
170170
url: 'http://www.example.com/',
171-
range: new monaco.Range(1, 12, 1, 35),
171+
range: new monaco.Range(1, 20, 1, 43),
172172
},
173173
{ url: 'mailto:mail@gmail.com', range: new monaco.Range(2, 1, 2, 15) },
174174
],

packages/console/src/monaco/MonacoUtils.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,14 @@ class MonacoUtils {
463463

464464
for (let i = 1; i <= model.getLineCount(); i += 1) {
465465
const lineText = model.getLineContent(i);
466-
const tokens = linkifyFind(lineText);
466+
const originalTokens = linkifyFind(lineText);
467+
468+
const tokens = originalTokens.filter(token => {
469+
if (token.type === 'url') {
470+
return /^https?:\/\//.test(token.value);
471+
}
472+
return true;
473+
});
467474
// map the tokens to the ranges - you know the line number now, use the token start/end as the startColumn/endColumn
468475
tokens.forEach(token => {
469476
newTokens.push({

packages/grid/src/GridModel.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { EventTarget, Event } from 'event-target-shim';
2-
import { find as linkifyFind } from 'linkifyjs';
32
import type { IColumnHeaderGroup } from './ColumnHeaderGroup';
43
import { ModelIndex } from './GridMetrics';
54
import { GridColor, GridTheme, NullableGridColor } from './GridTheme';
65
import memoizeClear from './memoizeClear';
7-
import { Token } from './GridUtils';
6+
import GridUtils, { Token } from './GridUtils';
87

98
const LINK_TRUNCATION_LENGTH = 5000;
109

@@ -209,7 +208,7 @@ abstract class GridModel<
209208
(text: string, visibleLength: number): Token[] => {
210209
// If no text is truncated, then directly search in text
211210
if (visibleLength >= text.length) {
212-
return linkifyFind(text);
211+
return GridUtils.findTokensWithProtocolInText(text);
213212
}
214213

215214
// To check for links, we should check to the first space after the truncatedText length
@@ -223,7 +222,8 @@ abstract class GridModel<
223222
lengthOfContent = Math.min(LINK_TRUNCATION_LENGTH, text.length);
224223
}
225224
const contentToCheckForLinks = text.substring(0, lengthOfContent);
226-
return linkifyFind(contentToCheckForLinks);
225+
226+
return GridUtils.findTokensWithProtocolInText(contentToCheckForLinks);
227227
}
228228
);
229229
}

packages/grid/src/GridRenderer.test.tsx

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ describe('getTokenBoxesForVisibleCell', () => {
107107
renderState = makeMockGridRenderState({
108108
model: new MockGridModel({
109109
editedData: [
110-
['google.com', 'google', 'google.com youtube.com email@gmail.com'],
110+
[
111+
'https://google.com',
112+
'google',
113+
'http://google.com youtube.com email@gmail.com',
114+
],
111115
],
112116
}),
113117
});
@@ -128,11 +132,11 @@ describe('getTokenBoxesForVisibleCell', () => {
128132

129133
const expectedValue: LinkToken = {
130134
type: 'url',
131-
value: 'google.com',
132-
href: 'http://google.com',
135+
value: 'https://google.com',
136+
href: 'https://google.com',
133137
isLink: true,
134138
start: 0,
135-
end: 10,
139+
end: 18,
136140
};
137141

138142
expect(tokens).not.toBeNull();
@@ -145,35 +149,27 @@ describe('getTokenBoxesForVisibleCell', () => {
145149
const expectedValue: LinkToken[] = [
146150
{
147151
type: 'url',
148-
value: 'google.com',
152+
value: 'http://google.com',
149153
isLink: true,
150154
href: 'http://google.com',
151155
start: 0,
152-
end: 10,
153-
},
154-
{
155-
type: 'url',
156-
value: 'youtube.com',
157-
isLink: true,
158-
href: 'http://youtube.com',
159-
start: 11,
160-
end: 22,
156+
end: 17,
161157
},
158+
162159
{
163160
type: 'email',
164161
value: 'email@gmail.com',
165162
isLink: true,
166163
href: 'mailto:email@gmail.com',
167-
start: 23,
168-
end: 38,
164+
start: 30,
165+
end: 45,
169166
},
170167
];
171168

172169
expect(tokens).not.toBeNull();
173-
expect(tokens?.length).toBe(3);
170+
expect(tokens?.length).toBe(2);
174171
expect(tokens?.[0].token).toEqual(expectedValue[0]);
175172
expect(tokens?.[1].token).toEqual(expectedValue[1]);
176-
expect(tokens?.[2].token).toEqual(expectedValue[2]);
177173
});
178174

179175
it('should return empty array if there are no tokens', () => {

packages/grid/src/GridUtils.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import clamp from 'lodash.clamp';
3+
import { find as linkifyFind } from 'linkifyjs';
34
import { EMPTY_ARRAY } from '@deephaven/utils';
45
import GridRange, { GridRangeIndex } from './GridRange';
56
import {
@@ -1439,6 +1440,22 @@ export class GridUtils {
14391440
token: tokenBox.token,
14401441
};
14411442
}
1443+
1444+
/**
1445+
* Finds tokens in text (urls, emails) that start with https:// or http://
1446+
* @param text The text to search in
1447+
* @returns An array of tokens
1448+
*/
1449+
static findTokensWithProtocolInText(text: string): Token[] {
1450+
const tokens = linkifyFind(text);
1451+
1452+
return tokens.filter(token => {
1453+
if (token.type === 'url') {
1454+
return /^https?:\/\//.test(token.value);
1455+
}
1456+
return true;
1457+
});
1458+
}
14421459
}
14431460

14441461
export default GridUtils;

packages/grid/src/MockGridModel.test.ts

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import MockGridModel from './MockGridModel';
33

44
describe('tokensForCell', () => {
55
it('should return tokens for a cell', () => {
6-
const model = new MockGridModel({ editedData: [['google.com']] });
6+
const model = new MockGridModel({ editedData: [['https://google.com']] });
77
const expectedValue: LinkToken[] = [
88
{
99
type: 'url',
10-
value: 'google.com',
10+
value: 'https://google.com',
1111
isLink: true,
12-
href: 'http://google.com',
12+
href: 'https://google.com',
1313
start: 0,
14-
end: 10,
14+
end: 18,
1515
},
1616
];
1717

@@ -21,41 +21,34 @@ describe('tokensForCell', () => {
2121
});
2222

2323
it('should return multiple tokens for a cell', () => {
24-
const text = 'google.com youtube.com blah@gmail.com';
24+
const text = 'https://google.com youtube.com blah@gmail.com';
2525
const model = new MockGridModel({ editedData: [[text]] });
2626
const expectedValue: LinkToken[] = [
2727
{
2828
type: 'url',
29-
value: 'google.com',
29+
value: 'https://google.com',
3030
isLink: true,
31-
href: 'http://google.com',
31+
href: 'https://google.com',
3232
start: 0,
33-
end: 10,
34-
},
35-
{
36-
type: 'url',
37-
value: 'youtube.com',
38-
isLink: true,
39-
href: 'http://youtube.com',
40-
start: 11,
41-
end: 22,
33+
end: 18,
4234
},
35+
4336
{
4437
type: 'email',
4538
value: 'blah@gmail.com',
4639
isLink: true,
4740
href: 'mailto:blah@gmail.com',
48-
start: 23,
49-
end: 37,
41+
start: 31,
42+
end: 45,
5043
},
5144
];
5245

5346
const tokens = model.tokensForCell(0, 0, text.length);
54-
expect(tokens).toHaveLength(3);
47+
expect(tokens).toHaveLength(2);
5548
expect(tokens).toEqual(expectedValue);
5649
});
5750

58-
it('should an empty array for a cell with no tokens', () => {
51+
it('should return an empty array for a cell with no tokens', () => {
5952
const text = 'google youtube';
6053
const model = new MockGridModel({ editedData: [[text]] });
6154
const expectedValue: LinkToken[] = [];

0 commit comments

Comments
 (0)