From afa615e01dc7f6724d20a11abfe1fcdf8f6ecd57 Mon Sep 17 00:00:00 2001 From: Kyle Shockey Date: Mon, 30 Oct 2017 17:43:23 -0700 Subject: [PATCH] Add URL sanitizer to avoid `javascript:` XSS attack vector --- package.json | 1 + src/core/components/info.jsx | 13 +++++++------ src/core/components/online-validator-badge.jsx | 7 +++++-- src/core/components/operation.jsx | 3 ++- src/core/components/operations.jsx | 4 ++-- src/core/utils.js | 6 +++++- 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 19974910..1919fc6f 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "e2e": "npm-run-all --parallel -r hot-server mock-api test-e2e" }, "dependencies": { + "@braintree/sanitize-url": "^2.0.2", "base64-js": "^1.2.0", "brace": "0.7.0", "classnames": "^2.2.5", diff --git a/src/core/components/info.jsx b/src/core/components/info.jsx index 7036de3e..0d83a792 100644 --- a/src/core/components/info.jsx +++ b/src/core/components/info.jsx @@ -2,6 +2,7 @@ import React from "react" import PropTypes from "prop-types" import { fromJS } from "immutable" import ImPropTypes from "react-immutable-proptypes" +import { sanitizeUrl } from "core/utils" class Path extends React.Component { @@ -35,9 +36,9 @@ class Contact extends React.Component { return (
- { url &&
{ name } - Website
} + { url &&
{ name } - Website
} { email && - + { url ? `Send email to ${name}` : `Contact ${name}`} } @@ -59,7 +60,7 @@ class License extends React.Component { return (
{ - url ? { name } + url ? { name } : { name } }
@@ -97,7 +98,7 @@ export default class Info extends React.Component { { version && } { host || basePath ? : null } - { url && { url } } + { url && { url } }
@@ -106,14 +107,14 @@ export default class Info extends React.Component { { termsOfService &&
- Terms of service + Terms of service
} { contact && contact.size ? : null } { license && license.size ? : null } { externalDocsUrl ? - {externalDocsDescription || externalDocsUrl} + {externalDocsDescription || externalDocsUrl} : null }
diff --git a/src/core/components/online-validator-badge.jsx b/src/core/components/online-validator-badge.jsx index 466d4c97..36fab756 100644 --- a/src/core/components/online-validator-badge.jsx +++ b/src/core/components/online-validator-badge.jsx @@ -1,5 +1,6 @@ import React from "react" import PropTypes from "prop-types" +import { sanitizeUrl } from "core/utils" export default class OnlineValidatorBadge extends React.Component { static propTypes = { @@ -32,6 +33,8 @@ export default class OnlineValidatorBadge extends React.Component { let { getConfigs } = this.props let { spec } = getConfigs() + let sanitizedValidatorUrl = sanitizeUrl(this.state.validatorUrl) + if ( typeof spec === "object" && Object.keys(spec).length) return null if (!this.state.url || !this.state.validatorUrl || this.state.url.indexOf("localhost") >= 0 @@ -40,8 +43,8 @@ export default class OnlineValidatorBadge extends React.Component { } return ( - - + + ) } diff --git a/src/core/components/operation.jsx b/src/core/components/operation.jsx index 3bf0f9b7..c3210ddf 100644 --- a/src/core/components/operation.jsx +++ b/src/core/components/operation.jsx @@ -2,6 +2,7 @@ import React, { PureComponent } from "react" import PropTypes from "prop-types" import { getList } from "core/utils" import * as CustomPropTypes from "core/proptypes" +import { sanitizeUrl } from "core/utils" //import "less/opblock" @@ -206,7 +207,7 @@ export default class Operation extends PureComponent { - { externalDocs.get("url") } + { externalDocs.get("url") }
: null } diff --git a/src/core/components/operations.jsx b/src/core/components/operations.jsx index 8e282a60..6d760d53 100644 --- a/src/core/components/operations.jsx +++ b/src/core/components/operations.jsx @@ -1,7 +1,7 @@ import React from "react" import PropTypes from "prop-types" import { helpers } from "swagger-client" -import { createDeepLinkPath } from "core/utils" +import { createDeepLinkPath, sanitizeUrl } from "core/utils" const { opId } = helpers export default class Operations extends React.Component { @@ -101,7 +101,7 @@ export default class Operations extends React.Component { { tagExternalDocsUrl ? ": " : null } { tagExternalDocsUrl ? e.stopPropagation()} target={"_blank"} >{tagExternalDocsUrl} : null diff --git a/src/core/utils.js b/src/core/utils.js index 0f247b52..de572105 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -1,5 +1,5 @@ import Im from "immutable" - +import { sanitizeUrl as braintreeSanitizeUrl } from "@braintree/sanitize-url" import camelCase from "lodash/camelCase" import upperFirst from "lodash/upperFirst" import _memoize from "lodash/memoize" @@ -722,6 +722,10 @@ export const shallowEqualKeys = (a,b, keys) => { }) } +export function sanitizeUrl(url) { + return braintreeSanitizeUrl(url) +} + export function getAcceptControllingResponse(responses) { if(!Im.OrderedMap.isOrderedMap(responses)) { // wrong type!