/// <summary>
        /// AnalyzeScript: Run Test Module Manifest to check that none of the required fields are missing. 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);

            if (String.Equals(System.IO.Path.GetExtension(fileName), ".psd1", StringComparison.OrdinalIgnoreCase))
            {
                IEnumerable<ErrorRecord> errorRecords;
                var psModuleInfo = Helper.Instance.GetModuleManifest(fileName, out errorRecords);
                if (errorRecords != null)
                {
                    foreach (var errorRecord in errorRecords)
                    {
                        if (Helper.IsMissingManifestMemberException(errorRecord))
                        {
                            System.Diagnostics.Debug.Assert(
                                errorRecord.Exception != null && !String.IsNullOrWhiteSpace(errorRecord.Exception.Message),
                                Strings.NullErrorMessage);
                            var hashTableAst = ast.Find(x => x is HashtableAst, false);
                            if (hashTableAst == null)
                            {
                                yield break;
                            }
                            yield return new DiagnosticRecord(
                                errorRecord.Exception.Message,
                                hashTableAst.Extent,
                                GetName(),
                                DiagnosticSeverity.Warning,
                                fileName,
                                suggestedCorrections:GetCorrectionExtent(hashTableAst as HashtableAst));
                        }

                    }
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Analyzes the AST to check if AliasToExport, CmdletsToExport, FunctionsToExport
        /// and VariablesToExport fields do not use wildcards and $null in their entries.
        /// </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);
            }

            if (fileName == null || !fileName.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase))
            {
                yield break;
            }

            if (!IsValidManifest(ast, fileName))
            {
                yield break;
            }

            String[] manifestFields = { "FunctionsToExport", "CmdletsToExport", "VariablesToExport", "AliasesToExport" };
            var      hashtableAst   = ast.Find(x => x is HashtableAst, false) as HashtableAst;

            if (hashtableAst == null)
            {
                yield break;
            }

            foreach (String field in manifestFields)
            {
                IScriptExtent extent;
                if (!HasAcceptableExportField(field, hashtableAst, ast.Extent.Text, out extent) && extent != null)
                {
                    yield return(new DiagnosticRecord(GetError(field), extent, GetName(), DiagnosticSeverity.Warning, fileName));
                }
            }
        }
        /// <summary>
        /// AnalyzeScript: Analyzes the AST to check if AliasToExport, CmdletsToExport, FunctionsToExport 
        /// and VariablesToExport fields do not use wildcards and $null in their entries. 
        /// </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);
            }

            if (fileName == null || !fileName.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase))
            {
                yield break;
            }

            if (!IsValidManifest(ast, fileName))
            {
                yield break;
            }

            String[] manifestFields = {"FunctionsToExport", "CmdletsToExport", "VariablesToExport", "AliasesToExport"};
            var hashtableAst = ast.Find(x => x is HashtableAst, false) as HashtableAst;

            if (hashtableAst == null)
            {
                yield break;
            }

            foreach(String field in manifestFields)
            {
                IScriptExtent extent;
                if (!HasAcceptableExportField(field, hashtableAst, ast.Extent.Text, out extent) && extent != null)
                {
                    yield return new DiagnosticRecord(GetError(field), extent, GetName(), DiagnosticSeverity.Warning, fileName);
                }
            }
        }
