/// <summary>
        /// AnalyzeScript: Analyzes the ast to check that $null is on the left side of any equality comparisons.
        /// <param name="ast">The script's ast</param>
        /// <param name="fileName">The script's file name</param>
        /// <returns>The diagnostic results of this rule</returns>
        /// </summary>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName) {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            IEnumerable<Ast> binExpressionAsts = ast.FindAll(testAst => testAst is BinaryExpressionAst, false);

            foreach (BinaryExpressionAst binExpressionAst in binExpressionAsts) {
                if ((binExpressionAst.Operator.Equals(TokenKind.Equals) || binExpressionAst.Operator.Equals(TokenKind.Ceq) 
                    || binExpressionAst.Operator.Equals(TokenKind.Cne) || binExpressionAst.Operator.Equals(TokenKind.Ine) || binExpressionAst.Operator.Equals(TokenKind.Ieq))
                    && binExpressionAst.Right.Extent.Text.Equals("$null", StringComparison.OrdinalIgnoreCase)) 
                {
                    if (IncorrectComparisonWithNull(binExpressionAst, ast))
                    {
                        yield return new DiagnosticRecord(Strings.PossibleIncorrectComparisonWithNullError, binExpressionAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                    }
                }
            }

            IEnumerable<Ast> funcAsts = ast.FindAll(item => item is FunctionDefinitionAst, true).Union(ast.FindAll(item => item is FunctionMemberAst, true));
            foreach (Ast funcAst in funcAsts)
            {
                IEnumerable<Ast> binAsts = funcAst.FindAll(item => item is BinaryExpressionAst, true);
                foreach (BinaryExpressionAst binAst in binAsts)
                {
                    if (IncorrectComparisonWithNull(binAst, funcAst))
                    {
                        yield return new DiagnosticRecord(Strings.PossibleIncorrectComparisonWithNullError, binAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                    }
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Analyzes the ast to check that cmdlets that have a Credential parameter accept PSCredential.
        /// </summary>
        /// <param name="ast">The script's ast</param>
        /// <param name="fileName">The script's file name</param>
        /// <returns>A List of diagnostic results of this rule</returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            IEnumerable<Ast> funcDefAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);
            IEnumerable<Ast> scriptBlockAsts = ast.FindAll(testAst => testAst is ScriptBlockAst, true);

            string funcName;

            foreach (FunctionDefinitionAst funcDefAst in funcDefAsts)
            {
                funcName = funcDefAst.Name;

                if (funcDefAst.Parameters != null)
                {
                    foreach (ParameterAst parameter in funcDefAst.Parameters)
                    {
                        if (WrongCredentialUsage(parameter))
                        {
                            yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeError, funcName), funcDefAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                        }
                    }
                }

                if (funcDefAst.Body.ParamBlock != null)
                {
                    foreach (ParameterAst parameter in funcDefAst.Body.ParamBlock.Parameters)
                    {
                        if (WrongCredentialUsage(parameter))
                        {
                            yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeError, funcName), funcDefAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                        }
                    }
                }
            }

            foreach (ScriptBlockAst scriptBlockAst in scriptBlockAsts)
            {
                // check for the case where it's parent is function, in that case we already processed above
                if (scriptBlockAst.Parent != null && scriptBlockAst.Parent is FunctionDefinitionAst)
                {
                    continue;
                }

                if (scriptBlockAst.ParamBlock != null && scriptBlockAst.ParamBlock.Parameters != null)
                {
                    foreach (ParameterAst parameter in scriptBlockAst.ParamBlock.Parameters)
                    {
                        if (WrongCredentialUsage(parameter))
                        {
                            yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeErrorSB), scriptBlockAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Analyzes the ast to check that cmdlets that have a Credential parameter accept PSCredential.
        /// </summary>
        /// <param name="ast">The script's ast</param>
        /// <param name="fileName">The script's file name</param>
        /// <returns>A List of diagnostic results of this rule</returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            IEnumerable<Ast> funcDefAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);
            IEnumerable<Ast> scriptBlockAsts = ast.FindAll(testAst => testAst is ScriptBlockAst, true);

            string funcName;

            foreach (FunctionDefinitionAst funcDefAst in funcDefAsts)
            {
                funcName = funcDefAst.Name;

                if (funcDefAst.Parameters != null)
                {
                    foreach (ParameterAst parameter in funcDefAst.Parameters)
                    {
                        if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) && parameter.StaticType != typeof(PSCredential))
                        {
                            yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeError, funcName), funcDefAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                        }
                    }
                }

                if (funcDefAst.Body.ParamBlock != null)
                {
                    foreach (ParameterAst parameter in funcDefAst.Body.ParamBlock.Parameters)
                    {
                        if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) && parameter.StaticType != typeof(PSCredential))
                        {
                            yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeError, funcName), funcDefAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                        }
                    }
                }
            }

            foreach (ScriptBlockAst scriptBlockAst in scriptBlockAsts)
            {
                if (scriptBlockAst.ParamBlock != null && scriptBlockAst.ParamBlock.Parameters != null)
                {
                    foreach (ParameterAst parameter in scriptBlockAst.ParamBlock.Parameters)
                    {
                        if (parameter.Name.VariablePath.UserPath.Equals("Credential", StringComparison.OrdinalIgnoreCase) && parameter.StaticType != typeof(PSCredential))
                        {
                            yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UsePSCredentialTypeErrorSB), scriptBlockAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// AvoidUsingPlainTextForPassword: Check that switch parameter does not default to true.
        /// </summary>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // Finds all ParamAsts.
            IEnumerable<Ast> paramAsts = ast.FindAll(testAst => testAst is ParameterAst, true);

            // Iterates all ParamAsts and check if any are switch.
            foreach (ParameterAst paramAst in paramAsts)
            {
                if (paramAst.Attributes.Any(attr => attr.TypeName.GetReflectionType() == typeof(System.Management.Automation.SwitchParameter))
                    && paramAst.DefaultValue != null && String.Equals(paramAst.DefaultValue.Extent.Text, "$true", StringComparison.OrdinalIgnoreCase))
                {
                    if (String.IsNullOrWhiteSpace(fileName))
                    {
                        yield return new DiagnosticRecord(
                            String.Format(CultureInfo.CurrentCulture, Strings.AvoidDefaultValueSwitchParameterErrorScriptDefinition),
                            paramAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                    }
                    else
                    {
                        yield return new DiagnosticRecord(
                            String.Format(CultureInfo.CurrentCulture, Strings.AvoidDefaultValueSwitchParameterError, System.IO.Path.GetFileName(fileName)),
                            paramAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                    }
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Analyze the script to check if cmdlet alias is used.
        /// </summary>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // Finds all CommandAsts.
            IEnumerable<Ast> foundAsts = ast.FindAll(testAst => testAst is CommandAst, true);

            // Iterates all CommandAsts and check the command name.
            foreach (Ast foundAst in foundAsts)
            {
                CommandAst cmdAst = (CommandAst)foundAst;
                string aliasName = cmdAst.GetCommandName();

                // Handles the exception caused by commands like, {& $PLINK $args 2> $TempErrorFile}.
                // You can also review the remark section in following document,
                // MSDN: CommandAst.GetCommandName Method
                if (aliasName == null)
                {
                    continue;
                }
                string cmdletName = Helper.Instance.GetCmdletNameFromAlias(aliasName);
                if (!String.IsNullOrEmpty(cmdletName))
                {
                    yield return new DiagnosticRecord(
                        string.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingCmdletAliasesError, aliasName, cmdletName),
                        cmdAst.Extent,
                        GetName(),
                        DiagnosticSeverity.Warning,
                        fileName,
                        aliasName,
                        suggestedCorrections: GetCorrectionExtent(cmdAst, cmdletName));
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Avoid Using Get-WMIObject, Remove-WMIObject, Invoke-WmiMethod, Register-WmiEvent, Set-WmiInstance
        /// </summary>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // Rule is applicable only when PowerShell Version is < 3.0, since CIM cmdlet was introduced in 3.0
            int majorPSVersion = GetPSMajorVersion(ast);
            if (!(3 > majorPSVersion && 0 < majorPSVersion))
            {
                // Finds all CommandAsts.
                IEnumerable<Ast> commandAsts = ast.FindAll(testAst => testAst is CommandAst, true);

                // Iterate all CommandAsts and check the command name
                foreach (CommandAst cmdAst in commandAsts)
                {
                    if (cmdAst.GetCommandName() != null &&
                        (String.Equals(cmdAst.GetCommandName(), "get-wmiobject", StringComparison.OrdinalIgnoreCase)
                            || String.Equals(cmdAst.GetCommandName(), "remove-wmiobject", StringComparison.OrdinalIgnoreCase)
                            || String.Equals(cmdAst.GetCommandName(), "invoke-wmimethod", StringComparison.OrdinalIgnoreCase)
                            || String.Equals(cmdAst.GetCommandName(), "register-wmievent", StringComparison.OrdinalIgnoreCase)
                            || String.Equals(cmdAst.GetCommandName(), "set-wmiinstance", StringComparison.OrdinalIgnoreCase))
                        )
                    {
                        yield return new DiagnosticRecord(String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingWMICmdletError, System.IO.Path.GetFileName(fileName)),
                            cmdAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                    }
                }
            }
        }
        private static List<string> GetNodeLevelRequiredModules(Ast ast)
        {
            IEnumerable<CommandAst> importAsts = ast.FindAll(IsCommandImportDscResource, true).OfType<CommandAst>();
            List<string> modules = new List<string>();
            foreach (CommandAst importAst in importAsts)
            {
                // TODO: refactor code to avoid calling a script, just use StaticBindingResult directly,
                // once System.Management.Automation.dll version will be updated from 3.0.0.0.

                using (PowerShell powerShell = PowerShell.Create()) 
                {
                    powerShell.AddScript(
                     @"function BindArguments($ast, $out) 
                        {
                            $dic = ([System.Management.Automation.Language.StaticParameterBinder]::BindCommand($ast)).BoundParameters 
                            foreach ($binding in $dic.GetEnumerator()) 
                            {
                                if ($binding.Key -like ""[N]*"") { $out.Add( (Get-DscResource $binding.Value.Value.Value).Module.Name ) }
                                else {if ($binding.Key -like ""[M]*"") { $out.Add( $binding.Value.Value.Value ) }}
                            }
                        }");
                    powerShell.Invoke();
                    powerShell.Commands.Clear();
                    powerShell.AddCommand("BindArguments").AddParameter("ast", importAst).AddParameter("out", modules);
                    powerShell.Invoke();
                }
            }
            return modules;
        }
        /// <summary>
        /// AnalyzeScript: Check that cmdlets are invoked with the correct mandatory parameter
        /// </summary>
        /// <param name="ast"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // Finds all CommandAsts.
            IEnumerable<Ast> foundAsts = ast.FindAll(testAst => testAst is CommandAst, true);

            // Iterates all CommandAsts and check the command name.
            foreach (Ast foundAst in foundAsts)
            {
                CommandAst cmdAst = (CommandAst)foundAst;

                // Handles the exception caused by commands like, {& $PLINK $args 2> $TempErrorFile}.
                // You can also review the remark section in following document,
                // MSDN: CommandAst.GetCommandName Method
                if (cmdAst.GetCommandName() == null) continue;

                // Checks mandatory parameters.
                if (!IsMandatoryParameterExisted(cmdAst))
                {
                    yield return new DiagnosticRecord(String.Format(CultureInfo.CurrentCulture, Strings.UseCmdletCorrectlyError, cmdAst.GetCommandName()),
                        cmdAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Analyzes the script to check if any non-constant members have been invoked.
        /// </summary>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            IEnumerable<Ast> memberExpression = ast.FindAll(testAst => testAst is MemberExpressionAst, true);
            foreach (MemberExpressionAst member in memberExpression)
            {
                string context = member.Member.Extent.ToString();
                if (context.Contains("("))
                {
                    //check if parenthesis and have non-constant members
                    IEnumerable<Ast> binaryExpression = member.FindAll(
                        binaryAst => binaryAst is BinaryExpressionAst, true);
                    if (binaryExpression.Any())
                    {
                        foreach (BinaryExpressionAst bin in binaryExpression)
                        {
                            if (!bin.Operator.Equals(null))
                            {
                                yield return
                                    new DiagnosticRecord(
                                        string.Format(CultureInfo.CurrentCulture,
                                            Strings.AvoidInvokingEmptyMembersError,
                                            context),
                                        member.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                            }
                        }
                    }
                }
            }
        
        }
        /// <summary>
        /// AnalyzeScript: Analyzes the ast to check for reserved parameters in function definitions.
        /// </summary>
        /// <param name="ast">The script's ast</param>
        /// <param name="fileName">The script's file name</param>
        /// <returns>A List of diagnostic results of this rule</returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName) {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            IEnumerable<Ast> funcAsts = ast.FindAll(item => item is FunctionDefinitionAst, true);

            List<string> commonParamNames = typeof(CommonParameters).GetProperties().Select(param => param.Name).ToList();

            foreach (FunctionDefinitionAst funcAst in funcAsts)
            {
                // this rule only applies to function with param block
                if (funcAst.Body != null && funcAst.Body.ParamBlock != null
                    && funcAst.Body.ParamBlock.Attributes != null && funcAst.Body.ParamBlock.Parameters != null)
                {
                    // no cmdlet binding
                    if (!funcAst.Body.ParamBlock.Attributes.Any(attr => attr.TypeName.GetReflectionType() == typeof(CmdletBindingAttribute)))
                    {
                        continue;
                    }

                    foreach (ParameterAst paramAst in funcAst.Body.ParamBlock.Parameters)
                    {
                        string paramName = paramAst.Name.VariablePath.UserPath;

                        if (commonParamNames.Contains(paramName, StringComparer.OrdinalIgnoreCase))
                        {
                            yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ReservedParamsError, funcAst.Name, paramName),
                                paramAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Check if any uninitialized variable is used.
        /// </summary>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // Finds all functionAst
            IEnumerable<Ast> functionAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);

            foreach (FunctionDefinitionAst funcAst in functionAsts)
            {
                if (funcAst.Body != null && funcAst.Body.ParamBlock != null
                    && funcAst.Body.ParamBlock.Attributes != null && funcAst.Body.ParamBlock.Parameters != null)
                {
                    // only raise this rule for function with cmdletbinding
                    if (!funcAst.Body.ParamBlock.Attributes.Any(attr => attr.TypeName.GetReflectionType() == typeof(CmdletBindingAttribute)))
                    {
                        continue;
                    }

                    foreach (var paramAst in funcAst.Body.ParamBlock.Parameters)
                    {
                        bool mandatory = false;

                        // check that param is mandatory
                        foreach (var paramAstAttribute in paramAst.Attributes)
                        {
                            if (paramAstAttribute is AttributeAst)
                            {
                                var namedArguments = (paramAstAttribute as AttributeAst).NamedArguments;
                                if (namedArguments != null)
                                {
                                    foreach (NamedAttributeArgumentAst namedArgument in namedArguments)
                                    {
                                        if (String.Equals(namedArgument.ArgumentName, "mandatory", StringComparison.OrdinalIgnoreCase))
                                        {
                                            // 2 cases: [Parameter(Mandatory)] and [Parameter(Mandatory=$true)]
                                            if (namedArgument.ExpressionOmitted || (!namedArgument.ExpressionOmitted && String.Equals(namedArgument.Argument.Extent.Text, "$true", StringComparison.OrdinalIgnoreCase)))
                                            {
                                                mandatory = true;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        if (!mandatory)
                        {
                            break;
                        }

                        if (paramAst.DefaultValue != null)
                        {
                            yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.AvoidDefaultValueForMandatoryParameterError, paramAst.Name.VariablePath.UserPath),
                            paramAst.Name.Extent, GetName(), DiagnosticSeverity.Warning, fileName, paramAst.Name.VariablePath.UserPath);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// AnalyzeDSCResource: Analyzes given DSC Resource
        /// </summary>
        /// <param name="ast">The script's ast</param>
        /// <param name="fileName">The name of the script file being analyzed</param>
        /// <returns>The results of the analysis</returns>
        public IEnumerable<DiagnosticRecord> AnalyzeDSCResource(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // Expected TargetResource functions in the DSC Resource module
            List<string> expectedTargetResourceFunctionNames = new List<string>(new string[]  { "Get-TargetResource", "Set-TargetResource", "Test-TargetResource" });

            // Retrieve a list of Asts where the function name contains TargetResource
            IEnumerable<Ast> functionDefinitionAsts = (ast.FindAll(dscAst => dscAst is FunctionDefinitionAst && ((dscAst as FunctionDefinitionAst).Name.IndexOf("targetResource", StringComparison.CurrentCultureIgnoreCase) != -1), true));

            List<string> targetResourceFunctionNamesInAst = new List<string>();
            foreach (FunctionDefinitionAst functionDefinitionAst in functionDefinitionAsts)
            {
                targetResourceFunctionNamesInAst.Add(functionDefinitionAst.Name);
            }

            foreach (string expectedTargetResourceFunctionName in expectedTargetResourceFunctionNames)
            {
                // If the Ast does not contain the expected functions, provide a Rule violation message
                if (!targetResourceFunctionNamesInAst.Contains(expectedTargetResourceFunctionName, StringComparer.CurrentCultureIgnoreCase))
                {
                    yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UseStandardDSCFunctionsInResourceError, expectedTargetResourceFunctionName),
                        ast.Extent, GetName(), DiagnosticSeverity.Error, fileName);
                }
            }
        }
        /// <summary>
        /// Checks that all defined cmdlet use singular noun
        /// </summary>
        /// <param name="ast"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName) {
            if (ast == null) throw new ArgumentNullException(Strings.NullCommandInfoError);

            IEnumerable<Ast> funcAsts = ast.FindAll(item => item is FunctionDefinitionAst, true);

            char[] funcSeperator = { '-' };
            string[] funcNamePieces = new string[2];

            foreach (FunctionDefinitionAst funcAst in funcAsts)
            {
                if (funcAst.Name != null && funcAst.Name.Contains('-'))
                {
                    funcNamePieces = funcAst.Name.Split(funcSeperator);
                    String noun = funcNamePieces[1];
                    var ps = System.Data.Entity.Design.PluralizationServices.PluralizationService.CreateService(CultureInfo.GetCultureInfo("en-us"));

                    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);
                    }
                }
            }

        }
        /// <summary>
        /// AnalyzeDSCClass: Analyzes dsc classes and the file and check that they have get, set and test
        /// </summary>
        /// <param name="ast"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public IEnumerable<DiagnosticRecord> AnalyzeDSCClass(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            List<string> resourceFunctionNames = new List<string>(new string[] {"Test", "Get", "Set"});

            IEnumerable<Ast> 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);

            foreach (TypeDefinitionAst dscClass in dscClasses)
            {
                IEnumerable<Ast> functions = dscClass.Members.Where(member => member is FunctionMemberAst);

                foreach (string resourceFunctionName in resourceFunctionNames)
                {
                    if (!functions.Any(function => String.Equals(resourceFunctionName, (function as FunctionMemberAst).Name)))
                    {
                        yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UseStandardDSCFunctionsInClassError, resourceFunctionName),
                            dscClass.Extent, GetName(), DiagnosticSeverity.Error, fileName);
                    }
                }
            }
        }
        /// <summary>
        /// Analyze ast to check that all the cmdlet does not use reserved char
        /// </summary>
        /// <param name="ast"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            IEnumerable<Ast> funcAsts = ast.FindAll(item => item is FunctionDefinitionAst, true);

            string reservedChars = Strings.ReserverCmdletChars;
            HashSet<string> exportedFunction = Helper.Instance.GetExportedFunction(ast);

            foreach (FunctionDefinitionAst funcAst in funcAsts)
            {
                if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Attributes != null)
                {
                    // only raise this rule for function with cmdletbinding
                    if (!funcAst.Body.ParamBlock.Attributes.Any(attr => attr.TypeName.GetReflectionType() == typeof(CmdletBindingAttribute)))
                    {
                        continue;
                    }

                    string funcName = Helper.Instance.FunctionNameWithoutScope(funcAst.Name);

                    // only raise if the function is exported
                    if (funcName != null && funcName.Intersect(reservedChars).Count() > 0 && (exportedFunction.Contains(funcAst.Name) || exportedFunction.Contains(funcName)))
                    {
                        yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ReservedCmdletCharError, funcAst.Name),
                            funcAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                    }
                }
            }
        }
        /// <summary>
        /// AvoidUsingPlainTextForPassword: Check that parameter "password", "passphrase" and do not use plaintext.
        /// </summary>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // Finds all ParamAsts.
            IEnumerable<Ast> paramAsts = ast.FindAll(testAst => testAst is ParameterAst, true);

            List<String> passwords = new List<String>() {"Password", "Passphrase", "Auth", "Cred", "Credential"};

            // Iterrates all ParamAsts and check if their names are on the list.
            foreach (ParameterAst paramAst in paramAsts)
            {
                TypeInfo paramType = (TypeInfo) paramAst.StaticType;
                bool hasPwd = false;
                String paramName = paramAst.Name.VariablePath.ToString();

                foreach (String password in passwords)
                {
                    if (paramName.IndexOf(password, StringComparison.OrdinalIgnoreCase) != -1)
                    {
                        hasPwd = true;
                        break;
                    }
                }

                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),
                        paramAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Check that a function does not use both username
        /// and password parameters.
        /// </summary>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // Finds all functionAst
            IEnumerable<Ast> functionAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);

            List<String> passwords = new List<String>() {"Password", "Passphrase"};
            List<String> usernames = new List<String>() { "Username", "User"};

            foreach (FunctionDefinitionAst funcAst in functionAsts)
            {
                bool hasPwd = false;
                bool hasUserName = false;

                // Finds all ParamAsts.
                IEnumerable<Ast> paramAsts = funcAst.FindAll(testAst => testAst is ParameterAst, true);
                // Iterrates all ParamAsts and check if their names are on the list.
                foreach (ParameterAst paramAst in paramAsts)
                {
                    TypeInfo paramType = (TypeInfo)paramAst.StaticType;
                    String paramName = paramAst.Name.VariablePath.ToString();

                    if (paramType == typeof(PSCredential) || (paramType.IsArray && paramType.GetElementType() == typeof (PSCredential)))
                    {
                        continue;
                    }

                    foreach (String password in passwords)
                    {
                        if (paramName.IndexOf(password, StringComparison.OrdinalIgnoreCase) != -1)
                        {
                            hasPwd = true;
                            break;
                        }
                    }

                    foreach (String username in usernames)
                    {
                        if (paramName.IndexOf(username, StringComparison.OrdinalIgnoreCase) != -1)
                        {
                            hasUserName = true;
                            break;
                        }
                    }
                }

                if (hasUserName && hasPwd)
                {
                    yield return new DiagnosticRecord(
                        String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsernameAndPasswordParamsError, funcAst.Name),
                        funcAst.Extent, GetName(), DiagnosticSeverity.Error, fileName);
                }
            }
        }
        /// <summary>
        /// AvoidShouldContinueWithoutForceCheck that if ShouldContinue is used,
        /// the function should have a boolean force parameter
        /// </summary>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // Finds all ParamAsts.
            IEnumerable<Ast> funcAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);

            // Iterates all ParamAsts and check if there are any force.
            foreach (FunctionDefinitionAst funcAst in funcAsts)
            {
                IEnumerable<Ast> paramAsts = funcAst.FindAll(testAst => testAst is ParameterAst, true);
                bool hasForce = false;

                foreach (ParameterAst paramAst in paramAsts)
                {
                    if (String.Equals(paramAst.Name.VariablePath.ToString(), "force", StringComparison.OrdinalIgnoreCase)
                        && (String.Equals(paramAst.StaticType.FullName, "System.Boolean", StringComparison.OrdinalIgnoreCase)
                        || String.Equals(paramAst.StaticType.FullName, "System.Management.Automation.SwitchParameter", StringComparison.OrdinalIgnoreCase)))
                    {
                        hasForce = true;
                        break;
                    }
                }

                if (hasForce)
                {
                    continue;
                }

                IEnumerable<Ast> imeAsts = funcAst.FindAll(testAst => testAst is InvokeMemberExpressionAst, true);

                foreach (InvokeMemberExpressionAst imeAst in imeAsts)
                {
                    VariableExpressionAst typeAst = imeAst.Expression as VariableExpressionAst;
                    if (typeAst == null) continue;

                    if (String.Equals(typeAst.VariablePath.UserPath, "pscmdlet", StringComparison.OrdinalIgnoreCase)
                        && (String.Equals(imeAst.Member.Extent.Text, "shouldcontinue", StringComparison.OrdinalIgnoreCase)))
                    {
                        if (String.IsNullOrWhiteSpace(fileName))
                        {
                            yield return new DiagnosticRecord(
                                String.Format(CultureInfo.CurrentCulture, Strings.AvoidShouldContinueWithoutForceErrorScriptDefinition, funcAst.Name),
                                imeAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                        }
                        else
                        {
                            yield return new DiagnosticRecord(
                                String.Format(CultureInfo.CurrentCulture, Strings.AvoidShouldContinueWithoutForceError, funcAst.Name,
                                System.IO.Path.GetFileName(fileName)), imeAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Check if any uninitialized variable is used.
        /// </summary>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // Finds all functionAst
            IEnumerable<Ast> functionAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);

            // Checks whether this is a dsc resource file (we don't raise this rule for get, set and test-target resource
            bool isDscResourceFile = Helper.Instance.IsDscResourceModule(fileName);

            List<string> targetResourcesFunctions = new List<string>(new string[] { "get-targetresource", "set-targetresource", "test-targetresource" });

            foreach (FunctionDefinitionAst funcAst in functionAsts)
            {
                // Finds all ParamAsts.
                IEnumerable<Ast> varAsts = funcAst.FindAll(testAst => testAst is VariableExpressionAst, true);

                // Iterrates all ParamAsts and check if their names are on the list.

                HashSet<string> dscVariables = new HashSet<string>();
                if (isDscResourceFile && targetResourcesFunctions.Contains(funcAst.Name, StringComparer.OrdinalIgnoreCase))
                {
                    // don't raise the rules for variables in the param block.
                    if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null)
                    {
                        dscVariables.UnionWith(funcAst.Body.ParamBlock.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath));
                    }
                }
                // only raise the rules for variables in the param block.
                if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null)
                {
                    foreach (var paramAst in funcAst.Body.ParamBlock.Parameters)
                    {
                        if (Helper.Instance.IsUninitialized(paramAst.Name, funcAst) && !dscVariables.Contains(paramAst.Name.VariablePath.UserPath))
                        {
                            yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueError, paramAst.Name.VariablePath.UserPath),
                            paramAst.Name.Extent, GetName(), DiagnosticSeverity.Warning, fileName, paramAst.Name.VariablePath.UserPath);
                        }
                    }
                }

                if (funcAst.Parameters != null)
                {
                    foreach (var paramAst in funcAst.Parameters)
                    {
                        if (Helper.Instance.IsUninitialized(paramAst.Name, funcAst) && !dscVariables.Contains(paramAst.Name.VariablePath.UserPath))
                        {
                            yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueError, paramAst.Name.VariablePath.UserPath),
                            paramAst.Name.Extent, GetName(), DiagnosticSeverity.Warning, fileName, paramAst.Name.VariablePath.UserPath);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Checks that objects return in a cmdlet have their types declared in OutputType Attribute
        /// </summary>
        /// <param name="ast">The script's ast</param>
        /// <param name="fileName">The name of the script</param>
        /// <returns>A List of diagnostic results of this rule</returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            DiagnosticRecords.Clear();
            this.fileName = fileName;

            _classes = ast.FindAll(item => item is TypeDefinitionAst && ((item as TypeDefinitionAst).IsClass), true).Cast<TypeDefinitionAst>();

            ast.Visit(this);

            return DiagnosticRecords;
        }
        /// <summary>
        /// AnalyzeDSCResource: Analyzes given DSC Resource
        /// </summary>
        /// <param name="ast">The script's ast</param>
        /// <param name="fileName">The name of the script file being analyzed</param>
        /// <returns>The results of the analysis</returns>
        public IEnumerable<DiagnosticRecord> AnalyzeDSCResource(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // TODO: Add logic for DSC Resources

            IEnumerable<Ast> functionDefinitionAsts = Helper.Instance.DscResourceFunctions(ast);

            IEnumerable<TypeDefinitionAst> classes = ast.FindAll(item =>
                item is TypeDefinitionAst
                && ((item as TypeDefinitionAst).IsClass), true).Cast<TypeDefinitionAst>();

            foreach (FunctionDefinitionAst func in functionDefinitionAsts)
            {
                List<Tuple<string, StatementAst>> outputTypes = FindPipelineOutput.OutputTypes(func, classes);

                if (String.Equals(func.Name, "Set-TargetResource", StringComparison.OrdinalIgnoreCase))
                {
                    foreach (Tuple<string, StatementAst> outputType in outputTypes)
                    {
                        yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ReturnCorrectTypesForSetTargetResourceFunctionsDSCError),
                            outputType.Item2.Extent, GetName(), DiagnosticSeverity.Information, fileName);
                    }
                }
                else
                {
                    Dictionary<string, string> returnTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                    returnTypes["Get-TargetResource"] = typeof(System.Collections.Hashtable).FullName;
                    returnTypes["Test-TargetResource"] = typeof(bool).FullName;

                    foreach (Tuple<string, StatementAst> outputType in outputTypes)
                    {
                        string type = outputType.Item1;

                        if (String.IsNullOrEmpty(type)
                            || String.Equals(typeof(Unreached).FullName, type, StringComparison.OrdinalIgnoreCase)
                            || String.Equals(typeof(Undetermined).FullName, type, StringComparison.OrdinalIgnoreCase)
                            || String.Equals(typeof(object).FullName, type, StringComparison.OrdinalIgnoreCase)
                            || String.Equals(type, returnTypes[func.Name], StringComparison.OrdinalIgnoreCase))
                        {
                            continue;
                        }
                        else
                        {
                            yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ReturnCorrectTypesForGetTestTargetResourceFunctionsDSCResourceError,
                                func.Name, returnTypes[func.Name], type), outputType.Item2.Extent, GetName(), DiagnosticSeverity.Information, fileName);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Analyzes the ast to check if ShouldProcess is included in Advanced functions if the Verb of the function could change system state.
        /// </summary>
        /// <param name="ast">The script's ast</param>
        /// <param name="fileName">The script's file name</param>
        /// <returns>A List of diagnostic results of this rule</returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            IEnumerable<Ast> funcDefAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);
            string supportsShouldProcess = "SupportsShouldProcess";
            string trueString = "$true";
            foreach (FunctionDefinitionAst funcDefAst in funcDefAsts)
            {
                string funcName = funcDefAst.Name;
                bool hasShouldProcessAttribute = false;

                if (funcName.StartsWith("Restart-", StringComparison.OrdinalIgnoreCase) ||
                    funcName.StartsWith("Stop-", StringComparison.OrdinalIgnoreCase)||
                    funcName.StartsWith("New-", StringComparison.OrdinalIgnoreCase) ||
                    funcName.StartsWith("Set-", StringComparison.OrdinalIgnoreCase) ||
                    funcName.StartsWith("Update-", StringComparison.OrdinalIgnoreCase) ||
                    funcName.StartsWith("Reset-", StringComparison.OrdinalIgnoreCase))
                {
                    IEnumerable<Ast> attributeAsts = funcDefAst.FindAll(testAst => testAst is NamedAttributeArgumentAst, true);
                    if (funcDefAst.Body != null && funcDefAst.Body.ParamBlock != null
                        && funcDefAst.Body.ParamBlock.Attributes != null &&
                        funcDefAst.Body.ParamBlock.Parameters != null)
                    {
                        if (!funcDefAst.Body.ParamBlock.Attributes.Any(attr => attr.TypeName.GetReflectionType() == typeof (CmdletBindingAttribute)))
                        {
                            continue;
                        }

                        foreach (NamedAttributeArgumentAst attributeAst in attributeAsts)
                        {
                            if (attributeAst.ArgumentName.Equals(supportsShouldProcess, StringComparison.OrdinalIgnoreCase))
                            {
                                if((attributeAst.Argument.Extent.Text.Equals(trueString, StringComparison.OrdinalIgnoreCase)) && !attributeAst.ExpressionOmitted || 
                                    attributeAst.ExpressionOmitted)
                                {
                                    hasShouldProcessAttribute = true;
                                }
                            }
                        }

                        if (!hasShouldProcessAttribute)
                        {
                            yield return
                                 new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture,Strings.UseShouldProcessForStateChangingFunctionsError, funcName),funcDefAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                        }
                    }
                }
            }
        }
        private static FunctionDefinitionAst FindDefinition(CommandAst ast, Ast parentAst)
        {
            if (ast == null) throw new ArgumentNullException("ast");
            if (parentAst == null) return null;

            var definitions = parentAst.FindAll(
                m => m is FunctionDefinitionAst && ((FunctionDefinitionAst) m).Name.Equals(ast.GetCommandName()), false).ToList();

            if (definitions.Any())
            {
                return definitions.Last() as FunctionDefinitionAst;
            }

            return FindDefinition(ast, parentAst.Parent);
        }
 /// <summary>
 /// AnalyzeScript: Analyzes the ast to check if ShouldProcess is included in Advanced functions if the Verb of the function could change system state.
 /// </summary>
 /// <param name="ast">The script's ast</param>
 /// <param name="fileName">The script's file name</param>
 /// <returns>A List of diagnostic results of this rule</returns>
 public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
 {
     if (ast == null)
     {
         throw new ArgumentNullException(Strings.NullAstErrorMessage);
     }
     IEnumerable<Ast> funcDefWithNoShouldProcessAttrAsts = ast.FindAll(IsStateChangingFunctionWithNoShouldProcessAttribute, true);
     foreach (FunctionDefinitionAst funcDefAst in funcDefWithNoShouldProcessAttrAsts)
     {
         yield return new DiagnosticRecord(
             string.Format(CultureInfo.CurrentCulture, Strings.UseShouldProcessForStateChangingFunctionsError, funcDefAst.Name),
             Helper.Instance.GetScriptExtentForFunctionName(funcDefAst),
             this.GetName(),
             DiagnosticSeverity.Warning,
             fileName);
     }
 }
        /// <summary>
        /// Analyze ast to check that all the cmdlet does not use reserved char
        /// </summary>
        /// <param name="ast"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            IEnumerable<Ast> funcAsts = ast.FindAll(item => item is FunctionDefinitionAst, true);

            string reservedChars = Strings.ReserverCmdletChars;

            foreach (FunctionDefinitionAst funcAst in funcAsts)
            {
                if (funcAst.Name != null && funcAst.Name.Intersect(reservedChars).Count() > 0)
                {
                    yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ReservedCmdletCharError, funcAst.Name),
                        funcAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                }
            }            
        }
        /// <summary>
        /// GetPSMajorVersion: Retrieves Major PowerShell Version when supplied using #requires keyword in the script
        /// </summary>
        /// <returns>The name of this rule</returns>
        private int GetPSMajorVersion(Ast ast)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            IEnumerable<Ast> scriptBlockAsts = ast.FindAll(testAst => testAst is ScriptBlockAst, true);
            
            foreach (ScriptBlockAst scriptBlockAst in scriptBlockAsts)
            {
                if (null != scriptBlockAst.ScriptRequirements && null != scriptBlockAst.ScriptRequirements.RequiredPSVersion)
                {
                    return scriptBlockAst.ScriptRequirements.RequiredPSVersion.Major;
                }
            }

            // return a non valid Major version if #requires -Version is not supplied in the Script
            return -1;
        }
Exemple #27
0
        // Static Method to parse a script file
        public static List <Node> ParseFile(string file)
        {
            string script = File.ReadAllText(file);

            FileInfo ScriptFileInfo = new FileInfo(file);

            ScriptBlock scriptblock = ScriptBlock.Create(script);
            Ast         NamedBlock  = scriptblock.Ast.Find(Args => Args is NamedBlockAst, false);
            // IEnumerable<Ast> enumerable = NamedBlock.FindAll(Args => Args is Ast && FlowChartCore.Utility.GetValidTypes().Contains(Args.GetType()) && Args.Parent == NamedBlock, false);
            IEnumerable <Ast> enumerable = NamedBlock.FindAll(Args => Args is Ast && Args.Parent == NamedBlock, false);

            int         Position = 1;
            List <Node> Nodes    = new List <Node>();
            Tree        Arbre    = new Tree(Nodes, scriptblock.Ast, NodesOrigin.File, ScriptFileInfo);
            bool        tmp      = true;

            foreach (var block in enumerable)
            {
                if (FlowChartCore.Utility.GetValidTypes().Contains(block.GetType()))
                {
                    // valid type found
                    Node tmpNode = block.CreateNode(0, Position, null, Arbre);
                    // tmpNode.Origin = NodesOrigin.File;
                    // tmpNode.FileInfo = ScriptFileInfo;

                    Nodes.Add(tmpNode);
                    Position++;
                    // reset tmp
                    tmp = true;
                }
                else if (tmp)
                {
                    // not a valid type, and tmp is false, create code node
                    tmp = false;
                    Node tmpNode = new CodeNode(0, Position, null, Arbre);
                    // tmpNode.Origin = NodesOrigin.File;
                    // tmpNode.FileInfo = ScriptFileInfo;

                    Nodes.Add(tmpNode);
                    Position++;
                }
            }

            return(Arbre.Nodes);
        }
        /// <summary>
        /// Analyze script to check that all defined functions use approved verbs
        /// </summary>
        /// <param name="ast"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            List<string> approvedVerbs = typeof(VerbsCommon).GetFields().Concat<FieldInfo>(
                typeof(VerbsCommunications).GetFields()).Concat<FieldInfo>(
                typeof(VerbsData).GetFields()).Concat<FieldInfo>(
                typeof(VerbsDiagnostic).GetFields()).Concat<FieldInfo>(
                typeof(VerbsLifecycle).GetFields()).Concat<FieldInfo>(
                typeof(VerbsSecurity).GetFields()).Concat<FieldInfo>(
                typeof(VerbsOther).GetFields()).Select<FieldInfo, String>(
                item => item.Name).ToList();

            string funcName;
            char[] funcSeperator = { '-' };
            string[] funcNamePieces = new string[2];
            string verb;

            IEnumerable<Ast> funcAsts = ast.FindAll(item => item is FunctionDefinitionAst, true);

            foreach (FunctionDefinitionAst funcAst in funcAsts)
            {
                funcName = Helper.Instance.FunctionNameWithoutScope(funcAst.Name);

                if (funcName != null && funcName.Contains('-'))
                {
                    funcNamePieces = funcName.Split(funcSeperator);
                    verb = funcNamePieces[0];

                    if (!approvedVerbs.Contains(verb, StringComparer.OrdinalIgnoreCase))
                    {
                        IScriptExtent extent = Helper.Instance.GetScriptExtentForFunctionName(funcAst);

                        if (null == extent)
                        {
                            extent = funcAst.Extent;
                        }

                        yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.UseApprovedVerbsError, funcName),
                            extent, GetName(), DiagnosticSeverity.Warning, fileName);
                    }
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Analyze the script to check if any empty catch block is used.
        /// </summary>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            // Finds all CommandAsts.
            IEnumerable<Ast> foundAsts = ast.FindAll(testAst => testAst is CatchClauseAst, true);

            // Iterrates all CatchClauseAst and check the statements count.
            foreach (Ast foundAst in foundAsts)
            {
                CatchClauseAst catchAst = (CatchClauseAst)foundAst;

                if (catchAst.Body.Statements.Count == 0)
                {
                    yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.AvoidEmptyCatchBlockError),
                        catchAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
                }
            }
        }
        private static List <string> GetRequiredModulesFromAst(Ast ast)
        {
            List <string> modules = new List <string>();

            // We use System.Management.Automation.Language.Parser to extract required modules from ast,
            // but format of ast is a bit tricky and have changed in time.
            //
            // There are two place where 'Import-DscResource' keyword can appear:
            // 1)
            // Configuration Foo {
            //   Import-DscResource ....  # outside node
            //   Node Bar {...}
            // }
            // 2)
            // Configuration Foo {
            //   Node Bar {
            //     Import-DscResource .... # inside node
            //     ...
            //   }
            // }
            //
            // The old version of System.Management.Automation.Language.Parser produces slightly different AST for the first case.
            // In new version, Configuration corresponds to ConfigurationDefinitionAst.
            // In old version is's a generic CommandAst with specific commandElements which capture top-level Imports (case 1).
            // In new version all imports correspond to their own CommandAsts, same for case 2 in old version.

            // Old version, case 1:
            IEnumerable <CommandAst> legacyConfigurationAsts = ast.FindAll(IsLegacyAstConfiguration, true).Select(x => (CommandAst)x);

            foreach (var legacyConfigurationAst in legacyConfigurationAsts)
            {
                // Example: Import-DscResource -Module xPSDesiredStateConfiguration
                modules.AddRange(GetLegacyTopLevelParametersFromAst(legacyConfigurationAst, "ModuleDefinition"));
                // Example: Import-DscResource -Name MSFT_xComputer
                modules.AddRange(GetLegacyTopLevelParametersFromAst(legacyConfigurationAst, "ResourceDefinition").Select(GetModuleNameForDscResource));
            }

            // Both cases in new version and 2 case in old version:
            modules.AddRange(GetNodeLevelRequiredModules(ast));

            return(modules.Distinct().ToList());
        }
        /// <summary>
        /// AnalyzeScript: Analyzes the ast to check that global variables are not used. From the ILintScriptRule interface.
        /// </summary>
        /// <param name="ast">The script's ast</param>
        /// <param name="fileName">The script's file name</param>
        /// <returns>A List of diagnostic results of this rule</returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

            IEnumerable<Ast> varAsts = ast.FindAll(testAst => testAst is VariableExpressionAst, true);

            if (varAsts != null)
            {
                foreach (VariableExpressionAst varAst in varAsts)
                {
                    if (Helper.Instance.IsVariableGlobal(varAst))
                    {
                        yield return
                            new DiagnosticRecord(
                                string.Format(CultureInfo.CurrentCulture, Strings.AvoidGlobalVarsError,
                                    varAst.VariablePath.UserPath), varAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName, varAst.VariablePath.UserPath);
                    }
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Analyzes the given Ast and returns DiagnosticRecords based on the analysis.
        /// </summary>
        /// <param name="ast">The script's ast</param>
        /// <param name="fileName">The name of the script file being analyzed</param>
        /// <returns>The results of the analysis</returns>
        public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null) throw new ArgumentNullException("ast");

            // Finds all CommandAsts.
            IEnumerable<Ast> commandAsts = ast.FindAll(testAst => testAst is CommandAst, true);

            List<String> cmdletNameAndAliases = Microsoft.Windows.PowerShell.ScriptAnalyzer.Helper.Instance.CmdletNameAndAliases(GetCmdletName());

            // Iterates all CommandAsts and check the command name.
            foreach (CommandAst cmdAst in commandAsts)
            {
                if (cmdAst.GetCommandName() == null) continue;

                if (cmdletNameAndAliases.Contains(cmdAst.GetCommandName(), StringComparer.OrdinalIgnoreCase))
                {
                    yield return new DiagnosticRecord(GetError(fileName), cmdAst.Extent, GetName(), GetDiagnosticSeverity(), fileName);
                }
            }
        }
