feat: sanitize HTML in footer
moved sanitize function into a separate file including some simple tests closes #91
This commit is contained in:
committed by
Bastian
parent
5a5928f2dd
commit
e0113c446d
@@ -186,6 +186,8 @@ To add a footnote to the footer, create a public folder in your application and
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> The footnote information may include HTML like `<a href="https://foo.bar">My Link</a>` which will be sanitized.
|
||||||
|
|
||||||
### Add a help page with explanations
|
### Add a help page with explanations
|
||||||
To add a help page, create a public folder in your application and put a `messages.json` in it.
|
To add a help page, create a public folder in your application and put a `messages.json` in it.
|
||||||
```json
|
```json
|
||||||
@@ -252,6 +254,8 @@ To add a help page, create a public folder in your application and put a `messag
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> The information in `description`s for `rings` and `quadrants` as well as the `values` for `paragraphs` may include HTML like `<a href="https://foo.bar">My Link</a>` which will be sanitized.
|
||||||
|
|
||||||
> For more information see the source code of the [Messages Context](./src/context/MessagesContext/index.tsx).
|
> For more information see the source code of the [Messages Context](./src/context/MessagesContext/index.tsx).
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { assetUrl, getItemPageNames, isMobileViewport } from "../../config";
|
|||||||
import { Item } from "../../model";
|
import { Item } from "../../model";
|
||||||
import "./footer.scss";
|
import "./footer.scss";
|
||||||
import { useMessages } from "../../context/MessagesContext";
|
import { useMessages } from "../../context/MessagesContext";
|
||||||
|
import { sanitize } from "../../sanitize";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
items: Item[];
|
items: Item[];
|
||||||
@@ -34,7 +35,7 @@ const Footer: React.FC<Props> = ({ items, pageName }) => {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span className="footnote">{footerFootnote}</span>
|
<div className="footnote" dangerouslySetInnerHTML={sanitize(footerFootnote)}></div>
|
||||||
</Branding>
|
</Branding>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,16 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import sanitizeHtml from 'sanitize-html';
|
|
||||||
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||||
import Fadeable from "../Fadeable/Fadeable";
|
import Fadeable from "../Fadeable/Fadeable";
|
||||||
import SetTitle from "../SetTitle";
|
import SetTitle from "../SetTitle";
|
||||||
import { radarName } from "../../config";
|
import { radarName } from "../../config";
|
||||||
import { useMessages } from "../../context/MessagesContext";
|
import { useMessages } from "../../context/MessagesContext";
|
||||||
|
import { sanitize } from "../../sanitize";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
leaving: boolean;
|
leaving: boolean;
|
||||||
onLeave: () => void;
|
onLeave: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sanitize = (dirty: string, options: sanitizeHtml.IOptions = {}) => ({
|
|
||||||
__html: sanitizeHtml(
|
|
||||||
dirty,
|
|
||||||
options
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
const PageHelp: React.FC<Props> = ({ leaving, onLeave }) => {
|
const PageHelp: React.FC<Props> = ({ leaving, onLeave }) => {
|
||||||
const { pageHelp } = useMessages();
|
const { pageHelp } = useMessages();
|
||||||
|
|
||||||
@@ -34,12 +27,7 @@ const PageHelp: React.FC<Props> = ({ leaving, onLeave }) => {
|
|||||||
<h3>{headline}</h3>
|
<h3>{headline}</h3>
|
||||||
{values.map((element, index) => {
|
{values.map((element, index) => {
|
||||||
return (
|
return (
|
||||||
<p key={index} dangerouslySetInnerHTML={sanitize(element, {
|
<p key={index} dangerouslySetInnerHTML={sanitize(element)}></p>
|
||||||
allowedTags: ['b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li'],
|
|
||||||
allowedAttributes: {
|
|
||||||
'a': ['href', 'target']
|
|
||||||
},
|
|
||||||
})}></p>
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -50,7 +38,7 @@ const PageHelp: React.FC<Props> = ({ leaving, onLeave }) => {
|
|||||||
<ul>
|
<ul>
|
||||||
{quadrants.map(({ name, description }) => (
|
{quadrants.map(({ name, description }) => (
|
||||||
<li key={name}>
|
<li key={name}>
|
||||||
<strong>{name}:</strong> <span dangerouslySetInnerHTML={sanitize(description)}></span>
|
<strong>{name}:</strong> <span dangerouslySetInnerHTML={sanitize(description, {})}></span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -59,7 +47,7 @@ const PageHelp: React.FC<Props> = ({ leaving, onLeave }) => {
|
|||||||
<ul>
|
<ul>
|
||||||
{rings.map(({ name, description }) => (
|
{rings.map(({ name, description }) => (
|
||||||
<li key={name}>
|
<li key={name}>
|
||||||
<strong>{name}:</strong> <span dangerouslySetInnerHTML={sanitize(description)}></span>
|
<strong>{name}:</strong> <span dangerouslySetInnerHTML={sanitize(description, {})}></span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
20
src/sanitize.test.tsx
Normal file
20
src/sanitize.test.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { sanitize } from "./sanitize";
|
||||||
|
|
||||||
|
describe("sanitize", () => {
|
||||||
|
it("should sanitize the string input to HTML output", () => {
|
||||||
|
let res = sanitize('foo');
|
||||||
|
expect(res.__html).toEqual("foo");
|
||||||
|
res = sanitize('<a href="https://example.org">Example.org</a>');
|
||||||
|
expect(res.__html).toEqual("<a href=\"https://example.org\">Example.org</a>");
|
||||||
|
});
|
||||||
|
it("should not sanitize not allowed tags", () => {
|
||||||
|
let res = sanitize('Before <iframe src="https://example.org"></iframe> After');
|
||||||
|
expect(res.__html).toEqual("Before After");
|
||||||
|
});
|
||||||
|
it("should accept options for rendering", () => {
|
||||||
|
let res = sanitize('<a href="https://example.org" target="_blank">Example.org</a>', { allowedAttributes: { a: ['href']}});
|
||||||
|
expect(res.__html).toEqual("<a href=\"https://example.org\">Example.org</a>");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
15
src/sanitize.ts
Normal file
15
src/sanitize.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import sanitizeHtml from 'sanitize-html';
|
||||||
|
|
||||||
|
const defaultSanitizeOptions = {
|
||||||
|
allowedTags: ['b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li'],
|
||||||
|
allowedAttributes: {
|
||||||
|
'a': ['href', 'target']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sanitize = (dirty: string, options: sanitizeHtml.IOptions = defaultSanitizeOptions) => ({
|
||||||
|
__html: sanitizeHtml(
|
||||||
|
dirty,
|
||||||
|
options
|
||||||
|
)
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user