1
+ //
2
+ // Copyright (c) Microsoft Corporation.
3
+ //
4
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10
+ // THE SOFTWARE.
11
+ //
12
+
13
+ using System ;
14
+ using System . Collections ;
15
+ using System . Collections . Generic ;
16
+ using System . IO ;
17
+ using System . Linq ;
18
+ using System . Management . Automation . Language ;
19
+
20
+ namespace Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic
21
+ {
22
+ public class Settings
23
+ {
24
+ private List < string > includeRules ;
25
+ private List < string > excludeRules ;
26
+ private List < string > severities ;
27
+ private Dictionary < string , Dictionary < string , object > > ruleArguments ;
28
+
29
+ public IEnumerable < string > IncludeRules { get { return includeRules ; } }
30
+ public IEnumerable < string > ExcludeRules { get { return excludeRules ; } }
31
+ public IEnumerable < string > Severity { get { return severities ; } }
32
+ public Dictionary < string , Dictionary < string , object > > RuleArguments { get { return ruleArguments ; } }
33
+
34
+ public Settings ( object settings , Func < string , string > presetResolver )
35
+ {
36
+ if ( settings == null )
37
+ {
38
+ throw new ArgumentNullException ( "settings" ) ;
39
+ }
40
+
41
+ var settingsFilePath = settings as string ;
42
+
43
+ //it can either be a preset or path to a file or a hashtable
44
+ if ( settingsFilePath != null )
45
+ {
46
+ if ( presetResolver != null )
47
+ {
48
+ var resolvedFilePath = presetResolver ( settingsFilePath ) ;
49
+ if ( resolvedFilePath != null )
50
+ {
51
+ settingsFilePath = resolvedFilePath ;
52
+ }
53
+ }
54
+
55
+ if ( File . Exists ( settingsFilePath ) )
56
+ {
57
+ parseSettingsFile ( settingsFilePath ) ;
58
+ }
59
+ else
60
+ {
61
+ throw new ArgumentException ( String . Format ( "File does not exist: {0}" , settingsFilePath ) ) ;
62
+ }
63
+ }
64
+ else
65
+ {
66
+ var settingsHashtable = settings as Hashtable ;
67
+ if ( settingsHashtable != null )
68
+ {
69
+ parseSettingsHashtable ( settingsHashtable ) ;
70
+ }
71
+ else
72
+ {
73
+ throw new ArgumentException ( "Input object should either be a string or a hashtable" ) ;
74
+ }
75
+ }
76
+ }
77
+
78
+ public Settings ( object settings ) : this ( settings , null )
79
+ {
80
+ }
81
+
82
+ /// <summary>
83
+ /// Recursively convert hashtable to dictionary
84
+ /// </summary>
85
+ /// <param name="hashtable"></param>
86
+ /// <returns>Dictionary that maps string to object</returns>
87
+ private Dictionary < string , object > GetDictionaryFromHashtable ( Hashtable hashtable )
88
+ {
89
+ var dictionary = new Dictionary < string , object > ( StringComparer . OrdinalIgnoreCase ) ;
90
+ foreach ( var obj in hashtable . Keys )
91
+ {
92
+ string key = obj as string ;
93
+ if ( key == null )
94
+ {
95
+ throw new InvalidDataException ( "key not string" ) ;
96
+ // writer.WriteError(
97
+ // new ErrorRecord(
98
+ // new InvalidDataException(string.Format(CultureInfo.CurrentCulture, Strings.KeyNotString, key)),
99
+ // Strings.ConfigurationKeyNotAString,
100
+ // ErrorCategory.InvalidData,
101
+ // hashtable));
102
+ // hasError = true;
103
+ }
104
+ var valueHashtableObj = hashtable [ obj ] ;
105
+ if ( valueHashtableObj == null )
106
+ {
107
+ throw new InvalidDataException ( "wrong hash table value" ) ;
108
+ // writer.WriteError(
109
+ // new ErrorRecord(
110
+ // new InvalidDataException(string.Format(CultureInfo.CurrentCulture, Strings.WrongValueHashTable, valueHashtableObj, key)),
111
+ // Strings.WrongConfigurationKey,
112
+ // ErrorCategory.InvalidData,
113
+ // hashtable));
114
+ // hasError = true;
115
+ // return null;
116
+ }
117
+ var valueHashtable = valueHashtableObj as Hashtable ;
118
+ if ( valueHashtable == null )
119
+ {
120
+ dictionary . Add ( key , valueHashtableObj ) ;
121
+ }
122
+ else
123
+ {
124
+ dictionary . Add ( key , GetDictionaryFromHashtable ( valueHashtable ) ) ;
125
+ }
126
+ }
127
+ return dictionary ;
128
+ }
129
+
130
+ private bool IsStringOrStringArray ( object val )
131
+ {
132
+ if ( val is string )
133
+ {
134
+ return true ;
135
+ }
136
+
137
+ var valArr = val as object [ ] ;
138
+ return val == null ? false : valArr . All ( x => x is string ) ;
139
+ }
140
+
141
+ private List < string > GetData ( object val , string key )
142
+ {
143
+ // value must be either string or or an array of strings
144
+ if ( val == null )
145
+ {
146
+ throw new InvalidDataException (
147
+ String . Format (
148
+ "value should be a string or string array for {0} key" ,
149
+ key ) ) ;
150
+ // writer.WriteError(
151
+ // new ErrorRecord(
152
+ // new InvalidDataException(string.Format(CultureInfo.CurrentCulture, Strings.WrongValueHashTable, value, key)),
153
+ // Strings.WrongConfigurationKey,
154
+ // ErrorCategory.InvalidData,
155
+ // profile));
156
+ // hasError = true;
157
+ // break;
158
+ }
159
+
160
+ List < string > values = new List < string > ( ) ;
161
+ var valueStr = val as string ;
162
+ if ( valueStr != null )
163
+ {
164
+ values . Add ( valueStr ) ;
165
+ }
166
+ else
167
+ {
168
+ var valueArr = val as object [ ] ;
169
+ if ( valueArr != null )
170
+ {
171
+ foreach ( var item in valueArr )
172
+ {
173
+ var itemStr = item as string ;
174
+ if ( itemStr != null )
175
+ {
176
+ values . Add ( itemStr ) ;
177
+ }
178
+ else
179
+ {
180
+ throw new InvalidDataException ( "array items should be of string type" ) ;
181
+ // writer.WriteError(
182
+ // new ErrorRecord(
183
+ // new InvalidDataException(string.Format(CultureInfo.CurrentCulture, Strings.WrongValueHashTable, val, key)),
184
+ // Strings.WrongConfigurationKey,
185
+ // ErrorCategory.InvalidData,
186
+ // profile));
187
+ // hasError = true;
188
+ // break;
189
+ }
190
+ }
191
+ }
192
+ else
193
+ {
194
+ throw new InvalidDataException ( "array items should be of string type" ) ;
195
+ }
196
+ }
197
+
198
+ return values ;
199
+ }
200
+
201
+ /// <summary>
202
+ /// Sets the arguments for consumption by rules
203
+ /// </summary>
204
+ /// <param name="ruleArgs">A hashtable with rule names as keys</param>
205
+ public Dictionary < string , Dictionary < string , object > > ConvertToRuleArgumentType ( object ruleArguments )
206
+ {
207
+ var ruleArgs = ruleArguments as Dictionary < string , object > ;
208
+ if ( ruleArgs == null )
209
+ {
210
+ throw new ArgumentException (
211
+ "input should be a dictionary" ,
212
+ "ruleArguments" ) ;
213
+ }
214
+
215
+ if ( ruleArgs . Comparer != StringComparer . OrdinalIgnoreCase )
216
+ {
217
+ throw new ArgumentException (
218
+ "Input dictionary should have OrdinalIgnoreCase comparer." ,
219
+ "ruleArguments" ) ;
220
+ }
221
+
222
+ var ruleArgsDict = new Dictionary < string , Dictionary < string , object > > ( StringComparer . OrdinalIgnoreCase ) ;
223
+ foreach ( var rule in ruleArgs . Keys )
224
+ {
225
+ var argsDict = ruleArgs [ rule ] as Dictionary < string , object > ;
226
+ if ( argsDict == null )
227
+ {
228
+ throw new ArgumentException (
229
+ "input should be a dictionary" ,
230
+ "ruleArguments" ) ;
231
+ }
232
+ ruleArgsDict [ rule ] = argsDict ;
233
+ }
234
+
235
+ return ruleArgsDict ;
236
+ }
237
+
238
+ private void parseSettingsHashtable ( Hashtable settingsHashtable )
239
+ {
240
+ HashSet < string > validKeys = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
241
+ var settings = GetDictionaryFromHashtable ( settingsHashtable ) ;
242
+ foreach ( var settingKey in settings . Keys )
243
+ {
244
+ var key = settingKey . ToLower ( ) ;
245
+ object val = settings [ key ] ;
246
+ switch ( key )
247
+ {
248
+ case "severity" :
249
+ severities = GetData ( val , key ) ;
250
+ break ;
251
+
252
+ case "includerules" :
253
+ includeRules = GetData ( val , key ) ;
254
+ break ;
255
+
256
+ case "excluderules" :
257
+ excludeRules = GetData ( val , key ) ;
258
+ break ;
259
+
260
+ case "rules" :
261
+ ruleArguments = ConvertToRuleArgumentType ( val ) ;
262
+ break ;
263
+
264
+ default :
265
+ throw new InvalidDataException ( String . Format ( "Invalid key: {0}" , key ) ) ;
266
+ // writer.WriteError(
267
+ // new ErrorRecord(
268
+ // new InvalidDataException(string.Format(CultureInfo.CurrentCulture, Strings.WrongKeyHashTable, key)),
269
+ // Strings.WrongConfigurationKey,
270
+ // ErrorCategory.InvalidData,
271
+ // profile));
272
+ // hasError = true;
273
+ // break;
274
+ }
275
+ }
276
+ }
277
+
278
+ private void parseSettingsFile ( string settingsFilePath )
279
+ {
280
+ Token [ ] parserTokens = null ;
281
+ ParseError [ ] parserErrors = null ;
282
+ Ast profileAst = Parser . ParseFile ( settingsFilePath , out parserTokens , out parserErrors ) ;
283
+ IEnumerable < Ast > hashTableAsts = profileAst . FindAll ( item => item is HashtableAst , false ) ;
284
+
285
+ // no hashtable, raise warning
286
+ if ( hashTableAsts . Count ( ) == 0 )
287
+ {
288
+ throw new ArgumentException ( "Given file does not contain a hashtable" ) ;
289
+ // writer.WriteError(new ErrorRecord(new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.InvalidProfile, profile)),
290
+ // Strings.ConfigurationFileHasNoHashTable, ErrorCategory.ResourceUnavailable, profile));
291
+ // hasError = true;
292
+ }
293
+
294
+ HashtableAst hashTableAst = hashTableAsts . First ( ) as HashtableAst ;
295
+ Hashtable hashtable ;
296
+ try
297
+ {
298
+ hashtable = hashTableAst . SafeGetValue ( ) as Hashtable ;
299
+ }
300
+ catch ( InvalidOperationException e )
301
+ {
302
+ throw new ArgumentException ( "input file has invalid hashtable" , e ) ;
303
+ }
304
+
305
+ if ( hashtable == null )
306
+ {
307
+ throw new ArgumentException ( "input file has invalid hashtable" ) ;
308
+ }
309
+
310
+ parseSettingsHashtable ( hashtable ) ;
311
+ }
312
+ }
313
+ }
0 commit comments