Exemple #4
0
        /// <summary>
        /// Finds all possible function definitions for the token at the caret position
        /// </summary>
        public static IEnumerable <FunctionDefinitionAst> FindFunctionDefinitions(Ast script, ITextSnapshot currentSnapshot, int caretPosition)
        {
            if (script != null)
            {
                var reference = script.Find(node =>
                                            node is CommandAst &&
                                            caretPosition >= node.Extent.StartOffset &&
                                            caretPosition <= node.Extent.EndOffset, true) as CommandAst;

                FunctionDefinitionAst definition = null;
                if (reference != null)
                {
                    return(FindDefinition(reference));
                }
                else
                {
                    definition = script.Find(node =>
                    {
                        if (node is FunctionDefinitionAst)
                        {
                            var functionNameSpan = GetFunctionNameSpan(node as FunctionDefinitionAst);
                            return(functionNameSpan.HasValue &&
                                   caretPosition >= functionNameSpan.Value.Start &&
                                   caretPosition <= functionNameSpan.Value.End);
                        }

                        return(false);
                    }, true) as FunctionDefinitionAst;

                    if (definition != null)
                    {
                        return(new List <FunctionDefinitionAst>()
                        {
                            definition
                        });
                    }
                }
            }

            return(null);
        }
        /// <summary>
        /// Gets the CommandAst for the whole command line.
        /// </summary>
        /// <param name="commandLine">The command line to get the CommandAst.</param>
        /// <returns>The CommandAst.</returns>
        /// <remarks>This parses the command line and returns the first one it encounters. It doesn't work well in a complex command line, for example: <c>Get-AzContext | Set-AzContext</c> will return <c>Get-AzContext</c>.</remarks>
        public static CommandAst GetCommandAst(string commandLine)
        {
            if (string.IsNullOrWhiteSpace(commandLine))
            {
                return(null);
            }

            Ast ast        = Parser.ParseInput(commandLine, out _, out _);
            var commandAst = ast.Find((ast) => ast is CommandAst, searchNestedScriptBlocks: false) as CommandAst;

            return(commandAst);
        }
        /// <summary>
        /// AnalyzeScript: Analyzes the AST to check if AliasToExport, CmdletsToExport, FunctionsToExport
        /// and VariablesToExport fields do not use wildcards and $null in their entries.
        /// </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);
            }

            if (fileName == null || !fileName.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase))
            {
                yield break;
            }

            // check if valid module manifest
            IEnumerable <ErrorRecord> errorRecord = null;
            PSModuleInfo psModuleInfo             = Helper.Instance.GetModuleManifest(fileName, out errorRecord);

            if ((errorRecord != null && errorRecord.Count() > 0) || psModuleInfo == null)
            {
                yield break;
            }

            var hashtableAst = ast.Find(x => x is HashtableAst, false) as HashtableAst;

            if (hashtableAst == null)
            {
                yield break;
            }

            string[] manifestFields = { functionsToExport, cmdletsToExport, aliasesToExport };

            foreach (string field in manifestFields)
            {
                IScriptExtent extent;
                if (!HasAcceptableExportField(field, hashtableAst, ast.Extent.Text, out extent) && extent != null)
                {
                    yield return(new DiagnosticRecord(
                                     GetError(field),
                                     extent,
                                     GetName(),
                                     DiagnosticSeverity.Warning,
                                     fileName,
                                     suggestedCorrections: GetCorrectionExtent(field, extent, psModuleInfo)));
                }
                else
                {
                }
            }
        }
        /// <summary>
        /// Predictor must be initialized with a list of string suggestions.
        /// </summary>
        /// <param name="modelPredictions">List of suggestions from the model, sorted by frequency (most to least)</param>
        /// <param name="parameterValuePredictor">Provide the prediction to the parameter values.</param>
        public Predictor(IList <string> modelPredictions, ParameterValuePredictor parameterValuePredictor)
        {
            this._parameterValuePredictor = parameterValuePredictor;
            this._predictions             = new List <Prediction>();

            foreach (var predictionTextRaw in modelPredictions ?? Enumerable.Empty <string>())
            {
                var predictionText = EscapePredictionText(predictionTextRaw);
                Ast ast            = Parser.ParseInput(predictionText, out Token[] tokens, out _);
                var commandAst     = (ast.Find((ast) => ast is CommandAst, searchNestedScriptBlocks: false) as CommandAst);

                if (commandAst?.CommandElements[0] is StringConstantExpressionAst commandName)
                {
                    var parameterSet = new ParameterSet(commandAst);
                    this._predictions.Add(new Prediction(commandName.Value, parameterSet));
                }
            }
        }
