Skip to content

Commit

Permalink
Safer construction of notebook html
Browse files Browse the repository at this point in the history
  • Loading branch information
mjbvz committed Jan 9, 2023
1 parent 30cda85 commit 8d8dfae
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 28 deletions.
Expand Up @@ -236,16 +236,20 @@ class CellOutputElement extends Disposable {

private _renderSearchForMimetype(viewModel: ICellOutputViewModel, mimeType: string): IInsetRenderOutput {
const query = `@tag:notebookRenderer ${mimeType}`;

const p = DOM.$('p', undefined, `No renderer could be found for mimetype "${mimeType}", but one might be available on the Marketplace.`);
const a = DOM.$('a', { href: `command:workbench.extensions.search?%22${query}%22`, class: 'monaco-button monaco-text-button', tabindex: 0, role: 'button', style: 'padding: 8px; text-decoration: none; color: rgb(255, 255, 255); background-color: rgb(14, 99, 156); max-width: 200px;' }, `Search Marketplace`);

return {
type: RenderOutputType.Html,
source: viewModel,
htmlContent: `<p>No renderer could be found for mimetype "${mimeType}", but one might be available on the Marketplace.</p>
<a href="command:workbench.extensions.search?%22${query}%22" class="monaco-button monaco-text-button" tabindex="0" role="button" style="padding: 8px; text-decoration: none; color: rgb(255, 255, 255); background-color: rgb(14, 99, 156); max-width: 200px;">Search Marketplace</a>`
htmlContent: p.outerHTML + a.outerHTML
};
}

private _renderMessage(viewModel: ICellOutputViewModel, message: string): IInsetRenderOutput {
return { type: RenderOutputType.Html, source: viewModel, htmlContent: `<p>${message}</p>` };
const el = DOM.$('p', undefined, message);
return { type: RenderOutputType.Html, source: viewModel, htmlContent: el.outerHTML };
}

private async _attachToolbar(outputItemDiv: HTMLElement, notebookTextModel: NotebookTextModel, kernel: INotebookKernel | undefined, index: number, mimeTypes: readonly IOrderedMimeType[]) {
Expand Down
Expand Up @@ -182,30 +182,6 @@ async function webviewPreloads(ctx: PreloadContext) {

document.body.addEventListener('click', handleInnerClick);

const preservedScriptAttributes: (keyof HTMLScriptElement)[] = [
'type', 'src', 'nonce', 'noModule', 'async',
];

// derived from https://github.com/jquery/jquery/blob/d0ce00cdfa680f1f0c38460bc51ea14079ae8b07/src/core/DOMEval.js
const domEval = (container: Element) => {
const arr = Array.from(container.getElementsByTagName('script'));
for (let n = 0; n < arr.length; n++) {
const node = arr[n];
const scriptTag = document.createElement('script');
const trustedScript = ttPolicy?.createScript(node.innerText) ?? node.innerText;
scriptTag.text = trustedScript as string;
for (const key of preservedScriptAttributes) {
const val = node[key] || node.getAttribute && node.getAttribute(key);
if (val) {
scriptTag.setAttribute(key, val as any);
}
}

// TODO@connor4312: should script with src not be removed?
container.appendChild(scriptTag).parentNode!.removeChild(scriptTag);
}
};

async function loadScriptSource(url: string, originalUri: string): Promise<string> {
const res = await fetch(url);
const text = await res.text();
Expand Down Expand Up @@ -2312,7 +2288,6 @@ async function webviewPreloads(ctx: PreloadContext) {
if (content.type === 0 /* RenderOutputType.Html */) {
const trustedHtml = ttPolicy?.createHTML(content.htmlContent) ?? content.htmlContent;
this.element.innerHTML = trustedHtml as string;
domEval(this.element);
} else if (preloadErrors.some(e => e instanceof Error)) {
const errors = preloadErrors.filter((e): e is Error => e instanceof Error);
showRenderError(`Error loading preloads`, this.element, errors);
Expand Down

0 comments on commit 8d8dfae

Please sign in to comment.