Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions Rules/AvoidValidateRangeTypeMismatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Globalization;
using System.Linq;
using System.Management.Automation.Language;
using System.Reflection;
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
#if !CORECLR
using System.ComponentModel.Composition;
Expand Down Expand Up @@ -109,16 +110,22 @@ private static bool IsIntegralBoundary(ExpressionAst expressionAst)
return IsIntegralBoundary(unaryExpressionAst.Child);
}

if (expressionAst is MemberExpressionAst memberExpressionAst
&& memberExpressionAst.Static
&& memberExpressionAst.Expression is TypeExpressionAst typeExpressionAst
&& memberExpressionAst.Member is StringConstantExpressionAst memberName)
{
Type type = typeExpressionAst.TypeName.GetReflectionType();
FieldInfo field = type?.GetField(memberName.Value, BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy);
return field != null && field.IsLiteral && IntegralTypes.Contains(field.FieldType);
Comment thread
bergmeister marked this conversation as resolved.
}

return false;
}

private static bool IsValidateRangeAttribute(AttributeAst attributeAst)
{
string attributeName = attributeAst.TypeName.FullName;
int lastDot = attributeName.LastIndexOf('.');
string simpleName = lastDot < 0 ? attributeName : attributeName.Substring(lastDot + 1);
return String.Equals(simpleName, "ValidateRange", StringComparison.OrdinalIgnoreCase)
|| String.Equals(simpleName, "ValidateRangeAttribute", StringComparison.OrdinalIgnoreCase);
return attributeAst.TypeName.GetReflectionAttributeType() == typeof(System.Management.Automation.ValidateRangeAttribute);
}

public string GetName()
Expand Down
52 changes: 52 additions & 0 deletions Tests/Rules/AvoidValidateRangeTypeMismatch.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,39 @@
Describe 'AvoidValidateRangeTypeMismatch' {
BeforeAll {
$ruleName = 'PSAvoidValidateRangeTypeMismatch'
Add-Type -TypeDefinition @'
Comment thread
bergmeister marked this conversation as resolved.
public class ValidateRangeBoundsBaseForScriptAnalyzer
{
public const int Max = 100;
}

public class ValidateRangeBoundsForScriptAnalyzer : ValidateRangeBoundsBaseForScriptAnalyzer
{
public static readonly int ReadonlyIntegral = 100;
public const double FloatingPoint = 100.0;
public static int ThrowingGetterInvocationCount;

public static int ThrowingProperty
{
get
{
ThrowingGetterInvocationCount++;
throw new System.InvalidOperationException();
}
}
}

namespace Contoso
{
[System.AttributeUsage(System.AttributeTargets.All)]
public class ValidateRangeAttribute : System.Attribute
{
public ValidateRangeAttribute(int minRange, int maxRange)
{
}
}
}
'@
}

It 'reports integral boundaries for floating-point and decimal parameter types' {
Expand All @@ -15,6 +48,9 @@ Describe 'AvoidValidateRangeTypeMismatch' {
'function Test { param([double][System.Management.Automation.ValidateRangeAttribute(1, 2)]$Value) }'
'function Test { param([double][ValidateRange([int]0.0, [int]100.0)]$Value) }'
'function Test { param([double][ValidateRange([int32]0.0, [int32]100.0)]$Value) }'
'function Test { param([double][ValidateRange(0, [int]::MaxValue)]$Value) }'
'function Test { param([double][ValidateRange(0, [ValidateRangeBoundsForScriptAnalyzer]::Max)]$Value) }'
'function Test { param([double][System.Management.Automation.ValidateRange(0, 100)]$Value) }'
)

foreach ($scriptDefinition in $scripts) {
Expand All @@ -30,11 +66,27 @@ Describe 'AvoidValidateRangeTypeMismatch' {
'function Test { param([single][ValidateRange(1.0, 2.0)]$Value) }'
'function Test { param([decimal][ValidateRange(1.0, 2.0)]$Value) }'
'function Test { param([double][ValidateRange(1, 2.0)]$Value) }'
'function Test { param([double][Contoso.ValidateRange(0, 100)]$Value) }'
'function Test { param([int][ValidateRange(1, 2)]$Value) }'
)

foreach ($scriptDefinition in $scripts) {
Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -IncludeRule $ruleName | Should -BeNullOrEmpty
}
}

It 'ignores non-constant static member expressions without invoking getters' {
$scripts = @(
'function Test { param([double][ValidateRange(0, [ValidateRangeBoundsForScriptAnalyzer]::ThrowingProperty)]$Value) }'
'function Test { param([double][ValidateRange(0, [ValidateRangeBoundsForScriptAnalyzer]::ReadonlyIntegral)]$Value) }'
'function Test { param([double][ValidateRange(0, [ValidateRangeBoundsForScriptAnalyzer]::FloatingPoint)]$Value) }'
)

foreach ($scriptDefinition in $scripts) {
Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -IncludeRule $ruleName |
Where-Object RuleName -eq $ruleName | Should -BeNullOrEmpty
}

[ValidateRangeBoundsForScriptAnalyzer]::ThrowingGetterInvocationCount | Should -Be 0
}
}
Loading