import * as vscode from 'vscode' ; import { RSTPreviewManager } from '../preview/previewManager' ; import * as nls from 'vscode-nls' ; const localize = nls . loadMessageBundle ( ) ; export enum RSTPreviewSecurityLevel { Strict = 0 , AllowInsecureContent = 1 , AllowScriptsAndAllContent = 2 , AllowInsecureLocalContent = 3 } export interface ContentSecurityPolicyArbiter { getSecurityLevelForResource ( resource ) : RSTPreviewSecurityLevel ; setSecurityLevelForResource ( resource , level ) : Thenable < void > ; shouldAllowSvgsForResource ( resource ) : void ; shouldDisableSecurityWarnings ( ) : boolean ; setShouldDisableSecurityWarning ( shouldShow ) : Thenable < void > ; } export class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPolicyArbiter { private readonly old_trusted_workspace_key = 'trusted_preview_workspace:' ; private readonly security_level_key = 'preview_security_level:' ; private readonly should_disable_security_warning_key = 'preview_should_show_security_warning:' ; constructor ( private readonly globalState , private readonly workspaceState ) { } public getSecurityLevelForResource ( resource ) { const level = this . globalState . get < RSTPreviewSecurityLevel | undefined > ( this . security_level_key + this . getRoot ( resource ) , undefined ) ; if ( typeof level !== 'undefined' ) { return level ; } if ( this . globalState . get < boolean > ( this . old_trusted_workspace_key + this . getRoot ( resource ) , false ) ) { return RSTPreviewSecurityLevel . AllowScriptsAndAllContent ; } return RSTPreviewSecurityLevel . Strict ; } public setSecurityLevelForResource ( resource , level ) { return this . globalState . update ( this . security_level_key + this . getRoot ( resource ) , level ) ; } public shouldAllowSvgsForResource ( resource ) { const securityLevel = this . getSecurityLevelForResource ( resource ) ; return securityLevel === RSTPreviewSecurityLevel . AllowInsecureContent || securityLevel === RSTPreviewSecurityLevel . AllowScriptsAndAllContent ; } public shouldDisableSecurityWarnings ( ) { return this . workspaceState . get < boolean > ( this . should_disable_security_warning_key , false ) ; } public setShouldDisableSecurityWarning ( disabled ) { return this . workspaceState . update ( this . should_disable_security_warning_key , disabled ) ; } private getRoot ( resource ) { if ( vscode . workspace . workspaceFolders ) { const folderForResource = vscode . workspace . getWorkspaceFolder ( resource ) ; if ( folderForResource ) { return folderForResource . uri ; } if ( vscode . workspace . workspaceFolders . length ) { return vscode . workspace . workspaceFolders [ 0 ] . uri ; } } return resource ; } } export class PreviewSecuritySelector { public constructor ( private readonly cspArbiter , private readonly webviewManager ) { } public async showSecuritySelectorForResource ( resource ) { interface PreviewSecurityPickItem extends vscode . QuickPickItem { readonly type : 'moreinfo' | 'toggle' | RSTPreviewSecurityLevel ; } function markActiveWhen ( when ) { return when ? '• ' : '' ; } const currentSecurityLevel = this . cspArbiter . getSecurityLevelForResource ( resource ) ; const selection = await vscode . window . showQuickPick < PreviewSecurityPickItem > ( [ { type : RSTPreviewSecurityLevel . Strict , label : markActiveWhen ( currentSecurityLevel === RSTPreviewSecurityLevel . Strict ) + localize ( 'strict.title' , 'Strict' ) , description : localize ( 'strict.description' , 'Only load secure content' ) , } , { type : RSTPreviewSecurityLevel . AllowInsecureLocalContent , label : markActiveWhen ( currentSecurityLevel === RSTPreviewSecurityLevel . AllowInsecureLocalContent ) + localize ( 'insecureLocalContent.title' , 'Allow insecure local content' ) , description : localize ( 'insecureLocalContent.description' , 'Enable loading content over http served from localhost' ) , } , { type : RSTPreviewSecurityLevel . AllowInsecureContent , label : markActiveWhen ( currentSecurityLevel === RSTPreviewSecurityLevel . AllowInsecureContent ) + localize ( 'insecureContent.title' , 'Allow insecure content' ) , description : localize ( 'insecureContent.description' , 'Enable loading content over http' ) , } , { type : RSTPreviewSecurityLevel . AllowScriptsAndAllContent , label : markActiveWhen ( currentSecurityLevel === RSTPreviewSecurityLevel . AllowScriptsAndAllContent ) + localize ( 'disable.title' , 'Disable' ) , description : localize ( 'disable.description' , 'Allow all content and script execution. Not recommended' ) , } , { type : 'moreinfo' , label : localize ( 'moreInfo.title' , 'More Information' ) , description : '' } , { type : 'toggle' , label : this . cspArbiter . shouldDisableSecurityWarnings ( ) ? localize ( 'enableSecurityWarning.title' , "Enable preview security warnings in this workspace" ) : localize ( 'disableSecurityWarning.title' , "Disable preview security warning in this workspace" ) , description : localize ( 'toggleSecurityWarning.description' , 'Does not affect the content security level' ) } , ] , { placeHolder : localize ( 'preview.showPreviewSecuritySelector.title' , 'Select security settings for RST previews in this workspace' ) , } ) ; if ( ! selection ) { return ; } if ( selection . type === 'moreinfo' ) { vscode . commands . executeCommand ( 'vscode.open' , vscode . Uri . parse ( 'https://go.microsoft.com/fwlink/?linkid=854414' ) ) ; return ; } if ( selection . type === 'toggle' ) { this . cspArbiter . setShouldDisableSecurityWarning ( ! this . cspArbiter . shouldDisableSecurityWarnings ( ) ) ; return ; } else { await this . cspArbiter . setSecurityLevelForResource ( resource , selection . type ) ; } this . webviewManager . refresh ( ) ; } }