diff --git a/Engine/Commands/GetScriptAnalyzerRuleCommand.cs b/Engine/Commands/GetScriptAnalyzerRuleCommand.cs index b749bc6e9..ed4799c40 100644 --- a/Engine/Commands/GetScriptAnalyzerRuleCommand.cs +++ b/Engine/Commands/GetScriptAnalyzerRuleCommand.cs @@ -10,16 +10,15 @@ // THE SOFTWARE. // -using Microsoft.PowerShell.Commands; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Management.Automation; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Commands +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands { /// /// GetScriptAnalyzerRuleCommand: Cmdlet to list all the analyzer rule names and descriptions. diff --git a/Engine/Commands/InvokeScriptAnalyzerCommand.cs b/Engine/Commands/InvokeScriptAnalyzerCommand.cs index 2c1e2f3f4..02ef89a75 100644 --- a/Engine/Commands/InvokeScriptAnalyzerCommand.cs +++ b/Engine/Commands/InvokeScriptAnalyzerCommand.cs @@ -11,22 +11,17 @@ // using System.Text.RegularExpressions; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; using System; using System.Collections.Generic; -using System.ComponentModel.Composition; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Management.Automation; using System.Management.Automation.Language; -using System.Resources; -using System.Threading; -using System.Reflection; using System.IO; -using System.Text; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Commands +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands { /// /// InvokeScriptAnalyzerCommand: Cmdlet to statically check PowerShell scripts. @@ -129,8 +124,8 @@ public SwitchParameter SuppressedOnly #region Private Members Dictionary> validationResults = new Dictionary>(); - private ScriptBlockAst ast = null; - private IEnumerable rules = null; + private ScriptBlockAst ast = null; + private IEnumerable rules = null; #endregion @@ -163,9 +158,9 @@ protected override void BeginProcessing() } else { - validationResults.Add("InvalidPaths", new List()); + validationResults.Add("InvalidPaths", new List()); validationResults.Add("ValidModPaths", new List()); - validationResults.Add("ValidDllPaths", new List()); + validationResults.Add("ValidDllPaths", new List()); } #endregion @@ -174,7 +169,7 @@ protected override void BeginProcessing() try { - if (validationResults["ValidDllPaths"].Count == 0 && + if (validationResults["ValidDllPaths"].Count == 0 && validationResults["ValidModPaths"].Count == 0) { ScriptAnalyzer.Instance.Initialize(); @@ -185,7 +180,7 @@ protected override void BeginProcessing() } } catch (Exception ex) - { + { ThrowTerminatingError(new ErrorRecord(ex, ex.HResult.ToString("X", CultureInfo.CurrentCulture), ErrorCategory.NotSpecified, this)); } @@ -228,8 +223,8 @@ private void ProcessPath(string path) if (path == null) { - ThrowTerminatingError(new ErrorRecord(new FileNotFoundException(), - string.Format(CultureInfo.CurrentCulture, Strings.FileNotFound, path), + ThrowTerminatingError(new ErrorRecord(new FileNotFoundException(), + string.Format(CultureInfo.CurrentCulture, Strings.FileNotFound, path), ErrorCategory.InvalidArgument, this)); } @@ -315,11 +310,11 @@ private void AnalyzeFile(string filePath) else { ThrowTerminatingError(new ErrorRecord(new FileNotFoundException(), - string.Format(CultureInfo.CurrentCulture, Strings.InvalidPath, filePath), + string.Format(CultureInfo.CurrentCulture, Strings.InvalidPath, filePath), ErrorCategory.InvalidArgument, filePath)); } - if (errors != null && errors.Length > 0) + if (errors != null && errors.Length > 0) { foreach (ParseError error in errors) { @@ -362,7 +357,7 @@ private void AnalyzeFile(string filePath) #region Run ScriptRules //Trim down to the leaf element of the filePath and pass it to Diagnostic Record string fileName = System.IO.Path.GetFileName(filePath); - + if (ScriptAnalyzer.Instance.ScriptRules != null) { foreach (IScriptRule scriptRule in ScriptAnalyzer.Instance.ScriptRules) @@ -433,7 +428,7 @@ private void AnalyzeFile(string filePath) break; } } - if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch)) + if ((includeRule == null || includeRegexMatch) && (excludeRule == null || !excludeRegexMatch)) { WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, tokenRule.GetName())); @@ -448,7 +443,7 @@ private void AnalyzeFile(string filePath) catch (Exception tokenRuleException) { WriteError(new ErrorRecord(tokenRuleException, Strings.RuleErrorMessage, ErrorCategory.InvalidOperation, fileName)); - } + } } } } @@ -458,46 +453,50 @@ private void AnalyzeFile(string filePath) #region DSC Resource Rules if (ScriptAnalyzer.Instance.DSCResourceRules != null) { - // Run DSC Class rule - foreach (IDSCResourceRule dscResourceRule in ScriptAnalyzer.Instance.DSCResourceRules) + // Invoke AnalyzeDSCClass only if the ast is a class based resource + if (Helper.Instance.IsDscResourceClassBased(ast)) { - bool includeRegexMatch = false; - bool excludeRegexMatch = false; - - foreach (Regex include in includeRegexList) + // Run DSC Class rule + foreach (IDSCResourceRule dscResourceRule in ScriptAnalyzer.Instance.DSCResourceRules) { - if (include.IsMatch(dscResourceRule.GetName())) + bool includeRegexMatch = false; + bool excludeRegexMatch = false; + + foreach (Regex include in includeRegexList) { - includeRegexMatch = true; - break; + if (include.IsMatch(dscResourceRule.GetName())) + { + includeRegexMatch = true; + break; + } } - } - foreach (Regex exclude in excludeRegexList) - { - if (exclude.IsMatch(dscResourceRule.GetName())) + foreach (Regex exclude in excludeRegexList) { - excludeRegexMatch = true; - break; + if (exclude.IsMatch(dscResourceRule.GetName())) + { + excludeRegexMatch = true; + break; + } } - } - - if ((includeRule == null || includeRegexMatch) && (excludeRule == null || excludeRegexMatch)) - { - WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, dscResourceRule.GetName())); - // Ensure that any unhandled errors from Rules are converted to non-terminating errors - // We want the Engine to continue functioning even if one or more Rules throws an exception - try + if ((includeRule == null || includeRegexMatch) && (excludeRule == null || excludeRegexMatch)) { - var records = Helper.Instance.SuppressRule(dscResourceRule.GetName(), ruleSuppressions, dscResourceRule.AnalyzeDSCClass(ast, filePath).ToList()); - diagnostics.AddRange(records.Item2); - suppressed.AddRange(records.Item1); + WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseRunningMessage, dscResourceRule.GetName())); + + // Ensure that any unhandled errors from Rules are converted to non-terminating errors + // We want the Engine to continue functioning even if one or more Rules throws an exception + try + { + var records = Helper.Instance.SuppressRule(dscResourceRule.GetName(), ruleSuppressions, dscResourceRule.AnalyzeDSCClass(ast, filePath).ToList()); + diagnostics.AddRange(records.Item2); + suppressed.AddRange(records.Item1); + } + catch (Exception dscResourceRuleException) + { + WriteError(new ErrorRecord(dscResourceRuleException, Strings.RuleErrorMessage, ErrorCategory.InvalidOperation, filePath)); + } } - catch (Exception dscResourceRuleException) - { - WriteError(new ErrorRecord(dscResourceRuleException, Strings.RuleErrorMessage, ErrorCategory.InvalidOperation, filePath)); - } } } @@ -543,7 +542,7 @@ private void AnalyzeFile(string filePath) } } - } + } } #endregion @@ -607,4 +606,4 @@ private void AnalyzeFile(string filePath) #endregion } -} +} \ No newline at end of file diff --git a/Engine/Generic/AvoidCmdletGeneric.cs b/Engine/Generic/AvoidCmdletGeneric.cs index 1ae87fa31..5546295bf 100644 --- a/Engine/Generic/AvoidCmdletGeneric.cs +++ b/Engine/Generic/AvoidCmdletGeneric.cs @@ -13,11 +13,9 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Management.Automation.Language; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents an abstract class for rule that checks whether the script @@ -38,7 +36,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) // Finds all CommandAsts. IEnumerable commandAsts = ast.FindAll(testAst => testAst is CommandAst, true); - List cmdletNameAndAliases = Microsoft.Windows.Powershell.ScriptAnalyzer.Helper.Instance.CmdletNameAndAliases(GetCmdletName()); + List cmdletNameAndAliases = Microsoft.Windows.PowerShell.ScriptAnalyzer.Helper.Instance.CmdletNameAndAliases(GetCmdletName()); // Iterrates all CommandAsts and check the command name. foreach (CommandAst cmdAst in commandAsts) diff --git a/Engine/Generic/AvoidParameterGeneric.cs b/Engine/Generic/AvoidParameterGeneric.cs index 2c140e39c..b61835056 100644 --- a/Engine/Generic/AvoidParameterGeneric.cs +++ b/Engine/Generic/AvoidParameterGeneric.cs @@ -12,12 +12,9 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Management.Automation.Language; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents an abstract class for rule that checks that diff --git a/Engine/Generic/DiagnosticRecord.cs b/Engine/Generic/DiagnosticRecord.cs index bc2ada6dd..d62a5217c 100644 --- a/Engine/Generic/DiagnosticRecord.cs +++ b/Engine/Generic/DiagnosticRecord.cs @@ -10,14 +10,9 @@ // THE SOFTWARE. // -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Management.Automation.Language; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents a result from a PSScriptAnalyzer rule. diff --git a/Engine/Generic/ExternalRule.cs b/Engine/Generic/ExternalRule.cs index c73792e28..b637ce166 100644 --- a/Engine/Generic/ExternalRule.cs +++ b/Engine/Generic/ExternalRule.cs @@ -10,13 +10,8 @@ // THE SOFTWARE. // -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { internal class ExternalRule : IExternalRule { diff --git a/Engine/Generic/IDSCResourceRule.cs b/Engine/Generic/IDSCResourceRule.cs index 935179b3a..cbb645fb2 100644 --- a/Engine/Generic/IDSCResourceRule.cs +++ b/Engine/Generic/IDSCResourceRule.cs @@ -13,7 +13,7 @@ using System.Collections.Generic; using System.Management.Automation.Language; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents an interface for a DSC rule that analyzes a DSC resource diff --git a/Engine/Generic/IExternalRule.cs b/Engine/Generic/IExternalRule.cs index d51f33bea..935f7fdde 100644 --- a/Engine/Generic/IExternalRule.cs +++ b/Engine/Generic/IExternalRule.cs @@ -10,13 +10,8 @@ // THE SOFTWARE. // -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents an interface for an external analyzer rule. diff --git a/Engine/Generic/ILogger.cs b/Engine/Generic/ILogger.cs index 5d51378a3..9f0adac0a 100644 --- a/Engine/Generic/ILogger.cs +++ b/Engine/Generic/ILogger.cs @@ -11,13 +11,9 @@ // using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Commands; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// ILogger: An interface for a PSScriptAnalyzer logger to output the results of PSScriptAnalyzer rules. diff --git a/Engine/Generic/IRule.cs b/Engine/Generic/IRule.cs index e9ddbbf8e..4ba707d7d 100644 --- a/Engine/Generic/IRule.cs +++ b/Engine/Generic/IRule.cs @@ -10,7 +10,7 @@ // THE SOFTWARE. // -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// An interface for an analyzer rule that analyzes the Ast. diff --git a/Engine/Generic/IScriptRule.cs b/Engine/Generic/IScriptRule.cs index c9993d266..15bafbe87 100644 --- a/Engine/Generic/IScriptRule.cs +++ b/Engine/Generic/IScriptRule.cs @@ -17,7 +17,7 @@ using System.Threading.Tasks; using System.Management.Automation.Language; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents an interface for an analyzer rule that analyzes the Ast. diff --git a/Engine/Generic/ITokenRule.cs b/Engine/Generic/ITokenRule.cs index 2213f8d57..e4450d9aa 100644 --- a/Engine/Generic/ITokenRule.cs +++ b/Engine/Generic/ITokenRule.cs @@ -13,7 +13,7 @@ using System.Collections.Generic; using System.Management.Automation.Language; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents an interface for an analyzer rule that analyzes the tokens of a script. diff --git a/Engine/Generic/LoggerInfo.cs b/Engine/Generic/LoggerInfo.cs index ebb9d72e9..c5d32bce8 100644 --- a/Engine/Generic/LoggerInfo.cs +++ b/Engine/Generic/LoggerInfo.cs @@ -10,14 +10,9 @@ // THE SOFTWARE. // -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Diagnostics.CodeAnalysis; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents an internal class to properly display the name and description of a logger. diff --git a/Engine/Generic/RuleInfo.cs b/Engine/Generic/RuleInfo.cs index 666c84afa..8c90d5e6a 100644 --- a/Engine/Generic/RuleInfo.cs +++ b/Engine/Generic/RuleInfo.cs @@ -10,14 +10,9 @@ // THE SOFTWARE. // -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Diagnostics.CodeAnalysis; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents an internal class to properly display the name and description of a rule. diff --git a/Engine/Generic/RuleSeverity.cs b/Engine/Generic/RuleSeverity.cs index 75496ed43..d975aa239 100644 --- a/Engine/Generic/RuleSeverity.cs +++ b/Engine/Generic/RuleSeverity.cs @@ -10,7 +10,7 @@ // THE SOFTWARE. // -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents the severity of a PSScriptAnalyzer rule diff --git a/Engine/Generic/RuleSuppression.cs b/Engine/Generic/RuleSuppression.cs index 93319a254..a90f1b88c 100644 --- a/Engine/Generic/RuleSuppression.cs +++ b/Engine/Generic/RuleSuppression.cs @@ -17,7 +17,7 @@ using System.Text.RegularExpressions; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// diff --git a/Engine/Generic/SkipNamedBlock.cs b/Engine/Generic/SkipNamedBlock.cs index 448c668f5..6de4dd094 100644 --- a/Engine/Generic/SkipNamedBlock.cs +++ b/Engine/Generic/SkipNamedBlock.cs @@ -13,11 +13,9 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Management.Automation.Language; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// This class extends AstVisitor2 and will skip any namedblockast and commandast diff --git a/Engine/Generic/SkipTypeDefinition.cs b/Engine/Generic/SkipTypeDefinition.cs index c95d772fe..4f6ad239d 100644 --- a/Engine/Generic/SkipTypeDefinition.cs +++ b/Engine/Generic/SkipTypeDefinition.cs @@ -13,7 +13,7 @@ using System.Collections.Generic; using System.Management.Automation.Language; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// This class extends AstVisitor and will skip any typedefinitionast diff --git a/Engine/Generic/SourceType.cs b/Engine/Generic/SourceType.cs index bab7cd0e3..dfff87f2a 100644 --- a/Engine/Generic/SourceType.cs +++ b/Engine/Generic/SourceType.cs @@ -10,13 +10,7 @@ // THE SOFTWARE. // -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents a source name of a script analyzer rule. diff --git a/Engine/Generic/SuppressedRecord.cs b/Engine/Generic/SuppressedRecord.cs index 11fbefcfd..8a096087e 100644 --- a/Engine/Generic/SuppressedRecord.cs +++ b/Engine/Generic/SuppressedRecord.cs @@ -1,10 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +// +// Copyright (c) Microsoft Corporation. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Generic +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic { /// /// Represents a suppressed diagnostic record diff --git a/Engine/Helper.cs b/Engine/Helper.cs index c603b20ce..373f4bd58 100644 --- a/Engine/Helper.cs +++ b/Engine/Helper.cs @@ -16,11 +16,10 @@ using System.Linq; using System.Management.Automation; using System.Management.Automation.Language; -using System.Management.Automation.Runspaces; using System.Globalization; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer { /// @@ -191,6 +190,35 @@ public bool IsDscResourceModule(string filePath) return false; } + /// + /// Given an AST, checks whether dsc resource is class based or not + /// + /// + /// + public bool IsDscResourceClassBased(ScriptBlockAst ast) + { + if (null == ast) + { + return false; + } + + List dscResourceFunctionNames = new List(new string[] { "Test", "Get", "Set" }); + + IEnumerable dscClasses = ast.FindAll(item => + item is TypeDefinitionAst + && ((item as TypeDefinitionAst).IsClass) + && (item as TypeDefinitionAst).Attributes.Any(attr => String.Equals("DSCResource", attr.TypeName.FullName, StringComparison.OrdinalIgnoreCase)), true); + + // Found one or more classes marked with DscResource attribute + // So this might be a DscResource. Further validation will be performed by the individual rules + if (null != dscClasses && 0 < dscClasses.Count()) + { + return true; + } + + return false; + } + /// /// Given a commandast, checks whether positional parameters are used or not. /// @@ -280,7 +308,7 @@ public bool PositionalParameterUsed(CommandAst cmdAst) /// /// /// - public CommandInfo GetCommandInfo(string name, CommandTypes commandType=CommandTypes.All) + public CommandInfo GetCommandInfo(string name, CommandTypes commandType = CommandTypes.All) { return Helper.Instance.MyCmdlet.InvokeCommand.GetCommand(name, commandType); } @@ -294,7 +322,7 @@ public IEnumerable DscResourceFunctions(Ast ast) { List resourceFunctionNames = new List(new string[] { "Set-TargetResource", "Get-TargetResource", "Test-TargetResource" }); return ast.FindAll(item => item is FunctionDefinitionAst - && resourceFunctionNames.Contains((item as FunctionDefinitionAst).Name, StringComparer.OrdinalIgnoreCase), true); + && resourceFunctionNames.Contains((item as FunctionDefinitionAst).Name, StringComparer.OrdinalIgnoreCase), true); } /// @@ -465,6 +493,24 @@ public bool IsVariableGlobalOrEnvironment(VariableExpressionAst varAst, Ast ast) return VariableAnalysisDictionary[ast].IsGlobalOrEnvironment(varAst); } + + /// + /// Checks whether a variable is a global variable. + /// + /// + /// + public bool IsVariableGlobal(VariableExpressionAst varAst) + { + //We ignore the use of built-in variable as global variable + if (varAst.VariablePath.IsGlobal) + { + string varName = varAst.VariablePath.UserPath.Remove(varAst.VariablePath.UserPath.IndexOf("global:", StringComparison.OrdinalIgnoreCase), "global:".Length); + return !SpecialVars.InitializedVariables.Contains(varName, StringComparer.OrdinalIgnoreCase); + } + return false; + } + + /// /// Checks whether all the code path of ast returns. /// Runs InitializeVariableAnalysis before calling this method @@ -481,7 +527,7 @@ public bool AllCodePathReturns(Ast ast) var analysis = VariableAnalysisDictionary[ast]; return analysis.Exit._predecessors.All(block => block._returns || block._unreachable || block._throws); } - + /// /// Initialize variable analysis on the script ast /// @@ -701,7 +747,7 @@ public Dictionary> GetRuleSuppression(Ast ast) { ruleSuppressionList.AddRange(RuleSuppression.GetSuppressions(sbAst.ParamBlock.Attributes, sbAst.Extent.StartOffset, sbAst.Extent.EndOffset, sbAst)); } - + // Get rule suppression from functions IEnumerable funcAsts = ast.FindAll(item => item is FunctionDefinitionAst, true).Cast(); @@ -727,13 +773,13 @@ public Dictionary> GetRuleSuppression(Ast ast) List ruleSuppressions = new List(); results.Add(ruleSuppression.RuleName, ruleSuppressions); } - + results[ruleSuppression.RuleName].Add(ruleSuppression); } return results; } - + /// /// Returns a list of rule suppressions from the function /// @@ -813,102 +859,64 @@ public Tuple, List> SuppressRule(string List ruleSuppressions = ruleSuppressionsDict[ruleName]; int recordIndex = 0; - int ruleSuppressionIndex = 0; - DiagnosticRecord record = diagnostics.First(); - RuleSuppression ruleSuppression = ruleSuppressions.First(); - int suppressionCount = 0; + int startRecord = 0; + bool[] suppressed = new bool[diagnostics.Count]; - while (recordIndex < diagnostics.Count) + foreach (RuleSuppression ruleSuppression in ruleSuppressions) { - if (!String.IsNullOrWhiteSpace(ruleSuppression.Error)) + int suppressionCount = 0; + while (startRecord < diagnostics.Count && diagnostics[startRecord].Extent.StartOffset < ruleSuppression.StartOffset) { - ruleSuppressionIndex += 1; + startRecord += 1; + } + + // at this point, start offset of startRecord is greater or equals to rulesuppression.startoffset + recordIndex = startRecord; - if (ruleSuppressionIndex == ruleSuppressions.Count) + while (recordIndex < diagnostics.Count) + { + DiagnosticRecord record = diagnostics[recordIndex]; + + if (record.Extent.EndOffset > ruleSuppression.EndOffset) { break; } - ruleSuppression = ruleSuppressions[ruleSuppressionIndex]; - suppressionCount = 0; - - continue; - } - - // if the record precedes the rule suppression then we don't apply the suppression - // so we check that start of record is greater than start of suppression - if (record.Extent.StartOffset >= ruleSuppression.StartOffset) - { - // end of the rule suppression is less than the record start offset so move on to next rule suppression - if (ruleSuppression.EndOffset < record.Extent.StartOffset) + if (string.IsNullOrWhiteSpace(ruleSuppression.RuleSuppressionID)) { - ruleSuppressionIndex += 1; - - // If we cannot found any error but the rulesuppression has a rulesuppressionid then it must be used wrongly - if (!String.IsNullOrWhiteSpace(ruleSuppression.RuleSuppressionID) && suppressionCount == 0) - { - ruleSuppression.Error = String.Format(CultureInfo.CurrentCulture, Strings.RuleSuppressionErrorFormat, ruleSuppression.StartAttributeLine, - System.IO.Path.GetFileName(record.Extent.File), String.Format(Strings.RuleSuppressionIDError, ruleSuppression.RuleSuppressionID)); - Helper.Instance.MyCmdlet.WriteError(new ErrorRecord(new ArgumentException(ruleSuppression.Error), ruleSuppression.Error, ErrorCategory.InvalidArgument, ruleSuppression)); - } - - if (ruleSuppressionIndex == ruleSuppressions.Count) - { - break; - } - - ruleSuppression = ruleSuppressions[ruleSuppressionIndex]; - suppressionCount = 0; - - continue; + suppressed[recordIndex] = true; + suppressionCount += 1; } - // at this point, the record is inside the interval else { - // if the rule suppression id from the rule suppression is not null and the one from diagnostic record is not null - // and they are they are not the same then we cannot ignore the record - if (!string.IsNullOrWhiteSpace(ruleSuppression.RuleSuppressionID) && !string.IsNullOrWhiteSpace(record.RuleSuppressionID) - && !string.Equals(ruleSuppression.RuleSuppressionID, record.RuleSuppressionID, StringComparison.OrdinalIgnoreCase)) - { - suppressionCount -= 1; - unSuppressedRecords.Add(record); - } - // otherwise, we suppress the record, move on to the next. - else + //if there is a rule suppression id, we only suppressed if it matches + if (!String.IsNullOrWhiteSpace(record.RuleSuppressionID) && + string.Equals(ruleSuppression.RuleSuppressionID, record.RuleSuppressionID, StringComparison.OrdinalIgnoreCase)) { + suppressed[recordIndex] = true; suppressedRecords.Add(new SuppressedRecord(record, ruleSuppression)); + suppressionCount += 1; } } - } - else - { - unSuppressedRecords.Add(record); - } - // important assumption: this point is reached only if we want to move to the next record - recordIndex += 1; - suppressionCount += 1; + recordIndex += 1; + } - if (recordIndex == diagnostics.Count) + // If we cannot found any error but the rulesuppression has a rulesuppressionid then it must be used wrongly + if (!String.IsNullOrWhiteSpace(ruleSuppression.RuleSuppressionID) && suppressionCount == 0) { - // If we cannot found any error but the rulesuppression has a rulesuppressionid then it must be used wrongly - if (!String.IsNullOrWhiteSpace(ruleSuppression.RuleSuppressionID) && suppressionCount == 0) - { - ruleSuppression.Error = String.Format(CultureInfo.CurrentCulture, Strings.RuleSuppressionErrorFormat, ruleSuppression.StartAttributeLine, - System.IO.Path.GetFileName(record.Extent.File), String.Format(Strings.RuleSuppressionIDError, ruleSuppression.RuleSuppressionID)); - Helper.Instance.MyCmdlet.WriteError(new ErrorRecord(new ArgumentException(ruleSuppression.Error), ruleSuppression.Error, ErrorCategory.InvalidArgument, ruleSuppression)); - } - - break; + ruleSuppression.Error = String.Format(CultureInfo.CurrentCulture, Strings.RuleSuppressionErrorFormat, ruleSuppression.StartAttributeLine, + System.IO.Path.GetFileName(diagnostics.First().Extent.File), String.Format(Strings.RuleSuppressionIDError, ruleSuppression.RuleSuppressionID)); + Helper.Instance.MyCmdlet.WriteError(new ErrorRecord(new ArgumentException(ruleSuppression.Error), ruleSuppression.Error, ErrorCategory.InvalidArgument, ruleSuppression)); } - - record = diagnostics[recordIndex]; } - while (recordIndex < diagnostics.Count) + for (int i = 0; i < suppressed.Length; i += 1) { - unSuppressedRecords.Add(diagnostics[recordIndex]); - recordIndex += 1; + if (!suppressed[i]) + { + unSuppressedRecords.Add(diagnostics[i]); + } } return result; @@ -943,11 +951,11 @@ public int Compare(Tuple t1, Tuple t2) } } } - + /// /// Class used to do variable analysis on the whole script /// - public class ScriptAnalysis: ICustomAstVisitor + public class ScriptAnalysis : ICustomAstVisitor { private VariableAnalysis OuterAnalysis; @@ -1148,7 +1156,7 @@ public object VisitBreakStatement(BreakStatementAst breakStatementAst) { return null; } - + /// /// Visits body /// @@ -2396,7 +2404,7 @@ public object VisitArrayExpression(ArrayExpressionAst arrayExprAst) { return typeof(System.Array).FullName; } - + /// /// Returns type of array /// @@ -2499,7 +2507,7 @@ public object VisitIndexExpression(IndexExpressionAst indexAst) return null; } - + /// /// Only returns boolean type for unary operator that returns boolean /// @@ -2540,4 +2548,4 @@ public object VisitUsingExpression(UsingExpressionAst usingExpressionAst) return null; } } -} +} \ No newline at end of file diff --git a/Engine/Loggers/WriteObjectsLogger.cs b/Engine/Loggers/WriteObjectsLogger.cs index 3b7829f3f..1ca5d7703 100644 --- a/Engine/Loggers/WriteObjectsLogger.cs +++ b/Engine/Loggers/WriteObjectsLogger.cs @@ -11,19 +11,15 @@ // using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Commands; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands; using System.ComponentModel.Composition; using System.Globalization; using System.Reflection; using System.Resources; using System.Threading; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.Loggers +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Loggers { /// /// WriteObjectsLogger: Logs Diagnostics though WriteObject. @@ -34,7 +30,7 @@ public class WriteObjectsLogger : ILogger #region Private members private CultureInfo cul = Thread.CurrentThread.CurrentCulture; - private ResourceManager rm = new ResourceManager("Microsoft.Windows.Powershell.ScriptAnalyzer.Strings", + private ResourceManager rm = new ResourceManager("Microsoft.Windows.PowerShell.ScriptAnalyzer.Strings", Assembly.GetExecutingAssembly()); #endregion diff --git a/Engine/PSScriptAnalyzer.psd1 b/Engine/PSScriptAnalyzer.psd1 index bfa38cf53..2057d6ca3 100644 --- a/Engine/PSScriptAnalyzer.psd1 +++ b/Engine/PSScriptAnalyzer.psd1 @@ -8,7 +8,7 @@ Author = 'Microsoft Corporation' # Script module or binary module file associated with this manifest. -RootModule = 'Microsoft.Windows.Powershell.ScriptAnalyzer.dll' +RootModule = 'Microsoft.Windows.PowerShell.ScriptAnalyzer.dll' # Version number of this module. ModuleVersion = '1.0.1' diff --git a/Engine/ScriptAnalyzer.cs b/Engine/ScriptAnalyzer.cs index 715575a6e..24c2d0152 100644 --- a/Engine/ScriptAnalyzer.cs +++ b/Engine/ScriptAnalyzer.cs @@ -11,8 +11,8 @@ // using System.Text.RegularExpressions; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Commands; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System; using System.Collections.Generic; using System.ComponentModel.Composition; @@ -23,11 +23,9 @@ using System.Management.Automation.Language; using System.Management.Automation.Runspaces; using System.Reflection; -using System.Resources; using System.Globalization; -using System.Threading; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer { internal class ScriptAnalyzer { diff --git a/Engine/ScriptAnalyzer.format.ps1xml b/Engine/ScriptAnalyzer.format.ps1xml index 9839d5eac..be7ef3ab0 100644 --- a/Engine/ScriptAnalyzer.format.ps1xml +++ b/Engine/ScriptAnalyzer.format.ps1xml @@ -4,7 +4,7 @@ PSScriptAnalyzerView - Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord + Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord @@ -57,7 +57,7 @@ PSScriptAnalyzerView - Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.SuppressedRecord + Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.SuppressedRecord @@ -110,7 +110,7 @@ ScriptAnalyzerRules - Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.RuleInfo + Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.RuleInfo diff --git a/Engine/ScriptAnalyzer.types.ps1xml b/Engine/ScriptAnalyzer.types.ps1xml index ea37859ab..da989657e 100644 --- a/Engine/ScriptAnalyzer.types.ps1xml +++ b/Engine/ScriptAnalyzer.types.ps1xml @@ -1,7 +1,7 @@  - Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord + Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord Line @@ -33,7 +33,7 @@ - Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.SuppressedRecord + Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.SuppressedRecord Line @@ -71,7 +71,7 @@ - Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.RuleInfo + Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.RuleInfo PSStandardMembers diff --git a/Engine/ScriptAnalyzerEngine.csproj b/Engine/ScriptAnalyzerEngine.csproj index c259d0ea9..06f02c73e 100644 --- a/Engine/ScriptAnalyzerEngine.csproj +++ b/Engine/ScriptAnalyzerEngine.csproj @@ -6,7 +6,7 @@ {F4BDE3D0-3EEF-4157-8A3E-722DF7ADEF60} Library false - Microsoft.Windows.Powershell.ScriptAnalyzer + Microsoft.Windows.PowerShell.ScriptAnalyzer v4.5 512 @@ -31,7 +31,7 @@ false - Microsoft.Windows.Powershell.ScriptAnalyzer + Microsoft.Windows.PowerShell.ScriptAnalyzer diff --git a/Engine/SpecialVars.cs b/Engine/SpecialVars.cs index f616234cc..6c41f57fb 100644 --- a/Engine/SpecialVars.cs +++ b/Engine/SpecialVars.cs @@ -11,16 +11,11 @@ // using System; -using System.Collections; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Concurrent; using System.Linq; -using System.Reflection; -using System.Management.Automation.Language; using System.Management.Automation; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer { internal class SpecialVars { diff --git a/Engine/Strings.Designer.cs b/Engine/Strings.Designer.cs index 41b4079d7..74cebd11d 100644 --- a/Engine/Strings.Designer.cs +++ b/Engine/Strings.Designer.cs @@ -1,16 +1,14 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.35317 // -// Copyright (c) Microsoft Corporation. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ -namespace Microsoft.Windows.Powershell.ScriptAnalyzer { +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer { using System; @@ -41,7 +39,7 @@ internal Strings() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Windows.Powershell.ScriptAnalyzer.Strings", typeof(Strings).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Windows.PowerShell.ScriptAnalyzer.Strings", typeof(Strings).Assembly); resourceMan = temp; } return resourceMan; diff --git a/Engine/VariableAnalysis.cs b/Engine/VariableAnalysis.cs index 5445df4bc..314457ec3 100644 --- a/Engine/VariableAnalysis.cs +++ b/Engine/VariableAnalysis.cs @@ -11,18 +11,12 @@ // using System; -using System.Collections; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Concurrent; using System.Linq; -using System.Reflection; using System.Management.Automation.Language; -using System.Management.Automation; using System.Globalization; -using System.Text.RegularExpressions; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer { /// /// This class is used to analyze variable based on data flow @@ -86,7 +80,7 @@ private void ProcessParameters(IEnumerable parameters) type = paramAst.TypeName.GetReflectionType(); } - if (String.Equals(paramAst.TypeName.FullName, "switch", StringComparison.OrdinalIgnoreCase)) + if (paramAst.TypeName.GetReflectionType() == typeof(System.Management.Automation.SwitchParameter)) { isSwitchOrMandatory = true; } diff --git a/Engine/VariableAnalysisBase.cs b/Engine/VariableAnalysisBase.cs index 17e7cc6c8..f1d40ca66 100644 --- a/Engine/VariableAnalysisBase.cs +++ b/Engine/VariableAnalysisBase.cs @@ -14,14 +14,13 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Collections.Concurrent; using System.Linq; using System.Reflection; using System.Management.Automation.Language; using System.Management.Automation; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer { /// /// Base class for variable details @@ -406,15 +405,36 @@ public class Block internal bool _returns; internal bool _unreachable; + // Only Entry block, that can be constructed via NewEntryBlock() is reachable initially. + // all other blocks are unreachable. + // reachability of block should be proved with FlowsTo() calls. + public Block() + { + this._unreachable = true; + } + + public static Block NewEntryBlock() + { + return new Block(unreachable: false); + } + + private Block(bool unreachable) + { + this._unreachable = unreachable; + } + + /// + /// Tell flow analysis that this block can flow to next block. + /// + /// internal void FlowsTo(Block next) { if (_successors.IndexOf(next) < 0) { - if (_unreachable) + if (!_unreachable) { - next._unreachable = true; + next._unreachable = false; } - _successors.Add(next); next._predecessors.Add(this); } @@ -726,6 +746,25 @@ internal static void RenameVariables(Block block) internal static void InitializeSSA(Dictionary VariableAnalysis, Block Entry, List Blocks) { VariablesDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var block in Blocks) + { + List _unreachables = new List(); + foreach (var pred in block._predecessors) + { + if (pred._unreachable) + { + _unreachables.Add(pred); + pred._successors.Remove(block); + } + } + + foreach (var pred in _unreachables) + { + block._predecessors.Remove(pred); + } + } + InternalVariablesDictionary.Clear(); SSADictionary.Clear(); Counters.Clear(); @@ -1878,7 +1917,7 @@ public Block Exit /// public void Init() { - _entryBlock = new Block(); + _entryBlock = Block.NewEntryBlock(); _exitBlock = new Block(); } @@ -2058,6 +2097,13 @@ public object VisitIfStatement(IfStatementAst ifStmtAst) if (ifStmtAst == null) return null; Block afterStmt = new Block(); + + if (ifStmtAst.ElseClause == null) + { + // There is no else, flow can go straight to afterStmt. + _currentBlock.FlowsTo(afterStmt); + } + int clauseCount = ifStmtAst.Clauses.Count; for (int i = 0; i < clauseCount; i++) { @@ -2404,52 +2450,62 @@ public object VisitTryStatement(TryStatementAst tryStatementAst) tryStatementAst.Body.Visit(this.Decorator); - // This is either the first block in the finally, or the first block after all the catches if there is no finally. - // For return analysis, we start off assuming this block is reachable only if the end of the try body - // is reachable, but that can change if we find a catch that falls through. - var afterTry = new Block { _unreachable = _currentBlock._unreachable }; + Block lastBlockInTry = _currentBlock; + var finallyFirstBlock = tryStatementAst.Finally == null ? null : new Block(); + Block finallyLastBlock = null; + + // This is the first block after all the catches and finally (if present). + var afterTry = new Block(); + + bool isCatchAllPresent = false; foreach (var catchAst in tryStatementAst.CatchClauses) { + if (catchAst.IsCatchAll) + { + isCatchAllPresent = true; + } + // Any statement in the try block could throw and reach the catch, so assume the worst (from a data // flow perspective) and make the predecessor to the catch the block before entering the try. _currentBlock = new Block(); blockBeforeTry.FlowsTo(_currentBlock); - catchAst.Visit(this.Decorator); - - if (!_currentBlock._unreachable) - { - // The last block of the catch falls through, so we can get reach blocks after the try. - afterTry._unreachable = false; - } + _currentBlock.FlowsTo(finallyFirstBlock ?? afterTry); } - // Assume nothing in the try was executed, so flow comes from before the try. We could have the catch blocks - // also flow to this block, but that won't improve the analysis any, so skip that. - _currentBlock = afterTry; - blockBeforeTry.FlowsTo(_currentBlock); - - if (tryStatementAst.Finally != null) + if (finallyFirstBlock != null) { + lastBlockInTry.FlowsTo(finallyFirstBlock); + + _currentBlock = finallyFirstBlock; tryStatementAst.Finally.Visit(this.Decorator); + _currentBlock.FlowsTo(afterTry); + + finallyLastBlock = _currentBlock; - if (!_currentBlock._unreachable) + // For finally block, there are 2 cases: when try-body throw and when it doesn't. + // For these two cases value of 'finallyLastBlock._throws' would be different. + if (!isCatchAllPresent) { - // If the finally throws (it can't have other flow like return, break, or continue) - // then after the try/catch/finally is unreachable, otherwise it is reachable. - afterTry._unreachable = false; + // This flow exist only, if there is no catch for all exceptions. + blockBeforeTry.FlowsTo(finallyFirstBlock); + + var rethrowAfterFinallyBlock = new Block(); + finallyLastBlock.FlowsTo(rethrowAfterFinallyBlock); + rethrowAfterFinallyBlock._throws = true; + rethrowAfterFinallyBlock.FlowsTo(_exitBlock); } - // We can assume that flow from the finally reaches the code following the finally. Of course, if an exception occurs, - // then the code can't be reached, but our conservative data flow modeling assures us correctness, the exception will - // either leave the current function (meaning it doesn't matter that we assumed flow reached after the finally), or - // it will be caught and handled by a containing catch, and our conversative data flow ensures the correctness of the - // generated code. - var newBlock = new Block(); - _currentBlock.FlowsTo(newBlock); - _currentBlock = newBlock; + // This flow always exists. + finallyLastBlock.FlowsTo(afterTry); } + else + { + lastBlockInTry.FlowsTo(afterTry); + } + + _currentBlock = afterTry; return null; } @@ -2487,7 +2543,7 @@ where t.Label.Equals(labelStrAst.Value, StringComparison.OrdinalIgnoreCase) } // The next block is unreachable, but is necessary to keep the flow graph correct. - _currentBlock = new Block { _unreachable = true }; + _currentBlock = new Block(); } /// @@ -2526,7 +2582,7 @@ internal Block ControlFlowStatement(PipelineBaseAst pipelineAst) var lastBlockInStatement = _currentBlock; // The next block is unreachable, but is necessary to keep the flow graph correct. - _currentBlock = new Block { _unreachable = true }; + _currentBlock = new Block(); return lastBlockInStatement; } diff --git a/License.txt b/LICENSE similarity index 100% rename from License.txt rename to LICENSE diff --git a/README.md b/README.md index 25d4aea1a..efe28c450 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ +|Master |BugFixes |Development | +|:------:|:------:|:-------:|:-------:| +[![Build status](https://ci.appveyor.com/api/projects/status/h5mot3vqtvxw5d7l/branch/master?svg=true)](https://ci.appveyor.com/project/PowerShell/psscriptanalyzer/branch/master)|[![Build status](https://ci.appveyor.com/api/projects/status/h5mot3vqtvxw5d7l/branch/bugfixes?svg=true)](https://ci.appveyor.com/project/PowerShell/psscriptanalyzer/branch/bugfixes)|[![Build status](https://ci.appveyor.com/api/projects/status/h5mot3vqtvxw5d7l/branch/development?svg=true)](https://ci.appveyor.com/project/PowerShell/psscriptanalyzer/branch/development) | -Introduction + + +Introduction ============ PSScriptAnalyzer is a static code checker for Windows PowerShell modules and scripts. PSScriptAnalyzer checks the quality of Windows PowerShell code by running a set of rules. The rules are based on PowerShell best practices identified by PowerShell Team and the community. It generates DiagnosticResults (errors and warnings) to inform users about potential code defects and suggests possible solutions for improvements. @@ -42,14 +47,6 @@ Use Visual Studio to build "PSScriptAnalyzer.sln". Use ~/PSScriptAnalyzer/ folde **Note: If there are any build errors, please refer to Requirements section and make sure all dependencies are properly installed** -Build Status -============== - -| |Master Branch | -|---------|:------:|:------:|:-------:|:-------:| -|**Debug Version**|[![Build status](https://ci.appveyor.com/api/projects/status/h5mot3vqtvxw5d7l/branch/master?svg=true)](https://ci.appveyor.com/project/PowerShell/psscriptanalyzer/branch/master) | - - Running Tests ============= diff --git a/Rules/AvoidAlias.cs b/Rules/AvoidAlias.cs index dba65dfd0..5e9626923 100644 --- a/Rules/AvoidAlias.cs +++ b/Rules/AvoidAlias.cs @@ -13,11 +13,11 @@ using System; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidAlias: Check if cmdlet alias is used. @@ -45,7 +45,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) // MSDN: CommandAst.GetCommandName Method if (aliasName == null) continue; - string cmdletName = Microsoft.Windows.Powershell.ScriptAnalyzer.Helper.Instance.GetCmdletNameFromAlias(aliasName); + string cmdletName = Microsoft.Windows.PowerShell.ScriptAnalyzer.Helper.Instance.GetCmdletNameFromAlias(aliasName); if (!String.IsNullOrEmpty(cmdletName)) { diff --git a/Rules/AvoidDefaultTrueValueSwitchParameter.cs b/Rules/AvoidDefaultTrueValueSwitchParameter.cs index 2572a1fb9..a2f892571 100644 --- a/Rules/AvoidDefaultTrueValueSwitchParameter.cs +++ b/Rules/AvoidDefaultTrueValueSwitchParameter.cs @@ -14,11 +14,11 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidDefaultTrueValueSwitchParameter: Check that switch parameter does not default to true. diff --git a/Rules/AvoidEmptyCatchBlock.cs b/Rules/AvoidEmptyCatchBlock.cs index 1c9c57b76..6ee312d76 100644 --- a/Rules/AvoidEmptyCatchBlock.cs +++ b/Rules/AvoidEmptyCatchBlock.cs @@ -13,11 +13,11 @@ using System; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidEmptyCatchBlock: Check if any empty catch block is used. diff --git a/Rules/AvoidGlobalVars.cs b/Rules/AvoidGlobalVars.cs index 6da1cdb91..4850ff5d6 100644 --- a/Rules/AvoidGlobalVars.cs +++ b/Rules/AvoidGlobalVars.cs @@ -13,11 +13,11 @@ using System; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidGlobalVars: Analyzes the ast to check that global variables are not used. @@ -41,7 +41,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (VariableExpressionAst varAst in varAsts) { - if (varAst.VariablePath.IsGlobal) + if (Helper.Instance.IsVariableGlobal(varAst)) { yield return new DiagnosticRecord( diff --git a/Rules/AvoidInvokingEmptyMembers.cs b/Rules/AvoidInvokingEmptyMembers.cs index 562f335fd..df30c3451 100644 --- a/Rules/AvoidInvokingEmptyMembers.cs +++ b/Rules/AvoidInvokingEmptyMembers.cs @@ -14,11 +14,11 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidAlias: Check if cmdlet alias is used. diff --git a/Rules/AvoidPositionalParameters.cs b/Rules/AvoidPositionalParameters.cs index 3a644275f..248dc4f71 100644 --- a/Rules/AvoidPositionalParameters.cs +++ b/Rules/AvoidPositionalParameters.cs @@ -13,11 +13,11 @@ using System; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidPositionalParameters: Check to make sure that positional parameters are not used. diff --git a/Rules/AvoidReservedCharInCmdlet.cs b/Rules/AvoidReservedCharInCmdlet.cs index 9f4637a3a..2dddfbe3c 100644 --- a/Rules/AvoidReservedCharInCmdlet.cs +++ b/Rules/AvoidReservedCharInCmdlet.cs @@ -13,13 +13,12 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Management.Automation; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// diff --git a/Rules/AvoidReservedParams.cs b/Rules/AvoidReservedParams.cs index b0211f4f9..da1077a78 100644 --- a/Rules/AvoidReservedParams.cs +++ b/Rules/AvoidReservedParams.cs @@ -14,13 +14,13 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; using System.Management.Automation; using System.Management.Automation.Internal; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidReservedParams: Analyzes the ast to check for reserved parameters in function definitions. diff --git a/Rules/AvoidShouldContinueWithoutForce.cs b/Rules/AvoidShouldContinueWithoutForce.cs index ca91b62b0..0222200e9 100644 --- a/Rules/AvoidShouldContinueWithoutForce.cs +++ b/Rules/AvoidShouldContinueWithoutForce.cs @@ -13,11 +13,11 @@ using System; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidShouldContinueWithoutForceParameter: Check that if ShouldContinue is used, diff --git a/Rules/AvoidTrapStatement.cs b/Rules/AvoidTrapStatement.cs deleted file mode 100644 index 0dc9b43b1..000000000 --- a/Rules/AvoidTrapStatement.cs +++ /dev/null @@ -1,101 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -using System; -using System.Collections.Generic; -using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; -using System.ComponentModel.Composition; -using System.Globalization; - -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules -{ - /// - /// AvoidTrapStatement: Analyzes the ast to check that no traps are used. - /// - [Export(typeof(IScriptRule))] - public class AvoidTrapStatement : IScriptRule - { - /// - /// AnalyzeScript: Analyzes the ast to check that no traps are used. - /// - /// The script's ast - /// The script's file name - /// A List of diagnostic results of this rule - public IEnumerable AnalyzeScript(Ast ast, string fileName) - { - if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage); - - IEnumerable trapAsts = ast.FindAll(testAst => testAst is TrapStatementAst, true); - - if (trapAsts != null) - { - foreach (Ast trapAst in trapAsts) - { - yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.AvoidTrapStatementError), trapAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName); - } - } - } - - /// - /// GetName: Retrieves the name of this rule. - /// - /// The name of this rule - public string GetName() - { - return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.AvoidTrapStatementName); - } - - /// - /// GetCommonName: Retrieves the common name of this rule. - /// - /// The common name of this rule - public string GetCommonName() - { - return string.Format(CultureInfo.CurrentCulture, Strings.AvoidTrapStatementCommonName); - } - - /// - /// GetDescription: Retrieves the description of this rule. - /// - /// The description of this rule - public string GetDescription() - { - return string.Format(CultureInfo.CurrentCulture, Strings.AvoidTrapStatementDescription); - } - - /// - /// GetSourceType: Retrieves the type of the rule: builtin, managed or module. - /// - public SourceType GetSourceType() - { - return SourceType.Builtin; - } - - /// - /// GetSeverity: Retrieves the severity of the rule: error, warning of information. - /// - /// - public RuleSeverity GetSeverity() - { - return RuleSeverity.Warning; - } - - /// - /// GetSourceName: Retrieves the module/assembly name the rule is from. - /// - public string GetSourceName() - { - return string.Format(CultureInfo.CurrentCulture, Strings.SourceName); - } - } -} diff --git a/Rules/AvoidUninitializedVariable.cs b/Rules/AvoidUninitializedVariable.cs index 7a0031740..accbbcc3d 100644 --- a/Rules/AvoidUninitializedVariable.cs +++ b/Rules/AvoidUninitializedVariable.cs @@ -14,11 +14,11 @@ using System.Linq; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidUnitializedVariable: Check if any uninitialized variable is used. diff --git a/Rules/AvoidUserNameAndPasswordParams.cs b/Rules/AvoidUserNameAndPasswordParams.cs index a593b1ae7..823ca6e3c 100644 --- a/Rules/AvoidUserNameAndPasswordParams.cs +++ b/Rules/AvoidUserNameAndPasswordParams.cs @@ -14,12 +14,12 @@ using System.Collections.Generic; using System.Management.Automation; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; using System.Reflection; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidUsernameAndPasswordParams: Check that a function does not use both username and password diff --git a/Rules/AvoidUsingComputerNameHardcoded.cs b/Rules/AvoidUsingComputerNameHardcoded.cs index dab94246a..f0454d412 100644 --- a/Rules/AvoidUsingComputerNameHardcoded.cs +++ b/Rules/AvoidUsingComputerNameHardcoded.cs @@ -12,11 +12,11 @@ using System; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidUsingComputerNameHardcoded: Check that parameter ComputerName is not hardcoded. diff --git a/Rules/AvoidUsingConvertToSecureStringWithPlainText.cs b/Rules/AvoidUsingConvertToSecureStringWithPlainText.cs index 090dcb059..d9105235a 100644 --- a/Rules/AvoidUsingConvertToSecureStringWithPlainText.cs +++ b/Rules/AvoidUsingConvertToSecureStringWithPlainText.cs @@ -14,11 +14,11 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidUsingConvertToSecureStringWithPlainText: Check that convertto-securestring does not use plaintext. diff --git a/Rules/AvoidUsingDeprecatedManifestFields.cs b/Rules/AvoidUsingDeprecatedManifestFields.cs index 8e20179cd..964522490 100644 --- a/Rules/AvoidUsingDeprecatedManifestFields.cs +++ b/Rules/AvoidUsingDeprecatedManifestFields.cs @@ -14,11 +14,11 @@ using System.Collections.Generic; using System.Management.Automation.Language; using System.Management.Automation; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidUsingDeprecatedManifestFields: Run Test Module Manifest to check that no deprecated fields are being used. diff --git a/Rules/AvoidUsingInternalURLs.cs b/Rules/AvoidUsingInternalURLs.cs index 095cd5412..99844de4f 100644 --- a/Rules/AvoidUsingInternalURLs.cs +++ b/Rules/AvoidUsingInternalURLs.cs @@ -14,12 +14,12 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; using System.IO; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidUsingInternalURLs: Check if a URL is potentially an internal URL, @@ -44,6 +44,26 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (StringConstantExpressionAst expressionAst in expressionAsts) { + //Check if XPath is used. If XPath is used, then we don't throw warnings. + Ast parentAst = expressionAst.Parent; + if (parentAst is InvokeMemberExpressionAst) + { + InvokeMemberExpressionAst invocation = parentAst as InvokeMemberExpressionAst; + if (invocation != null) + { + if (String.Equals(invocation.Member.ToString(), "SelectSingleNode",StringComparison.OrdinalIgnoreCase) || + String.Equals(invocation.Member.ToString(), "SelectNodes",StringComparison.OrdinalIgnoreCase) || + String.Equals(invocation.Member.ToString(), "Select", StringComparison.OrdinalIgnoreCase) || + String.Equals(invocation.Member.ToString(), "Evaluate",StringComparison.OrdinalIgnoreCase) || + String.Equals(invocation.Member.ToString(), "Matches",StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + } + } + + bool isPathValid = false; bool isInternalURL = false; //make sure there is no path @@ -128,7 +148,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) new DiagnosticRecord( String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingInternalURLsError, expressionAst.Value), expressionAst.Extent, - GetName(), DiagnosticSeverity.Warning, fileName); + GetName(), DiagnosticSeverity.Information, fileName); } } @@ -177,7 +197,7 @@ public SourceType GetSourceType() /// public RuleSeverity GetSeverity() { - return RuleSeverity.Warning; + return RuleSeverity.Information; } /// diff --git a/Rules/AvoidUsingInvokeExpression.cs b/Rules/AvoidUsingInvokeExpression.cs index 81db6dc7a..597383495 100644 --- a/Rules/AvoidUsingInvokeExpression.cs +++ b/Rules/AvoidUsingInvokeExpression.cs @@ -11,11 +11,11 @@ // using System; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// InvokeExpressionRule: Check to make sure that Invoke-Expression is not used. diff --git a/Rules/AvoidUsingPlainTextForPassword.cs b/Rules/AvoidUsingPlainTextForPassword.cs index a4f6e0d9d..4a69c6812 100644 --- a/Rules/AvoidUsingPlainTextForPassword.cs +++ b/Rules/AvoidUsingPlainTextForPassword.cs @@ -13,12 +13,12 @@ using System; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; using System.Reflection; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidUsingPlainTextForPassword: Check that parameter "password", "passphrase" do not use plaintext @@ -55,8 +55,8 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) } } - if (hasPwd && (!paramType.IsArray && paramType != typeof(System.Security.SecureString) - || (paramType.IsArray && paramType.GetElementType() != typeof(System.Security.SecureString)))) + if (hasPwd && ((!paramType.IsArray && (paramType == typeof(String) || paramType == typeof(object))) + || (paramType.IsArray && (paramType.GetElementType() == typeof(String) || paramType.GetElementType() == typeof(object))))) { yield return new DiagnosticRecord( String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingPlainTextForPasswordError, paramAst.Name), diff --git a/Rules/AvoidUsingWMICmdlet.cs b/Rules/AvoidUsingWMICmdlet.cs index 079f0ec14..d5104a298 100644 --- a/Rules/AvoidUsingWMICmdlet.cs +++ b/Rules/AvoidUsingWMICmdlet.cs @@ -11,21 +11,13 @@ // using System; -using System.Collections.ObjectModel; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Management.Automation; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; -using System.Resources; using System.Globalization; -using System.Threading; -using System.Reflection; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidUsingWMICmdlet: Avoid Using Get-WMIObject, Remove-WMIObject, Invoke-WmiMethod, Register-WmiEvent, Set-WmiInstance diff --git a/Rules/AvoidUsingWriteHost.cs b/Rules/AvoidUsingWriteHost.cs index fcc06a38b..e2339cb75 100644 --- a/Rules/AvoidUsingWriteHost.cs +++ b/Rules/AvoidUsingWriteHost.cs @@ -13,11 +13,11 @@ using System; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// AvoidUsingWriteHost: Check that Write-Host or Console.Write are not used diff --git a/Rules/DscExamplesPresent.cs b/Rules/DscExamplesPresent.cs index 4d3129c4c..e0f9b53a1 100644 --- a/Rules/DscExamplesPresent.cs +++ b/Rules/DscExamplesPresent.cs @@ -14,13 +14,12 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; using System.IO; -using System.Management.Automation; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// DscExamplesPresent: Checks that DSC examples for given resource are present. diff --git a/Rules/DscTestsPresent.cs b/Rules/DscTestsPresent.cs index 15fbbda23..9a25ece36 100644 --- a/Rules/DscTestsPresent.cs +++ b/Rules/DscTestsPresent.cs @@ -14,13 +14,12 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; using System.IO; -using System.Management.Automation; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// DscTestsPresent: Checks that DSC tests for given resource are present. diff --git a/Rules/MissingModuleManifestField.cs b/Rules/MissingModuleManifestField.cs index 012c18069..9b0dd5954 100644 --- a/Rules/MissingModuleManifestField.cs +++ b/Rules/MissingModuleManifestField.cs @@ -12,14 +12,13 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Management.Automation.Language; using System.Management.Automation; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// MissingModuleManifestField: Run Test Module Manifest to check that none of the required fields are missing. diff --git a/Rules/PossibleIncorrectComparisonWithNull.cs b/Rules/PossibleIncorrectComparisonWithNull.cs index 507832322..da97c13f6 100644 --- a/Rules/PossibleIncorrectComparisonWithNull.cs +++ b/Rules/PossibleIncorrectComparisonWithNull.cs @@ -13,11 +13,11 @@ using System; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// NullComparisonRule: Analyzes the ast to check that $null is on the left side of any equality comparisons. diff --git a/Rules/ProvideCommentHelp.cs b/Rules/ProvideCommentHelp.cs index fbb03b2b9..d98aa26aa 100644 --- a/Rules/ProvideCommentHelp.cs +++ b/Rules/ProvideCommentHelp.cs @@ -14,12 +14,12 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; using System.Management.Automation; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// ProvideCommentHelp: Analyzes ast to check that cmdlets have help. diff --git a/Rules/ProvideDefaultParameterValue.cs b/Rules/ProvideDefaultParameterValue.cs index b10c2a70b..3d5c24023 100644 --- a/Rules/ProvideDefaultParameterValue.cs +++ b/Rules/ProvideDefaultParameterValue.cs @@ -14,11 +14,11 @@ using System.Linq; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// ProvideDefaultParameterValue: Check if any uninitialized variable is used. diff --git a/Rules/ProvideVerboseMessage.cs b/Rules/ProvideVerboseMessage.cs index fb4b3f15b..b3d191b56 100644 --- a/Rules/ProvideVerboseMessage.cs +++ b/Rules/ProvideVerboseMessage.cs @@ -14,12 +14,12 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; using System.Management.Automation; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// ProvideVerboseMessage: Analyzes the ast to check that Write-Verbose is called at least once in every cmdlet or script. diff --git a/Rules/ReturnCorrectTypesForDSCFunctions.cs b/Rules/ReturnCorrectTypesForDSCFunctions.cs index 90aeeeabc..127f12d6e 100644 --- a/Rules/ReturnCorrectTypesForDSCFunctions.cs +++ b/Rules/ReturnCorrectTypesForDSCFunctions.cs @@ -14,11 +14,11 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// ReturnCorrectTypeDSCFunctions: Check that DSC functions return the correct type. diff --git a/Rules/ScriptAnalyzerBuiltinRules.csproj b/Rules/ScriptAnalyzerBuiltinRules.csproj index 087930117..e9b8a56f6 100644 --- a/Rules/ScriptAnalyzerBuiltinRules.csproj +++ b/Rules/ScriptAnalyzerBuiltinRules.csproj @@ -6,7 +6,7 @@ {C33B6B9D-E61C-45A3-9103-895FD82A5C1E} Library false - Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules + Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules v4.5 512 @@ -31,7 +31,7 @@ false - Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules + Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules @@ -58,7 +58,6 @@ - diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index 00f24aa6f..54175ef88 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules { +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { using System; @@ -39,7 +39,7 @@ internal Strings() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules.Strings", typeof(Strings).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.Strings", typeof(Strings).Assembly); resourceMan = temp; } return resourceMan; @@ -331,7 +331,7 @@ internal static string AvoidUnloadableModuleCommonName { } /// - /// Looks up a localized string similar to If a script file is in a Powershell module folder, then that folder must be loadable.. + /// Looks up a localized string similar to If a script file is in a PowerShell module folder, then that folder must be loadable.. /// internal static string AvoidUnloadableModuleDescription { get { diff --git a/Rules/Strings.resx b/Rules/Strings.resx index 7686518a9..c16fa35e3 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -295,7 +295,7 @@ Module Manifest Fields - If a script file is in a Powershell module folder, then that folder must be loadable. + If a script file is in a PowerShell module folder, then that folder must be loadable. Cannot load the module '{0}' that file '{1}' is in. diff --git a/Rules/UseApprovedVerbs.cs b/Rules/UseApprovedVerbs.cs index 833bf4f03..4a5ae083b 100644 --- a/Rules/UseApprovedVerbs.cs +++ b/Rules/UseApprovedVerbs.cs @@ -15,12 +15,12 @@ using System.Linq; using System.Management.Automation; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; using System.Reflection; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// UseApprovedVerbs: Analyzes scripts to check that all defined functions use approved verbs. diff --git a/Rules/UseCmdletCorrectly.cs b/Rules/UseCmdletCorrectly.cs index a5e854b0b..430e9a2d4 100644 --- a/Rules/UseCmdletCorrectly.cs +++ b/Rules/UseCmdletCorrectly.cs @@ -15,11 +15,11 @@ using System.Linq; using System.Management.Automation; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// Use CmdletCorrectly: Check that cmdlets are invoked with the correct mandatory parameter diff --git a/Rules/UseDeclaredVarsMoreThanAssignments.cs b/Rules/UseDeclaredVarsMoreThanAssignments.cs index c6e346a4f..1f1ecf089 100644 --- a/Rules/UseDeclaredVarsMoreThanAssignments.cs +++ b/Rules/UseDeclaredVarsMoreThanAssignments.cs @@ -13,11 +13,11 @@ using System; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// ExtraVarsRule: Analyzes the ast to check that variables are used in more than just their assignment. @@ -49,12 +49,14 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (AssignmentStatementAst assignmentAst in assignmentAsts) { - assingmentVarAsts = assignmentAst.Left.FindAll(testAst => testAst is VariableExpressionAst, true); ; + // Only checks for the case where lhs is a variable. Ignore things like $foo.property + VariableExpressionAst assignmentVarAst = assignmentAst.Left as VariableExpressionAst; - foreach (VariableExpressionAst assignmentVarAst in assingmentVarAsts) + if (assignmentVarAst != null) { - //Ignore if variable is global or environment variable - if (!Helper.Instance.IsVariableGlobalOrEnvironment(assignmentVarAst, ast)) + //Ignore if variable is global or environment variable + if (!Helper.Instance.IsVariableGlobalOrEnvironment(assignmentVarAst, ast) + && !assignmentVarAst.VariablePath.IsScript) { if (!assignments.ContainsKey(assignmentVarAst.VariablePath.UserPath)) { diff --git a/Rules/UseIdenticalMandatoryParametersDSC.cs b/Rules/UseIdenticalMandatoryParametersDSC.cs index b2aaa3c16..f43c6f6c3 100644 --- a/Rules/UseIdenticalMandatoryParametersDSC.cs +++ b/Rules/UseIdenticalMandatoryParametersDSC.cs @@ -14,11 +14,11 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// UseIdenticalMandatoryParametersDSC: Check that the Get/Test/Set TargetResource diff --git a/Rules/UseIdenticalParametersDSC.cs b/Rules/UseIdenticalParametersDSC.cs index 8318c6155..99b9fac5e 100644 --- a/Rules/UseIdenticalParametersDSC.cs +++ b/Rules/UseIdenticalParametersDSC.cs @@ -14,11 +14,11 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// UseIdenticalParametersDSC: Check that the Test-TargetResource and diff --git a/Rules/UseOutputTypeCorrectly.cs b/Rules/UseOutputTypeCorrectly.cs index 5d04de225..a41c3a23c 100644 --- a/Rules/UseOutputTypeCorrectly.cs +++ b/Rules/UseOutputTypeCorrectly.cs @@ -14,12 +14,12 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; using System.Management.Automation; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// ProvideCommentHelp: Checks that objects return in a cmdlet have their types declared in OutputType Attribute diff --git a/Rules/UsePSCredentialType.cs b/Rules/UsePSCredentialType.cs index a932686d4..99ddbd70f 100644 --- a/Rules/UsePSCredentialType.cs +++ b/Rules/UsePSCredentialType.cs @@ -14,11 +14,11 @@ using System.Collections.Generic; using System.Management.Automation; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// diff --git a/Rules/UseShouldProcessCorrectly.cs b/Rules/UseShouldProcessCorrectly.cs index de40351cc..fef838fd0 100644 --- a/Rules/UseShouldProcessCorrectly.cs +++ b/Rules/UseShouldProcessCorrectly.cs @@ -13,11 +13,11 @@ using System; using System.Collections.Generic; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// UseShouldProcessCorrectly: Analyzes the ast to check that if the ShouldProcess attribute is present, the function calls ShouldProcess and vice versa. diff --git a/Rules/UseShouldProcessForStateChangingFunctions.cs b/Rules/UseShouldProcessForStateChangingFunctions.cs index 28c50aca4..c4fcf8449 100644 --- a/Rules/UseShouldProcessForStateChangingFunctions.cs +++ b/Rules/UseShouldProcessForStateChangingFunctions.cs @@ -15,11 +15,11 @@ using System.Linq; using System.Management.Automation; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules {/// /// UseShouldProcessCorrectly: Analyzes the ast to check that if the ShouldProcess attribute is present, the function calls ShouldProcess and vice versa. /// diff --git a/Rules/UseSingularNouns.cs b/Rules/UseSingularNouns.cs index 503e7162b..78ecf16fd 100644 --- a/Rules/UseSingularNouns.cs +++ b/Rules/UseSingularNouns.cs @@ -13,13 +13,12 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Management.Automation; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// CmdletSingularNoun: Analyzes scripts to check that all defined cmdlets use singular nouns. @@ -49,7 +48,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { String noun = funcNamePieces[1]; var ps = System.Data.Entity.Design.PluralizationServices.PluralizationService.CreateService(CultureInfo.GetCultureInfo("en-us")); - if (!ps.IsSingular(noun)) + if (!ps.IsSingular(noun) && ps.IsPlural(noun)) { yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UseSingularNounsError, funcAst.Name), funcAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName); diff --git a/Rules/UseStandardDSCFunctionsInResource.cs b/Rules/UseStandardDSCFunctionsInResource.cs index 67e9728df..d8091eacb 100644 --- a/Rules/UseStandardDSCFunctionsInResource.cs +++ b/Rules/UseStandardDSCFunctionsInResource.cs @@ -14,11 +14,11 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation.Language; -using Microsoft.Windows.Powershell.ScriptAnalyzer.Generic; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; -namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// /// UseStandardDSCFunctionsInResource: diff --git a/ScriptRuleDocumentation.md b/ScriptRuleDocumentation.md index 51e58f5a1..2be261fee 100644 --- a/ScriptRuleDocumentation.md +++ b/ScriptRuleDocumentation.md @@ -20,7 +20,7 @@ This documentation serves as a basic guideline on how to define customized rules - Output type should be DiagnosticRecord: ``` -[OutputType([Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]])] +[OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])] ``` - Make sure each function takes either a Token or an Ast as a parameter @@ -36,7 +36,7 @@ Param - DiagnosticRecord should have four properties: Message, Extent, RuleName and Severity ``` -$result = [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]]@{"Message" = "This is a sample rule"; +$result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]]@{"Message" = "This is a sample rule"; "Extent" = $ast.Extent; "RuleName" = $PSCmdlet.MyInvocation.InvocationName; "Severity" = "Warning"} @@ -62,14 +62,14 @@ Export-ModuleMember -Function (FunctionName) .INPUTS [System.Management.Automation.Language.ScriptBlockAst] .OUTPUTS - [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]] + [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]] .NOTES None #> function Measure-RequiresRunAsAdministrator { [CmdletBinding()] - [OutputType([Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord[]])] + [OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]])] Param ( [Parameter(Mandatory = $true)] @@ -127,7 +127,7 @@ function Measure-RequiresRunAsAdministrator if ((!$ScriptBlockAst.ScriptRequirements.IsElevationRequired) -and ($methodAst.Count -ne 0) -and ($assignmentAst.Count -ne 0)) { - $result = [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord]@{"Message" = $Messages.MeasureRequiresRunAsAdministrator; + $result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{"Message" = $Messages.MeasureRequiresRunAsAdministrator; "Extent" = $assignmentAst.Extent; "RuleName" = $PSCmdlet.MyInvocation.InvocationName; "Severity" = "Information"} @@ -138,7 +138,7 @@ function Measure-RequiresRunAsAdministrator { if (($methodAst.Count -ne 0) -and ($assignmentAst.Count -ne 0)) { - $result = [Microsoft.Windows.Powershell.ScriptAnalyzer.Generic.DiagnosticRecord]@{"Message" = $Messages.MeasureRequiresRunAsAdministrator; + $result = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{"Message" = $Messages.MeasureRequiresRunAsAdministrator; "Extent" = $assignmentAst.Extent; "RuleName" = $PSCmdlet.MyInvocation.InvocationName; "Severity" = "Information"} diff --git a/Tests/Rules/AvoidTrapStatements.ps1 b/Tests/Disabled Rules/AvoidTrapStatements.ps1 similarity index 100% rename from Tests/Rules/AvoidTrapStatements.ps1 rename to Tests/Disabled Rules/AvoidTrapStatements.ps1 diff --git a/Tests/Rules/AvoidTrapStatements.tests.ps1 b/Tests/Disabled Rules/AvoidTrapStatements.tests.ps1 similarity index 100% rename from Tests/Rules/AvoidTrapStatements.tests.ps1 rename to Tests/Disabled Rules/AvoidTrapStatements.tests.ps1 diff --git a/Tests/Engine/GetScriptAnalyzerRule.tests.ps1 b/Tests/Engine/GetScriptAnalyzerRule.tests.ps1 index e299b45e2..fa5346b6a 100644 --- a/Tests/Engine/GetScriptAnalyzerRule.tests.ps1 +++ b/Tests/Engine/GetScriptAnalyzerRule.tests.ps1 @@ -118,7 +118,7 @@ Describe "TestSeverity" { It "filters rules based on multiple severity inputs"{ $rules = Get-ScriptAnalyzerRule -Severity Error,Information - $rules.Count | Should be 12 + $rules.Count | Should be 13 } It "takes lower case inputs" { @@ -137,4 +137,4 @@ Describe "TestWildCard" { $rules = Get-ScriptAnalyzerRule -Name PSDSC* -Severity Information $rules.Count | Should be 3 } -} \ No newline at end of file +} diff --git a/Tests/Engine/RuleSuppression.ps1 b/Tests/Engine/RuleSuppression.ps1 index 44fb3e867..d5cd4e055 100644 --- a/Tests/Engine/RuleSuppression.ps1 +++ b/Tests/Engine/RuleSuppression.ps1 @@ -14,6 +14,15 @@ function SuppressMe () } +function SuppressTwoVariables() +{ + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSProvideDefaultParameterValue", "b")] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSProvideDefaultParameterValue", "a")] + Param([string]$a, [int]$b) + { + } +} + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "", Scope="Class")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("pSAvoidUsingInvokeExpression", "")] class TestClass diff --git a/Tests/Rules/AvoidGlobalOrUnitializedVars.ps1 b/Tests/Rules/AvoidGlobalOrUnitializedVars.ps1 index ef6988ad9..e606404f3 100644 --- a/Tests/Rules/AvoidGlobalOrUnitializedVars.ps1 +++ b/Tests/Rules/AvoidGlobalOrUnitializedVars.ps1 @@ -1,4 +1,7 @@ -$Global:1 = "Globalization?" +$Global:1 = "globalVar“ +$Global:DebugPreference + + function NotGlobal { $localVars = "Localization?" diff --git a/Tests/Rules/AvoidGlobalOrUnitializedVarsNoViolations.ps1 b/Tests/Rules/AvoidGlobalOrUnitializedVarsNoViolations.ps1 index 432dd1a91..42c137872 100644 --- a/Tests/Rules/AvoidGlobalOrUnitializedVarsNoViolations.ps1 +++ b/Tests/Rules/AvoidGlobalOrUnitializedVarsNoViolations.ps1 @@ -1,4 +1,7 @@ -function Test { +$Global:DebugPreference + + +function Test { $initialized = "Initialized" $noglobal = "local" $env:ShouldNotRaiseError @@ -38,4 +41,21 @@ function Test-PreferenceVariable } $VerbosePreference - } \ No newline at end of file +} + +function Test-Throw +{ + if ($true) + { + throw "First time" + } + + $a = 4 + + if ($false) + { + throw "Second time" + } + + $a +} \ No newline at end of file diff --git a/Tests/Rules/AvoidUsingInternalURLs.ps1 b/Tests/Rules/AvoidUsingInternalURLs.ps1 index da001d85c..2cbd676f0 100644 --- a/Tests/Rules/AvoidUsingInternalURLs.ps1 +++ b/Tests/Rules/AvoidUsingInternalURLs.ps1 @@ -3,4 +3,4 @@ $internalSite = "//msw" $externalSite = "http:\\msw" if (-not $scratch.EndsWith("/")) { $scratch += "/"; -} \ No newline at end of file +} diff --git a/Tests/Rules/AvoidUsingInternalURLsNoViolations.ps1 b/Tests/Rules/AvoidUsingInternalURLsNoViolations.ps1 index cdd8b7cc5..337bb0832 100644 --- a/Tests/Rules/AvoidUsingInternalURLsNoViolations.ps1 +++ b/Tests/Rules/AvoidUsingInternalURLsNoViolations.ps1 @@ -1,4 +1,8 @@ $correctPath = "www.bing.com" $externalSite = "//outside.co/test" rmdir /s /q ".\Directory" +function Test +{ + $filesNode = $infoXml.SelectSingleNode("//files") +} $sd = "O:BAG:BAD:(A;;0x800;;;WD)(A;;0x120fff;;;SY)(A;;0x120fff;;;LS)(A;;0x120fff;;;NS)(A;;0x120fff;;;BA)(A;;0xee5;;;LU)(A;;LC;;;MU)(A;;0x800;;;AG)" \ No newline at end of file diff --git a/Tests/Rules/AvoidUsingPlainTextForPasswordNoViolations.ps1 b/Tests/Rules/AvoidUsingPlainTextForPasswordNoViolations.ps1 index a4e84f4b9..0de5015ac 100644 --- a/Tests/Rules/AvoidUsingPlainTextForPasswordNoViolations.ps1 +++ b/Tests/Rules/AvoidUsingPlainTextForPasswordNoViolations.ps1 @@ -10,7 +10,8 @@ ValueFromPipelineByPropertyName=$true, Position=0)] $Param1, - + [int] + $passwordinteger, # Param2 help description [int] $Param2, @@ -25,7 +26,9 @@ [securestring] $passwordparam, [string] - $PassThru + $PassThru, + [string[]] + $shouldnotraiseerror ) Begin diff --git a/Tests/Rules/UseDeclaredVarsMoreThanAssignmentsNoViolations.ps1 b/Tests/Rules/UseDeclaredVarsMoreThanAssignmentsNoViolations.ps1 index 237f66589..769c9a2e3 100644 --- a/Tests/Rules/UseDeclaredVarsMoreThanAssignmentsNoViolations.ps1 +++ b/Tests/Rules/UseDeclaredVarsMoreThanAssignmentsNoViolations.ps1 @@ -1,2 +1,4 @@ $declaredVars = "Declared Vars" -Write-Ouput $declaredVars \ No newline at end of file +Write-Ouput $declaredVars +$script:thisshouldnotraiseerrors = "this should not raise errors" +$foo.property = "This also should not raise errors" \ No newline at end of file