Skip to content

Commit

Permalink
fixes #1061 - santize descriptions to mute the possibility of xss att…
Browse files Browse the repository at this point in the history
…acks
  • Loading branch information
mrin9 committed Dec 7, 2024
1 parent b0d6eb2 commit 3a7988f
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 38 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@types/js-yaml": "^4.0.9",
"base64-arraybuffer": "^1.0.2",
"buffer": "^6.0.3",
"dompurify": "^3.2.2",
"github-slugger": "^2.0.0",
"js-yaml": "^4.1.0",
"lit": "^3.2.1",
Expand Down
27 changes: 21 additions & 6 deletions src/components/api-request.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LitElement, html, css } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import DOMPurify from 'dompurify';
import { guard } from 'lit/directives/guard.js';
import { live } from 'lit/directives/live.js';
import { ifDefined } from 'lit/directives/if-defined.js';
Expand Down Expand Up @@ -332,7 +333,9 @@ export default class ApiRequest extends LitElement {
(v) =>
html`<li>
${this.renderExample(v, paramType, paramName)} ${v.summary?.length > 0 ? html`<span>&lpar;${v.summary}&rpar;</span>` : ''}
${v.description?.length > 0 ? html`<p>${unsafeHTML(marked(v.description))}</p>` : ''}
${v.description?.length > 0
? html`<p>${unsafeHTML(DOMPurify.sanitize(marked(v.description), { USE_PROFILES: { html: true } }))}</p>`
: ''}
</li>`
)}
</ul>`;
Expand Down Expand Up @@ -607,7 +610,9 @@ export default class ApiRequest extends LitElement {
<tr>
${this.allowTry === 'true' ? html`<td style="border:none"></td>` : ''}
<td colspan="2" style="border:none">
<span class="m-markdown-small">${unsafeHTML(marked(param.description || ''))}</span>
<span class="m-markdown-small">
${unsafeHTML(DOMPurify.sanitize(marked(param.description || ''), { USE_PROFILES: { html: true } }))}
</span>
${this.exampleListTemplate.call(this, param.name, paramSchema.type, example.exampleList)}
</td>
</tr>
Expand Down Expand Up @@ -765,7 +770,9 @@ export default class ApiRequest extends LitElement {
>
${v.exampleSummary && v.exampleSummary.length > 80 ? html`<div style="padding: 4px 0">${v.exampleSummary}</div>` : ''}
${v.exampleDescription
? html`<div class="m-markdown-small" style="padding: 4px 0">${unsafeHTML(marked(v.exampleDescription || ''))}</div>`
? html`<div class="m-markdown-small" style="padding: 4px 0">
${unsafeHTML(DOMPurify.sanitize(marked(v.exampleDescription || ''), { USE_PROFILES: { html: true } }))}
</div>`
: ''}
<!-- This pre(hidden) is to store the original example value, this will remain unchanged when users switches from one example to another, its is used to populate the editable textarea -->
<pre
Expand Down Expand Up @@ -896,7 +903,9 @@ ${v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, n
${reqBodyTypeSelectorHtml}
</div>
${this.request_body.description
? html`<div class="m-markdown" style="margin-bottom:12px">${unsafeHTML(marked(this.request_body.description))}</div>`
? html`<div class="m-markdown" style="margin-bottom:12px">
${unsafeHTML(DOMPurify.sanitize(marked(this.request_body.description), { USE_PROFILES: { html: true } }))}
</div>`
: ''}
${this.selectedRequestBodyType.includes('json') ||
this.selectedRequestBodyType.includes('xml') ||
Expand Down Expand Up @@ -1161,7 +1170,9 @@ ${v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, n
<tr>
<td style="border:none"></td>
<td colspan="2" style="border:none; margin-top:0; padding:0 5px 8px 5px;">
<span class="m-markdown-small">${unsafeHTML(marked(fieldSchema.description || ''))}</span>
<span class="m-markdown-small">
${unsafeHTML(DOMPurify.sanitize(marked(fieldSchema.description || '')), { USE_PROFILES: { html: true } })}
</span>
${this.exampleListTemplate.call(this, fieldName, paramSchema.type, example.exampleList)}
</td>
</tr>
Expand All @@ -1185,7 +1196,11 @@ ${v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, n
.textContent="${exampleValue}"
style="width:100%"
></textarea>
${schema.description ? html`<span class="m-markdown-small">${unsafeHTML(marked(schema.description))}</span>` : ''}
${schema.description
? html`<span class="m-markdown-small">
${unsafeHTML(DOMPurify.sanitize(marked(schema.description), { USE_PROFILES: { html: true } }))}
</span>`
: ''}
`;
}

