Skip to content

Commit

Permalink
Merge pull request #2334 from bodnia/feature/xss
Browse files Browse the repository at this point in the history
fix for xss issue
  • Loading branch information
fehguy committed Aug 23, 2016
2 parents 826bfcc + a1aea70 commit a906cff
Show file tree
Hide file tree
Showing 28 changed files with 610 additions and 532 deletions.
5 changes: 5 additions & 0 deletions dist/css/print.css
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,11 @@
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a {
text-decoration: none;
}
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a .markdown p {
color: inherit;
padding: 0;
line-height: inherit;
}
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access {
color: black;
}
Expand Down
5 changes: 5 additions & 0 deletions dist/css/screen.css
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,11 @@
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a {
text-decoration: none;
}
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a .markdown p {
color: inherit;
padding: 0;
line-height: inherit;
}
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access {
color: black;
}
Expand Down
455 changes: 245 additions & 210 deletions dist/swagger-ui.js

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions dist/swagger-ui.min.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions src/main/html/css/print.css
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,11 @@
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a {
text-decoration: none;
}
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a .markdown p {
color: inherit;
padding: 0;
line-height: inherit;
}
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access {
color: black;
}
Expand Down
5 changes: 5 additions & 0 deletions src/main/html/css/screen.css
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,11 @@
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a {
text-decoration: none;
}
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a .markdown p {
color: inherit;
padding: 0;
line-height: inherit;
}
.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access {
color: black;
}
Expand Down
56 changes: 41 additions & 15 deletions src/main/javascript/helpers/handlebars.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,43 @@
'use strict';
/*jslint eqeq: true*/

