From d1942a14083dd191e5d409ea289ade1ed554deaf Mon Sep 17 00:00:00 2001 From: Robert Holt Date: Wed, 4 Aug 2021 08:50:02 -0700 Subject: [PATCH] Implement -IncludeSuppressions parameter (#1701) --- .../Commands/InvokeScriptAnalyzerCommand.cs | 109 +++++++++------ Engine/Engine.csproj | 1 + Engine/Formatter.cs | 2 +- Engine/Generic/DiagnosticRecord.cs | 13 +- Engine/Generic/SuppressedRecord.cs | 1 + Engine/ScriptAnalyzer.cs | 37 ++++-- README.md | 22 ++- Tests/DisabledRules/AvoidOneChar.tests.ps1 | 3 +- .../AvoidTrapStatements.tests.ps1 | 3 +- .../AvoidUnloadableModule.tests.ps1 | 3 +- .../AvoidUsingClearHost.tests.ps1 | 3 +- .../AvoidUsingFilePath.tests.ps1 | 3 +- .../AvoidUsingInternalURLs.tests.ps1 | 3 +- .../AvoidUsingUninitializedVariable.Tests.ps1 | 5 +- Tests/DisabledRules/CommandNotFound.tests.ps1 | 3 +- .../ProvideVerboseMessage.tests.ps1 | 3 +- Tests/DisabledRules/TypeNotFound.tests.ps1 | 3 +- Tests/Engine/InvokeScriptAnalyzer.tests.ps1 | 64 ++++----- Tests/Engine/LibraryUsage.tests.ps1 | 30 +++-- Tests/Engine/ModuleHelp.Tests.ps1 | 2 +- Tests/Engine/RuleSuppression.tests.ps1 | 30 +++++ Utils/RuleMaker.psm1 | 1 - build.psm1 | 3 +- docs/markdown/Invoke-ScriptAnalyzer.md | 125 ++++++++++++++---- tools/appveyor.psm1 | 2 +- 25 files changed, 319 insertions(+), 155 deletions(-) diff --git a/Engine/Commands/InvokeScriptAnalyzerCommand.cs b/Engine/Commands/InvokeScriptAnalyzerCommand.cs index f5598d5bd..77364ac9c 100644 --- a/Engine/Commands/InvokeScriptAnalyzerCommand.cs +++ b/Engine/Commands/InvokeScriptAnalyzerCommand.cs @@ -21,13 +21,17 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands /// [Cmdlet(VerbsLifecycle.Invoke, "ScriptAnalyzer", - DefaultParameterSetName = "File", + DefaultParameterSetName = ParameterSet_Path_SuppressedOnly, SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=525914")] - [OutputType(typeof(DiagnosticRecord))] - [OutputType(typeof(SuppressedRecord))] + [OutputType(typeof(DiagnosticRecord), typeof(SuppressedRecord))] public class InvokeScriptAnalyzerCommand : PSCmdlet, IOutputWriter { + private const string ParameterSet_Path_SuppressedOnly = nameof(Path) + "_" + nameof(SuppressedOnly); + private const string ParameterSet_Path_IncludeSuppressed = nameof(Path) + "_" + nameof(IncludeSuppressed); + private const string ParameterSet_ScriptDefinition_SuppressedOnly = nameof(ScriptDefinition) + "_" + nameof(SuppressedOnly); + private const string ParameterSet_ScriptDefinition_IncludeSuppressed = nameof(ScriptDefinition) + "_" + nameof(IncludeSuppressed); + #region Private variables List processedPaths; #endregion // Private variables @@ -37,7 +41,12 @@ public class InvokeScriptAnalyzerCommand : PSCmdlet, IOutputWriter /// Path: The path to the file or folder to invoke PSScriptAnalyzer on. /// [Parameter(Position = 0, - ParameterSetName = "File", + ParameterSetName = ParameterSet_Path_IncludeSuppressed, + Mandatory = true, + ValueFromPipeline = true, + ValueFromPipelineByPropertyName = true)] + [Parameter(Position = 0, + ParameterSetName = ParameterSet_Path_SuppressedOnly, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -54,7 +63,12 @@ public string Path /// ScriptDefinition: a script definition in the form of a string to run rules on. /// [Parameter(Position = 0, - ParameterSetName = "ScriptDefinition", + ParameterSetName = ParameterSet_ScriptDefinition_IncludeSuppressed, + Mandatory = true, + ValueFromPipeline = true, + ValueFromPipelineByPropertyName = true)] + [Parameter(Position = 0, + ParameterSetName = ParameterSet_ScriptDefinition_SuppressedOnly, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -84,7 +98,6 @@ public string[] CustomRulePath /// RecurseCustomRulePath: Find rules within subfolders under the path /// [Parameter(Mandatory = false)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public SwitchParameter RecurseCustomRulePath { get { return recurseCustomRulePath; } @@ -96,7 +109,6 @@ public SwitchParameter RecurseCustomRulePath /// IncludeDefaultRules: Invoke default rules along with Custom rules /// [Parameter(Mandatory = false)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public SwitchParameter IncludeDefaultRules { get { return includeDefaultRules; } @@ -143,11 +155,15 @@ public string[] Severity } private string[] severity; + // TODO: This should be only in the Path parameter sets, and is ignored otherwise, + // but we already have a test that depends on it being otherwise + //[Parameter(ParameterSetName = ParameterSet_Path_IncludeSuppressed)] + //[Parameter(ParameterSetName = ParameterSet_Path_SuppressedOnly)] + // /// /// Recurse: Apply to all files within subfolders under the path /// - [Parameter(Mandatory = false)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + [Parameter] public SwitchParameter Recurse { get { return recurse; } @@ -158,19 +174,22 @@ public SwitchParameter Recurse /// /// ShowSuppressed: Show the suppressed message /// - [Parameter(Mandatory = false)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public SwitchParameter SuppressedOnly - { - get { return suppressedOnly; } - set { suppressedOnly = value; } - } - private bool suppressedOnly; + [Parameter(ParameterSetName = ParameterSet_Path_SuppressedOnly)] + [Parameter(ParameterSetName = ParameterSet_ScriptDefinition_SuppressedOnly)] + public SwitchParameter SuppressedOnly { get; set; } + + /// + /// Include suppressed diagnostics in the output. + /// + [Parameter(ParameterSetName = ParameterSet_Path_IncludeSuppressed, Mandatory = true)] + [Parameter(ParameterSetName = ParameterSet_ScriptDefinition_IncludeSuppressed, Mandatory = true)] + public SwitchParameter IncludeSuppressed { get; set; } /// /// Resolves rule violations automatically where possible. /// - [Parameter(Mandatory = false, ParameterSetName = "File")] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_Path_IncludeSuppressed)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_Path_SuppressedOnly)] public SwitchParameter Fix { get { return fix; } @@ -334,6 +353,12 @@ protected override void BeginProcessing() this.settings)); } + SuppressionPreference suppressionPreference = SuppressedOnly + ? SuppressionPreference.SuppressedOnly + : IncludeSuppressed + ? SuppressionPreference.Include + : SuppressionPreference.Omit; + ScriptAnalyzer.Instance.Initialize( this, combRulePaths, @@ -341,7 +366,7 @@ protected override void BeginProcessing() this.excludeRule, this.severity, combRulePaths == null || combIncludeDefaultRules, - this.suppressedOnly); + suppressionPreference); } /// @@ -402,29 +427,32 @@ protected override void StopProcessing() private void ProcessInput() { - IEnumerable diagnosticsList = Enumerable.Empty(); - if (IsFileParameterSet()) + WriteToOutput(RunAnalysis()); + } + + private IEnumerable RunAnalysis() + { + if (!IsFileParameterSet()) { - foreach (var p in processedPaths) - { - if (fix) - { - ShouldProcess(p, $"Analyzing and fixing path with Recurse={this.recurse}"); - diagnosticsList = ScriptAnalyzer.Instance.AnalyzeAndFixPath(p, this.ShouldProcess, this.recurse); - } - else - { - ShouldProcess(p, $"Analyzing path with Recurse={this.recurse}"); - diagnosticsList = ScriptAnalyzer.Instance.AnalyzePath(p, this.ShouldProcess, this.recurse); - } - WriteToOutput(diagnosticsList); - } + return ScriptAnalyzer.Instance.AnalyzeScriptDefinition(scriptDefinition, out _, out _); } - else if (String.Equals(this.ParameterSetName, "ScriptDefinition", StringComparison.OrdinalIgnoreCase)) + + var diagnostics = new List(); + foreach (string path in this.processedPaths) { - diagnosticsList = ScriptAnalyzer.Instance.AnalyzeScriptDefinition(scriptDefinition, out _, out _); - WriteToOutput(diagnosticsList); + if (fix) + { + ShouldProcess(path, $"Analyzing and fixing path with Recurse={this.recurse}"); + diagnostics.AddRange(ScriptAnalyzer.Instance.AnalyzeAndFixPath(path, this.ShouldProcess, this.recurse)); + } + else + { + ShouldProcess(path, $"Analyzing path with Recurse={this.recurse}"); + diagnostics.AddRange(ScriptAnalyzer.Instance.AnalyzePath(path, this.ShouldProcess, this.recurse)); + } } + + return diagnostics; } private void WriteToOutput(IEnumerable diagnosticRecords) @@ -497,10 +525,7 @@ private void ProcessPath() } } - private bool IsFileParameterSet() - { - return String.Equals(this.ParameterSetName, "File", StringComparison.OrdinalIgnoreCase); - } + private bool IsFileParameterSet() => Path is not null; private bool OverrideSwitchParam(bool paramValue, string paramName) { diff --git a/Engine/Engine.csproj b/Engine/Engine.csproj index debb7c4ef..f3f60f840 100644 --- a/Engine/Engine.csproj +++ b/Engine/Engine.csproj @@ -7,6 +7,7 @@ 1.20.0 Engine Microsoft.Windows.PowerShell.ScriptAnalyzer + 9.0 diff --git a/Engine/Formatter.cs b/Engine/Formatter.cs index 0067757cd..251060a6d 100644 --- a/Engine/Formatter.cs +++ b/Engine/Formatter.cs @@ -60,7 +60,7 @@ public static string Format( var currentSettings = GetCurrentSettings(settings, rule); ScriptAnalyzer.Instance.UpdateSettings(currentSettings); - ScriptAnalyzer.Instance.Initialize(cmdlet, null, null, null, null, true, false); + ScriptAnalyzer.Instance.Initialize(cmdlet, null, null, null, null, true, SuppressionPreference.Omit); text = ScriptAnalyzer.Instance.Fix(text, range, skipParsing, out Range updatedRange, out bool fixesWereApplied, ref scriptAst, ref scriptTokens, skipVariableAnalysis: true); skipParsing = !fixesWereApplied; diff --git a/Engine/Generic/DiagnosticRecord.cs b/Engine/Generic/DiagnosticRecord.cs index a02f8a273..cd325ecf2 100644 --- a/Engine/Generic/DiagnosticRecord.cs +++ b/Engine/Generic/DiagnosticRecord.cs @@ -92,12 +92,13 @@ public IEnumerable SuggestedCorrections set { suggestedCorrections = value; } } + public bool IsSuppressed { get; protected set; } = false; + /// /// DiagnosticRecord: The constructor for DiagnosticRecord class. /// public DiagnosticRecord() { - } /// @@ -109,7 +110,14 @@ public DiagnosticRecord() /// The severity of this diagnostic /// The full path of the script file being analyzed /// The correction suggested by the rule to replace the extent text - public DiagnosticRecord(string message, IScriptExtent extent, string ruleName, DiagnosticSeverity severity, string scriptPath, string ruleId = null, IEnumerable suggestedCorrections = null) + public DiagnosticRecord( + string message, + IScriptExtent extent, + string ruleName, + DiagnosticSeverity severity, + string scriptPath, + string ruleId = null, + IEnumerable suggestedCorrections = null) { Message = message; RuleName = ruleName; @@ -119,7 +127,6 @@ public DiagnosticRecord(string message, IScriptExtent extent, string ruleName, D RuleSuppressionID = ruleId; this.suggestedCorrections = suggestedCorrections; } - } diff --git a/Engine/Generic/SuppressedRecord.cs b/Engine/Generic/SuppressedRecord.cs index 57ca1a24a..66d1b4739 100644 --- a/Engine/Generic/SuppressedRecord.cs +++ b/Engine/Generic/SuppressedRecord.cs @@ -24,6 +24,7 @@ public class SuppressedRecord : DiagnosticRecord public SuppressedRecord(DiagnosticRecord record, IReadOnlyList suppressions) { Suppression = new ReadOnlyCollection(new List(suppressions)); + IsSuppressed = true; if (record != null) { RuleName = record.RuleName; diff --git a/Engine/ScriptAnalyzer.cs b/Engine/ScriptAnalyzer.cs index ffe277375..0a8169173 100644 --- a/Engine/ScriptAnalyzer.cs +++ b/Engine/ScriptAnalyzer.cs @@ -25,6 +25,13 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer { + internal enum SuppressionPreference + { + Omit = 0, + Include = 1, + SuppressedOnly = 2, + } + public sealed class ScriptAnalyzer { #region Private members @@ -41,7 +48,7 @@ public sealed class ScriptAnalyzer string[] severity; List includeRegexList; List excludeRegexList; - bool suppressedOnly; + private SuppressionPreference _suppressionPreference; #if !PSV3 ModuleDependencyHandler moduleHandler; #endif @@ -118,7 +125,7 @@ internal void Initialize( string[] excludeRuleNames = null, string[] severity = null, bool includeDefaultRules = false, - bool suppressedOnly = false) + SuppressionPreference suppressionPreference = SuppressionPreference.Omit) where TCmdlet : PSCmdlet, IOutputWriter { if (cmdlet == null) @@ -135,7 +142,7 @@ internal void Initialize( excludeRuleNames, severity, includeDefaultRules, - suppressedOnly); + suppressionPreference); } /// @@ -150,6 +157,7 @@ public void Initialize( string[] severity = null, bool includeDefaultRules = false, bool suppressedOnly = false, + bool includeSuppression = false, string profile = null) { if (runspace == null) @@ -163,6 +171,11 @@ public void Initialize( outputWriter); Helper.Instance.Initialize(); + SuppressionPreference suppressionPreference = suppressedOnly + ? SuppressionPreference.SuppressedOnly + : includeSuppression + ? SuppressionPreference.Include + : SuppressionPreference.Omit; this.Initialize( outputWriter, @@ -173,7 +186,7 @@ public void Initialize( excludeRuleNames, severity, includeDefaultRules, - suppressedOnly, + suppressionPreference, profile); } @@ -187,7 +200,7 @@ public void CleanUp() severity = null; includeRegexList = null; excludeRegexList = null; - suppressedOnly = false; + _suppressionPreference = SuppressionPreference.Omit; } /// @@ -671,7 +684,7 @@ private void Initialize( string[] excludeRuleNames, string[] severity, bool includeDefaultRules = false, - bool suppressedOnly = false, + SuppressionPreference suppressionPreference = SuppressionPreference.Omit, string profile = null) { if (outputWriter == null) @@ -730,7 +743,7 @@ private void Initialize( } } - this.suppressedOnly = suppressedOnly; + _suppressionPreference = suppressionPreference; this.includeRegexList = new List(); this.excludeRegexList = new List(); @@ -2324,9 +2337,13 @@ public IEnumerable AnalyzeSyntaxTree( // Need to reverse the concurrentbag to ensure that results are sorted in the increasing order of line numbers IEnumerable diagnosticsList = diagnostics.Reverse(); - return this.suppressedOnly ? - suppressed.OfType() : - diagnosticsList; + return _suppressionPreference switch + { + SuppressionPreference.SuppressedOnly => suppressed.OfType(), + SuppressionPreference.Omit => diagnosticsList, + SuppressionPreference.Include => diagnosticsList.Concat(suppressed.OfType()), + _ => throw new ArgumentException($"SuppressionPreference has invalid value '{_suppressionPreference}'"), + }; } } } diff --git a/README.md b/README.md index 56c04c1c1..aaaf09435 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,25 @@ Usage ``` PowerShell Get-ScriptAnalyzerRule [-CustomRulePath ] [-RecurseCustomRulePath] [-Name ] [-Severity ] [] -Invoke-ScriptAnalyzer [-Path] [-CustomRulePath ] [-RecurseCustomRulePath] [-ExcludeRule ] [-IncludeDefaultRules] [-IncludeRule ] [-Severity ] [-Recurse] [-SuppressedOnly] [-Fix] [-EnableExit] [-ReportSummary] [-Settings ] [-SaveDscDependency] [] - -Invoke-ScriptAnalyzer [-ScriptDefinition] [-CustomRulePath ] [-RecurseCustomRulePath] [-ExcludeRule ] [-IncludeDefaultRules] [-IncludeRule ] [-Severity ] [-Recurse] [-SuppressedOnly] [-EnableExit] [-ReportSummary] [-Settings ] [-SaveDscDependency] [] +Invoke-ScriptAnalyzer [-Path] [-CustomRulePath ] [-RecurseCustomRulePath] + [-IncludeDefaultRules] [-ExcludeRule ] [-IncludeRule ] [-Severity ] [-Recurse] + [-SuppressedOnly] [-Fix] [-EnableExit] [-Settings ] [-SaveDscDependency] [-ReportSummary] [-WhatIf] + [-Confirm] [] + +Invoke-ScriptAnalyzer [-Path] [-CustomRulePath ] [-RecurseCustomRulePath] + [-IncludeDefaultRules] [-ExcludeRule ] [-IncludeRule ] [-Severity ] [-Recurse] + [-IncludeSuppressed] [-Fix] [-EnableExit] [-Settings ] [-SaveDscDependency] [-ReportSummary] [-WhatIf] + [-Confirm] [] + +Invoke-ScriptAnalyzer [-ScriptDefinition] [-CustomRulePath ] [-RecurseCustomRulePath] + [-IncludeDefaultRules] [-ExcludeRule ] [-IncludeRule ] [-Severity ] + [-IncludeSuppressed] [-EnableExit] [-Settings ] [-SaveDscDependency] [-ReportSummary] [-WhatIf] + [-Confirm] [] + +Invoke-ScriptAnalyzer [-ScriptDefinition] [-CustomRulePath ] [-RecurseCustomRulePath] + [-IncludeDefaultRules] [-ExcludeRule ] [-IncludeRule ] [-Severity ] + [-SuppressedOnly] [-EnableExit] [-Settings ] [-SaveDscDependency] [-ReportSummary] [-WhatIf] + [-Confirm] [] Invoke-Formatter [-ScriptDefinition] [[-Settings] ] [[-Range] ] [] ``` diff --git a/Tests/DisabledRules/AvoidOneChar.tests.ps1 b/Tests/DisabledRules/AvoidOneChar.tests.ps1 index 29ff0b7db..dae259602 100644 --- a/Tests/DisabledRules/AvoidOneChar.tests.ps1 +++ b/Tests/DisabledRules/AvoidOneChar.tests.ps1 @@ -1,5 +1,4 @@ -Import-Module PSScriptAnalyzer -$oneCharMessage = "The cmdlet name O only has one character." +$oneCharMessage = "The cmdlet name O only has one character." $oneCharName = "PSOneChar" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $invoke = Invoke-ScriptAnalyzer $directory\AvoidUsingReservedCharOneCharNames.ps1 | Where-Object {$_.RuleName -eq $oneCharName} diff --git a/Tests/DisabledRules/AvoidTrapStatements.tests.ps1 b/Tests/DisabledRules/AvoidTrapStatements.tests.ps1 index 348d13f7c..985ced734 100644 --- a/Tests/DisabledRules/AvoidTrapStatements.tests.ps1 +++ b/Tests/DisabledRules/AvoidTrapStatements.tests.ps1 @@ -1,5 +1,4 @@ -Import-Module PSScriptAnalyzer -$violationMessage = "Trap found." +$violationMessage = "Trap found." $violationName = "PSAvoidTrapStatement" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $violations = Invoke-ScriptAnalyzer $directory\AvoidTrapStatements.ps1 | Where-Object {$_.RuleName -eq $violationName} diff --git a/Tests/DisabledRules/AvoidUnloadableModule.tests.ps1 b/Tests/DisabledRules/AvoidUnloadableModule.tests.ps1 index 283bdc748..7149c3631 100644 --- a/Tests/DisabledRules/AvoidUnloadableModule.tests.ps1 +++ b/Tests/DisabledRules/AvoidUnloadableModule.tests.ps1 @@ -1,5 +1,4 @@ -Import-Module PSScriptAnalyzer -$unloadableMessage = [regex]::Escape("Cannot load the module TestBadModule that the file TestBadModule.psd1 is in.") +$unloadableMessage = [regex]::Escape("Cannot load the module TestBadModule that the file TestBadModule.psd1 is in.") $unloadableName = "PSAvoidUnloadableModule" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $violations = Invoke-ScriptAnalyzer $directory\TestBadModule\TestBadModule.psd1 | Where-Object {$_.RuleName -eq $unloadableName} diff --git a/Tests/DisabledRules/AvoidUsingClearHost.tests.ps1 b/Tests/DisabledRules/AvoidUsingClearHost.tests.ps1 index 143ffb3b2..4f883cfa4 100644 --- a/Tests/DisabledRules/AvoidUsingClearHost.tests.ps1 +++ b/Tests/DisabledRules/AvoidUsingClearHost.tests.ps1 @@ -1,5 +1,4 @@ -Import-Module PSScriptAnalyzer -Set-Alias ctss ConvertTo-SecureString +Set-Alias ctss ConvertTo-SecureString $clearHostMessage = "File 'AvoidUsingClearHostWriteHost.ps1' uses Clear-Host. This is not recommended because it may not work in some hosts or there may even be no hosts at all." $clearHostName = "PSAvoidUsingClearHost" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path diff --git a/Tests/DisabledRules/AvoidUsingFilePath.tests.ps1 b/Tests/DisabledRules/AvoidUsingFilePath.tests.ps1 index bd7f37b5e..a93ae3141 100644 --- a/Tests/DisabledRules/AvoidUsingFilePath.tests.ps1 +++ b/Tests/DisabledRules/AvoidUsingFilePath.tests.ps1 @@ -1,5 +1,4 @@ -Import-Module PSScriptAnalyzer -$violationMessage = @' +$violationMessage = @' The file path "D:\\Code" of AvoidUsingFilePath.ps1 is rooted. This should be avoided if AvoidUsingFilePath.ps1 is published online '@ $violationUNCMessage = @' diff --git a/Tests/DisabledRules/AvoidUsingInternalURLs.tests.ps1 b/Tests/DisabledRules/AvoidUsingInternalURLs.tests.ps1 index 376f840d0..3584dae84 100644 --- a/Tests/DisabledRules/AvoidUsingInternalURLs.tests.ps1 +++ b/Tests/DisabledRules/AvoidUsingInternalURLs.tests.ps1 @@ -1,5 +1,4 @@ -Import-Module PSScriptAnalyzer -$violationMessage = "could be an internal URL. Using internal URL directly in the script may cause potential information disclosure." +$violationMessage = "could be an internal URL. Using internal URL directly in the script may cause potential information disclosure." $violationName = "PSAvoidUsingInternalURLs" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $violations = Invoke-ScriptAnalyzer $directory\AvoidUsingInternalURLs.ps1 | Where-Object {$_.RuleName -eq $violationName} diff --git a/Tests/DisabledRules/AvoidUsingUninitializedVariable.Tests.ps1 b/Tests/DisabledRules/AvoidUsingUninitializedVariable.Tests.ps1 index f2922535f..45843cc10 100644 --- a/Tests/DisabledRules/AvoidUsingUninitializedVariable.Tests.ps1 +++ b/Tests/DisabledRules/AvoidUsingUninitializedVariable.Tests.ps1 @@ -1,5 +1,4 @@ -Import-Module PSScriptAnalyzer -$AvoidUninitializedVariable = "PSAvoidUninitializedVariable" +$AvoidUninitializedVariable = "PSAvoidUninitializedVariable" $violationMessage = "Variable 'MyVerbosePreference' is not initialized. Non-global variables must be initialized. To fix a violation of this rule, please initialize non-global variables." $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $violations = Invoke-ScriptAnalyzer $directory\AvoidUsingUninitializedVariable.ps1 -IncludeRule $AvoidUninitializedVariable @@ -12,7 +11,7 @@ Describe "AvoidUsingUninitializedVariable" { } It "has the correct description message for UninitializedVariable rule violation" { - $violations[0].Message | Should -Be $violationMessage + $violations[0].Message | Should -Be $violationMessage } } diff --git a/Tests/DisabledRules/CommandNotFound.tests.ps1 b/Tests/DisabledRules/CommandNotFound.tests.ps1 index e311fdca5..c6b198b09 100644 --- a/Tests/DisabledRules/CommandNotFound.tests.ps1 +++ b/Tests/DisabledRules/CommandNotFound.tests.ps1 @@ -1,5 +1,4 @@ -Import-Module -Verbose ScriptAnalyzer -$violationMessage = "Command Get-WrongCommand Is Not Found" +$violationMessage = "Command Get-WrongCommand Is Not Found" $violationName = "PSCommandNotFound" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $violations = Invoke-ScriptAnalyzer $directory\CommandNotFound.ps1 | Where-Object {$_.RuleName -eq $violationName} diff --git a/Tests/DisabledRules/ProvideVerboseMessage.tests.ps1 b/Tests/DisabledRules/ProvideVerboseMessage.tests.ps1 index 290e1ec91..7f5867177 100644 --- a/Tests/DisabledRules/ProvideVerboseMessage.tests.ps1 +++ b/Tests/DisabledRules/ProvideVerboseMessage.tests.ps1 @@ -1,5 +1,4 @@ -Import-Module PSScriptAnalyzer -$violationMessage = [regex]::Escape("There is no call to Write-Verbose in the function 'Verb-Files'.") +$violationMessage = [regex]::Escape("There is no call to Write-Verbose in the function 'Verb-Files'.") $violationName = "PSProvideVerboseMessage" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $violations = Invoke-ScriptAnalyzer $directory\BadCmdlet.ps1 | Where-Object {$_.RuleName -eq $violationName} diff --git a/Tests/DisabledRules/TypeNotFound.tests.ps1 b/Tests/DisabledRules/TypeNotFound.tests.ps1 index 48e4261fb..51b36b2b3 100644 --- a/Tests/DisabledRules/TypeNotFound.tests.ps1 +++ b/Tests/DisabledRules/TypeNotFound.tests.ps1 @@ -1,5 +1,4 @@ -Import-Module -Verbose PSScriptAnalyzer -$violationMessage = "Type Stre is not found" +$violationMessage = "Type Stre is not found" $violationName = "PSTypeNotFound" $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $violations = Invoke-ScriptAnalyzer $directory\TypeNotFound.ps1 | Where-Object {$_.RuleName -eq $violationName} diff --git a/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 b/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 index a1b8b7380..06b94cb78 100644 --- a/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 +++ b/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 @@ -2,6 +2,8 @@ # Licensed under the MIT License. BeforeAll { + # NOTE: We also run these tests with a custom Invoke-ScriptAnalyzer function defined in LibraryUsage.Tests.ps1 + # This can cause issues if the cmdlet is updated and the function isn't $sa = Get-Command Invoke-ScriptAnalyzer $singularNouns = "PSUseSingularNouns" $approvedVerb = "PSUseApprovedVerbs" @@ -83,35 +85,15 @@ Describe "Test available parameters" { } } - Context "It has 2 parameter sets: File and ScriptDefinition" { - It "Has 2 parameter sets" { - $sa.ParameterSets.Count | Should -Be 2 - } - - It "Has File parameter set" { - $hasFile = $false - foreach ($paramSet in $sa.ParameterSets) { - if ($paramSet.Name -eq "File") { - $hasFile = $true - break - } - } - - $hasFile | Should -BeTrue - } - - It "Has ScriptDefinition parameter set" { - $hasFile = $false - foreach ($paramSet in $sa.ParameterSets) { - if ($paramSet.Name -eq "ScriptDefinition") { - $hasFile = $true - break - } - } - - $hasFile | Should -BeTrue - } + It "Has 4 parameter sets" { + $parameterSets = @( + 'Path_IncludeSuppressed' + 'Path_SuppressedOnly' + 'ScriptDefinition_IncludeSuppressed' + 'ScriptDefinition_SuppressedOnly' + ) + $sa.ParameterSets | Select-Object -ExpandProperty Name | Sort-Object | Should -Be $parameterSets } } @@ -577,13 +559,17 @@ Describe "Test -EnableExit Switch" { $pwshExe = 'powershell' } - & $pwshExe -Command 'Import-Module PSScriptAnalyzer; Invoke-ScriptAnalyzer -ScriptDefinition gci -EnableExit' + $pssaPath = (Get-Module PSScriptAnalyzer).Path + + & $pwshExe -Command "Import-Module '$pssaPath'; Invoke-ScriptAnalyzer -ScriptDefinition gci -EnableExit" $LASTEXITCODE | Should -Be 1 } Describe "-ReportSummary switch" { BeforeAll { + $pssaPath = (Get-Module PSScriptAnalyzer).Path + if ($IsCoreCLR) { $pwshExe = (Get-Process -Id $PID).Path @@ -597,12 +583,12 @@ Describe "Test -EnableExit Switch" { } It "prints the correct report summary using the -NoReportSummary switch" { - $result = & $pwshExe -Command 'Import-Module PSScriptAnalyzer; Invoke-ScriptAnalyzer -ScriptDefinition gci -ReportSummary' + $result = & $pwshExe -Command "Import-Module '$pssaPath'; Invoke-ScriptAnalyzer -ScriptDefinition gci -ReportSummary" "$result" | Should -BeLike $reportSummaryFor1Warning } It "does not print the report summary when not using -NoReportSummary switch" { - $result = & $pwshExe -Command 'Import-Module PSScriptAnalyzer; Invoke-ScriptAnalyzer -ScriptDefinition gci' + $result = & $pwshExe -Command "Import-Module '$pssaPath'; Invoke-ScriptAnalyzer -ScriptDefinition gci" "$result" | Should -Not -BeLike $reportSummaryFor1Warning } @@ -640,3 +626,19 @@ Describe "Test -EnableExit Switch" { } } } + +Describe 'Suppression switch parameter sets' { + It 'Should not allow both suppression switches to be used' { + try + { + Invoke-ScriptAnalyzer -ScriptDefinition 'gci' -IncludeSuppressed -SuppressedOnly + } + catch + { + $errorId = $_.FullyQualifiedErrorId + } + + $errorId = $errorId.Substring(0, $errorId.IndexOf(',')) + $errorId | Should -BeExactly 'AmbiguousParameterSet' + } +} diff --git a/Tests/Engine/LibraryUsage.tests.ps1 b/Tests/Engine/LibraryUsage.tests.ps1 index 4dbc34c43..f8f30226b 100644 --- a/Tests/Engine/LibraryUsage.tests.ps1 +++ b/Tests/Engine/LibraryUsage.tests.ps1 @@ -7,13 +7,15 @@ Describe 'Library Usage' -Skip:$IsCoreCLR { # wraps the usage of ScriptAnalyzer as a .NET library function Invoke-ScriptAnalyzer { param ( - [CmdletBinding(DefaultParameterSetName="File", SupportsShouldProcess = $true)] + [CmdletBinding(DefaultParameterSetName="Path_SuppressedOnly", SupportsShouldProcess = $true)] - [parameter(Mandatory = $true, Position = 0, ParameterSetName="File")] + [parameter(Mandatory = $true, Position = 0, ParameterSetName="Path_SuppressedOnly")] + [parameter(Mandatory = $true, Position = 0, ParameterSetName="Path_IncludeSuppressed")] [Alias("PSPath")] [string] $Path, - [parameter(Mandatory = $true, ParameterSetName="ScriptDefinition")] + [parameter(Mandatory = $true, ParameterSetName="ScriptDefinition_SuppressedOnly")] + [parameter(Mandatory = $true, ParameterSetName="ScriptDefinition_IncludeSuppressed")] [string] $ScriptDefinition, [Parameter(Mandatory = $false)] @@ -39,9 +41,14 @@ Describe 'Library Usage' -Skip:$IsCoreCLR { [Parameter(Mandatory = $false)] [switch] $IncludeDefaultRules, - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $false, ParameterSetName = "Path_SuppressedOnly")] + [Parameter(Mandatory = $false, ParameterSetName = "ScriptDefinition_SuppressedOnly")] [switch] $SuppressedOnly, + [Parameter(Mandatory, ParameterSetName = "Path_IncludeSuppressed")] + [Parameter(Mandatory, ParameterSetName = "ScriptDefinition_IncludeSuppressed")] + [switch] $IncludeSuppressed, + [Parameter(Mandatory = $false)] [switch] $Fix, @@ -71,25 +78,26 @@ Describe 'Library Usage' -Skip:$IsCoreCLR { $IncludeRule, $ExcludeRule, $Severity, - $IncludeDefaultRules.IsPresent, - $SuppressedOnly.IsPresent + $IncludeDefaultRules, + $SuppressedOnly, + $IncludeSuppressed ); - if ($PSCmdlet.ParameterSetName -eq "File") + if ($Path) { $supportsShouldProcessFunc = [Func[string, string, bool]] { return $PSCmdlet.Shouldprocess } - if ($Fix.IsPresent) + if ($Fix) { - $results = $scriptAnalyzer.AnalyzeAndFixPath($Path, $supportsShouldProcessFunc, $Recurse.IsPresent); + $results = $scriptAnalyzer.AnalyzeAndFixPath($Path, $supportsShouldProcessFunc, $Recurse); } else { - $results = $scriptAnalyzer.AnalyzePath($Path, $supportsShouldProcessFunc, $Recurse.IsPresent); + $results = $scriptAnalyzer.AnalyzePath($Path, $supportsShouldProcessFunc, $Recurse); } } else { - $results = $scriptAnalyzer.AnalyzeScriptDefinition($ScriptDefinition, [ref] $null, [ref] $null) + $results = $scriptAnalyzer.AnalyzeScriptDefinition($ScriptDefinition, [ref] $null, [ref] $null) } $results diff --git a/Tests/Engine/ModuleHelp.Tests.ps1 b/Tests/Engine/ModuleHelp.Tests.ps1 index 9fc2e2e0a..c866bc179 100644 --- a/Tests/Engine/ModuleHelp.Tests.ps1 +++ b/Tests/Engine/ModuleHelp.Tests.ps1 @@ -180,7 +180,7 @@ Describe 'Cmdlet parameter help' { ) BEGIN { - $Common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable', 'WhatIf', 'Confirm' + $Common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable' $parameters = @() } PROCESS { diff --git a/Tests/Engine/RuleSuppression.tests.ps1 b/Tests/Engine/RuleSuppression.tests.ps1 index 891675b3b..c014dbc12 100644 --- a/Tests/Engine/RuleSuppression.tests.ps1 +++ b/Tests/Engine/RuleSuppression.tests.ps1 @@ -119,8 +119,10 @@ function MyFunc $diagnostics | Should -HaveCount 1 $diagnostics[0].RuleName | Should -BeExactly "PSAvoidUsingPlainTextForPassword" $diagnostics[0].RuleSuppressionID | Should -BeExactly "password2" + $diagnostics[0].IsSuppressed | Should -BeFalse $suppressions | Should -HaveCount 1 + $suppressions[0].IsSuppressed | Should -BeTrue $suppressions[0].RuleName | Should -BeExactly "PSAvoidUsingPlainTextForPassword" $suppressions[0].RuleSuppressionID | Should -BeExactly "password1" $suppressions[0].Suppression | Should -HaveCount 2 @@ -154,6 +156,34 @@ function MyFunc $suppressions[0].Suppression.Justification | Sort-Object | Should -Be @('a', 'a') } + It "Includes both emitted and suppressed diagnostics when -IncludeSuppressed is used" { + $script = @' +function MyFunc +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("PSAvoidUsingPlainTextForPassword", "password1", Justification='a')] + [System.Diagnostics.CodeAnalysis.SuppressMessage("PSAvoidUsingPlainTextForPassword", "password1", Justification='a')] + param( + [string]$password1, + [string]$password2 + ) +} +'@ + + $diagnostics = Invoke-ScriptAnalyzer -ScriptDefinition $script -IncludeRule 'PSAvoidUsingPlainTextForPassword' -IncludeSuppressed + + $diagnostics | Should -HaveCount 2 + $diagnostics[0].RuleName | Should -BeExactly "PSAvoidUsingPlainTextForPassword" + $diagnostics[0].RuleSuppressionID | Should -BeExactly "password2" + $diagnostics[0].IsSuppressed | Should -BeFalse + + $diagnostics[1].RuleName | Should -BeExactly "PSAvoidUsingPlainTextForPassword" + $diagnostics[1].RuleSuppressionID | Should -BeExactly "password1" + $diagnostics[1].Suppression | Should -HaveCount 2 + $diagnostics[1].Suppression.Justification | Sort-Object | Should -Be @('a', 'a') + $diagnostics[1].IsSuppressed | Should -BeTrue + } + + It "Records no suppressions for a different rule" { $script = @' function MyFunc diff --git a/Utils/RuleMaker.psm1 b/Utils/RuleMaker.psm1 index c3cad6622..71a5d551f 100644 --- a/Utils/RuleMaker.psm1 +++ b/Utils/RuleMaker.psm1 @@ -344,7 +344,6 @@ Function Add-RuleTest($Rule) $ruleTestFilePath = Get-RuleTestFilePath $Rule New-Item -Path $ruleTestFilePath -ItemType File $ruleTestTemplate = @' -Import-Module PSScriptAnalyzer $ruleName = "{0}" Describe "{0}" {{ diff --git a/build.psm1 b/build.psm1 index 5743be761..4a18c68b5 100644 --- a/build.psm1 +++ b/build.psm1 @@ -388,7 +388,8 @@ function Test-ScriptAnalyzer } $savedModulePath = $env:PSModulePath $env:PSModulePath = "${testModulePath}{0}${env:PSModulePath}" -f [System.IO.Path]::PathSeparator - $scriptBlock = [scriptblock]::Create("Import-Module PSScriptAnalyzer; Invoke-Pester -Path $testScripts") + $analyzerPsd1Path = Join-Path -Path $script:destinationDir -ChildPath "$analyzerName.psd1" + $scriptBlock = [scriptblock]::Create("Import-Module '$analyzerPsd1Path'; Invoke-Pester -Path $testScripts") if ( $InProcess ) { & $scriptBlock } diff --git a/docs/markdown/Invoke-ScriptAnalyzer.md b/docs/markdown/Invoke-ScriptAnalyzer.md index 4150da099..5282efbc8 100644 --- a/docs/markdown/Invoke-ScriptAnalyzer.md +++ b/docs/markdown/Invoke-ScriptAnalyzer.md @@ -1,26 +1,46 @@ --- external help file: Microsoft.Windows.PowerShell.ScriptAnalyzer.dll-Help.xml +Module Name: PSScriptAnalyzer schema: 2.0.0 --- # Invoke-ScriptAnalyzer + ## SYNOPSIS Evaluates a script or module based on selected best practice rules ## SYNTAX -### UNNAMED_PARAMETER_SET_1 +### Path_SuppressedOnly (Default) +``` +Invoke-ScriptAnalyzer [-Path] [-CustomRulePath ] [-RecurseCustomRulePath] + [-IncludeDefaultRules] [-ExcludeRule ] [-IncludeRule ] [-Severity ] [-Recurse] + [-SuppressedOnly] [-Fix] [-EnableExit] [-Settings ] [-SaveDscDependency] [-ReportSummary] [-WhatIf] + [-Confirm] [] +``` + +### Path_IncludeSuppressed ``` -Invoke-ScriptAnalyzer [-Path] [-CustomRulePath ] [-RecurseCustomRulePath] - [-ExcludeRule ] [-IncludeRule ] [-Severity ] [-Recurse] [-SuppressedOnly] [-Fix] [-EnableExit] [-ReportSummary] - [-Settings ] +Invoke-ScriptAnalyzer [-Path] [-CustomRulePath ] [-RecurseCustomRulePath] + [-IncludeDefaultRules] [-ExcludeRule ] [-IncludeRule ] [-Severity ] [-Recurse] + [-IncludeSuppressed] [-Fix] [-EnableExit] [-Settings ] [-SaveDscDependency] [-ReportSummary] [-WhatIf] + [-Confirm] [] ``` -### UNNAMED_PARAMETER_SET_2 +### ScriptDefinition_IncludeSuppressed ``` -Invoke-ScriptAnalyzer [-ScriptDefinition] [-CustomRulePath ] [-RecurseCustomRulePath] - [-ExcludeRule ] [-IncludeRule ] [-Severity ] [-Recurse] [-SuppressedOnly] [-EnableExit] [-ReportSummary] - [-Settings ] +Invoke-ScriptAnalyzer [-ScriptDefinition] [-CustomRulePath ] [-RecurseCustomRulePath] + [-IncludeDefaultRules] [-ExcludeRule ] [-IncludeRule ] [-Severity ] + [-IncludeSuppressed] [-EnableExit] [-Settings ] [-SaveDscDependency] [-ReportSummary] [-WhatIf] + [-Confirm] [] +``` + +### ScriptDefinition_SuppressedOnly +``` +Invoke-ScriptAnalyzer [-ScriptDefinition] [-CustomRulePath ] [-RecurseCustomRulePath] + [-IncludeDefaultRules] [-ExcludeRule ] [-IncludeRule ] [-Severity ] + [-SuppressedOnly] [-EnableExit] [-Settings ] [-SaveDscDependency] [-ReportSummary] [-WhatIf] + [-Confirm] [] ``` ## DESCRIPTION @@ -46,14 +66,14 @@ For more information about PSScriptAnalyzer, to contribute or file an issue, see ## EXAMPLES -### -------------------------- EXAMPLE 1 -------------------------- +### EXAMPLE 1 ``` Invoke-ScriptAnalyzer -Path C:\Scripts\Get-LogData.ps1 ``` This command runs all Script Analyzer rules on the Get-LogData.ps1 script. -### -------------------------- EXAMPLE 2 -------------------------- +### EXAMPLE 2 ``` Invoke-ScriptAnalyzer -Path $home\Documents\WindowsPowerShell\Modules -Recurse ``` @@ -61,7 +81,7 @@ Invoke-ScriptAnalyzer -Path $home\Documents\WindowsPowerShell\Modules -Recurse This command runs all Script Analyzer rules on all .ps1 and .psm1 files in the Modules directory and its subdirectories. -### -------------------------- EXAMPLE 3 -------------------------- +### EXAMPLE 3 ``` Invoke-ScriptAnalyzer -Path C:\Windows\System32\WindowsPowerShell\v1.0\Modules\PSDiagnostics -IncludeRule PSAvoidUsingPositionalParameters ``` @@ -69,21 +89,21 @@ Invoke-ScriptAnalyzer -Path C:\Windows\System32\WindowsPowerShell\v1.0\Modules\P This command runs only the PSAvoidUsingPositionalParameters rule on the files in the PSDiagnostics module. You might use a command like this to find all instances of a particular rule violation while working to eliminate it. -### -------------------------- EXAMPLE 4 -------------------------- +### EXAMPLE 4 ``` Invoke-ScriptAnalyzer -Path C:\ps-test\MyModule -Recurse -ExcludeRule PSAvoidUsingCmdletAliases, PSAvoidUsingInternalURLs ``` This command runs Script Analyzer on the .ps1 and .psm1 files in the MyModules directory, including the scripts in its subdirectories, with all rules except for PSAvoidUsingCmdletAliases and PSAvoidUsingInternalURLs. -### -------------------------- EXAMPLE 5 -------------------------- +### EXAMPLE 5 ``` Invoke-ScriptAnalyzer -Path D:\test_scripts\Test-Script.ps1 -CustomRulePath C:\CommunityAnalyzerRules ``` This command runs Script Analyzer on Test-Script.ps1 with the standard rules and rules in the C:\CommunityAnalyzerRules path. -### -------------------------- EXAMPLE 6 -------------------------- +### EXAMPLE 6 ``` $DSCError = Get-ScriptAnalyzerRule -Severity Error | Where SourceName -eq PSDSC @@ -94,7 +114,7 @@ PS C:\> Invoke-ScriptAnalyzerRule -Path $Path -IncludeRule $DSCError -Recurse This example runs only the rules that are Error severity and have the PSDSC source name. -### -------------------------- EXAMPLE 7 -------------------------- +### EXAMPLE 7 ``` function Get-Widgets { @@ -140,7 +160,7 @@ The second command uses the SuppressedOnly parameter to discover the rules that file. The output reports the suppressed rules. -### -------------------------- EXAMPLE 8 -------------------------- +### EXAMPLE 8 ``` # In .\ScriptAnalyzerProfile.txt @{ @@ -162,7 +182,7 @@ Script Analyzer profile. If you include a conflicting parameter in the Invoke-ScriptAnalyzer command, such as '-Severity Error', Invoke-ScriptAnalyzer uses the profile value and ignores the parameter. -### -------------------------- EXAMPLE 9 -------------------------- +### EXAMPLE 9 ``` Invoke-ScriptAnalyzer -ScriptDefinition "function Get-Widgets {Write-Host 'Hello'}" @@ -196,13 +216,13 @@ To analyze files that are not in the root directory of the specified path, use a ```yaml Type: String -Parameter Sets: UNNAMED_PARAMETER_SET_1 +Parameter Sets: Path_SuppressedOnly, Path_IncludeSuppressed Aliases: PSPath Required: True Position: 0 -Default value: -Accept pipeline input: False +Default value: None +Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` @@ -224,7 +244,7 @@ Aliases: CustomizedRulePath Required: False Position: Named -Default value: +Default value: None Accept pipeline input: False Accept wildcard characters: False ``` @@ -241,7 +261,7 @@ Aliases: Required: False Position: Named -Default value: +Default value: None Accept pipeline input: False Accept wildcard characters: False ``` @@ -348,7 +368,7 @@ To search the CustomRulePath recursively, use the RecurseCustomRulePath paramete ```yaml Type: SwitchParameter -Parameter Sets: (All) +Parameter Sets: Path_SuppressedOnly, Path_IncludeSuppressed Aliases: Required: False @@ -369,7 +389,7 @@ For help, see the examples. ```yaml Type: SwitchParameter -Parameter Sets: (All) +Parameter Sets: Path_SuppressedOnly, ScriptDefinition_SuppressedOnly Aliases: Required: False @@ -388,7 +408,7 @@ It tries to preserve the file encoding but there are still some cases where the ```yaml Type: SwitchParameter -Parameter Sets: UNNAMED_PARAMETER_SET_1 +Parameter Sets: Path_SuppressedOnly, Path_IncludeSuppressed Aliases: Required: False @@ -476,7 +496,7 @@ Aliases: Profile Required: False Position: Named -Default value: +Default value: None Accept pipeline input: False Accept wildcard characters: False ``` @@ -489,13 +509,13 @@ Unlike ScriptBlock parameters, the ScriptDefinition parameter requires a string ```yaml Type: String -Parameter Sets: UNNAMED_PARAMETER_SET_2 +Parameter Sets: ScriptDefinition_IncludeSuppressed, ScriptDefinition_SuppressedOnly Aliases: Required: True Position: 0 -Default value: -Accept pipeline input: False +Default value: None +Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` @@ -516,6 +536,53 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -IncludeSuppressed +Include suppressed diagnostics in output. + +```yaml +Type: SwitchParameter +Parameter Sets: Path_IncludeSuppressed, ScriptDefinition_IncludeSuppressed +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS diff --git a/tools/appveyor.psm1 b/tools/appveyor.psm1 index 5528670ed..bbad9643d 100644 --- a/tools/appveyor.psm1 +++ b/tools/appveyor.psm1 @@ -141,7 +141,7 @@ function Invoke-AppveyorTest { Write-Verbose -Verbose "Module versions:" Get-Module PSScriptAnalyzer,Pester,PowershellGet -ErrorAction SilentlyContinue | ForEach-Object { - Write-Verbose -Verbose "$($_.Name): $($_.Version)" + Write-Verbose -Verbose "$($_.Name): $($_.Version) [$($_.Path)]" } $configuration = [PesterConfiguration]::Default