mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-23 01:25:38 -05:00
Fix markdown security and enable most CSS (#5263)
* Fix markdown security and enable most CSS Stops using our sanitizer and instead disables HTML in markdown engine - This was blocking Note because it converted > to > - It's slightly more strict in that it fully disables HTML unless trusted. Will need to improve handling of Trusted to support this in a future PR Adds in correct CSS, both from .css file in markdown extension and from built-into all webviews global CSS - Fix #3765 standard markdown support - Fix Support of Notes by bringing correct styles - Fix code block colorization - Fix link handling so it's not bolded / gets underlined on hover - Fixes table rendering (for markdown and HTML tables) * Reduce scope of CSS changes - Removed some CSS that wasn't needed or caused issues - Scoped most things under the preview section not the whole component * Avoid markdown html block by sanitizing after render * Fix pre node not overflowing - This was a bug in existing implementation too
This commit is contained in:
182
src/sql/workbench/parts/notebook/cellViews/media/highlight.css
Normal file
182
src/sql/workbench/parts/notebook/cellViews/media/highlight.css
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs2015.css
|
||||
*/
|
||||
/*
|
||||
* Visual Studio 2015 dark style
|
||||
* Author: Nicolas LLOBERA <nllobera@gmail.com>
|
||||
*/
|
||||
|
||||
|
||||
.notebook-preview .hljs-keyword,
|
||||
.notebook-preview .hljs-literal,
|
||||
.notebook-preview .hljs-symbol,
|
||||
.notebook-preview .hljs-name {
|
||||
color: #569CD6;
|
||||
}
|
||||
.notebook-preview .hljs-link {
|
||||
color: #569CD6;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-built_in,
|
||||
.notebook-preview .hljs-type {
|
||||
color: #4EC9B0;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-number,
|
||||
.notebook-preview .hljs-class {
|
||||
color: #B8D7A3;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-string,
|
||||
.notebook-preview .hljs-meta-string {
|
||||
color: #D69D85;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-regexp,
|
||||
.notebook-preview .hljs-template-tag {
|
||||
color: #9A5334;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-subst,
|
||||
.notebook-preview .hljs-function,
|
||||
.notebook-preview .hljs-title,
|
||||
.notebook-preview .hljs-params,
|
||||
.notebook-preview .hljs-formula {
|
||||
color: #DCDCDC;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-comment,
|
||||
.notebook-preview .hljs-quote {
|
||||
color: #57A64A;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-doctag {
|
||||
color: #608B4E;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-meta,
|
||||
.notebook-preview .hljs-meta-keyword,
|
||||
.notebook-preview .hljs-tag {
|
||||
color: #9B9B9B;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-variable,
|
||||
.notebook-preview .hljs-template-variable {
|
||||
color: #BD63C5;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-attr,
|
||||
.notebook-preview .hljs-attribute,
|
||||
.notebook-preview .hljs-builtin-name {
|
||||
color: #9CDCFE;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-section {
|
||||
color: gold;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*.hljs-code {
|
||||
font-family:'Monospace';
|
||||
}*/
|
||||
|
||||
.notebook-preview .hljs-bullet,
|
||||
.notebook-preview .hljs-selector-tag,
|
||||
.notebook-preview .hljs-selector-id,
|
||||
.notebook-preview .hljs-selector-class,
|
||||
.notebook-preview .hljs-selector-attr,
|
||||
.notebook-preview .hljs-selector-pseudo {
|
||||
color: #D7BA7D;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-addition {
|
||||
background-color: var(--vscode-diffEditor-insertedTextBackground, rgba(155, 185, 85, 0.2));
|
||||
color: rgb(155, 185, 85);
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-deletion {
|
||||
background: var(--vscode-diffEditor-removedTextBackground, rgba(255, 0, 0, 0.2));
|
||||
color: rgb(255, 0, 0);
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
From https://raw.githubusercontent.com/isagalaev/highlight.js/master/src/styles/vs.css
|
||||
*/
|
||||
/*
|
||||
|
||||
Visual Studio-like style based on original C# coloring by Jason Diamond <jason@diamond.name>
|
||||
|
||||
*/
|
||||
|
||||
.notebook-preview .hljs-function,
|
||||
.notebook-preview .hljs-params {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-comment,
|
||||
.notebook-preview .hljs-quote,
|
||||
.notebook-preview .hljs-variable {
|
||||
color: #008000;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-keyword,
|
||||
.notebook-preview .hljs-selector-tag,
|
||||
.notebook-preview .hljs-built_in,
|
||||
.notebook-preview .hljs-name,
|
||||
.notebook-preview .hljs-tag {
|
||||
color: #00f;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-string,
|
||||
.notebook-preview .hljs-title,
|
||||
.notebook-preview .hljs-section,
|
||||
.notebook-preview .hljs-attribute,
|
||||
.notebook-preview .hljs-literal,
|
||||
.notebook-preview .hljs-template-tag,
|
||||
.notebook-preview .hljs-template-variable,
|
||||
.notebook-preview .hljs-type {
|
||||
color: #a31515;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-selector-attr,
|
||||
.notebook-preview .hljs-selector-pseudo,
|
||||
.notebook-preview .hljs-meta {
|
||||
color: #2b91af;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-doctag {
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-attr {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-symbol,
|
||||
.notebook-preview .hljs-bullet,
|
||||
.notebook-preview .hljs-link {
|
||||
color: #00b0e8;
|
||||
}
|
||||
|
||||
|
||||
.notebook-preview .hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.notebook-preview .hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
231
src/sql/workbench/parts/notebook/cellViews/media/markdown.css
Normal file
231
src/sql/workbench/parts/notebook/cellViews/media/markdown.css
Normal file
@@ -0,0 +1,231 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.notebook-preview {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.notebook-preview #code-csp-warning {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: white;
|
||||
margin: 16px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-family: sans-serif;
|
||||
background-color:#444444;
|
||||
cursor: pointer;
|
||||
padding: 6px;
|
||||
box-shadow: 1px 1px 1px rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
.notebook-preview #code-csp-warning:hover {
|
||||
text-decoration: none;
|
||||
background-color:#007acc;
|
||||
box-shadow: 2px 2px 2px rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
|
||||
.notebook-preview .scrollBeyondLastLine {
|
||||
margin-bottom: calc(100vh - 22px);
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection .code-line {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection .code-active-line:before,
|
||||
.notebook-preview .showEditorSelection .code-line:hover:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -12px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection li.code-active-line:before,
|
||||
.notebook-preview .showEditorSelection li.code-line:hover:before {
|
||||
left: -30px;
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection .code-active-line:before {
|
||||
border-left: 3px solid rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection .code-line:hover:before {
|
||||
border-left: 3px solid rgba(0, 0, 0, 0.40);
|
||||
}
|
||||
|
||||
.notebook-preview .showEditorSelection .code-line .code-line:hover:before {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview .showEditorSelection .code-active-line:before {
|
||||
border-left: 3px solid rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview .showEditorSelection .code-line:hover:before {
|
||||
border-left: 3px solid rgba(255, 255, 255, 0.60);
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview .showEditorSelection .code-line .code-line:hover:before {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.hc-black .notebook-preview .showEditorSelection .code-active-line:before {
|
||||
border-left: 3px solid rgba(255, 160, 0, 0.7);
|
||||
}
|
||||
|
||||
.hc-black .notebook-preview .showEditorSelection .code-line:hover:before {
|
||||
border-left: 3px solid rgba(255, 160, 0, 1);
|
||||
}
|
||||
|
||||
.hc-black .notebook-preview .showEditorSelection .code-line .code-line:hover:before {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.notebook-preview img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.notebookEditor a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.notebookEditor a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notebook-preview a:focus,
|
||||
.notebook-preview input:focus,
|
||||
.notebook-preview select:focus,
|
||||
.notebook-preview textarea:focus {
|
||||
outline: 1px solid -webkit-focus-ring-color;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.notebook-preview hr {
|
||||
border: 0;
|
||||
height: 2px;
|
||||
border-bottom: 2px solid;
|
||||
}
|
||||
|
||||
.notebook-preview h1 {
|
||||
padding-bottom: 0.3em;
|
||||
line-height: 1.2;
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
.notebook-preview h1, .notebook-preview h2, .notebook-preview h3 {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.notebook-preview h1 code,
|
||||
.notebook-preview h2 code,
|
||||
.notebook-preview h3 code,
|
||||
.notebook-preview h4 code,
|
||||
.notebook-preview h5 code,
|
||||
.notebook-preview h6 code {
|
||||
font-size: inherit;
|
||||
line-height: auto;
|
||||
}
|
||||
|
||||
.notebook-preview table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.notebook-preview table > thead > tr > th {
|
||||
text-align: left;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
.notebook-preview table > thead > tr > th,
|
||||
.notebook-preview table > thead > tr > td,
|
||||
.notebook-preview table > tbody > tr > th,
|
||||
.notebook-preview .notebook-preview table > tbody > tr > td {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.notebook-preview table > tbody > tr + tr > td {
|
||||
border-top: 1px solid;
|
||||
}
|
||||
|
||||
.notebook-preview blockquote {
|
||||
margin: 0 7px 0 5px;
|
||||
padding: 0 16px 0 10px;
|
||||
border-left-width: 5px;
|
||||
border-left-style: solid;
|
||||
}
|
||||
|
||||
.notebook-preview code {
|
||||
font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
|
||||
font-size: 12px;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
.notebook-preview pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.notebook-preview .mac code {
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.notebook-preview pre:not(.hljs),
|
||||
.notebook-preview pre.hljs code > div {
|
||||
padding: 16px;
|
||||
border-radius: 3px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/** Theming */
|
||||
|
||||
.notebook-preview pre code {
|
||||
color: var(--vscode-editor-foreground);
|
||||
}
|
||||
|
||||
|
||||
.notebook-preview pre {
|
||||
background-color: rgba(220, 220, 220, 0.4);
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview pre {
|
||||
background-color: rgba(10, 10, 10, 0.4);
|
||||
}
|
||||
|
||||
.hc-black .notebook-preview pre {
|
||||
background-color: rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
.hc-black .notebook-preview h1 {
|
||||
border-color: rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
.notebook-preview table > thead > tr > th {
|
||||
border-color: rgba(0, 0, 0, 0.69);
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview table > thead > tr > th {
|
||||
border-color: rgba(255, 255, 255, 0.69);
|
||||
}
|
||||
|
||||
.notebook-preview h1,
|
||||
.notebook-preview hr,
|
||||
.notebook-preview table > tbody > tr + tr > td {
|
||||
border-color: rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.vs-dark .notebook-preview h1,
|
||||
.vs-dark .notebook-preview hr,
|
||||
.vs-dark .notebook-preview table > tbody > tr + tr > td {
|
||||
border-color: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import 'vs/css!./textCell';
|
||||
import 'vs/css!./media/markdown';
|
||||
import 'vs/css!./media/highlight';
|
||||
|
||||
import { OnInit, Component, Input, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, OnChanges, SimpleChange, HostListener, AfterContentInit } from '@angular/core';
|
||||
import * as path from 'path';
|
||||
@@ -66,6 +68,7 @@ export class TextCellComponent extends CellView implements OnInit, AfterContentI
|
||||
}
|
||||
|
||||
private _content: string;
|
||||
private _lastTrustedMode: boolean;
|
||||
private isEditMode: boolean;
|
||||
private _sanitizer: ISanitizer;
|
||||
private _model: NotebookModel;
|
||||
@@ -192,15 +195,19 @@ export class TextCellComponent extends CellView implements OnInit, AfterContentI
|
||||
* Sanitizes the data to be shown in markdown cell
|
||||
*/
|
||||
private updatePreview() {
|
||||
if (this._content !== this.cellModel.source || this.cellModel.source.length === 0) {
|
||||
let trustedChanged = this.cellModel && this._lastTrustedMode !== this.cellModel.trustedMode;
|
||||
let contentChanged = this._content !== this.cellModel.source || this.cellModel.source.length === 0;
|
||||
if (trustedChanged || contentChanged) {
|
||||
this._lastTrustedMode = this.cellModel.trustedMode;
|
||||
if (!this.cellModel.source && !this.isEditMode) {
|
||||
this._content = localize('doubleClickEdit', 'Double-click to edit');
|
||||
} else {
|
||||
this._content = this.sanitizeContent(this.cellModel.source);
|
||||
this._content = this.cellModel.source;
|
||||
}
|
||||
|
||||
this._commandService.executeCommand<string>('notebook.showPreview', this.cellModel.notebookModel.notebookUri, this._content).then((htmlcontent) => {
|
||||
htmlcontent = this.convertVscodeResourceToFileInSubDirectories(htmlcontent);
|
||||
htmlcontent = this.sanitizeContent(htmlcontent);
|
||||
let outputElement = <HTMLElement>this.output.nativeElement;
|
||||
outputElement.innerHTML = htmlcontent;
|
||||
});
|
||||
@@ -214,7 +221,6 @@ export class TextCellComponent extends CellView implements OnInit, AfterContentI
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
// Only replace vscode-resource with file when in the same (or a sub) directory
|
||||
// This matches Jupyter Notebook viewer behavior
|
||||
private convertVscodeResourceToFileInSubDirectories(htmlContent: string): string {
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'vs/css!./notebook';
|
||||
|
||||
import { registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
|
||||
import { SIDE_BAR_BACKGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, EDITOR_GROUP_HEADER_TABS_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { activeContrastBorder, contrastBorder, buttonBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { activeContrastBorder, contrastBorder, buttonBackground, textLinkForeground, textLinkActiveForeground, textPreformatForeground, textBlockQuoteBackground, textBlockQuoteBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IDisposable } from 'vscode-xterm';
|
||||
import { editorLineHighlight, editorLineHighlightBorder } from 'vs/editor/common/view/editorColorRegistry';
|
||||
|
||||
@@ -179,8 +179,6 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean): IDi
|
||||
if (linkForeground) {
|
||||
collector.addRule(`
|
||||
.notebookEditor a:link {
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
color: ${linkForeground};
|
||||
}
|
||||
`);
|
||||
@@ -195,5 +193,43 @@ export function registerNotebookThemes(overrideEditorThemeSetting: boolean): IDi
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
// Styling for markdown cells & links in notebooks.
|
||||
// This matches the values used by default in all web views
|
||||
if (linkForeground) {
|
||||
collector.addRule(`
|
||||
.notebookEditor a:link {
|
||||
color: ${linkForeground};
|
||||
}
|
||||
`);
|
||||
}
|
||||
let activeForeground = theme.getColor(textLinkActiveForeground);
|
||||
if (activeForeground) {
|
||||
collector.addRule(`
|
||||
.notebookEditor a:hover {
|
||||
color: ${activeForeground};
|
||||
}
|
||||
`);
|
||||
}
|
||||
let preformatForeground = theme.getColor(textPreformatForeground);
|
||||
if (preformatForeground) {
|
||||
collector.addRule(`
|
||||
.notebook-preview code {
|
||||
color: ${preformatForeground};
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
|
||||
let blockQuoteBackground = theme.getColor(textBlockQuoteBackground);
|
||||
let blockQuoteBorder = theme.getColor(textBlockQuoteBorder);
|
||||
if (preformatForeground) {
|
||||
collector.addRule(`
|
||||
.notebookEditor blockquote {
|
||||
background: ${blockQuoteBackground};
|
||||
border-color: ${blockQuoteBorder};
|
||||
}
|
||||
`);
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user