Exemple #8
0
        /// <summary>
        /// Create a new instance of <see cref="CommandLine"/> from <see cref="PredictiveCommand" />.
        /// </summary>
        /// <param name="predictiveCommand">The command information.</param>
        /// <param name="azContext">The current PowerShell conext.</param>
        public CommandLine(PredictiveCommand predictiveCommand, IAzContext azContext = null)
        {
            Validation.CheckArgument(predictiveCommand, $"{nameof(predictiveCommand)} cannot be null.");

            var predictionText = CommandLineUtilities.EscapePredictionText(predictiveCommand.Command);
            Ast ast            = Parser.ParseInput(predictionText, out Token[] tokens, out _);
            var commandAst     = ast.Find((ast) => ast is CommandAst, searchNestedScriptBlocks: false) as CommandAst;
            var commandName    = commandAst?.GetCommandName();

            Validation.CheckInvariant <CommandLineException>(!string.IsNullOrWhiteSpace(commandName), $"Cannot get the command name from the model {predictiveCommand.Command}");

            var parameterSet = new ParameterSet(commandAst, azContext);

            Name         = commandName;
            Description  = predictiveCommand.Description;
            ParameterSet = parameterSet;
            SourceText   = predictiveCommand.Command;
        }
Exemple #9
0
        public static IEnumerable <Token> GetTokens(Ast outerAst, Ast innerAst, Token[] outerTokens)
        {
            ThrowIfNull(outerAst, nameof(outerAst));
            ThrowIfNull(innerAst, nameof(innerAst));
            ThrowIfNull(outerTokens, nameof(outerTokens));

            // check if inner ast belongs in outerAst
            var foundAst = outerAst.Find(x => x.Equals(innerAst), true);

            if (foundAst == null)
            {
                // todo localize
                throw new ArgumentException(String.Format("innerAst cannot be found within outerAst"));
            }

            var tokenOps = new TokenOperations(outerTokens, outerAst);

            return(tokenOps.GetTokens(innerAst));
        }
