11import  React  from  'react' ; 
22import  {  ChartProps  }  from  '../Chart' ; 
33import  ReactMarkdown  from  'react-markdown' ; 
4- import  gfm  from  'remark-gfm' ; 
4+ import  remarkGfm  from  'remark-gfm' ; 
5+ import  URI  from  'urijs' ; 
56import  {  replaceDashboardParameters  }  from  '../ChartUtils' ; 
67
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+ 
726/** 
827 * Renders Markdown text provided by the user. 
928 */ 
1029const  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+ 
1138  // Records are overridden to be a single element array with a field called 'input'. 
1239  const  {  records }  =  props ; 
1340  const  parameters  =  props . parameters  ? props . parameters  : { } ; 
@@ -17,13 +44,18 @@ const NeoMarkdownChart = (props: ChartProps) => {
1744      : true ; 
1845  const  markdown  =  records [ 0 ] . input ; 
1946  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 
2147  return  ( 
2248    < div 
2349      className = 'markdown-widget' 
2450      style = { {  marginTop : '0px' ,  marginLeft : '15px' ,  marginRight : '15px' ,  marginBottom : '0px'  } } 
2551    > 
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+       /> 
2759    </ div > 
2860  ) ; 
2961} ; 
0 commit comments