@@ -34,6 +34,7 @@ public class Helper
3434 private CommandInvocationIntrinsics invokeCommand ;
3535 private IOutputWriter outputWriter ;
3636 private Object getCommandLock = new object ( ) ;
37+ private readonly static Version minSupportedPSVersion = new Version ( 3 , 0 ) ;
3738
3839 #endregion
3940
@@ -1602,6 +1603,182 @@ public bool GetNamedArgumentAttributeValue(NamedAttributeArgumentAst namedAttrib
16021603 return false ;
16031604 }
16041605
1606+ /// <summary>
1607+ /// Gets valid keys of a PowerShell module manifest file for a given PowerShell version
1608+ /// </summary>
1609+ /// <param name="powershellVersion">Version parameter; valid if >= 3.0</param>
1610+ /// <returns>Returns an enumerator over valid keys</returns>
1611+ public static IEnumerable < string > GetModuleManifestKeys ( Version powershellVersion )
1612+ {
1613+ if ( powershellVersion == null )
1614+ {
1615+ throw new ArgumentNullException ( "powershellVersion" ) ;
1616+ }
1617+ if ( ! IsPowerShellVersionSupported ( powershellVersion ) )
1618+ {
1619+ throw new ArgumentException ( "Invalid PowerShell version. Choose from version greater than or equal to 3.0" ) ;
1620+ }
1621+ var keys = new List < string > ( ) ;
1622+ var keysCommon = new List < string > {
1623+ "RootModule" ,
1624+ "ModuleVersion" ,
1625+ "GUID" ,
1626+ "Author" ,
1627+ "CompanyName" ,
1628+ "Copyright" ,
1629+ "Description" ,
1630+ "PowerShellVersion" ,
1631+ "PowerShellHostName" ,
1632+ "PowerShellHostVersion" ,
1633+ "DotNetFrameworkVersion" ,
1634+ "CLRVersion" ,
1635+ "ProcessorArchitecture" ,
1636+ "RequiredModules" ,
1637+ "RequiredAssemblies" ,
1638+ "ScriptsToProcess" ,
1639+ "TypesToProcess" ,
1640+ "FormatsToProcess" ,
1641+ "NestedModules" ,
1642+ "FunctionsToExport" ,
1643+ "CmdletsToExport" ,
1644+ "VariablesToExport" ,
1645+ "AliasesToExport" ,
1646+ "ModuleList" ,
1647+ "FileList" ,
1648+ "PrivateData" ,
1649+ "HelpInfoURI" ,
1650+ "DefaultCommandPrefix" } ;
1651+ keys . AddRange ( keysCommon ) ;
1652+ if ( powershellVersion . Major >= 5 )
1653+ {
1654+ keys . Add ( "DscResourcesToExport" ) ;
1655+ }
1656+ if ( powershellVersion >= new Version ( 5 , 1 ) )
1657+ {
1658+ keys . Add ( "CompatiblePSEditions" ) ;
1659+ }
1660+ return keys ;
1661+ }
1662+
1663+ /// <summary>
1664+ /// Gets deprecated keys of PowerShell module manifest
1665+ /// </summary>
1666+ /// <returns>Returns an enumerator over deprecated keys</returns>
1667+ public static IEnumerable < string > GetDeprecatedModuleManifestKeys ( )
1668+ {
1669+ return new List < string > { "ModuleToProcess" } ;
1670+ }
1671+
1672+ /// <summary>
1673+ /// Get a mapping between string type keys and StatementAsts from module manifest hashtable ast
1674+ ///
1675+ /// This is a workaround as SafeGetValue is not supported on PS v5 and below.
1676+ /// </summary>
1677+ /// <param name="hast">Hashtable Ast obtained from module manifest</param>
1678+ /// <returns>A dictionary that maps string keys to values of StatementAst type</returns>
1679+ private static Dictionary < string , StatementAst > GetMapFromHashtableAst ( HashtableAst hast )
1680+ {
1681+ var map = new Dictionary < string , StatementAst > ( StringComparer . OrdinalIgnoreCase ) ;
1682+ foreach ( var pair in hast . KeyValuePairs )
1683+ {
1684+ var key = pair . Item1 as StringConstantExpressionAst ;
1685+ if ( key == null )
1686+ {
1687+ return null ;
1688+ }
1689+ map [ key . Value ] = pair . Item2 ;
1690+ }
1691+ return map ;
1692+ }
1693+
1694+ /// <summary>
1695+ /// Checks if the version is supported
1696+ ///
1697+ /// PowerShell versions with Major greater than 3 are supported
1698+ /// </summary>
1699+ /// <param name="version">PowerShell version</param>
1700+ /// <returns>true if the given version is supported else false</returns>
1701+ public static bool IsPowerShellVersionSupported ( Version version )
1702+ {
1703+ if ( version == null )
1704+ {
1705+ throw new ArgumentNullException ( "version" ) ;
1706+ }
1707+ return version >= minSupportedPSVersion ;
1708+ }
1709+
1710+ /// <summary>
1711+ /// Checks if a given file is a valid PowerShell module manifest
1712+ /// </summary>
1713+ /// <param name="filepath">Path to module manifest</param>
1714+ /// <param name="powershellVersion">Version parameter; valid if >= 3.0</param>
1715+ /// <returns>true if given filepath points to a module manifest, otherwise false</returns>
1716+ public static bool IsModuleManifest ( string filepath , Version powershellVersion = null )
1717+ {
1718+ Token [ ] tokens ;
1719+ ParseError [ ] errors ;
1720+ if ( filepath == null )
1721+ {
1722+ throw new ArgumentNullException ( "filepath" ) ;
1723+ }
1724+ if ( powershellVersion != null
1725+ && ! IsPowerShellVersionSupported ( powershellVersion ) )
1726+ {
1727+ return false ;
1728+ }
1729+ if ( ! Path . GetExtension ( filepath ) . Equals ( ".psd1" , StringComparison . OrdinalIgnoreCase ) )
1730+ {
1731+ return false ;
1732+ }
1733+
1734+ //using parsefile causes the parser to crash!
1735+ string fileContent = File . ReadAllText ( filepath ) ;
1736+ var ast = Parser . ParseInput ( fileContent , out tokens , out errors ) ;
1737+ var hast = ast . Find ( x => x is HashtableAst , false ) as HashtableAst ;
1738+ if ( hast == null )
1739+ {
1740+ return false ;
1741+ }
1742+ var map = GetMapFromHashtableAst ( hast ) ;
1743+ var deprecatedKeys = GetDeprecatedModuleManifestKeys ( ) ;
1744+ IEnumerable < string > allKeys ;
1745+ if ( powershellVersion != null )
1746+ {
1747+ allKeys = GetModuleManifestKeys ( powershellVersion ) ;
1748+ }
1749+ else
1750+ {
1751+ Version version = null ;
1752+ if ( map . ContainsKey ( "PowerShellVersion" ) )
1753+ {
1754+ var versionStrAst = map [ "PowerShellVersion" ] . Find ( x => x is StringConstantExpressionAst , false ) ;
1755+ if ( versionStrAst != null )
1756+ {
1757+ try
1758+ {
1759+ version = new Version ( ( versionStrAst as StringConstantExpressionAst ) . Value ) ;
1760+ }
1761+ catch
1762+ {
1763+ // we just ignore if the value is not a valid version
1764+ }
1765+ }
1766+ }
1767+ if ( version != null
1768+ && IsPowerShellVersionSupported ( version ) )
1769+ {
1770+ allKeys = GetModuleManifestKeys ( version ) ;
1771+ }
1772+ else
1773+ {
1774+ // default to version 5.1
1775+ allKeys = GetModuleManifestKeys ( new Version ( "5.1" ) ) ;
1776+ }
1777+ }
1778+
1779+ // check if the keys given in module manifest are a proper subset of Keys
1780+ return map . Keys . All ( x => allKeys . Concat ( deprecatedKeys ) . Contains ( x , StringComparer . OrdinalIgnoreCase ) ) ;
1781+ }
16051782#endregion Methods
16061783 }
16071784
0 commit comments