Compare commits

..
94 Commits
Author SHA1 Message Date
kyle 164747d407 Merge pull request #3849 from swagger-api/v/3.4.2
v3.4.2
2017-10-30 18:48:20 -07:00
Kyle Shockey 1c13649d02 v3.4.2 2017-10-30 18:45:37 -07:00
kyle 2b25e655ca Merge pull request #3848 from swagger-api/bug/3847-href-xss
Add URL sanitizer to avoid `href` XSS attack vector
2017-10-30 18:12:50 -07:00
Kyle Shockey 551a82d120 Remove describe.only 2017-10-30 18:00:59 -07:00
Kyle Shockey b9fa5e5bb2 Add sanitizeUrl tests 2017-10-30 17:57:43 -07:00
Kyle Shockey afa615e01d Add URL sanitizer to avoid javascript: XSS attack vector 2017-10-30 17:43:23 -07:00
kyle 4eae9b681b Merge pull request #3835 from swagger-api/v/3.4.1
v3.4.1
2017-10-28 00:43:16 -07:00
Kyle Shockey bdc0435683 v3.4.1 2017-10-28 00:37:55 -07:00
kyle 3c371915af Merge pull request #3834 from swagger-api/bug/3818-definitions-to-authorize-cache-invalidation
memoize definitionsToAuthorize based on securityDefinitons value
2017-10-28 00:03:14 -07:00
kyle ba6e8d14a3 Merge branch 'master' into bug/3818-definitions-to-authorize-cache-invalidation 2017-10-27 23:55:25 -07:00
kyle b90a4d3c71 Merge pull request #3833 from swagger-api/bug/3811-oas3-enum-execute
Pass correct schema values to parameter state builder for OAS3
2017-10-27 23:55:17 -07:00
Kyle Shockey 2157988212 Don't reach into schema for value 2017-10-27 23:31:47 -07:00
Kyle Shockey 109f7ed430 memoize definitionsToAuthorize based on securityDefinitons value 2017-10-27 22:41:27 -07:00
Kyle Shockey 79ddae1526 Pass correct schema values to parameter state builder for OAS3 2017-10-27 22:23:11 -07:00
kyle ac36d46467 Merge pull request #3829 from swagger-api/pull-request-template
Create pull request template
2017-10-27 13:01:14 -07:00
kyle b3a8e4d380 Merge branch 'master' into pull-request-template 2017-10-27 12:44:23 -07:00
kyle c33a37d8db Merge pull request #3831 from swagger-api/bug/3816-oas3-authorize-always-showing
Return null instead of empty ImmutableMap in OAS3 securityDefinitions selector
2017-10-26 15:55:32 -07:00
kyle 2caca555b1 Merge branch 'master' into bug/3816-oas3-authorize-always-showing 2017-10-26 15:49:05 -07:00
Kyle Shockey 7159649501 Return null instead of empty ImmutableMap in OAS3 securityDefinitions selector 2017-10-26 15:32:14 -07:00
kyle 3e7a0764bb Merge pull request #3830 from swagger-api/bug/resolver-interception
Provide interceptors to SwaggerClient.resolve
2017-10-26 15:06:32 -07:00
kyle 35f1e94c04 Merge branch 'master' into bug/resolver-interception 2017-10-26 14:58:56 -07:00
Kyle Shockey 26428cd63b Provide interceptors to SwaggerClient.resolve 2017-10-26 14:51:00 -07:00
kyle 896e160e2b Improve spacing and section header sizes 2017-10-26 12:08:52 -07:00
kyle 565318739c Add no code changes and dependency changes 2017-10-26 12:02:06 -07:00
kyle f5217f9c99 Merge pull request #3828 from swagger-api/shockey-patch-1
Improve UX
2017-10-26 11:59:09 -07:00
kyle cb9804b5ca Create pull_request_template.md 2017-10-26 11:58:54 -07:00
kyle 8f0b498374 Update add-plugin.md 2017-10-26 10:59:06 -07:00
kyle 71c33d0af8 Update topbar.jsx 2017-10-26 10:57:36 -07:00
Ron c2bdb3af24 Merge pull request #3810 from iiska/relative-json-path
Fix accessing bundled swagger json behind reverse proxy
2017-10-24 09:58:25 -07:00
Juhamatti Niemelä 6dd5d80770 Fix accessing bundled swagger json behind reverse proxy
If swagger-ui is run behind reverse proxy in path other than / accessing
bundled swagger json configured in SWAGGER_JSON env variable will fail.