Handlebars.registerHelper('sanitize', function(html) {
// Strip the script tags from the html, and return it as a Handlebars.SafeString
var _sanitize = function(html) {
// Strip the script tags from the html and inline evenhandlers
html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
return new Handlebars.SafeString(html);
});
html = html.replace(/(on\w+="[^"]*")*(on\w+='[^']*')*(on\w+=\w*\(\w*\))*/gi, '');

return html;
};

var sanitize =function (html) {
var _html;

if ( _.isUndefined(html) || _.isNull(html)) {
return new Handlebars.SafeString('');
}

if (_.isNumber(html)) {
return new Handlebars.SafeString(html);
}

if (_.isObject(html)){
_html = JSON.stringify(html);
return new Handlebars.SafeString(JSON.parse(_sanitize(_html)));
}

return new Handlebars.SafeString(_sanitize(html));
};

Handlebars.registerHelper('sanitize', sanitize);

Handlebars.registerHelper('renderTextParam', function(param) {
var result, type = 'text', idAtt = '';
var paramType = param.type || param.schema && param.schema.type || '';
var isArray = paramType.toLowerCase() === 'array' || param.allowMultiple;
var defaultValue = isArray && Array.isArray(param.default) ? param.default.join('\n') : param.default;
var name = Handlebars.Utils.escapeExpression(param.name);
var valueId = Handlebars.Utils.escapeExpression(param.valueId);
paramType = Handlebars.Utils.escapeExpression(paramType);

var dataVendorExtensions = Object.keys(param).filter(function(property) {
// filter X-data- properties
Expand All @@ -21,24 +47,18 @@ Handlebars.registerHelper('renderTextParam', function(param) {
return result += ' ' + property.substring(2, property.length) + '=\'' + param[property] + '\'';
}, '');

if (typeof defaultValue === 'undefined') {
defaultValue = '';
}

if(param.format && param.format === 'password') {
type = 'password';
}

if(param.valueId) {
idAtt = ' id=\'' + param.valueId + '\'';
if(valueId) {
idAtt = ' id=\'' + valueId + '\'';
}

if (typeof defaultValue === 'string' || defaultValue instanceof String) {
defaultValue = defaultValue.replace(/'/g,'&apos;');
}
defaultValue = sanitize(defaultValue);

if(isArray) {
result = '<textarea class=\'body-textarea' + (param.required ? ' required' : '') + '\' name=\'' + param.name + '\'' + idAtt + dataVendorExtensions;
result = '<textarea class=\'body-textarea' + (param.required ? ' required' : '') + '\' name=\'' + name + '\'' + idAtt + dataVendorExtensions;
result += ' placeholder=\'Provide multiple values in new lines' + (param.required ? ' (at least one required).' : '.') + '\'>';
result += defaultValue + '</textarea>';
} else {
Expand All @@ -47,7 +67,7 @@ Handlebars.registerHelper('renderTextParam', function(param) {
parameterClass += ' required';
}
result = '<input class=\'' + parameterClass + '\' minlength=\'' + (param.required ? 1 : 0) + '\'';
result += ' name=\'' + param.name +'\' placeholder=\'' + (param.required ? '(required)' : '') + '\'' + idAtt + dataVendorExtensions;
result += ' name=\'' + name +'\' placeholder=\'' + (param.required ? '(required)' : '') + '\'' + idAtt + dataVendorExtensions;
result += ' type=\'' + type + '\' value=\'' + defaultValue + '\'/>';
}
return new Handlebars.SafeString(result);
Expand Down Expand Up @@ -76,3 +96,9 @@ Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
return options.inverse(this);
}
});

Handlebars.registerHelper('escape', function (value) {
var text = Handlebars.Utils.escapeExpression(value);

return new Handlebars.SafeString(text);
});
8 changes: 8 additions & 0 deletions src/main/javascript/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,13 @@ window.SwaggerUi.utils = {
}

return result;
},

sanitize: function(html) {
// Strip the script tags from the html and inline evenhandlers
html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
html = html.replace(/(on\w+="[^"]*")*(on\w+='[^']*')*(on\w+=\w*\(\w*\))*/gi, '');

return html;
}
};
2 changes: 1 addition & 1 deletion src/main/javascript/view/MainView.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ SwaggerUi.Views.MainView = Backbone.View.extend({
id = id + '_' + counter;
counter += 1;
}
resource.id = id;
resource.id = SwaggerUi.utils.sanitize(id);
resources[id] = resource;
this.addResource(resource, this.model.auths);
}
Expand Down
5 changes: 5 additions & 0 deletions src/main/less/specs.less
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,11 @@
font-size: 0.9em;
a {
text-decoration: none;
.markdown p {
color: inherit;
padding: 0;
line-height: inherit;
}
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/main/template/apikey_auth.handlebars
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<div class="key_input_container">
<h3 class="auth__title">Api key authorization</h3>
<div class="auth__description">{{description}}</div>
<div class="auth__description">{{{sanitize description}}}</div>
<div>
<div class="key_auth__field">
<span class="key_auth__label">name:</span>
<span class="key_auth__value">{{name}}</span>
<span class="key_auth__value">{{{escape name}}}</span>
</div>
<div class="key_auth__field">
<span class="key_auth__label">in:</span>
<span class="key_auth__value">{{in}}</span>
<span class="key_auth__value">{{{escape in}}}</span>
</div>
<div class="key_auth__field">
<span class="key_auth__label">value:</span>
{{#if isLogout}}
<span class="key_auth__value">{{value}}</span>
<span class="key_auth__value">{{{sanitize value}}}</span>
{{else}}
<input placeholder="api_key" class="auth_input input_apiKey_entry" name="apiKey" type="text"/>
{{/if}}
Expand Down
2 changes: 1 addition & 1 deletion src/main/template/auth_button_operation.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{{#if scopes}}
<ul class="authorize-scopes">
{{#each scopes}}
<li class="authorize__scope" title="{{description}}">{{scope}}</li>
<li class="authorize__scope" title="{{{escape description}}}">{{{escape scope}}}</li>
{{/each}}
</ul>
{{/if}}
Expand Down
4 changes: 2 additions & 2 deletions src/main/template/basic_auth.handlebars
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<div class='basic_auth_container'>
<h3 class="auth__title">Basic authentication{{#if isLogout}} - authorized{{/if}}</h3>
<form class="basic_input_container">
<div class="auth__description">{{description}}</div>
<div class="auth__description">{{{sanitize description}}}</div>
<div class="auth_label">
<span class="basic_auth__label" data-sw-translate>username:</span>
{{#if isLogout}}
<span class="basic_auth__value">{{username}}</span>
<span class="basic_auth__value">{{{escape username}}}</span>
{{else}}
<input required placeholder="username" class="basic_auth__username auth_input" name="username" type="text"/>
{{/if}}
Expand Down
6 changes: 3 additions & 3 deletions src/main/template/content_type.handlebars
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<label data-sw-translate for="{{contentTypeId}}">Response Content Type</label>
<select name="contentType" id="{{contentTypeId}}">
<label data-sw-translate for="{{{escape contentTypeId}}}">Response Content Type</label>
<select name="contentType" id="{{{escape contentTypeId}}}">
{{#if produces}}
{{#each produces}}
<option value="{{this}}">{{this}}</option>
<option value="{{{sanitize this}}}">{{{sanitize this}}}</option>
{{/each}}
{{else}}
<option value="application/json">application/json</option>
Expand Down
24 changes: 12 additions & 12 deletions src/main/template/main.handlebars
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<div class='info' id='api_info'>
{{#if info}}
<div class="info_title">{{info.title}}</div>
<div class="info_description markdown">{{{info.description}}}</div>
<div class="info_title">{{{sanitize info.title}}}</div>
<div class="info_description markdown">{{{sanitize info.description}}}</div>
{{#if externalDocs}}
<p>{{externalDocs.description}}</p>
<a href="{{externalDocs.url}}" target="_blank">{{externalDocs.url}}</a>
<p>{{{sanitize externalDocs.description}}}</p>
<a href="{{{escape externalDocs.url}}}" target="_blank">{{{escape externalDocs.url}}}</a>
{{/if}}
{{#if info.termsOfServiceUrl}}<div class="info_tos"><a target="_blank" href="{{info.termsOfServiceUrl}}" data-sw-translate>Terms of service</a></div>{{/if}}
{{#if info.contact.name}}<div><div class='info_name' style="display: inline" data-sw-translate>Created by </div> {{info.contact.name}}</div>{{/if}}
{{#if info.contact.url}}<div class='info_url' data-sw-translate>See more at <a href="{{info.contact.url}}">{{info.contact.url}}</a></div>{{/if}}
{{#if info.contact.email}}<div class='info_email'><a target="_parent" href="mailto:{{info.contact.email}}?subject={{info.title}}" data-sw-translate>Contact the developer</a></div>{{/if}}
{{#if info.license}}<div class='info_license'><a target="_blank" href='{{info.license.url}}'>{{info.license.name}}</a></div>{{/if}}
{{#if info.termsOfServiceUrl}}<div class="info_tos"><a target="_blank" href="{{{escape info.termsOfServiceUrl}}}" data-sw-translate>Terms of service</a></div>{{/if}}
{{#if info.contact.name}}<div><div class='info_name' style="display: inline" data-sw-translate>Created by </div> {{{escape info.contact.name}}}</div>{{/if}}
{{#if info.contact.url}}<div class='info_url' data-sw-translate>See more at <a href="{{{escape info.contact.url}}}">{{{escape info.contact.url}}}</a></div>{{/if}}
{{#if info.contact.email}}<div class='info_email'><a target="_parent" href="mailto:{{{escape info.contact.email}}}?subject={{{escape info.title}}}" data-sw-translate>Contact the developer</a></div>{{/if}}
{{#if info.license}}<div class='info_license'><a target="_blank" href='{{{escape info.license.url}}}'>{{{escape info.license.name}}}</a></div>{{/if}}
{{/if}}
</div>
<div class='container' id='resources_container'>
Expand All @@ -19,12 +19,12 @@
<ul id='resources'></ul>

<div class="footer">
<h4 style="color: #999">[ <span style="font-variant: small-caps">base url</span>: {{basePath}}
<h4 style="color: #999">[ <span style="font-variant: small-caps">base url</span>: {{{escape basePath}}}
{{#if info.version}}
, <span style="font-variant: small-caps" data-sw-translate>api version</span>: {{info.version}}
, <span style="font-variant: small-caps" data-sw-translate>api version</span>: {{{escape info.version}}}
{{/if}}]
{{#if validatorUrl}}
<span style="float:right"><a target="_blank" href="{{validatorUrl}}/debug?url={{url}}"><img id="validator" src="{{validatorUrl}}?url={{url}}"></a>
<span style="float:right"><a target="_blank" href="{{{escape validatorUrl}}}/debug?url={{{escape url}}}"><img id="validator" src="{{{escape validatorUrl}}}?url={{{escape url}}}"></a>
</span>
{{/if}}
</h4>
Expand Down
16 changes: 8 additions & 8 deletions src/main/template/oauth2.handlebars
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<div>
<h3 class="auth__title">Select OAuth2.0 Scopes</h3>
<p>{{description}}</p>
<p>{{{sanitize description}}}</p>
<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.
<a href="#">Learn how to use</a>
</p>
<p><strong> {{appName}} </strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>
<p>Authorization URL: {{authorizationUrl}}</p>
<p>flow: {{flow}}</p>
<p><strong> {{{escape appName}}} </strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>
<p>Authorization URL: {{{sanitize authorizationUrl}}}</p>
<p>flow: {{{escape flow}}}</p>
<ul class="api-popup-scopes">
{{#each scopes}}
<li>
<input class="oauth-scope" type="checkbox" data-scope="{{scope}}" oauthtype="{{OAuthSchemeKey}}"/>
<label>{{scope}}</label><br/>
<span class="api-scope-desc">{{description}}
<input class="oauth-scope" type="checkbox" data-scope="{{{escape scope}}}" oauthtype="{{{escape OAuthSchemeKey}}}"/>
<label>{{{escape scope}}}</label><br/>
<span class="api-scope-desc">{{{escape description}}}
{{#if OAuthSchemeKey}}
({{OAuthSchemeKey}})
({{{escape OAuthSchemeKey}}})
{{/if}}
</span>
</li>
Expand Down
38 changes: 11 additions & 27 deletions src/main/template/operation.handlebars
Original file line number Diff line number Diff line change
@@ -1,51 +1,35 @@
<ul class='operations' >
<li class='{{method}} operation' id='{{parentId}}_{{nickname}}'>
<li class='{{{escape method}}} operation' id='{{{escape parentId}}}_{{{escape nickname}}}'>
<div class='heading'>
<h3>
<span class='http_method'>
<a href='#!/{{encodedParentId}}/{{nickname}}' class="toggleOperation">{{method}}</a>
<a href='#!/{{sanitize encodedParentId}}/{{sanitize nickname}}' class="toggleOperation">{{{escape method}}}</a>
</span>
<span class='path'>
<a href='#!/{{encodedParentId}}/{{nickname}}' class="toggleOperation {{#if deprecated}}deprecated{{/if}}">{{path}}</a>
<a href='#!/{{sanitize encodedParentId}}/{{sanitize nickname}}' class="toggleOperation {{#if deprecated}}deprecated{{/if}}">{{{escape path}}}</a>
</span>
</h3>
<ul class='options'>
<li>
<a href='#!/{{encodedParentId}}/{{nickname}}' class="toggleOperation">{{{summary}}}</a>
<a href='#!/{{sanitize encodedParentId}}/{{sanitize nickname}}' class="toggleOperation"><span class="markdown">{{{escape summary}}}</span></a>
</li>
</ul>
</div>
<div class='content' id='{{parentId}}_{{nickname}}_content' style='display:none'>
<div class='content' id='{{sanitize encodedParentId}}_{{sanitize nickname}}_content' style='display:none'>
{{#if deprecated}}
<h4><span data-sw-translate>Warning: Deprecated</span></h4>
{{/if}}
{{#if description}}
<h4><span data-sw-translate>Implementation Notes</span></h4>
<div class="markdown">{{{description}}}</div>
<div class="markdown">{{{sanitize description}}}</div>
{{/if}}
{{#if security}}
<div class='authorize-wrapper authorize-wrapper_operation'></div>
{{/if}}
{{!--{{#oauth}}--}}
{{!--<div class="auth">--}}
{{!--<span class="api-ic ic-error">{{/oauth}}--}}
{{!--{{#each oauth}}--}}
{{!--<div class="api_information_panel">--}}
{{!--{{#each this}}--}}
{{!--<div title='{{{this.description}}}'>{{this.scope}}</div>--}}
{{!--{{/each}}--}}
{{!--</div>--}}
{{!--{{/each}}--}}
{{!--{{#oauth}}</span></div>{{/oauth}}--}}
{{!--{{#oauth}}--}}
{{!--<div class='access'>--}}
{{!--<span class="api-ic ic-off" title="click to authenticate"></span>--}}
{{!--</div>--}}
{{!--{{/oauth}}--}}
{{#if type}}
<div class="response-class">
<h4><span data-sw-translate>Response Class</span> (<span data-sw-translate>Status</span> {{successCode}})</h4>
{{#if successDescription}}<div class="markdown">{{{successDescription}}}</div>{{/if}}
<h4><span data-sw-translate>Response Class</span> (<span data-sw-translate>Status</span> {{{escape successCode}}})</h4>
{{#if successDescription}}<div class="markdown">{{{sanitize successDescription}}}</div>{{/if}}
<p><span class="model-signature" /></p>
<br/>
<div class="response-content-type" />
Expand All @@ -67,9 +51,9 @@
{{#each headers}}
<tr>
<td>{{@key}}</td>
<td>{{this.description}}</td>
<td>{{this.type}}</td>
<td>{{this.other}}</td>
<td>{{{sanitize this.description}}}</td>
<td>{{{escape this.type}}}</td>
<td>{{{escape this.other}}}</td>
</tr>
{{/each}}
</tbody>
Expand Down
Loading

0 comments on commit a906cff

Please sign in to comment.