From d8757e77c373284dc648cee88adc04d0e90da7ae Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Wed, 10 Feb 2016 20:34:46 -0800 Subject: [PATCH 1/7] Initial commit of a rule that checks the *ToExport manifest fields for explicit lists. --- RuleDocumentation/UseManifestExportFields.md | 0 Rules/ScriptAnalyzerBuiltinRules.csproj | 1 + Rules/Strings.Designer.cs | 36 ++++ Rules/Strings.resx | 12 ++ Rules/UseManifestExportFields.cs | 159 ++++++++++++++++++ Tests/Rules/UseManifestExportFields.ps1 | 0 Tests/Rules/UseManifestExportFields.tests.ps1 | 0 7 files changed, 208 insertions(+) create mode 100644 RuleDocumentation/UseManifestExportFields.md create mode 100644 Rules/UseManifestExportFields.cs create mode 100644 Tests/Rules/UseManifestExportFields.ps1 create mode 100644 Tests/Rules/UseManifestExportFields.tests.ps1 diff --git a/RuleDocumentation/UseManifestExportFields.md b/RuleDocumentation/UseManifestExportFields.md new file mode 100644 index 000000000..e69de29bb diff --git a/Rules/ScriptAnalyzerBuiltinRules.csproj b/Rules/ScriptAnalyzerBuiltinRules.csproj index 3862c8b8f..77f9cded2 100644 --- a/Rules/ScriptAnalyzerBuiltinRules.csproj +++ b/Rules/ScriptAnalyzerBuiltinRules.csproj @@ -95,6 +95,7 @@ Strings.resx + diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index f9679f1b5..c7abfb8b5 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -1797,6 +1797,42 @@ internal static string UseIdenticalParametersDSCName { } } + /// + /// Looks up a localized string similar to Use the *ToExport module manifest fields.. + /// + internal static string UseManifestExportFieldsCommonName { + get { + return ResourceManager.GetString("UseManifestExportFieldsCommonName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specify entries for AliasToExport, CmdletsToExport, FunctionsToExport and do not use wildcards and $null in these entries. During module auto-discovery, if any of these entries are missing or $null, PowerShell do some potentially expensive work to analyze the rest of the module. + /// + internal static string UseManifestExportFieldsDescription { + get { + return ResourceManager.GetString("UseManifestExportFieldsDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not use wildcards and $null in these entry. Explicitly specify a list for {0}. . + /// + internal static string UseManifestExportFieldsError { + get { + return ResourceManager.GetString("UseManifestExportFieldsError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to UseManifestExportFields. + /// + internal static string UseManifestExportFieldsName { + get { + return ResourceManager.GetString("UseManifestExportFieldsName", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use OutputType Correctly. /// diff --git a/Rules/Strings.resx b/Rules/Strings.resx index 0b2fad980..d7a045d3e 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -798,4 +798,16 @@ AvoidNullOrEmptyHelpMessageAttribute + + Use the *ToExport module manifest fields. + + + Specify entries for AliasToExport, CmdletsToExport, FunctionsToExport and do not use wildcards and $null in these entries. During module auto-discovery, if any of these entries are missing or $null, PowerShell do some potentially expensive work to analyze the rest of the module + + + Do not use wildcards and $null in these entry. Explicitly specify a list for {0}. + + + UseManifestExportFields + \ No newline at end of file diff --git a/Rules/UseManifestExportFields.cs b/Rules/UseManifestExportFields.cs new file mode 100644 index 000000000..9a9f187cf --- /dev/null +++ b/Rules/UseManifestExportFields.cs @@ -0,0 +1,159 @@ +// +// Copyright (c) Microsoft Corporation. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Management.Automation.Language; +using System.Management.Automation; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; +using System.ComponentModel.Composition; +using System.Globalization; + +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules +{ + /// + /// UseManifestExportFields: Run Test Module Manifest to check that no deprecated fields are being used. + /// + [Export(typeof(IScriptRule))] + public class UseManifestExportFields : IScriptRule + { + /// + /// AnalyzeScript: Run Test Module Manifest to check that no deprecated fields are being used. + /// + /// The script's ast + /// The script's file name + /// A List of diagnostic results of this rule + public IEnumerable AnalyzeScript(Ast ast, string fileName) + { + if (ast == null) + { + throw new ArgumentNullException(Strings.NullAstErrorMessage); + } + + if (!fileName.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase)) + { + yield break; + } + + String[] manifestFields = {"FunctionsToExport", "CmdletsToExport", "VariablesToExport", "AliasesToExport"}; + var hashtableAst = ast.Find(x => x is HashtableAst, false) as HashtableAst; + + if (hashtableAst == null) + { + //Should we emit a warning if the parser cannot find a hashtable? + yield break; + } + + foreach(String field in manifestFields) + { + IScriptExtent extent; + if (!HasAcceptableExportField(field, hashtableAst, out extent) && extent != null) + { + yield return new DiagnosticRecord(GetError(field), extent, GetName(), DiagnosticSeverity.Warning, fileName); + } + } + + } + + private bool HasAcceptableExportField(string key, HashtableAst hast, out IScriptExtent extent) + { + extent = null; + foreach (var pair in hast.KeyValuePairs) + { + if (key.Equals(pair.Item1.Extent.Text.Trim(), StringComparison.OrdinalIgnoreCase)) + { + var arrayAst = pair.Item2.Find(x => x is ArrayLiteralAst, true) as ArrayLiteralAst; + if (arrayAst == null) + { + extent = GetScriptExtent(pair); + return false; + } + else + { + return true; + } + } + } + return true; + } + + + private ScriptExtent GetScriptExtent(Tuple pair) + { + return new ScriptExtent(new ScriptPosition(pair.Item1.Extent.StartScriptPosition.File, + pair.Item1.Extent.StartScriptPosition.LineNumber, + pair.Item1.Extent.StartScriptPosition.Offset, + pair.Item1.Extent.StartScriptPosition.Line), + new ScriptPosition(pair.Item2.Extent.EndScriptPosition.File, + pair.Item2.Extent.EndScriptPosition.LineNumber, + pair.Item2.Extent.EndScriptPosition.Offset, + pair.Item2.Extent.EndScriptPosition.Line)); + } + + public string GetError(string field) + { + return string.Format(CultureInfo.CurrentCulture, Strings.UseManifestExportFieldsError, field); + } + + /// + /// GetName: Retrieves the name of this rule. + /// + /// The name of this rule + public string GetName() + { + return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.UseManifestExportFieldsName); + } + + /// + /// GetCommonName: Retrieves the common name of this rule. + /// + /// The common name of this rule + public string GetCommonName() + { + return String.Format(CultureInfo.CurrentCulture, Strings.UseManifestExportFieldsCommonName); + } + + /// + /// GetDescription: Retrieves the description of this rule. + /// + /// The description of this rule + public string GetDescription() + { + return String.Format(CultureInfo.CurrentCulture, Strings.UseManifestExportFieldsDescription); + } + + /// + /// Method: Retrieves the type of the rule: builtin, managed or module. + /// + public SourceType GetSourceType() + { + return SourceType.Builtin; + } + + /// + /// GetSeverity: Retrieves the severity of the rule: error, warning of information. + /// + /// + public RuleSeverity GetSeverity() + { + return RuleSeverity.Warning; + } + + /// + /// Method: Retrieves the module/assembly name the rule is from. + /// + public string GetSourceName() + { + return string.Format(CultureInfo.CurrentCulture, Strings.SourceName); + } + } +} diff --git a/Tests/Rules/UseManifestExportFields.ps1 b/Tests/Rules/UseManifestExportFields.ps1 new file mode 100644 index 000000000..e69de29bb diff --git a/Tests/Rules/UseManifestExportFields.tests.ps1 b/Tests/Rules/UseManifestExportFields.tests.ps1 new file mode 100644 index 000000000..e69de29bb From 961dd5e933e9ba88860ada3fd5f2fc3e2188090a Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Thu, 11 Feb 2016 20:35:33 -0800 Subject: [PATCH 2/7] Adds tests and documetation for the UseToExportFieldsInManifest --- .../UseToExportFieldsInManifest.md | 28 ++++++ Rules/ScriptAnalyzerBuiltinRules.csproj | 2 +- Rules/Strings.Designer.cs | 72 +++++++-------- Rules/Strings.resx | 14 +-- ...elds.cs => UseToExportFieldsInManifest.cs} | 86 +++++++++++++----- Tests/Engine/GetScriptAnalyzerRule.tests.ps1 | 2 +- .../ManifestBadAliasesWildcard.psd1 | Bin 0 -> 6598 bytes Tests/Rules/TestManifest/ManifestBadAll.psd1 | Bin 0 -> 6566 bytes .../ManifestBadCmdletsWildcard.psd1 | Bin 0 -> 6598 bytes .../ManifestBadFunctionsNull.psd1 | Bin 0 -> 6602 bytes .../ManifestBadFunctionsWildcard.psd1 | Bin 0 -> 6598 bytes .../ManifestBadVariablesWildcard.psd1 | Bin 0 -> 6598 bytes Tests/Rules/TestManifest/ManifestGood.psd1 | Bin 0 -> 6566 bytes Tests/Rules/TestManifest/ManifestInvalid.psd1 | Bin 0 -> 6576 bytes Tests/Rules/UseManifestExportFields.ps1 | 0 Tests/Rules/UseManifestExportFields.tests.ps1 | 0 .../UseToExportFieldsInManifest.tests.ps1 | 78 ++++++++++++++++ 17 files changed, 212 insertions(+), 70 deletions(-) create mode 100644 RuleDocumentation/UseToExportFieldsInManifest.md rename Rules/{UseManifestExportFields.cs => UseToExportFieldsInManifest.cs} (60%) create mode 100644 Tests/Rules/TestManifest/ManifestBadAliasesWildcard.psd1 create mode 100644 Tests/Rules/TestManifest/ManifestBadAll.psd1 create mode 100644 Tests/Rules/TestManifest/ManifestBadCmdletsWildcard.psd1 create mode 100644 Tests/Rules/TestManifest/ManifestBadFunctionsNull.psd1 create mode 100644 Tests/Rules/TestManifest/ManifestBadFunctionsWildcard.psd1 create mode 100644 Tests/Rules/TestManifest/ManifestBadVariablesWildcard.psd1 create mode 100644 Tests/Rules/TestManifest/ManifestGood.psd1 create mode 100644 Tests/Rules/TestManifest/ManifestInvalid.psd1 delete mode 100644 Tests/Rules/UseManifestExportFields.ps1 delete mode 100644 Tests/Rules/UseManifestExportFields.tests.ps1 create mode 100644 Tests/Rules/UseToExportFieldsInManifest.tests.ps1 diff --git a/RuleDocumentation/UseToExportFieldsInManifest.md b/RuleDocumentation/UseToExportFieldsInManifest.md new file mode 100644 index 000000000..11ae40353 --- /dev/null +++ b/RuleDocumentation/UseToExportFieldsInManifest.md @@ -0,0 +1,28 @@ +#UseToExportFieldsInManifest +**Severity Level: Warning** + + +##Description + +In a module manifest, AliasesToExport, CmdletsToExport, FunctionsToExport and VariablesToExport fields should not use wildcards or $null in their entries. During module auto-discovery, if any of these entries are missing or $null or wildcard, PowerShell does some potentially expensive work to analyze the rest of the module. + +##How to Fix + +Please consider using an explicit list. + +##Example 1 + +Wrong: + FunctionsToExport = $null + +Correct: + FunctionToExport = @() + +##Example 2 +Suppose there are only two functions in your module, Get-Foo and Set-Foo that you want to export. Then, + +Wrong: + FunctionsToExport = '*' + +Correct: + FunctionToExport = @(Get-Foo, Set-Foo) \ No newline at end of file diff --git a/Rules/ScriptAnalyzerBuiltinRules.csproj b/Rules/ScriptAnalyzerBuiltinRules.csproj index 77f9cded2..9138a7e33 100644 --- a/Rules/ScriptAnalyzerBuiltinRules.csproj +++ b/Rules/ScriptAnalyzerBuiltinRules.csproj @@ -95,7 +95,7 @@ Strings.resx - + diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index c7abfb8b5..aae951eeb 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -1797,42 +1797,6 @@ internal static string UseIdenticalParametersDSCName { } } - /// - /// Looks up a localized string similar to Use the *ToExport module manifest fields.. - /// - internal static string UseManifestExportFieldsCommonName { - get { - return ResourceManager.GetString("UseManifestExportFieldsCommonName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Specify entries for AliasToExport, CmdletsToExport, FunctionsToExport and do not use wildcards and $null in these entries. During module auto-discovery, if any of these entries are missing or $null, PowerShell do some potentially expensive work to analyze the rest of the module. - /// - internal static string UseManifestExportFieldsDescription { - get { - return ResourceManager.GetString("UseManifestExportFieldsDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Do not use wildcards and $null in these entry. Explicitly specify a list for {0}. . - /// - internal static string UseManifestExportFieldsError { - get { - return ResourceManager.GetString("UseManifestExportFieldsError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UseManifestExportFields. - /// - internal static string UseManifestExportFieldsName { - get { - return ResourceManager.GetString("UseManifestExportFieldsName", resourceCulture); - } - } - /// /// Looks up a localized string similar to Use OutputType Correctly. /// @@ -2031,6 +1995,42 @@ internal static string UseStandardDSCFunctionsInResourceName { } } + /// + /// Looks up a localized string similar to Use the *ToExport module manifest fields.. + /// + internal static string UseToExportFieldsInManifestCommonName { + get { + return ResourceManager.GetString("UseToExportFieldsInManifestCommonName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to In a module manifest, AliasesToExport, CmdletsToExport, FunctionsToExport and VariablesToExport fields should not use wildcards or $null in their entries. During module auto-discovery, if any of these entries are missing or $null or wildcard, PowerShell does some potentially expensive work to analyze the rest of the module.. + /// + internal static string UseToExportFieldsInManifestDescription { + get { + return ResourceManager.GetString("UseToExportFieldsInManifestDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Do not use wildcard or $null in this field. Explicitly specify a list for {0}. . + /// + internal static string UseToExportFieldsInManifestError { + get { + return ResourceManager.GetString("UseToExportFieldsInManifestError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to UseToExportFieldsInManifest. + /// + internal static string UseToExportFieldsInManifestName { + get { + return ResourceManager.GetString("UseToExportFieldsInManifestName", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use Type At Variable Assignment. /// diff --git a/Rules/Strings.resx b/Rules/Strings.resx index d7a045d3e..233f99a5e 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -798,16 +798,16 @@ AvoidNullOrEmptyHelpMessageAttribute - + Use the *ToExport module manifest fields. - - Specify entries for AliasToExport, CmdletsToExport, FunctionsToExport and do not use wildcards and $null in these entries. During module auto-discovery, if any of these entries are missing or $null, PowerShell do some potentially expensive work to analyze the rest of the module + + In a module manifest, AliasesToExport, CmdletsToExport, FunctionsToExport and VariablesToExport fields should not use wildcards or $null in their entries. During module auto-discovery, if any of these entries are missing or $null or wildcard, PowerShell does some potentially expensive work to analyze the rest of the module. - - Do not use wildcards and $null in these entry. Explicitly specify a list for {0}. + + Do not use wildcard or $null in this field. Explicitly specify a list for {0}. - - UseManifestExportFields + + UseToExportFieldsInManifest \ No newline at end of file diff --git a/Rules/UseManifestExportFields.cs b/Rules/UseToExportFieldsInManifest.cs similarity index 60% rename from Rules/UseManifestExportFields.cs rename to Rules/UseToExportFieldsInManifest.cs index 9a9f187cf..0cedcca0f 100644 --- a/Rules/UseManifestExportFields.cs +++ b/Rules/UseToExportFieldsInManifest.cs @@ -17,17 +17,20 @@ using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.ComponentModel.Composition; using System.Globalization; +using System.Text.RegularExpressions; namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules { /// - /// UseManifestExportFields: Run Test Module Manifest to check that no deprecated fields are being used. + /// UseToExportFieldsInManifest: Checks if AliasToExport, CmdletsToExport, FunctionsToExport and VariablesToExport + /// fields do not use wildcards and $null in their entries. /// [Export(typeof(IScriptRule))] - public class UseManifestExportFields : IScriptRule + public class UseToExportFieldsInManifest : IScriptRule { /// - /// AnalyzeScript: Run Test Module Manifest to check that no deprecated fields are being used. + /// AnalyzeScript: Analyzes the AST to check if AliasToExport, CmdletsToExport, FunctionsToExport + /// and VariablesToExport fields do not use wildcards and $null in their entries. /// /// The script's ast /// The script's file name @@ -44,19 +47,23 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) 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) - { - //Should we emit a warning if the parser cannot find a hashtable? + { yield break; } foreach(String field in manifestFields) { IScriptExtent extent; - if (!HasAcceptableExportField(field, hashtableAst, out extent) && extent != null) + if (!HasAcceptableExportField(field, hashtableAst, ast.Extent.Text, out extent) && extent != null) { yield return new DiagnosticRecord(GetError(field), extent, GetName(), DiagnosticSeverity.Warning, fileName); } @@ -64,17 +71,38 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) } - private bool HasAcceptableExportField(string key, HashtableAst hast, out IScriptExtent extent) + /// + /// Checks if the manifest file is valid. + /// + /// + /// + /// A boolean value indicating the validity of the manifest file. + private bool IsValidManifest(Ast ast, string fileName) + { + var missingManifestRule = new MissingModuleManifestField(); + return !missingManifestRule.AnalyzeScript(ast, fileName).GetEnumerator().MoveNext(); + + } + + /// + /// Checks if the *ToExport fields are explicitly set to lists, @(...) + /// + /// + /// + /// + /// + /// A boolean value indicating if the the ToExport fields are explicitly set to lists or not. + private bool HasAcceptableExportField(string key, HashtableAst hast, string scriptText, out IScriptExtent extent) { extent = null; foreach (var pair in hast.KeyValuePairs) { if (key.Equals(pair.Item1.Extent.Text.Trim(), StringComparison.OrdinalIgnoreCase)) { - var arrayAst = pair.Item2.Find(x => x is ArrayLiteralAst, true) as ArrayLiteralAst; + var arrayAst = pair.Item2.Find(x => x is ArrayLiteralAst || x is ArrayExpressionAst, true); if (arrayAst == null) { - extent = GetScriptExtent(pair); + extent = GetScriptExtent(pair, scriptText); return false; } else @@ -85,23 +113,31 @@ private bool HasAcceptableExportField(string key, HashtableAst hast, out IScript } return true; } - - - private ScriptExtent GetScriptExtent(Tuple pair) + + /// + /// Gets the script extent. + /// + /// + /// + /// + private ScriptExtent GetScriptExtent(Tuple pair, string scriptText) { - return new ScriptExtent(new ScriptPosition(pair.Item1.Extent.StartScriptPosition.File, - pair.Item1.Extent.StartScriptPosition.LineNumber, - pair.Item1.Extent.StartScriptPosition.Offset, - pair.Item1.Extent.StartScriptPosition.Line), - new ScriptPosition(pair.Item2.Extent.EndScriptPosition.File, - pair.Item2.Extent.EndScriptPosition.LineNumber, - pair.Item2.Extent.EndScriptPosition.Offset, - pair.Item2.Extent.EndScriptPosition.Line)); + string[] scriptLines = Regex.Split(scriptText, "\r\n|\r|\n"); + return new ScriptExtent(new ScriptPosition(pair.Item1.Extent.File, + pair.Item1.Extent.StartLineNumber, + pair.Item1.Extent.StartColumnNumber, + scriptLines[pair.Item1.Extent.StartLineNumber - 1]), //line number begins with 1 + new ScriptPosition(pair.Item2.Extent.File, + pair.Item2.Extent.EndLineNumber, + pair.Item2.Extent.EndColumnNumber, + scriptLines[pair.Item2.Extent.EndLineNumber - 1])); //line number begins with 1 + + } public string GetError(string field) { - return string.Format(CultureInfo.CurrentCulture, Strings.UseManifestExportFieldsError, field); + return string.Format(CultureInfo.CurrentCulture, Strings.UseToExportFieldsInManifestError, field); } /// @@ -110,7 +146,7 @@ public string GetError(string field) /// The name of this rule public string GetName() { - return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.UseManifestExportFieldsName); + return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.UseToExportFieldsInManifestName); } /// @@ -119,7 +155,7 @@ public string GetName() /// The common name of this rule public string GetCommonName() { - return String.Format(CultureInfo.CurrentCulture, Strings.UseManifestExportFieldsCommonName); + return String.Format(CultureInfo.CurrentCulture, Strings.UseToExportFieldsInManifestCommonName); } /// @@ -128,7 +164,7 @@ public string GetCommonName() /// The description of this rule public string GetDescription() { - return String.Format(CultureInfo.CurrentCulture, Strings.UseManifestExportFieldsDescription); + return String.Format(CultureInfo.CurrentCulture, Strings.UseToExportFieldsInManifestDescription); } /// diff --git a/Tests/Engine/GetScriptAnalyzerRule.tests.ps1 b/Tests/Engine/GetScriptAnalyzerRule.tests.ps1 index e9bb657f5..ef2cefc31 100644 --- a/Tests/Engine/GetScriptAnalyzerRule.tests.ps1 +++ b/Tests/Engine/GetScriptAnalyzerRule.tests.ps1 @@ -56,7 +56,7 @@ Describe "Test Name parameters" { It "Get Rules with no parameters supplied" { $defaultRules = Get-ScriptAnalyzerRule - $defaultRules.Count | Should be 40 + $defaultRules.Count | Should be 41 } } diff --git a/Tests/Rules/TestManifest/ManifestBadAliasesWildcard.psd1 b/Tests/Rules/TestManifest/ManifestBadAliasesWildcard.psd1 new file mode 100644 index 0000000000000000000000000000000000000000..bc7994035c8ff78b864da7472221f4f2173f901d GIT binary patch literal 6598 zcmc(jYi}D>5QfiZB>sb?d`OCDF3^;VgjAC>ZKSkBxfH(oj%j?saY!rFzYe_58Bfk0 z?{09?6lB@E>pf@Yo%_uB`>(a|dH5`>g+_Q4CZQXy!bRxmX%t#v9EPE)MyE{87){a3D(K(9zDJ_Kij3XWd=sO-Ju%x~?xcJO3BrG}d>f_j6sl;?)lC z!}q$r)g9cyX6tX;1wVFu6}G~TK3lrm*7a#&FZ-W`KjS}6>r~igLOG4{jxv3i>}|ar zNBr+gn&?OQ&|OQ?UTSwo|0G$i^)wH^>OH?htte}GbAhlCPPBibmAO@Z(4O^$<{b5# zsIyB^MDB*ZSF&C^{@c)h{Uwgcdd98DVVRck+Xy2IkluLw5jD_V zkz-EdwP=mBzY&c0xhTKYcM`7kTjo9$Uwn8V35i-_*L)Bv*dax7y(Pe~Ov(6fNXL&v z!y_t>%}u>&%JLVwCKP!3Ox*F6SwI_Ic~p*lKfX`#=iZ#`gAV%9!%|jhi4#$UuBW0s z5q0x8&mMWS<^Hy&U3Gj-+8pC6(Z;_=LNU@_B$-L7YOLhDu_VP(Xy7>BvEx*>AwJL& zHW|c~$cL7Qo?*OORfa0c)sdXblYA@J@@mM5Agc@yhx+A$WlRBb=(R+;(VJc?DV#k6U z$ofp0f0T7|&1kQlu(XA6C>poRj5Ktv*3Wzj+|`Jx(dWIW_m)Vop@^SKmYwKH$%go( z)eyQcY{;)$i>{lBL;YEgnxOJLw8c{YTEu^;>@-pR!#6yR>a2FAT0V_(RLlHM_Pkg~ zPRD8fn%nL=W);&x^5fNf&H3L2^Fj1)CMF{2|G{0va2M>$u~d#xn`f9SY!bBiAXode zh*#zvMDZ;>C>CWJLN~VGyz@l+mZzMpZ00F@j%Nj`6FCcd%TiRwRCy-#oYV{NZt@)# z#8c_;h#sHRlDr@&(*o)9j7~L*JyGt)NSwzwG>zXKK~85??r-F!WaL)Nik=gRq`M<1 z(^?e;GXfZx2rMh#4?#|+n)`bAqBLuqs(Lc7G%Xl9g(d zxKyK{Y7AAUY=&(pa=2?r=DDf67;PTzGY3ap_!p>8J|(uH)j*U!$NoO=OJ^yaK|eWbG(PR*+4O{>qjP7^A# zKspm^hzg$^d)+Euy`~*!LN$n1X~|os!tZ(CtX7>@U9ZFs8nG#Ps=K6{+^%Sem}Bmz zv#}E=bWRrvSE^P+SS*%UW1XOB!ZG_b$6oeGU*}iWH}ySN)F0`Zq=PH`$t#?G^6V$5 zb`;&La6$7f_{ix*sZ}f{Jo=8j@2Tz++!}txD)rRx zxZ?S)@A{zs+4)>DlkY7f=W$uiDfkoLy4}UhnzBGW9T2>XnwaXGb{zfLeE;wUQ?+k< z{aM~k+^~09Za8r=*AFdK=L>1u&iD9ds2(eMsh=F+FWqR7Vjp9@=D+oG>QF2Qg`iJIXrxa5L@x1I{xf AVE_OC literal 0 HcmV?d00001 diff --git a/Tests/Rules/TestManifest/ManifestBadAll.psd1 b/Tests/Rules/TestManifest/ManifestBadAll.psd1 new file mode 100644 index 0000000000000000000000000000000000000000..eeed4260d2f45cf0713ec320320fe1658e7fffad GIT binary patch literal 6566 zcmc(jX-{ND6o%`wiT|M!KEN0OF))gW$#iI7NSKi@fPTHb+77+gH1-%Xe_ef_I$hMQ zyI`}$bZ+0Js?K@OeyaZYd(D06zHn=<;oiB4>$+Qa?Rt6}xy+5-(Dn6p**WaU+31c{*C*~e+jfcxQ=)X-PFr7@+gD6CRnEC-YW+v8Szl@{puX}tyAeg?Zs@udvbO))(6@ep zW5jIS@*Ik^jNgvilK#wIf$NEDiSDhWFiXwFiFbVBcinr^1v(naj@Wgsr@sG^mp)>F z?y2~$BFS~AP4)Zeqtt}qAnz83s?)g>w1>{Yk z1A|%gKK+0xoo%@C-k56TKvYNaA3PrEjmIBe1KkxlrZnD*)=29c&Ujyn@<&}0x6pr? z`&4}K;ejM1Dv3?=L8xGd7^(G^07H?I@!ybFzY>iQQE6;$X{0I3U+W%G;OR4Q$5&PDfX5hkMS40Iobyu^u335B!!J<;zU%T>#1l@MBO|tWREo3Qh!_1sye z77LBelqVkRQ7x^Xi*8TKdWHZl7gtX)qUW{XFl$Uddr0y?!cX@ zMv{ph=Y6(*6-my#zB|HURh=G(4xl6Kn| z-hBe*GLmL3j_SCH`Hzf*_Nfi@Ayjgn=o_foR55%gbYf&atMS2R1Ld!Y=97ahkCA)v z&9S$V)f%!g`4O$z`aEr@4*W>$h$6<&Yn%&HJ}c%s7UV$IH`4s8q?>Dcd-a5+ErdhS zxL;~M??huKe+Q49)i6XOXV0P+u<+dG;)N+2e~@z1{qB|i2Nu$C>CWmgl=rT zdFQ#-6?Zw?$)2a|IUy@hodTYs6xA_xHz$_S~nte1`?`R60DOC+yUsydWsk z0_kBw$8zvjg--WCl)Lg_YU@)Fq;yv0ekE&?ku#qaLrx@;9=)id3s{l`Gb%_cd~eMH7{k+M16hw8j3i zqwdbSBX$YZXA7yvf-oN^ky~}N#PJkOKEFL4$&q9w1|pYg6jY6&>Xhwa8;TsBGRZQR zmP{A)W#K*4yS&bxI|H%Hy~t&T%ZbX4ObSKp9 zXhXV?uGZ#vxv3ZMM*d)C&__Ir;nb{pziIV3)oDa!7KmqJ4N>8jVy~MOX0K_*g-{K= zRWf<&R1$`~Z&s_$t3j{C4;ryu@>EYzH>q9G5;4cVpU%ckoX|O4C|s#p4PmiZVvco! zrip+#+*9mjj|}s|o%u~&mx}sR-J^7Hg+F;^yp$)Lkk(00T?v%K9S+1uq;u(I2+r(7r{QD=NDqr`c_LSX!+eA2 z0)747p&sSQlIBD3k<*D%t61#t=!3{QvG1ww6M{9u9jnw+!{hSjyJ6L5eP`vNWG3Gm z*EBB6IR$^>s#~4UtT7AJ(*eO-uZgM7X~*i5$aeVt$sML@-v&k1>aeybH*-0e>$^eSCr%Z^WARuUt`Q1VE_OC literal 0 HcmV?d00001 diff --git a/Tests/Rules/TestManifest/ManifestBadCmdletsWildcard.psd1 b/Tests/Rules/TestManifest/ManifestBadCmdletsWildcard.psd1 new file mode 100644 index 0000000000000000000000000000000000000000..3782c9e7bb21694edf01bec4ae1eb955eb4037e9 GIT binary patch literal 6598 zcmc(jYi}D>5QfiZB>sb?d`OCDF3^;Vgwzl>ZKSkBX;S#=JErjk$04my|2ptKXFNH3 zyt~1<6lB@E>pe5`&VA1L`>(a|dH5`>g+_Q4CZQWH!+GfGX%t#v9EPE!Qn7{;20}I1s0C=!kMCy0Litth;l)>FE7T*Y$;Dm;XYN#`@0mey(d*vfAN8 z_+Hm{x7#xmSJ=&HBP}j{0r1 z*@ZZwcSGMxd9NM+ZRo%LlEmaalU9_l%**8MgiYDc{N+*)LsNV&rGnk&B;I5pdTYFRh5<`kyY4wD&7-u zw}|uVk!M>TZ)>8elWW@M7+;Au@jViXk!aCmCatQmQt!sn6i;D+<9G+f8~KL(z)JXJ z5LcofRw8?b@orTesyJ6?a;{H`t=!71At!^ZGdvvXmkXW=*?;ASPpQo+zE~}`SVfLt zd3;7TZ-&?5c{tVGf!cm|msww)p`1bT$=K65@P=Sd1j^vH>u?}`FEy#*X zT?g^~Eap8u#A@M2*bhhQkyN6)MW4~HqRDZzcSks^t1|;J!E}V3+C$fw3m-TdVMA6! zi(dFrPrrn3)seW{6Y{R^pWFj;8OdHPj_SNg^^b~#_2~`FA#`%?=%#dSx)?F!lNhzk zdc6B=p!zk@dTOxMF={WdIgVbkUPD!;K4LYaFY<=#AihZxQNrl6#)&W$t736tK@W6& zCd)s{rn$Z7ub!~9g>WbyH|vZxOs+Q1dOS&x>W^E|Z2()?P)f1&C$QU4=0Jdf(UcB)=Jje1n;{9gV%Uue$6 zY4Mu-?kZLlvq6gE)nd)%-v;wRjBj>KWYGVEyNKa7*q3vuoTIj%VP9c8LCX(nweJ>* z%6x(-zGVi*qwI#TjnP|lo``OFm$Q}adCHOFRe|nA&4S;u7S%ab-jjMw>V@~$^$rgb zsZ4lekMGoyz91;`0_pRNPc@2taqh-UoX0#gi{BnW&Zk=XdiY{#&VH*Xr6RXtRrH!j zCfy!EnO8N6s@hCNW}kdN1ne~upH-zdHG%I-W9lVy6usN((%s`>P5heT*3ne5%ym^< zM={o@A(l;6vFXk!_Q*B%mmN)aHXX4`s6JarRGu@%JW0~3<21d`(G=_3-H{wfSGrM> zQjLPHF;t(jJ#0gn!(B@{&t(;}1#_7{d%r8%?4c_VyWG=4WoX6yC*C5OY?@{IX$d)9 z2(-tFx#l%?=B^8IU=7m=eLMM(DWoe}KRbhS9tAuxo3qmGksI;dHBp=j)gXGMrD&ZBzt?^9T6JA@yOKXx#CFM3-6h-Pe#J`U9Q%GI z8#{5r=1ieTrE4{W#d3)?HVK*~9J60@?B$5`b$(_2p1x>)5>uVij$=Gq>>plZs*Y{% zKg-nQ4YbR8BZ-r_erTyXpUc{IzQ;d9%~&Z){p5gn=|+zf=NOwc|81V5?)R3Af`=`qHLp&*Sp<+0JP5} AVE_OC literal 0 HcmV?d00001 diff --git a/Tests/Rules/TestManifest/ManifestBadFunctionsNull.psd1 b/Tests/Rules/TestManifest/ManifestBadFunctionsNull.psd1 new file mode 100644 index 0000000000000000000000000000000000000000..c8fc4b534dcedd49d5a26ec378ad500c648760f5 GIT binary patch literal 6602 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 BBw+vm literal 0 HcmV?d00001 diff --git a/Tests/Rules/TestManifest/ManifestGood.psd1 b/Tests/Rules/TestManifest/ManifestGood.psd1 new file mode 100644 index 0000000000000000000000000000000000000000..cd360aed1b952a6d2be187421d2afb3e71af5c14 GIT binary patch literal 6566 zcmc(j+fG|Y6o%)zQr}@qTtK7(B_W}$R4EpQ1c{`GK-yd5Q{CW0jfqoLdG)scH_OA! zc<)WkA+0R$)6A^(uk)Ju`>!?kwfoAgxx&41W7l!l?$UMjHgqjFas$`X+m)N?OHbod zKa$Q{*SdV>E6roZ`hs$x|4Nib`cE}~rh7-UF5DmP2i-sD3F`2+`M0eaiw>)vx^1_k z-?pB<)%``Xmh~^)pZ-gr_0F}$W8fxUo}ot>`CsX0G)gwPt;xxq$k;*V&aQB6p$xwUAx-uZ6z# zmN-Vt#x3QrOw0J~xGm|=>=n2kxu)n|OA52pOq_VfCw|wxD_x+Yf$WG~XL{=SFRAnq z3v^$(b7ypoZ&y-0ce~Qzo_nV2rfBWSj!*T(*N!M|y6<)UPP*RImtBo^bbYQXzde0_ zwuHPPbYL)x-liWg<+BY}>W#Tp_C<9l|H0#t-gtcU8tAOZF{klXw1!$=IOBaT${+L} zySc7q?i2CFhx?L{s3bPc2cd!;Vx-nv0u0NPjQ@r-`b0EBMCGx$rICg#f2n&!fu~Q! z9bcI`w9%1A<=C5kJjP$}=4c;u(DNSFmJ~ML5+|YxT~9=NEb8WQA$#P}miyb9R@Lz} zX>*KkL>vDe3dK-skz^{VsXPDuuDlnR9x%c_v2F^_e8zr!VS0Y zj#VScMECPPTfd4VCtlxe;jpSsk3$F07Itz^S5GtH14rRDq&1}Ix^MLMiz?F#|I`rj zj-GcPfVqsMQH!HGZeso;BcXk21APdUoG1DQsy0;&9}1lqna^r`uvuUEYpnU?V9R6V zUVL-ptz@-^tW17HYqmb64b{FMi5*eI7u0_M?rKEU=<{AQ^p;4lp@^SImL2a&(T4b>)eyQcY{)fOtL^PXe>zp= zg$|YHu`L$+*A)MiveQ`g58nuJRA;p_)$)m#qgv)qvghSOaym})*W7kwvm+vc{vX^ahPz;2j-_&plI`#pavC|p;)7frc7u%O9Yi`x4~j+E4WS!b zZ{B&Tb<4Y)?QG9e_MDIvs7?XTvJ}-Zb?B=|>>j~w@^;;)n|y}_@l-lIq9^RsqP!p| z(*o&1MyDFZz9@I(!_?M?Ajs*g%Kg2pNk(q@tQc}4k#u(*lxeMsf;|El*bzKL2RWTp zxi{5;?@MCxC4CgN+w#)={b5b?nxfWLSF%iXRb0p3*T^9jO;lEDYZ^^xi~VI=-JNwu z>=LTa7IKdTVHziqTeY9Y@f=N>-|mm(P_hyOkxMlSs>VQd%J#5@B8R7zWSL1zrVIMA z@E+=2sD*STnb+f|kHLW-ks=l{MOWrz> zgdy*n)vEJq&@1tSMr@Zn(Nok-ZdbHK%(3sMv#}E=bWRrvSE^PaEEY@5u};u55io~) zj=k)WVP3d1zoGxRqW)O-C>>nkPhJ@<CtUI?M>WvWhx=!M7bH_fAV)@#l>yfE)!IN1Tx!(v;W$kpI5-ypg`U%z*# zM|rfMc^7=-bfVNM7CSupAhJ&Ed#d|{V2yCcD)rRxxb%ECtop3)tUQp+Io?0t>~{YF<)R*n literal 0 HcmV?d00001 diff --git a/Tests/Rules/TestManifest/ManifestInvalid.psd1 b/Tests/Rules/TestManifest/ManifestInvalid.psd1 new file mode 100644 index 0000000000000000000000000000000000000000..6a1d32571683db3af8ebf86afe96960860bda669 GIT binary patch literal 6576 zcmc(jYfoE85QgV-rTzy?e1KF1LP%3msZtOQ2@**WfwW(ZFVu~VU1Q=@RsMS0_u28X zXMN70=8{&H&uw>S-nq~2-+!&x*Y=gISi|1f$U1gq1MBK-Xf3<2zV-BWX;XdaX?zk! zit|>rE}wazbszO@pw%5)ozst8UWwj?{u9lZ>fRB>GyB7S(EWp+-~j@kf77aw=&-m`&H|>_BErd%Z)k0h8=7DNHcTY|EM*qbInE6?}N@RMG?6h`d_@TI7M zga^8>?9`mD^WC;>`%YTiwjFz}>$)iH1goyw)9~EVx97r!jn{SE3AWx6&nKGqShK#@ z`W*SL(1F3NdXtX8l+SitMUTvB?}_SAK7{Whz4Q1OG|*X+V@~6(XbrW#Vb1$hlt1V{ zvYD=B?ql)AkIy9`@kx}rA3_B?q?oO@1Q-@6IsY|z_9M}VQI*H%hDLT}`GM{U1>Qap zcYI}{EP%&sEMRYj@f3s6qmzBmK`(e%TT=MA=aXsB^;on=qV66SGe{nBxxcN5VjW+X zHrMYP(Z;`rLNU}@B$-I6YOFtp*;sw1ISo4sPpo(++YldU37edUnaGEhh@O6UT2_WC z%GHsa%aeR7*Ya}6i6E~54~P2ooMuAyU)kYHO0$eDmh&xU5hG|Gn-R?$_SRn7iJtZ~ z;@M)R(TVcJT|KI)^;6O9>RP7yaPp9W@cXv-4fXRXHjorV#g*>oVSEzeo~ZX+xMq8H zs5(g|x|{d;`eh_J3i@sfhgWvG9lC(Fuv68$dYTF!I2yJltszC%zSY|=sz}em)2@(r z^!)T5n9E3ZYjIS^P0D{{B(zU$pckQ%^F;4J)uxK!L$Mnp^LdSrHal1T8fiW`*z*{< z7vH=HR`OayRwh59HD6z(4b}57lDeXVG4>kA!c@$P#f}9zkoAQ$|0wI|c7wgT!g3~r zebKmCW~6C_ZiT%2C2&_Gsz#spqp`n4f;C0_Sh8#dPf9k#C%uNyg<(Uk(OT_bCwkP0 zDlc@XJP&QL)KwPoUn)C|RR8de7)NzhJ5enk2RW)`{v>-2=91HKy1(YOn}w|6I!J!J zoUcXxx52z0{F}WI5%mAyE@HS1_QhB#$0*qje<7!lBfQ@ySI2!Jqj?8W9Hj@vqU?y! zjjwm_Jl49!oz7;q?X_OORU~zf;C6Yt_DL8$-(f*Kl@5>SiMzEV zF9^!CK>9MH<2m@7g-&-*lsocaYU_Ov+T4Dc*weTkIX6b%fLQWL|?U8)0d5xXE>rxb$ zLw7>mPBx?q>1b_nmz#S5Z{!bVMt!8S7*5Wr_nTgybDbtsZh>?z)({nbIrh3)arTN< zoCwu&9@q|7YbLZRzp}k zmYCz6plc#xj`tjU*(2k;cxV2u{-=uiL*0{faD_j4WcdmI{mU9aJ#J_HJF|(#DP)`R0uY)GWI;Xu*pG3C9_YbZyRr@w5s#eFfi*hrU zlewO?RGkOXw(rRJ(pQg_yfmI1;4htEkzya?z2?95bL5?$<{1Q?aAHj?``H9<^*G47 q(Gw4pm17sVp4HlhPN#{Eh~K@lEajdw$~lNjQ(RGw_s`e6-G2b+$sM== literal 0 HcmV?d00001 diff --git a/Tests/Rules/UseManifestExportFields.ps1 b/Tests/Rules/UseManifestExportFields.ps1 deleted file mode 100644 index e69de29bb..000000000 diff --git a/Tests/Rules/UseManifestExportFields.tests.ps1 b/Tests/Rules/UseManifestExportFields.tests.ps1 deleted file mode 100644 index e69de29bb..000000000 diff --git a/Tests/Rules/UseToExportFieldsInManifest.tests.ps1 b/Tests/Rules/UseToExportFieldsInManifest.tests.ps1 new file mode 100644 index 000000000..adc389d31 --- /dev/null +++ b/Tests/Rules/UseToExportFieldsInManifest.tests.ps1 @@ -0,0 +1,78 @@ +Import-Module PSScriptAnalyzer +$testManifestPath = "TestManifest" +$testManifestBadFunctionsWildcardPath = "ManifestBadFunctionsWildcard.psd1" +$testManifestBadFunctionsNullPath = "ManifestBadFunctionsNull.psd1" +$testManifestBadCmdletsWildcardPath = "ManifestBadCmdletsWildcard.psd1" +$testManifestBadAliasesWildcardPath = "ManifestBadAliasesWildcard.psd1" +$testManifestBadVariablesWildcardPath = "ManifestBadVariablesWildcard.psd1" +$testManifestBadAllPath = "ManifestBadAll.psd1" +$testManifestGoodPath = "ManifestGood.psd1" +$testManifestInvalidPath = "ManifestInvalid.psd1" + +Function Run-PSScriptAnalyzerRule +{ + Param( + [Parameter(Mandatory)] + [String] $ManifestPath + ) + + Invoke-ScriptAnalyzer -Path (Resolve-Path (Join-Path $testManifestPath $ManifestPath))` + -IncludeRule PSUseToExportFieldsInManifest +} + +Describe "UseManifestExportFields" { + + Context "invalid manifest file" { + It "does not process the manifest" { + $results = Run-PSScriptAnalyzerRule $testManifestInvalidPath + $results | Should BeNullOrEmpty + } + } + + Context "manifest contains violations" { + + It "detects FunctionsToExport with wildcard" { + $results = Run-PSScriptAnalyzerRule $testManifestBadFunctionsWildcardPath + $results.Count | Should be 1 + $results[0].Extent.Text | Should be "FunctionsToExport = '*'" + } + + It "detects FunctionsToExport with null" { + $results = Run-PSScriptAnalyzerRule $testManifestBadFunctionsNullPath + $results.Count | Should be 1 + $results[0].Extent.Text | Should be 'FunctionsToExport = $null' + } + + It "detects CmdletsToExport with wildcard" { + $results = Run-PSScriptAnalyzerRule $testManifestBadCmdletsWildcardPath + $results.Count | Should be 1 + $results[0].Extent.Text | Should be "CmdletsToExport = '*'" + } + + It "detects AliasesToExport with wildcard" { + $results = Run-PSScriptAnalyzerRule $testManifestBadAliasesWildcardPath + $results.Count | Should be 1 + $results[0].Extent.Text | Should be "AliasesToExport = '*'" + } + + It "detects VariablesToExport with wildcard" { + $results = Run-PSScriptAnalyzerRule $testManifestBadVariablesWildcardPath + $results.Count | Should be 1 + $results[0].Extent.Text | Should be "VariablesToExport = '*'" + } + + It "detects all the *ToExport violations" { + $results = Run-PSScriptAnalyzerRule $testManifestBadAllPath + $results.Count | Should be 4 + } + } + + Context "manifest contains no violations" { + It "detects all the *ToExport fields explicitly stating lists" { + $results = Run-PSScriptAnalyzerRule $testManifestGoodPath + $results.Count | Should be 0 + } + } +} + + From 036f16408f264914f58321ff03be7d63a4c2e794 Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Thu, 11 Feb 2016 20:57:51 -0800 Subject: [PATCH 3/7] Handles a null argument in UseToExportFieldsInManifest rule. --- Rules/UseToExportFieldsInManifest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules/UseToExportFieldsInManifest.cs b/Rules/UseToExportFieldsInManifest.cs index 0cedcca0f..675d3ef49 100644 --- a/Rules/UseToExportFieldsInManifest.cs +++ b/Rules/UseToExportFieldsInManifest.cs @@ -42,7 +42,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) throw new ArgumentNullException(Strings.NullAstErrorMessage); } - if (!fileName.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase)) + if (fileName == null || !fileName.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase)) { yield break; } From 025c8de944d6a44e89fa7f93e7fcb3905e7dea83 Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Fri, 12 Feb 2016 02:16:03 -0800 Subject: [PATCH 4/7] Sets invocation directory in the UseToExportFieldsInManifest rule test file. --- Tests/Rules/UseToExportFieldsInManifest.tests.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/Rules/UseToExportFieldsInManifest.tests.ps1 b/Tests/Rules/UseToExportFieldsInManifest.tests.ps1 index adc389d31..bbe6cb5f8 100644 --- a/Tests/Rules/UseToExportFieldsInManifest.tests.ps1 +++ b/Tests/Rules/UseToExportFieldsInManifest.tests.ps1 @@ -1,5 +1,6 @@ Import-Module PSScriptAnalyzer -$testManifestPath = "TestManifest" +$directory = Split-Path -Parent $MyInvocation.MyCommand.Path +$testManifestPath = Join-Path $directory "TestManifest" $testManifestBadFunctionsWildcardPath = "ManifestBadFunctionsWildcard.psd1" $testManifestBadFunctionsNullPath = "ManifestBadFunctionsNull.psd1" $testManifestBadCmdletsWildcardPath = "ManifestBadCmdletsWildcard.psd1" From 5173062c8e215b978784ce989b0333b0b8574548 Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Fri, 12 Feb 2016 17:43:10 -0800 Subject: [PATCH 5/7] Modifies scriptextent for UseToExporFieldsInModuleManifest --- Rules/UseToExportFieldsInManifest.cs | 36 ++++++------------- .../UseToExportFieldsInManifest.tests.ps1 | 22 +++++++++--- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/Rules/UseToExportFieldsInManifest.cs b/Rules/UseToExportFieldsInManifest.cs index 675d3ef49..3e3d5709c 100644 --- a/Rules/UseToExportFieldsInManifest.cs +++ b/Rules/UseToExportFieldsInManifest.cs @@ -101,39 +101,25 @@ private bool HasAcceptableExportField(string key, HashtableAst hast, string scri { var arrayAst = pair.Item2.Find(x => x is ArrayLiteralAst || x is ArrayExpressionAst, true); if (arrayAst == null) - { - extent = GetScriptExtent(pair, scriptText); + { + extent = pair.Item2.Extent; return false; - } + } else { + var elementWithWildcard = arrayAst.Find(x => x is StringConstantExpressionAst + && x.Extent.Text.Contains("*"), false); + if (elementWithWildcard != null) + { + extent = elementWithWildcard.Extent; + return false; + } return true; } } } return true; - } - - /// - /// Gets the script extent. - /// - /// - /// - /// - private ScriptExtent GetScriptExtent(Tuple pair, string scriptText) - { - string[] scriptLines = Regex.Split(scriptText, "\r\n|\r|\n"); - return new ScriptExtent(new ScriptPosition(pair.Item1.Extent.File, - pair.Item1.Extent.StartLineNumber, - pair.Item1.Extent.StartColumnNumber, - scriptLines[pair.Item1.Extent.StartLineNumber - 1]), //line number begins with 1 - new ScriptPosition(pair.Item2.Extent.File, - pair.Item2.Extent.EndLineNumber, - pair.Item2.Extent.EndColumnNumber, - scriptLines[pair.Item2.Extent.EndLineNumber - 1])); //line number begins with 1 - - - } + } public string GetError(string field) { diff --git a/Tests/Rules/UseToExportFieldsInManifest.tests.ps1 b/Tests/Rules/UseToExportFieldsInManifest.tests.ps1 index bbe6cb5f8..9baeab55e 100644 --- a/Tests/Rules/UseToExportFieldsInManifest.tests.ps1 +++ b/Tests/Rules/UseToExportFieldsInManifest.tests.ps1 @@ -2,6 +2,7 @@ Import-Module PSScriptAnalyzer $directory = Split-Path -Parent $MyInvocation.MyCommand.Path $testManifestPath = Join-Path $directory "TestManifest" $testManifestBadFunctionsWildcardPath = "ManifestBadFunctionsWildcard.psd1" +$testManifestBadFunctionsWildcardInArrayPath = "ManifestBadFunctionsWildcardInArray.psd1" $testManifestBadFunctionsNullPath = "ManifestBadFunctionsNull.psd1" $testManifestBadCmdletsWildcardPath = "ManifestBadCmdletsWildcard.psd1" $testManifestBadAliasesWildcardPath = "ManifestBadAliasesWildcard.psd1" @@ -35,31 +36,42 @@ Describe "UseManifestExportFields" { It "detects FunctionsToExport with wildcard" { $results = Run-PSScriptAnalyzerRule $testManifestBadFunctionsWildcardPath $results.Count | Should be 1 - $results[0].Extent.Text | Should be "FunctionsToExport = '*'" + $results[0].Extent.Text | Should be "'*'" } It "detects FunctionsToExport with null" { $results = Run-PSScriptAnalyzerRule $testManifestBadFunctionsNullPath $results.Count | Should be 1 - $results[0].Extent.Text | Should be 'FunctionsToExport = $null' + $results[0].Extent.Text | Should be '$null' } + It "detects array element containing wildcard" { + $results = Run-PSScriptAnalyzerRule $testManifestBadFunctionsWildcardInArrayPath + $results.Count | Should be 3 + $results.Where({$_.Message -match "FunctionsToExport"}).Extent.Text | Should be "'Get-*'" + $results.Where({$_.Message -match "CmdletsToExport"}).Extent.Text | Should be "'Update-*'" + + # if more than two elements contain wildcard we can show only the first one as of now. + $results.Where({$_.Message -match "VariablesToExport"}).Extent.Text | Should be "'foo*'" + } + + It "detects CmdletsToExport with wildcard" { $results = Run-PSScriptAnalyzerRule $testManifestBadCmdletsWildcardPath $results.Count | Should be 1 - $results[0].Extent.Text | Should be "CmdletsToExport = '*'" + $results[0].Extent.Text | Should be "'*'" } It "detects AliasesToExport with wildcard" { $results = Run-PSScriptAnalyzerRule $testManifestBadAliasesWildcardPath $results.Count | Should be 1 - $results[0].Extent.Text | Should be "AliasesToExport = '*'" + $results[0].Extent.Text | Should be "'*'" } It "detects VariablesToExport with wildcard" { $results = Run-PSScriptAnalyzerRule $testManifestBadVariablesWildcardPath $results.Count | Should be 1 - $results[0].Extent.Text | Should be "VariablesToExport = '*'" + $results[0].Extent.Text | Should be "'*'" } It "detects all the *ToExport violations" { From 604f3f461860a6715d61ac148240aaa3db3be9ac Mon Sep 17 00:00:00 2001 From: Kapil Borle Date: Fri, 12 Feb 2016 17:48:38 -0800 Subject: [PATCH 6/7] Adds a test data file. --- .../ManifestBadFunctionsWildcardInArray.psd1 | Bin 0 -> 6712 bytes .../Rules/UseToExportFieldsInManifest.tests.ps1 | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 Tests/Rules/TestManifest/ManifestBadFunctionsWildcardInArray.psd1 diff --git a/Tests/Rules/TestManifest/ManifestBadFunctionsWildcardInArray.psd1 b/Tests/Rules/TestManifest/ManifestBadFunctionsWildcardInArray.psd1 new file mode 100644 index 0000000000000000000000000000000000000000..acb43a6e9fe9c912f3dd4532e5d8225f7438bd9d GIT binary patch literal 6712 zcmc(jYj0ac5Qg_N692(cJ~&0>22Ht0NHs~*MoLSRCWWuQ2GjV0@L5<3jqox|LN8o}i_q8GD73>k48uTgmtn3i1C7t( zNO4|MbotC5_1+5y;xrCjQ4U2n7LT9xbfJ;1#%H=W7c2Yv&$ZH6|Cz?;y7#nJC%g~e z>;6_xtPVDte;XC+LG@MG3Oo93>1kW{Ck0yQpN2o<7w7d_*k(dGjq;8%eVFVWjgBM! z_a#jXqI~GCEom=B-PM;Q%eCI-;a81w9co5d%bN>?jc_9RiDu?j`9U`BFiXv~67TrT@4EM8 zD|9rJ9kDBZFo<7@(kCqNeICw&@wIh#rNM2{e=XY$bls3PxAp7l>9MZ6;Tt_6=}!1g zPknuRqW4|BcQn5jsdkr;w}cK1X3^X91Ezeo;VOD#u9ZD;9m#))2&A_){)igrt+-;& zKhHBr^AYtrTzUy3*WJratMXpv+lsj9J(@5Yi8OQC_|_yomk*@pN)OW5Q* z&O|=6MDz^f)2cF5ajuT!T%P1xxt3Q$P6SzHcsSHA7CaNO|H=-ZQkqq4v6^o&ix@%k z*oi@K=^%E>y7lY3U(nWh>9!S z&*S(k#yvH}a^ZT|3rDJvWTN|dpV6-($#K+oS2(Py(*x1LbcLPVL)DoJA2=FeU0Oqm ze)v*vzl3j9k$7qec~8%eKLT?ZNvjq|b=;)r#q3?K4Nj9g|l z-fecS{58>ha_2hqhSiUyJxJm7ON4fB1&SQJvLJRm-PQj%u0T$(|Pr$>})F zUvt}C$E;#HNPfJUulf3S!F&+?n;jDo^#9;4Vz>+T*>gNAP@TwG@LQImI;P5dQqM{K@a`txVL?2V z4v*;Zom!F?1Z7?zeU|ZY6iv-W-k$a-rwfkFSQVPu^H5_q!WW9lJbK`%z^JjSb8 z=I%&yKGi6j+IO)>XRkn(DC0bnetOZv`wO&a!ENzIBjovZ%(b3riO;*^E%U0zOvZ1@ zR(KHFqslg8J(^W$vn8p(#~#G$!2QtWeCmL;xqV_#Bznvtm7Gm4R!R`bZnwUowlG{ua1 zeXyX+mWdNT+ZOap9L^ubbtw*Fy`LHBepM}>M7|a zw<}sA<~V7fmqKpSIejmyQYRb2VzI;=>pe{qj@j=y_OeI%Jijx)rT>|t{z&&E9jmfF zd4*igyPw(CNl%nIXTP%oF_QS4MH&1ADxD!Xh2L+-Mt@|Qx)!#M5Zdmk3MJk5B>bj% z?5*lGXFa9Q`#7BJIk6?S<%wJk4)YD73-tB7m3owi3!ZnuM@}b7tzxmKq|?bet?JIi zt>Jg9QqK*KE1oDj)dzit@|k2N-&;n`ELG Date: Tue, 16 Feb 2016 11:53:18 -0800 Subject: [PATCH 7/7] minor changes to comments in the UseToExportFieldsInManifest rule. --- Rules/UseToExportFieldsInManifest.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Rules/UseToExportFieldsInManifest.cs b/Rules/UseToExportFieldsInManifest.cs index 3e3d5709c..f7623c085 100644 --- a/Rules/UseToExportFieldsInManifest.cs +++ b/Rules/UseToExportFieldsInManifest.cs @@ -85,13 +85,13 @@ private bool IsValidManifest(Ast ast, string fileName) } /// - /// Checks if the *ToExport fields are explicitly set to lists, @(...) + /// Checks if the *ToExport fields are explicitly set to arrays, eg. @(...), and the array entries do not contain any wildcard. /// /// /// /// /// - /// A boolean value indicating if the the ToExport fields are explicitly set to lists or not. + /// A boolean value indicating if the the ToExport fields are explicitly set to arrays or not. private bool HasAcceptableExportField(string key, HashtableAst hast, string scriptText, out IScriptExtent extent) { extent = null; @@ -99,6 +99,7 @@ private bool HasAcceptableExportField(string key, HashtableAst hast, string scri { if (key.Equals(pair.Item1.Extent.Text.Trim(), StringComparison.OrdinalIgnoreCase)) { + // checks if the right hand side of the assignment is an array. var arrayAst = pair.Item2.Find(x => x is ArrayLiteralAst || x is ArrayExpressionAst, true); if (arrayAst == null) { @@ -107,6 +108,7 @@ private bool HasAcceptableExportField(string key, HashtableAst hast, string scri } else { + //checks if any entry within the array has a wildcard. var elementWithWildcard = arrayAst.Find(x => x is StringConstantExpressionAst && x.Extent.Text.Contains("*"), false); if (elementWithWildcard != null)