Skip to content

Commit afa615e

Browse files
committed
Add URL sanitizer to avoid javascript: XSS attack vector
1 parent 4eae9b6 commit afa615e

File tree

6 files changed

+22
-12
lines changed

6 files changed

+22
-12
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"e2e": "npm-run-all --parallel -r hot-server mock-api test-e2e"
4040
},
4141
"dependencies": {
42+
"@braintree/sanitize-url": "^2.0.2",
4243
"base64-js": "^1.2.0",
4344
"brace": "0.7.0",
4445
"classnames": "^2.2.5",

src/core/components/info.jsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from "react"
22
import PropTypes from "prop-types"
33
import { fromJS } from "immutable"
44
import ImPropTypes from "react-immutable-proptypes"
5+
import { sanitizeUrl } from "core/utils"
56

67

78
class Path extends React.Component {
@@ -35,9 +36,9 @@ class Contact extends React.Component {
3536

3637
return (
3738
<div>
38-
{ url && <div><a href={ url } target="_blank">{ name } - Website</a></div> }
39+
{ url && <div><a href={ sanitizeUrl(url) } target="_blank">{ name } - Website</a></div> }
3940
{ email &&
40-
<a href={`mailto:${email}`}>
41+
<a href={sanitizeUrl(`mailto:${email}`)}>
4142
{ url ? `Send email to ${name}` : `Contact ${name}`}
4243
</a>
4344
}
@@ -59,7 +60,7 @@ class License extends React.Component {
5960
return (
6061
<div>
6162
{
62-
url ? <a target="_blank" href={ url }>{ name }</a>
63+
url ? <a target="_blank" href={ sanitizeUrl(url) }>{ name }</a>
6364
: <span>{ name }</span>
6465
}
6566
</div>
@@ -97,7 +98,7 @@ export default class Info extends React.Component {
9798
{ version && <VersionStamp version={version}></VersionStamp> }
9899
</h2>
99100
{ host || basePath ? <Path host={ host } basePath={ basePath } /> : null }
100-
{ url && <a target="_blank" href={ url }><span className="url"> { url } </span></a> }
101+
{ url && <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a> }
101102
</hgroup>
102103

103104
<div className="description">
@@ -106,14 +107,14 @@ export default class Info extends React.Component {
106107

107108
{
108109
termsOfService && <div>
109-
<a target="_blank" href={ termsOfService }>Terms of service</a>
110+
<a target="_blank" href={ sanitizeUrl(termsOfService) }>Terms of service</a>
110111
</div>
111112
}
112113

113114
{ contact && contact.size ? <Contact data={ contact } /> : null }
114115
{ license && license.size ? <License license={ license } /> : null }
115116
{ externalDocsUrl ?
116-
<a target="_blank" href={externalDocsUrl}>{externalDocsDescription || externalDocsUrl}</a>
117+
<a target="_blank" href={sanitizeUrl(externalDocsUrl)}>{externalDocsDescription || externalDocsUrl}</a>
117118
: null }
118119

119120
</div>

src/core/components/online-validator-badge.jsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from "react"
22
import PropTypes from "prop-types"
3+
import { sanitizeUrl } from "core/utils"
34

45
export default class OnlineValidatorBadge extends React.Component {
56
static propTypes = {
@@ -32,6 +33,8 @@ export default class OnlineValidatorBadge extends React.Component {
3233
let { getConfigs } = this.props
3334
let { spec } = getConfigs()
3435

36+
let sanitizedValidatorUrl = sanitizeUrl(this.state.validatorUrl)
37+
3538
if ( typeof spec === "object" && Object.keys(spec).length) return null
3639

3740
if (!this.state.url || !this.state.validatorUrl || this.state.url.indexOf("localhost") >= 0
@@ -40,8 +43,8 @@ export default class OnlineValidatorBadge extends React.Component {
4043
}
4144

4245
return (<span style={{ float: "right"}}>
43-
<a target="_blank" href={`${ this.state.validatorUrl }/debug?url=${ this.state.url }`}>
44-
<ValidatorImage src={`${ this.state.validatorUrl }?url=${ this.state.url }`} alt="Online validator badge"/>
46+
<a target="_blank" href={`${ sanitizedValidatorUrl }/debug?url=${ this.state.url }`}>
47+
<ValidatorImage src={`${ sanitizedValidatorUrl }?url=${ this.state.url }`} alt="Online validator badge"/>
4548
</a>
4649
</span>)
4750
}

src/core/components/operation.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { PureComponent } from "react"
22
import PropTypes from "prop-types"
33
import { getList } from "core/utils"
44
import * as CustomPropTypes from "core/proptypes"
5+
import { sanitizeUrl } from "core/utils"
56

67
//import "less/opblock"
78

@@ -206,7 +207,7 @@ export default class Operation extends PureComponent {
206207
<span className="opblock-external-docs__description">
207208
<Markdown source={ externalDocs.get("description") } />
208209
</span>
209-
<a className="opblock-external-docs__link" href={ externalDocs.get("url") }>{ externalDocs.get("url") }</a>
210+
<a className="opblock-external-docs__link" href={ sanitizeUrl(externalDocs.get("url")) }>{ externalDocs.get("url") }</a>
210211
</div>
211212
</div> : null
212213
}

src/core/components/operations.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react"
22
import PropTypes from "prop-types"
33
import { helpers } from "swagger-client"
4-
import { createDeepLinkPath } from "core/utils"
4+
import { createDeepLinkPath, sanitizeUrl } from "core/utils"
55
const { opId } = helpers
66

77
export default class Operations extends React.Component {
@@ -101,7 +101,7 @@ export default class Operations extends React.Component {
101101
{ tagExternalDocsUrl ? ": " : null }
102102
{ tagExternalDocsUrl ?
103103
<a
104-
href={tagExternalDocsUrl}
104+
href={sanitizeUrl(tagExternalDocsUrl)}
105105
onClick={(e) => e.stopPropagation()}
106106
target={"_blank"}
107107
>{tagExternalDocsUrl}</a> : null

src/core/utils.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Im from "immutable"
2-
2+
import { sanitizeUrl as braintreeSanitizeUrl } from "@braintree/sanitize-url"
33
import camelCase from "lodash/camelCase"
44
import upperFirst from "lodash/upperFirst"
55
import _memoize from "lodash/memoize"
@@ -722,6 +722,10 @@ export const shallowEqualKeys = (a,b, keys) => {
722722
})
723723
}
724724

725+
export function sanitizeUrl(url) {
726+
return braintreeSanitizeUrl(url)
727+
}
728+
725729
export function getAcceptControllingResponse(responses) {
726730
if(!Im.OrderedMap.isOrderedMap(responses)) {
727731
// wrong type!

0 commit comments

Comments
 (0)