Expand Down
23 changes: 18 additions & 5 deletions src/components/api-response.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LitElement, html, css } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import DOMPurify from 'dompurify';
import { marked } from 'marked';
import { schemaInObjectNotation, generateExample, standardizeExample } from '~/utils/schema-utils';
import FontStyles from '~/styles/font-styles';
Expand Down Expand Up @@ -177,7 +178,11 @@ export default class ApiResponse extends LitElement {
(status) =>
html`<div style="display: ${status === this.selectedStatus ? 'block' : 'none'}">
<div class="top-gap">
<span class="resp-descr m-markdown ">${unsafeHTML(marked(this.responses[status]?.description || ''))}</span>
<span class="resp-descr m-markdown"
>${unsafeHTML(
DOMPurify.sanitize(marked(this.responses[status]?.description || ''), { USE_PROFILES: { html: true } })
)}</span
>
${this.headersForEachRespStatus[status] && this.headersForEachRespStatus[status]?.length > 0
? html`${this.responseHeaderListTemplate(this.headersForEachRespStatus[status])}`
: ''}
Expand Down Expand Up @@ -241,7 +246,9 @@ export default class ApiResponse extends LitElement {
${v.schema?.type || ''}
</td>
<td style="padding:8px; vertical-align: baseline; border-top: 1px solid var(--light-border-color);text-overflow: ellipsis;">
<div class="m-markdown-small regular-font">${unsafeHTML(marked(v.description || ''))}</div>
<div class="m-markdown-small regular-font">
${unsafeHTML(DOMPurify.sanitize(marked(v.description || ''), { USE_PROFILES: { html: true } }))}
</div>
</td>
<td style="padding:8px; vertical-align: baseline; border-top: 1px solid var(--light-border-color); text-overflow: ellipsis;">
${v.schema?.example || ''}
Expand Down Expand Up @@ -293,7 +300,9 @@ export default class ApiResponse extends LitElement {
: ''}
${mimeRespDetails.examples[0].exampleDescription
? html`<div class="m-markdown-small" style="padding: 4px 0">
${unsafeHTML(marked(mimeRespDetails.examples[0].exampleDescription || ''))}
${unsafeHTML(DOMPurify.sanitize(marked(mimeRespDetails.examples[0].exampleDescription || '')), {
USE_PROFILES: { html: true },
})}
</div>`
: ''}
<json-tree
Expand All @@ -308,7 +317,9 @@ export default class ApiResponse extends LitElement {
: ''}
${mimeRespDetails.examples[0].exampleDescription
? html`<div class="m-markdown-small" style="padding: 4px 0">
${unsafeHTML(marked(mimeRespDetails.examples[0].exampleDescription || ''))}
${unsafeHTML(
DOMPurify.sanitize(marked(mimeRespDetails.examples[0].exampleDescription || ''), { USE_PROFILES: { html: true } })
)}
</div>`
: ''}
<pre class="example-panel ${this.renderStyle === 'read' ? 'border pad-8-16' : 'border-top pad-top-8'}">
Expand All @@ -334,7 +345,9 @@ ${mimeRespDetails.examples[0].exampleValue}</pre
>
${v.exampleSummary && v.exampleSummary.length > 80 ? html`<div style="padding: 4px 0">${v.exampleSummary}</div>` : ''}
${v.exampleDescription
? html`<div class="m-markdown-small" style="padding: 4px 0">${unsafeHTML(marked(v.exampleDescription || ''))}</div>`
? html`<div class="m-markdown-small" style="padding: 4px 0">
${unsafeHTML(DOMPurify.sanitize(marked(v.exampleDescription || ''), { USE_PROFILES: { html: true } }))}
</div>`
: ''}
${v.exampleFormat === 'json'
? html`<json-tree
Expand Down
24 changes: 16 additions & 8 deletions src/components/schema-tree.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LitElement, html, css } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import DOMPurify from 'dompurify';
import { marked } from 'marked';
import FontStyles from '~/styles/font-styles';
import SchemaStyles from '~/styles/schema-styles';
Expand Down Expand Up @@ -112,7 +113,9 @@ export default class SchemaTree extends LitElement {
</div>`
: ''}
</div>
<span part="schema-description" class="m-markdown"> ${unsafeHTML(marked(this.data?.['::description'] || ''))}</span>
<span part="schema-description" class="m-markdown">
${unsafeHTML(DOMPurify.sanitize(marked(this.data?.['::description'] || ''), { USE_PROFILES: { html: true } }))}</span
>
${this.data
? html` ${this.generateTree(
this.data['::type'] === 'array' ? this.data['::props'] : this.data,
Expand Down Expand Up @@ -250,7 +253,9 @@ export default class SchemaTree extends LitElement {
: ''}
${openBracket}
</div>
<div class="td key-descr m-markdown-small">${unsafeHTML(marked(description || ''))}</div>
<div class="td key-descr m-markdown-small">
${unsafeHTML(DOMPurify.sanitize(marked(description || ''), { USE_PROFILES: { html: true } }))}
</div>
</div>
<div
class="inside-bracket ${data['::type'] || 'no-type-info'}"
Expand Down Expand Up @@ -367,12 +372,15 @@ export default class SchemaTree extends LitElement {
${description || schemaTitle || schemaDescription
? html`${html`<span class="m-markdown-small">
${unsafeHTML(
marked(
dataType === 'array'
? `${descrExpander} ${description}`
: schemaTitle
? `${descrExpander} <b>${schemaTitle}:</b> ${schemaDescription}`
: `${descrExpander} ${schemaDescription}`
DOMPurify.sanitize(
marked(
dataType === 'array'
? `${descrExpander} ${description}`
: schemaTitle
? `${descrExpander} <b>${schemaTitle}:</b> ${schemaDescription}`
: `${descrExpander} ${schemaDescription}`
),
{ USE_PROFILES: { html: true } }
)
)}
</span>`}`
Expand Down
7 changes: 6 additions & 1 deletion src/templates/components-template.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import DOMPurify from 'dompurify';
import { marked } from 'marked';
import { schemaInObjectNotation } from '~/utils/schema-utils';
import '~/components/json-tree';
Expand Down Expand Up @@ -72,7 +73,11 @@ export default function componentsTemplate() {
>
<div class="title tag">${component.name}</div>
<div class="regular-font-size">
${unsafeHTML(`<div class='m-markdown regular-font'>${marked(component.description ? component.description : '')}</div>`)}
${unsafeHTML(
`<div class='m-markdown regular-font'>${DOMPurify.sanitize(marked(component.description || ''), {
USE_PROFILES: { html: true },
})}</div>`
)}
</div>
</div>
<div class="regular-font section-gap--read-mode">
Expand Down
17 changes: 11 additions & 6 deletions src/templates/endpoint-template.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import DOMPurify from 'dompurify';
import { marked } from 'marked';
import '~/components/api-request';
import '~/components/api-response';
Expand Down Expand Up @@ -125,11 +126,15 @@ function endpointBodyTemplate(path) {
`
: ''}
${path.description
? html`<div part="section-endpoint-body-description" class="m-markdown">${unsafeHTML(marked(path.description))}</div>`
? html`<div part="section-endpoint-body-description" class="m-markdown">
${unsafeHTML(DOMPurify.sanitize(marked(path.description), { USE_PROFILES: { html: true } }))}
</div>`
: ''}
${path.externalDocs?.url || path.externalDocs?.description
? html`<div style="background:var(--bg3); padding:2px 8px 8px 8px; margin:8px 0; border-radius:var(--border-radius)">
<div class="m-markdown">${unsafeHTML(marked(path.externalDocs?.description || ''))}</div>
<div class="m-markdown">
${unsafeHTML(DOMPurify.sanitize(marked(path.externalDocs?.description || ''), { USE_PROFILES: { html: true } }))}
</div>
${path.externalDocs?.url
? html`<a
style="font-family:var(--font-mono); font-size:var(--font-size-small)"
Expand Down Expand Up @@ -158,7 +163,7 @@ function endpointBodyTemplate(path) {
.request_body="${path.requestBody}"
.api_keys="${nonEmptyApiKeys}"
.servers="${path.servers}"
server-url="${path.servers && path.servers.length > 0 ? path.servers[0].url : this.selectedServer?.computedUrl}"
server-url="${path.servers?.length > 0 ? path.servers[0].url : this.selectedServer?.computedUrl}"
active-schema-tab="${this.defaultSchemaTab}"
fill-request-fields-with-example="${this.fillRequestFieldsWithExample}"
allow-try="${this.allowTry}"
Expand Down Expand Up @@ -210,9 +215,9 @@ export default function endpointTemplate(isMini = false, pathsExpanded = false)
if (!this.resolvedSpec) {
return '';
}
return html` ${isMini
return html`${isMini
? ''
: html` <div style="display:flex; justify-content:flex-end;">
: html`<div style="display:flex; justify-content:flex-end;">
<span @click="${(e) => onExpandCollapseAll(e, 'expand-all')}" style="color:var(--primary-color); cursor:pointer;">
Expand all
</span>
Expand Down Expand Up @@ -260,7 +265,7 @@ export default function endpointTemplate(isMini = false, pathsExpanded = false)
<div class="section-tag-body">
<slot name="${tag.elementId}"></slot>
<div class="regular-font regular-font-size m-markdown" style="padding-bottom:12px">
${unsafeHTML(marked(tag.description || ''))}
${unsafeHTML(DOMPurify.sanitize(marked(tag.description || ''), { USE_PROFILES: { html: true } }))}
</div>
${tag.paths
.filter((v) => {
Expand Down
21 changes: 17 additions & 4 deletions src/templates/expanded-endpoint-template.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import DOMPurify from 'dompurify';
import { marked } from 'marked';
import Slugger from 'github-slugger';
import { rapidocApiKey } from '~/utils/common-utils';
Expand Down Expand Up @@ -90,7 +91,9 @@ export function expandedEndpointBodyTemplate(path, tagName = '', tagDescription
class="tag-description collapsed"
style="max-height:0px; overflow:hidden; margin-top:16px; border:1px solid var(--border-color)"
>
<div class="m-markdown" style="padding:8px">${unsafeHTML(marked(tagDescription))}</div>
<div class="m-markdown" style="padding:8px">
${unsafeHTML(DOMPurify.sanitize(marked(tagDescription), { USE_PROFILES: { html: true } }))}
</div>
</div>`
: ''}
</div>
Expand Down Expand Up @@ -130,11 +133,15 @@ export function expandedEndpointBodyTemplate(path, tagName = '', tagDescription
</div>
`}
<slot name="${path.elementId}"></slot>`}
${path.description ? html`<div class="m-markdown">${unsafeHTML(marked(path.description))}</div>` : ''}
${path.description
? html`<div class="m-markdown">${unsafeHTML(DOMPurify.sanitize(marked(path.description), { USE_PROFILES: { html: true } }))}</div>`
: ''}
${pathSecurityTemplate.call(this, path.security)}
${path.externalDocs?.url || path.externalDocs?.description
? html`<div style="background:var(--bg3); padding:2px 8px 8px 8px; margin:8px 0; border-radius:var(--border-radius)">
<div class="m-markdown">${unsafeHTML(marked(path.externalDocs?.description || ''))}</div>
<div class="m-markdown">
${unsafeHTML(DOMPurify.sanitize(marked(path.externalDocs?.description || ''), { USE_PROFILES: { html: true } }))}
</div>
${path.externalDocs?.url
? html`<a
style="font-family:var(--font-mono); font-size:var(--font-size-small)"
Expand Down Expand Up @@ -225,7 +232,13 @@ export default function expandedEndpointTemplate() {
<div class="regular-font-size">
${unsafeHTML(`
<div class="m-markdown regular-font">
${marked(tag.description || '', this.infoDescriptionHeadingsInNavBar === 'true' ? { renderer: headingRenderer(tag.elementId) } : undefined)}
${DOMPurify.sanitize(
marked(
tag.description || '',
this.infoDescriptionHeadingsInNavBar === 'true' ? { renderer: headingRenderer(tag.elementId) } : undefined
),
{ USE_PROFILES: { html: true } }
)}
</div>`)}
</div>
</section>
Expand Down
9 changes: 8 additions & 1 deletion src/templates/focused-endpoint-template.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import DOMPurify from 'dompurify';
import { marked } from 'marked';
import Slugger from 'github-slugger';
import { expandedEndpointBodyTemplate } from '~/templates/expanded-endpoint-template';
Expand Down Expand Up @@ -41,7 +42,13 @@ function focusedTagBodyTemplate(tag) {
${this.onNavTagClick === 'show-description' && tag.description
? html`<div class="m-markdown">
${unsafeHTML(`<div class="m-markdown regular-font">
${marked(tag.description || '', this.infoDescriptionHeadingsInNavBar === 'true' ? { renderer: headingRenderer(tag.elementId) } : undefined)}
${DOMPurify.sanitize(
marked(
tag.description || '',
this.infoDescriptionHeadingsInNavBar === 'true' ? { renderer: headingRenderer(tag.elementId) } : undefined
),
{ USE_PROFILES: { html: true } }
)}
</div>`)}
</div>`
: ''}
Expand Down
Loading

0 comments on commit 3a7988f

Please sign in to comment.