TextPanel: Allow markdown to-do checkboxes in TextPanel (#104136)

TextPanel: Allow markdown to-do checkboxes
Fixes #95054
mrdlesniak-patch-1
Luminessa Starlight 3 months ago committed by GitHub
parent 82184686dc
commit 8b28c84017
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 16
      packages/grafana-data/src/text/markdown.test.ts
  2. 24
      packages/grafana-data/src/text/sanitize.test.ts
  3. 8
      packages/grafana-data/src/text/sanitize.ts

@ -23,6 +23,22 @@ describe('Markdown wrapper', () => {
); );
}); });
it('should allow markdown todo checkbox inputs', () => {
const str = renderTextPanelMarkdown(`- [ ] unchecked
- [x] checked`);
expect(str).toMatch(/<input disabled(="")? type="checkbox">/);
expect(str).toMatch(/<input checked(="")? disabled(="")? type="checkbox">/);
});
it('should sanitize arbitrary input elements', () => {
const str = renderTextPanelMarkdown(`<input>
<input type="text">
<input disabled="" type="radio">
<input disabled="" type="checkbox" class="some-class">
<input checked="" disabled="" type="checkbox" class="some-class">`);
expect(str).not.toMatch(/<input/);
});
it('should sanitize content in text panel by default', () => { it('should sanitize content in text panel by default', () => {
const str = renderTextPanelMarkdown('<script>alert()</script>'); const str = renderTextPanelMarkdown('<script>alert()</script>');
expect(str).toBe('&lt;script&gt;alert()&lt;/script&gt;'); expect(str).toBe('&lt;script&gt;alert()&lt;/script&gt;');

@ -1,6 +1,6 @@
import { sanitizeTextPanelContent, sanitizeUrl, sanitize } from './sanitize'; import { sanitizeTextPanelContent, sanitizeUrl, sanitize } from './sanitize';
describe('Sanitize wrapper', () => { describe('sanitizeTextPanelContent', () => {
it('should allow whitelisted styles in text panel', () => { it('should allow whitelisted styles in text panel', () => {
const html = const html =
'<div style="display:flex; flex-direction: column; flex-wrap: wrap; justify-content: start; gap: 2px;"><div style="flex-basis: 50%"></div></div>'; '<div style="display:flex; flex-direction: column; flex-wrap: wrap; justify-content: start; gap: 2px;"><div style="flex-basis: 50%"></div></div>';
@ -9,6 +9,28 @@ describe('Sanitize wrapper', () => {
'<div style="display:flex; flex-direction:column; flex-wrap:wrap; justify-content:start; gap:2px;"><div style="flex-basis:50%;"></div></div>' '<div style="display:flex; flex-direction:column; flex-wrap:wrap; justify-content:start; gap:2px;"><div style="flex-basis:50%;"></div></div>'
); );
}); });
it('should escape xss payload', () => {
const html = '<script>alert(1)</script>';
const str = sanitizeTextPanelContent(html);
expect(str).toBe('&lt;script&gt;alert(1)&lt;/script&gt;');
});
it('should allow markdown generated unstyled disabled checkbox inputs', () => {
const str = sanitizeTextPanelContent(`<input disabled="" type="checkbox">
<input checked="" disabled="" type="checkbox">`);
expect(str).toMatch(/<input disabled(="")? type="checkbox">/);
expect(str).toMatch(/<input checked(="")? disabled(="")? type="checkbox">/);
});
it('should sanitize arbitrary input elements', () => {
const str = sanitizeTextPanelContent(`<input>
<input type="text">
<input disabled="" type="radio">
<input disabled="" type="checkbox" class="some-class">
<input checked="" disabled="" type="checkbox" class="some-class">`);
expect(str).not.toMatch(/<input/);
});
}); });
describe('sanitizeUrl', () => { describe('sanitizeUrl', () => {

@ -13,7 +13,7 @@ XSSWL.iframe = ['src', 'width', 'height'];
const sanitizeTextPanelWhitelist = new xss.FilterXSS({ const sanitizeTextPanelWhitelist = new xss.FilterXSS({
// Add sandbox attribute to iframe tags if an attribute is allowed. // Add sandbox attribute to iframe tags if an attribute is allowed.
onTagAttr: function (tag, name, value, isWhiteAttr) { onTagAttr(tag, name, value, isWhiteAttr) {
if (tag === 'iframe') { if (tag === 'iframe') {
return isWhiteAttr return isWhiteAttr
? ` ${name}="${xss.escapeAttrValue(sanitizeUrl(value))}" sandbox credentialless referrerpolicy=no-referrer` ? ` ${name}="${xss.escapeAttrValue(sanitizeUrl(value))}" sandbox credentialless referrerpolicy=no-referrer`
@ -21,6 +21,12 @@ const sanitizeTextPanelWhitelist = new xss.FilterXSS({
} }
return; return;
}, },
onTag(tag, html, options) {
if (html === '<input disabled="" type="checkbox">' || html === '<input checked="" disabled="" type="checkbox">') {
return html;
}
return;
},
whiteList: XSSWL, whiteList: XSSWL,
css: { css: {
whiteList: { whiteList: {

Loading…
Cancel
Save