diff --git a/package.json b/package.json index 14456728..18229513 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "test": "npm run lint-errors && npm run just-test-in-node", "test-in-node": "npm run lint-errors && npm run just-test-in-node", "just-test": "karma start --config karma.conf.js", - "just-test-in-node": "mocha --recursive --compilers js:babel-core/register test/core test/components test/bugs test/swagger-ui-dist-package", + "just-test-in-node": "mocha --recursive --compilers js:babel-core/register test/core test/components test/bugs test/swagger-ui-dist-package test/xss", "test-e2e": "sleep 3 && nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json", "e2e-initial-render": "nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json --group initial-render", "mock-api": "json-server --watch test/e2e/db.json --port 3204", diff --git a/src/core/components/providers/markdown.jsx b/src/core/components/providers/markdown.jsx index 2b21c10b..2ef8b6a6 100644 --- a/src/core/components/providers/markdown.jsx +++ b/src/core/components/providers/markdown.jsx @@ -29,7 +29,10 @@ Markdown.propTypes = { export default Markdown const sanitizeOptions = { - allowedTags: sanitize.defaults.allowedTags.concat([ "img" ]), + allowedTags: sanitize.defaults.allowedTags.concat([ "h1", "h2", "img" ]), + allowedAttributes: { + "img": sanitize.defaults.allowedAttributes.img.concat(["title"]) + }, textFilter: function(text) { return text.replace(/"/g, "\"") } diff --git a/src/core/components/response-body.jsx b/src/core/components/response-body.jsx index 265ff679..c55c1d6d 100644 --- a/src/core/components/response-body.jsx +++ b/src/core/components/response-body.jsx @@ -83,8 +83,12 @@ export default class ResponseBody extends React.Component { // Anything else (CORS) } else if (typeof content === "string") { bodyEl = - } else { + } else if ( content.size > 0 ) { + // We don't know the contentType, but there was some content returned bodyEl =
Unknown response type
+ } else { + // We don't know the contentType and there was no content returned + bodyEl = null } return ( !bodyEl ? null :
diff --git a/src/core/plugins/download-url.js b/src/core/plugins/download-url.js index c8dff0b8..d80e982b 100644 --- a/src/core/plugins/download-url.js +++ b/src/core/plugins/download-url.js @@ -7,13 +7,16 @@ export default function downloadUrlPlugin (toolbox) { let { fn } = toolbox const actions = { - download: (url)=> ({ errActions, specSelectors, specActions }) => { + download: (url)=> ({ errActions, specSelectors, specActions, getConfigs }) => { let { fetch } = fn + const config = getConfigs() url = url || specSelectors.url() specActions.updateLoadingStatus("loading") fetch({ url, loadSpec: true, + requestInterceptor: config.requestInterceptor || (a => a), + responseInterceptor: config.responseInterceptor || (a => a), credentials: "same-origin", headers: { "Accept": "application/json,*/*" diff --git a/src/core/plugins/oas3/wrap-components/markdown.js b/src/core/plugins/oas3/wrap-components/markdown.js index 103a2801..2d7f27e7 100644 --- a/src/core/plugins/oas3/wrap-components/markdown.js +++ b/src/core/plugins/oas3/wrap-components/markdown.js @@ -1,10 +1,11 @@ import React from "react" +import PropTypes from "prop-types" import ReactMarkdown from "react-markdown" import { Parser, HtmlRenderer } from "commonmark" import { OAS3ComponentWrapFactory } from "../helpers" import { sanitizer } from "core/components/providers/markdown" -export default OAS3ComponentWrapFactory(({ source }) => { +export const Markdown = ({ source }) => { if ( source ) { const parser = new Parser() const writer = new HtmlRenderer() @@ -23,4 +24,9 @@ export default OAS3ComponentWrapFactory(({ source }) => { ) } return null -}) \ No newline at end of file +} +Markdown.propTypes = { + source: PropTypes.string +} + +export default OAS3ComponentWrapFactory(Markdown) \ No newline at end of file diff --git a/src/style/_layout.scss b/src/style/_layout.scss index 9b38a700..95584a7c 100644 --- a/src/style/_layout.scss +++ b/src/style/_layout.scss @@ -543,14 +543,14 @@ .response-col_description__inner { - span + div.markdown, div.renderedMarkdown { font-size: 12px; font-style: italic; display: block; - margin: 10px 0; + margin: 0; padding: 10px; border-radius: 4px; diff --git a/test/components/markdown.js b/test/components/markdown.js new file mode 100644 index 00000000..01a55e1c --- /dev/null +++ b/test/components/markdown.js @@ -0,0 +1,48 @@ +/* eslint-env mocha */ +import React from "react" +import expect from "expect" +import { render } from "enzyme" +import Markdown from "components/providers/markdown" +import { Markdown as OAS3Markdown } from "corePlugins/oas3/wrap-components/markdown.js" + +describe("Markdown component", function() { + describe("Swagger 2.0", function() { + it("allows image elements", function() { + const str = `![Image alt text](http://image.source "Image title")` + const el = render() + expect(el.html()).toEqual(`

\n
`) + }) + + it("allows heading elements", function() { + const str = ` +# h1 +## h2 +### h3 +#### h4 +##### h5 +###### h6` + const el = render() + expect(el.html()).toEqual(`

h1

\n

h2

\n

h3

\n

h4

\n
h5
\n
h6
\n
`) + }) + }) + + describe("OAS 3", function() { + it("allows image elements", function() { + const str = `![Image alt text](http://image.source "Image title")` + const el = render() + expect(el.html()).toEqual(`

`) + }) + + it("allows heading elements", function() { + const str = ` +# h1 +## h2 +### h3 +#### h4 +##### h5 +###### h6` + const el = render() + expect(el.html()).toEqual(`

h1

\n

h2

\n

h3

\n

h4

\n
h5
\n
h6
`) + }) + }) +}) diff --git a/test/xss/info-sanitization.js b/test/xss/info-sanitization.js new file mode 100644 index 00000000..e868fe9f --- /dev/null +++ b/test/xss/info-sanitization.js @@ -0,0 +1,33 @@ +/* eslint-env mocha */ +import React from "react" +import expect from "expect" +import { render } from "enzyme" +import { fromJS } from "immutable" +import Info from "components/info" +import Markdown from "components/providers/markdown" + +describe(" Sanitization", function(){ + const dummyComponent = () => null + const components = { + Markdown + } + const props = { + getComponent: c => components[c] || dummyComponent, + info: fromJS({ + title: "Test Title **strong** ", + description: "Description *with* " + }), + host: "example.test", + basePath: "/api" + } + + it("renders sanitized .title content", function(){ + let wrapper = render() + expect(wrapper.find(".title").html()).toEqual("Test Title **strong** <script>alert(1)</script>") + }) + + it("renders sanitized .description content", function() { + let wrapper = render() + expect(wrapper.find(".description").html()).toEqual("

Description with

\n
") + }) +}) diff --git a/test/xss/markdown-script-sanitization.js b/test/xss/markdown-script-sanitization.js new file mode 100644 index 00000000..9d6624c7 --- /dev/null +++ b/test/xss/markdown-script-sanitization.js @@ -0,0 +1,36 @@ +/* eslint-env mocha */ +import React from "react" +import expect from "expect" +import { render } from "enzyme" +import Markdown from "components/providers/markdown" +import { Markdown as OAS3Markdown } from "corePlugins/oas3/wrap-components/markdown.js" + +describe("Markdown Script Sanitization", function() { + describe("Swagger 2.0", function() { + it("sanitizes ` + const el = render() + expect(el.html()).toEqual(`

script

\n
`) + }) + + it("sanitizes elements", function() { + const str = `` + const el = render() + expect(el.html()).toEqual(`

\n
`) + }) + }) + + describe("OAS 3", function() { + it("sanitizes ` + const el = render() + expect(el.html()).toEqual(`

script

`) + }) + + it("sanitizes elements", function() { + const str = `` + const el = render() + expect(el.html()).toEqual(`
`) + }) + }) +})