fix: prevent possible xss using untrusted-spec option

This commit is contained in:
Roman Hotsiy 2017-05-12 12:38:05 +03:00
parent 7a5d315e09
commit c0698bb215
No known key found for this signature in database
GPG Key ID: 5CB7B3ACABA57CB0
5 changed files with 13 additions and 7 deletions

View File

@ -131,6 +131,7 @@ ReDoc makes use of the following [vendor extensions](http://swagger.io/specifica
### `<redoc>` tag attributes ### `<redoc>` tag attributes
* `spec-url` - relative or absolute url to your spec file; * `spec-url` - relative or absolute url to your spec file;
* `untrusted-spec` - if set, the spec is considered untrusted and all HTML/markdown is sanitized to prevent XSS. **Disabled by default** for performance reasons. **Enable this option if you work with untrusted user data!**
* `scroll-y-offset` - If set, specifies a vertical scroll-offset. This is often useful when there are fixed positioned elements at the top of the page, such as navbars, headers etc; * `scroll-y-offset` - If set, specifies a vertical scroll-offset. This is often useful when there are fixed positioned elements at the top of the page, such as navbars, headers etc;
`scroll-y-offset` can be specified in various ways: `scroll-y-offset` can be specified in various ways:
* **number**: A fixed number of pixels to be used as offset; * **number**: A fixed number of pixels to be used as offset;

View File

@ -22,7 +22,7 @@
frameborder="0" scrolling="0" width="130px" height="30px"></iframe> frameborder="0" scrolling="0" width="130px" height="30px"></iframe>
</nav> </nav>
<redoc scroll-y-offset="body > nav" spec-url='swagger.yaml' lazy-rendering></redoc> <redoc scroll-y-offset="body > nav" spec-url='swagger.yaml' lazy-rendering untrusted-spec></redoc>
<script src="main.js"> </script> <script src="main.js"> </script>
<script src="./dist/redoc.min.js"> </script> <script src="./dist/redoc.min.js"> </script>

View File

@ -22,7 +22,7 @@
frameborder="0" scrolling="0" width="130px" height="30px"></iframe> frameborder="0" scrolling="0" width="130px" height="30px"></iframe>
</nav> </nav>
<redoc scroll-y-offset="body > nav" spec-url='swagger.yaml' lazy-rendering></redoc> <redoc scroll-y-offset="body > nav" spec-url='swagger.yaml' lazy-rendering untrusted-spec></redoc>
<script> <script>
window.__REDOC_DEV__ = true; window.__REDOC_DEV__ = true;

View File

@ -19,6 +19,7 @@ const OPTION_NAMES = new Set([
'requiredPropsFirst', 'requiredPropsFirst',
'noAutoAuth', 'noAutoAuth',
'pathInMiddlePanel', 'pathInMiddlePanel',
'untrustedSpec'
]); ]);
export interface Options { export interface Options {
@ -33,6 +34,7 @@ export interface Options {
requiredPropsFirst?: boolean; requiredPropsFirst?: boolean;
noAutoAuth?: boolean; noAutoAuth?: boolean;
pathInMiddlePanel?: boolean; pathInMiddlePanel?: boolean;
untrustedSpec?: boolean;
spec?: any; spec?: any;
} }
@ -101,6 +103,7 @@ export class OptionsService {
if (isString(this._options.requiredPropsFirst)) this._options.requiredPropsFirst = true; if (isString(this._options.requiredPropsFirst)) this._options.requiredPropsFirst = true;
if (isString(this._options.noAutoAuth)) this._options.noAutoAuth = true; if (isString(this._options.noAutoAuth)) this._options.noAutoAuth = true;
if (isString(this._options.pathInMiddlePanel)) this._options.pathInMiddlePanel = true; if (isString(this._options.pathInMiddlePanel)) this._options.pathInMiddlePanel = true;
if (isString(this._options.untrustedSpec)) this._options.untrustedSpec = true;
if (isString(this._options.expandResponses)) { if (isString(this._options.expandResponses)) {
let str = this._options.expandResponses as string; let str = this._options.expandResponses as string;
if (str === 'all') return; if (str === 'all') return;

View File

@ -6,6 +6,7 @@ import { isString, stringify, isBlank } from './helpers';
import JsonPointer from './JsonPointer'; import JsonPointer from './JsonPointer';
import { MdRenderer } from './'; import { MdRenderer } from './';
import { JsonFormatter } from './JsonFormatterPipe'; import { JsonFormatter } from './JsonFormatterPipe';
import { OptionsService } from '../services/options.service';
declare var Prism: any; declare var Prism: any;
@ -48,18 +49,19 @@ export class JsonPointerEscapePipe implements PipeTransform {
@Pipe({ name: 'marked' }) @Pipe({ name: 'marked' })
export class MarkedPipe implements PipeTransform { export class MarkedPipe implements PipeTransform {
renderer: MdRenderer; renderer: MdRenderer;
constructor(private sanitizer: DomSanitizer) { unstrustedSpec: boolean;
constructor(private sanitizer: DomSanitizer, optionsService: OptionsService) {
this.renderer = new MdRenderer(true); this.renderer = new MdRenderer(true);
this.unstrustedSpec = !!optionsService.options.untrustedSpec;
} }
transform(value:string) { transform(value:string) {
if (isBlank(value)) return value; if (isBlank(value)) return value;
if (!isString(value)) { if (!isString(value)) {
throw new InvalidPipeArgumentException(JsonPointerEscapePipe, value); throw new InvalidPipeArgumentException(JsonPointerEscapePipe, value);
} }
let res = `<span class="redoc-markdown-block">${this.renderer.renderMd(value)}</span>`;
return this.sanitizer.bypassSecurityTrustHtml( return this.unstrustedSpec ? res : this.sanitizer.bypassSecurityTrustHtml(res);
`<span class="redoc-markdown-block">${this.renderer.renderMd(value)}</span>`
);
} }
} }