Fix handling of non-boolean results in rules #187 (#193)

- Fix handling of non-boolean results in rules. #187
This commit is contained in:
Bernie White 2019-06-19 00:33:01 +08:00 коммит произвёл GitHub
Родитель 536442ea8a
Коммит e3b3695c80
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 120 добавлений и 22 удалений

Просмотреть файл

@ -1,6 +1,7 @@
## Unreleased
- Fix handling of non-boolean results in rules. Rule is failed with more specific error message. [#187](https://github.com/BernieWhite/PSRule/issues/187)
- Include .ps1 files that are specified directly with `-Path`, instead of only .rule.ps1 files. [#182](https://github.com/BernieWhite/PSRule/issues/182)
- Improved warning message displayed when no Rule.ps1 files are founds.

Просмотреть файл

@ -4,30 +4,31 @@
## SHORT DESCRIPTION
Describes the language keywords that can be used within PSRule document definitions.
Describes the language keywords that can be used within PSRule rule definitions.
## LONG DESCRIPTION
PSRule lets you define rules using PowerShell blocks. To create a rule use the `Rule` keyword. Within a rule several assertions can be used.
PSRule lets you define rules using PowerShell blocks. To define a rule use the `Rule` keyword.
- Assertion - A specific test that always evaluates to true or false.
- [Rule](#rule) - Creates a rule definition.
The following are the built-in keywords that can be used within PSRule:
The following are the built-in keywords that can be used within a rule definition:
- [Rule](#rule) - A rule definition
- [Exists](#exists) - Assert that a field or property must exist
- [Match](#match) - Assert that the field must match any of the regular expressions
- [AnyOf](#anyof) - Assert that any of the child expressions must be true
- [AllOf](#allof) - Assert that all of the child expressions must be true
- [Within](#within) - Assert that the field must match any of the values
- [TypeOf](#typeof) - Assert that the object must be of a specific type
- [Exists](#exists) - Assert that a field or property must exist.
- [Match](#match) - Assert that the field must match any of the regular expressions.
- [AnyOf](#anyof) - Assert that any of the child expressions must be true.
- [AllOf](#allof) - Assert that all of the child expressions must be true.
- [Within](#within) - Assert that the field must match any of the values.
- [TypeOf](#typeof) - Assert that the object must be of a specific type.
- [Recommend](#recommend) - Return the process to resolve the issue and pass the rule.
### Rule
A `Rule` definition describes an individual business rule that will be applied to pipeline objects.
A `Rule` definition describes an individual business rule that will be executed against each input object. Input objects can be passed on the PowerShell pipeline or supplied from file.
To define a Rule use the `Rule` keyword followed by a name and a pair of squiggly brackets `{`. Within the `{ }` one or more expressions can be used.
To define a Rule use the `Rule` keyword followed by a name and a pair of squiggly brackets `{`. Within the `{ }` one or more conditions can be used.
Conditions determine if the input object either _Pass_ or _Fail_ the rule.
Syntax:
@ -43,7 +44,18 @@ Rule [-Name] <string> [-Tag <hashtable>] [-Type <string[]>] [-If <scriptBlock>]
- `If` - A script precondition that must evaluate to `$True` before the rule is executed.
- `DependsOn` - A list of rules this rule depends on. Rule dependencies must execute successfully before this rule is executed.
- `Configure` - A set of default configuration values. These values are only used when the baseline configuration does not contain the key.
- `Body` - A script block definition of the rule containing one or more PSRule keywords and PowerShell expressions.
- `Body` - A script block that specifies one or more conditions that are required for the rule to _Pass_.
A condition is any valid PowerShell that return either `$True` or `$False`. Optionally, PSRule keywords can be used to help build out conditions quickly. When a rule contains more then one condition, all must return `$True` for the rule to _Pass_. If any one condition returns `$False` the rule has failed.
The following restrictions apply:
- Rule conditions should only return `$True` or `$False`. Other objects should be caught with `Out-Null` or null assigned like `$Null = `.
- The `Rule` keyword can not be nested in a `Rule` definition.
- Variables and functions defined within `.Rule.ps1` files, but outside the `Rule` definition block are not accessible unless the `Global` scope is applied.
- Functions and variables within the caller's scope (the scope calling `Invoke-PSRule`, `Get-PSRule`, `Test-PSRuleTarget`) are not accessible.
- Cmdlets that require user interaction are not supported, i.e. `Read-Host`.
- `Write-Debug` is not currently supported, `Write-Error`, `Write-Warning`, `Write-Verbose` and `Write-Information` are.
Examples:

Просмотреть файл

@ -2,7 +2,6 @@
using PSRule.Rules;
using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.Management.Automation;
namespace PSRule.Commands

Просмотреть файл

@ -1,5 +1,4 @@
using PSRule.Annotations;
using PSRule.Configuration;
using PSRule.Pipeline;
using PSRule.Resources;
using PSRule.Rules;

Просмотреть файл

@ -297,7 +297,7 @@ namespace PSRule.Pipeline
return;
}
DoWriteWarning(string.Format(PSRuleResources.ObjectNotProcessed, TargetName));
DoWriteWarning(message: string.Format(PSRuleResources.ObjectNotProcessed, TargetName));
}
public void WarnRuleNotFound()
@ -307,7 +307,22 @@ namespace PSRule.Pipeline
return;
}
DoWriteWarning(PSRuleResources.RuleNotFound);
DoWriteWarning(message: PSRuleResources.RuleNotFound);
}
public void ErrorInvaildRuleResult()
{
if (!_LogError)
{
return;
}
DoWriteError(errorRecord: new ErrorRecord(
exception: new RuleRuntimeException(message: string.Format(PSRuleResources.InvalidRuleResult, RuleBlock.RuleId)),
errorId: "PSRule.Runtime.InvalidRuleResult",
errorCategory: ErrorCategory.InvalidResult,
targetObject: null
));
}
#endregion Logging

9
src/PSRule/Resources/PSRuleResources.Designer.cs сгенерированный
Просмотреть файл

@ -60,6 +60,15 @@ namespace PSRule.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to An invalid rule result was returned for {0}. Conditions must return boolean $True or $False..
/// </summary>
internal static string InvalidRuleResult {
get {
return ResourceManager.GetString("InvalidRuleResult", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Target object &apos;{0}&apos; has not been processed because no matching rules were found..
/// </summary>

Просмотреть файл

@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="InvalidRuleResult" xml:space="preserve">
<value>An invalid rule result was returned for {0}. Conditions must return boolean $True or $False.</value>
</data>
<data name="ObjectNotProcessed" xml:space="preserve">
<value>Target object '{0}' has not been processed because no matching rules were found.</value>
</data>

Просмотреть файл

@ -1,4 +1,5 @@
using System.Collections.Generic;
using PSRule.Pipeline;
using System.Collections.Generic;
using System.Management.Automation;
namespace PSRule.Rules
@ -38,11 +39,14 @@ namespace PSRule.Rules
{
count++;
if (v is bool && (bool)v)
if (!TryBoolean(v, out bool bresult))
{
pass++;
PipelineContext.CurrentThread.ErrorInvaildRuleResult();
continue;
}
else if (v is PSObject && (bool)((PSObject)v).BaseObject)
if (bresult)
{
pass++;
}
@ -50,5 +54,29 @@ namespace PSRule.Rules
return new RuleConditionResult(pass: pass, count: count);
}
private static bool TryBoolean(object o, out bool result)
{
result = false;
if (o == null)
{
return false;
}
if (o is bool bresult)
{
result = bresult;
return true;
}
if (o is PSObject pso && pso.BaseObject is bool psoresult)
{
result = psoresult;
return true;
}
return false;
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
#
# Pester unit test rules for error handling
#
# Synopsis: Should fail
Rule 'WithNonBoolean' {
$True
'false' # Not a boolean
}

Просмотреть файл

@ -1092,3 +1092,26 @@ Describe 'Get-PSRuleHelp' -Tag 'Get-PSRuleHelp', 'Common' {
}
#endregion Get-PSRuleHelp
#region Rule processing
Describe 'Rule processing' -Tag 'Common', 'RuleProcessing' {
Context 'Error handling' {
$ruleFilePath = (Join-Path -Path $here -ChildPath 'FromFileWithError.Rule.ps1');
$testObject = [PSCustomObject]@{
Name = 'TestObject1'
Value = 1
}
It 'Handles non-boolean results' {
$result = $testObject | Invoke-PSRule -Path $ruleFilePath -ErrorVariable outError -ErrorAction SilentlyContinue;
$messages = @($outError);
$result | Should -Not -BeNullOrEmpty;
$result.IsSuccess() | Should -Be $False;
$messages.Length | Should -BeGreaterThan 0;
$messages.Exception.Message | Should -BeLike 'An invalid rule result was returned for *';
}
}
}
#endregion Rule processing