GoodEnoughRules.psm1
function Get-CommandParameters { param ( [System.Management.Automation.Language.CommandAst] $Command ) $commandElements = $CommandAst.CommandElements #region Gather Parameters # Create a hash to hold the parameter name as the key, and the value $parameterHash = @{} for($i=1; $i -lt $commandElements.Count; $i++){ # Switch on type switch ($commandElements[$i].GetType().Name){ 'CommandParameterAst' { $parameterName = $commandElements[$i].ParameterName } default { # Grab the string from the extent $value = $commandElements[$i].SafeGetValue() $parameterHash[$parameterName] = $value } } } return $parameterHash } function Measure-SecureStringWithKey { <# .SYNOPSIS Rule to detect if ConvertFrom-SecureString is used without a Key. .DESCRIPTION This rule detects if ConvertFrom-SecureString is used without a Key which means the secret is user and machine bound. .EXAMPLE Measure-SecureStringWithKey -ScriptBlockAst $ScriptBlockAst This will check if the given ScriptBlockAst contains any ConvertFrom-SecureString commands without a Key parameter. .PARAMETER ScriptBlockAst The scriptblock AST to check. .INPUTS [System.Management.Automation.Language.ScriptBlockAst] .OUTPUTS [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]] .NOTES None #> [CmdletBinding()] [OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord])] Param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.Management.Automation.Language.ScriptBlockAst] $ScriptBlockAst ) Begin { $predicate = { param($Ast) $Ast -is [System.Management.Automation.Language.CommandAst] -and $Ast.GetCommandName() -eq 'ConvertFrom-SecureString' } } Process { [System.Management.Automation.Language.Ast[]]$commands = $ScriptBlockAst.FindAll($predicate, $true) $commands | ForEach-Object { $command = $_ $parameterHash = Get-CommandParameters -Command $command if (-not $parameterHash.ContainsKey('Key')) { [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ Message = 'ConvertFrom-SecureString should be used with a Key.' Extent = $command.Extent Severity = [Microsoft.Windows.PowerShell.ScriptAnalyzer.Severity]::Error } } } } } function Measure-TODOComment { <# .SYNOPSIS Rule to detect if TODO style comments are present. .DESCRIPTION This rule detects if TODO style comments are present in the given ScriptBlockAst. .EXAMPLE Measure-TODOComment -Token $Token This will check if the given Token contains any TODO comments. .PARAMETER Token The token to check for TODO comments. .INPUTS [System.Management.Automation.Language.Token] .OUTPUTS [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord[]] .NOTES None #> [CmdletBinding()] [OutputType([Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord])] Param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.Management.Automation.Language.Token] $Token ) Begin { $toDoIndicators = @( 'TODO', 'FIXME', 'BUG', 'MARK', '\[.\]' ) -join '|' $regExOptions = [System.Text.RegularExpressions.RegexOptions]::IgnoreCase #, IgnorePatternWhitespace, Multiline" # TODO: Add more comments to make it easier to understand the regular expression. # so meta hehe $regExPattern = "((\/\/|#|<!--|;|\*|^)((\s+(!|\?|\*|\-))?(\s+\[ \])?|(\s+(!|\?|\*)\s+\[.\])?)\s*($toDoIndicators)\s*\:?)" $regEx = [regex]::new($regExPattern, $regExOptions) } Process { if (-not $Token.Type -ne 'Comment') { return } #region Finds ASTs that match the predicates. foreach ($i in $Token.Extent.Text) { try { $matches = $regEx.Matches($i) } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } if ($matches.Count -eq 0) { continue } $matches | ForEach-Object { [Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.DiagnosticRecord]@{ 'Message' = "TODO comment found. Please consider removing it or tracking with issue." 'Extent' = $Token.Extent 'RuleName' = $PSCmdlet.MyInvocation.InvocationName 'Severity' = 'Information' } } } #endregion } } # Don't need to dot load because we are compiling! |