diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts
index 17b6d4f4ebb..9b70fe3beb3 100644
--- a/extensions/markdown-language-features/src/features/previewContentProvider.ts
+++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts
@@ -209,7 +209,7 @@ export class MarkdownContentProvider {
return ``;
case MarkdownPreviewSecurityLevel.AllowScriptsAndAllContent:
- return '';
+ return '';
case MarkdownPreviewSecurityLevel.Strict:
default:
diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js
index 6568b02f011..0b25585c611 100644
--- a/src/vs/workbench/contrib/webview/browser/pre/main.js
+++ b/src/vs/workbench/contrib/webview/browser/pre/main.js
@@ -285,6 +285,12 @@
applyStyles(newDocument, newDocument.body);
+ // Check for CSP
+ const csp = newDocument.querySelector('meta[http-equiv="Content-Security-Policy"]');
+ if (!csp) {
+ host.postMessage('no-csp-found');
+ }
+
// set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off
// and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden
return '\n' + newDocument.documentElement.outerHTML;
diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts
index 20daf1ec4bc..4b06dd21bb1 100644
--- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts
+++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts
@@ -13,9 +13,11 @@ import { endsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
+import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
+import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping';
import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing';
@@ -284,6 +286,8 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview {
@IFileService fileService: IFileService,
@ITunnelService tunnelService: ITunnelService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
+ @ITelemetryService private readonly _telemetryService: ITelemetryService,
+ @IEnvironmentService private readonly _environementService: IEnvironmentService,
) {
super();
this.content = {
@@ -412,6 +416,10 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview {
case 'did-blur':
this.handleFocusChange(false);
return;
+
+ case 'no-csp-found':
+ this.handleNoCspFound();
+ return;
}
}));
this._register(addDisposableListener(this._webview, 'devtools-opened', () => {
@@ -546,6 +554,32 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview {
}
}
+ private _hasAlertedAboutMissingCsp = false;
+
+ private handleNoCspFound(): void {
+ if (this._hasAlertedAboutMissingCsp) {
+ return;
+ }
+ this._hasAlertedAboutMissingCsp = true;
+
+ if (this._options.extension && this._options.extension.id) {
+ if (this._environementService.isExtensionDevelopment) {
+ console.warn(`${this._options.extension.id.value} created a webview without a content security policy: https://aka.ms/vscode-webview-missing-csp`);
+ }
+
+ type TelemetryClassification = {
+ extension?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }
+ };
+ type TelemetryData = {
+ extension?: string,
+ };
+
+ this._telemetryService.publicLog2('webviewMissingCsp', {
+ extension: this._options.extension.id.value
+ });
+ }
+ }
+
public sendMessage(data: any): void {
this._send('message', data);
}