Exemple #10
0
        /// <summary>
        /// Creates a new instance of <see cref="CommandLinePredictor"/>.
        /// </summary>
        /// <param name="modelPredictions">List of suggestions from the model, sorted by frequency (most to least).</param>
        /// <param name="parameterValuePredictor">Provide the prediction to the parameter values.</param>
        public CommandLinePredictor(IList <PredictiveCommand> modelPredictions, ParameterValuePredictor parameterValuePredictor)
        {
            Validation.CheckArgument(modelPredictions, $"{nameof(modelPredictions)} cannot be null.");

            _parameterValuePredictor = parameterValuePredictor;
            var commnadLines = new List <CommandLine>();

            foreach (var predictiveCommand in modelPredictions ?? Enumerable.Empty <PredictiveCommand>())
            {
                var predictionText = CommandLineUtilities.EscapePredictionText(predictiveCommand.Command);
                Ast ast            = Parser.ParseInput(predictionText, out Token[] tokens, out _);
                var commandAst     = (ast.Find((ast) => ast is CommandAst, searchNestedScriptBlocks: false) as CommandAst);

                if (commandAst?.CommandElements[0] is StringConstantExpressionAst commandName)
                {
                    var parameterSet = new ParameterSet(commandAst);
                    this._commandLinePredictions.Add(new CommandLine(commandName.Value, predictiveCommand.Description, parameterSet));
                }
            }
        }
 /// <summary>
 /// AnalyzeScript: Run Test Module Manifest to check that none of the required fields are missing. 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);
     }
     if (fileName == null)
     {
         yield break;
     }
     if (Helper.IsModuleManifest(fileName))
     {
         IEnumerable <ErrorRecord> errorRecords;
         var psModuleInfo = Helper.Instance.GetModuleManifest(fileName, out errorRecords);
         if (errorRecords != null)
         {
             foreach (var errorRecord in errorRecords)
             {
                 if (Helper.IsMissingManifestMemberException(errorRecord))
                 {
                     System.Diagnostics.Debug.Assert(
                         errorRecord.Exception != null && !String.IsNullOrWhiteSpace(errorRecord.Exception.Message),
                         Strings.NullErrorMessage);
                     var hashTableAst = ast.Find(x => x is HashtableAst, false);
                     if (hashTableAst == null)
                     {
                         yield break;
                     }
                     yield return(new DiagnosticRecord(
                                      errorRecord.Exception.Message,
                                      hashTableAst.Extent,
                                      GetName(),
                                      DiagnosticSeverity.Warning,
                                      fileName,
                                      suggestedCorrections: GetCorrectionExtent(hashTableAst as HashtableAst)));
                 }
             }
         }
     }
 }
        public void Execute(object sender, EventArgs args)
        {
            var commandAst = _ast.Find(m => m.Extent.StartOffset == _offset, true) as CommandAst;

            if (commandAst == null)
            {
                MessageBox.Show("Whoops! Something went wrong finding the definition of that function!", "Command Error",
                                MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            var functionDefinitionAst = FindDefinition(commandAst, commandAst.Parent);

            if (functionDefinitionAst == null)
            {
                MessageBox.Show("Unable to locate the definition to that function.", "Command Warning",
                                MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            var dte2 = (DTE2)Package.GetGlobalService(typeof(SDTE));

            if (dte2 != null)
            {
                var buffer = _textBuffers.FirstOrDefault(
                    m =>
                    m.GetFilePath() != null && m.GetFilePath().Equals(_fileName, StringComparison.OrdinalIgnoreCase));
                if (buffer != null)
                {
                    var ts = dte2.ActiveDocument.Selection as ITextSelection;
                    if (ts != null)
                    {
                        ts.Select(new SnapshotSpan(buffer.CurrentSnapshot, _offset, 0), false);
                    }
                }
            }
        }
Exemple #13
0
        /// <summary>
        /// Try to find a Param block on the top level of an AST.
        /// </summary>
        /// <param name="ast">The targeting AST.</param>
        /// <param name="paramBlockAst">Wanted Param block.</param>
        /// <returns>True if there is one. Otherwise, false.</returns>
        public static bool HasParamBlock(Ast ast, out ParamBlockAst paramBlockAst)
        {
            paramBlockAst = (ParamBlockAst)ast.Find(p => p is ParamBlockAst, false);

            return(paramBlockAst != null);
        }
Exemple #14
0
        // Quick check for script blocks that may have suspicious content. If this
        // is true, we log them to the event log despite event log settings.
        //
        // Performance notes:
        // 
        // For the current number of search terms, the this approach is about as high
        // performance as we can get. It adds about 1ms to the invocation of a script
        // block (we don't do this at parse time).
        // The manual tokenization is much faster than either Regex.Split
        // or a series of String.Split calls. Lookups in the HashSet are much faster
        // than a ton of calls to String.IndexOf (which .NET implements in native code).
        //
        // If we were to expand this set of keywords much farther, it would make sense
        // to look into implementing the Aho-Corasick algorithm (the one used by antimalware
        // engines), but Aho-Corasick is slower than the current approach for relatively
        // small match sets.
        internal static string CheckSuspiciousContent(Ast scriptBlockAst)
        {
            // Split the script block text into an array of elements that have
            // a-Z A-Z dash.
            string scriptBlockText = scriptBlockAst.Extent.Text;
            IEnumerable<string> elements = TokenizeWordElements(scriptBlockText);

            // First check for plain-text signatures
            ParallelOptions parallelOptions = new ParallelOptions();
            string foundSignature = null;

            Parallel.ForEach(elements, parallelOptions, (element, loopState) =>
            {
                if (foundSignature == null)
                {
                    if (s_signatures.Contains(element))
                    {
                        foundSignature = element;
                        loopState.Break();
                    }
                }
            });

            if (!String.IsNullOrEmpty(foundSignature))
            {
                return foundSignature;
            }

            if (scriptBlockAst.HasSuspiciousContent)
            {
                Ast foundAst = scriptBlockAst.Find(ast =>
                {
                    // Try to find the lowest AST that was not considered suspicious, but its parent
                    // was.
                    return (!ast.HasSuspiciousContent) && (ast.Parent.HasSuspiciousContent);
                }, true);

                if (foundAst != null)
                {
                    return foundAst.Parent.Extent.Text;
                }
                else
                {
                    return scriptBlockAst.Extent.Text;
                }
            }

            return null;
        }