@@ -6,6 +6,47 @@ export function get<T>(key: string): T {
6
6
return substitute ( vscode . workspace . getConfiguration ( 'clangd' ) . get ( key ) ) ;
7
7
}
8
8
9
+ // Like get(), but won't load settings from workspace config unless the user has
10
+ // previously explicitly allowed this.
11
+ export function getSecure < T > ( key : string , workspaceState : vscode . Memento ) : T |
12
+ undefined {
13
+ const prop = new SecureProperty < T > ( key , workspaceState ) ;
14
+ return prop . get ( prop . blessed ?? false ) ;
15
+ }
16
+
17
+ // Like get(), but won't implicitly load settings from workspace config.
18
+ // If there is workspace config, prompts the user and caches the decision.
19
+ export async function getSecureOrPrompt < T > (
20
+ key : string , workspaceState : vscode . Memento ) : Promise < T > {
21
+ const prop = new SecureProperty < T > ( key , workspaceState ) ;
22
+ // Common case: value not overridden in workspace.
23
+ if ( ! prop . mismatched )
24
+ return prop . get ( false ) ;
25
+ // Check whether user has blessed or blocked this value.
26
+ const blessed = prop . blessed ;
27
+ if ( blessed !== null )
28
+ return prop . get ( blessed ) ;
29
+ // No cached decision for this value, ask the user.
30
+ const Yes = 'Yes, use this setting' , No = 'No, use my default' ,
31
+ Info = 'More Info'
32
+ switch ( await vscode . window . showWarningMessage (
33
+ `This workspace wants to set clangd.${ key } to ${ prop . insecureJSON } .
34
+ \u2029
35
+ This will override your default of ${ prop . secureJSON } .` ,
36
+ Yes , No , Info ) ) {
37
+ case Info :
38
+ vscode . env . openExternal ( vscode . Uri . parse (
39
+ 'https://github.com/clangd/vscode-clangd/blob/master/docs/settings.md#security' ) ) ;
40
+ break ;
41
+ case Yes :
42
+ await prop . bless ( true ) ;
43
+ return prop . get ( true ) ;
44
+ case No :
45
+ await prop . bless ( false ) ;
46
+ }
47
+ return prop . get ( false ) ;
48
+ }
49
+
9
50
// Sets the config value `clangd.<key>`. Does not apply substitutions.
10
51
export function update < T > ( key : string , value : T ,
11
52
target ?: vscode . ConfigurationTarget ) {
@@ -54,3 +95,48 @@ function replacement(name: string): string|null {
54
95
55
96
return null ;
56
97
}
98
+
99
+ // Caches a user's decision about whether to respect a workspace override of a
100
+ // sensitive setting. Valid only for a particular variable, workspace, and
101
+ // value.
102
+ interface BlessCache {
103
+ json : string // JSON-serialized workspace value that the decision governs.
104
+ allowed : boolean // Whether the user chose to allow this value.
105
+ }
106
+
107
+ // Common logic between getSecure and getSecureOrPrompt.
108
+ class SecureProperty < T > {
109
+ secure : T | undefined ;
110
+ insecure : T | undefined ;
111
+ public secureJSON : string ;
112
+ public insecureJSON : string ;
113
+ blessKey : string ;
114
+
115
+ constructor ( key : string , private workspaceState : vscode . Memento ) {
116
+ const cfg = vscode . workspace . getConfiguration ( 'clangd' ) ;
117
+ const inspect = cfg . inspect < T > ( key ) ;
118
+ this . secure = inspect . globalValue ?? inspect . defaultValue ;
119
+ this . insecure = cfg . get < T > ( key ) ;
120
+ this . secureJSON = JSON . stringify ( this . secure ) ;
121
+ this . insecureJSON = JSON . stringify ( this . insecure ) ;
122
+ this . blessKey = 'bless.' + key ;
123
+ }
124
+
125
+ get mismatched ( ) : boolean { return this . secureJSON != this . insecureJSON ; }
126
+
127
+ get ( trusted : boolean ) : T | undefined {
128
+ return substitute ( trusted ? this . insecure : this . secure ) ;
129
+ }
130
+
131
+ get blessed ( ) : boolean | null {
132
+ let cache = this . workspaceState . get < BlessCache > ( this . blessKey ) ;
133
+ if ( ! cache || cache . json != this . insecureJSON )
134
+ return null ;
135
+ return cache . allowed ;
136
+ }
137
+
138
+ async bless ( b : boolean ) : Promise < void > {
139
+ await this . workspaceState . update ( this . blessKey ,
140
+ { json : this . insecureJSON , allowed : b } ) ;
141
+ }
142
+ }
0 commit comments