Merge branch 'master' into bug/3730-empty-body-responses
This commit is contained in:
@@ -22,7 +22,7 @@ The OpenAPI Specification has undergone 5 revisions since initial creation in 20
|
|||||||
|
|
||||||
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
|
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
|
||||||
------------------ | ------------ | -------------------------- | -----
|
------------------ | ------------ | -------------------------- | -----
|
||||||
3.3.0 | 2017-09-29 | 2.0, 3.0 | [tag v3.3.0](https://github.com/swagger-api/swagger-ui/tree/v3.3.0)
|
3.3.1 | 2017-10-02 | 2.0, 3.0 | [tag v3.3.1](https://github.com/swagger-api/swagger-ui/tree/v3.3.1)
|
||||||
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21)
|
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21)
|
||||||
2.2.10 | 2017-01-04 | 1.1, 1.2, 2.0 | [tag v2.2.10](https://github.com/swagger-api/swagger-ui/tree/v2.2.10)
|
2.2.10 | 2017-01-04 | 1.1, 1.2, 2.0 | [tag v2.2.10](https://github.com/swagger-api/swagger-ui/tree/v2.2.10)
|
||||||
2.1.5 | 2016-07-20 | 1.1, 1.2, 2.0 | [tag v2.1.5](https://github.com/swagger-api/swagger-ui/tree/v2.1.5)
|
2.1.5 | 2016-07-20 | 1.1, 1.2, 2.0 | [tag v2.1.5](https://github.com/swagger-api/swagger-ui/tree/v2.1.5)
|
||||||
@@ -119,7 +119,7 @@ scopeSeparator | scope separator for passing scopes, encoded before calling, def
|
|||||||
additionalQueryStringParams | Additional query parameters added to `authorizationUrl` and `tokenUrl`. MUST be an object
|
additionalQueryStringParams | Additional query parameters added to `authorizationUrl` and `tokenUrl`. MUST be an object
|
||||||
useBasicAuthenticationWithAccessCodeGrant | Only activated for the `accessCode` flow. During the `authorization_code` request to the `tokenUrl`, pass the [Client Password](https://tools.ietf.org/html/rfc6749#section-2.3.1) using the HTTP Basic Authentication scheme (`Authorization` header with `Basic base64encoded[client_id:client_secret]`). The default is `false`
|
useBasicAuthenticationWithAccessCodeGrant | Only activated for the `accessCode` flow. During the `authorization_code` request to the `tokenUrl`, pass the [Client Password](https://tools.ietf.org/html/rfc6749#section-2.3.1) using the HTTP Basic Authentication scheme (`Authorization` header with `Basic base64encoded[client_id:client_secret]`). The default is `false`
|
||||||
|
|
||||||
```
|
```javascript
|
||||||
const ui = SwaggerUIBundle({...})
|
const ui = SwaggerUIBundle({...})
|
||||||
|
|
||||||
// Method can be called in any place after calling constructor SwaggerUIBundle
|
// Method can be called in any place after calling constructor SwaggerUIBundle
|
||||||
@@ -171,7 +171,7 @@ showMutatedRequest | If set to `true` (the default), uses the mutated request re
|
|||||||
#### Topbar plugin
|
#### Topbar plugin
|
||||||
Topbar plugin enables top bar with input for spec path and explore button or a dropdown if `urls` is used. By default the plugin is enabled, and to disable it you need to remove Topbar plugin from presets in `src/standalone/index.js`:
|
Topbar plugin enables top bar with input for spec path and explore button or a dropdown if `urls` is used. By default the plugin is enabled, and to disable it you need to remove Topbar plugin from presets in `src/standalone/index.js`:
|
||||||
|
|
||||||
```
|
```javascript
|
||||||
let preset = [
|
let preset = [
|
||||||
// TopbarPlugin,
|
// TopbarPlugin,
|
||||||
ConfigsPlugin,
|
ConfigsPlugin,
|
||||||
|
|||||||
4
dist/swagger-ui-bundle.js
vendored
4
dist/swagger-ui-bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/swagger-ui-bundle.js.map
vendored
2
dist/swagger-ui-bundle.js.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;AAonMA;;;;;;AAm5DA;;;;;;;;;;;;;;;;;;;;;;;;;;AAukUA;;;;;;;;;;;;;;AAq4JA;AAw+iBA;;;;;;;;;AA8oIA;;;;;AA87QA;;;;;AAynBA;AAo0CA;;;;;;AA6gZA;;;;;;AAkzZA;AAixYA","sourceRoot":""}
|
{"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;AAonMA;;;;;;AAm5DA;;;;;;;;;;;;;;;;;;;;;;;;;;AAukUA;;;;;;;;;;;;;;AAq4JA;AA4+iBA;;;;;;;;;AA8oIA;;;;;AA87QA;;;;;AAynBA;AAo0CA;;;;;;AA6gZA;;;;;;AAkzZA;AAixYA","sourceRoot":""}
|
||||||
2
dist/swagger-ui.js
vendored
2
dist/swagger-ui.js
vendored
File diff suppressed because one or more lines are too long
2
dist/swagger-ui.js.map
vendored
2
dist/swagger-ui.js.map
vendored
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AAy7eA","sourceRoot":""}
|
{"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AA67eA","sourceRoot":""}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "swagger-ui",
|
"name": "swagger-ui",
|
||||||
"version": "3.3.0",
|
"version": "3.3.1",
|
||||||
"main": "dist/swagger-ui.js",
|
"main": "dist/swagger-ui.js",
|
||||||
"repository": "git@github.com:swagger-api/swagger-ui.git",
|
"repository": "git@github.com:swagger-api/swagger-ui.git",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"test": "npm run lint-errors && npm run just-test-in-node",
|
"test": "npm run lint-errors && npm run just-test-in-node",
|
||||||
"test-in-node": "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": "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",
|
"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",
|
"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",
|
"mock-api": "json-server --watch test/e2e/db.json --port 3204",
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ Markdown.propTypes = {
|
|||||||
export default Markdown
|
export default Markdown
|
||||||
|
|
||||||
const sanitizeOptions = {
|
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) {
|
textFilter: function(text) {
|
||||||
return text.replace(/"/g, "\"")
|
return text.replace(/"/g, "\"")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,16 @@ export default function downloadUrlPlugin (toolbox) {
|
|||||||
let { fn } = toolbox
|
let { fn } = toolbox
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
download: (url)=> ({ errActions, specSelectors, specActions }) => {
|
download: (url)=> ({ errActions, specSelectors, specActions, getConfigs }) => {
|
||||||
let { fetch } = fn
|
let { fetch } = fn
|
||||||
|
const config = getConfigs()
|
||||||
url = url || specSelectors.url()
|
url = url || specSelectors.url()
|
||||||
specActions.updateLoadingStatus("loading")
|
specActions.updateLoadingStatus("loading")
|
||||||
fetch({
|
fetch({
|
||||||
url,
|
url,
|
||||||
loadSpec: true,
|
loadSpec: true,
|
||||||
|
requestInterceptor: config.requestInterceptor || (a => a),
|
||||||
|
responseInterceptor: config.responseInterceptor || (a => a),
|
||||||
credentials: "same-origin",
|
credentials: "same-origin",
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "application/json,*/*"
|
"Accept": "application/json,*/*"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createSelector } from "reselect"
|
import { createSelector } from "reselect"
|
||||||
|
import { List } from "immutable"
|
||||||
import { isOAS3 as isOAS3Helper } from "../helpers"
|
import { isOAS3 as isOAS3Helper } from "../helpers"
|
||||||
|
|
||||||
|
|
||||||
@@ -23,6 +24,6 @@ const OAS3NullSelector = onlyOAS3(nullSelector)
|
|||||||
export const shownDefinitions = OAS3NullSelector
|
export const shownDefinitions = OAS3NullSelector
|
||||||
export const definitionsToAuthorize = OAS3NullSelector
|
export const definitionsToAuthorize = OAS3NullSelector
|
||||||
export const getDefinitionsByNames = OAS3NullSelector
|
export const getDefinitionsByNames = OAS3NullSelector
|
||||||
export const authorized = OAS3NullSelector
|
export const authorized = onlyOAS3(() => List())
|
||||||
export const isAuthorized = OAS3NullSelector
|
export const isAuthorized = OAS3NullSelector
|
||||||
export const getConfigs = OAS3NullSelector
|
export const getConfigs = OAS3NullSelector
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { OrderedMap } from "immutable"
|
|||||||
const RequestBody = ({
|
const RequestBody = ({
|
||||||
requestBody,
|
requestBody,
|
||||||
getComponent,
|
getComponent,
|
||||||
|
getConfigs,
|
||||||
specSelectors,
|
specSelectors,
|
||||||
contentType,
|
contentType,
|
||||||
isExecute,
|
isExecute,
|
||||||
@@ -27,6 +28,7 @@ const RequestBody = ({
|
|||||||
}
|
}
|
||||||
<ModelExample
|
<ModelExample
|
||||||
getComponent={ getComponent }
|
getComponent={ getComponent }
|
||||||
|
getConfigs={ getConfigs }
|
||||||
specSelectors={ specSelectors }
|
specSelectors={ specSelectors }
|
||||||
expandDepth={1}
|
expandDepth={1}
|
||||||
isExecute={isExecute}
|
isExecute={isExecute}
|
||||||
@@ -46,6 +48,7 @@ const RequestBody = ({
|
|||||||
RequestBody.propTypes = {
|
RequestBody.propTypes = {
|
||||||
requestBody: ImPropTypes.orderedMap.isRequired,
|
requestBody: ImPropTypes.orderedMap.isRequired,
|
||||||
getComponent: PropTypes.func.isRequired,
|
getComponent: PropTypes.func.isRequired,
|
||||||
|
getConfigs: PropTypes.func.isRequired,
|
||||||
specSelectors: PropTypes.object.isRequired,
|
specSelectors: PropTypes.object.isRequired,
|
||||||
contentType: PropTypes.string.isRequired,
|
contentType: PropTypes.string.isRequired,
|
||||||
isExecute: PropTypes.bool.isRequired,
|
isExecute: PropTypes.bool.isRequired,
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
|
import PropTypes from "prop-types"
|
||||||
import ReactMarkdown from "react-markdown"
|
import ReactMarkdown from "react-markdown"
|
||||||
import { Parser, HtmlRenderer } from "commonmark"
|
import { Parser, HtmlRenderer } from "commonmark"
|
||||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||||
import { sanitizer } from "core/components/providers/markdown"
|
import { sanitizer } from "core/components/providers/markdown"
|
||||||
|
|
||||||
export default OAS3ComponentWrapFactory(({ source }) => {
|
export const Markdown = ({ source }) => {
|
||||||
if ( source ) {
|
if ( source ) {
|
||||||
const parser = new Parser()
|
const parser = new Parser()
|
||||||
const writer = new HtmlRenderer()
|
const writer = new HtmlRenderer()
|
||||||
@@ -23,4 +24,9 @@ export default OAS3ComponentWrapFactory(({ source }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
})
|
}
|
||||||
|
Markdown.propTypes = {
|
||||||
|
source: PropTypes.string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OAS3ComponentWrapFactory(Markdown)
|
||||||
@@ -543,14 +543,14 @@
|
|||||||
|
|
||||||
.response-col_description__inner
|
.response-col_description__inner
|
||||||
{
|
{
|
||||||
span
|
div.markdown, div.renderedMarkdown
|
||||||
{
|
{
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
margin: 10px 0;
|
margin: 0;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|||||||
48
test/components/markdown.js
Normal file
48
test/components/markdown.js
Normal file
@@ -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 = ``
|
||||||
|
const el = render(<Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="markdown"><p><img src="http://image.source" title="Image title"></p>\n</div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("allows heading elements", function() {
|
||||||
|
const str = `
|
||||||
|
# h1
|
||||||
|
## h2
|
||||||
|
### h3
|
||||||
|
#### h4
|
||||||
|
##### h5
|
||||||
|
###### h6`
|
||||||
|
const el = render(<Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="markdown"><h1>h1</h1>\n<h2>h2</h2>\n<h3>h3</h3>\n<h4>h4</h4>\n<h5>h5</h5>\n<h6>h6</h6>\n</div>`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("OAS 3", function() {
|
||||||
|
it("allows image elements", function() {
|
||||||
|
const str = ``
|
||||||
|
const el = render(<OAS3Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p><img src="http://image.source" title="Image title"></p></div></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("allows heading elements", function() {
|
||||||
|
const str = `
|
||||||
|
# h1
|
||||||
|
## h2
|
||||||
|
### h3
|
||||||
|
#### h4
|
||||||
|
##### h5
|
||||||
|
###### h6`
|
||||||
|
const el = render(<OAS3Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><h1>h1</h1>\n<h2>h2</h2>\n<h3>h3</h3>\n<h4>h4</h4>\n<h5>h5</h5>\n<h6>h6</h6></div></div>`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
33
test/xss/info-sanitization.js
Normal file
33
test/xss/info-sanitization.js
Normal file
@@ -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("<Info/> Sanitization", function(){
|
||||||
|
const dummyComponent = () => null
|
||||||
|
const components = {
|
||||||
|
Markdown
|
||||||
|
}
|
||||||
|
const props = {
|
||||||
|
getComponent: c => components[c] || dummyComponent,
|
||||||
|
info: fromJS({
|
||||||
|
title: "Test Title **strong** <script>alert(1)</script>",
|
||||||
|
description: "Description *with* <script>Markdown</script>"
|
||||||
|
}),
|
||||||
|
host: "example.test",
|
||||||
|
basePath: "/api"
|
||||||
|
}
|
||||||
|
|
||||||
|
it("renders sanitized .title content", function(){
|
||||||
|
let wrapper = render(<Info {...props}/>)
|
||||||
|
expect(wrapper.find(".title").html()).toEqual("Test Title **strong** <script>alert(1)</script>")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("renders sanitized .description content", function() {
|
||||||
|
let wrapper = render(<Info {...props}/>)
|
||||||
|
expect(wrapper.find(".description").html()).toEqual("<div class=\"markdown\"><p>Description <em>with</em> </p>\n</div>")
|
||||||
|
})
|
||||||
|
})
|
||||||
36
test/xss/markdown-script-sanitization.js
Normal file
36
test/xss/markdown-script-sanitization.js
Normal file
@@ -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 <script> elements", function() {
|
||||||
|
const str = `script <script>alert(1)</script>`
|
||||||
|
const el = render(<Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="markdown"><p>script </p>\n</div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("sanitizes <img> elements", function() {
|
||||||
|
const str = `<img src=x onerror="alert('img-in-description')">`
|
||||||
|
const el = render(<Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="markdown"><p><img src="x"></p>\n</div>`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("OAS 3", function() {
|
||||||
|
it("sanitizes <script> elements", function() {
|
||||||
|
const str = `script <script>alert(1)</script>`
|
||||||
|
const el = render(<OAS3Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><p>script </p></div></div>`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("sanitizes <img> elements", function() {
|
||||||
|
const str = `<img src=x onerror="alert('img-in-description')">`
|
||||||
|
const el = render(<OAS3Markdown source={str} />)
|
||||||
|
expect(el.html()).toEqual(`<div class="renderedMarkdown"><div><img src="x"></div></div>`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user