Skip to content

Commit

Permalink
add dompurify
Browse files Browse the repository at this point in the history
Fix grafana review comments:
- sanitize the externally provided xml using dompurify
- correct typo in timeSlider description

This also corrects a bug with all drivable cells being treated
as if they had valid links. Now the cursor only changes when there's
a configured link.
  • Loading branch information
andymchugh committed Feb 16, 2024
1 parent 428c98c commit f19b721
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 13 deletions.
32 changes: 27 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@swc/jest": "^0.2.26",
"@testing-library/jest-dom": "6.1.4",
"@testing-library/react": "14.0.0",
"@types/dompurify": "^3.0.5",
"@types/jest": "^29.5.0",
"@types/lodash": "^4.14.194",
"@types/node": "^20.8.7",
Expand Down Expand Up @@ -63,6 +64,7 @@
"@grafana/runtime": "10.0.3",
"@grafana/schema": "10.0.3",
"@grafana/ui": "10.0.3",
"dompurify": "^3.0.8",
"js-yaml-loader": "^1.2.2",
"react": "18.2.0",
"react-dom": "18.2.0",
Expand Down
21 changes: 20 additions & 1 deletion src/components/FlowPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,24 @@ import { seriesExtend, seriesInterpolate , seriesTransform } from 'components/Ti
import { TimeSliderFactory } from 'components/TimeSlider';
import { displayColorsInner, displayDataInner, displayMappingsInner } from 'components/TroubleshootingEditor';
import { primeColorCache, appendUrlParams } from 'components/Utils';
import { addHook, sanitize } from 'dompurify';

interface Props extends PanelProps<FlowOptions> {}

// Sanitize externally defined SVG using DOMPurify. DrawIO svg's rely on
// foreignObject. In addition, the cursor changing to a pointer on mouse-over of
// cells with links, relies on foreignObject defining 'pointer-events: none'. As
// such sanitization is configured to retain these objects and put back the
// pointer-events attribute.
addHook('afterSanitizeAttributes', function(el) {
if (el.nodeName === 'foreignObject') {
el.setAttribute('pointer-events', 'none');
}
});
function sanitizeSvgStr(svgStr: string) {
return sanitize(svgStr, {ADD_TAGS: ['foreignObject']});
}

const getStyles = () => {
return {
wrapper: css`
Expand Down Expand Up @@ -92,7 +107,8 @@ export const FlowPanel: React.FC<Props> = ({ options, data, width, height, timeZ
useEffect(() => {
if (svgStr && panelConfig && siteConfig) {
configInit(siteConfig, panelConfig);
const svgDoc = new DOMParser().parseFromString(svgStr, "text/xml");

const svgDoc = new DOMParser().parseFromString(sanitizeSvgStr(svgStr), "text/xml");
const svgAttribs = svgInit(svgDoc, panelConfig, siteConfig);
primeColorCache(grafanaTheme.current, svgAttribs);
svgHolderRef.current = {
Expand Down Expand Up @@ -224,6 +240,9 @@ export const FlowPanel: React.FC<Props> = ({ options, data, width, height, timeZ
`
)}
onClick={clickHandlerRef.current}
// The externally received svg is sanitised when read in via sanitizeSvgStr which uses
// dompurify. We don't re-sanitize it on each rendering as we are in control of the
// modifications being made.
dangerouslySetInnerHTML={{__html: svgElement.outerHTML}}/>
<hr/>
<div>{timeSliderEnabled && timeSlider}</div>
Expand Down
14 changes: 8 additions & 6 deletions src/components/SvgUpdater.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,12 @@ function dimensionCoherence(doc: Document) {
function recurseElements(el: HTMLElement, cellData: SvgCell, cellIdMaker: CellIdMaker): boolean {
const setAttributes = function(el: HTMLElement) {
el.style.whiteSpace = 'pre';
el.style.cursor = 'pointer';
el.setAttribute('cursor', 'pointer');

if (cellData.cellProps.link) {
el.style.cursor = 'pointer';
el.setAttribute('cursor', 'pointer');
}

if (!el.id) {
el.setAttribute('id', cellIdMaker());
}
Expand Down Expand Up @@ -130,12 +134,10 @@ export function svgInit(doc: Document, panelConfig: PanelConfig, siteConfig: Sit
const link = panelConfigCell ? panelConfigCell.link : null;
if (link) {
cell.textElements.forEach((el) => {
let cellId = el.id;
elementLinks.set(cellId, link);
elementLinks.set(el.id, link);
});
cell.fillElements.forEach((el) => {
let cellId = el.id;
elementLinks.set(cellId, link);
elementLinks.set(el.id, link);
});
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const plugin = new PanelPlugin<FlowOptions>(FlowPanel).setPanelOptions((b
path: 'timeSliderEnabled',
name: 'Time Slider',
description: `When enabled a time-slider is added to the bottom of
the panel to support visualisaion of any time point in the time range.`,
the panel to support visualization of any time point in the time range.`,
defaultValue: true,
})
.addBooleanSwitch({
Expand Down

0 comments on commit f19b721

Please sign in to comment.