fix(Markdown): render markdown in more secure way
This commit changes markdown sanitization behaviour in following way: class, style and data-* attributes are removed by default. These attributes open possible vulnerability vectors to attackers. The original behavior of sanitizer (before this commit) can be enabled by *useUnsafeMarkdown* configuration option. Use this configuration option with caution and only in cases when you know what you're doing.
This commit is contained in:
@@ -71,6 +71,10 @@ const standardVariables = {
|
|||||||
type: "boolean",
|
type: "boolean",
|
||||||
name: "showCommonExtensions"
|
name: "showCommonExtensions"
|
||||||
},
|
},
|
||||||
|
USE_UNSAFE_MARKDOWN: {
|
||||||
|
type: "boolean",
|
||||||
|
name: "useUnsafeMarkdown"
|
||||||
|
},
|
||||||
OAUTH2_REDIRECT_URL: {
|
OAUTH2_REDIRECT_URL: {
|
||||||
type: "string",
|
type: "string",
|
||||||
name: "oauth2RedirectUrl"
|
name: "oauth2RedirectUrl"
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ Parameter name | Docker variable | Description
|
|||||||
<a name="showExtensions"></a>`showExtensions` | `SHOW_EXTENSIONS` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema.
|
<a name="showExtensions"></a>`showExtensions` | `SHOW_EXTENSIONS` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema.
|
||||||
<a name="showCommonExtensions"></a>`showCommonExtensions` | `SHOW_COMMON_EXTENSIONS` | `Boolean=false`. Controls the display of extensions (`pattern`, `maxLength`, `minLength`, `maximum`, `minimum`) fields and values for Parameters.
|
<a name="showCommonExtensions"></a>`showCommonExtensions` | `SHOW_COMMON_EXTENSIONS` | `Boolean=false`. Controls the display of extensions (`pattern`, `maxLength`, `minLength`, `maximum`, `minimum`) fields and values for Parameters.
|
||||||
<a name="tagSorter"></a>`tagsSorter` | _Unavailable_ | `Function=(a => a)`. Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger UI.
|
<a name="tagSorter"></a>`tagsSorter` | _Unavailable_ | `Function=(a => a)`. Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger UI.
|
||||||
|
<a name="useUnsafeMarkdown"></a>`useUnsafeMarkdown` | `USE_UNSAFE_MARKDOWN` | `Boolean=false`. When enabled, sanitizer will leave `style`, `class` and `data-*` attributes untouched on all HTML Elements declared inside markdown strings. This parameter is **Deprecated** and will be removed in `4.0.0`.
|
||||||
<a name="onComplete"></a>`onComplete` | _Unavailable_ | `Function=NOOP`. Provides a mechanism to be notified when Swagger UI has finished rendering a newly provided definition.
|
<a name="onComplete"></a>`onComplete` | _Unavailable_ | `Function=NOOP`. Provides a mechanism to be notified when Swagger UI has finished rendering a newly provided definition.
|
||||||
|
|
||||||
##### Network
|
##### Network
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export default class ArrayModel extends Component {
|
|||||||
let title = schema.get("title") || displayName || name
|
let title = schema.get("title") || displayName || name
|
||||||
let properties = schema.filter( ( v, key) => ["type", "items", "description", "$$ref"].indexOf(key) === -1 )
|
let properties = schema.filter( ( v, key) => ["type", "items", "description", "$$ref"].indexOf(key) === -1 )
|
||||||
|
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown", true)
|
||||||
const ModelCollapse = getComponent("ModelCollapse")
|
const ModelCollapse = getComponent("ModelCollapse")
|
||||||
const Model = getComponent("Model")
|
const Model = getComponent("Model")
|
||||||
const Property = getComponent("Property")
|
const Property = getComponent("Property")
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export default class ApiKeyAuth extends React.Component {
|
|||||||
const Row = getComponent("Row")
|
const Row = getComponent("Row")
|
||||||
const Col = getComponent("Col")
|
const Col = getComponent("Col")
|
||||||
const AuthError = getComponent("authError")
|
const AuthError = getComponent("authError")
|
||||||
const Markdown = getComponent( "Markdown" )
|
const Markdown = getComponent("Markdown", true)
|
||||||
const JumpToPath = getComponent("JumpToPath", true)
|
const JumpToPath = getComponent("JumpToPath", true)
|
||||||
let value = this.getValue()
|
let value = this.getValue()
|
||||||
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
|
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export default class BasicAuth extends React.Component {
|
|||||||
const Col = getComponent("Col")
|
const Col = getComponent("Col")
|
||||||
const AuthError = getComponent("authError")
|
const AuthError = getComponent("authError")
|
||||||
const JumpToPath = getComponent("JumpToPath", true)
|
const JumpToPath = getComponent("JumpToPath", true)
|
||||||
const Markdown = getComponent( "Markdown" )
|
const Markdown = getComponent("Markdown", true)
|
||||||
let username = this.getValue().username
|
let username = this.getValue().username
|
||||||
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
|
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export default class Oauth2 extends React.Component {
|
|||||||
const Button = getComponent("Button")
|
const Button = getComponent("Button")
|
||||||
const AuthError = getComponent("authError")
|
const AuthError = getComponent("authError")
|
||||||
const JumpToPath = getComponent("JumpToPath", true)
|
const JumpToPath = getComponent("JumpToPath", true)
|
||||||
const Markdown = getComponent( "Markdown" )
|
const Markdown = getComponent("Markdown", true)
|
||||||
const InitializedInput = getComponent("InitializedInput")
|
const InitializedInput = getComponent("InitializedInput")
|
||||||
|
|
||||||
const { isOAS3 } = specSelectors
|
const { isOAS3 } = specSelectors
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { stringify } from "core/utils"
|
|||||||
export default function Example(props) {
|
export default function Example(props) {
|
||||||
const { example, showValue, getComponent } = props
|
const { example, showValue, getComponent } = props
|
||||||
|
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown", true)
|
||||||
const HighlightCode = getComponent("highlightCode")
|
const HighlightCode = getComponent("highlightCode")
|
||||||
|
|
||||||
if(!example) return null
|
if(!example) return null
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default class Headers extends React.Component {
|
|||||||
let { headers, getComponent } = this.props
|
let { headers, getComponent } = this.props
|
||||||
|
|
||||||
const Property = getComponent("Property")
|
const Property = getComponent("Property")
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown", true)
|
||||||
|
|
||||||
if ( !headers || !headers.size )
|
if ( !headers || !headers.size )
|
||||||
return null
|
return null
|
||||||
@@ -36,7 +36,7 @@ export default class Headers extends React.Component {
|
|||||||
if(!Im.Map.isMap(header)) {
|
if(!Im.Map.isMap(header)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const description = header.get("description")
|
const description = header.get("description")
|
||||||
const type = header.getIn(["schema"]) ? header.getIn(["schema", "type"]) : header.getIn(["type"])
|
const type = header.getIn(["schema"]) ? header.getIn(["schema", "type"]) : header.getIn(["type"])
|
||||||
const schemaExample = header.getIn(["schema", "example"])
|
const schemaExample = header.getIn(["schema", "example"])
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class License extends React.Component {
|
|||||||
let { license, getComponent } = this.props
|
let { license, getComponent } = this.props
|
||||||
|
|
||||||
const Link = getComponent("Link")
|
const Link = getComponent("Link")
|
||||||
|
|
||||||
let name = license.get("name") || "License"
|
let name = license.get("name") || "License"
|
||||||
let url = license.get("url")
|
let url = license.get("url")
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ export class InfoUrl extends React.PureComponent {
|
|||||||
getComponent: PropTypes.func.isRequired
|
getComponent: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { url, getComponent } = this.props
|
const { url, getComponent } = this.props
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ export default class Info extends React.Component {
|
|||||||
let license = info.get("license")
|
let license = info.get("license")
|
||||||
const { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS()
|
const { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS()
|
||||||
|
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown", true)
|
||||||
const Link = getComponent("Link")
|
const Link = getComponent("Link")
|
||||||
const VersionStamp = getComponent("VersionStamp")
|
const VersionStamp = getComponent("VersionStamp")
|
||||||
const InfoUrl = getComponent("InfoUrl")
|
const InfoUrl = getComponent("InfoUrl")
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default class ObjectModel extends Component {
|
|||||||
let requiredProperties = schema.get("required")
|
let requiredProperties = schema.get("required")
|
||||||
|
|
||||||
const JumpToPath = getComponent("JumpToPath", true)
|
const JumpToPath = getComponent("JumpToPath", true)
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown", true)
|
||||||
const Model = getComponent("Model")
|
const Model = getComponent("Model")
|
||||||
const ModelCollapse = getComponent("ModelCollapse")
|
const ModelCollapse = getComponent("ModelCollapse")
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export default class OperationTag extends React.Component {
|
|||||||
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||||
|
|
||||||
const Collapse = getComponent("Collapse")
|
const Collapse = getComponent("Collapse")
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown", true)
|
||||||
const DeepLink = getComponent("DeepLink")
|
const DeepLink = getComponent("DeepLink")
|
||||||
const Link = getComponent("Link")
|
const Link = getComponent("Link")
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export default class Operation extends PureComponent {
|
|||||||
const Execute = getComponent( "execute" )
|
const Execute = getComponent( "execute" )
|
||||||
const Clear = getComponent( "clear" )
|
const Clear = getComponent( "clear" )
|
||||||
const Collapse = getComponent( "Collapse" )
|
const Collapse = getComponent( "Collapse" )
|
||||||
const Markdown = getComponent( "Markdown" )
|
const Markdown = getComponent("Markdown", true)
|
||||||
const Schemes = getComponent( "schemes" )
|
const Schemes = getComponent( "schemes" )
|
||||||
const OperationServers = getComponent( "OperationServers" )
|
const OperationServers = getComponent( "OperationServers" )
|
||||||
const OperationExt = getComponent( "OperationExt" )
|
const OperationExt = getComponent( "OperationExt" )
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default class ParameterRow extends Component {
|
|||||||
.get("content", Map())
|
.get("content", Map())
|
||||||
.keySeq()
|
.keySeq()
|
||||||
.first()
|
.first()
|
||||||
|
|
||||||
// getSampleSchema could return null
|
// getSampleSchema could return null
|
||||||
const generatedSampleValue = schema ? getSampleSchema(schema.toJS(), parameterMediaType, {
|
const generatedSampleValue = schema ? getSampleSchema(schema.toJS(), parameterMediaType, {
|
||||||
includeWriteOnly: true
|
includeWriteOnly: true
|
||||||
@@ -144,7 +144,7 @@ export default class ParameterRow extends Component {
|
|||||||
this.onChangeWrapper(initialValue)
|
this.onChangeWrapper(initialValue)
|
||||||
} else if(
|
} else if(
|
||||||
schema && schema.get("type") === "object"
|
schema && schema.get("type") === "object"
|
||||||
&& generatedSampleValue
|
&& generatedSampleValue
|
||||||
&& !paramWithMeta.get("examples")
|
&& !paramWithMeta.get("examples")
|
||||||
) {
|
) {
|
||||||
// Object parameters get special treatment.. if the user doesn't set any
|
// Object parameters get special treatment.. if the user doesn't set any
|
||||||
@@ -202,7 +202,7 @@ export default class ParameterRow extends Component {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
const ModelExample = getComponent("modelExample")
|
const ModelExample = getComponent("modelExample")
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown", true)
|
||||||
const ParameterExt = getComponent("ParameterExt")
|
const ParameterExt = getComponent("ParameterExt")
|
||||||
const ParameterIncludeEmpty = getComponent("ParameterIncludeEmpty")
|
const ParameterIncludeEmpty = getComponent("ParameterIncludeEmpty")
|
||||||
const ExamplesSelectValueRetainer = getComponent("ExamplesSelectValueRetainer")
|
const ExamplesSelectValueRetainer = getComponent("ExamplesSelectValueRetainer")
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default class Primitive extends Component {
|
|||||||
let properties = schema
|
let properties = schema
|
||||||
.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
|
.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
|
||||||
.filterNot( (v, key) => extensions.has(key) )
|
.filterNot( (v, key) => extensions.has(key) )
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown", true)
|
||||||
const EnumModel = getComponent("EnumModel")
|
const EnumModel = getComponent("EnumModel")
|
||||||
const Property = getComponent("Property")
|
const Property = getComponent("Property")
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ DomPurify.addHook("beforeSanitizeElements", function (current, ) {
|
|||||||
return current
|
return current
|
||||||
})
|
})
|
||||||
|
|
||||||
function Markdown({ source, className = "" }) {
|
function Markdown({ source, className = "", getConfigs }) {
|
||||||
if (typeof source !== "string") {
|
if (typeof source !== "string") {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -30,8 +30,9 @@ function Markdown({ source, className = "" }) {
|
|||||||
|
|
||||||
md.core.ruler.disable(["replacements", "smartquotes"])
|
md.core.ruler.disable(["replacements", "smartquotes"])
|
||||||
|
|
||||||
|
const { useUnsafeMarkdown } = getConfigs()
|
||||||
const html = md.render(source)
|
const html = md.render(source)
|
||||||
const sanitized = sanitizer(html)
|
const sanitized = sanitizer(html, { useUnsafeMarkdown })
|
||||||
|
|
||||||
if (!source || !html || !sanitized) {
|
if (!source || !html || !sanitized) {
|
||||||
return null
|
return null
|
||||||
@@ -44,14 +45,30 @@ function Markdown({ source, className = "" }) {
|
|||||||
|
|
||||||
Markdown.propTypes = {
|
Markdown.propTypes = {
|
||||||
source: PropTypes.string.isRequired,
|
source: PropTypes.string.isRequired,
|
||||||
className: PropTypes.string
|
className: PropTypes.string,
|
||||||
|
getConfigs: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
Markdown.defaultProps = {
|
||||||
|
getConfigs: () => ({ useUnsafeMarkdown: false }),
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Markdown
|
export default Markdown
|
||||||
|
|
||||||
export function sanitizer(str) {
|
export function sanitizer(str, { useUnsafeMarkdown = false } = {}) {
|
||||||
|
const ALLOW_DATA_ATTR = useUnsafeMarkdown
|
||||||
|
const FORBID_ATTR = useUnsafeMarkdown ? [] : ["style", "class"]
|
||||||
|
|
||||||
|
if (useUnsafeMarkdown && !sanitizer.hasWarnedAboutDeprecation) {
|
||||||
|
console.warn(`useUnsafeMarkdown display configuration parameter is deprecated since >3.26.0 and will be removed in v4.0.0.`)
|
||||||
|
sanitizer.hasWarnedAboutDeprecation = true
|
||||||
|
}
|
||||||
|
|
||||||
return DomPurify.sanitize(str, {
|
return DomPurify.sanitize(str, {
|
||||||
ADD_ATTR: ["target"],
|
ADD_ATTR: ["target"],
|
||||||
FORBID_TAGS: ["style"],
|
FORBID_TAGS: ["style"],
|
||||||
|
ALLOW_DATA_ATTR,
|
||||||
|
FORBID_ATTR,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
sanitizer.hasWarnedAboutDeprecation = false
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export default class Response extends React.Component {
|
|||||||
const Headers = getComponent("headers")
|
const Headers = getComponent("headers")
|
||||||
const HighlightCode = getComponent("highlightCode")
|
const HighlightCode = getComponent("highlightCode")
|
||||||
const ModelExample = getComponent("modelExample")
|
const ModelExample = getComponent("modelExample")
|
||||||
const Markdown = getComponent( "Markdown" )
|
const Markdown = getComponent("Markdown", true)
|
||||||
const OperationLink = getComponent("operationLink")
|
const OperationLink = getComponent("operationLink")
|
||||||
const ContentType = getComponent("contentType")
|
const ContentType = getComponent("contentType")
|
||||||
const ExamplesSelect = getComponent("ExamplesSelect")
|
const ExamplesSelect = getComponent("ExamplesSelect")
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { fromJS } from "immutable"
|
|||||||
|
|
||||||
const Callbacks = (props) => {
|
const Callbacks = (props) => {
|
||||||
let { callbacks, getComponent, specPath } = props
|
let { callbacks, getComponent, specPath } = props
|
||||||
// const Markdown = getComponent("Markdown")
|
// const Markdown = getComponent("Markdown", true)
|
||||||
const OperationContainer = getComponent("OperationContainer", true)
|
const OperationContainer = getComponent("OperationContainer", true)
|
||||||
|
|
||||||
if(!callbacks) {
|
if(!callbacks) {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export default class HttpAuth extends React.Component {
|
|||||||
const Row = getComponent("Row")
|
const Row = getComponent("Row")
|
||||||
const Col = getComponent("Col")
|
const Col = getComponent("Col")
|
||||||
const AuthError = getComponent("authError")
|
const AuthError = getComponent("authError")
|
||||||
const Markdown = getComponent( "Markdown" )
|
const Markdown = getComponent("Markdown", true)
|
||||||
const JumpToPath = getComponent("JumpToPath", true)
|
const JumpToPath = getComponent("JumpToPath", true)
|
||||||
|
|
||||||
const scheme = (schema.get("scheme") || "").toLowerCase()
|
const scheme = (schema.get("scheme") || "").toLowerCase()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class OperationLink extends Component {
|
|||||||
render() {
|
render() {
|
||||||
const { link, name, getComponent } = this.props
|
const { link, name, getComponent } = this.props
|
||||||
|
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown", true)
|
||||||
|
|
||||||
let targetOp = link.get("operationId") || link.get("operationRef")
|
let targetOp = link.get("operationId") || link.get("operationRef")
|
||||||
let parameters = link.get("parameters") && link.get("parameters").toJS()
|
let parameters = link.get("parameters") && link.get("parameters").toJS()
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const RequestBody = ({
|
|||||||
onChange(e.target.files[0])
|
onChange(e.target.files[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown", true)
|
||||||
const ModelExample = getComponent("modelExample")
|
const ModelExample = getComponent("modelExample")
|
||||||
const RequestBodyEditor = getComponent("RequestBodyEditor")
|
const RequestBodyEditor = getComponent("RequestBodyEditor")
|
||||||
const HighlightCode = getComponent("highlightCode")
|
const HighlightCode = getComponent("highlightCode")
|
||||||
|
|||||||
@@ -9,14 +9,15 @@ const parser = new Remarkable("commonmark")
|
|||||||
parser.block.ruler.enable(["table"])
|
parser.block.ruler.enable(["table"])
|
||||||
parser.set({ linkTarget: "_blank" })
|
parser.set({ linkTarget: "_blank" })
|
||||||
|
|
||||||
export const Markdown = ({ source, className = "" }) => {
|
export const Markdown = ({ source, className = "", getConfigs }) => {
|
||||||
if(typeof source !== "string") {
|
if(typeof source !== "string") {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( source ) {
|
if ( source ) {
|
||||||
|
const { useUnsafeMarkdown } = getConfigs()
|
||||||
const html = parser.render(source)
|
const html = parser.render(source)
|
||||||
const sanitized = sanitizer(html)
|
const sanitized = sanitizer(html, { useUnsafeMarkdown })
|
||||||
|
|
||||||
let trimmed
|
let trimmed
|
||||||
|
|
||||||
@@ -38,6 +39,11 @@ export const Markdown = ({ source, className = "" }) => {
|
|||||||
Markdown.propTypes = {
|
Markdown.propTypes = {
|
||||||
source: PropTypes.string,
|
source: PropTypes.string,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
getConfigs: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
Markdown.defaultProps = {
|
||||||
|
getConfigs: () => ({ useUnsafeMarkdown: false }),
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OAS3ComponentWrapFactory(Markdown)
|
export default OAS3ComponentWrapFactory(Markdown)
|
||||||
|
|||||||
@@ -7,10 +7,18 @@ import { Markdown as OAS3Markdown } from "corePlugins/oas3/wrap-components/markd
|
|||||||
|
|
||||||
describe("Markdown component", function() {
|
describe("Markdown component", function() {
|
||||||
describe("Swagger 2.0", function() {
|
describe("Swagger 2.0", function() {
|
||||||
it("allows span elements with class attrib", function() {
|
it("allows elements with class, style and data-* attribs", function() {
|
||||||
const str = `<span class="method">ONE</span>`
|
const getConfigs = () => ({ useUnsafeMarkdown: true })
|
||||||
const el = render(<Markdown source={str} />)
|
const str = `<span class="method" style="border-width: 1px" data-attr="value">ONE</span>`
|
||||||
expect(el.html()).toEqual(`<div class="markdown"><p><span class="method">ONE</span></p>\n</div>`)
|
const el = render(<Markdown source={str} getConfigs={getConfigs} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="markdown"><p><span data-attr="value" style="border-width: 1px" class="method">ONE</span></p>\n</div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("strips class, style and data-* attribs from elements", function() {
|
||||||
|
const getConfigs = () => ({ useUnsafeMarkdown: false })
|
||||||
|
const str = `<span class="method" style="border-width: 1px" data-attr="value">ONE</span>`
|
||||||
|
const el = render(<Markdown source={str} getConfigs={getConfigs} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="markdown"><p><span>ONE</span></p>\n</div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("allows td elements with colspan attrib", function() {
|
it("allows td elements with colspan attrib", function() {
|
||||||
@@ -57,6 +65,20 @@ describe("Markdown component", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("OAS 3", function() {
|
describe("OAS 3", function() {
|
||||||
|
it("allows elements with class, style and data-* attribs", function() {
|
||||||
|
const getConfigs = () => ({ useUnsafeMarkdown: true })
|
||||||
|
const str = `<span class="method" style="border-width: 1px" data-attr="value">ONE</span>`
|
||||||
|
const el = render(<OAS3Markdown source={str} getConfigs={getConfigs} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="renderedMarkdown"><p><span data-attr="value" style="border-width: 1px" class="method">ONE</span></p></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("strips class, style and data-* attribs from elements", function() {
|
||||||
|
const getConfigs = () => ({ useUnsafeMarkdown: false })
|
||||||
|
const str = `<span class="method" style="border-width: 1px" data-attr="value">ONE</span>`
|
||||||
|
const el = render(<OAS3Markdown source={str} getConfigs={getConfigs} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="renderedMarkdown"><p><span>ONE</span></p></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
it("allows image elements", function() {
|
it("allows image elements", function() {
|
||||||
const str = ``
|
const str = ``
|
||||||
const el = render(<OAS3Markdown source={str} />)
|
const el = render(<OAS3Markdown source={str} />)
|
||||||
|
|||||||
Reference in New Issue
Block a user