Fix that by changing REL_PATH prefix from `/` to `./`
2017-10-24 16:42:51 +03:00
kyle 96b0b04538 Merge pull request #3799 from swagger-api/bug/mocha-exclusive-tests
Add ESLint rule that catches `describe.only`, `it.only`
2017-10-21 12:25:36 -07:00
Kyle Shockey 075c94f68c Remove describe.only 2017-10-21 11:13:01 -07:00
Kyle Shockey fc11f7a556 Add ESLint rule that catches describe.only, it.only 2017-10-21 11:11:13 -07:00
kyle c4a74d4db7 Merge pull request #3797 from swagger-api/v/3.4.0
v3.4.0
2017-10-20 20:37:07 -07:00
Kyle Shockey d6b9ace648 v3.4.0 2017-10-20 20:30:13 -07:00
kyle c46e18deeb Merge pull request #3780 from swagger-api/ft/oas3-authorization
OAS 3.0 Authorization
2017-10-20 19:45:24 -07:00
Kyle Shockey 7c917325f0 Linter fixes 2017-10-20 19:31:52 -07:00
kyle 6d22e38a04 Merge branch 'master' into ft/oas3-authorization 2017-10-20 19:07:38 -07:00
kyle 353fd00b08 Merge pull request #3746 from racedale/master
Support for mobile view on Swagger UI
2017-10-20 18:12:51 -07:00
kyle bc676601a8 Merge branch 'master' into master 2017-10-20 16:59:19 -07:00
kyle 3387d4762f Merge pull request #3796 from swagger-api/bug/editor-1530-array-parameter-input
Pass `parameter.schema` as schema in OAS 3.0
2017-10-20 16:58:04 -07:00
Ron d953b7790d Merge branch 'master' into master 2017-10-20 16:54:03 -07:00
Kyle Shockey 631e69f6f8 Pass parameter.schema as schema in OAS 3.0
Parameters were restructured in OAS 3.0; schemas are no longer at
the top level of parameters. Adjusting to account for the change.
2017-10-20 16:01:26 -07:00
kyle 9a442b16e3 Merge pull request #3793 from swagger-api/ft/sourcemaps
Sourcemaps
2017-10-20 14:21:11 -07:00
kyle 4ac3212341 Merge branch 'master' into ft/sourcemaps 2017-10-19 23:06:04 -07:00
Kyle Shockey e51d6c2ed5 Stop babelifying dev server twice; use eval-source-map 2017-10-19 22:48:34 -07:00
Kyle Shockey dccc9cfc36 Switch to nosource-source-map for dist builds 2017-10-19 22:47:44 -07:00
Owen Conti 53a4af6885 Merge pull request #3790 from owenconti/bug/3783-response-description-styles
Fixes #3783
2017-10-19 16:27:54 -06:00
Owen Conti 95002ba6e5 Fixes #3783
Remove extra styles for .renderedMarkdown content.
2017-10-19 14:44:17 -06:00
Kyle Shockey c9674a1fc5 Refactor Auths component flow; create component that supports HTTP auths 2017-10-18 21:23:28 -07:00
kyle e184762b0f Merge pull request #3768 from mhw/fix-markdown-links
Fix regression with rendering links in Markdown.
2017-10-16 14:15:23 -07:00
Mark H. Wilkinson e1a7af4c92 Fix regression with rendering links in Markdown. 2017-10-16 14:40:07 +01:00
kyle b873ee78d4 Merge pull request #3760 from swagger-api/v/3.3.2
v3.3.2
2017-10-13 21:52:23 -07:00
Kyle Shockey 2c86cd5820 v3.3.2 2017-10-13 21:46:10 -07:00
kyle 7de652de0b Merge pull request #3755 from swagger-api/bug/auth-display-regression
Fix auth icon display regression
2017-10-13 21:35:27 -07:00
kyle 897362a301 Merge branch 'master' into bug/auth-display-regression 2017-10-13 21:27:43 -07:00
Kyle Shockey d677e534c3 WIP 2017-10-13 21:23:45 -07:00
kyle 7c195f0f35 Merge pull request #3756 from swagger-api/swagger-ui/good-first-issue-readme-update
`Good first contribution` -> `Good first issue`
2017-10-12 16:19:25 -07:00
kyle a03bcbdaf4 Well, it's "authorization" not "authentication". 2017-10-12 14:58:11 -07:00
kyle 80de498ca4 Merge branch 'master' into good-first-issue-readme-update 2017-10-12 14:57:33 -07:00
kyle 8dab91f184 Merge pull request #3751 from owenconti/bug/3747-oas3-validate-param
Fixes #3747 OAS3 validate param
2017-10-12 14:09:59 -07:00
kyle 4705502efb Good first contribution -> Good first issue 2017-10-12 13:43:33 -07:00
kyle 6815f1d7bb Merge branch 'master' into bug/auth-display-regression 2017-10-12 13:18:00 -07:00
Kyle Shockey 6cf6a85637 Capture state in isAuthorized wrapAction 2017-10-12 13:10:56 -07:00
Kyle Shockey 98b36bdbd2 Moar whitespace
This was messing up my editor's syntax highlighting
2017-10-12 13:10:10 -07:00
Owen Conti 18a681e231 Update all the validateParam tests to use the assertValidateParam factory 2017-10-11 19:50:02 -06:00
Owen Conti 537b6d88b5 Merge branch 'bug/3747-oas3-validate-param' of github.com:owenconti/swagger-ui into bug/3747-oas3-validate-param 2017-10-11 19:20:33 -06:00
Owen Conti 3ac24da271 Merge branch 'master' of github.com:swagger-api/swagger-ui into bug/3747-oas3-validate-param 2017-10-11 19:19:51 -06:00
Owen Conti 7c8942e61c Update to utils.js to use a common method for asserting the validateParam function. 2017-10-11 19:19:24 -06:00
kyle 0103364880 Merge branch 'master' into bug/3747-oas3-validate-param 2017-10-11 17:57:46 -07:00
Owen Conti 7344315c34 Added tests for isOAS3 in validateParam function. 2017-10-11 18:34:24 -06:00
Kyle Shockey 8b81bcac48 OAS3 Auth MVP 2017-10-11 16:58:24 -07:00
Owen Conti a0f8f9a05d Fixes #3747
Update `validateParam` to handle OAS3 parameters.
2017-10-11 17:46:38 -06:00
Race, Dale 4da430c08e Lower select and input button width's on mobile 2017-10-11 09:12:55 -05:00
Race, Dale 499d61161a Decrease summary-path font size on mobile 2017-10-11 09:12:17 -05:00
Race, Dale 8acac155e1 Tweak padding values for topbar to fit mobile 2017-10-11 08:46:19 -05:00
Race, Dale 59188b1bfe Remove min-width from search url input field 2017-10-11 08:45:41 -05:00
Race, Dale 3711d19064 Add viewport meta tag 2017-10-11 08:43:02 -05:00
kyle 13f888999f Merge pull request #3736 from owenconti/bug/3730-empty-body-responses
Fixes #3730 - Empty response content
2017-10-10 17:53:11 -07:00
kyle f32492b4ee Merge branch 'master' into bug/3730-empty-body-responses 2017-10-10 17:44:58 -07:00
Owen Conti 5659b2c8eb Merge pull request #3740 from owenconti/bug/3646-markdown-rendering-styles
Fixes #3740
2017-10-10 16:02:21 -06:00
Owen Conti 2cd4989c05 Fixes #3646
Fix CSS selector for markdown content inside response component.
2017-10-10 15:54:22 -06:00
kyle 1e146627d2 Merge pull request #3738 from swagger-api/ft/spec-download-interception
Pass configuration interceptors to spec download fetch
2017-10-09 18:16:02 -07:00
kyle 37aead915e Merge branch 'master' into ft/spec-download-interception 2017-10-09 18:07:08 -07:00
Kyle Shockey 74165f5292 Pass configuration interceptors to spec download fetch 2017-10-09 12:10:41 -07:00
kyle f11ab532e8 Merge pull request #3737 from owenconti/test/3715-xss-unit-tests
Start of unit tests for XSS testing
2017-10-08 21:58:01 -07:00
Owen Conti 60e8091eed Add unit test for images in markdown 2017-10-08 10:26:32 -06:00
Owen Conti 1785d48746 Remove .only from existing tests. Add markdown test cases for heading elements. 2017-10-08 10:13:09 -06:00
Owen Conti a5763a56cc Merge branch 'bug/3734-markdown-headings' into test/3715-xss-unit-tests 2017-10-08 10:08:45 -06:00
Owen Conti 13aa95ff00 Merge branch 'master' of github.com:swagger-api/swagger-ui into test/3715-xss-unit-tests 2017-10-08 09:58:41 -06:00
Owen Conti ec76e512e2 Fixes #3730
Add extra check for missing response content and contentType
2017-10-08 09:38:08 -06:00
Owen Conti 729fd71546 Fixes #3734
Add <h1> and <h2> elements to sanitizer options.
2017-10-08 09:09:29 -06:00
Owen Conti 5a69603beb Test for sanitizing <img> elements. Test sanitization of the <Info /> component 2017-10-01 13:59:28 -06:00
Owen Conti 559b315d0d Initial test for Markdown <script> sanitization 2017-10-01 10:30:41 -06:00
58 changed files with 992 additions and 351 deletions
+4 -2
View File
@@ -16,7 +16,8 @@
"extends": ["eslint:recommended", "plugin:react/recommended"],
"plugins": [
"react"
"react",
"mocha"
],
"rules": {
@@ -32,6 +33,7 @@
"comma-dangle": 0,
"no-console": ["error", { allow: ["warn", "error"] }],
"react/jsx-no-bind": 1,
"react/display-name": 0
"react/display-name": 0,
"mocha/no-exclusive-tests": "error"
}
}
+42
View File
@@ -0,0 +1,42 @@
<!--- Provide a general summary of your changes in the Title above -->
### Description
<!--- Describe your changes in detail -->
### Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
<!--- Use the magic "Fixes #1234" format, so the issues are -->
<!--- automatically closed when this PR is merged. -->
### How Has This Been Tested?
<!--- Please describe in detail how you manually tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
### Screenshots (if appropriate):
### Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] No code changes (changes to documentation, CI, metadata, etc)
- [ ] Dependency changes (any modification to dependencies in `package.json`)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
### Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.
+2 -2
View File
@@ -6,7 +6,7 @@
**This is the new version of swagger-ui, 3.x. Want to learn more? Check out our [FAQ](http://swagger.io/new-ui-faq/).**
**👉🏼 Want to score an easy open-source contribution?** Check out our [Good first contribution](https://github.com/swagger-api/swagger-ui/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+contribution%22) label.
**👉🏼 Want to score an easy open-source contribution?** Check out our [Good first issue](https://github.com/swagger-api/swagger-ui/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+issue%22) label.
As a brand new version, written from the ground up, there are some known issues and unimplemented features. Check out the [Known Issues](#known-issues) section for more details.
@@ -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
------------------ | ------------ | -------------------------- | -----
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.4.2 | 2017-10-30 | 2.0, 3.0 | [tag v3.4.2](https://github.com/swagger-api/swagger-ui/tree/v3.4.2)
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.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)
+1
View File
@@ -3,6 +3,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Swagger UI</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
+4 -1
View File
@@ -27,7 +27,10 @@
isValid = qp.state === sentState
if (oauth2.auth.schema.get("flow") === "accessCode" && !oauth2.auth.code) {
if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
+4 -1
View File
@@ -27,7 +27,10 @@
isValid = qp.state === sentState
if (oauth2.auth.schema.get("flow") === "accessCode" && !oauth2.auth.code) {
if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
+62 -30
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"swagger-ui.css","sources":[],"mappings":"","sourceRoot":""}
{"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""}
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -32,7 +32,7 @@ fi
if [[ -f $SWAGGER_JSON ]]; then
cp $SWAGGER_JSON $NGINX_ROOT
REL_PATH="/$(basename $SWAGGER_JSON)"
REL_PATH="./$(basename $SWAGGER_JSON)"
sed -i "s|http://petstore.swagger.io/v2/swagger.json|$REL_PATH|g" $INDEX_FILE
sed -i "s|http://example.com/api|$REL_PATH|g" $INDEX_FILE
else
+1 -1
View File
@@ -137,7 +137,7 @@ module.exports = function(rules, options) {
}
},
devtool: specialOptions.sourcemaps ? "cheap-module-source-map" : null,
devtool: specialOptions.sourcemaps ? "nosource-source-map" : null,
plugins,
+5 -3
View File
@@ -1,6 +1,6 @@
{
"name": "swagger-ui",
"version": "3.3.1",
"version": "3.4.2",
"main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [
@@ -32,13 +32,14 @@
"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",
"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",
@@ -78,7 +79,7 @@
"scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0",
"shallowequal": "0.2.2",
"swagger-client": "^3.2.0",
"swagger-client": "^3.3.1",
"url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1",
@@ -105,6 +106,7 @@
"enzyme": "^2.7.1",
"eslint": "^4.1.1",
"eslint-plugin-import": "^2.6.0",
"eslint-plugin-mocha": "^4.11.0",
"eslint-plugin-react": "^7.1.0",
"extract-text-webpack-plugin": "^2.1.2",
"file-loader": "0.11.2",
+5 -4
View File
@@ -51,14 +51,15 @@ export default class ApiKeyAuth extends React.Component {
return (
<div>
<h4>Api key authorization<JumpToPath path={[ "securityDefinitions", name ]} /></h4>
<h4>
<code>{ name || schema.get("name") }</code>&nbsp;
(apiKey)
<JumpToPath path={[ "securityDefinitions", name ]} />
</h4>
{ value && <h6>Authorized</h6>}
<Row>
<Markdown source={ schema.get("description") } />
</Row>
<Row>
<p>Name: <code>{ schema.get("name") }</code></p>
</Row>
<Row>
<p>In: <code>{ schema.get("in") }</code></p>
</Row>
+62
View File
@@ -0,0 +1,62 @@
import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
export default class Auths extends React.Component {
static propTypes = {
schema: ImPropTypes.orderedMap.isRequired,
name: PropTypes.string.isRequired,
onAuthChange: PropTypes.func.isRequired,
authorized: ImPropTypes.orderedMap.isRequired
}
render() {
let {
schema,
name,
getComponent,
onAuthChange,
authorized,
errSelectors
} = this.props
const ApiKeyAuth = getComponent("apiKeyAuth")
const BasicAuth = getComponent("basicAuth")
let authEl
const type = schema.get("type")
switch(type) {
case "apiKey": authEl = <ApiKeyAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ onAuthChange } />
break
case "basic": authEl = <BasicAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ onAuthChange } />
break
default: authEl = <div key={ name }>Unknown security definition type { type }</div>
}
return (<div key={`${name}-jump`}>
{ authEl }
</div>)
}
static propTypes = {
errSelectors: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
authSelectors: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
definitions: ImPropTypes.iterable.isRequired
}
}
+10 -30
View File
@@ -27,7 +27,6 @@ export default class Auths extends React.Component {
e.preventDefault()
let { authActions } = this.props
authActions.authorize(this.state)
}
@@ -44,8 +43,7 @@ export default class Auths extends React.Component {
render() {
let { definitions, getComponent, authSelectors, errSelectors } = this.props
const ApiKeyAuth = getComponent("apiKeyAuth")
const BasicAuth = getComponent("basicAuth")
const AuthItem = getComponent("AuthItem")
const Oauth2 = getComponent("oauth2", true)
const Button = getComponent("Button")
@@ -64,33 +62,15 @@ export default class Auths extends React.Component {
!!nonOauthDefinitions.size && <form onSubmit={ this.submitAuth }>
{
nonOauthDefinitions.map( (schema, name) => {
let type = schema.get("type")
let authEl
switch(type) {
case "apiKey": authEl = <ApiKeyAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ this.onAuthChange } />
break
case "basic": authEl = <BasicAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ this.onAuthChange } />
break
default: authEl = <div key={ name }>Unknown security definition type { type }</div>
}
return (<div key={`${name}-jump`}>
{ authEl }
</div>)
return <AuthItem
key={name}
schema={schema}
name={name}
getComponent={getComponent}
onAuthChange={this.onAuthChange}
authorized={authorized}
errSelectors={errSelectors}
/>
}).toArray()
}
<div className="auth-btn-wrapper">
+13 -7
View File
@@ -2,11 +2,6 @@ import React from "react"
import PropTypes from "prop-types"
import oauth2Authorize from "core/oauth2-authorize"
const IMPLICIT = "implicit"
const ACCESS_CODE = "accessCode"
const PASSWORD = "password"
const APPLICATION = "application"
export default class Oauth2 extends React.Component {
static propTypes = {
name: PropTypes.string,
@@ -16,6 +11,7 @@ export default class Oauth2 extends React.Component {
authSelectors: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
errSelectors: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
errActions: PropTypes.object.isRequired,
getConfigs: PropTypes.any
}
@@ -83,7 +79,9 @@ export default class Oauth2 extends React.Component {
}
render() {
let { schema, getComponent, authSelectors, errSelectors, name } = this.props
let {
schema, getComponent, authSelectors, errSelectors, name, specSelectors
} = this.props
const Input = getComponent("Input")
const Row = getComponent("Row")
const Col = getComponent("Col")
@@ -92,6 +90,14 @@ export default class Oauth2 extends React.Component {
const JumpToPath = getComponent("JumpToPath", true)
const Markdown = getComponent( "Markdown" )
const { isOAS3 } = specSelectors
// Auth type consts
const IMPLICIT = "implicit"
const PASSWORD = "password"
const ACCESS_CODE = isOAS3() ? "authorizationCode" : "accessCode"
const APPLICATION = isOAS3() ? "clientCredentials" : "application"
let flow = schema.get("flow")
let scopes = schema.get("allowedScopes") || schema.get("scopes")
let authorizedAuth = authSelectors.authorized().get(name)
@@ -102,7 +108,7 @@ export default class Oauth2 extends React.Component {
return (
<div>
<h4>OAuth2.0 <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
<h4>{name} (OAuth2, { schema.get("flow") }) <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
{ !this.state.appName ? null : <h5>Application: { this.state.appName } </h5> }
{ description && <Markdown source={ schema.get("description") } /> }
+7 -6
View File
@@ -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 (
<div>
{ url && <div><a href={ url } target="_blank">{ name } - Website</a></div> }
{ url && <div><a href={ sanitizeUrl(url) } target="_blank">{ name } - Website</a></div> }
{ email &&
<a href={`mailto:${email}`}>
<a href={sanitizeUrl(`mailto:${email}`)}>
{ url ? `Send email to ${name}` : `Contact ${name}`}
</a>
}
@@ -59,7 +60,7 @@ class License extends React.Component {
return (
<div>
{
url ? <a target="_blank" href={ url }>{ name }</a>
url ? <a target="_blank" href={ sanitizeUrl(url) }>{ name }</a>
: <span>{ name }</span>
}
</div>
@@ -97,7 +98,7 @@ export default class Info extends React.Component {
{ version && <VersionStamp version={version}></VersionStamp> }
</h2>
{ host || basePath ? <Path host={ host } basePath={ basePath } /> : null }
{ url && <a target="_blank" href={ url }><span className="url"> { url } </span></a> }
{ url && <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a> }
</hgroup>
<div className="description">
@@ -106,14 +107,14 @@ export default class Info extends React.Component {
{
termsOfService && <div>
<a target="_blank" href={ termsOfService }>Terms of service</a>
<a target="_blank" href={ sanitizeUrl(termsOfService) }>Terms of service</a>
</div>
}
{ contact && contact.size ? <Contact data={ contact } /> : null }
{ license && license.size ? <License license={ license } /> : null }
{ externalDocsUrl ?
<a target="_blank" href={externalDocsUrl}>{externalDocsDescription || externalDocsUrl}</a>
<a target="_blank" href={sanitizeUrl(externalDocsUrl)}>{externalDocsDescription || externalDocsUrl}</a>
: null }
</div>
@@ -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 (<span style={{ float: "right"}}>
<a target="_blank" href={`${ this.state.validatorUrl }/debug?url=${ this.state.url }`}>
<ValidatorImage src={`${ this.state.validatorUrl }?url=${ this.state.url }`} alt="Online validator badge"/>
<a target="_blank" href={`${ sanitizedValidatorUrl }/debug?url=${ this.state.url }`}>
<ValidatorImage src={`${ sanitizedValidatorUrl }?url=${ this.state.url }`} alt="Online validator badge"/>
</a>
</span>)
}
+2 -1
View File
@@ -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 {
<span className="opblock-external-docs__description">
<Markdown source={ externalDocs.get("description") } />
</span>
<a className="opblock-external-docs__link" href={ externalDocs.get("url") }>{ externalDocs.get("url") }</a>
<a className="opblock-external-docs__link" href={ sanitizeUrl(externalDocs.get("url")) }>{ externalDocs.get("url") }</a>
</div>
</div> : null
}
+2 -2
View File
@@ -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 ?
<a
href={tagExternalDocsUrl}
href={sanitizeUrl(tagExternalDocsUrl)}
onClick={(e) => e.stopPropagation()}
target={"_blank"}
>{tagExternalDocsUrl}</a> : null
+13 -2
View File
@@ -1,4 +1,5 @@
import React, { Component } from "react"
import { Map } from "immutable"
import PropTypes from "prop-types"
import win from "core/window"
@@ -29,11 +30,21 @@ export default class ParameterRow extends Component {
componentWillReceiveProps(props) {
let { specSelectors, pathMethod, param } = props
let { isOAS3 } = specSelectors
let example = param.get("example")
let defaultValue = param.get("default")
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
let enumValue
if(isOAS3()) {
let schema = param.get("schema") || Map()
enumValue = schema.get("enum")
} else {
enumValue = parameter ? parameter.get("enum") : undefined
}
let paramValue = parameter ? parameter.get("value") : undefined
let enumValue = parameter ? parameter.get("enum") : undefined
let value
if ( paramValue !== undefined ) {
@@ -115,7 +126,7 @@ export default class ParameterRow extends Component {
required={ required }
description={param.get("description") ? `${param.get("name")} - ${param.get("description")}` : `${param.get("name")}`}
onChange={ this.onChangeWrapper }
schema={ param }/>
schema={ isOAS3 && isOAS3() ? param.get("schema") : param }/>
}
+5 -1
View File
@@ -29,7 +29,11 @@ Markdown.propTypes = {
export default Markdown
const sanitizeOptions = {
allowedTags: sanitize.defaults.allowedTags.concat([ "img" ]),
allowedTags: sanitize.defaults.allowedTags.concat([ "h1", "h2", "img" ]),
allowedAttributes: {
...sanitize.defaults.allowedAttributes,
"img": sanitize.defaults.allowedAttributes.img.concat(["title"])
},
textFilter: function(text) {
return text.replace(/&quot;/g, "\"")
}
+5 -1
View File
@@ -83,8 +83,12 @@ export default class ResponseBody extends React.Component {
// Anything else (CORS)
} else if (typeof content === "string") {
bodyEl = <HighlightCode value={ content } />
} else {
} else if ( content.size > 0 ) {
// We don't know the contentType, but there was some content returned
bodyEl = <div>Unknown response type</div>
} else {
// We don't know the contentType and there was no content returned
bodyEl = null
}
return ( !bodyEl ? null : <div>
+10
View File
@@ -22,6 +22,16 @@ export default function authorize ( { auth, authActions, errActions, configs, au
case "implicit":
query.push("response_type=token")
break
case "clientCredentials":
// OAS3
authActions.authorizeApplication(auth)
return
case "authorizationCode":
// OAS3
query.push("response_type=code")
break
}
if (typeof clientId === "string") {
+1 -1
View File
@@ -22,7 +22,7 @@ export default {
securities.entrySeq().forEach( ([ key, security ]) => {
let type = security.getIn(["schema", "type"])
if ( type === "apiKey" ) {
if ( type === "apiKey" || type === "http" ) {
map = map.set(key, security)
} else if ( type === "basic" ) {
let username = security.getIn(["value", "username"])
+3 -3
View File
@@ -10,7 +10,7 @@ export const shownDefinitions = createSelector(
export const definitionsToAuthorize = createSelector(
state,
() =>( { specSelectors } ) => {
() => ( { specSelectors } ) => {
let definitions = specSelectors.securityDefinitions()
let list = List()
@@ -27,7 +27,7 @@ export const definitionsToAuthorize = createSelector(
)
export const getDefinitionsByNames = ( state, securities ) =>( { specSelectors } ) => {
export const getDefinitionsByNames = ( state, securities ) => ( { specSelectors } ) => {
let securityDefinitions = specSelectors.securityDefinitions()
let result = List()
@@ -64,7 +64,7 @@ export const authorized = createSelector(
)
export const isAuthorized = ( state, securities ) =>( { authSelectors } ) => {
export const isAuthorized = ( state, securities ) => ( { authSelectors } ) => {
let authorized = authSelectors.authorized()
if(!List.isList(securities)) {
+4 -1
View File
@@ -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,*/*"
@@ -1,29 +1,60 @@
import { createSelector } from "reselect"
import { List } from "immutable"
import { List, Map, fromJS } from "immutable"
import { isOAS3 as isOAS3Helper } from "../helpers"
// Helpers
const state = state => state
function onlyOAS3(selector) {
return (ori, system) => (...args) => {
return (ori, system) => (state, ...args) => {
const spec = system.getSystem().specSelectors.specJson()
if(isOAS3Helper(spec)) {
return selector(...args)
return selector(system, ...args)
} else {
return ori(...args)
}
}
}
const nullSelector = createSelector(() => null)
export const definitionsToAuthorize = onlyOAS3(createSelector(
state,
({specSelectors}) => specSelectors.securityDefinitions(),
(system, definitions) => {
// Coerce our OpenAPI 3.0 definitions into monoflow definitions
// that look like Swagger2 definitions.
let list = List()
const OAS3NullSelector = onlyOAS3(nullSelector)
definitions.entrySeq().forEach( ([ defName, definition ]) => {
const type = definition.get("type")
// Hasta la vista, authentication!
export const shownDefinitions = OAS3NullSelector
export const definitionsToAuthorize = OAS3NullSelector
export const getDefinitionsByNames = OAS3NullSelector
export const authorized = onlyOAS3(() => List())
export const isAuthorized = OAS3NullSelector
export const getConfigs = OAS3NullSelector
if(type === "oauth2") {
definition.get("flows").entrySeq().forEach(([flowKey, flowVal]) => {
let translatedDef = fromJS({
flow: flowKey,
authorizationUrl: flowVal.get("authorizationUrl"),
tokenUrl: flowVal.get("tokenUrl"),
scopes: flowVal.get("scopes"),
type: definition.get("type")
})
list = list.push(new Map({
[defName]: translatedDef.filter((v) => {
// filter out unset values, sometimes `authorizationUrl`
// and `tokenUrl` come out as `undefined` in the data
return v !== undefined
})
}))
})
}
if(type === "http" || type === "apiKey") {
list = list.push(new Map({
[defName]: definition
}))
}
})
return list
}
))
@@ -0,0 +1,134 @@
import React from "react"
import PropTypes from "prop-types"
export default class HttpAuth extends React.Component {
static propTypes = {
authorized: PropTypes.object,
getComponent: PropTypes.func.isRequired,
errSelectors: PropTypes.object.isRequired,
schema: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func
}
constructor(props, context) {
super(props, context)
let { name, schema } = this.props
let value = this.getValue()
this.state = {
name: name,
schema: schema,
value: value
}
}
getValue () {
let { name, authorized } = this.props
return authorized && authorized.getIn([name, "value"])
}
onChange =(e) => {
let { onChange } = this.props
let { value, name } = e.target
let newValue = this.state.value || {}
if(name) {
newValue[name] = value
} else {
newValue = value
}
this.setState({ value: newValue }, () => onChange(this.state))
}
render() {
let { schema, getComponent, errSelectors, name } = this.props
const Input = getComponent("Input")
const Row = getComponent("Row")
const Col = getComponent("Col")
const AuthError = getComponent("authError")
const Markdown = getComponent( "Markdown" )
const JumpToPath = getComponent("JumpToPath", true)
const scheme = schema.get("scheme")
let value = this.getValue()
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
if(scheme === "basic") {
let username = value ? value.get("username") : null
return <div>
<h4>
<code>{ name || schema.get("name") }</code>&nbsp;
(http, Basic)
<JumpToPath path={[ "securityDefinitions", name ]} />
</h4>
{ username && <h6>Authorized</h6> }
<Row>
<Markdown source={ schema.get("description") } />
</Row>
<Row>
<label>Username:</label>
{
username ? <code> { username } </code>
: <Col><Input type="text" required="required" name="username" onChange={ this.onChange }/></Col>
}
</Row>
<Row>
<label>Password:</label>
{
username ? <code> ****** </code>
: <Col><Input required="required"
autoComplete="new-password"
name="password"
type="password"
onChange={ this.onChange }/></Col>
}
</Row>
{
errors.valueSeq().map( (error, key) => {
return <AuthError error={ error }
key={ key }/>
} )
}
</div>
}
if(scheme === "bearer") {
return (
<div>
<h4>
<code>{ name || schema.get("name") }</code>&nbsp;
(http, Bearer)
<JumpToPath path={[ "securityDefinitions", name ]} />
</h4>
{ value && <h6>Authorized</h6>}
<Row>
<Markdown source={ schema.get("description") } />
</Row>
<Row>
<p>In: <code>{ schema.get("in") }</code></p>
</Row>
<Row>
<label>Value:</label>
{
value ? <code> ****** </code>
: <Col><Input type="text" onChange={ this.onChange }/></Col>
}
</Row>
{
errors.valueSeq().map( (error, key) => {
return <AuthError error={ error }
key={ key }/>
} )
}
</div>
)
}
return <div>
<em><b>{name}</b> HTTP authentication: unsupported or missing scheme</em>
</div>
}
}
@@ -3,9 +3,11 @@ import RequestBody from "./request-body"
import OperationLink from "./operation-link.jsx"
import Servers from "./servers"
import RequestBodyEditor from "./request-body-editor"
import HttpAuth from "./http-auth"
export default {
Callbacks,
HttpAuth,
RequestBody,
Servers,
RequestBodyEditor,
@@ -48,12 +48,16 @@ export const definitions = onlyOAS3(createSelector(
spec => spec.getIn(["components", "schemas"]) || Map()
))
export const securityDefinitions = onlyOAS3(createSelector(
spec,
spec => spec.getIn(["components", "securitySchemes"]) || null
))
export const host = OAS3NullSelector
export const basePath = OAS3NullSelector
export const consumes = OAS3NullSelector
export const produces = OAS3NullSelector
export const schemes = OAS3NullSelector
export const securityDefinitions = OAS3NullSelector
// New selectors
@@ -0,0 +1,23 @@
import React from "react"
import { OAS3ComponentWrapFactory } from "../helpers"
export default OAS3ComponentWrapFactory(({ Ori, ...props }) => {
const {
schema, getComponent, errSelectors, authorized, onAuthChange, name
} = props
const HttpAuth = getComponent("HttpAuth")
const type = schema.get("type")
if(type === "http") {
return <HttpAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ onAuthChange }/>
} else {
return <Ori {...props} />
}
})
@@ -1,4 +1,5 @@
import Markdown from "./markdown"
import AuthItem from "./auth-item"
import parameters from "./parameters"
import VersionStamp from "./version-stamp"
import OnlineValidatorBadge from "./online-validator-badge"
@@ -6,6 +7,7 @@ import Model from "./model"
export default {
Markdown,
AuthItem,
parameters,
VersionStamp,
model: Model,
@@ -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
})
}
Markdown.propTypes = {
source: PropTypes.string
}
export default OAS3ComponentWrapFactory(Markdown)
+20 -5
View File
@@ -80,7 +80,12 @@ export const parseToJson = (str) => ({specActions, specSelectors, errActions}) =
}
export const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST }, getConfigs}) => {
const { modelPropertyMacro, parameterMacro } = getConfigs()
const {
modelPropertyMacro,
parameterMacro,
requestInterceptor,
responseInterceptor
} = getConfigs()
if(typeof(json) === "undefined") {
json = specSelectors.specJson()
@@ -93,8 +98,15 @@ export const resolveSpec = (json, url) => ({specActions, specSelectors, errActio
let specStr = specSelectors.specStr()
return resolve({fetch, spec: json, baseDoc: url, modelPropertyMacro, parameterMacro })
.then( ({spec, errors}) => {
return resolve({
fetch,
spec: json,
baseDoc: url,
modelPropertyMacro,
parameterMacro,
requestInterceptor,
responseInterceptor
}).then( ({spec, errors}) => {
errActions.clear({
type: "thrown"
})
@@ -137,10 +149,13 @@ export function changeParam( path, paramName, paramIn, value, isXml ){
}
}
export function validateParams( payload ){
export const validateParams = ( payload, isOAS3 ) =>{
return {
type: VALIDATE_PARAMS,
payload:{ pathMethod: payload }
payload:{
pathMethod: payload,
isOAS3
}
}
}
+2 -2
View File
@@ -51,14 +51,14 @@ export default {
})
},
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod } } ) => {
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
let operation = state.getIn( [ "resolved", "paths", ...pathMethod ] )
let isXml = /xml/i.test(operation.get("consumes_value"))
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
return parameters.withMutations( parameters => {
for ( let i = 0, len = parameters.count(); i < len; i++ ) {
let errors = validateParam(parameters.get(i), isXml)
let errors = validateParam(parameters.get(i), isXml, isOAS3)
parameters.setIn([i, "errors"], fromJS(errors))
}
})
+4
View File
@@ -13,3 +13,7 @@ export const executeRequest = (ori, { specActions }) => (req) => {
specActions.logRequest(req)
return ori(req)
}
export const validateParams = (ori, { specSelectors }) => (req) => {
return ori(req, specSelectors.isOAS3())
}
+2
View File
@@ -17,6 +17,7 @@ import AuthorizationPopup from "core/components/auth/authorization-popup"
import AuthorizeBtn from "core/components/auth/authorize-btn"
import AuthorizeOperationBtn from "core/components/auth/authorize-operation-btn"
import Auths from "core/components/auth/auths"
import AuthItem from "core/components/auth/auth-item"
import AuthError from "core/components/auth/error"
import ApiKeyAuth from "core/components/auth/api-key-auth"
import BasicAuth from "core/components/auth/basic-auth"
@@ -70,6 +71,7 @@ export default function() {
authorizeBtn: AuthorizeBtn,
authorizeOperationBtn: AuthorizeOperationBtn,
auths: Auths,
AuthItem: AuthItem,
authError: AuthError,
oauth2: Oauth2,
apiKeyAuth: ApiKeyAuth,
+15 -9
View File
@@ -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"
@@ -537,16 +537,18 @@ export const validateMinLength = (val, min) => {
}
// validation of parameters before execute
export const validateParam = (param, isXml) => {
export const validateParam = (param, isXml, isOAS3 = false) => {
let errors = []
let value = isXml && param.get("in") === "body" ? param.get("value_xml") : param.get("value")
let required = param.get("required")
let maximum = param.get("maximum")
let minimum = param.get("minimum")
let type = param.get("type")
let format = param.get("format")
let maxLength = param.get("maxLength")
let minLength = param.get("minLength")
let paramDetails = isOAS3 ? param.get("schema") : param
let maximum = paramDetails.get("maximum")
let minimum = paramDetails.get("minimum")
let type = paramDetails.get("type")
let format = paramDetails.get("format")
let maxLength = paramDetails.get("maxLength")
let minLength = paramDetails.get("minLength")
/*
If the parameter is required OR the parameter has a value (meaning optional, but filled in)
@@ -616,7 +618,7 @@ export const validateParam = (param, isXml) => {
if ( !value.count() ) { return errors }
itemType = param.getIn(["items", "type"])
itemType = paramDetails.getIn(["items", "type"])
value.forEach((item, index) => {
let err
@@ -720,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!
+1 -1
View File
@@ -1,6 +1,6 @@
# Add a plugin
### Swagger-UX relies on plugins for all the good stuff.
### Swagger-UI relies on plugins for all the good stuff.
Plugins allow you to add
- `statePlugins`
+1 -1
View File
@@ -129,7 +129,7 @@ export default class Topbar extends React.Component {
<div className="topbar">
<div className="wrapper">
<div className="topbar-wrapper">
<Link href="#" title="Swagger UX">
<Link href="#">
<img height="30" width="30" src={ Logo } alt="Swagger UI"/>
<span>swagger</span>
</Link>
+8
View File
@@ -30,6 +30,10 @@ select
.opblock-body select
{
min-width: 230px;
@media (max-width: 768px)
{
min-width: 180px;
}
}
label
@@ -56,6 +60,10 @@ input[type=file]
border: 1px solid #d9d9d9;
border-radius: 4px;
background: #fff;
@media (max-width: 768px) {
max-width: 175px;
}
&.invalid
{
+6 -10
View File
@@ -250,6 +250,10 @@
.opblock-summary-path__deprecated
{
font-size: 16px;
@media (max-width: 768px) {
font-size: 12px;
}
display: flex;
flex: 0 3 auto;
@@ -543,14 +547,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;
@@ -768,14 +772,6 @@
}
}
.renderedMarkdown {
p {
@include text_body();
margin-top: 0px;
margin-bottom: 0px;
}
}
.response-content-type {
padding-top: 1em;
+2 -3
View File
@@ -1,6 +1,6 @@
.topbar
{
padding: 8px 30px;
padding: 8px 0;
background-color: #89bf04;
.topbar-wrapper
@@ -39,7 +39,6 @@
input[type=text]
{
width: 100%;
min-width: 350px;
margin: 0;
border: 2px solid #547f00;
@@ -84,7 +83,7 @@
font-size: 16px;
font-weight: bold;
padding: 4px 40px;
padding: 4px 30px;
border: none;
border-radius: 0 4px 4px 0;
+54
View File
@@ -0,0 +1,54 @@
/* 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(<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>`)
})
it("allows links", function() {
const str = `[Link](https://example.com/)`
const el = render(<Markdown source={str} />)
expect(el.html()).toEqual(`<div class="markdown"><p><a href="https://example.com/" target="_blank">Link</a></p>\n</div>`)
})
})
describe("OAS 3", function() {
it("allows image elements", function() {
const str = `![Image alt text](http://image.source "Image title")`
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>`)
})
})
})
@@ -0,0 +1,116 @@
/* eslint-env mocha */
import expect, { createSpy } from "expect"
import { Map, fromJS } from "immutable"
import {
definitionsToAuthorize
} from "corePlugins/oas3/auth-extensions/wrap-selectors"
describe("oas3 plugin - auth extensions - wrapSelectors", function(){
describe("execute", function(){
it("should add `securities` to the oriAction call", function(){
// Given
const system = {
getSystem: () => system,
specSelectors: {
specJson: () => fromJS({
openapi: "3.0.0"
}),
securityDefinitions: () => {
return fromJS({
"oauth2AuthorizationCode": {
"type": "oauth2",
"flows": {
"authorizationCode": {
"authorizationUrl": "http://google.com/",
"tokenUrl": "http://google.com/",
"scopes": {
"myScope": "our only scope"
}
}
}
},
"oauth2Multiflow": {
"type": "oauth2",
"flows": {
"clientCredentials": {
"tokenUrl": "http://google.com/",
"scopes": {
"myScope": "our only scope"
}
},
"password": {
"tokenUrl": "http://google.com/",
"scopes": {
"myScope": "our only scope"
}
},
"authorizationCode": {
"authorizationUrl": "http://google.com/",
"tokenUrl": "http://google.com/",
"scopes": {
"myScope": "our only scope"
}
}
}
}
})
}
}
}
// When
let res = definitionsToAuthorize(() => null, system)()
// Then
expect(res.toJS()).toEqual([
{
oauth2AuthorizationCode: {
flow: "authorizationCode",
authorizationUrl: "http://google.com/",
tokenUrl: "http://google.com/",
scopes: {
"myScope": "our only scope"
},
type: "oauth2"
}
},
{
oauth2Multiflow: {
flow: "clientCredentials",
tokenUrl: "http://google.com/",
scopes: {
"myScope": "our only scope"
},
type: "oauth2"
}
},
{
oauth2Multiflow: {
flow: "password",
tokenUrl: "http://google.com/",
scopes: {
"myScope": "our only scope"
},
type: "oauth2"
}
},
{
oauth2Multiflow: {
flow: "authorizationCode",
authorizationUrl: "http://google.com/",
tokenUrl: "http://google.com/",
scopes: {
"myScope": "our only scope"
},
type: "oauth2"
}
},
])
})
})
})
+189 -184
View File
@@ -16,7 +16,8 @@ import {
fromJSOrdered,
getAcceptControllingResponse,
createDeepLinkPath,
escapeDeepLinkPath
escapeDeepLinkPath,
sanitizeUrl
} from "core/utils"
import win from "core/window"
@@ -277,461 +278,438 @@ describe("utils", function() {
let param = null
let result = null
it("skips validation when `type` is not specified", function() {
// invalid type
const assertValidateParam = (param, expectedError) => {
// Swagger 2.0 version
result = validateParam( fromJS(param), false )
expect( result ).toEqual( expectedError )
// OAS3 version, using `schema` sub-object
let oas3Param = {
value: param.value,
required: param.required,
schema: {
...param,
value: undefined,
required: undefined
}
}
result = validateParam( fromJS(oas3Param), false, true )
expect( result ).toEqual( expectedError )
}
it("should check the isOAS3 flag when validating parameters", function() {
// This should "skip" validation because there is no `schema.type` property
// and we are telling `validateParam` this is an OAS3 spec
param = fromJS({
required: false,
type: undefined,
value: ""
value: "",
required: true,
schema: {
notTheTypeProperty: "string"
}
})
result = validateParam( param, false )
result = validateParam( param, false, true )
expect( result ).toEqual( [] )
})
it("validates required strings", function() {
// invalid string
param = fromJS({
param = {
required: true,
type: "string",
value: ""
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
}
assertValidateParam(param, ["Required field is not provided"])
// valid string
param = fromJS({
param = {
required: true,
type: "string",
value: "test string"
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// valid string with min and max length
param = fromJS({
param = {
required: true,
type: "string",
value: "test string",
maxLength: 50,
minLength: 1
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
})
it("validates required strings with min and max length", function() {
// invalid string with max length
param = fromJS({
param = {
required: true,
type: "string",
value: "test string",
maxLength: 5
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be less than MaxLength"] )
}
assertValidateParam(param, ["Value must be less than MaxLength"])
// invalid string with max length 0
param = fromJS({
param = {
required: true,
type: "string",
value: "test string",
maxLength: 0
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be less than MaxLength"] )
}
assertValidateParam(param, ["Value must be less than MaxLength"])
// invalid string with min length
param = fromJS({
param = {
required: true,
type: "string",
value: "test string",
minLength: 50
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be greater than MinLength"] )
}
assertValidateParam(param, ["Value must be greater than MinLength"])
})
it("validates optional strings", function() {
// valid (empty) string
param = fromJS({
param = {
required: false,
type: "string",
value: ""
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// valid string
param = fromJS({
param = {
required: false,
type: "string",
value: "test"
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
})
it("validates required files", function() {
// invalid file
param = fromJS({
param = {
required: true,
type: "file",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
}
assertValidateParam(param, ["Required field is not provided"])
// valid file
param = fromJS({
param = {
required: true,
type: "file",
value: new win.File()
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
})
it("validates optional files", function() {
// invalid file
param = fromJS({
param = {
required: false,
type: "file",
value: "not a file"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be a file"] )
}
assertValidateParam(param, ["Value must be a file"])
// valid (empty) file
param = fromJS({
param = {
required: false,
type: "file",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// valid file
param = fromJS({
param = {
required: false,
type: "file",
value: new win.File()
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
})
it("validates required arrays", function() {
// invalid (empty) array
param = fromJS({
param = {
required: true,
type: "array",
value: []
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
}
assertValidateParam(param, ["Required field is not provided"])
// invalid (not an array)
param = fromJS({
param = {
required: true,
type: "array",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
}
assertValidateParam(param, ["Required field is not provided"])
// invalid array, items do not match correct type
param = fromJS({
param = {
required: true,
type: "array",
value: [1],
items: {
type: "string"
}
})
result = validateParam( param, false )
expect( result ).toEqual( [{index: 0, error: "Value must be a string"}] )
}
assertValidateParam(param, [{index: 0, error: "Value must be a string"}])
// valid array, with no 'type' for items
param = fromJS({
param = {
required: true,
type: "array",
value: ["1"]
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// valid array, items match type
param = fromJS({
param = {
required: true,
type: "array",
value: ["1"],
items: {
type: "string"
}
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
})
it("validates optional arrays", function() {
// valid, empty array
param = fromJS({
param = {
required: false,
type: "array",
value: []
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// invalid, items do not match correct type
param = fromJS({
param = {
required: false,
type: "array",
value: ["number"],
items: {
type: "number"
}
})
result = validateParam( param, false )
expect( result ).toEqual( [{index: 0, error: "Value must be a number"}] )
}
assertValidateParam(param, [{index: 0, error: "Value must be a number"}])
// valid
param = fromJS({
param = {
required: false,
type: "array",
value: ["test"],
items: {
type: "string"
}
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
})
it("validates required booleans", function() {
// invalid boolean value
param = fromJS({
param = {
required: true,
type: "boolean",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
}
assertValidateParam(param, ["Required field is not provided"])
// invalid boolean value (not a boolean)
param = fromJS({
param = {
required: true,
type: "boolean",
value: "test string"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
}
assertValidateParam(param, ["Required field is not provided"])
// valid boolean value
param = fromJS({
param = {
required: true,
type: "boolean",
value: "true"
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// valid boolean value
param = fromJS({
param = {
required: true,
type: "boolean",
value: false
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
})
it("validates optional booleans", function() {
// valid (empty) boolean value
param = fromJS({
param = {
required: false,
type: "boolean",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// invalid boolean value (not a boolean)
param = fromJS({
param = {
required: false,
type: "boolean",
value: "test string"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be a boolean"] )
}
assertValidateParam(param, ["Value must be a boolean"])
// valid boolean value
param = fromJS({
param = {
required: false,
type: "boolean",
value: "true"
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// valid boolean value
param = fromJS({
param = {
required: false,
type: "boolean",
value: false
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
})
it("validates required numbers", function() {
// invalid number, string instead of a number
param = fromJS({
param = {
required: true,
type: "number",
value: "test"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
}
assertValidateParam(param, ["Required field is not provided"])
// invalid number, undefined value
param = fromJS({
param = {
required: true,
type: "number",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
}
assertValidateParam(param, ["Required field is not provided"])
// valid number with min and max
param = fromJS({
param = {
required: true,
type: "number",
value: 10,
minimum: 5,
maximum: 99
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// valid negative number with min and max
param = fromJS({
param = {
required: true,
type: "number",
value: -10,
minimum: -50,
maximum: -5
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// invalid number with maximum:0
param = fromJS({
param = {
required: true,
type: "number",
value: 1,
maximum: 0
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be less than Maximum"] )
}
assertValidateParam(param, ["Value must be less than Maximum"])
// invalid number with minimum:0
param = fromJS({
param = {
required: true,
type: "number",
value: -10,
minimum: 0
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be greater than Minimum"] )
}
assertValidateParam(param, ["Value must be greater than Minimum"])
})
it("validates optional numbers", function() {
// invalid number, string instead of a number
param = fromJS({
param = {
required: false,
type: "number",
value: "test"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be a number"] )
}
assertValidateParam(param, ["Value must be a number"])
// valid (empty) number
param = fromJS({
param = {
required: false,
type: "number",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// valid number
param = fromJS({
param = {
required: false,
type: "number",
value: 10
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
})
it("validates required integers", function() {
// invalid integer, string instead of an integer
param = fromJS({
param = {
required: true,
type: "integer",
value: "test"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
}
assertValidateParam(param, ["Required field is not provided"])
// invalid integer, undefined value
param = fromJS({
param = {
required: true,
type: "integer",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
}
assertValidateParam(param, ["Required field is not provided"])
// valid integer
param = fromJS({
param = {
required: true,
type: "integer",
value: 10
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
})
it("validates optional integers", function() {
// invalid integer, string instead of an integer
param = fromJS({
param = {
required: false,
type: "integer",
value: "test"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be an integer"] )
}
assertValidateParam(param, ["Value must be an integer"])
// valid (empty) integer
param = fromJS({
param = {
required: false,
type: "integer",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
// integers
param = fromJS({
param = {
required: false,
type: "integer",
value: 10
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}
assertValidateParam(param, [])
})
})
@@ -908,4 +886,31 @@ describe("utils", function() {
expect(result).toEqual("hello\\#world")
})
})
describe("sanitizeUrl", function() {
it("should sanitize a `javascript:` url", function() {
const res = sanitizeUrl("javascript:alert('bam!')")
expect(res).toEqual("about:blank")
})
it("should sanitize a `data:` url", function() {
const res = sanitizeUrl(`data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGV
sbG8iKTs8L3NjcmlwdD4=`)
expect(res).toEqual("about:blank")
})
it("should not modify a `http:` url", function() {
const res = sanitizeUrl(`http://swagger.io/`)
expect(res).toEqual("http://swagger.io/")
})
it("should not modify a `https:` url", function() {
const res = sanitizeUrl(`https://swagger.io/`)
expect(res).toEqual("https://swagger.io/")
})
})
})
+33
View 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** &lt;script&gt;alert(1)&lt;/script&gt;")
})
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
View 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>`)
})
})
})
+1 -1
View File
@@ -11,7 +11,7 @@ let rules = [
name: "[name].js"
}
},
{ loader: "babel-loader" }
{ loader: "babel-loader?retainLines=true" }
]
}
]
+1 -1
View File
@@ -11,7 +11,7 @@ let rules = [
name: "[name].js"
}
},
{ loader: "babel-loader" }
{ loader: "babel-loader?retainLines=true" }
]
}
]
+1 -1
View File
@@ -13,7 +13,7 @@ let rules = [
name: "[name].js"
}
},
{ loader: "babel-loader" }
{ loader: "babel-loader?retainLines=true" }
]
}
]
+2 -8
View File
@@ -9,13 +9,7 @@ const rules = [
inline: true
}
},
{ loader: "babel-loader" }
]
},
{ test: /\.(jsx)(\?.*)?$/,
use: [
{ loader: "react-hot-loader" },
{ loader: "babel-loader" }
{ loader: "babel-loader?retainLines=true" }
]
},
{ test: /\.(css)(\?.*)?$/,
@@ -48,7 +42,7 @@ module.exports = require("./make-webpack-config")(rules, {
_special: {
separateStylesheets: false,
},
devtool: "eval",
devtool: "eval-source-map",
entry: {
"swagger-ui-bundle": [
"./src/polyfills",