1
1
import React from 'react' ;
2
2
import { ChartProps } from '../Chart' ;
3
3
import ReactMarkdown from 'react-markdown' ;
4
- import gfm from 'remark-gfm' ;
4
+ import remarkGfm from 'remark-gfm' ;
5
+ import URI from 'urijs' ;
5
6
import { replaceDashboardParameters } from '../ChartUtils' ;
6
7
8
+ // Sanitizes URIs
9
+ const transformUri = ( uri : string ) : string | undefined => {
10
+ const parsedUri = URI ( uri ) ;
11
+ if ( parsedUri . protocol ( ) === 'http' || parsedUri . protocol ( ) === 'https' ) {
12
+ return parsedUri . toString ( ) ; // Convert URI object back to string
13
+ }
14
+ return undefined ; // Return undefined to skip rendering of potentially unsafe URLs
15
+ } ;
16
+
17
+ // Define custom components for Markdown elements
18
+ const CustomTable = ( { _, ...props } ) => < table { ...props } className = 'markdown-table' /> ;
19
+ const CustomTh = ( { _, ...props } ) => < th { ...props } className = 'markdown-th' /> ;
20
+ const CustomTd = ( { _, ...props } ) => < td { ...props } className = 'markdown-td' /> ;
21
+ const CustomATag = ( { _, href, ...props } ) => (
22
+ // Apply URI transformation right in the anchor element for additional security
23
+ < a href = { href ? transformUri ( href ) : undefined } { ...props } rel = 'noopener noreferrer' target = '_blank' />
24
+ ) ;
25
+
7
26
/**
8
27
* Renders Markdown text provided by the user.
9
28
*/
10
29
const NeoMarkdownChart = ( props : ChartProps ) => {
30
+ // Define custom components for Markdown elements
31
+ const components = {
32
+ table : CustomTable ,
33
+ th : CustomTh ,
34
+ td : CustomTd ,
35
+ a : CustomATag ,
36
+ } ;
37
+
11
38
// Records are overridden to be a single element array with a field called 'input'.
12
39
const { records } = props ;
13
40
const parameters = props . parameters ? props . parameters : { } ;
@@ -17,13 +44,18 @@ const NeoMarkdownChart = (props: ChartProps) => {
17
44
: true ;
18
45
const markdown = records [ 0 ] . input ;
19
46
const modifiedMarkdown = replaceGlobalParameters ? replaceDashboardParameters ( markdown , parameters ) : markdown ;
20
- // TODO: we should check if the gfm plugin has an impact on the standard security provided by ReactMarkdown
21
47
return (
22
48
< div
23
49
className = 'markdown-widget'
24
50
style = { { marginTop : '0px' , marginLeft : '15px' , marginRight : '15px' , marginBottom : '0px' } }
25
51
>
26
- < base target = '_blank' /> < ReactMarkdown remarkPLugins = { [ gfm ] } children = { modifiedMarkdown } />
52
+ < base target = '_blank' />
53
+ < ReactMarkdown
54
+ children = { modifiedMarkdown }
55
+ remarkPlugins = { [ remarkGfm ] }
56
+ components = { components }
57
+ transformLinkUri = { transformUri }
58
+ />
27
59
</ div >
28
60
) ;
29
61
} ;
0 commit comments