Exemple #33
0
        /// <summary>
        /// AnalyzeScript: Analyze the script to check if cmdlet alias is used.
        /// </summary>
        public IEnumerable <DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
        {
            if (ast == null)
            {
                throw new ArgumentNullException(Strings.NullAstErrorMessage);
            }
            if (!isPropertiesSet)
            {
                SetProperties();
            }
            // Finds all CommandAsts.
            IEnumerable <Ast> foundAsts = ast.FindAll(testAst => testAst is CommandAst, true);

            // Iterates all CommandAsts and check the command name.
            foreach (Ast foundAst in foundAsts)
            {
                CommandAst cmdAst    = (CommandAst)foundAst;
                string     aliasName = cmdAst.GetCommandName();

                // Handles the exception caused by commands like, {& $PLINK $args 2> $TempErrorFile}.
                // You can also review the remark section in following document,
                // MSDN: CommandAst.GetCommandName Method
                if (aliasName == null ||
                    whiteList.Contains(aliasName))
                {
                    continue;
                }
                string cmdletName = Helper.Instance.GetCmdletNameFromAlias(aliasName);
                if (!String.IsNullOrEmpty(cmdletName))
                {
                    yield return(new DiagnosticRecord(
                                     string.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingCmdletAliasesError, aliasName, cmdletName),
                                     cmdAst.Extent,
                                     GetName(),
                                     DiagnosticSeverity.Warning,
                                     fileName,
                                     aliasName,
                                     suggestedCorrections: GetCorrectionExtent(cmdAst, cmdletName)));
                }
            }
        }