forked from asciidoctor/asciidoctor-vscode
/
security.ts
160 lines (133 loc) · 6.7 KB
/
security.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*---------------------------------------------------------------------------------------------
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode'
import { AsciidocPreviewManager } from './features/previewManager'
import * as nls from 'vscode-nls'
const localize = nls.loadMessageBundle()
export const enum AsciidocPreviewSecurityLevel {
Strict = 0,
AllowInsecureContent = 1,
AllowScriptsAndAllContent = 2,
AllowInsecureLocalContent = 3
}
export interface ContentSecurityPolicyArbiter {
getSecurityLevelForResource(resource: vscode.Uri): AsciidocPreviewSecurityLevel;
setSecurityLevelForResource(resource: vscode.Uri, level: AsciidocPreviewSecurityLevel): Promise<void>;
shouldAllowSvgsForResource(resource: vscode.Uri): void;
shouldDisableSecurityWarnings(): boolean;
setShouldDisableSecurityWarning(shouldShow: boolean): Promise<void>;
}
export class ExtensionContentSecurityPolicyArbiter implements ContentSecurityPolicyArbiter {
private readonly oldTrustedWorkspaceKey = 'trusted_preview_workspace:'
private readonly securityLevelKey = 'preview_security_level:'
private readonly shouldDisableSecurityWarningKey = 'preview_should_show_security_warning:'
constructor (
private readonly globalState: vscode.Memento,
private readonly workspaceState: vscode.Memento
) {
this.globalState = globalState
this.workspaceState = workspaceState
}
public getSecurityLevelForResource (resource: vscode.Uri): AsciidocPreviewSecurityLevel {
// Use new security level setting first
const level = this.globalState.get<AsciidocPreviewSecurityLevel | undefined>(this.securityLevelKey + this.getRoot(resource), undefined)
if (typeof level !== 'undefined') {
return level
}
// Fallback to old trusted workspace setting
if (this.globalState.get<boolean>(this.oldTrustedWorkspaceKey + this.getRoot(resource), false)) {
return AsciidocPreviewSecurityLevel.AllowScriptsAndAllContent
}
return AsciidocPreviewSecurityLevel.Strict
}
public async setSecurityLevelForResource (resource: vscode.Uri, level: AsciidocPreviewSecurityLevel): Promise<void> {
return this.globalState.update(this.securityLevelKey + this.getRoot(resource), level)
}
public shouldAllowSvgsForResource (resource: vscode.Uri) {
const securityLevel = this.getSecurityLevelForResource(resource)
return securityLevel === AsciidocPreviewSecurityLevel.AllowInsecureContent || securityLevel === AsciidocPreviewSecurityLevel.AllowScriptsAndAllContent
}
public shouldDisableSecurityWarnings (): boolean {
return this.workspaceState.get<boolean>(this.shouldDisableSecurityWarningKey, false)
}
public async setShouldDisableSecurityWarning (disabled: boolean): Promise<void> {
return this.workspaceState.update(this.shouldDisableSecurityWarningKey, disabled)
}
private getRoot (resource: vscode.Uri): vscode.Uri {
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: ContentSecurityPolicyArbiter,
private readonly webviewManager: AsciidocPreviewManager
) {
this.cspArbiter = cspArbiter
this.webviewManager = webviewManager
}
public async showSecuritySelectorForResource (resource: vscode.Uri): Promise<void> {
interface PreviewSecurityPickItem extends vscode.QuickPickItem {
readonly type: 'moreinfo' | 'toggle' | AsciidocPreviewSecurityLevel;
}
function markActiveWhen (when: boolean): string {
return when ? '• ' : ''
}
const currentSecurityLevel = this.cspArbiter.getSecurityLevelForResource(resource)
const selection = await vscode.window.showQuickPick<PreviewSecurityPickItem>(
[
{
type: AsciidocPreviewSecurityLevel.Strict,
label: markActiveWhen(currentSecurityLevel === AsciidocPreviewSecurityLevel.Strict) + localize('strict.title', 'Strict'),
description: localize('strict.description', 'Only load secure content'),
}, {
type: AsciidocPreviewSecurityLevel.AllowInsecureLocalContent,
label: markActiveWhen(currentSecurityLevel === AsciidocPreviewSecurityLevel.AllowInsecureLocalContent) + localize('insecureLocalContent.title', 'Allow insecure local content'),
description: localize('insecureLocalContent.description', 'Enable loading content over http served from localhost'),
}, {
type: AsciidocPreviewSecurityLevel.AllowInsecureContent,
label: markActiveWhen(currentSecurityLevel === AsciidocPreviewSecurityLevel.AllowInsecureContent) + localize('insecureContent.title', 'Allow insecure content'),
description: localize('insecureContent.description', 'Enable loading content over http'),
}, {
type: AsciidocPreviewSecurityLevel.AllowScriptsAndAllContent,
label: markActiveWhen(currentSecurityLevel === AsciidocPreviewSecurityLevel.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 Asciidoc 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') {
await this.cspArbiter.setShouldDisableSecurityWarning(!this.cspArbiter.shouldDisableSecurityWarnings())
return
}
await this.cspArbiter.setSecurityLevelForResource(resource, selection.type)
this.webviewManager.refresh()
}
}