UNPKG

@angular/core

Version:

Angular - the core framework

291 lines • 42.7 kB
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { XSS_SECURITY_URL } from '../error_details_base_url'; import { trustedHTMLFromString } from '../util/security/trusted_types'; import { getInertBodyHelper } from './inert_body'; import { _sanitizeUrl } from './url_sanitizer'; function tagSet(tags) { const res = {}; for (const t of tags.split(',')) res[t] = true; return res; } function merge(...sets) { const res = {}; for (const s of sets) { for (const v in s) { if (s.hasOwnProperty(v)) res[v] = true; } } return res; } // Good source of info about elements and attributes // https://html.spec.whatwg.org/#semantics // https://simon.html5.org/html-elements // Safe Void Elements - HTML5 // https://html.spec.whatwg.org/#void-elements const VOID_ELEMENTS = tagSet('area,br,col,hr,img,wbr'); // Elements that you can, intentionally, leave open (and which close themselves) // https://html.spec.whatwg.org/#optional-tags const OPTIONAL_END_TAG_BLOCK_ELEMENTS = tagSet('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr'); const OPTIONAL_END_TAG_INLINE_ELEMENTS = tagSet('rp,rt'); const OPTIONAL_END_TAG_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, OPTIONAL_END_TAG_BLOCK_ELEMENTS); // Safe Block Elements - HTML5 const BLOCK_ELEMENTS = merge(OPTIONAL_END_TAG_BLOCK_ELEMENTS, tagSet('address,article,' + 'aside,blockquote,caption,center,del,details,dialog,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' + 'h6,header,hgroup,hr,ins,main,map,menu,nav,ol,pre,section,summary,table,ul')); // Inline Elements - HTML5 const INLINE_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, tagSet('a,abbr,acronym,audio,b,' + 'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,picture,q,ruby,rp,rt,s,' + 'samp,small,source,span,strike,strong,sub,sup,time,track,tt,u,var,video')); export const VALID_ELEMENTS = merge(VOID_ELEMENTS, BLOCK_ELEMENTS, INLINE_ELEMENTS, OPTIONAL_END_TAG_ELEMENTS); // Attributes that have href and hence need to be sanitized export const URI_ATTRS = tagSet('background,cite,href,itemtype,longdesc,poster,src,xlink:href'); const HTML_ATTRS = tagSet('abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,' + 'compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,' + 'ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,' + 'scope,scrolling,shape,size,sizes,span,srclang,srcset,start,summary,tabindex,target,title,translate,type,usemap,' + 'valign,value,vspace,width'); // Accessibility attributes as per WAI-ARIA 1.1 (W3C Working Draft 14 December 2018) const ARIA_ATTRS = tagSet('aria-activedescendant,aria-atomic,aria-autocomplete,aria-busy,aria-checked,aria-colcount,aria-colindex,' + 'aria-colspan,aria-controls,aria-current,aria-describedby,aria-details,aria-disabled,aria-dropeffect,' + 'aria-errormessage,aria-expanded,aria-flowto,aria-grabbed,aria-haspopup,aria-hidden,aria-invalid,' + 'aria-keyshortcuts,aria-label,aria-labelledby,aria-level,aria-live,aria-modal,aria-multiline,' + 'aria-multiselectable,aria-orientation,aria-owns,aria-placeholder,aria-posinset,aria-pressed,aria-readonly,' + 'aria-relevant,aria-required,aria-roledescription,aria-rowcount,aria-rowindex,aria-rowspan,aria-selected,' + 'aria-setsize,aria-sort,aria-valuemax,aria-valuemin,aria-valuenow,aria-valuetext'); // NB: This currently consciously doesn't support SVG. SVG sanitization has had several security // issues in the past, so it seems safer to leave it out if possible. If support for binding SVG via // innerHTML is required, SVG attributes should be added here. // NB: Sanitization does not allow <form> elements or other active elements (<button> etc). Those // can be sanitized, but they increase security surface area without a legitimate use case, so they // are left out here. export const VALID_ATTRS = merge(URI_ATTRS, HTML_ATTRS, ARIA_ATTRS); // Elements whose content should not be traversed/preserved, if the elements themselves are invalid. // // Typically, `<invalid>Some content</invalid>` would traverse (and in this case preserve) // `Some content`, but strip `invalid-element` opening/closing tags. For some elements, though, we // don't want to preserve the content, if the elements themselves are going to be removed. const SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS = tagSet('script,style,template'); /** * SanitizingHtmlSerializer serializes a DOM fragment, stripping out any unsafe elements and unsafe * attributes. */ class SanitizingHtmlSerializer { constructor() { // Explicitly track if something was stripped, to avoid accidentally warning of sanitization just // because characters were re-encoded. this.sanitizedSomething = false; this.buf = []; } sanitizeChildren(el) { // This cannot use a TreeWalker, as it has to run on Angular's various DOM adapters. // However this code never accesses properties off of `document` before deleting its contents // again, so it shouldn't be vulnerable to DOM clobbering. let current = el.firstChild; let traverseContent = true; let parentNodes = []; while (current) { if (current.nodeType === Node.ELEMENT_NODE) { traverseContent = this.startElement(current); } else if (current.nodeType === Node.TEXT_NODE) { this.chars(current.nodeValue); } else { // Strip non-element, non-text nodes. this.sanitizedSomething = true; } if (traverseContent && current.firstChild) { // Push current node to the parent stack before entering its content. parentNodes.push(current); current = getFirstChild(current); continue; } while (current) { // Leaving the element. // Walk up and to the right, closing tags as we go. if (current.nodeType === Node.ELEMENT_NODE) { this.endElement(current); } let next = getNextSibling(current); if (next) { current = next; break; } // There was no next sibling, walk up to the parent node (extract it from the stack). current = parentNodes.pop(); } } return this.buf.join(''); } /** * Sanitizes an opening element tag (if valid) and returns whether the element's contents should * be traversed. Element content must always be traversed (even if the element itself is not * valid/safe), unless the element is one of `SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS`. * * @param element The element to sanitize. * @return True if the element's contents should be traversed. */ startElement(element) { const tagName = getNodeName(element).toLowerCase(); if (!VALID_ELEMENTS.hasOwnProperty(tagName)) { this.sanitizedSomething = true; return !SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS.hasOwnProperty(tagName); } this.buf.push('<'); this.buf.push(tagName); const elAttrs = element.attributes; for (let i = 0; i < elAttrs.length; i++) { const elAttr = elAttrs.item(i); const attrName = elAttr.name; const lower = attrName.toLowerCase(); if (!VALID_ATTRS.hasOwnProperty(lower)) { this.sanitizedSomething = true; continue; } let value = elAttr.value; // TODO(martinprobst): Special case image URIs for data:image/... if (URI_ATTRS[lower]) value = _sanitizeUrl(value); this.buf.push(' ', attrName, '="', encodeEntities(value), '"'); } this.buf.push('>'); return true; } endElement(current) { const tagName = getNodeName(current).toLowerCase(); if (VALID_ELEMENTS.hasOwnProperty(tagName) && !VOID_ELEMENTS.hasOwnProperty(tagName)) { this.buf.push('</'); this.buf.push(tagName); this.buf.push('>'); } } chars(chars) { this.buf.push(encodeEntities(chars)); } } /** * Verifies whether a given child node is a descendant of a given parent node. * It may not be the case when properties like `.firstChild` are clobbered and * accessing `.firstChild` results in an unexpected node returned. */ function isClobberedElement(parentNode, childNode) { return ((parentNode.compareDocumentPosition(childNode) & Node.DOCUMENT_POSITION_CONTAINED_BY) !== Node.DOCUMENT_POSITION_CONTAINED_BY); } /** * Retrieves next sibling node and makes sure that there is no * clobbering of the `nextSibling` property happening. */ function getNextSibling(node) { const nextSibling = node.nextSibling; // Make sure there is no `nextSibling` clobbering: navigating to // the next sibling and going back to the previous one should result // in the original node. if (nextSibling && node !== nextSibling.previousSibling) { throw clobberedElementError(nextSibling); } return nextSibling; } /** * Retrieves first child node and makes sure that there is no * clobbering of the `firstChild` property happening. */ function getFirstChild(node) { const firstChild = node.firstChild; if (firstChild && isClobberedElement(node, firstChild)) { throw clobberedElementError(firstChild); } return firstChild; } /** Gets a reasonable nodeName, even for clobbered nodes. */ export function getNodeName(node) { const nodeName = node.nodeName; // If the property is clobbered, assume it is an `HTMLFormElement`. return typeof nodeName === 'string' ? nodeName : 'FORM'; } function clobberedElementError(node) { return new Error(`Failed to sanitize html because the element is clobbered: ${node.outerHTML}`); } // Regular Expressions for parsing tags and attributes const SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g; // ! to ~ is the ASCII range. const NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g; /** * Escapes all potentially dangerous characters, so that the * resulting string can be safely inserted into attribute or * element text. * @param value */ function encodeEntities(value) { return value .replace(/&/g, '&amp;') .replace(SURROGATE_PAIR_REGEXP, function (match) { const hi = match.charCodeAt(0); const low = match.charCodeAt(1); return '&#' + ((hi - 0xd800) * 0x400 + (low - 0xdc00) + 0x10000) + ';'; }) .replace(NON_ALPHANUMERIC_REGEXP, function (match) { return '&#' + match.charCodeAt(0) + ';'; }) .replace(/</g, '&lt;') .replace(/>/g, '&gt;'); } let inertBodyHelper; /** * Sanitizes the given unsafe, untrusted HTML fragment, and returns HTML text that is safe to add to * the DOM in a browser environment. */ export function _sanitizeHtml(defaultDoc, unsafeHtmlInput) { let inertBodyElement = null; try { inertBodyHelper = inertBodyHelper || getInertBodyHelper(defaultDoc); // Make sure unsafeHtml is actually a string (TypeScript types are not enforced at runtime). let unsafeHtml = unsafeHtmlInput ? String(unsafeHtmlInput) : ''; inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml); // mXSS protection. Repeatedly parse the document to make sure it stabilizes, so that a browser // trying to auto-correct incorrect HTML cannot cause formerly inert HTML to become dangerous. let mXSSAttempts = 5; let parsedHtml = unsafeHtml; do { if (mXSSAttempts === 0) { throw new Error('Failed to sanitize html because the input is unstable'); } mXSSAttempts--; unsafeHtml = parsedHtml; parsedHtml = inertBodyElement.innerHTML; inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml); } while (unsafeHtml !== parsedHtml); const sanitizer = new SanitizingHtmlSerializer(); const safeHtml = sanitizer.sanitizeChildren(getTemplateContent(inertBodyElement) || inertBodyElement); if ((typeof ngDevMode === 'undefined' || ngDevMode) && sanitizer.sanitizedSomething) { console.warn(`WARNING: sanitizing HTML stripped some content, see ${XSS_SECURITY_URL}`); } return trustedHTMLFromString(safeHtml); } finally { // In case anything goes wrong, clear out inertElement to reset the entire DOM structure. if (inertBodyElement) { const parent = getTemplateContent(inertBodyElement) || inertBodyElement; while (parent.firstChild) { parent.removeChild(parent.firstChild); } } } } export function getTemplateContent(el) { return 'content' in el /** Microsoft/TypeScript#21517 */ && isTemplateElement(el) ? el.content : null; } function isTemplateElement(el) { return el.nodeType === Node.ELEMENT_NODE && el.nodeName === 'TEMPLATE'; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHRtbF9zYW5pdGl6ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9jb3JlL3NyYy9zYW5pdGl6YXRpb24vaHRtbF9zYW5pdGl6ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBRUgsT0FBTyxFQUFDLGdCQUFnQixFQUFDLE1BQU0sMkJBQTJCLENBQUM7QUFFM0QsT0FBTyxFQUFDLHFCQUFxQixFQUFDLE1BQU0sZ0NBQWdDLENBQUM7QUFFckUsT0FBTyxFQUFDLGtCQUFrQixFQUFrQixNQUFNLGNBQWMsQ0FBQztBQUNqRSxPQUFPLEVBQUMsWUFBWSxFQUFDLE1BQU0saUJBQWlCLENBQUM7QUFFN0MsU0FBUyxNQUFNLENBQUMsSUFBWTtJQUMxQixNQUFNLEdBQUcsR0FBMkIsRUFBRSxDQUFDO0lBQ3ZDLEtBQUssTUFBTSxDQUFDLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUM7UUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDO0lBQy9DLE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQztBQUVELFNBQVMsS0FBSyxDQUFDLEdBQUcsSUFBOEI7SUFDOUMsTUFBTSxHQUFHLEdBQTJCLEVBQUUsQ0FBQztJQUN2QyxLQUFLLE1BQU0sQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDO1FBQ3JCLEtBQUssTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDbEIsSUFBSSxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQztnQkFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDO1FBQ3pDLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxHQUFHLENBQUM7QUFDYixDQUFDO0FBRUQsb0RBQW9EO0FBQ3BELDBDQUEwQztBQUMxQyx3Q0FBd0M7QUFFeEMsNkJBQTZCO0FBQzdCLDhDQUE4QztBQUM5QyxNQUFNLGFBQWEsR0FB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QUNGLElBQUksQ0FBQyxPQUFPLFNBQVMsS0FBSyxXQUFXLElBQUksU0FBUyxDQUFDLElBQUksU0FBUyxDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDcEYsT0FBTyxDQUFDLElBQUksQ0FBQyx1REFBdUQsZ0JBQWdCLEVBQUUsQ0FBQyxDQUFDO1FBQzFGLENBQUM7UUFFRCxPQUFPLHFCQUFxQixDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3pDLENBQUM7WUFBUyxDQUFDO1FBQ1QseUZBQXlGO1FBQ3pGLElBQUksZ0JBQWdCLEVBQUUsQ0FBQztZQUNyQixNQUFNLE1BQU0sR0FBRyxrQkFBa0IsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLGdCQUFnQixDQUFDO1lBQ3hFLE9BQU8sTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUN6QixNQUFNLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUN4QyxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7QUFDSCxDQUFDO0FBRUQsTUFBTSxVQUFVLGtCQUFrQixDQUFDLEVBQVE7SUFDekMsT0FBTyxTQUFTLElBQUssRUFBVSxDQUFDLGlDQUFpQyxJQUFJLGlCQUFpQixDQUFDLEVBQUUsQ0FBQztRQUN4RixDQUFDLENBQUMsRUFBRSxDQUFDLE9BQU87UUFDWixDQUFDLENBQUMsSUFBSSxDQUFDO0FBQ1gsQ0FBQztBQUNELFNBQVMsaUJBQWlCLENBQUMsRUFBUTtJQUNqQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLEtBQUssSUFBSSxDQUFDLFlBQVksSUFBSSxFQUFFLENBQUMsUUFBUSxLQUFLLFVBQVUsQ0FBQztBQUN6RSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAbGljZW5zZVxuICogQ29weXJpZ2h0IEdvb2dsZSBMTEMgQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhbiBNSVQtc3R5bGUgbGljZW5zZSB0aGF0IGNhbiBiZVxuICogZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBhdCBodHRwczovL2FuZ3VsYXIuaW8vbGljZW5zZVxuICovXG5cbmltcG9ydCB7WFNTX1NFQ1VSSVRZX1VSTH0gZnJvbSAnLi4vZXJyb3JfZGV0YWlsc19iYXNlX3VybCc7XG5pbXBvcnQge1RydXN0ZWRIVE1MfSBmcm9tICcuLi91dGlsL3NlY3VyaXR5L3RydXN0ZWRfdHlwZV9kZWZzJztcbmltcG9ydCB7dHJ1c3RlZEhUTUxGcm9tU3RyaW5nfSBmcm9tICcuLi91dGlsL3NlY3VyaXR5L3RydXN0ZWRfdHlwZXMnO1xuXG5pbXBvcnQge2dldEluZXJ0Qm9keUhlbHBlciwgSW5lcnRCb2R5SGVscGVyfSBmcm9tICcuL2luZXJ0X2JvZHknO1xuaW1wb3J0IHtfc2FuaXRpemVVcmx9IGZyb20gJy4vdXJsX3Nhbml0aXplcic7XG5cbmZ1bmN0aW9uIHRhZ1NldCh0YWdzOiBzdHJpbmcpOiB7W2s6IHN0cmluZ106IGJvb2xlYW59IHtcbiAgY29uc3QgcmVzOiB7W2s6IHN0cmluZ106IGJvb2xlYW59ID0ge307XG4gIGZvciAoY29uc3QgdCBvZiB0YWdzLnNwbGl0KCcsJykpIHJlc1t0XSA9IHRydWU7XG4gIHJldHVybiByZXM7XG59XG5cbmZ1bmN0aW9uIG1lcmdlKC4uLnNldHM6IHtbazogc3RyaW5nXTogYm9vbGVhbn1bXSk6IHtbazogc3RyaW5nXTogYm9vbGVhbn0ge1xuICBjb25zdCByZXM6IHtbazogc3RyaW5nXTogYm9vbGVhbn0gPSB7fTtcbiAgZm9yIChjb25zdCBzIG9mIHNldHMpIHtcbiAgICBmb3IgKGNvbnN0IHYgaW4gcykge1xuICAgICAgaWYgKHMuaGFzT3duUHJvcGVydHkodikpIHJlc1t2XSA9IHRydWU7XG4gICAgfVxuICB9XG4gIHJldHVybiByZXM7XG59XG5cbi8vIEdvb2Qgc291cmNlIG9mIGluZm8gYWJvdXQgZWxlbWVudHMgYW5kIGF0dHJpYnV0ZXNcbi8vIGh0dHBzOi8vaHRtbC5zcGVjLndoYXR3Zy5vcmcvI3NlbWFudGljc1xuLy8gaHR0cHM6Ly9zaW1vbi5odG1sNS5vcmcvaHRtbC1lbGVtZW50c1xuXG4vLyBTYWZlIFZvaWQgRWxlbWVudHMgLSBIVE1MNVxuLy8gaHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy8jdm9pZC1lbGVtZW50c1xuY29uc3QgVk9JRF9FTEVNRU5UUyA9IHRhZ1NldCgnYXJlYSxicixjb2wsaHIsaW1nLHdicicpO1xuXG4vLyBFbGVtZW50cyB0aGF0IHlvdSBjYW4sIGludGVudGlvbmFsbHksIGxlYXZlIG9wZW4gKGFuZCB3aGljaCBjbG9zZSB0aGVtc2VsdmVzKVxuLy8gaHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy8jb3B0aW9uYWwtdGFnc1xuY29uc3QgT1BUSU9OQUxfRU5EX1RBR19CTE9DS19FTEVNRU5UUyA9IHRhZ1NldCgnY29sZ3JvdXAsZGQsZHQsbGkscCx0Ym9keSx0ZCx0Zm9vdCx0aCx0aGVhZCx0cicpO1xuY29uc3QgT1BUSU9OQUxfRU5EX1RBR19JTkxJTkVfRUxFTUVOVFMgPSB0YWdTZXQoJ3JwLHJ0Jyk7XG5jb25zdCBPUFRJT05BTF9FTkRfVEFHX0VMRU1FTlRTID0gbWVyZ2UoXG4gIE9QVElPTkFMX0VORF9UQUdfSU5MSU5FX0VMRU1FTlRTLFxuICBPUFRJT05BTF9FTkRfVEFHX0JMT0NLX0VMRU1FTlRTLFxuKTtcblxuLy8gU2FmZSBCbG9jayBFbGVtZW50cyAtIEhUTUw1XG5jb25zdCBCTE9DS19FTEVNRU5UUyA9IG1lcmdlKFxuICBPUFRJT05BTF9FTkRfVEFHX0JMT0NLX0VMRU1FTlRTLFxuICB0YWdTZXQoXG4gICAgJ2FkZHJlc3MsYXJ0aWNsZSwnICtcbiAgICAgICdhc2lkZSxibG9ja3F1b3RlLGNhcHRpb24sY2VudGVyLGRlbCxkZXRhaWxzLGRpYWxvZyxkaXIsZGl2LGRsLGZpZ3VyZSxmaWdjYXB0aW9uLGZvb3RlcixoMSxoMixoMyxoNCxoNSwnICtcbiAgICAgICdoNixoZWFkZXIsaGdyb3VwLGhyLGlucyxtYWluLG1hcCxtZW51LG5hdixvbCxwcmUsc2VjdGlvbixzdW1tYXJ5LHRhYmxlLHVsJyxcbiAgKSxcbik7XG5cbi8vIElubGluZSBFbGVtZW50cyAtIEhUTUw1XG5jb25zdCBJTkxJTkVfRUxFTUVOVFMgPSBtZXJnZShcbiAgT1BUSU9OQUxfRU5EX1RBR19JTkxJTkVfRUxFTUVOVFMsXG4gIHRhZ1NldChcbiAgICAnYSxhYmJyLGFjcm9ueW0sYXVkaW8sYiwnICtcbiAgICAgICdiZGksYmRvLGJpZyxicixjaXRlLGNvZGUsZGVsLGRmbixlbSxmb250LGksaW1nLGlucyxrYmQsbGFiZWwsbWFwLG1hcmsscGljdHVyZSxxLHJ1YnkscnAscnQscywnICtcbiAgICAgICdzYW1wLHNtYWxsLHNvdXJjZSxzcGFuLHN0cmlrZSxzdHJvbmcsc3ViLHN1cCx0aW1lLHRyYWNrLHR0LHUsdmFyLHZpZGVvJyxcbiAgKSxcbik7XG5cbmV4cG9ydCBjb25zdCBWQUxJRF9FTEVNRU5UUyA9IG1lcmdlKFxuICBWT0lEX0VMRU1FTlRTLFxuICBCTE9DS19FTEVNRU5UUyxcbiAgSU5MSU5FX0VMRU1FTlRTLFxuICBPUFRJT05BTF9FTkRfVEFHX0VMRU1FTlRTLFxuKTtcblxuLy8gQXR0cmlidXRlcyB0aGF0IGhhdmUgaHJlZiBhbmQgaGVuY2UgbmVlZCB0byBiZSBzYW5pdGl6ZWRcbmV4cG9ydCBjb25zdCBVUklfQVRUUlMgPSB0YWdTZXQoJ2JhY2tncm91bmQsY2l0ZSxocmVmLGl0ZW10eXBlLGxvbmdkZXNjLHBvc3RlcixzcmMseGxpbms6aHJlZicpO1xuXG5jb25zdCBIVE1MX0FUVFJTID0gdGFnU2V0KFxuICAnYWJicixhY2Nlc3NrZXksYWxpZ24sYWx0LGF1dG9wbGF5LGF4aXMsYmdjb2xvcixib3JkZXIsY2VsbHBhZGRpbmcsY2VsbHNwYWNpbmcsY2xhc3MsY2xlYXIsY29sb3IsY29scyxjb2xzcGFuLCcgK1xuICAgICdjb21wYWN0LGNvbnRyb2xzLGNvb3JkcyxkYXRldGltZSxkZWZhdWx0LGRpcixkb3dubG9hZCxmYWNlLGhlYWRlcnMsaGVpZ2h0LGhpZGRlbixocmVmbGFuZyxoc3BhY2UsJyArXG4gICAgJ2lzbWFwLGl0ZW1zY29wZSxpdGVtcHJvcCxraW5kLGxhYmVsLGxhbmcsbGFuZ3VhZ2UsbG9vcCxtZWRpYSxtdXRlZCxub2hyZWYsbm93cmFwLG9wZW4scHJlbG9hZCxyZWwscmV2LHJvbGUscm93cyxyb3dzcGFuLHJ1bGVzLCcgK1xuICAgICdzY29wZSxzY3JvbGxpbmcsc2hhcGUsc2l6ZSxzaXplcyxzcGFuLHNyY2xhbmcsc3Jjc2V0LHN0YXJ0LHN1bW1hcnksdGFiaW5kZXgsdGFyZ2V0LHRpdGxlLHRyYW5zbGF0ZSx0eXBlLHVzZW1hcCwnICtcbiAgICAndmFsaWduLHZhbHVlLHZzcGFjZSx3aWR0aCcsXG4pO1xuXG4vLyBBY2Nlc3NpYmlsaXR5IGF0dHJpYnV0ZXMgYXMgcGVyIFdBSS1BUklBIDEuMSAoVzNDIFdvcmtpbmcgRHJhZnQgMTQgRGVjZW1iZXIgMjAxOClcbmNvbnN0IEFSSUFfQVRUUlMgPSB0YWdTZXQoXG4gICdhcmlhLWFjdGl2ZWRlc2NlbmRhbnQsYXJpYS1hdG9taWMsYXJpYS1hdXRvY29tcGxldGUsYXJpYS1idXN5LGFyaWEtY2hlY2tlZCxhcmlhLWNvbGNvdW50LGFyaWEtY29saW5kZXgsJyArXG4gICAgJ2FyaWEtY29sc3BhbixhcmlhLWNvbnRyb2xzLGFyaWEtY3VycmVudCxhcmlhLWRlc2NyaWJlZGJ5LGFyaWEtZGV0YWlscyxhcmlhLWRpc2FibGVkLGFyaWEtZHJvcGVmZmVjdCwnICtcbiAgICAnYXJpYS1lcnJvcm1lc3NhZ2UsYXJpYS1leHBhbmRlZCxhcmlhLWZsb3d0byxhcmlhLWdyYWJiZWQsYXJpYS1oYXNwb3B1cCxhcmlhLWhpZGRlbixhcmlhLWludmFsaWQsJyArXG4gICAgJ2FyaWEta2V5c2hvcnRjdXRzLGFyaWEtbGFiZWwsYXJpYS1sYWJlbGxlZGJ5LGFyaWEtbGV2ZWwsYXJpYS1saXZlLGFyaWEtbW9kYWwsYXJpYS1tdWx0aWxpbmUsJyArXG4gICAgJ2FyaWEtbXVsdGlzZWxlY3RhYmxlLGFyaWEtb3JpZW50YXRpb24sYXJpYS1vd25zLGFyaWEtcGxhY2Vob2xkZXIsYXJpYS1wb3NpbnNldCxhcmlhLXByZXNzZWQsYXJpYS1yZWFkb25seSwnICtcbiAgICAnYXJpYS1yZWxldmFudCxhcmlhLXJlcXVpcmVkLGFyaWEtcm9sZWRlc2NyaXB0aW9uLGFyaWEtcm93Y291bnQsYXJpYS1yb3dpbmRleCxhcmlhLXJvd3NwYW4sYXJpYS1zZWxlY3RlZCwnICtcbiAgICAnYXJpYS1zZXRzaXplLGFyaWEtc29ydCxhcmlhLXZhbHVlbWF4LGFyaWEtdmFsdWVtaW4sYXJpYS12YWx1ZW5vdyxhcmlhLXZhbHVldGV4dCcsXG4pO1xuXG4vLyBOQjogVGhpcyBjdXJyZW50bHkgY29uc2Npb3VzbHkgZG9lc24ndCBzdXBwb3J0IFNWRy4gU1ZHIHNhbml0aXphdGlvbiBoYXMgaGFkIHNldmVyYWwgc2VjdXJpdHlcbi8vIGlzc3VlcyBpbiB0aGUgcGFzdCwgc28gaXQgc2VlbXMgc2FmZXIgdG8gbGVhdmUgaXQgb3V0IGlmIHBvc3NpYmxlLiBJZiBzdXBwb3J0IGZvciBiaW5kaW5nIFNWRyB2aWFcbi8vIGlubmVySFRNTCBpcyByZXF1aXJlZCwgU1ZHIGF0dHJpYnV0ZXMgc2hvdWxkIGJlIGFkZGVkIGhlcmUuXG5cbi8vIE5COiBTYW5pdGl6YXRpb24gZG9lcyBub3QgYWxsb3cgPGZvcm0+IGVsZW1lbnRzIG9yIG90aGVyIGFjdGl2ZSBlbGVtZW50cyAoPGJ1dHRvbj4gZXRjKS4gVGhvc2Vcbi8vIGNhbiBiZSBzYW5pdGl6ZWQsIGJ1dCB0aGV5IGluY3JlYXNlIHNlY3VyaXR5IHN1cmZhY2UgYXJlYSB3aXRob3V0IGEgbGVnaXRpbWF0ZSB1c2UgY2FzZSwgc28gdGhleVxuLy8gYXJlIGxlZnQgb3V0IGhlcmUuXG5cbmV4cG9ydCBjb25zdCBWQUxJRF9BVFRSUyA9IG1lcmdlKFVSSV9BVFRSUywgSFRNTF9BVFRSUywgQVJJQV9BVFRSUyk7XG5cbi8vIEVsZW1lbnRzIHdob3NlIGNvbnRlbnQgc2hvdWxkIG5vdCBiZSB0cmF2ZXJzZWQvcHJlc2VydmVkLCBpZiB0aGUgZWxlbWVudHMgdGhlbXNlbHZlcyBhcmUgaW52YWxpZC5cbi8vXG4vLyBUeXBpY2FsbHksIGA8aW52YWxpZD5Tb21lIGNvbnRlbnQ8L2ludmFsaWQ+YCB3b3VsZCB0cmF2ZXJzZSAoYW5kIGluIHRoaXMgY2FzZSBwcmVzZXJ2ZSlcbi8vIGBTb21lIGNvbnRlbnRgLCBidXQgc3RyaXAgYGludmFsaWQtZWxlbWVudGAgb3BlbmluZy9jbG9zaW5nIHRhZ3MuIEZvciBzb21lIGVsZW1lbnRzLCB0aG91Z2gsIHdlXG4vLyBkb24ndCB3YW50IHRvIHByZXNlcnZlIHRoZSBjb250ZW50LCBpZiB0aGUgZWxlbWVudHMgdGhlbXNlbHZlcyBhcmUgZ29pbmcgdG8gYmUgcmVtb3ZlZC5cbmNvbnN0IFNLSVBfVFJBVkVSU0lOR19DT05URU5UX0lGX0lOVkFMSURfRUxFTUVOVFMgPSB0YWdTZXQoJ3NjcmlwdCxzdHlsZSx0ZW1wbGF0ZScpO1xuXG4vKipcbiAqIFNhbml0aXppbmdIdG1sU2VyaWFsaXplciBzZXJpYWxpemVzIGEgRE9NIGZyYWdtZW50LCBzdHJpcHBpbmcgb3V0IGFueSB1bnNhZmUgZWxlbWVudHMgYW5kIHVuc2FmZVxuICogYXR0cmlidXRlcy5cbiAqL1xuY2xhc3MgU2FuaXRpemluZ0h0bWxTZXJpYWxpemVyIHtcbiAgLy8gRXhwbGljaXRseSB0cmFjayBpZiBzb21ldGhpbmcgd2FzIHN0cmlwcGVkLCB0byBhdm9pZCBhY2NpZGVudGFsbHkgd2FybmluZyBvZiBzYW5pdGl6YXRpb24ganVzdFxuICAvLyBiZWNhdXNlIGNoYXJhY3RlcnMgd2VyZSByZS1lbmNvZGVkLlxuICBwdWJsaWMgc2FuaXRpemVkU29tZXRoaW5nID0gZmFsc2U7XG4gIHByaXZhdGUgYnVmOiBzdHJpbmdbXSA9IFtdO1xuXG4gIHNhbml0aXplQ2hpbGRyZW4oZWw6IEVsZW1lbnQpOiBzdHJpbmcge1xuICAgIC8vIFRoaXMgY2Fubm90IHVzZSBhIFRyZWVXYWxrZXIsIGFzIGl0IGhhcyB0byBydW4gb24gQW5ndWxhcidzIHZhcmlvdXMgRE9NIGFkYXB0ZXJzLlxuICAgIC8vIEhvd2V2ZXIgdGhpcyBjb2RlIG5ldmVyIGFjY2Vzc2VzIHByb3BlcnRpZXMgb2ZmIG9mIGBkb2N1bWVudGAgYmVmb3JlIGRlbGV0aW5nIGl0cyBjb250ZW50c1xuICAgIC8vIGFnYWluLCBzbyBpdCBzaG91bGRuJ3QgYmUgdnVsbmVyYWJsZSB0byBET00gY2xvYmJlcmluZy5cbiAgICBsZXQgY3VycmVudDogTm9kZSA9IGVsLmZpcnN0Q2hpbGQhO1xuICAgIGxldCB0cmF2ZXJzZUNvbnRlbnQgPSB0cnVlO1xuICAgIGxldCBwYXJlbnROb2RlcyA9IFtdO1xuICAgIHdoaWxlIChjdXJyZW50KSB7XG4gICAgICBpZiAoY3VycmVudC5ub2RlVHlwZSA9PT0gTm9kZS5FTEVNRU5UX05PREUpIHtcbiAgICAgICAgdHJhdmVyc2VDb250ZW50ID0gdGhpcy5zdGFydEVsZW1lbnQoY3VycmVudCBhcyBFbGVtZW50KTtcbiAgICAgIH0gZWxzZSBpZiAoY3VycmVudC5ub2RlVHlwZSA9PT0gTm9kZS5URVhUX05PREUpIHtcbiAgICAgICAgdGhpcy5jaGFycyhjdXJyZW50Lm5vZGVWYWx1ZSEpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gU3RyaXAgbm9uLWVsZW1lbnQsIG5vbi10ZXh0IG5vZGVzLlxuICAgICAgICB0aGlzLnNhbml0aXplZFNvbWV0aGluZyA9IHRydWU7XG4gICAgICB9XG4gICAgICBpZiAodHJhdmVyc2VDb250ZW50ICYmIGN1cnJlbnQuZmlyc3RDaGlsZCkge1xuICAgICAgICAvLyBQdXNoIGN1cnJlbnQgbm9kZSB0byB0aGUgcGFyZW50IHN0YWNrIGJlZm9yZSBlbnRlcmluZyBpdHMgY29udGVudC5cbiAgICAgICAgcGFyZW50Tm9kZXMucHVzaChjdXJyZW50KTtcbiAgICAgICAgY3VycmVudCA9IGdldEZpcnN0Q2hpbGQoY3VycmVudCkhO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIHdoaWxlIChjdXJyZW50KSB7XG4gICAgICAgIC8vIExlYXZpbmcgdGhlIGVsZW1lbnQuXG4gICAgICAgIC8vIFdhbGsgdXAgYW5kIHRvIHRoZSByaWdodCwgY2xvc2luZyB0YWdzIGFzIHdlIGdvLlxuICAgICAgICBpZiAoY3VycmVudC5ub2RlVHlwZSA9PT0gTm9kZS5FTEVNRU5UX05PREUpIHtcbiAgICAgICAgICB0aGlzLmVuZEVsZW1lbnQoY3VycmVudCBhcyBFbGVtZW50KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBuZXh0ID0gZ2V0TmV4dFNpYmxpbmcoY3VycmVudCkhO1xuXG4gICAgICAgIGlmIChuZXh0KSB7XG4gICAgICAgICAgY3VycmVudCA9IG5leHQ7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBUaGVyZSB3YXMgbm8gbmV4dCBzaWJsaW5nLCB3YWxrIHVwIHRvIHRoZSBwYXJlbnQgbm9kZSAoZXh0cmFjdCBpdCBmcm9tIHRoZSBzdGFjaykuXG4gICAgICAgIGN1cnJlbnQgPSBwYXJlbnROb2Rlcy5wb3AoKSE7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmJ1Zi5qb2luKCcnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTYW5pdGl6ZXMgYW4gb3BlbmluZyBlbGVtZW50IHRhZyAoaWYgdmFsaWQpIGFuZCByZXR1cm5zIHdoZXRoZXIgdGhlIGVsZW1lbnQncyBjb250ZW50cyBzaG91bGRcbiAgICogYmUgdHJhdmVyc2VkLiBFbGVtZW50IGNvbnRlbnQgbXVzdCBhbHdheXMgYmUgdHJhdmVyc2VkIChldmVuIGlmIHRoZSBlbGVtZW50IGl0c2VsZiBpcyBub3RcbiAgICogdmFsaWQvc2FmZSksIHVubGVzcyB0aGUgZWxlbWVudCBpcyBvbmUgb2YgYFNLSVBfVFJBVkVSU0lOR19DT05URU5UX0lGX0lOVkFMSURfRUxFTUVOVFNgLlxuICAgKlxuICAgKiBAcGFyYW0gZWxlbWVudCBUaGUgZWxlbWVudCB0byBzYW5pdGl6ZS5cbiAgICogQHJldHVybiBUcnVlIGlmIHRoZSBlbGVtZW50J3MgY29udGVudHMgc2hvdWxkIGJlIHRyYXZlcnNlZC5cbiAgICovXG4gIHByaXZhdGUgc3RhcnRFbGVtZW50KGVsZW1lbnQ6IEVsZW1lbnQpOiBib29sZWFuIHtcbiAgICBjb25zdCB0YWdOYW1lID0gZ2V0Tm9kZU5hbWUoZWxlbWVudCkudG9Mb3dlckNhc2UoKTtcbiAgICBpZiAoIVZBTElEX0VMRU1FTlRTLmhhc093blByb3BlcnR5KHRhZ05hbWUpKSB7XG4gICAgICB0aGlzLnNhbml0aXplZFNvbWV0aGluZyA9IHRydWU7XG4gICAgICByZXR1cm4gIVNLSVBfVFJBVkVSU0lOR19DT05URU5UX0lGX0lOVkFMSURfRUxFTUVOVFMuaGFzT3duUHJvcGVydHkodGFnTmFtZSk7XG4gICAgfVxuICAgIHRoaXMuYnVmLnB1c2goJzwnKTtcbiAgICB0aGlzLmJ1Zi5wdXNoKHRhZ05hbWUpO1xuICAgIGNvbnN0IGVsQXR0cnMgPSBlbGVtZW50LmF0dHJpYnV0ZXM7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBlbEF0dHJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBlbEF0dHIgPSBlbEF0dHJzLml0ZW0oaSk7XG4gICAgICBjb25zdCBhdHRyTmFtZSA9IGVsQXR0ciEubmFtZTtcbiAgICAgIGNvbnN0IGxvd2VyID0gYXR0ck5hbWUudG9Mb3dlckNhc2UoKTtcbiAgICAgIGlmICghVkFMSURfQVRUUlMuaGFzT3duUHJvcGVydHkobG93ZXIpKSB7XG4gICAgICAgIHRoaXMuc2FuaXRpemVkU29tZXRoaW5nID0gdHJ1ZTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBsZXQgdmFsdWUgPSBlbEF0dHIhLnZhbHVlO1xuICAgICAgLy8gVE9ETyhtYXJ0aW5wcm9ic3QpOiBTcGVjaWFsIGNhc2UgaW1hZ2UgVVJJcyBmb3IgZGF0YTppbWFnZS8uLi5cbiAgICAgIGlmIChVUklfQVRUUlNbbG93ZXJdKSB2YWx1ZSA9IF9zYW5pdGl6ZVVybCh2YWx1ZSk7XG4gICAgICB0aGlzLmJ1Zi5wdXNoKCcgJywgYXR0ck5hbWUsICc9XCInLCBlbmNvZGVFbnRpdGllcyh2YWx1ZSksICdcIicpO1xuICAgIH1cbiAgICB0aGlzLmJ1Zi5wdXNoKCc+Jyk7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICBwcml2YXRlIGVuZEVsZW1lbnQoY3VycmVudDogRWxlbWVudCkge1xuICAgIGNvbnN0IHRhZ05hbWUgPSBnZXROb2RlTmFtZShjdXJyZW50KS50b0xvd2VyQ2FzZSgpO1xuICAgIGlmIChWQUxJRF9FTEVNRU5UUy5oYXNPd25Qcm9wZXJ0eSh0YWdOYW1lKSAmJiAhVk9JRF9FTEVNRU5UUy5oYXNPd25Qcm9wZXJ0eSh0YWdOYW1lKSkge1xuICAgICAgdGhpcy5idWYucHVzaCgnPC8nKTtcbiAgICAgIHRoaXMuYnVmLnB1c2godGFnTmFtZSk7XG4gICAgICB0aGlzLmJ1Zi5wdXNoKCc+Jyk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBjaGFycyhjaGFyczogc3RyaW5nKSB7XG4gICAgdGhpcy5idWYucHVzaChlbmNvZGVFbnRpdGllcyhjaGFycykpO1xuICB9XG59XG5cbi8qKlxuICogVmVyaWZpZXMgd2hldGhlciBhIGdpdmVuIGNoaWxkIG5vZGUgaXMgYSBkZXNjZW5kYW50IG9mIGEgZ2l2ZW4gcGFyZW50IG5vZGUuXG4gKiBJdCBtYXkgbm90IGJlIHRoZSBjYXNlIHdoZW4gcHJvcGVydGllcyBsaWtlIGAuZmlyc3RDaGlsZGAgYXJlIGNsb2JiZXJlZCBhbmRcbiAqIGFjY2Vzc2luZyBgLmZpcnN0Q2hpbGRgIHJlc3VsdHMgaW4gYW4gdW5leHBlY3RlZCBub2RlIHJldHVybmVkLlxuICovXG5mdW5jdGlvbiBpc0Nsb2JiZXJlZEVsZW1lbnQocGFyZW50Tm9kZTogTm9kZSwgY2hpbGROb2RlOiBOb2RlKTogYm9vbGVhbiB7XG4gIHJldHVybiAoXG4gICAgKHBhcmVudE5vZGUuY29tcGFyZURvY3VtZW50UG9zaXRpb24oY2hpbGROb2RlKSAmIE5vZGUuRE9DVU1FTlRfUE9TSVRJT05fQ09OVEFJTkVEX0JZKSAhPT1cbiAgICBOb2RlLkRPQ1VNRU5UX1BPU0lUSU9OX0NPTlRBSU5FRF9CWVxuICApO1xufVxuXG4vKipcbiAqIFJldHJpZXZlcyBuZXh0IHNpYmxpbmcgbm9kZSBhbmQgbWFrZXMgc3VyZSB0aGF0IHRoZXJlIGlzIG5vXG4gKiBjbG9iYmVyaW5nIG9mIHRoZSBgbmV4dFNpYmxpbmdgIHByb3BlcnR5IGhhcHBlbmluZy5cbiAqL1xuZnVuY3Rpb24gZ2V0TmV4dFNpYmxpbmcobm9kZTogTm9kZSk6IE5vZGUgfCBudWxsIHtcbiAgY29uc3QgbmV4dFNpYmxpbmcgPSBub2RlLm5leHRTaWJsaW5nO1xuICAvLyBNYWtlIHN1cmUgdGhlcmUgaXMgbm8gYG5leHRTaWJsaW5nYCBjbG9iYmVyaW5nOiBuYXZpZ2F0aW5nIHRvXG4gIC8vIHRoZSBuZXh0IHNpYmxpbmcgYW5kIGdvaW5nIGJhY2sgdG8gdGhlIHByZXZpb3VzIG9uZSBzaG91bGQgcmVzdWx0XG4gIC8vIGluIHRoZSBvcmlnaW5hbCBub2RlLlxuICBpZiAobmV4dFNpYmxpbmcgJiYgbm9kZSAhPT0gbmV4dFNpYmxpbmcucHJldmlvdXNTaWJsaW5nKSB7XG4gICAgdGhyb3cgY2xvYmJlcmVkRWxlbWVudEVycm9yKG5leHRTaWJsaW5nKTtcbiAgfVxuICByZXR1cm4gbmV4dFNpYmxpbmc7XG59XG5cbi8qKlxuICogUmV0cmlldmVzIGZpcnN0IGNoaWxkIG5vZGUgYW5kIG1ha2VzIHN1cmUgdGhhdCB0aGVyZSBpcyBub1xuICogY2xvYmJlcmluZyBvZiB0aGUgYGZpcnN0Q2hpbGRgIHByb3BlcnR5IGhhcHBlbmluZy5cbiAqL1xuZnVuY3Rpb24gZ2V0Rmlyc3RDaGlsZChub2RlOiBOb2RlKTogTm9kZSB8IG51bGwge1xuICBjb25zdCBmaXJzdENoaWxkID0gbm9kZS5maXJzdENoaWxkO1xuICBpZiAoZmlyc3RDaGlsZCAmJiBpc0Nsb2JiZXJlZEVsZW1lbnQobm9kZSwgZmlyc3RDaGlsZCkpIHtcbiAgICB0aHJvdyBjbG9iYmVyZWRFbGVtZW50RXJyb3IoZmlyc3RDaGlsZCk7XG4gIH1cbiAgcmV0dXJuIGZpcnN0Q2hpbGQ7XG59XG5cbi8qKiBHZXRzIGEgcmVhc29uYWJsZSBub2RlTmFtZSwgZXZlbiBmb3IgY2xvYmJlcmVkIG5vZGVzLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldE5vZGVOYW1lKG5vZGU6IE5vZGUpOiBzdHJpbmcge1xuICBjb25zdCBub2RlTmFtZSA9IG5vZGUubm9kZU5hbWU7XG4gIC8vIElmIHRoZSBwcm9wZXJ0eSBpcyBjbG9iYmVyZWQsIGFzc3VtZSBpdCBpcyBhbiBgSFRNTEZvcm1FbGVtZW50YC5cbiAgcmV0dXJuIHR5cGVvZiBub2RlTmFtZSA9PT0gJ3N0cmluZycgPyBub2RlTmFtZSA6ICdGT1JNJztcbn1cblxuZnVuY3Rpb24gY2xvYmJlcmVkRWxlbWVudEVycm9yKG5vZGU6IE5vZGUpIHtcbiAgcmV0dXJuIG5ldyBFcnJvcihcbiAgICBgRmFpbGVkIHRvIHNhbml0aXplIGh0bWwgYmVjYXVzZSB0aGUgZWxlbWVudCBpcyBjbG9iYmVyZWQ6ICR7KG5vZGUgYXMgRWxlbWVudCkub3V0ZXJIVE1MfWAsXG4gICk7XG59XG5cbi8vIFJlZ3VsYXIgRXhwcmVzc2lvbnMgZm9yIHBhcnNpbmcgdGFncyBhbmQgYXR0cmlidXRlc1xuY29uc3QgU1VSUk9HQVRFX1BBSVJfUkVHRVhQID0gL1tcXHVEODAwLVxcdURCRkZdW1xcdURDMDAtXFx1REZGRl0vZztcbi8vICEgdG8gfiBpcyB0aGUgQVNDSUkgcmFuZ2UuXG5jb25zdCBOT05fQUxQSEFOVU1FUklDX1JFR0VYUCA9IC8oW15cXCMtfiB8IV0pL2c7XG5cbi8qKlxuICogRXNjYXBlcyBhbGwgcG90ZW50aWFsbHkgZGFuZ2Vyb3VzIGNoYXJhY3RlcnMsIHNvIHRoYXQgdGhlXG4gKiByZXN1bHRpbmcgc3RyaW5nIGNhbiBiZSBzYWZlbHkgaW5zZXJ0ZWQgaW50byBhdHRyaWJ1dGUgb3JcbiAqIGVsZW1lbnQgdGV4dC5cbiAqIEBwYXJhbSB2YWx1ZVxuICovXG5mdW5jdGlvbiBlbmNvZGVFbnRpdGllcyh2YWx1ZTogc3RyaW5nKSB7XG4gIHJldHVybiB2YWx1ZVxuICAgIC5yZXBsYWNlKC8mL2csICcmYW1wOycpXG4gICAgLnJlcGxhY2UoU1VSUk9HQVRFX1BBSVJfUkVHRVhQLCBmdW5jdGlvbiAobWF0Y2g6IHN0cmluZykge1xuICAgICAgY29uc3QgaGkgPSBtYXRjaC5jaGFyQ29kZUF0KDApO1xuICAgICAgY29uc3QgbG93ID0gbWF0Y2guY2hhckNvZGVBdCgxKTtcbiAgICAgIHJldHVybiAnJiMnICsgKChoaSAtIDB4ZDgwMCkgKiAweDQwMCArIChsb3cgLSAweGRjMDApICsgMHgxMDAwMCkgKyAnOyc7XG4gICAgfSlcbiAgICAucmVwbGFjZShOT05fQUxQSEFOVU1FUklDX1JFR0VYUCwgZnVuY3Rpb24gKG1hdGNoOiBzdHJpbmcpIHtcbiAgICAgIHJldHVybiAnJiMnICsgbWF0Y2guY2hhckNvZGVBdCgwKSArICc7JztcbiAgICB9KVxuICAgIC5yZXBsYWNlKC88L2csICcmbHQ7JylcbiAgICAucmVwbGFjZSgvPi9nLCAnJmd0OycpO1xufVxuXG5sZXQgaW5lcnRCb2R5SGVscGVyOiBJbmVydEJvZHlIZWxwZXI7XG5cbi8qKlxuICogU2FuaXRpemVzIHRoZSBnaXZlbiB1bnNhZmUsIHVudHJ1c3RlZCBIVE1MIGZyYWdtZW50LCBhbmQgcmV0dXJucyBIVE1MIHRleHQgdGhhdCBpcyBzYWZlIHRvIGFkZCB0b1xuICogdGhlIERPTSBpbiBhIGJyb3dzZXIgZW52aXJvbm1lbnQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBfc2FuaXRpemVIdG1sKGRlZmF1bHREb2M6IGFueSwgdW5zYWZlSHRtbElucHV0OiBzdHJpbmcpOiBUcnVzdGVkSFRNTCB8IHN0cmluZyB7XG4gIGxldCBpbmVydEJvZHlFbGVtZW50OiBIVE1MRWxlbWVudCB8IG51bGwgPSBudWxsO1xuICB0cnkge1xuICAgIGluZXJ0Qm9keUhlbHBlciA9IGluZXJ0Qm9keUhlbHBlciB8fCBnZXRJbmVydEJvZHlIZWxwZXIoZGVmYXVsdERvYyk7XG4gICAgLy8gTWFrZSBzdXJlIHVuc2FmZUh0bWwgaXMgYWN0dWFsbHkgYSBzdHJpbmcgKFR5cGVTY3JpcHQgdHlwZXMgYXJlIG5vdCBlbmZvcmNlZCBhdCBydW50aW1lKS5cbiAgICBsZXQgdW5zYWZlSHRtbCA9IHVuc2FmZUh0bWxJbnB1dCA/IFN0cmluZyh1bnNhZmVIdG1sSW5wdXQpIDogJyc7XG4gICAgaW5lcnRCb2R5RWxlbWVudCA9IGluZXJ0Qm9keUhlbHBlci5nZXRJbmVydEJvZHlFbGVtZW50KHVuc2FmZUh0bWwpO1xuXG4gICAgLy8gbVhTUyBwcm90ZWN0aW9uLiBSZXBlYXRlZGx5IHBhcnNlIHRoZSBkb2N1bWVudCB0byBtYWtlIHN1cmUgaXQgc3RhYmlsaXplcywgc28gdGhhdCBhIGJyb3dzZXJcbiAgICAvLyB0cnlpbmcgdG8gYXV0by1jb3JyZWN0IGluY29ycmVjdCBIVE1MIGNhbm5vdCBjYXVzZSBmb3JtZXJseSBpbmVydCBIVE1MIHRvIGJlY29tZSBkYW5nZXJvdXMuXG4gICAgbGV0IG1YU1NBdHRlbXB0cyA9IDU7XG4gICAgbGV0IHBhcnNlZEh0bWwgPSB1bnNhZmVIdG1sO1xuXG4gICAgZG8ge1xuICAgICAgaWYgKG1YU1NBdHRlbXB0cyA9PT0gMCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ZhaWxlZCB0byBzYW5pdGl6ZSBodG1sIGJlY2F1c2UgdGhlIGlucHV0IGlzIHVuc3RhYmxlJyk7XG4gICAgICB9XG4gICAgICBtWFNTQXR0ZW1wdHMtLTtcblxuICAgICAgdW5zYWZlSHRtbCA9IHBhcnNlZEh0bWw7XG4gICAgICBwYXJzZWRIdG1sID0gaW5lcnRCb2R5RWxlbWVudCEuaW5uZXJIVE1MO1xuICAgICAgaW5lcnRCb2R5RWxlbWVudCA9IGluZXJ0Qm9keUhlbHBlci5nZXRJbmVydEJvZHlFbGVtZW50KHVuc2FmZUh0bWwpO1xuICAgIH0gd2hpbGUgKHVuc2FmZUh0bWwgIT09IHBhcnNlZEh0bWwpO1xuXG4gICAgY29uc3Qgc2FuaXRpemVyID0gbmV3IFNhbml0aXppbmdIdG1sU2VyaWFsaXplcigpO1xuICAgIGNvbnN0IHNhZmVIdG1sID0gc2FuaXRpemVyLnNhbml0aXplQ2hpbGRyZW4oXG4gICAgICAoZ2V0VGVtcGxhdGVDb250ZW50KGluZXJ0Qm9keUVsZW1lbnQhKSBhcyBFbGVtZW50KSB8fCBpbmVydEJvZHlFbGVtZW50LFxuICAgICk7XG4gICAgaWYgKCh0eXBlb2YgbmdEZXZNb2RlID09PSAndW5kZWZpbmVkJyB8fCBuZ0Rldk1vZGUpICYmIHNhbml0aXplci5zYW5pdGl6ZWRTb21ldGhpbmcpIHtcbiAgICAgIGNvbnNvbGUud2FybihgV0FSTklORzogc2FuaXRpemluZyBIVE1MIHN0cmlwcGVkIHNvbWUgY29udGVudCwgc2VlICR7WFNTX1NFQ1VSSVRZX1VSTH1gKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1c3RlZEhUTUxGcm9tU3RyaW5nKHNhZmVIdG1sKTtcbiAgfSBmaW5hbGx5IHtcbiAgICAvLyBJbiBjYXNlIGFueXRoaW5nIGdvZXMgd3JvbmcsIGNsZWFyIG91dCBpbmVydEVsZW1lbnQgdG8gcmVzZXQgdGhlIGVudGlyZSBET00gc3RydWN0dXJlLlxuICAgIGlmIChpbmVydEJvZHlFbGVtZW50KSB7XG4gICAgICBjb25zdCBwYXJlbnQgPSBnZXRUZW1wbGF0ZUNvbnRlbnQoaW5lcnRCb2R5RWxlbWVudCkgfHwgaW5lcnRCb2R5RWxlbWVudDtcbiAgICAgIHdoaWxlIChwYXJlbnQuZmlyc3RDaGlsZCkge1xuICAgICAgICBwYXJlbnQucmVtb3ZlQ2hpbGQocGFyZW50LmZpcnN0Q2hpbGQpO1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGVtcGxhdGVDb250ZW50KGVsOiBOb2RlKTogTm9kZSB8IG51bGwge1xuICByZXR1cm4gJ2NvbnRlbnQnIGluIChlbCBhcyBhbnkpIC8qKiBNaWNyb3NvZnQvVHlwZVNjcmlwdCMyMTUxNyAqLyAmJiBpc1RlbXBsYXRlRWxlbWVudChlbClcbiAgICA/IGVsLmNvbnRlbnRcbiAgICA6IG51bGw7XG59XG5mdW5jdGlvbiBpc1RlbXBsYXRlRWxlbWVudChlbDogTm9kZSk6IGVsIGlzIEhUTUxUZW1wbGF0ZUVsZW1lbnQge1xuICByZXR1cm4gZWwubm9kZVR5cGUgPT09IE5vZGUuRUxFTUVOVF9OT0RFICYmIGVsLm5vZGVOYW1lID09PSAnVEVNUExBVEUnO1xufVxuIl19