Skip to content

Commit a616cb4

Browse files
committed
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.
1 parent 48a0b46 commit a616cb4

File tree

22 files changed

+83
-33
lines changed

22 files changed

+83
-33
lines changed

docker/configurator/variables.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ const standardVariables = {
7171
type: "boolean",
7272
name: "showCommonExtensions"
7373
},
74+
USE_UNSAFE_MARKDOWN: {
75+
type: "boolean",
76+
name: "useUnsafeMarkdown"
77+
},
7478
OAUTH2_REDIRECT_URL: {
7579
type: "string",
7680
name: "oauth2RedirectUrl"

docs/usage/configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Parameter name | Docker variable | Description
5959
<a name="showExtensions"></a>`showExtensions` | `SHOW_EXTENSIONS` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema.
6060
<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.
6161
<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.
62+
<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`.
6263
<a name="onComplete"></a>`onComplete` | _Unavailable_ | `Function=NOOP`. Provides a mechanism to be notified when Swagger UI has finished rendering a newly provided definition.
6364

6465
##### Network

src/core/components/array-model.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default class ArrayModel extends Component {
2525
let title = schema.get("title") || displayName || name
2626
let properties = schema.filter( ( v, key) => ["type", "items", "description", "$$ref"].indexOf(key) === -1 )
2727

28-
const Markdown = getComponent("Markdown")
28+
const Markdown = getComponent("Markdown", true)
2929
const ModelCollapse = getComponent("ModelCollapse")
3030
const Model = getComponent("Model")
3131
const Property = getComponent("Property")

src/core/components/auth/api-key-auth.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default class ApiKeyAuth extends React.Component {
4444
const Row = getComponent("Row")
4545
const Col = getComponent("Col")
4646
const AuthError = getComponent("authError")
47-
const Markdown = getComponent( "Markdown" )
47+
const Markdown = getComponent("Markdown", true)
4848
const JumpToPath = getComponent("JumpToPath", true)
4949
let value = this.getValue()
5050
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)

src/core/components/auth/basic-auth.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export default class BasicAuth extends React.Component {
5151
const Col = getComponent("Col")
5252
const AuthError = getComponent("authError")
5353
const JumpToPath = getComponent("JumpToPath", true)
54-
const Markdown = getComponent( "Markdown" )
54+
const Markdown = getComponent("Markdown", true)
5555
let username = this.getValue().username
5656
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
5757

src/core/components/auth/oauth2.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export default class Oauth2 extends React.Component {
109109
const Button = getComponent("Button")
110110
const AuthError = getComponent("authError")
111111
const JumpToPath = getComponent("JumpToPath", true)
112-
const Markdown = getComponent( "Markdown" )
112+
const Markdown = getComponent("Markdown", true)
113113
const InitializedInput = getComponent("InitializedInput")
114114

115115
const { isOAS3 } = specSelectors

src/core/components/example.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { stringify } from "core/utils"
1010
export default function Example(props) {
1111
const { example, showValue, getComponent } = props
1212

13-
const Markdown = getComponent("Markdown")
13+
const Markdown = getComponent("Markdown", true)
1414
const HighlightCode = getComponent("highlightCode")
1515

1616
if(!example) return null

src/core/components/headers.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export default class Headers extends React.Component {
1414
let { headers, getComponent } = this.props
1515

1616
const Property = getComponent("Property")
17-
const Markdown = getComponent("Markdown")
17+
const Markdown = getComponent("Markdown", true)
1818

1919
if ( !headers || !headers.size )
2020
return null
@@ -36,7 +36,7 @@ export default class Headers extends React.Component {
3636
if(!Im.Map.isMap(header)) {
3737
return null
3838
}
39-
39+
4040
const description = header.get("description")
4141
const type = header.getIn(["schema"]) ? header.getIn(["schema", "type"]) : header.getIn(["type"])
4242
const schemaExample = header.getIn(["schema", "example"])

src/core/components/info.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class License extends React.Component {
6161
let { license, getComponent } = this.props
6262

6363
const Link = getComponent("Link")
64-
64+
6565
let name = license.get("name") || "License"
6666
let url = license.get("url")
6767

@@ -82,7 +82,7 @@ export class InfoUrl extends React.PureComponent {
8282
getComponent: PropTypes.func.isRequired
8383
}
8484

85-
85+
8686
render() {
8787
const { url, getComponent } = this.props
8888

@@ -112,7 +112,7 @@ export default class Info extends React.Component {
112112
let license = info.get("license")
113113
const { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS()
114114

115-
const Markdown = getComponent("Markdown")
115+
const Markdown = getComponent("Markdown", true)
116116
const Link = getComponent("Link")
117117
const VersionStamp = getComponent("VersionStamp")
118118
const InfoUrl = getComponent("InfoUrl")

src/core/components/object-model.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export default class ObjectModel extends Component {
4040
let requiredProperties = schema.get("required")
4141

4242
const JumpToPath = getComponent("JumpToPath", true)
43-
const Markdown = getComponent("Markdown")
43+
const Markdown = getComponent("Markdown", true)
4444
const Model = getComponent("Model")
4545
const ModelCollapse = getComponent("ModelCollapse")
4646

0 commit comments

Comments
 (0)