зеркало из https://github.com/microsoft/PSRule.git
* Added rule ref and aliases #792 #881 * Minor fixes Co-authored-by: ArmaanMcleod <armaan_mcleod@outlook.com>
This commit is contained in:
Родитель
d9d8d32503
Коммит
af6531aa20
|
@ -354,6 +354,7 @@ The following conceptual topics exist in the `PSRule` module:
|
|||
- [Binding.UseQualifiedName](docs/concepts/PSRule/en-US/about_PSRule_Options.md#bindingusequalifiedname)
|
||||
- [Configuration](docs/concepts/PSRule/en-US/about_PSRule_Options.md#configuration)
|
||||
- [Convention.Include](docs/concepts/PSRule/en-US/about_PSRule_Options.md#conventioninclude)
|
||||
- [Execution.AliasReferenceWarning](docs/concepts/PSRule/en-US/about_PSRule_Options.md#executionaliasreferencewarning)
|
||||
- [Execution.LanguageMode](docs/concepts/PSRule/en-US/about_PSRule_Options.md#executionlanguagemode)
|
||||
- [Execution.InconclusiveWarning](docs/concepts/PSRule/en-US/about_PSRule_Options.md#executioninconclusivewarning)
|
||||
- [Execution.NotProcessedWarning](docs/concepts/PSRule/en-US/about_PSRule_Options.md#executionnotprocessedwarning)
|
||||
|
|
|
@ -11,6 +11,21 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers
|
|||
|
||||
## Unreleased
|
||||
|
||||
What's changed since pre-release v2.0.0-B2201054:
|
||||
|
||||
- General improvements:
|
||||
- Added support for rule aliases. [#792](https://github.com/microsoft/PSRule/issues/792)
|
||||
- Aliases allow rules to be references by an alternative name.
|
||||
- When renaming rules, add a rule alias to avoid breaking references to the old rule name.
|
||||
- To specify an alias use the `-Alias` parameter or `alias` metadata property in YAML or JSON.
|
||||
- Added support for stable identifiers with rule refs. [#881](https://github.com/microsoft/PSRule/issues/881)
|
||||
- A rule ref may be optionally be used to reference a rule.
|
||||
- Rule refs should be:
|
||||
stable, not changing between releases;
|
||||
opaque, as opposed to being a human-readable string.
|
||||
Stable and opaque refs ease web lookup and to help to avoid language difficulties.
|
||||
- To specify a rule ref use the `-Ref` parameter or `ref` metadata property in YAML or JSON.
|
||||
|
||||
## v2.0.0-B2201054 (pre-release)
|
||||
|
||||
What's changed since v1.11.0:
|
||||
|
|
|
@ -286,7 +286,7 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
|
|||
|
||||
## OUTPUTS
|
||||
|
||||
### PSRule.Rules.Rule
|
||||
### PSRule.Definitions.Rules.IRuleV1
|
||||
|
||||
## NOTES
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ This topic describes what options are available, when to and how to use them.
|
|||
The following workspace options are available for use:
|
||||
|
||||
- [Convention.Include](#conventioninclude)
|
||||
- [Execution.AliasReferenceWarning](#executionaliasreferencewarning)
|
||||
- [Execution.LanguageMode](#executionlanguagemode)
|
||||
- [Execution.InconclusiveWarning](#executioninconclusivewarning)
|
||||
- [Execution.NotProcessedWarning](#executionnotprocessedwarning)
|
||||
|
@ -662,6 +663,58 @@ variables:
|
|||
value: 'Convention1;Convention2'
|
||||
```
|
||||
|
||||
### Execution.AliasReferenceWarning
|
||||
|
||||
Rules may define one or more aliases.
|
||||
These aliases are alternative names to identify the rule.
|
||||
An alias may be used to reference the rule anywhere a rule name is used.
|
||||
The primary purpose of an alias is to provide a non-breaking method to change the rule name.
|
||||
Alises can be removed at a later revision once the rule is no longer referenced by the alias.
|
||||
|
||||
A warning is logged by default to help identify when an alias is used.
|
||||
We recommend taking action to update your usage of the alis to use the rule name or ref instead.
|
||||
|
||||
Alternatively, the alias reference warning can be disabled by using:
|
||||
|
||||
```powershell
|
||||
# PowerShell: Using the AliasReferenceWarning parameter
|
||||
$option = New-PSRuleOption -AliasReferenceWarning $False;
|
||||
```
|
||||
|
||||
```powershell
|
||||
# PowerShell: Using the Execution.AliasReferenceWarning hashtable key
|
||||
$option = New-PSRuleOption -Option @{ 'Execution.AliasReferenceWarning' = $False };
|
||||
```
|
||||
|
||||
```powershell
|
||||
# PowerShell: Using the AliasReferenceWarning parameter to set YAML
|
||||
Set-PSRuleOption -AliasReferenceWarning $False;
|
||||
```
|
||||
|
||||
```yaml
|
||||
# YAML: Using the execution/aliasReferenceWarning property
|
||||
execution:
|
||||
aliasReferenceWarning: false
|
||||
```
|
||||
|
||||
```bash
|
||||
# Bash: Using environment variable
|
||||
export PSRULE_EXECUTION_ALIASREFERENCEWARNING=false
|
||||
```
|
||||
|
||||
```yaml
|
||||
# GitHub Actions: Using environment variable
|
||||
env:
|
||||
PSRULE_EXECUTION_ALIASREFERENCEWARNING: false
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Azure Pipelines: Using environment variable
|
||||
variables:
|
||||
- name: PSRULE_EXECUTION_ALIASREFERENCEWARNING
|
||||
value: false
|
||||
```
|
||||
|
||||
### Execution.LanguageMode
|
||||
|
||||
Unless PowerShell has been constrained, full language features of PowerShell are available to use within rule definitions.
|
||||
|
@ -2378,9 +2431,11 @@ convention:
|
|||
|
||||
# Configure execution options
|
||||
execution:
|
||||
aliasReferenceWarning: false
|
||||
languageMode: ConstrainedLanguage
|
||||
inconclusiveWarning: false
|
||||
notProcessedWarning: false
|
||||
suppressedRuleWarning: false
|
||||
|
||||
# Configure include options
|
||||
include:
|
||||
|
@ -2480,9 +2535,11 @@ convention:
|
|||
|
||||
# Configure execution options
|
||||
execution:
|
||||
aliasReferenceWarning: true
|
||||
languageMode: FullLanguage
|
||||
inconclusiveWarning: true
|
||||
notProcessedWarning: true
|
||||
suppressedRuleWarning: true
|
||||
|
||||
# Configure include options
|
||||
include:
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
author: BernieWhite
|
||||
---
|
||||
|
||||
# Deprecations
|
||||
|
||||
## Deprecations for v3
|
||||
|
||||
### Rule output object
|
||||
|
||||
Several properties of the rule object have been renamed to improve consistency with other objects.
|
||||
Previously rules returned by `Get-PSRule` returned a rule object which included the following properties:
|
||||
|
||||
- `RuleId`
|
||||
- `RuleName`
|
||||
- `Description`
|
||||
|
||||
These have been replaced with the following properties:
|
||||
|
||||
- `Id` instead of `RuleId`.
|
||||
- `Name` instead of `RuleName`.
|
||||
- `Synopsis` instead of `Description`.
|
||||
|
||||
From _v3_ these properties will be removed.
|
||||
These changes do not affect normal usage of PSRule.
|
||||
Supporting scripts that directly use the old names may not work correctly until you update these names.
|
||||
|
||||
## Deprecations for v2
|
||||
|
||||
### Default baseline by module manifest
|
||||
|
||||
When packaging baselines in a module, you may want to specify a default baseline.
|
||||
PSRule _v1.9.0_ added support for setting the default baseline in a module configuration.
|
||||
|
||||
Previously a default baseline could be set by specifying the baseline in the module manifest.
|
||||
From _v1.9.0_ this is deprecated and will be removed from _v2_.
|
||||
|
||||
For details on how to migrate to the new default baseline option, continue reading the [upgrade notes][1].
|
||||
|
||||
[1]: upgrade-notes.md#setting-default-module-baseline
|
||||
|
||||
### Resources without an API version
|
||||
|
||||
When creating YAML and JSON resources you define a resource by specifying the `apiVersion` and `kind`.
|
||||
To allow new schema versions for resources to be introduced in the future, an `apiVersion` was introduced.
|
||||
For backwards compatibility, resources without an `apiVersion` deprecated but supported.
|
||||
From _v2_ resources without an `apiVersion` will be ignored.
|
||||
|
||||
For details on how to add an `apiVersion` to a resource, continue reading the [upgrade notes][2].
|
||||
|
||||
[2]: upgrade-notes.md#setting-resource-api-version
|
|
@ -44,13 +44,15 @@ Conditions determine if the input object either _Pass_ or _Fail_ the rule.
|
|||
Syntax:
|
||||
|
||||
```text
|
||||
Rule [-Name] <string> [-Tag <hashtable>] [-When <string[]>] [-Type <string[]>] [-If <scriptBlock>] [-DependsOn <string[]>] [-Configure <hashtable>] [-ErrorAction <ActionPreference>] [-Body] {
|
||||
Rule [-Name] <string> [-Ref <string>] [-Alias <string[]>] [-Tag <hashtable>] [-When <string[]>] [-Type <string[]>] [-If <scriptBlock>] [-DependsOn <string[]>] [-Configure <hashtable>] [-ErrorAction <ActionPreference>] [-Body] {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
- `Name` - The name of the rule definition. Each rule name must be unique.
|
||||
When packaging rules within a module, rule names must only be unique within the module.
|
||||
- `Ref` - An optional stable and opaque identifier that can be used to reference the rule.
|
||||
- `Alias` - A list of alternative names that can be used to reference the rule.
|
||||
- `Tag` - A hashtable of key/ value metadata that can be used to filter and identify rules and rule results.
|
||||
- `When` - A selector precondition that must evaluate true before the rule is executed.
|
||||
- `Type` - A type precondition that must match the _TargetType_ of the pipeline object before the rule is executed.
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
``` ini
|
||||
|
||||
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000
|
||||
Intel Core i7-1065G7 CPU 1.30GHz, 1 CPU, 8 logical and 4 physical cores
|
||||
.NET SDK=5.0.404
|
||||
[Host] : .NET Core 3.1.22 (CoreCLR 4.700.21.56803, CoreFX 4.700.21.57101), X64 RyuJIT
|
||||
DefaultJob : .NET Core 3.1.22 (CoreCLR 4.700.21.56803, CoreFX 4.700.21.57101), X64 RyuJIT
|
||||
|
||||
|
||||
```
|
||||
| Method | Mean | Error | StdDev | Median | Gen 0 | Gen 1 | Allocated |
|
||||
|------------------------- |-----------------:|----------------:|----------------:|-----------------:|-----------:|----------:|----------:|
|
||||
| Invoke | 50,690,403.5 ns | 1,008,643.42 ns | 842,262.94 ns | 50,510,218.2 ns | 4000.0000 | 272.7273 | 17,758 KB |
|
||||
| InvokeIf | 51,910,565.7 ns | 684,957.47 ns | 607,196.95 ns | 52,036,680.0 ns | 4500.0000 | 200.0000 | 20,008 KB |
|
||||
| InvokeType | 50,323,385.8 ns | 419,714.32 ns | 327,685.33 ns | 50,448,415.0 ns | 4100.0000 | 400.0000 | 17,758 KB |
|
||||
| InvokeSummary | 49,852,085.7 ns | 932,333.23 ns | 826,489.12 ns | 49,792,581.8 ns | 4000.0000 | 363.6364 | 17,758 KB |
|
||||
| Assert | 51,774,439.2 ns | 534,962.40 ns | 446,717.83 ns | 51,741,530.0 ns | 4100.0000 | 300.0000 | 18,461 KB |
|
||||
| Get | 6,153,688.9 ns | 110,055.91 ns | 102,946.36 ns | 6,122,121.1 ns | 85.9375 | - | 367 KB |
|
||||
| GetHelp | 6,191,583.5 ns | 95,299.61 ns | 84,480.62 ns | 6,188,469.5 ns | 85.9375 | - | 367 KB |
|
||||
| Within | 92,791,338.3 ns | 1,666,664.31 ns | 1,558,998.83 ns | 92,849,275.0 ns | 8250.0000 | 1000.0000 | 34,197 KB |
|
||||
| WithinBulk | 136,104,105.9 ns | 2,647,952.81 ns | 4,275,950.21 ns | 135,769,100.0 ns | 14000.0000 | 2000.0000 | 61,224 KB |
|
||||
| WithinLike | 117,091,166.7 ns | 1,955,451.58 ns | 1,733,456.89 ns | 116,485,916.7 ns | 11666.6667 | 1666.6667 | 48,352 KB |
|
||||
| DefaultTargetNameBinding | 713,149.3 ns | 8,563.98 ns | 8,010.75 ns | 712,249.0 ns | 38.0859 | - | 156 KB |
|
||||
| CustomTargetNameBinding | 948,598.4 ns | 17,400.93 ns | 21,369.90 ns | 938,536.5 ns | 85.9375 | - | 352 KB |
|
||||
| NestedTargetNameBinding | 867,820.4 ns | 6,230.42 ns | 5,523.11 ns | 868,881.9 ns | 85.9375 | - | 352 KB |
|
||||
| AssertHasFieldValue | 3,522,024.3 ns | 70,142.49 ns | 133,453.38 ns | 3,464,502.7 ns | 253.9063 | 7.8125 | 1,040 KB |
|
||||
| PathTokenize | 863.4 ns | 17.13 ns | 27.66 ns | 852.0 ns | 0.2632 | - | 1 KB |
|
||||
| PathExpressionBuild | 546.2 ns | 4.86 ns | 4.55 ns | 546.4 ns | 0.3500 | - | 1 KB |
|
||||
| PathExpressionGet | 375,272.2 ns | 4,983.35 ns | 3,890.67 ns | 375,397.7 ns | 17.0898 | - | 70 KB |
|
|
@ -0,0 +1,29 @@
|
|||
``` ini
|
||||
|
||||
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000
|
||||
Intel Core i7-1065G7 CPU 1.30GHz, 1 CPU, 8 logical and 4 physical cores
|
||||
.NET SDK=5.0.404
|
||||
[Host] : .NET Core 3.1.22 (CoreCLR 4.700.21.56803, CoreFX 4.700.21.57101), X64 RyuJIT
|
||||
DefaultJob : .NET Core 3.1.22 (CoreCLR 4.700.21.56803, CoreFX 4.700.21.57101), X64 RyuJIT
|
||||
|
||||
|
||||
```
|
||||
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Allocated |
|
||||
|------------------------- |-----------------:|----------------:|----------------:|-----------:|----------:|----------:|
|
||||
| Invoke | 53,201,973.3 ns | 965,400.91 ns | 903,036.61 ns | 4100.0000 | 400.0000 | 17,758 KB |
|
||||
| InvokeIf | 54,266,264.0 ns | 516,824.36 ns | 483,437.83 ns | 4500.0000 | 200.0000 | 20,008 KB |
|
||||
| InvokeType | 53,299,662.9 ns | 1,041,990.81 ns | 1,240,415.94 ns | 4000.0000 | 400.0000 | 17,758 KB |
|
||||
| InvokeSummary | 52,364,196.7 ns | 741,213.07 ns | 693,331.17 ns | 4100.0000 | 400.0000 | 17,758 KB |
|
||||
| Assert | 53,926,665.9 ns | 1,048,995.55 ns | 929,907.24 ns | 4111.1111 | 222.2222 | 18,461 KB |
|
||||
| Get | 6,213,169.1 ns | 119,598.34 ns | 117,461.56 ns | 85.9375 | 7.8125 | 366 KB |
|
||||
| GetHelp | 6,185,384.7 ns | 121,492.72 ns | 144,628.44 ns | 85.9375 | 7.8125 | 366 KB |
|
||||
| Within | 91,044,076.7 ns | 1,250,486.23 ns | 1,169,705.59 ns | 8250.0000 | 1000.0000 | 34,198 KB |
|
||||
| WithinBulk | 131,636,590.3 ns | 2,585,126.29 ns | 3,361,394.39 ns | 14666.6667 | 1666.6667 | 61,224 KB |
|
||||
| WithinLike | 117,834,564.4 ns | 2,208,102.68 ns | 2,065,460.62 ns | 11666.6667 | 1666.6667 | 48,352 KB |
|
||||
| DefaultTargetNameBinding | 696,752.1 ns | 9,049.16 ns | 8,021.84 ns | 38.0859 | - | 156 KB |
|
||||
| CustomTargetNameBinding | 881,022.4 ns | 12,940.79 ns | 11,471.67 ns | 85.9375 | - | 352 KB |
|
||||
| NestedTargetNameBinding | 876,321.2 ns | 7,650.01 ns | 6,781.54 ns | 85.9375 | - | 352 KB |
|
||||
| AssertHasFieldValue | 3,076,694.5 ns | 30,768.35 ns | 25,692.97 ns | 253.9063 | 7.8125 | 1,040 KB |
|
||||
| PathTokenize | 869.1 ns | 16.88 ns | 25.27 ns | 0.2632 | - | 1 KB |
|
||||
| PathExpressionBuild | 529.8 ns | 8.39 ns | 7.44 ns | 0.3500 | - | 1 KB |
|
||||
| PathExpressionGet | 358,678.4 ns | 4,814.17 ns | 4,020.05 ns | 17.0898 | - | 70 KB |
|
|
@ -59,7 +59,7 @@ nav:
|
|||
- v1: 'CHANGELOG-v1.md'
|
||||
- v0: 'CHANGELOG-v0.md'
|
||||
- Upgrade notes: upgrade-notes.md
|
||||
# - Deprecations: deprecations.md
|
||||
- Deprecations: deprecations.md
|
||||
- Support: support.md
|
||||
# - Setup:
|
||||
# - Configuring options: setup/configuring-options.md
|
||||
|
|
|
@ -24,18 +24,44 @@
|
|||
"type": "string",
|
||||
"title": "Name",
|
||||
"description": "The name of the resource. This must be unique.",
|
||||
"minLength": 3
|
||||
"$ref": "#/definitions/resourceName"
|
||||
},
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"title": "Annotations"
|
||||
"title": "Annotations",
|
||||
"description": "Additional annotations for the resource.",
|
||||
"properties": {
|
||||
"obsolete": {
|
||||
"type": "boolean",
|
||||
"title": "Obsolete",
|
||||
"description": "A common annotation that flags the resource as obsolete.",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
"defaultSnippets": [
|
||||
{
|
||||
"label": "Annotation key/ value",
|
||||
"body": {
|
||||
"${1:Key}": "${2:Value}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"title": "Tags",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"defaultSnippets": [
|
||||
{
|
||||
"label": "Tag key/ value",
|
||||
"body": {
|
||||
"${1:Key}": "${2:Value}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
@ -104,14 +130,22 @@
|
|||
"title": "Include rules",
|
||||
"description": "Rules to include by name in the baseline.",
|
||||
"markdownDescription": "Rules to include by name in the baseline. [See help](https://microsoft.github.io/PSRule/concepts/PSRule/en-US/about_PSRule_Options.html#ruleinclude)",
|
||||
"$ref": "#/definitions/rule-names"
|
||||
"items": {
|
||||
"type": "string",
|
||||
"$ref": "#/definitions/resourceName"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"exclude": {
|
||||
"type": "array",
|
||||
"title": "Exclude rules",
|
||||
"description": "Rules to exclude by name from the baseline.",
|
||||
"markdownDescription": "Rules to exclude by name from the baseline. [See help](https://microsoft.github.io/PSRule/concepts/PSRule/en-US/about_PSRule_Options.html#ruleexclude)",
|
||||
"$ref": "#/definitions/rule-names"
|
||||
"items": {
|
||||
"type": "string",
|
||||
"$ref": "#/definitions/resourceName"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"tag": {
|
||||
"type": "object",
|
||||
|
@ -243,7 +277,7 @@
|
|||
"title": "Baseline",
|
||||
"description": "The name of a baseline to use.",
|
||||
"markdownDescription": "The name of a baseline to use. [See help](https://microsoft.github.io/PSRule/concepts/PSRule/en-US/about_PSRule_Options.html#rulebaseline)",
|
||||
"$ref": "#/definitions/rule-names"
|
||||
"$ref": "#/definitions/resourceName"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
@ -251,15 +285,7 @@
|
|||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"rule-names": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"$ref": "#/definitions/rule-name"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"rule-name": {
|
||||
"resourceName": {
|
||||
"type": "string",
|
||||
"minLength": 3
|
||||
},
|
||||
|
@ -443,7 +469,7 @@
|
|||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/resource-metadata"
|
||||
"$ref": "#/definitions/ruleMetadata"
|
||||
},
|
||||
"spec": {
|
||||
"type": "object",
|
||||
|
@ -490,6 +516,68 @@
|
|||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ruleMetadata": {
|
||||
"type": "object",
|
||||
"title": "Metadata",
|
||||
"description": "Additional information to identify the resource.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "Name",
|
||||
"description": "The name of the resource. This must be unique.",
|
||||
"$ref": "#/definitions/resourceName"
|
||||
},
|
||||
"ref": {
|
||||
"title": "Reference",
|
||||
"description": "An optional stable opaque identifier of this resource for lookup. This must be unique if set.",
|
||||
"$ref": "#/definitions/resourceName"
|
||||
},
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"title": "Annotations",
|
||||
"description": "Additional annotations for the resource.",
|
||||
"additionalProperties": true,
|
||||
"defaultSnippets": [
|
||||
{
|
||||
"label": "Annotation key/ value",
|
||||
"body": {
|
||||
"${1:Key}": "${2:Value}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"alias": {
|
||||
"type": "array",
|
||||
"title": "Aliases",
|
||||
"description": "Alternative names this resource is known by.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"title": "Alias",
|
||||
"description": "An alternative name.",
|
||||
"$ref": "#/definitions/resourceName"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"title": "Tags",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"defaultSnippets": [
|
||||
{
|
||||
"label": "Tag key/ value",
|
||||
"body": {
|
||||
"${1:Key}": "${2:Value}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"selectorExpression": {
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
|
|
|
@ -142,6 +142,13 @@
|
|||
"description": "Enable or disable warnings for suppressed rules. The default is `true`.",
|
||||
"markdownDescription": "Enable or disable warnings for suppressed rules. The default is `true`. [See help](https://microsoft.github.io/PSRule/concepts/PSRule/en-US/about_PSRule_Options.html#executionsuppressedrulewarning)",
|
||||
"default": true
|
||||
},
|
||||
"aliasReferenceWarning": {
|
||||
"type": "boolean",
|
||||
"title": "Warn on resource aliases",
|
||||
"description": "Enable or disable warnings when an alias to a resource is used. The default is true.",
|
||||
"markdownDescription": "Enable or disable warnings when an alias to a resource is used. The default is `true`. [See help](https://microsoft.github.io/PSRule/concepts/PSRule/en-US/about_PSRule_Options.html#executionaliasreferencewarning)",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
@ -537,11 +544,7 @@
|
|||
},
|
||||
"execution": {
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/execution-option"
|
||||
}
|
||||
]
|
||||
"$ref": "#/definitions/execution-option"
|
||||
},
|
||||
"include": {
|
||||
"type": "object",
|
||||
|
@ -549,11 +552,7 @@
|
|||
},
|
||||
"input": {
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/input-option"
|
||||
}
|
||||
]
|
||||
"$ref": "#/definitions/input-option"
|
||||
},
|
||||
"logging": {
|
||||
"type": "object",
|
||||
|
@ -621,7 +620,11 @@
|
|||
"title": "Include rules",
|
||||
"description": "Optionally filter to rules by name.",
|
||||
"markdownDescription": "Optionally filter to rules by name. [See help](https://microsoft.github.io/PSRule/concepts/PSRule/en-US/about_PSRule_Options.html#ruleinclude)",
|
||||
"$ref": "#/definitions/rule-names"
|
||||
"items": {
|
||||
"type": "string",
|
||||
"$ref": "#/definitions/resourceName"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"includeLocal": {
|
||||
"type": "boolean",
|
||||
|
@ -635,7 +638,11 @@
|
|||
"title": "Exclude rules",
|
||||
"description": "Specifies rules to exclude by name.",
|
||||
"markdownDescription": "Specifies rules to exclude by name. [See help](https://microsoft.github.io/PSRule/concepts/PSRule/en-US/about_PSRule_Options.html#ruleexclude)",
|
||||
"$ref": "#/definitions/rule-names"
|
||||
"items": {
|
||||
"type": "string",
|
||||
"$ref": "#/definitions/resourceName"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"tag": {
|
||||
"type": "object",
|
||||
|
@ -662,15 +669,7 @@
|
|||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"rule-names": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"$ref": "#/definitions/rule-name"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"rule-name": {
|
||||
"resourceName": {
|
||||
"type": "string",
|
||||
"minLength": 3
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Management.Automation;
|
||||
|
@ -18,6 +18,7 @@ namespace PSRule.Commands
|
|||
|
||||
[Parameter(Mandatory = true, Position = 0)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[ValidateLength(3, 128)]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -83,7 +84,8 @@ namespace PSRule.Commands
|
|||
begin: ConventionBlock(context, Begin, RunspaceScope.ConventionBegin),
|
||||
process: ConventionBlock(context, Process, RunspaceScope.ConventionProcess),
|
||||
end: ConventionBlock(context, End, RunspaceScope.ConventionEnd),
|
||||
errorPreference: errorPreference
|
||||
errorPreference: errorPreference,
|
||||
flags: ResourceFlags.None
|
||||
);
|
||||
#pragma warning restore CA2000 // Dispose objects before losing scope, needs to be passed to pipeline
|
||||
WriteObject(block);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
@ -30,6 +30,7 @@ namespace PSRule.Commands
|
|||
/// </summary>
|
||||
[Parameter(Mandatory = true, Position = 0)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[ValidateLength(3, 128)]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -75,6 +76,16 @@ namespace PSRule.Commands
|
|||
[Parameter(Mandatory = false)]
|
||||
public Hashtable Configure { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Any aliases for the rule.
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false)]
|
||||
public string[] Alias { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false)]
|
||||
[ValidateLength(3, 128)]
|
||||
public string Ref { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!IsSourceScope())
|
||||
|
@ -89,28 +100,33 @@ namespace PSRule.Commands
|
|||
file: source.Path,
|
||||
startLineNumber: Body.Ast.Extent.StartLineNumber
|
||||
);
|
||||
var flags = ResourceFlags.None;
|
||||
|
||||
context.VerboseFoundResource(name: Name, moduleName: source.ModuleName, scriptName: MyInvocation.ScriptName);
|
||||
|
||||
CheckDependsOn();
|
||||
var ps = GetCondition(context, errorPreference);
|
||||
var helpInfo = PSRule.Host.HostHelper.GetHelpInfo(context, Name, metadata.Synopsis) ?? new RuleHelpInfo(
|
||||
var info = PSRule.Host.HostHelper.GetHelpInfo(context, Name, metadata.Synopsis) ?? new RuleHelpInfo(
|
||||
name: Name,
|
||||
displayName: Name,
|
||||
moduleName: source.ModuleName
|
||||
);
|
||||
|
||||
var id = new ResourceId(source.ModuleName, Name, ResourceIdKind.Id);
|
||||
|
||||
#pragma warning disable CA2000 // Dispose objects before losing scope, needs to be passed to pipeline
|
||||
var block = new RuleBlock(
|
||||
source: source,
|
||||
ruleName: Name,
|
||||
info: helpInfo,
|
||||
id: id,
|
||||
@ref: ResourceHelper.GetIdNullable(source.ModuleName, Ref, ResourceIdKind.Ref),
|
||||
info: info,
|
||||
condition: ps,
|
||||
tag: tag,
|
||||
dependsOn: ResourceHelper.GetRuleIdStrings(source.ModuleName, DependsOn),
|
||||
alias: ResourceHelper.GetRuleId(source.ModuleName, Alias, ResourceIdKind.Alias),
|
||||
dependsOn: ResourceHelper.GetRuleId(source.ModuleName, DependsOn, ResourceIdKind.Unknown),
|
||||
configuration: Configure,
|
||||
extent: extent,
|
||||
errorPreference: errorPreference
|
||||
flags: flags
|
||||
);
|
||||
#pragma warning restore CA2000 // Dispose objects before losing scope, needs to be passed to pipeline
|
||||
WriteObject(block);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using PSRule.Definitions;
|
||||
using PSRule.Definitions.Baselines;
|
||||
|
||||
|
@ -27,5 +28,15 @@ namespace PSRule
|
|||
{
|
||||
return string.IsNullOrEmpty(resource.Module);
|
||||
}
|
||||
|
||||
internal static IEnumerable<ResourceId> GetIds(this IResource resource)
|
||||
{
|
||||
yield return resource.Id;
|
||||
if (resource.Ref.HasValue)
|
||||
yield return resource.Ref.Value;
|
||||
|
||||
for (var i = 0; resource.Alias != null && i < resource.Alias.Length; i++)
|
||||
yield return resource.Alias[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using PSRule.Configuration;
|
||||
using PSRule.Definitions;
|
||||
using PSRule.Pipeline;
|
||||
using PSRule.Resources;
|
||||
using PSRule.Runtime;
|
||||
|
||||
namespace PSRule
|
||||
{
|
||||
internal static class RunspaceContextExtensions
|
||||
{
|
||||
private const string WARN_KEY_PROPERTY = "Property";
|
||||
|
||||
public static void WarnResourceObsolete(this RunspaceContext context, ResourceKind kind, string id)
|
||||
{
|
||||
if (context.Writer == null || !context.Writer.ShouldWriteWarning())
|
||||
return;
|
||||
|
||||
context.Writer.WriteWarning(PSRuleResources.ResourceObsolete, Enum.GetName(typeof(ResourceKind), kind), id);
|
||||
}
|
||||
|
||||
public static void WarnPropertyObsolete(this RunspaceContext context, string variableName, string propertyName)
|
||||
{
|
||||
context.DebugPropertyObsolete(variableName, propertyName);
|
||||
if (context.Writer == null || !context.Writer.ShouldWriteWarning() || !context.ShouldWarnOnce(WARN_KEY_PROPERTY, variableName, propertyName))
|
||||
return;
|
||||
|
||||
context.Writer.WriteWarning(PSRuleResources.PropertyObsolete, variableName, propertyName);
|
||||
}
|
||||
|
||||
public static void WarnRuleNotFound(this RunspaceContext context)
|
||||
{
|
||||
if (context.Writer == null || !context.Writer.ShouldWriteWarning())
|
||||
return;
|
||||
|
||||
context.Writer.WriteWarning(PSRuleResources.RuleNotFound);
|
||||
}
|
||||
|
||||
public static void DebugPropertyObsolete(this RunspaceContext context, string variableName, string propertyName)
|
||||
{
|
||||
if (context.Writer == null || !context.Writer.ShouldWriteDebug())
|
||||
return;
|
||||
|
||||
context.Writer.WriteDebug(PSRuleResources.DebugPropertyObsolete, context.RuleBlock.Name, variableName, propertyName);
|
||||
}
|
||||
|
||||
public static void WarnAliasReference(this RunspaceContext context, ResourceKind kind, string resourceId, string targetId, string alias)
|
||||
{
|
||||
if (context.Writer == null || !context.Writer.ShouldWriteWarning() || !context.Pipeline.Option.Execution.AliasReferenceWarning.GetValueOrDefault(ExecutionOption.Default.AliasReferenceWarning.Value))
|
||||
return;
|
||||
|
||||
context.Writer.WriteWarning(PSRuleResources.AliasReference, kind.ToString(), resourceId, targetId, alias);
|
||||
}
|
||||
|
||||
public static void WarnAliasSuppression(this RunspaceContext context, string targetId, string alias)
|
||||
{
|
||||
if (context.Writer == null || !context.Writer.ShouldWriteWarning() || !context.Pipeline.Option.Execution.AliasReferenceWarning.GetValueOrDefault(ExecutionOption.Default.AliasReferenceWarning.Value))
|
||||
return;
|
||||
|
||||
context.Writer.WriteWarning(PSRuleResources.AliasSuppression, targetId, alias);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
@ -16,9 +16,11 @@ namespace PSRule.Configuration
|
|||
private const bool DEFAULT_INCONCLUSIVEWARNING = true;
|
||||
private const bool DEFAULT_NOTPROCESSEDWARNING = true;
|
||||
private const bool DEFAULT_SUPPRESSEDRULEWARNING = true;
|
||||
private const bool DEFAULT_ALIASREFERENCEWARNING = true;
|
||||
|
||||
internal static readonly ExecutionOption Default = new ExecutionOption
|
||||
{
|
||||
AliasReferenceWarning = DEFAULT_ALIASREFERENCEWARNING,
|
||||
LanguageMode = DEFAULT_LANGUAGEMODE,
|
||||
InconclusiveWarning = DEFAULT_INCONCLUSIVEWARNING,
|
||||
NotProcessedWarning = DEFAULT_NOTPROCESSEDWARNING,
|
||||
|
@ -27,6 +29,7 @@ namespace PSRule.Configuration
|
|||
|
||||
public ExecutionOption()
|
||||
{
|
||||
AliasReferenceWarning = null;
|
||||
LanguageMode = null;
|
||||
InconclusiveWarning = null;
|
||||
NotProcessedWarning = null;
|
||||
|
@ -38,6 +41,7 @@ namespace PSRule.Configuration
|
|||
if (option == null)
|
||||
return;
|
||||
|
||||
AliasReferenceWarning = option.AliasReferenceWarning;
|
||||
LanguageMode = option.LanguageMode;
|
||||
InconclusiveWarning = option.InconclusiveWarning;
|
||||
NotProcessedWarning = option.NotProcessedWarning;
|
||||
|
@ -52,6 +56,7 @@ namespace PSRule.Configuration
|
|||
public bool Equals(ExecutionOption other)
|
||||
{
|
||||
return other != null &&
|
||||
AliasReferenceWarning == other.AliasReferenceWarning &&
|
||||
LanguageMode == other.LanguageMode &&
|
||||
InconclusiveWarning == other.InconclusiveWarning &&
|
||||
NotProcessedWarning == other.NotProcessedWarning &&
|
||||
|
@ -63,6 +68,7 @@ namespace PSRule.Configuration
|
|||
unchecked // Overflow is fine
|
||||
{
|
||||
var hash = 17;
|
||||
hash = hash * 23 + (AliasReferenceWarning.HasValue ? AliasReferenceWarning.Value.GetHashCode() : 0);
|
||||
hash = hash * 23 + (LanguageMode.HasValue ? LanguageMode.Value.GetHashCode() : 0);
|
||||
hash = hash * 23 + (InconclusiveWarning.HasValue ? InconclusiveWarning.Value.GetHashCode() : 0);
|
||||
hash = hash * 23 + (NotProcessedWarning.HasValue ? NotProcessedWarning.Value.GetHashCode() : 0);
|
||||
|
@ -75,32 +81,54 @@ namespace PSRule.Configuration
|
|||
{
|
||||
var result = new ExecutionOption(o1)
|
||||
{
|
||||
AliasReferenceWarning = o1.AliasReferenceWarning ?? o2.AliasReferenceWarning,
|
||||
LanguageMode = o1.LanguageMode ?? o2.LanguageMode,
|
||||
InconclusiveWarning = o1.InconclusiveWarning ?? o2.InconclusiveWarning,
|
||||
NotProcessedWarning = o1.NotProcessedWarning ?? o2.NotProcessedWarning,
|
||||
SuppressedRuleWarning = o1.SuppressedRuleWarning ?? o2.SuppressedRuleWarning
|
||||
SuppressedRuleWarning = o1.SuppressedRuleWarning ?? o2.SuppressedRuleWarning,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a warning is raised when an alias to a resource is used.
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
public bool? AliasReferenceWarning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The langauge mode to execute PowerShell code with.
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
public LanguageMode? LanguageMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a warning is raised when a rule does not return pass or fail.
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
public bool? InconclusiveWarning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a warning is raised when an object is not processed by any rule.
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
public bool? NotProcessedWarning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a warning is raised when a rule is suppressed.
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
public bool? SuppressedRuleWarning { get; set; }
|
||||
|
||||
internal void Load(EnvironmentHelper env)
|
||||
{
|
||||
if (env.TryBool("PSRULE_EXECUTION_ALIASREFERENCEWARNING", out var bvalue))
|
||||
AliasReferenceWarning = bvalue;
|
||||
|
||||
if (env.TryEnum("PSRULE_EXECUTION_LANGUAGEMODE", out LanguageMode languageMode))
|
||||
LanguageMode = languageMode;
|
||||
|
||||
if (env.TryBool("PSRULE_EXECUTION_INCONCLUSIVEWARNING", out var bvalue))
|
||||
if (env.TryBool("PSRULE_EXECUTION_INCONCLUSIVEWARNING", out bvalue))
|
||||
InconclusiveWarning = bvalue;
|
||||
|
||||
if (env.TryBool("PSRULE_EXECUTION_NOTPROCESSEDWARNING", out bvalue))
|
||||
|
@ -112,10 +140,13 @@ namespace PSRule.Configuration
|
|||
|
||||
internal void Load(Dictionary<string, object> index)
|
||||
{
|
||||
if (index.TryPopBool("Execution.AliasReferenceWarning", out var bvalue))
|
||||
AliasReferenceWarning = bvalue;
|
||||
|
||||
if (index.TryPopEnum("Execution.LanguageMode", out LanguageMode languageMode))
|
||||
LanguageMode = languageMode;
|
||||
|
||||
if (index.TryPopBool("Execution.InconclusiveWarning", out var bvalue))
|
||||
if (index.TryPopBool("Execution.InconclusiveWarning", out bvalue))
|
||||
InconclusiveWarning = bvalue;
|
||||
|
||||
if (index.TryPopBool("Execution.NotProcessedWarning", out bvalue))
|
||||
|
|
|
@ -34,7 +34,11 @@ namespace PSRule.Definitions.Conventions
|
|||
|
||||
public bool Match(IResource resource)
|
||||
{
|
||||
return _Include != null && (_Include.Contains(resource.Name) || _Include.Contains(resource.Id) || MatchWildcard(resource.Name) || MatchWildcard(resource.Id));
|
||||
return _Include != null &&
|
||||
(_Include.Contains(resource.Name) ||
|
||||
_Include.Contains(resource.Id.Value) ||
|
||||
MatchWildcard(resource.Name) ||
|
||||
MatchWildcard(resource.Id.Value));
|
||||
}
|
||||
|
||||
private bool MatchWildcard(string name)
|
||||
|
@ -50,13 +54,16 @@ namespace PSRule.Definitions.Conventions
|
|||
{
|
||||
Source = source;
|
||||
Name = name;
|
||||
Id = ResourceHelper.GetIdString(Source.ModuleName, name);
|
||||
Id = new ResourceId(Source.ModuleName, name, ResourceIdKind.Id);
|
||||
}
|
||||
|
||||
public SourceFile Source { get; }
|
||||
|
||||
public string Id { get; }
|
||||
public ResourceId Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the convetion.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
public string SourcePath => Source.Path;
|
||||
|
|
|
@ -18,7 +18,16 @@ namespace PSRule.Definitions.Conventions
|
|||
|
||||
private bool _Disposed;
|
||||
|
||||
internal ScriptBlockConvention(SourceFile source, ResourceMetadata metadata, ResourceHelpInfo info, LanguageScriptBlock begin, LanguageScriptBlock initialize, LanguageScriptBlock process, LanguageScriptBlock end, ActionPreference errorPreference)
|
||||
internal ScriptBlockConvention(
|
||||
SourceFile source,
|
||||
ResourceMetadata metadata,
|
||||
ResourceHelpInfo info,
|
||||
LanguageScriptBlock begin,
|
||||
LanguageScriptBlock initialize,
|
||||
LanguageScriptBlock process,
|
||||
LanguageScriptBlock end,
|
||||
ActionPreference errorPreference,
|
||||
ResourceFlags flags)
|
||||
: base(source, metadata.Name)
|
||||
{
|
||||
Info = info;
|
||||
|
@ -26,16 +35,24 @@ namespace PSRule.Definitions.Conventions
|
|||
_Begin = begin;
|
||||
_Process = process;
|
||||
_End = end;
|
||||
Flags = flags;
|
||||
}
|
||||
|
||||
public ResourceHelpInfo Info { get; }
|
||||
|
||||
public ResourceFlags Flags { get; }
|
||||
|
||||
ResourceKind IResource.Kind => ResourceKind.Convention;
|
||||
|
||||
string IResource.ApiVersion => Specs.V1;
|
||||
|
||||
string IResource.Name => Name;
|
||||
// Not supported with conventions.
|
||||
ResourceId? IResource.Ref => null;
|
||||
|
||||
// Not supported with conventions.
|
||||
ResourceId[] IResource.Alias => null;
|
||||
|
||||
// Not supported with conventions.
|
||||
ResourceTags IResource.Tags => null;
|
||||
|
||||
public override void Initialize(RunspaceContext context, IEnumerable input)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PSRule.Host
|
||||
namespace PSRule.Definitions
|
||||
{
|
||||
internal sealed class DependencyGraph<T> : IDisposable where T : IDependencyTarget
|
||||
{
|
||||
|
@ -86,7 +86,7 @@ namespace PSRule.Host
|
|||
// Process each dependency
|
||||
for (var d = 0; d < target.Value.DependsOn.Length; d++)
|
||||
{
|
||||
var dTarget = _Index[target.Value.DependsOn[d]];
|
||||
var dTarget = _Index[target.Value.DependsOn[d].Value];
|
||||
|
||||
// Check if dependency was already completed
|
||||
if (dTarget.Passed)
|
||||
|
@ -105,12 +105,18 @@ namespace PSRule.Host
|
|||
}
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetAll()
|
||||
{
|
||||
for (var i = 0; i < _Targets.Length; i++)
|
||||
yield return _Targets[i].Value;
|
||||
}
|
||||
|
||||
private void Prepare(T[] targets)
|
||||
{
|
||||
for (var i = 0; i < targets.Length; i++)
|
||||
{
|
||||
_Targets[i] = new DependencyTarget(this, targets[i]);
|
||||
_Index.Add(targets[i].RuleId, _Targets[i]);
|
||||
_Index.Add(targets[i].Id.Value, _Targets[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using PSRule.Pipeline;
|
||||
using PSRule.Resources;
|
||||
using PSRule.Runtime;
|
||||
|
||||
namespace PSRule.Definitions
|
||||
{
|
||||
internal sealed class DependencyGraphBuilder<T> where T : IDependencyTarget
|
||||
{
|
||||
private readonly RunspaceContext _Context;
|
||||
private readonly IEqualityComparer<ResourceId> _Comparer;
|
||||
private readonly Dictionary<ResourceId, T> _Targets;
|
||||
private readonly Stack<ResourceId> _Stack;
|
||||
private readonly bool _IncludeDependencies;
|
||||
private readonly bool _IncludeDisabled;
|
||||
|
||||
public DependencyGraphBuilder(RunspaceContext context, bool includeDependencies, bool includeDisabled)
|
||||
{
|
||||
_Context = context;
|
||||
_Comparer = ResourceIdEqualityComparer.Default;
|
||||
_Targets = new Dictionary<ResourceId, T>(_Comparer);
|
||||
_Stack = new Stack<ResourceId>();
|
||||
_IncludeDependencies = includeDependencies;
|
||||
_IncludeDisabled = includeDisabled;
|
||||
}
|
||||
|
||||
public void Include(DependencyTargetCollection<T> index, Func<T, bool> filter)
|
||||
{
|
||||
// Include any matching items
|
||||
foreach (var item in index.GetAll())
|
||||
{
|
||||
if (item.Dependency)
|
||||
continue;
|
||||
|
||||
if (filter == null || filter(item))
|
||||
Include(index, item, parentId: null);
|
||||
}
|
||||
}
|
||||
|
||||
public DependencyGraph<T> Build()
|
||||
{
|
||||
return new DependencyGraph<T>(GetItems());
|
||||
}
|
||||
|
||||
public T[] GetItems()
|
||||
{
|
||||
return _Targets.Values.ToArray();
|
||||
}
|
||||
|
||||
private void Include(DependencyTargetCollection<T> index, T item, ResourceId? parentId)
|
||||
{
|
||||
// Check that the item is not already in the list of targets
|
||||
if (_Targets.ContainsKey(item.Id))
|
||||
return;
|
||||
|
||||
// Check for circular dependencies
|
||||
if (_Stack.Contains(item.Id, _Comparer))
|
||||
throw new RuleException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.DependencyCircularReference, parentId, item.Id));
|
||||
|
||||
try
|
||||
{
|
||||
_Stack.Push(item.Id);
|
||||
|
||||
// Check for dependencies
|
||||
if (item.DependsOn != null && _IncludeDependencies)
|
||||
{
|
||||
foreach (var d in item.DependsOn)
|
||||
{
|
||||
if (!index.TryGet(d, out var dep, out var kind))
|
||||
throw new RuleException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.DependencyNotFound, d, item.Id));
|
||||
|
||||
if (_Context != null && kind == ResourceIdKind.Alias)
|
||||
_Context.WarnAliasReference(ResourceKind.Rule, item.Id.Value, dep.Id.Value, d.Value);
|
||||
|
||||
// Handle dependencies
|
||||
if (!_Targets.ContainsKey(dep.Id))
|
||||
Include(index, dep, parentId: item.Id);
|
||||
}
|
||||
}
|
||||
_Targets.Add(key: item.Id, value: item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_Stack.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PSRule.Definitions
|
||||
{
|
||||
internal sealed class DependencyTargetCollection<T> where T : IDependencyTarget
|
||||
{
|
||||
private readonly Dictionary<ResourceId, TargetLink> _Index;
|
||||
private readonly List<T> _Items;
|
||||
|
||||
public DependencyTargetCollection()
|
||||
{
|
||||
_Items = new List<T>();
|
||||
_Index = new Dictionary<ResourceId, TargetLink>(ResourceIdEqualityComparer.Default);
|
||||
}
|
||||
|
||||
private sealed class TargetLink
|
||||
{
|
||||
public readonly T Link;
|
||||
public readonly ResourceIdKind Kind;
|
||||
|
||||
public TargetLink(T link, ResourceIdKind kind)
|
||||
{
|
||||
Link = link;
|
||||
Kind = kind;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(ResourceId id)
|
||||
{
|
||||
return _Index.ContainsKey(id);
|
||||
}
|
||||
|
||||
public bool TryGet(ResourceId id, out T value, out ResourceIdKind kind)
|
||||
{
|
||||
value = default;
|
||||
kind = default;
|
||||
if (!_Index.TryGetValue(id, out var link))
|
||||
return false;
|
||||
|
||||
value = link.Link;
|
||||
kind = link.Kind;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryAdd(T target)
|
||||
{
|
||||
if (_Index.ContainsKey(target.Id) || (target.Ref.HasValue && _Index.ContainsKey(target.Ref.Value)))
|
||||
return false;
|
||||
|
||||
for (var i = 0; target.Alias != null && i < target.Alias.Length; i++)
|
||||
if (_Index.ContainsKey(target.Alias[i]))
|
||||
return false;
|
||||
|
||||
// Add Id, Ref, and aliases to the index.
|
||||
_Index.Add(target.Id, new TargetLink(target, ResourceIdKind.Id));
|
||||
if (target.Ref.HasValue)
|
||||
_Index.Add(target.Ref.Value, new TargetLink(target, ResourceIdKind.Ref));
|
||||
|
||||
for (var i = 0; target.Alias != null && i < target.Alias.Length; i++)
|
||||
_Index.Add(target.Alias[i], new TargetLink(target, ResourceIdKind.Alias));
|
||||
|
||||
_Items.Add(target);
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetAll()
|
||||
{
|
||||
return _Items;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
|
||||
namespace PSRule.Definitions
|
||||
{
|
||||
|
@ -17,5 +18,7 @@ namespace PSRule.Definitions
|
|||
public interface ICondition : IDisposable
|
||||
{
|
||||
IConditionResult If();
|
||||
|
||||
ActionPreference ErrorAction { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections;
|
||||
using PSRule.Host;
|
||||
using PSRule.Runtime;
|
||||
|
||||
namespace PSRule.Definitions
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace PSRule.Definitions
|
||||
{
|
||||
public interface IDependencyTarget
|
||||
{
|
||||
ResourceId Id { get; }
|
||||
|
||||
ResourceId? Ref { get; }
|
||||
|
||||
ResourceId[] Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Resources this target depends on.
|
||||
/// </summary>
|
||||
ResourceId[] DependsOn { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the source was imported as a dependency.
|
||||
/// </summary>
|
||||
bool Dependency { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace PSRule.Definitions
|
||||
{
|
||||
public interface ILanguageBlock
|
||||
{
|
||||
ResourceId Id { get; }
|
||||
|
||||
string SourcePath { get; }
|
||||
|
||||
string Module { get; }
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using PSRule.Host;
|
||||
using PSRule.Pipeline;
|
||||
using PSRule.Runtime;
|
||||
using YamlDotNet.Core;
|
||||
|
@ -48,15 +47,50 @@ namespace PSRule.Definitions
|
|||
Convention = 5
|
||||
}
|
||||
|
||||
internal interface IResource : ILanguageBlock
|
||||
[Flags]
|
||||
public enum ResourceFlags
|
||||
{
|
||||
None = 0,
|
||||
|
||||
Obsolete = 1
|
||||
}
|
||||
|
||||
public interface IResource : ILanguageBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of resource.
|
||||
/// </summary>
|
||||
ResourceKind Kind { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The API version of the resource.
|
||||
/// </summary>
|
||||
string ApiVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the resource.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An optional reference identifer for the resource.
|
||||
/// </summary>
|
||||
ResourceId? Ref { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Any additional aliases for the resource.
|
||||
/// </summary>
|
||||
ResourceId[] Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Any resource tags.
|
||||
/// </summary>
|
||||
ResourceTags Tags { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Flags for the resource.
|
||||
/// </summary>
|
||||
ResourceFlags Flags { get; }
|
||||
}
|
||||
|
||||
internal abstract class ResourceRef
|
||||
|
@ -253,11 +287,24 @@ namespace PSRule.Definitions
|
|||
Tags = new ResourceTags();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the resource.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Ref { get; set; }
|
||||
|
||||
public string[] Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Any resource annotations.
|
||||
/// </summary>
|
||||
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitEmptyCollections)]
|
||||
public ResourceAnnotations Annotations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Any resource tags.
|
||||
/// </summary>
|
||||
[YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitEmptyCollections)]
|
||||
public ResourceTags Tags { get; set; }
|
||||
}
|
||||
|
@ -289,14 +336,17 @@ namespace PSRule.Definitions
|
|||
Info = info;
|
||||
Source = source;
|
||||
Spec = spec;
|
||||
Id = ResourceHelper.GetIdString(source.ModuleName, metadata.Name);
|
||||
Metadata = metadata;
|
||||
Name = metadata.Name;
|
||||
Id = new ResourceId(source.ModuleName, Name, ResourceIdKind.Id);
|
||||
}
|
||||
|
||||
[YamlIgnore()]
|
||||
public readonly string Id;
|
||||
public ResourceId Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the resource.
|
||||
/// </summary>
|
||||
[YamlIgnore()]
|
||||
public string Name { get; }
|
||||
|
||||
|
@ -309,10 +359,16 @@ namespace PSRule.Definitions
|
|||
[YamlIgnore()]
|
||||
public readonly ResourceHelpInfo Info;
|
||||
|
||||
/// <summary>
|
||||
/// Resource metadata details.
|
||||
/// </summary>
|
||||
public ResourceMetadata Metadata { get; }
|
||||
|
||||
public ResourceKind Kind { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The API version of the resource.
|
||||
/// </summary>
|
||||
public string ApiVersion { get; }
|
||||
|
||||
public TSpec Spec { get; }
|
||||
|
@ -327,12 +383,14 @@ namespace PSRule.Definitions
|
|||
{
|
||||
_Annotations = new Dictionary<Type, ResourceAnnotation>();
|
||||
Obsolete = ResourceHelper.IsObsolete(metadata);
|
||||
Flags |= ResourceHelper.IsObsolete(metadata) ? ResourceFlags.Obsolete : ResourceFlags.None;
|
||||
}
|
||||
|
||||
[YamlIgnore()]
|
||||
internal readonly bool Obsolete;
|
||||
|
||||
string ILanguageBlock.Id => Id;
|
||||
[YamlIgnore()]
|
||||
internal ResourceFlags Flags { get; }
|
||||
|
||||
string ILanguageBlock.SourcePath => Source.Path;
|
||||
|
||||
|
@ -344,8 +402,16 @@ namespace PSRule.Definitions
|
|||
|
||||
string IResource.Name => Name;
|
||||
|
||||
// Not supported with base resources.
|
||||
ResourceId? IResource.Ref => null;
|
||||
|
||||
// Not supported with base resources.
|
||||
ResourceId[] IResource.Alias => null;
|
||||
|
||||
ResourceTags IResource.Tags => Metadata.Tags;
|
||||
|
||||
ResourceFlags IResource.Flags => Flags;
|
||||
|
||||
TAnnotation IAnnotated<ResourceAnnotation>.GetAnnotation<TAnnotation>()
|
||||
{
|
||||
return _Annotations.TryGetValue(typeof(TAnnotation), out var annotation) ? (TAnnotation)annotation : null;
|
||||
|
@ -361,14 +427,17 @@ namespace PSRule.Definitions
|
|||
{
|
||||
private const string ANNOTATION_OBSOLETE = "obsolete";
|
||||
|
||||
private const char ID_SEPARATOR = '\\';
|
||||
private const char SCOPE_SEPARATOR = '\\';
|
||||
|
||||
internal static string GetIdString(string scope, string name)
|
||||
{
|
||||
if (name.IndexOf(ID_SEPARATOR) >= 0)
|
||||
return name;
|
||||
|
||||
return string.Concat(string.IsNullOrEmpty(scope) ? LanguageScope.STANDALONE_SCOPENAME : scope, ID_SEPARATOR, name);
|
||||
return name.IndexOf(SCOPE_SEPARATOR) >= 0
|
||||
? name
|
||||
: string.Concat(
|
||||
string.IsNullOrEmpty(scope) ? LanguageScope.STANDALONE_SCOPENAME : scope,
|
||||
SCOPE_SEPARATOR,
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
internal static void ParseIdString(string defaultScope, string id, out string scope, out string name)
|
||||
|
@ -388,9 +457,9 @@ namespace PSRule.Definitions
|
|||
if (string.IsNullOrEmpty(id))
|
||||
return;
|
||||
|
||||
var index = id.IndexOf(ID_SEPARATOR);
|
||||
scope = index >= 0 ? id.Substring(0, index) : null;
|
||||
name = id.Substring(index + 1);
|
||||
var scopeSeparator = id.IndexOf(SCOPE_SEPARATOR);
|
||||
scope = scopeSeparator >= 0 ? id.Substring(0, scopeSeparator) : null;
|
||||
name = id.Substring(scopeSeparator + 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -398,29 +467,21 @@ namespace PSRule.Definitions
|
|||
/// </summary>
|
||||
/// <param name="name">An array of names. Qualified names (RuleIds) supplied are left intact.</param>
|
||||
/// <returns>An array of RuleIds.</returns>
|
||||
internal static string[] GetRuleIdStrings(string scope, string[] name)
|
||||
internal static ResourceId[] GetRuleId(string defaultScope, string[] name, ResourceIdKind kind)
|
||||
{
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
var result = new string[name.Length];
|
||||
name.CopyTo(result, 0);
|
||||
|
||||
var result = new ResourceId[name.Length];
|
||||
for (var i = 0; i < name.Length; i++)
|
||||
{
|
||||
if (name[i] == null)
|
||||
continue;
|
||||
result[i] = name[i].IndexOf(SCOPE_SEPARATOR) > 0 ? ResourceId.Parse(name[i], kind) : new ResourceId(defaultScope, name[i], kind);
|
||||
|
||||
// The name is not already qualified
|
||||
if (name[i].IndexOf(ID_SEPARATOR) == -1)
|
||||
result[i] = GetRuleIdString(scope, name[i]);
|
||||
}
|
||||
return (result.Length == 0) ? null : result;
|
||||
}
|
||||
|
||||
internal static string GetRuleIdString(string scope, string name)
|
||||
internal static ResourceId? GetIdNullable(string scope, string name, ResourceIdKind kind)
|
||||
{
|
||||
return (scope == null) ? name : string.Concat(scope, ID_SEPARATOR, name);
|
||||
return string.IsNullOrEmpty(name) ? null : (ResourceId?)new ResourceId(scope, name, kind);
|
||||
}
|
||||
|
||||
internal static bool IsObsolete(ResourceMetadata metadata)
|
||||
|
@ -431,37 +492,4 @@ namespace PSRule.Definitions
|
|||
&& obsolete.GetValueOrDefault(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ResourceIdEqualityComparer : EqualityComparer<string>
|
||||
{
|
||||
public override bool Equals(string x, string y)
|
||||
{
|
||||
return IdEquals(x, y);
|
||||
}
|
||||
|
||||
public override int GetHashCode(string obj)
|
||||
{
|
||||
ResourceHelper.ParseIdString(obj, out var scope, out var name);
|
||||
unchecked // Overflow is fine
|
||||
{
|
||||
var hash = 17;
|
||||
hash = hash * 23 + (scope != null ? scope.GetHashCode() : 0);
|
||||
hash = hash * 23 + (name != null ? name.GetHashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IdEquals(string x, string y)
|
||||
{
|
||||
ResourceHelper.ParseIdString(x, out var scope_x, out var name_x);
|
||||
ResourceHelper.ParseIdString(y, out var scope_y, out var name_y);
|
||||
return EqualOrNull(scope_x, scope_y) &&
|
||||
EqualOrNull(name_x, name_y);
|
||||
}
|
||||
|
||||
private static bool EqualOrNull(string x, string y)
|
||||
{
|
||||
return x == null || y == null || StringComparer.OrdinalIgnoreCase.Equals(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using PSRule.Runtime;
|
||||
|
||||
namespace PSRule.Definitions
|
||||
{
|
||||
/// <summary>
|
||||
/// Additional information about the type of identifier if available.
|
||||
/// </summary>
|
||||
internal enum ResourceIdKind
|
||||
{
|
||||
None = 0,
|
||||
|
||||
Unknown = 1,
|
||||
|
||||
Id = 2,
|
||||
|
||||
Ref = 3,
|
||||
|
||||
Alias = 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A unique identifier for a resource.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Value}")]
|
||||
public struct ResourceId
|
||||
{
|
||||
private const char SCOPE_SEPARATOR = '\\';
|
||||
|
||||
private readonly int _HashCode;
|
||||
|
||||
private ResourceId(string id, string scope, string name, ResourceIdKind kind)
|
||||
{
|
||||
Value = id;
|
||||
Scope = scope;
|
||||
Name = name;
|
||||
Kind = kind;
|
||||
_HashCode = GetHashCode(id);
|
||||
}
|
||||
|
||||
internal ResourceId(string scope, string name, ResourceIdKind kind)
|
||||
: this(GetIdString(scope, name), NormalScope(scope), name, kind) { }
|
||||
|
||||
public string Value { get; }
|
||||
|
||||
public string Scope { get; }
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
internal ResourceIdKind Kind { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _HashCode;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ResourceId id && Equals(id);
|
||||
}
|
||||
|
||||
public bool Equals(ResourceId id)
|
||||
{
|
||||
return _HashCode == id._HashCode &&
|
||||
EqualOrNull(Scope, id.Scope) &&
|
||||
EqualOrNull(Name, id.Name);
|
||||
}
|
||||
|
||||
public bool Equals(string id)
|
||||
{
|
||||
return TryParse(id, out var scope, out var name) &&
|
||||
EqualOrNull(Scope, scope) &&
|
||||
EqualOrNull(Name, name);
|
||||
}
|
||||
|
||||
public static bool operator ==(ResourceId left, ResourceId right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(ResourceId left, ResourceId right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
private static bool EqualOrNull(string x, string y)
|
||||
{
|
||||
return x == null || y == null || StringComparer.OrdinalIgnoreCase.Equals(x, y);
|
||||
}
|
||||
|
||||
private static int GetHashCode(string id)
|
||||
{
|
||||
return id.ToLowerInvariant().GetHashCode();
|
||||
}
|
||||
|
||||
private static string GetIdString(string scope, string name)
|
||||
{
|
||||
return string.Concat(
|
||||
NormalScope(scope),
|
||||
SCOPE_SEPARATOR,
|
||||
name
|
||||
);
|
||||
}
|
||||
|
||||
internal static ResourceId Parse(string id, ResourceIdKind kind = ResourceIdKind.Unknown)
|
||||
{
|
||||
return TryParse(id, kind, out var value) ? value.Value : default;
|
||||
}
|
||||
|
||||
private static bool TryParse(string id, ResourceIdKind kind, out ResourceId? value)
|
||||
{
|
||||
value = null;
|
||||
if (string.IsNullOrEmpty(id) || !TryParse(id, out var scope, out var name))
|
||||
return false;
|
||||
|
||||
value = new ResourceId(id, scope, name, kind);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryParse(string id, out string scope, out string name)
|
||||
{
|
||||
scope = null;
|
||||
name = null;
|
||||
if (string.IsNullOrEmpty(id))
|
||||
return false;
|
||||
|
||||
var scopeSeparatorIndex = id.IndexOf(SCOPE_SEPARATOR);
|
||||
scope = scopeSeparatorIndex >= 0 ? id.Substring(0, scopeSeparatorIndex) : null;
|
||||
name = id.Substring(scopeSeparatorIndex + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string NormalScope(string scope)
|
||||
{
|
||||
return string.IsNullOrEmpty(scope) ? LanguageScope.STANDALONE_SCOPENAME : scope;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares to resource identifiers to determine if they are equal.
|
||||
/// </summary>
|
||||
internal sealed class ResourceIdEqualityComparer : IEqualityComparer<ResourceId>, IEqualityComparer<string>
|
||||
{
|
||||
public static ResourceIdEqualityComparer Default = new ResourceIdEqualityComparer();
|
||||
|
||||
public static bool IdEquals(ResourceId x, ResourceId y)
|
||||
{
|
||||
return EqualOrNull(x.Scope, y.Scope) &&
|
||||
EqualOrNull(x.Name, y.Name);
|
||||
}
|
||||
|
||||
public static bool IdEquals(ResourceId x, string y)
|
||||
{
|
||||
return x.Equals(y);
|
||||
}
|
||||
|
||||
public static bool IdEquals(string x, string y)
|
||||
{
|
||||
ResourceHelper.ParseIdString(x, out var scope_x, out var name_x);
|
||||
ResourceHelper.ParseIdString(y, out var scope_y, out var name_y);
|
||||
return EqualOrNull(scope_x, scope_y) &&
|
||||
EqualOrNull(name_x, name_y);
|
||||
}
|
||||
|
||||
#region IEqualityComparer<ResourceId>
|
||||
|
||||
public bool Equals(ResourceId x, ResourceId y)
|
||||
{
|
||||
return IdEquals(x, y);
|
||||
}
|
||||
|
||||
public int GetHashCode(ResourceId obj)
|
||||
{
|
||||
unchecked // Overflow is fine
|
||||
{
|
||||
var hash = 17;
|
||||
hash = hash * 23 + (obj.Scope != null ? obj.Scope.GetHashCode() : 0);
|
||||
hash = hash * 23 + (obj.Name != null ? obj.Name.GetHashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion IEqualityComparer<ResourceId>
|
||||
|
||||
#region IEqualityComparer<string>
|
||||
|
||||
public bool Equals(string x, string y)
|
||||
{
|
||||
return IdEquals(x, y);
|
||||
}
|
||||
|
||||
public int GetHashCode(string obj)
|
||||
{
|
||||
ResourceHelper.ParseIdString(obj, out var scope, out var name);
|
||||
unchecked // Overflow is fine
|
||||
{
|
||||
var hash = 17;
|
||||
hash = hash * 23 + (scope != null ? scope.GetHashCode() : 0);
|
||||
hash = hash * 23 + (name != null ? name.GetHashCode() : 0);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion IEqualityComparer<string>
|
||||
|
||||
private static bool EqualOrNull(string x, string y)
|
||||
{
|
||||
return x == null || y == null || StringComparer.OrdinalIgnoreCase.Equals(x, y);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PSRule.Definitions
|
||||
{
|
||||
internal sealed class ResourceIndex
|
||||
{
|
||||
private readonly IndexEntry[] _Items;
|
||||
|
||||
public ResourceIndex(IEnumerable<IResource> items)
|
||||
{
|
||||
_Items = Load(items);
|
||||
}
|
||||
|
||||
private sealed class IndexEntry
|
||||
{
|
||||
public readonly ResourceId Id;
|
||||
public readonly ResourceId Target;
|
||||
|
||||
public IndexEntry(ResourceId id, ResourceId target)
|
||||
{
|
||||
Id = id;
|
||||
Target = target;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryFind(string id, out ResourceId value, out ResourceIdKind kind)
|
||||
{
|
||||
value = default;
|
||||
kind = default;
|
||||
for (var i = 0; i < _Items.Length; i++)
|
||||
{
|
||||
if (_Items[i].Id.Equals(id))
|
||||
{
|
||||
value = _Items[i].Target;
|
||||
kind = _Items[i].Id.Kind;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static IndexEntry[] Load(IEnumerable<IResource> items)
|
||||
{
|
||||
var results = new List<IndexEntry>();
|
||||
foreach (var item in items)
|
||||
{
|
||||
var ids = item.GetIds();
|
||||
foreach (var id in ids)
|
||||
results.Add(new IndexEntry(id, item.Id));
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,27 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using PSRule.Definitions.Expressions;
|
||||
using PSRule.Host;
|
||||
using PSRule.Pipeline;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace PSRule.Definitions.Rules
|
||||
{
|
||||
public interface IRule
|
||||
public interface IRuleV1 : IResource, IDependencyTarget
|
||||
{
|
||||
[Obsolete("Use Name instead.")]
|
||||
string RuleName { get; }
|
||||
|
||||
string Synopsis { get; }
|
||||
|
||||
[Obsolete("Use Synopsis instead.")]
|
||||
string Description { get; }
|
||||
|
||||
ResourceTags Tag { get; }
|
||||
|
||||
SourceFile Source { get; }
|
||||
}
|
||||
|
||||
internal interface IRuleSpec
|
||||
|
@ -23,10 +34,22 @@ namespace PSRule.Definitions.Rules
|
|||
}
|
||||
|
||||
[Spec(Specs.V1, Specs.Rule)]
|
||||
internal sealed class RuleV1 : InternalResource<RuleV1Spec>, IResource
|
||||
internal sealed class RuleV1 : InternalResource<RuleV1Spec>, IResource, IRuleV1
|
||||
{
|
||||
public RuleV1(string apiVersion, SourceFile source, ResourceMetadata metadata, ResourceHelpInfo info, RuleV1Spec spec)
|
||||
: base(ResourceKind.Rule, apiVersion, source, metadata, info, spec) { }
|
||||
: base(ResourceKind.Rule, apiVersion, source, metadata, info, spec)
|
||||
{
|
||||
Ref = ResourceHelper.GetIdNullable(source.ModuleName, metadata.Ref, ResourceIdKind.Ref);
|
||||
Alias = ResourceHelper.GetRuleId(source.ModuleName, metadata.Alias, ResourceIdKind.Alias);
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
[YamlIgnore]
|
||||
public ResourceId? Ref { get; }
|
||||
|
||||
[JsonIgnore]
|
||||
[YamlIgnore]
|
||||
public ResourceId[] Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A human readable block of text, used to identify the purpose of the rule.
|
||||
|
@ -35,7 +58,26 @@ namespace PSRule.Definitions.Rules
|
|||
[YamlIgnore]
|
||||
public string Synopsis => Info.Synopsis;
|
||||
|
||||
string ILanguageBlock.Id => Name;
|
||||
ResourceId? IDependencyTarget.Ref => Ref;
|
||||
|
||||
ResourceId[] IDependencyTarget.Alias => Alias;
|
||||
|
||||
// Not supported with resource rules.
|
||||
ResourceId[] IDependencyTarget.DependsOn => Array.Empty<ResourceId>();
|
||||
|
||||
bool IDependencyTarget.Dependency => Source.IsDependency();
|
||||
|
||||
ResourceId? IResource.Ref => Ref;
|
||||
|
||||
ResourceId[] IResource.Alias => Alias;
|
||||
|
||||
string IRuleV1.RuleName => Name;
|
||||
|
||||
ResourceTags IRuleV1.Tag => Metadata.Tags;
|
||||
|
||||
SourceFile IRuleV1.Source => Source;
|
||||
|
||||
string IRuleV1.Description => Info.Synopsis;
|
||||
}
|
||||
|
||||
internal sealed class RuleV1Spec : Spec, IRuleSpec
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using PSRule.Configuration;
|
||||
using PSRule.Definitions;
|
||||
using PSRule.Resources;
|
||||
|
||||
namespace PSRule.Rules
|
||||
namespace PSRule.Definitions.Rules
|
||||
{
|
||||
/// <summary>
|
||||
/// A filter to include or exclude rules from being processed by id or tag.
|
||||
|
@ -48,7 +48,8 @@ namespace PSRule.Rules
|
|||
|
||||
internal bool Match(string name, ResourceTags tag)
|
||||
{
|
||||
return !IsExcluded(name) && IsIncluded(name, tag);
|
||||
return !IsExcluded(new ResourceId[] { ResourceId.Parse(name) }) &&
|
||||
IsIncluded(new ResourceId[] { ResourceId.Parse(name) }, tag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -57,42 +58,56 @@ namespace PSRule.Rules
|
|||
/// <returns>Return true if rule is matched, otherwise false.</returns>
|
||||
public bool Match(IResource resource)
|
||||
{
|
||||
return !IsExcluded(resource.Name) &&
|
||||
(_IncludeLocal && resource.IsLocalScope() ||
|
||||
IsIncluded(resource.Name, resource.Tags));
|
||||
var ids = resource.GetIds();
|
||||
return !IsExcluded(ids) && (_IncludeLocal && resource.IsLocalScope() || IsIncluded(ids, resource.Tags));
|
||||
}
|
||||
|
||||
private bool IsExcluded(string name)
|
||||
private bool IsExcluded(IEnumerable<ResourceId> ids)
|
||||
{
|
||||
return _Excluded != null && Contains(name, _Excluded);
|
||||
}
|
||||
if (_Excluded == null)
|
||||
return false;
|
||||
|
||||
private bool IsIncluded(string name, ResourceTags tag)
|
||||
{
|
||||
if (_Include == null || Contains(name, _Include) || MatchWildcard(name))
|
||||
foreach (var id in ids)
|
||||
{
|
||||
if (_Tag == null)
|
||||
if (Contains(id, _Excluded))
|
||||
return true;
|
||||
|
||||
if (tag == null || _Tag.Count > tag.Count)
|
||||
return false;
|
||||
|
||||
foreach (DictionaryEntry entry in _Tag)
|
||||
{
|
||||
if (!tag.Contains(entry.Key, entry.Value))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool Contains(string name, string[] set)
|
||||
private bool IsIncluded(IEnumerable<ResourceId> ids, ResourceTags tag)
|
||||
{
|
||||
foreach (var id in ids)
|
||||
{
|
||||
if (_Include == null || Contains(id, _Include) || MatchWildcard(id.Name))
|
||||
return TagEquals(tag);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TagEquals(ResourceTags tag)
|
||||
{
|
||||
if (_Tag == null)
|
||||
return true;
|
||||
|
||||
if (tag == null || _Tag.Count > tag.Count)
|
||||
return false;
|
||||
|
||||
foreach (DictionaryEntry entry in _Tag)
|
||||
{
|
||||
if (!tag.Contains(entry.Key, entry.Value))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool Contains(ResourceId id, string[] set)
|
||||
{
|
||||
for (var i = 0; set != null && i < set.Length; i++)
|
||||
if (ResourceIdEqualityComparer.IdEquals(name, set[i]))
|
||||
{
|
||||
if (ResourceIdEqualityComparer.IdEquals(id, set[i]))
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Management.Automation;
|
||||
using PSRule.Definitions.Expressions;
|
||||
using PSRule.Resources;
|
||||
using PSRule.Runtime;
|
||||
|
@ -16,6 +17,7 @@ namespace PSRule.Definitions.Rules
|
|||
|
||||
public RuleVisitor(string module, string id, IRuleSpec spec)
|
||||
{
|
||||
ErrorAction = ActionPreference.Stop;
|
||||
Module = module;
|
||||
Id = id;
|
||||
InstanceId = Guid.NewGuid();
|
||||
|
@ -32,6 +34,8 @@ namespace PSRule.Definitions.Rules
|
|||
|
||||
public string Id { get; }
|
||||
|
||||
public ActionPreference ErrorAction { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Do nothing
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using PSRule.Pipeline;
|
||||
using PSRule.Resources;
|
||||
|
||||
namespace PSRule.Host
|
||||
{
|
||||
internal sealed class DependencyGraphBuilder<T> where T : IDependencyTarget
|
||||
{
|
||||
private readonly StringComparer _Comparer;
|
||||
private readonly Dictionary<string, T> _Targets;
|
||||
private readonly Stack<string> _Stack;
|
||||
private readonly bool _IncludeDependencies;
|
||||
|
||||
public DependencyGraphBuilder(bool includeDependencies)
|
||||
{
|
||||
_Comparer = StringComparer.OrdinalIgnoreCase;
|
||||
_Targets = new Dictionary<string, T>(_Comparer);
|
||||
_Stack = new Stack<string>();
|
||||
_IncludeDependencies = includeDependencies;
|
||||
}
|
||||
|
||||
public void Include(IEnumerable<T> items, Func<T, bool> filter)
|
||||
{
|
||||
var index = new Dictionary<string, T>(_Comparer);
|
||||
|
||||
// Load items into index
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (index.ContainsKey(item.RuleId))
|
||||
throw new RuleException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.DuplicateRuleId, item.RuleId));
|
||||
|
||||
index.Add(item.RuleId, item);
|
||||
}
|
||||
|
||||
// Include any matching items
|
||||
foreach (var item in index.Values)
|
||||
{
|
||||
if (item.Dependency)
|
||||
continue;
|
||||
|
||||
if (filter == null || filter(item))
|
||||
Include(ruleId: item.RuleId, parentId: null, index: index);
|
||||
}
|
||||
}
|
||||
|
||||
public DependencyGraph<T> Build()
|
||||
{
|
||||
return new DependencyGraph<T>(_Targets.Values.ToArray());
|
||||
}
|
||||
|
||||
public T[] GetItems()
|
||||
{
|
||||
return _Targets.Values.ToArray();
|
||||
}
|
||||
|
||||
private void Include(string ruleId, string parentId, IDictionary<string, T> index)
|
||||
{
|
||||
// Check that the item is not already in the list of targets
|
||||
if (_Targets.ContainsKey(ruleId))
|
||||
return;
|
||||
|
||||
// Check for circular dependencies
|
||||
if (_Stack.Contains(value: ruleId, comparer: _Comparer))
|
||||
throw new RuleException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.DependencyCircularReference, parentId, ruleId));
|
||||
|
||||
try
|
||||
{
|
||||
_Stack.Push(item: ruleId);
|
||||
var item = index[ruleId];
|
||||
|
||||
// Check for dependencies
|
||||
if (item.DependsOn != null && _IncludeDependencies)
|
||||
{
|
||||
foreach (var d in item.DependsOn)
|
||||
{
|
||||
if (!index.ContainsKey(d))
|
||||
throw new RuleException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.DependencyNotFound, d, ruleId));
|
||||
|
||||
// Handle dependencies
|
||||
if (!_Targets.ContainsKey(d))
|
||||
Include(ruleId: d, parentId: ruleId, index: index);
|
||||
}
|
||||
}
|
||||
_Targets.Add(key: ruleId, value: item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_Stack.Pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ using System.Collections.ObjectModel;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
using System.Management.Automation.Language;
|
||||
using System.Threading;
|
||||
using Newtonsoft.Json;
|
||||
using PSRule.Annotations;
|
||||
using PSRule.Definitions;
|
||||
|
@ -18,6 +18,7 @@ using PSRule.Definitions.Rules;
|
|||
using PSRule.Definitions.Selectors;
|
||||
using PSRule.Parser;
|
||||
using PSRule.Pipeline;
|
||||
using PSRule.Resources;
|
||||
using PSRule.Rules;
|
||||
using PSRule.Runtime;
|
||||
using YamlDotNet.Core;
|
||||
|
@ -33,17 +34,17 @@ namespace PSRule.Host
|
|||
{
|
||||
private const string Markdown_Extension = ".md";
|
||||
|
||||
internal static Rule[] GetRule(Source[] source, RunspaceContext context, bool includeDependencies)
|
||||
internal static IRuleV1[] GetRule(Source[] source, RunspaceContext context, bool includeDependencies)
|
||||
{
|
||||
var rules = ToRuleV1(GetLanguageBlock(context, source), context);
|
||||
var builder = new DependencyGraphBuilder<Rule>(includeDependencies);
|
||||
var builder = new DependencyGraphBuilder<IRuleV1>(context, includeDependencies, includeDisabled: true);
|
||||
builder.Include(rules, filter: (b) => Match(context, b));
|
||||
return builder.GetItems();
|
||||
}
|
||||
|
||||
internal static RuleHelpInfo[] GetRuleHelp(Source[] source, RunspaceContext context)
|
||||
{
|
||||
return ToRuleHelp(ToRuleBlockV1(GetLanguageBlock(context, source), context), context);
|
||||
return ToRuleHelp(ToRuleBlockV1(GetLanguageBlock(context, source), context).GetAll(), context);
|
||||
}
|
||||
|
||||
internal static DependencyGraph<RuleBlock> GetRuleBlockGraph(Source[] source, RunspaceContext context)
|
||||
|
@ -51,22 +52,14 @@ namespace PSRule.Host
|
|||
var blocks = GetLanguageBlock(context, source);
|
||||
var rules = ToRuleBlockV1(blocks, context);
|
||||
Import(GetConventions(blocks, context), context);
|
||||
var builder = new DependencyGraphBuilder<RuleBlock>(includeDependencies: true);
|
||||
var builder = new DependencyGraphBuilder<RuleBlock>(context, includeDependencies: true, includeDisabled: false);
|
||||
builder.Include(rules, filter: (b) => Match(context, b));
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read YAML objects and return rules.
|
||||
/// </summary>
|
||||
internal static IEnumerable<Rule> GetRuleYaml(Source[] source, RunspaceContext context)
|
||||
{
|
||||
return ToRuleV1(ReadYamlObjects(source, context), context);
|
||||
}
|
||||
|
||||
internal static IEnumerable<RuleBlock> GetRuleYamlBlocks(Source[] source, RunspaceContext context)
|
||||
{
|
||||
return ToRuleBlockV1(ReadYamlObjects(source, context), context);
|
||||
return ToRuleBlockV1(GetYamlLanguageBlocks(source, context), context).GetAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -75,10 +68,8 @@ namespace PSRule.Host
|
|||
internal static IEnumerable<Baseline> GetBaseline(Source[] source, RunspaceContext context)
|
||||
{
|
||||
var results = new List<ILanguageBlock>();
|
||||
|
||||
results.AddRange(ReadYamlObjects(source, context));
|
||||
results.AddRange(ReadJsonObjects(source, context));
|
||||
|
||||
results.AddRange(GetYamlLanguageBlocks(source, context));
|
||||
results.AddRange(GetJsonLanguageBlocks(source, context));
|
||||
return ToBaselineV1(results, context);
|
||||
}
|
||||
|
||||
|
@ -87,7 +78,7 @@ namespace PSRule.Host
|
|||
/// </summary>
|
||||
internal static IEnumerable<ModuleConfigV1> GetModuleConfigYaml(Source[] source, RunspaceContext context)
|
||||
{
|
||||
return ToModuleConfigV1(ReadYamlObjects(source, context), context);
|
||||
return ToModuleConfigV1(GetYamlLanguageBlocks(source, context), context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -95,7 +86,7 @@ namespace PSRule.Host
|
|||
/// </summary>
|
||||
internal static IEnumerable<SelectorV1> GetSelectorYaml(Source[] source, RunspaceContext context)
|
||||
{
|
||||
return ToSelectorV1(ReadYamlObjects(source, context), context);
|
||||
return ToSelectorV1(GetYamlLanguageBlocks(source, context), context);
|
||||
}
|
||||
|
||||
internal static void ImportResource(Source[] source, RunspaceContext context)
|
||||
|
@ -104,10 +95,8 @@ namespace PSRule.Host
|
|||
return;
|
||||
|
||||
var results = new List<ILanguageBlock>();
|
||||
|
||||
results.AddRange(ReadYamlObjects(source, context));
|
||||
results.AddRange(ReadJsonObjects(source, context));
|
||||
|
||||
results.AddRange(GetYamlLanguageBlocks(source, context));
|
||||
results.AddRange(GetJsonLanguageBlocks(source, context));
|
||||
Import(results.ToArray(), context);
|
||||
}
|
||||
|
||||
|
@ -150,20 +139,18 @@ namespace PSRule.Host
|
|||
private static ILanguageBlock[] GetLanguageBlock(RunspaceContext context, Source[] sources)
|
||||
{
|
||||
var results = new List<ILanguageBlock>();
|
||||
results.AddRange(GetPowerShellRule(context, sources));
|
||||
results.AddRange(ReadYamlObjects(sources, context));
|
||||
results.AddRange(ReadJsonObjects(sources, context));
|
||||
results.AddRange(GetPSLanguageBlocks(context, sources));
|
||||
results.AddRange(GetYamlLanguageBlocks(sources, context));
|
||||
results.AddRange(GetJsonLanguageBlocks(sources, context));
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute one or more PowerShell script files to get language blocks.
|
||||
/// Execute PowerShell script files to get language blocks.
|
||||
/// </summary>
|
||||
/// <param name="sources"></param>
|
||||
/// <returns></returns>
|
||||
private static ILanguageBlock[] GetPowerShellRule(RunspaceContext context, Source[] sources)
|
||||
private static ILanguageBlock[] GetPSLanguageBlocks(RunspaceContext context, Source[] sources)
|
||||
{
|
||||
var results = new Collection<ILanguageBlock>();
|
||||
var results = new List<ILanguageBlock>();
|
||||
var ps = context.GetPowerShell();
|
||||
|
||||
try
|
||||
|
@ -229,7 +216,10 @@ namespace PSRule.Host
|
|||
return results.ToArray();
|
||||
}
|
||||
|
||||
private static ILanguageBlock[] ReadYamlObjects(Source[] sources, RunspaceContext context)
|
||||
/// <summary>
|
||||
/// Get language blocks from YAML source files.
|
||||
/// </summary>
|
||||
private static ILanguageBlock[] GetYamlLanguageBlocks(Source[] sources, RunspaceContext context)
|
||||
{
|
||||
var result = new Collection<ILanguageBlock>();
|
||||
var d = new DeserializerBuilder()
|
||||
|
@ -256,12 +246,13 @@ namespace PSRule.Host
|
|||
|
||||
context.VerboseRuleDiscovery(path: file.Path);
|
||||
context.EnterSourceScope(source: file);
|
||||
|
||||
using var reader = new StreamReader(file.Path);
|
||||
var parser = new YamlDotNet.Core.Parser(reader);
|
||||
parser.TryConsume<StreamStart>(out _);
|
||||
while (parser.Current is DocumentStart)
|
||||
{
|
||||
var item = d.Deserialize<ResourceObject>(parser: parser);
|
||||
var item = d.Deserialize<ResourceObject>(parser);
|
||||
if (item == null || item.Block == null)
|
||||
continue;
|
||||
|
||||
|
@ -279,10 +270,12 @@ namespace PSRule.Host
|
|||
return result.Count == 0 ? Array.Empty<ILanguageBlock>() : result.ToArray();
|
||||
}
|
||||
|
||||
private static ILanguageBlock[] ReadJsonObjects(Source[] sources, RunspaceContext context)
|
||||
/// <summary>
|
||||
/// Get language blocks from JSON source files.
|
||||
/// </summary>
|
||||
private static ILanguageBlock[] GetJsonLanguageBlocks(Source[] sources, RunspaceContext context)
|
||||
{
|
||||
var result = new Collection<ILanguageBlock>();
|
||||
|
||||
var deserializer = new JsonSerializer();
|
||||
|
||||
deserializer.Converters.Add(new ResourceObjectJsonConverter());
|
||||
|
@ -307,26 +300,19 @@ namespace PSRule.Host
|
|||
|
||||
// Consume lines until start of array is found
|
||||
while (reader.TokenType != JsonToken.StartArray)
|
||||
{
|
||||
reader.Read();
|
||||
}
|
||||
|
||||
if (reader.TokenType == JsonToken.StartArray && reader.Read())
|
||||
{
|
||||
while (reader.TokenType != JsonToken.EndArray)
|
||||
{
|
||||
var value = deserializer.Deserialize<ResourceObject>(reader);
|
||||
|
||||
if (value?.Block != null)
|
||||
{
|
||||
result.Add(value.Block);
|
||||
}
|
||||
|
||||
// Consume all end objects at the end of each resource
|
||||
while (reader.TokenType == JsonToken.EndObject)
|
||||
{
|
||||
reader.Read();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -339,7 +325,6 @@ namespace PSRule.Host
|
|||
context.PopScope(RunspaceScope.Resource);
|
||||
context.ExitSourceScope();
|
||||
}
|
||||
|
||||
return result.Count == 0 ? Array.Empty<ILanguageBlock>() : result.ToArray();
|
||||
}
|
||||
|
||||
|
@ -385,95 +370,104 @@ namespace PSRule.Host
|
|||
}
|
||||
}
|
||||
|
||||
private static RuleException ThrowDuplicateRuleId(IDependencyTarget block)
|
||||
{
|
||||
return new RuleException(string.Format(Thread.CurrentThread.CurrentCulture, PSRuleResources.DuplicateRuleId, block.Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert matching langauge blocks to rules.
|
||||
/// </summary>
|
||||
private static Rule[] ToRuleV1(IEnumerable<ILanguageBlock> blocks, RunspaceContext context)
|
||||
private static DependencyTargetCollection<IRuleV1> ToRuleV1(ILanguageBlock[] blocks, RunspaceContext context)
|
||||
{
|
||||
// Index rules by RuleId
|
||||
var results = new Dictionary<string, Rule>(StringComparer.OrdinalIgnoreCase);
|
||||
var results = new DependencyTargetCollection<IRuleV1>();
|
||||
try
|
||||
{
|
||||
foreach (var block in blocks.OfType<RuleBlock>())
|
||||
{
|
||||
if (!results.ContainsKey(block.RuleId))
|
||||
results.TryAdd(new Rule
|
||||
{
|
||||
results[block.RuleId] = new Rule
|
||||
{
|
||||
RuleId = block.RuleId,
|
||||
RuleName = block.RuleName,
|
||||
Source = block.Source,
|
||||
Tag = block.Tag,
|
||||
Info = block.Info,
|
||||
DependsOn = block.DependsOn
|
||||
};
|
||||
}
|
||||
Id = block.Id,
|
||||
Ref = block.Ref,
|
||||
Alias = block.Alias,
|
||||
Source = block.Source,
|
||||
Tag = block.Tag,
|
||||
Info = block.Info,
|
||||
DependsOn = block.DependsOn,
|
||||
Flags = block.Flags,
|
||||
});
|
||||
//throw ThrowDuplicateRuleId(block);
|
||||
}
|
||||
|
||||
foreach (var block in blocks.OfType<RuleV1>())
|
||||
{
|
||||
if (!results.ContainsKey(block.Id))
|
||||
context.EnterSourceScope(block.Source);
|
||||
var info = GetHelpInfo(context, block.Name, block.Synopsis);
|
||||
results.TryAdd(new Rule
|
||||
{
|
||||
context.EnterSourceScope(block.Source);
|
||||
var info = GetHelpInfo(context, block.Name, block.Synopsis);
|
||||
results[block.Id] = new Rule
|
||||
{
|
||||
RuleId = block.Id,
|
||||
RuleName = block.Name,
|
||||
Source = block.Source,
|
||||
Tag = block.Metadata.Tags,
|
||||
Info = info,
|
||||
DependsOn = null // TODO: No support for DependsOn yet
|
||||
};
|
||||
}
|
||||
Id = block.Id,
|
||||
Ref = block.Ref,
|
||||
Alias = block.Alias,
|
||||
Source = block.Source,
|
||||
Tag = block.Metadata.Tags,
|
||||
Info = info,
|
||||
DependsOn = null, // TODO: No support for DependsOn yet
|
||||
Flags = block.Flags,
|
||||
});
|
||||
//throw ThrowDuplicateRuleId(block);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.ExitSourceScope();
|
||||
}
|
||||
return results.Values.ToArray();
|
||||
return results;
|
||||
}
|
||||
|
||||
private static RuleBlock[] ToRuleBlockV1(IEnumerable<ILanguageBlock> blocks, RunspaceContext context)
|
||||
private static DependencyTargetCollection<RuleBlock> ToRuleBlockV1(ILanguageBlock[] blocks, RunspaceContext context)
|
||||
{
|
||||
// Index rules by RuleId
|
||||
var results = new Dictionary<string, RuleBlock>(StringComparer.OrdinalIgnoreCase);
|
||||
//var results = new Dictionary<string, RuleBlock>(StringComparer.OrdinalIgnoreCase);
|
||||
var results = new DependencyTargetCollection<RuleBlock>();
|
||||
try
|
||||
{
|
||||
foreach (var block in blocks)
|
||||
foreach (var block in blocks.OfType<RuleBlock>())
|
||||
{
|
||||
if (block is RuleBlock script && !results.ContainsKey(script.RuleId))
|
||||
results.TryAdd(block);
|
||||
//throw ThrowDuplicateRuleId(block);
|
||||
}
|
||||
|
||||
foreach (var yaml in blocks.OfType<RuleV1>())
|
||||
{
|
||||
context.EnterSourceScope(yaml.Source);
|
||||
var info = GetHelpInfo(context, yaml.Name, yaml.Synopsis) ?? new RuleHelpInfo(yaml.Name, yaml.Name, yaml.Source.ModuleName)
|
||||
{
|
||||
results[script.RuleId] = script;
|
||||
}
|
||||
else if (block is RuleV1 yaml && !results.ContainsKey(yaml.Id))
|
||||
{
|
||||
context.EnterSourceScope(yaml.Source);
|
||||
var info = GetHelpInfo(context, yaml.Name, yaml.Synopsis) ?? new RuleHelpInfo(yaml.Name, yaml.Name, yaml.Source.ModuleName)
|
||||
{
|
||||
Synopsis = yaml.Synopsis
|
||||
};
|
||||
results[yaml.Id] = new RuleBlock
|
||||
(
|
||||
source: yaml.Source,
|
||||
ruleName: yaml.Name,
|
||||
info: info,
|
||||
condition: new RuleVisitor(yaml.Source.ModuleName, yaml.Id, yaml.Spec),
|
||||
tag: yaml.Metadata.Tags,
|
||||
dependsOn: null, // TODO: No support for DependsOn yet
|
||||
configuration: null, // TODO: No support for rule configuration use module or workspace config
|
||||
extent: null,
|
||||
errorPreference: ActionPreference.Stop
|
||||
);
|
||||
}
|
||||
Synopsis = yaml.Synopsis
|
||||
};
|
||||
var block = new RuleBlock
|
||||
(
|
||||
source: yaml.Source,
|
||||
id: yaml.Id,
|
||||
@ref: yaml.Ref,
|
||||
info: info,
|
||||
condition: new RuleVisitor(yaml.Source.ModuleName, yaml.Id.Value, yaml.Spec),
|
||||
alias: yaml.Alias,
|
||||
tag: yaml.Metadata.Tags,
|
||||
dependsOn: null, // TODO: No support for DependsOn yet
|
||||
configuration: null, // TODO: No support for rule configuration use module or workspace config
|
||||
extent: null,
|
||||
flags: yaml.Flags
|
||||
);
|
||||
results.TryAdd(block);
|
||||
//throw ThrowDuplicateRuleId(block);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.ExitSourceScope();
|
||||
}
|
||||
return results.Values.ToArray();
|
||||
return results;
|
||||
}
|
||||
|
||||
private static RuleHelpInfo[] ToRuleHelp(IEnumerable<ILanguageBlock> blocks, RunspaceContext context)
|
||||
|
@ -488,8 +482,8 @@ namespace PSRule.Host
|
|||
if (!Match(context, block))
|
||||
continue;
|
||||
|
||||
if (!results.ContainsKey(block.RuleId))
|
||||
results[block.RuleId] = block.Info;
|
||||
if (!results.ContainsKey(block.Id.Value))
|
||||
results[block.Id.Value] = block.Info;
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
@ -563,7 +557,7 @@ namespace PSRule.Host
|
|||
if (!Match(context, block))
|
||||
continue;
|
||||
|
||||
if (!index.Contains(block.Id))
|
||||
if (!index.Contains(block.Id.Value))
|
||||
results.Add(block);
|
||||
}
|
||||
}
|
||||
|
@ -589,8 +583,8 @@ namespace PSRule.Host
|
|||
if (!Match(context, block))
|
||||
continue;
|
||||
|
||||
if (!results.ContainsKey(block.Id))
|
||||
results[block.Id] = block;
|
||||
if (!results.ContainsKey(block.Id.Value))
|
||||
results[block.Id.Value] = block;
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
@ -626,7 +620,7 @@ namespace PSRule.Host
|
|||
return filter == null || filter.Match(resource);
|
||||
}
|
||||
|
||||
private static bool Match(RunspaceContext context, Rule resource)
|
||||
private static bool Match(RunspaceContext context, IRuleV1 resource)
|
||||
{
|
||||
context.EnterSourceScope(resource.Source);
|
||||
var filter = context.LanguageScope.GetFilter(ResourceKind.Rule);
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace PSRule.Host
|
||||
{
|
||||
internal interface IDependencyTarget
|
||||
{
|
||||
string RuleId { get; }
|
||||
|
||||
string[] DependsOn { get; }
|
||||
|
||||
bool Dependency { get; }
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace PSRule.Host
|
||||
{
|
||||
public enum LanguageBlockKind
|
||||
{
|
||||
Unknown = 0,
|
||||
|
||||
Rule = 1,
|
||||
|
||||
Baseline = 2
|
||||
}
|
||||
|
||||
public interface ILanguageBlock
|
||||
{
|
||||
string Id { get; }
|
||||
|
||||
string SourcePath { get; }
|
||||
|
||||
string Module { get; }
|
||||
}
|
||||
}
|
|
@ -639,7 +639,7 @@ function Assert-PSRule {
|
|||
# .ExternalHelp PSRule-Help.xml
|
||||
function Get-PSRule {
|
||||
[CmdletBinding()]
|
||||
[OutputType([PSRule.Rules.Rule])]
|
||||
[OutputType([PSRule.Definitions.Rules.IRuleV1])]
|
||||
param (
|
||||
[Parameter(Mandatory = $False)]
|
||||
[Alias('m')]
|
||||
|
@ -1190,6 +1190,11 @@ function New-PSRuleOption {
|
|||
[Alias('ExecutionSuppressedRuleWarning')]
|
||||
[System.Boolean]$SuppressedRuleWarning = $True,
|
||||
|
||||
# Sets the Execution.AliasReferenceWarning option
|
||||
[Parameter(Mandatory = $False)]
|
||||
[Alias('ExecutionAliasReferenceWarning')]
|
||||
[System.Boolean]$AliasReferenceWarning = $True,
|
||||
|
||||
# Sets the Include.Module option
|
||||
[Parameter(Mandatory = $False)]
|
||||
[String[]]$IncludeModule,
|
||||
|
@ -1442,6 +1447,11 @@ function Set-PSRuleOption {
|
|||
[Alias('ExecutionSuppressedRuleWarning')]
|
||||
[System.Boolean]$SuppressedRuleWarning = $True,
|
||||
|
||||
# Sets the Execution.AliasReferenceWarning option
|
||||
[Parameter(Mandatory = $False)]
|
||||
[Alias('ExecutionAliasReferenceWarning')]
|
||||
[System.Boolean]$AliasReferenceWarning = $True,
|
||||
|
||||
# Sets the Include.Module option
|
||||
[Parameter(Mandatory = $False)]
|
||||
[String[]]$IncludeModule,
|
||||
|
@ -1649,8 +1659,18 @@ function Rule {
|
|||
param (
|
||||
# The name of the rule
|
||||
[Parameter(Position = 0, Mandatory = $True)]
|
||||
[ValidateLength(3, 128)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[String]$Name,
|
||||
|
||||
# An optional stable opaque identifier of this resource for lookup.
|
||||
[Parameter(Mandatory = $False)]
|
||||
[String]$Ref,
|
||||
|
||||
# Any aliases for the rule.
|
||||
[Parameter(Mandatory = $False)]
|
||||
[String[]]$Alias,
|
||||
|
||||
# The body of the rule
|
||||
[Parameter(Position = 1, Mandatory = $True)]
|
||||
[ScriptBlock]$Body,
|
||||
|
@ -2126,6 +2146,11 @@ function SetOptions {
|
|||
[Alias('ExecutionSuppressedRuleWarning')]
|
||||
[System.Boolean]$SuppressedRuleWarning = $True,
|
||||
|
||||
# Sets the Execution.AliasReferenceWarning option
|
||||
[Parameter(Mandatory = $False)]
|
||||
[Alias('ExecutionAliasReferenceWarning')]
|
||||
[System.Boolean]$AliasReferenceWarning = $True,
|
||||
|
||||
# Sets the Include.Module option
|
||||
[Parameter(Mandatory = $False)]
|
||||
[String[]]$IncludeModule,
|
||||
|
@ -2289,6 +2314,11 @@ function SetOptions {
|
|||
$Option.Execution.SuppressedRuleWarning = $SuppressedRuleWarning;
|
||||
}
|
||||
|
||||
# Sets option Execution.AliasReferenceWarning
|
||||
if ($PSBoundParameters.ContainsKey('AliasReferenceWarning')) {
|
||||
$Option.Execution.AliasReferenceWarning = $AliasReferenceWarning;
|
||||
}
|
||||
|
||||
# Sets option Include.Module
|
||||
if ($PSBoundParameters.ContainsKey('IncludeModule')) {
|
||||
$Option.Include.Module = $IncludeModule;
|
||||
|
|
|
@ -375,6 +375,46 @@ namespace PSRule.Pipeline
|
|||
LineBreak();
|
||||
}
|
||||
|
||||
protected static string GetErrorMessage(RuleRecord record)
|
||||
{
|
||||
return string.IsNullOrEmpty(record.Ref) ?
|
||||
string.Format(
|
||||
Thread.CurrentThread.CurrentCulture,
|
||||
FormatterStrings.Result_ErrorDetail,
|
||||
record.TargetName,
|
||||
record.RuleName,
|
||||
record.Error.Message
|
||||
) :
|
||||
string.Format(
|
||||
Thread.CurrentThread.CurrentCulture,
|
||||
FormatterStrings.Result_ErrorDetailWithRef,
|
||||
record.TargetName,
|
||||
record.RuleName,
|
||||
record.Error.Message,
|
||||
record.Ref
|
||||
);
|
||||
}
|
||||
|
||||
protected static string GetFailMessage(RuleRecord record)
|
||||
{
|
||||
return string.IsNullOrEmpty(record.Ref) ?
|
||||
string.Format(
|
||||
Thread.CurrentThread.CurrentCulture,
|
||||
FormatterStrings.Result_FailDetail,
|
||||
record.TargetName,
|
||||
record.RuleName,
|
||||
record.Info.Synopsis
|
||||
) :
|
||||
string.Format(
|
||||
Thread.CurrentThread.CurrentCulture,
|
||||
FormatterStrings.Result_FailDetailWithRef,
|
||||
record.TargetName,
|
||||
record.RuleName,
|
||||
record.Info.Synopsis,
|
||||
record.Ref
|
||||
);
|
||||
}
|
||||
|
||||
protected override void DoWriteError(ErrorRecord errorRecord)
|
||||
{
|
||||
Error(errorRecord);
|
||||
|
@ -431,7 +471,7 @@ namespace PSRule.Pipeline
|
|||
WriteLineFormat(FormatterStrings.FooterRunInfo, PipelineContext.CurrentThread.RunId, elapsed.ToString("c", Thread.CurrentThread.CurrentCulture));
|
||||
}
|
||||
|
||||
protected void WriteStatus(string status, string statusIndent, ConsoleColor? statusForeground, ConsoleColor? statusBackground, ConsoleColor? messageForeground, ConsoleColor? messageBackground, string message)
|
||||
protected void WriteStatus(string status, string statusIndent, ConsoleColor? statusForeground, ConsoleColor? statusBackground, ConsoleColor? messageForeground, ConsoleColor? messageBackground, string message, string suffix = null)
|
||||
{
|
||||
var output = message;
|
||||
if (statusForeground != null || statusBackground != null)
|
||||
|
@ -445,10 +485,11 @@ namespace PSRule.Pipeline
|
|||
NoNewLine = true
|
||||
});
|
||||
Writer.WriteHost(new HostInformationMessage { Message = " ", NoNewLine = true });
|
||||
output = string.IsNullOrEmpty(suffix) ? output : string.Concat(output, " (", suffix, ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
output = string.Concat(status, output);
|
||||
output = string.IsNullOrEmpty(suffix) ? string.Concat(status, output) : string.Concat(status, output, " (", suffix, ")"); ;
|
||||
}
|
||||
Writer.WriteHost(new HostInformationMessage
|
||||
{
|
||||
|
@ -657,7 +698,8 @@ namespace PSRule.Pipeline
|
|||
statusBackground: GetTerminalSupport().WarningStatusBackgroundColor,
|
||||
messageForeground: GetTerminalSupport().WarningForegroundColor,
|
||||
messageBackground: GetTerminalSupport().WarningBackgroundColor,
|
||||
message: message);
|
||||
message: message
|
||||
);
|
||||
UnbrokenInfo();
|
||||
}
|
||||
|
||||
|
@ -672,7 +714,9 @@ namespace PSRule.Pipeline
|
|||
statusBackground: GetTerminalSupport().PassStatusBackgroundColor,
|
||||
messageForeground: GetTerminalSupport().PassForegroundColor,
|
||||
messageBackground: GetTerminalSupport().PassBackgroundColor,
|
||||
message: record.RuleName);
|
||||
message: record.RuleName,
|
||||
suffix: record.Ref
|
||||
);
|
||||
UnbrokenContent();
|
||||
}
|
||||
|
||||
|
@ -687,7 +731,9 @@ namespace PSRule.Pipeline
|
|||
statusBackground: GetTerminalSupport().FailStatusBackgroundColor,
|
||||
messageForeground: GetTerminalSupport().FailForegroundColor,
|
||||
messageBackground: GetTerminalSupport().FailBackgroundColor,
|
||||
message: record.RuleName);
|
||||
message: record.RuleName,
|
||||
suffix: record.Ref
|
||||
);
|
||||
FailDetail(record);
|
||||
}
|
||||
|
||||
|
@ -702,7 +748,9 @@ namespace PSRule.Pipeline
|
|||
statusBackground: GetTerminalSupport().ErrorStatusBackgroundColor,
|
||||
messageForeground: GetTerminalSupport().ErrorForegroundColor,
|
||||
messageBackground: GetTerminalSupport().ErrorBackgroundColor,
|
||||
message: record.RuleName);
|
||||
message: record.RuleName,
|
||||
suffix: record.Ref
|
||||
);
|
||||
ErrorDetail(record);
|
||||
UnbrokenContent();
|
||||
}
|
||||
|
@ -820,12 +868,7 @@ namespace PSRule.Pipeline
|
|||
return;
|
||||
|
||||
LineBreak();
|
||||
Error(string.Format(
|
||||
Thread.CurrentThread.CurrentCulture,
|
||||
FormatterStrings.Result_ErrorDetail,
|
||||
record.TargetName,
|
||||
record.RuleName,
|
||||
record.Error.Message));
|
||||
Error(GetErrorMessage(record));
|
||||
LineBreak();
|
||||
WriteLine(record.Error.PositionMessage);
|
||||
LineBreak();
|
||||
|
@ -835,12 +878,7 @@ namespace PSRule.Pipeline
|
|||
protected override void FailDetail(RuleRecord record)
|
||||
{
|
||||
base.FailDetail(record);
|
||||
Error(string.Format(
|
||||
Thread.CurrentThread.CurrentCulture,
|
||||
FormatterStrings.Result_FailDetail,
|
||||
record.TargetName,
|
||||
record.RuleName,
|
||||
record.Info.Synopsis));
|
||||
Error(GetFailMessage(record));
|
||||
LineBreak();
|
||||
}
|
||||
}
|
||||
|
@ -877,12 +915,7 @@ namespace PSRule.Pipeline
|
|||
return;
|
||||
|
||||
LineBreak();
|
||||
Error(string.Format(
|
||||
Thread.CurrentThread.CurrentCulture,
|
||||
FormatterStrings.Result_ErrorDetail,
|
||||
record.TargetName,
|
||||
record.RuleName,
|
||||
record.Error.Message));
|
||||
Error(GetErrorMessage(record));
|
||||
LineBreak();
|
||||
WriteLine(record.Error.PositionMessage);
|
||||
LineBreak();
|
||||
|
@ -892,12 +925,7 @@ namespace PSRule.Pipeline
|
|||
protected override void FailDetail(RuleRecord record)
|
||||
{
|
||||
base.FailDetail(record);
|
||||
Error(string.Format(
|
||||
Thread.CurrentThread.CurrentCulture,
|
||||
FormatterStrings.Result_FailDetail,
|
||||
record.TargetName,
|
||||
record.RuleName,
|
||||
record.Info.Synopsis));
|
||||
Error(GetFailMessage(record));
|
||||
LineBreak();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||
using System.Management.Automation;
|
||||
using PSRule.Configuration;
|
||||
using PSRule.Data;
|
||||
using PSRule.Definitions;
|
||||
using PSRule.Host;
|
||||
using PSRule.Rules;
|
||||
|
||||
|
@ -170,7 +171,7 @@ namespace PSRule.Pipeline
|
|||
private readonly Dictionary<string, RuleSummaryRecord> _Summary;
|
||||
|
||||
private readonly ResultFormat _ResultFormat;
|
||||
private readonly RuleSuppressionFilter _SuppressionFilter;
|
||||
private readonly SuppressionFilter _SuppressionFilter;
|
||||
private readonly List<InvokeResult> _Completed;
|
||||
|
||||
// Track whether Dispose has been called.
|
||||
|
@ -187,7 +188,7 @@ namespace PSRule.Pipeline
|
|||
_Outcome = outcome;
|
||||
_Summary = new Dictionary<string, RuleSummaryRecord>();
|
||||
_ResultFormat = context.Option.Output.As.Value;
|
||||
_SuppressionFilter = new RuleSuppressionFilter(context.Option.Suppression);
|
||||
_SuppressionFilter = new SuppressionFilter(Context, context.Option.Suppression, _RuleGraph.GetAll());
|
||||
_Completed = new List<InvokeResult>();
|
||||
}
|
||||
|
||||
|
@ -254,7 +255,7 @@ namespace PSRule.Pipeline
|
|||
ruleRecord.OutcomeReason = RuleOutcomeReason.DependencyFail;
|
||||
}
|
||||
// Check for suppression
|
||||
else if (_SuppressionFilter.Match(ruleName: ruleRecord.RuleName, targetName: ruleRecord.TargetName))
|
||||
else if (_SuppressionFilter.Match(id: ruleBlockTarget.Value.Id, targetName: ruleRecord.TargetName))
|
||||
{
|
||||
ruleRecord.OutcomeReason = RuleOutcomeReason.Suppressed;
|
||||
suppressedRuleCounter++;
|
||||
|
@ -314,15 +315,15 @@ namespace PSRule.Pipeline
|
|||
/// </summary>
|
||||
private void AddToSummary(RuleBlock ruleBlock, RuleOutcome outcome)
|
||||
{
|
||||
if (!_Summary.TryGetValue(ruleBlock.RuleId, out var s))
|
||||
if (!_Summary.TryGetValue(ruleBlock.Id.Value, out var s))
|
||||
{
|
||||
s = new RuleSummaryRecord(
|
||||
ruleId: ruleBlock.RuleId,
|
||||
ruleName: ruleBlock.RuleName,
|
||||
ruleId: ruleBlock.Id.Value,
|
||||
ruleName: ruleBlock.Name,
|
||||
tag: ruleBlock.Tag,
|
||||
info: ruleBlock.Info
|
||||
);
|
||||
_Summary.Add(ruleBlock.RuleId, s);
|
||||
_Summary.Add(ruleBlock.Id.Value, s);
|
||||
}
|
||||
|
||||
if (outcome == RuleOutcome.Pass)
|
||||
|
|
|
@ -9,7 +9,7 @@ using PSRule.Definitions;
|
|||
using PSRule.Definitions.Baselines;
|
||||
using PSRule.Definitions.Conventions;
|
||||
using PSRule.Definitions.ModuleConfigs;
|
||||
using PSRule.Rules;
|
||||
using PSRule.Definitions.Rules;
|
||||
using PSRule.Runtime;
|
||||
|
||||
namespace PSRule.Pipeline
|
||||
|
@ -332,10 +332,10 @@ namespace PSRule.Pipeline
|
|||
foreach (var baseline in _ModuleBaselineScope.Values)
|
||||
{
|
||||
if (baseline.Obsolete)
|
||||
context.WarnBaselineObsolete(baseline.Id);
|
||||
context.WarnResourceObsolete(ResourceKind.Baseline, baseline.Id);
|
||||
}
|
||||
if (_Explicit != null && _Explicit.Obsolete)
|
||||
context.WarnBaselineObsolete(_Explicit.Id);
|
||||
context.WarnResourceObsolete(ResourceKind.Baseline, _Explicit.Id);
|
||||
}
|
||||
|
||||
internal void Add(BaselineScope scope)
|
||||
|
@ -378,7 +378,7 @@ namespace PSRule.Pipeline
|
|||
|
||||
internal int GetConventionOrder(IConvention convention)
|
||||
{
|
||||
var index = _ConventionOrder.IndexOf(convention.Id);
|
||||
var index = _ConventionOrder.IndexOf(convention.Id.Value);
|
||||
if (index == -1)
|
||||
index = _ConventionOrder.IndexOf(convention.Name);
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using PSRule.Configuration;
|
||||
using PSRule.Definitions.Rules;
|
||||
using PSRule.Rules;
|
||||
|
||||
namespace PSRule.Pipeline.Output
|
||||
|
@ -17,7 +18,7 @@ namespace PSRule.Pipeline.Output
|
|||
{
|
||||
if (sendToPipeline is InvokeResult result)
|
||||
WriteWideObject(result.AsRecord());
|
||||
else if (sendToPipeline is IEnumerable<Rule> rules)
|
||||
else if (sendToPipeline is IEnumerable<IRuleV1> rules)
|
||||
WriteWideObject(rules);
|
||||
else
|
||||
base.WriteObject(sendToPipeline, enumerateCollection);
|
||||
|
|
|
@ -167,7 +167,7 @@ namespace PSRule.Pipeline
|
|||
Baseline.Add(new OptionContext.BaselineScope(baselineRef.Type, baseline.BaselineId, resource.Module, baseline.Spec, baseline.Obsolete));
|
||||
}
|
||||
else if (resource.Kind == ResourceKind.Selector && resource is SelectorV1 selector)
|
||||
Selector[selector.Id] = new SelectorVisitor(resource.Module, selector.Id, selector.Spec.If);
|
||||
Selector[selector.Id.Value] = new SelectorVisitor(resource.Module, selector.Id.Value, selector.Spec.If);
|
||||
else if (TryModuleConfig(resource, out var moduleConfig))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(moduleConfig?.Spec?.Rule?.Baseline))
|
||||
|
@ -186,10 +186,10 @@ namespace PSRule.Pipeline
|
|||
// _TrackedIssues.Add(new ResourceIssue(resource.Kind, resource.Id, ResourceIssueType.MissingApiVersion));
|
||||
}
|
||||
|
||||
private bool TryBaselineRef(string resourceId, out BaselineRef baselineRef)
|
||||
private bool TryBaselineRef(ResourceId resourceId, out BaselineRef baselineRef)
|
||||
{
|
||||
baselineRef = null;
|
||||
var r = _Unresolved.FirstOrDefault(i => ResourceIdEqualityComparer.IdEquals(i.Id, resourceId));
|
||||
var r = _Unresolved.FirstOrDefault(i => ResourceIdEqualityComparer.IdEquals(i.Id, resourceId.Value));
|
||||
if (r == null || !(r is BaselineRef br))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -8,11 +8,10 @@
|
|||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PSRule.Resources
|
||||
{
|
||||
namespace PSRule.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
|
@ -23,400 +22,346 @@ namespace PSRule.Resources
|
|||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class FormatterStrings
|
||||
{
|
||||
|
||||
internal class FormatterStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal FormatterStrings()
|
||||
{
|
||||
internal FormatterStrings() {
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (object.ReferenceEquals(resourceMan, null))
|
||||
{
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PSRule.Resources.FormatterStrings", typeof(FormatterStrings).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ____ _____ ____ __\n / __ \/ ___// __ \__ __/ /__\n / /_/ /\__ \/ /_/ / / / / / _ \\n / ____/___/ / _, _/ /_/ / / __/\n/_/ /____/_/ |_|\__,_/_/\___/.
|
||||
/// </summary>
|
||||
internal static string Banner
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Banner {
|
||||
get {
|
||||
return ResourceManager.GetString("Banner", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Rules processed: {0}, failed: {1}, errored: {2}.
|
||||
/// </summary>
|
||||
internal static string FooterRuleCount
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string FooterRuleCount {
|
||||
get {
|
||||
return ResourceManager.GetString("FooterRuleCount", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Run {0} completed in {1}.
|
||||
/// </summary>
|
||||
internal static string FooterRunInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string FooterRunInfo {
|
||||
get {
|
||||
return ResourceManager.GetString("FooterRunInfo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to | HELP:.
|
||||
/// </summary>
|
||||
internal static string Help
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Help {
|
||||
get {
|
||||
return ResourceManager.GetString("Help", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Contribute and find source: https://github.com/microsoft/PSRule.
|
||||
/// </summary>
|
||||
internal static string HelpContribute
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string HelpContribute {
|
||||
get {
|
||||
return ResourceManager.GetString("HelpContribute", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Explore documentation: https://microsoft.github.io/PSRule/.
|
||||
/// </summary>
|
||||
internal static string HelpDocs
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string HelpDocs {
|
||||
get {
|
||||
return ResourceManager.GetString("HelpDocs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Report issues: https://github.com/microsoft/PSRule/issues.
|
||||
/// </summary>
|
||||
internal static string HelpIssues
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string HelpIssues {
|
||||
get {
|
||||
return ResourceManager.GetString("HelpIssues", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0}: {1}.
|
||||
/// </summary>
|
||||
internal static string HelpModule
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string HelpModule {
|
||||
get {
|
||||
return ResourceManager.GetString("HelpModule", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to | MESSAGE:.
|
||||
/// </summary>
|
||||
internal static string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Message {
|
||||
get {
|
||||
return ResourceManager.GetString("Message", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Using {0} v{1}.
|
||||
/// </summary>
|
||||
internal static string ModuleVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string ModuleVersion {
|
||||
get {
|
||||
return ResourceManager.GetString("ModuleVersion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to | POSITION:.
|
||||
/// </summary>
|
||||
internal static string Position
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Position {
|
||||
get {
|
||||
return ResourceManager.GetString("Position", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Using PSRule v{0}.
|
||||
/// </summary>
|
||||
internal static string PSRuleVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string PSRuleVersion {
|
||||
get {
|
||||
return ResourceManager.GetString("PSRuleVersion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to | REASON:.
|
||||
/// </summary>
|
||||
internal static string Reason
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Reason {
|
||||
get {
|
||||
return ResourceManager.GetString("Reason", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to | RECOMMEND:.
|
||||
/// </summary>
|
||||
internal static string Recommend
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Recommend {
|
||||
get {
|
||||
return ResourceManager.GetString("Recommend", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to [ERROR] .
|
||||
/// </summary>
|
||||
internal static string Result_Error
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Result_Error {
|
||||
get {
|
||||
return ResourceManager.GetString("Result_Error", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} failed {1} with the error '{2}'..
|
||||
/// </summary>
|
||||
internal static string Result_ErrorDetail
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Result_ErrorDetail {
|
||||
get {
|
||||
return ResourceManager.GetString("Result_ErrorDetail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {3}: {0} failed {1} with the error '{2}'..
|
||||
/// </summary>
|
||||
internal static string Result_ErrorDetailWithRef {
|
||||
get {
|
||||
return ResourceManager.GetString("Result_ErrorDetailWithRef", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to [FAIL] .
|
||||
/// </summary>
|
||||
internal static string Result_Fail
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Result_Fail {
|
||||
get {
|
||||
return ResourceManager.GetString("Result_Fail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} failed {1}. {2}.
|
||||
/// </summary>
|
||||
internal static string Result_FailDetail
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Result_FailDetail {
|
||||
get {
|
||||
return ResourceManager.GetString("Result_FailDetail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {3}: {0} failed {1}. {2}.
|
||||
/// </summary>
|
||||
internal static string Result_FailDetailWithRef {
|
||||
get {
|
||||
return ResourceManager.GetString("Result_FailDetailWithRef", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to [PASS] .
|
||||
/// </summary>
|
||||
internal static string Result_Pass
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Result_Pass {
|
||||
get {
|
||||
return ResourceManager.GetString("Result_Pass", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to [WARN] .
|
||||
/// </summary>
|
||||
internal static string Result_Warning
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string Result_Warning {
|
||||
get {
|
||||
return ResourceManager.GetString("Result_Warning", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to At.
|
||||
/// </summary>
|
||||
internal static string SourceAt
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string SourceAt {
|
||||
get {
|
||||
return ResourceManager.GetString("SourceAt", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to | STACK TRACE:.
|
||||
/// </summary>
|
||||
internal static string StackTrace
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string StackTrace {
|
||||
get {
|
||||
return ResourceManager.GetString("StackTrace", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to -> .
|
||||
/// </summary>
|
||||
internal static string StartObjectPrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string StartObjectPrefix {
|
||||
get {
|
||||
return ResourceManager.GetString("StartObjectPrefix", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to | SYNOPSIS: .
|
||||
/// </summary>
|
||||
internal static string SynopsisPrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string SynopsisPrefix {
|
||||
get {
|
||||
return ResourceManager.GetString("SynopsisPrefix", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ERROR .
|
||||
/// </summary>
|
||||
internal static string VSCode_Error
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string VSCode_Error {
|
||||
get {
|
||||
return ResourceManager.GetString("VSCode_Error", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to FAIL .
|
||||
/// </summary>
|
||||
internal static string VSCode_Fail
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string VSCode_Fail {
|
||||
get {
|
||||
return ResourceManager.GetString("VSCode_Fail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Help:.
|
||||
/// </summary>
|
||||
internal static string VSCode_Help
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string VSCode_Help {
|
||||
get {
|
||||
return ResourceManager.GetString("VSCode_Help", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to PASS .
|
||||
/// </summary>
|
||||
internal static string VSCode_Pass
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string VSCode_Pass {
|
||||
get {
|
||||
return ResourceManager.GetString("VSCode_Pass", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reason:.
|
||||
/// </summary>
|
||||
internal static string VSCode_Reason
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string VSCode_Reason {
|
||||
get {
|
||||
return ResourceManager.GetString("VSCode_Reason", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Recommend:.
|
||||
/// </summary>
|
||||
internal static string VSCode_Recommend
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string VSCode_Recommend {
|
||||
get {
|
||||
return ResourceManager.GetString("VSCode_Recommend", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to > .
|
||||
/// </summary>
|
||||
internal static string VSCode_StartObjectPrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string VSCode_StartObjectPrefix {
|
||||
get {
|
||||
return ResourceManager.GetString("VSCode_StartObjectPrefix", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to WARN .
|
||||
/// </summary>
|
||||
internal static string VSCode_Warning
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static string VSCode_Warning {
|
||||
get {
|
||||
return ResourceManager.GetString("VSCode_Warning", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,12 +167,18 @@
|
|||
<data name="Result_ErrorDetail" xml:space="preserve">
|
||||
<value>{0} failed {1} with the error '{2}'.</value>
|
||||
</data>
|
||||
<data name="Result_ErrorDetailWithRef" xml:space="preserve">
|
||||
<value>{3}: {0} failed {1} with the error '{2}'.</value>
|
||||
</data>
|
||||
<data name="Result_Fail" xml:space="preserve">
|
||||
<value> [FAIL] </value>
|
||||
</data>
|
||||
<data name="Result_FailDetail" xml:space="preserve">
|
||||
<value>{0} failed {1}. {2}</value>
|
||||
</data>
|
||||
<data name="Result_FailDetailWithRef" xml:space="preserve">
|
||||
<value>{3}: {0} failed {1}. {2}</value>
|
||||
</data>
|
||||
<data name="Result_Pass" xml:space="preserve">
|
||||
<value> [PASS] </value>
|
||||
</data>
|
||||
|
|
|
@ -61,11 +61,20 @@ namespace PSRule.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The baseline '{0}' is obsolete. Consider switching to an alternative baseline..
|
||||
/// Looks up a localized string similar to The {0} resource '{1}' is currently referencing '{2}' using the alias '{3}'. Consider updating the reference to use name or id directly..
|
||||
/// </summary>
|
||||
internal static string BaselineObsolete {
|
||||
internal static string AliasReference {
|
||||
get {
|
||||
return ResourceManager.GetString("BaselineObsolete", resourceCulture);
|
||||
return ResourceManager.GetString("AliasReference", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Suppression for the rule '{0}' was configured using the alias '{1}'. Consider updating the suppression to use the name or id directly..
|
||||
/// </summary>
|
||||
internal static string AliasSuppression {
|
||||
get {
|
||||
return ResourceManager.GetString("AliasSuppression", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,6 +402,15 @@ namespace PSRule.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The {0} '{1}' is obsolete. Consider switching to an alternative {0}..
|
||||
/// </summary>
|
||||
internal static string ResourceObsolete {
|
||||
get {
|
||||
return ResourceManager.GetString("ResourceObsolete", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} rule/s were suppressed for '{1}'..
|
||||
/// </summary>
|
||||
|
|
|
@ -117,9 +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="BaselineObsolete" xml:space="preserve">
|
||||
<value>The baseline '{0}' is obsolete. Consider switching to an alternative baseline.</value>
|
||||
<comment>Occurs when a baseline is used that has been flagged as obsolete.</comment>
|
||||
<data name="ResourceObsolete" xml:space="preserve">
|
||||
<value>The {0} '{1}' is obsolete. Consider switching to an alternative {0}.</value>
|
||||
<comment>Occurs when a resource is used that has been flagged as obsolete.</comment>
|
||||
</data>
|
||||
<data name="ConstrainedTargetBinding" xml:space="preserve">
|
||||
<value>Binding functions are not supported in this language mode.</value>
|
||||
|
@ -308,4 +308,10 @@
|
|||
<data name="WithinTrue" xml:space="preserve">
|
||||
<value>Within: {0}</value>
|
||||
</data>
|
||||
<data name="AliasReference" xml:space="preserve">
|
||||
<value>The {0} resource '{1}' is currently referencing '{2}' using the alias '{3}'. Consider updating the reference to use name or id directly.</value>
|
||||
</data>
|
||||
<data name="AliasSuppression" xml:space="preserve">
|
||||
<value>Suppression for the rule '{0}' was configured using the alias '{1}'. Consider updating the suppression to use the name or id directly.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
|
@ -12,16 +12,17 @@ namespace PSRule.Rules
|
|||
private const string ERROR_ACTION_PREFERENCE = "ErrorActionPreference";
|
||||
|
||||
private readonly PowerShell _Condition;
|
||||
private readonly ActionPreference _ErrorAction;
|
||||
|
||||
private bool _Disposed;
|
||||
|
||||
internal PowerShellCondition(PowerShell condition, ActionPreference errorAction)
|
||||
{
|
||||
_Condition = condition;
|
||||
_ErrorAction = errorAction;
|
||||
ErrorAction = errorAction;
|
||||
}
|
||||
|
||||
public ActionPreference ErrorAction { get; }
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_Disposed)
|
||||
|
@ -43,7 +44,7 @@ namespace PSRule.Rules
|
|||
public IConditionResult If()
|
||||
{
|
||||
_Condition.Streams.ClearStreams();
|
||||
_Condition.Runspace.SessionStateProxy.SetVariable(ERROR_ACTION_PREFERENCE, _ErrorAction);
|
||||
_Condition.Runspace.SessionStateProxy.SetVariable(ERROR_ACTION_PREFERENCE, ErrorAction);
|
||||
return GetResult(_Condition.Invoke<Runtime.RuleConditionResult>());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Newtonsoft.Json;
|
||||
using PSRule.Data;
|
||||
using PSRule.Definitions;
|
||||
using PSRule.Host;
|
||||
using PSRule.Definitions.Rules;
|
||||
using PSRule.Pipeline;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
|
@ -15,19 +16,33 @@ namespace PSRule.Rules
|
|||
/// Define a single rule.
|
||||
/// </summary>
|
||||
[JsonObject]
|
||||
public sealed class Rule : IDependencyTarget, ITargetInfo, IResource
|
||||
public sealed class Rule : IDependencyTarget, ITargetInfo, IResource, IRuleV1
|
||||
{
|
||||
/// <summary>
|
||||
/// A unique identifier for the rule.
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "ruleId", Required = Required.Always)]
|
||||
public string RuleId { get; set; }
|
||||
[JsonProperty(PropertyName = "id", Required = Required.Always)]
|
||||
public ResourceId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the rule.
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "name", Required = Required.Always)]
|
||||
public string Name => Id.Name;
|
||||
|
||||
/// <summary>
|
||||
/// Legacy. A unique identifier for the rule.
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "ruleId", Required = Required.Always)]
|
||||
[Obsolete("Use Id instead")]
|
||||
public string RuleId => Id.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Legacy. The name of the rule.
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "ruleName", Required = Required.Always)]
|
||||
public string RuleName { get; set; }
|
||||
[Obsolete("Use Name instead")]
|
||||
public string RuleName => Name;
|
||||
|
||||
/// <summary>
|
||||
/// The script file path where the rule is defined.
|
||||
|
@ -50,9 +65,12 @@ namespace PSRule.Rules
|
|||
[YamlIgnore]
|
||||
public string Synopsis => Info.Synopsis;
|
||||
|
||||
// Alias to synopsis
|
||||
/// <summary>
|
||||
/// Legacy. Alias to synopsis
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
[YamlIgnore]
|
||||
[Obsolete("Use Synopsis instead.")]
|
||||
public string Description => Info.Synopsis;
|
||||
|
||||
/// <summary>
|
||||
|
@ -74,9 +92,13 @@ namespace PSRule.Rules
|
|||
/// Other rules that must completed successfully before calling this rule.
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "dependsOn")]
|
||||
public string[] DependsOn { get; set; }
|
||||
public ResourceId[] DependsOn { get; set; }
|
||||
|
||||
string ITargetInfo.TargetName => RuleName;
|
||||
[JsonIgnore]
|
||||
[YamlIgnore]
|
||||
public ResourceFlags Flags { get; set; }
|
||||
|
||||
string ITargetInfo.TargetName => Name;
|
||||
|
||||
string ITargetInfo.TargetType => typeof(Rule).FullName;
|
||||
|
||||
|
@ -86,14 +108,20 @@ namespace PSRule.Rules
|
|||
|
||||
string IResource.ApiVersion => Specs.V1;
|
||||
|
||||
string IResource.Name => RuleName;
|
||||
string IResource.Name => Name;
|
||||
|
||||
ResourceTags IResource.Tags => Tag;
|
||||
|
||||
string ILanguageBlock.Id => RuleId;
|
||||
|
||||
string ILanguageBlock.SourcePath => Source.Path;
|
||||
|
||||
string ILanguageBlock.Module => Source.ModuleName;
|
||||
|
||||
[JsonIgnore]
|
||||
[YamlIgnore]
|
||||
public ResourceId? Ref { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[YamlIgnore]
|
||||
public ResourceId[] Alias { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Management.Automation;
|
||||
using Newtonsoft.Json;
|
||||
using PSRule.Definitions;
|
||||
using PSRule.Host;
|
||||
using PSRule.Definitions.Rules;
|
||||
using PSRule.Pipeline;
|
||||
using PSRule.Runtime;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace PSRule.Rules
|
||||
{
|
||||
|
@ -19,16 +20,18 @@ namespace PSRule.Rules
|
|||
/// <summary>
|
||||
/// Define an instance of a rule block. Each rule block has a unique id.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{RuleId} @{Source.Path}")]
|
||||
public sealed class RuleBlock : ILanguageBlock, IDependencyTarget, IDisposable, IResource
|
||||
[DebuggerDisplay("{Id} @{Source.Path}")]
|
||||
internal sealed class RuleBlock : ILanguageBlock, IDependencyTarget, IDisposable, IResource, IRuleV1
|
||||
{
|
||||
internal RuleBlock(SourceFile source, string ruleName, RuleHelpInfo info, ICondition condition, ResourceTags tag, string[] dependsOn, Hashtable configuration, RuleExtent extent, ActionPreference errorPreference)
|
||||
internal RuleBlock(SourceFile source, ResourceId id, ResourceId? @ref, RuleHelpInfo info, ICondition condition, ResourceTags tag, ResourceId[] alias, ResourceId[] dependsOn, Hashtable configuration, RuleExtent extent, ResourceFlags flags)
|
||||
{
|
||||
Source = source;
|
||||
RuleName = ruleName;
|
||||
Name = id.Name;
|
||||
|
||||
// Get fully qualified Id, either RuleName or Module\RuleName
|
||||
RuleId = ResourceHelper.GetRuleIdString(Source.ModuleName, ruleName);
|
||||
Id = id;
|
||||
Ref = @ref;
|
||||
Alias = alias;
|
||||
|
||||
Info = info;
|
||||
Condition = condition;
|
||||
|
@ -36,28 +39,34 @@ namespace PSRule.Rules
|
|||
DependsOn = dependsOn;
|
||||
Configuration = configuration;
|
||||
Extent = extent;
|
||||
ErrorPreference = errorPreference;
|
||||
Flags = flags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A unique identifier for the rule.
|
||||
/// </summary>
|
||||
public readonly string RuleId;
|
||||
public ResourceId Id { get; }
|
||||
|
||||
public ResourceId? Ref { get; }
|
||||
|
||||
public ResourceId[] Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the rule.
|
||||
/// </summary>
|
||||
public readonly string RuleName;
|
||||
public readonly string Name;
|
||||
|
||||
/// <summary>
|
||||
/// The body of the rule definition where conditions are provided that either pass or fail the rule.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
[YamlIgnore]
|
||||
public readonly ICondition Condition;
|
||||
|
||||
/// <summary>
|
||||
/// Other rules that must completed successfully before calling this rule.
|
||||
/// </summary>
|
||||
public readonly string[] DependsOn;
|
||||
public readonly ResourceId[] DependsOn;
|
||||
|
||||
/// <summary>
|
||||
/// Tags assigned to block. Tags are additional metadata used to select rules to execute and identify results.
|
||||
|
@ -78,17 +87,15 @@ namespace PSRule.Rules
|
|||
|
||||
internal readonly RuleExtent Extent;
|
||||
|
||||
internal readonly ActionPreference ErrorPreference;
|
||||
|
||||
string ILanguageBlock.Id => RuleId;
|
||||
[JsonIgnore]
|
||||
[YamlIgnore]
|
||||
public ResourceFlags Flags { get; }
|
||||
|
||||
string ILanguageBlock.SourcePath => Source.Path;
|
||||
|
||||
string ILanguageBlock.Module => Source.ModuleName;
|
||||
|
||||
string IDependencyTarget.RuleId => RuleId;
|
||||
|
||||
string[] IDependencyTarget.DependsOn => DependsOn;
|
||||
ResourceId[] IDependencyTarget.DependsOn => DependsOn;
|
||||
|
||||
bool IDependencyTarget.Dependency => Source.IsDependency();
|
||||
|
||||
|
@ -96,10 +103,20 @@ namespace PSRule.Rules
|
|||
|
||||
string IResource.ApiVersion => Specs.V1;
|
||||
|
||||
string IResource.Name => RuleName;
|
||||
string IResource.Name => Name;
|
||||
|
||||
ResourceTags IResource.Tags => Tag;
|
||||
|
||||
string IRuleV1.RuleName => Name;
|
||||
|
||||
ResourceTags IRuleV1.Tag => Tag;
|
||||
|
||||
string IRuleV1.Synopsis => Info.Synopsis;
|
||||
|
||||
string IRuleV1.Description => Info.Synopsis;
|
||||
|
||||
SourceFile IRuleV1.Source => Source;
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections;
|
||||
|
@ -20,11 +20,12 @@ namespace PSRule.Rules
|
|||
[JsonObject]
|
||||
public sealed class RuleRecord
|
||||
{
|
||||
internal RuleRecord(string runId, string ruleId, string ruleName, PSObject targetObject, string targetName, string targetType, ResourceTags tag, RuleHelpInfo info, Hashtable field, Hashtable data, TargetSourceInfo[] source, RuleOutcome outcome = RuleOutcome.None, RuleOutcomeReason reason = RuleOutcomeReason.None)
|
||||
internal RuleRecord(string runId, string ruleId, string ruleName, string @ref, PSObject targetObject, string targetName, string targetType, ResourceTags tag, RuleHelpInfo info, Hashtable field, Hashtable data, TargetSourceInfo[] source, RuleOutcome outcome = RuleOutcome.None, RuleOutcomeReason reason = RuleOutcomeReason.None)
|
||||
{
|
||||
RunId = runId;
|
||||
RuleId = ruleId;
|
||||
RuleName = ruleName;
|
||||
Ref = @ref;
|
||||
TargetObject = targetObject;
|
||||
TargetName = targetName;
|
||||
TargetType = targetType;
|
||||
|
@ -61,6 +62,8 @@ namespace PSRule.Rules
|
|||
[JsonProperty(PropertyName = "ruleName")]
|
||||
public readonly string RuleName;
|
||||
|
||||
public string Ref { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The outcome after the rule processes an object.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
@ -6,25 +6,27 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using PSRule.Configuration;
|
||||
using PSRule.Definitions;
|
||||
using PSRule.Runtime;
|
||||
|
||||
namespace PSRule.Rules
|
||||
{
|
||||
[DebuggerDisplay("{_Index.Count}")]
|
||||
internal sealed class RuleSuppressionFilter
|
||||
internal sealed class SuppressionFilter
|
||||
{
|
||||
private readonly HashSet<SuppressionKey> _Index;
|
||||
private readonly bool _IsEmpty;
|
||||
|
||||
public RuleSuppressionFilter(SuppressionOption option)
|
||||
public SuppressionFilter(RunspaceContext context, SuppressionOption option, IEnumerable<IResource> rules)
|
||||
{
|
||||
if (option == null || option.Count == 0)
|
||||
if (option == null || option.Count == 0 || rules == null)
|
||||
{
|
||||
_IsEmpty = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_Index = new HashSet<SuppressionKey>();
|
||||
Index(option);
|
||||
_Index = Index(context, option, rules);
|
||||
_IsEmpty = _Index.Count == 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,26 +79,36 @@ namespace PSRule.Rules
|
|||
}
|
||||
}
|
||||
|
||||
public bool Match(string ruleName, string targetName)
|
||||
public bool Match(ResourceId id, string targetName)
|
||||
{
|
||||
return !_IsEmpty &&
|
||||
!string.IsNullOrEmpty(ruleName) &&
|
||||
!string.IsNullOrEmpty(targetName) &&
|
||||
_Index.Contains(new SuppressionKey(ruleName, targetName));
|
||||
_Index.Contains(new SuppressionKey(id.Value, targetName));
|
||||
}
|
||||
|
||||
private void Index(SuppressionOption option)
|
||||
private static HashSet<SuppressionKey> Index(RunspaceContext context, SuppressionOption option, IEnumerable<IResource> rules)
|
||||
{
|
||||
var resolver = new ResourceIndex(rules);
|
||||
var index = new HashSet<SuppressionKey>();
|
||||
|
||||
// Read suppress rules into index combined key (RuleName + TargetName)
|
||||
foreach (var rule in option)
|
||||
{
|
||||
// Only add suppresion entries for rules that are loaded
|
||||
if (!resolver.TryFind(rule.Key, out var blockId, out var kind))
|
||||
continue;
|
||||
|
||||
if (kind == ResourceIdKind.Alias)
|
||||
context.WarnAliasSuppression(blockId.Value, rule.Key);
|
||||
|
||||
foreach (var targetName in rule.Value.TargetName)
|
||||
{
|
||||
var key = new SuppressionKey(rule.Key, targetName);
|
||||
if (!_Index.Contains(key))
|
||||
_Index.Add(key);
|
||||
var key = new SuppressionKey(blockId.Value, targetName);
|
||||
if (!index.Contains(key))
|
||||
index.Add(key);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
@ -9,7 +9,8 @@ namespace PSRule.Runtime
|
|||
internal sealed class LanguageScriptBlock : IDisposable
|
||||
{
|
||||
private readonly PowerShell _Block;
|
||||
private bool disposedValue;
|
||||
|
||||
private bool _Disposed;
|
||||
|
||||
public LanguageScriptBlock(PowerShell block)
|
||||
{
|
||||
|
@ -25,13 +26,13 @@ namespace PSRule.Runtime
|
|||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
if (!_Disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_Block.Dispose();
|
||||
}
|
||||
disposedValue = true;
|
||||
_Disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,6 @@ namespace PSRule.Runtime
|
|||
private const string SOURCE_OUTCOME_FAIL = "Rule.Outcome.Fail";
|
||||
private const string SOURCE_OUTCOME_PASS = "Rule.Outcome.Pass";
|
||||
private const string ERRORID_INVALIDRULERESULT = "PSRule.Runtime.InvalidRuleResult";
|
||||
private const string WARN_KEY_PROPERTY = "Property";
|
||||
private const string WARN_KEY_SEPARATOR = "_";
|
||||
|
||||
[ThreadStatic]
|
||||
|
@ -262,39 +261,6 @@ namespace PSRule.Runtime
|
|||
Writer.WriteWarning(PSRuleResources.RuleCountSuppressed, ruleCount, Binding.TargetName);
|
||||
}
|
||||
|
||||
public void WarnRuleNotFound()
|
||||
{
|
||||
if (Writer == null || !Writer.ShouldWriteWarning())
|
||||
return;
|
||||
|
||||
Writer.WriteWarning(PSRuleResources.RuleNotFound);
|
||||
}
|
||||
|
||||
public void WarnBaselineObsolete(string baselineId)
|
||||
{
|
||||
if (Writer == null || !Writer.ShouldWriteWarning())
|
||||
return;
|
||||
|
||||
Writer.WriteWarning(PSRuleResources.BaselineObsolete, baselineId);
|
||||
}
|
||||
|
||||
public void WarnPropertyObsolete(string variableName, string propertyName)
|
||||
{
|
||||
DebugPropertyObsolete(variableName, propertyName);
|
||||
if (Writer == null || !Writer.ShouldWriteWarning() || !ShouldWarnOnce(WARN_KEY_PROPERTY, variableName, propertyName))
|
||||
return;
|
||||
|
||||
Writer.WriteWarning(PSRuleResources.PropertyObsolete, variableName, propertyName);
|
||||
}
|
||||
|
||||
private void DebugPropertyObsolete(string variableName, string propertyName)
|
||||
{
|
||||
if (Writer == null || !Writer.ShouldWriteDebug())
|
||||
return;
|
||||
|
||||
Writer.WriteDebug(PSRuleResources.DebugPropertyObsolete, RuleBlock.RuleName, variableName, propertyName);
|
||||
}
|
||||
|
||||
public void ErrorInvaildRuleResult()
|
||||
{
|
||||
if (Writer == null || !Writer.ShouldWriteError())
|
||||
|
@ -304,7 +270,8 @@ namespace PSRule.Runtime
|
|||
exception: new RuleException(message: string.Format(
|
||||
Thread.CurrentThread.CurrentCulture,
|
||||
PSRuleResources.InvalidRuleResult,
|
||||
RuleBlock.RuleId)),
|
||||
RuleBlock.Id
|
||||
)),
|
||||
errorId: ERRORID_INVALIDRULERESULT,
|
||||
errorCategory: ErrorCategory.InvalidResult,
|
||||
targetObject: null
|
||||
|
@ -528,7 +495,7 @@ namespace PSRule.Runtime
|
|||
string.Format(
|
||||
Thread.CurrentThread.CurrentCulture,
|
||||
PSRuleResources.RuleStackTrace,
|
||||
RuleBlock.RuleName,
|
||||
RuleBlock.Name,
|
||||
RuleBlock.Extent.File,
|
||||
RuleBlock.Extent.StartLineNumber)
|
||||
);
|
||||
|
@ -541,7 +508,7 @@ namespace PSRule.Runtime
|
|||
: string.Concat(
|
||||
record.FullyQualifiedErrorId,
|
||||
",",
|
||||
RuleBlock.RuleName
|
||||
RuleBlock.Name
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -658,8 +625,9 @@ namespace PSRule.Runtime
|
|||
RuleBlock = ruleBlock;
|
||||
RuleRecord = new RuleRecord(
|
||||
runId: Pipeline.RunId,
|
||||
ruleId: ruleBlock.RuleId,
|
||||
ruleName: ruleBlock.RuleName,
|
||||
ruleId: ruleBlock.Id.Value,
|
||||
ruleName: ruleBlock.Name,
|
||||
@ref: ruleBlock.Ref.GetValueOrDefault().Name,
|
||||
targetObject: TargetObject.Value,
|
||||
targetName: Binding.TargetName,
|
||||
targetType: Binding.TargetType,
|
||||
|
@ -671,7 +639,7 @@ namespace PSRule.Runtime
|
|||
);
|
||||
|
||||
if (Writer != null)
|
||||
Writer.EnterScope(ruleBlock.RuleName);
|
||||
Writer.EnterScope(ruleBlock.Name);
|
||||
|
||||
// Starts rule execution timer
|
||||
_RuleTimer.Restart();
|
||||
|
@ -831,7 +799,7 @@ namespace PSRule.Runtime
|
|||
return null;
|
||||
}
|
||||
|
||||
private bool ShouldWarnOnce(params string[] key)
|
||||
internal bool ShouldWarnOnce(params string[] key)
|
||||
{
|
||||
var combinedKey = string.Join(WARN_KEY_SEPARATOR, key);
|
||||
if (_WarnOnce.Contains(combinedKey))
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
[
|
||||
{
|
||||
// Synopsis: A rule with an alias.
|
||||
"apiVersion": "github.com/microsoft/PSRule/v1",
|
||||
"kind": "Rule",
|
||||
"metadata": {
|
||||
"name": "JSON.RuleWithAlias1",
|
||||
"alias": [
|
||||
"JSON.AlternativeName"
|
||||
],
|
||||
"ref": "PSRZZ.0003"
|
||||
},
|
||||
"spec": {
|
||||
"condition": {
|
||||
"field": "name",
|
||||
"exists": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
|
@ -0,0 +1,7 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
# Synopsis: A rule with an alias.
|
||||
Rule 'PS.RuleWithAlias1' -Ref 'PSRZZ.0001' -Alias 'PS.AlternativeName' {
|
||||
$Assert.HasField($TargetObject, 'name');
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
---
|
||||
# Synopsis: A rule with an alias.
|
||||
apiVersion: github.com/microsoft/PSRule/v1
|
||||
kind: Rule
|
||||
metadata:
|
||||
name: 'YAML.RuleWithAlias1'
|
||||
alias:
|
||||
- 'YAML.AlternativeName'
|
||||
ref: PSRZZ.0002
|
||||
spec:
|
||||
condition:
|
||||
field: name
|
||||
exists: true
|
|
@ -708,7 +708,7 @@ Describe 'Baseline' -Tag 'Baseline' {
|
|||
$Null = @($testObject | Invoke-PSRule -Path $ruleFilePath,$baselineFilePath -Baseline 'TestBaseline5' -WarningVariable outWarn -WarningAction SilentlyContinue);
|
||||
$warnings = @($outWarn);
|
||||
$warnings.Length | Should -Be 1;
|
||||
$warnings[0] | Should -BeExactly "The baseline 'TestBaseline5' is obsolete. Consider switching to an alternative baseline.";
|
||||
$warnings[0] | Should -BeExactly "The Baseline 'TestBaseline5' is obsolete. Consider switching to an alternative Baseline.";
|
||||
}
|
||||
|
||||
It 'With -Module' {
|
||||
|
|
|
@ -281,6 +281,16 @@ Describe 'Invoke-PSRule' -Tag 'Invoke-PSRule','Common' {
|
|||
$result | Should -BeOfType PSRule.Rules.RuleRecord;
|
||||
($result | Where-Object { $_.TargetName -eq 'TestObject1' }).OutcomeReason | Should -BeIn 'Suppressed';
|
||||
($result | Where-Object { $_.TargetName -eq 'TestObject2' }).OutcomeReason | Should -BeIn 'Processed';
|
||||
|
||||
# With aliases
|
||||
$aliasRuleFilePath = @(
|
||||
(Join-Path -Path $here -ChildPath 'FromFileAlias.Rule.jsonc')
|
||||
(Join-Path -Path $here -ChildPath 'FromFileAlias.Rule.ps1')
|
||||
)
|
||||
$option = New-PSRuleOption -SuppressTargetName @{ 'JSON.AlternativeName' = 'TestObject1'; 'PSRZZ.0003' = 'testobject2'; 'PS.AlternativeName' = 'TestObject1' };
|
||||
$result = $testObject | Invoke-PSRule -Path $aliasRuleFilePath -Option $option -Name 'JSON.AlternativeName','PS.RuleWithAlias1' -Outcome All;
|
||||
($result | Where-Object { $_.TargetName -eq 'TestObject1' }).OutcomeReason | Should -BeIn 'Suppressed';
|
||||
$result.Count | Should -Be 4;
|
||||
}
|
||||
|
||||
It 'Processes configuration' {
|
||||
|
@ -1149,9 +1159,9 @@ Describe 'Invoke-PSRule' -Tag 'Invoke-PSRule','Common' {
|
|||
$warningMessages.Length | Should -Be 2;
|
||||
|
||||
$warningMessages[0] | Should -BeOfType [System.Management.Automation.WarningRecord];
|
||||
$warningMessages[0].Message | Should -BeExactly "Rule 'FromFile1' was suppressed for 'TestObject1'.";
|
||||
$warningMessages[0].Message | Should -BeExactly "Rule '.\FromFile1' was suppressed for 'TestObject1'.";
|
||||
$warningMessages[1] | Should -BeOfType [System.Management.Automation.WarningRecord];
|
||||
$warningMessages[1].Message | Should -BeExactly "Rule 'FromFile2' was suppressed for 'TestObject1'.";
|
||||
$warningMessages[1].Message | Should -BeExactly "Rule '.\FromFile2' was suppressed for 'TestObject1'.";
|
||||
}
|
||||
|
||||
It 'No warnings' {
|
||||
|
@ -1632,7 +1642,7 @@ Describe 'Get-PSRule' -Tag 'Get-PSRule','Common' {
|
|||
$result | Should -Not -BeNullOrEmpty;
|
||||
$result.Length | Should -Be 3;
|
||||
$result.RuleName | Should -BeIn 'M1.Rule1', 'M1.Rule2', 'M1.YamlTestName';
|
||||
($result | Get-Member).TypeName | Should -BeIn 'PSRule.Rules.Rule';
|
||||
$result | Should -BeOfType 'PSRule.Definitions.Rules.IRuleV1';
|
||||
}
|
||||
finally {
|
||||
Pop-Location;
|
||||
|
@ -1972,7 +1982,7 @@ Describe 'Get-PSRule' -Tag 'Get-PSRule','Common' {
|
|||
$result = @(Get-PSRule -Path $ruleFilePath -Name 'FromFile4');
|
||||
$result | Should -Not -BeNullOrEmpty;
|
||||
$result.Length | Should -Be 1;
|
||||
$result[0].DependsOn | Should -BeIn 'FromFile3';
|
||||
$result[0].DependsOn | Should -BeIn '.\FromFile3';
|
||||
|
||||
# Get a list of rules with dependencies
|
||||
$result = @(Get-PSRule -Path $ruleFilePath -Name 'FromFile4' -IncludeDependencies);
|
||||
|
@ -1980,7 +1990,7 @@ Describe 'Get-PSRule' -Tag 'Get-PSRule','Common' {
|
|||
$result.Length | Should -Be 2;
|
||||
$result[0].RuleName | Should -Be 'FromFile3';
|
||||
$result[1].RuleName | Should -Be 'FromFile4';
|
||||
$result[1].DependsOn | Should -BeIn 'FromFile3';
|
||||
$result[1].DependsOn | Should -BeIn '.\FromFile3';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1988,7 +1998,7 @@ Describe 'Get-PSRule' -Tag 'Get-PSRule','Common' {
|
|||
It 'Wide' {
|
||||
$result = Get-PSRule -Path $ruleFilePath -Name 'FromFile1' -OutputFormat Wide;
|
||||
$result | Should -Not -BeNullOrEmpty;
|
||||
($result | Get-Member).TypeName | Should -BeIn 'PSRule.Rules.Rule+Wide';
|
||||
($result | Get-Member).TypeName | Should -BeIn 'PSRule.Definitions.Rules.IRuleV1+Wide';
|
||||
}
|
||||
|
||||
It 'Yaml' {
|
||||
|
@ -2035,31 +2045,31 @@ Describe 'Get-PSRule' -Tag 'Get-PSRule','Common' {
|
|||
Title = '0 space indentation'
|
||||
OptionHashtable = @{'Output.JsonIndent' = 0}
|
||||
YamlPath = (Join-Path -Path $here -ChildPath 'PSRule.Tests9.yml')
|
||||
ExpectedJson = '"ruleId":"FromFile1","ruleName":"FromFile1"'
|
||||
ExpectedJson = '"ruleId":"\.\\\\FromFile1","ruleName":"FromFile1"'
|
||||
}
|
||||
@{
|
||||
Title = '1 space indentation'
|
||||
OptionHashtable = @{'Output.JsonIndent' = 1}
|
||||
YamlPath = (Join-Path -Path $here -ChildPath 'PSRule.Tests10.yml')
|
||||
ExpectedJson = "`"ruleId`": `"FromFile1`",$([Environment]::Newline) `"ruleName`": `"FromFile1`""
|
||||
ExpectedJson = "`"ruleId`": `"\.\\\\FromFile1`",$([Environment]::Newline) `"ruleName`": `"FromFile1`""
|
||||
}
|
||||
@{
|
||||
Title = '2 space indentation'
|
||||
OptionHashtable = @{'Output.JsonIndent' = 2}
|
||||
YamlPath = (Join-Path -Pat $here -ChildPath 'PSRule.Tests11.yml')
|
||||
ExpectedJson = "`"ruleId`": `"FromFile1`",$([Environment]::Newline) `"ruleName`": `"FromFile1`""
|
||||
ExpectedJson = "`"ruleId`": `"\.\\\\FromFile1`",$([Environment]::Newline) `"ruleName`": `"FromFile1`""
|
||||
}
|
||||
@{
|
||||
Title = '3 space indentation'
|
||||
OptionHashtable = @{'Output.JsonIndent' = 3}
|
||||
YamlPath = (Join-Path -Pat $here -ChildPath 'PSRule.Tests12.yml')
|
||||
ExpectedJson = "`"ruleId`": `"FromFile1`",$([Environment]::Newline) `"ruleName`": `"FromFile1`""
|
||||
ExpectedJson = "`"ruleId`": `"\.\\\\FromFile1`",$([Environment]::Newline) `"ruleName`": `"FromFile1`""
|
||||
}
|
||||
@{
|
||||
Title = '4 space indentation'
|
||||
OptionHashtable = @{'Output.JsonIndent' = 4}
|
||||
YamlPath = (Join-Path -Pat $here -ChildPath 'PSRule.Tests13.yml')
|
||||
ExpectedJson = "`"ruleId`": `"FromFile1`",$([Environment]::Newline) `"ruleName`": `"FromFile1`""
|
||||
ExpectedJson = "`"ruleId`": `"\.\\\\FromFile1`",$([Environment]::Newline) `"ruleName`": `"FromFile1`""
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -2116,12 +2126,12 @@ Describe 'Get-PSRule' -Tag 'Get-PSRule','Common' {
|
|||
Context 'Normalizie range' {
|
||||
It 'Normalize to 0 when indentation is less than 0' {
|
||||
$result = Get-PSRule -Path $ruleFilePath -Name 'FromFile1' -OutputFormat 'Json' -Option @{'Output.JsonIndent' = -1};
|
||||
$result | Should -MatchExactly '"ruleId":"FromFile1","ruleName":"FromFile1"';
|
||||
$result | Should -MatchExactly '"ruleId":"\.\\\\FromFile1","ruleName":"FromFile1"';
|
||||
}
|
||||
|
||||
It 'Normalize to 4 when indentation is more than 4' {
|
||||
$result = Get-PSRule -Path $ruleFilePath -Name 'FromFile1' -OutputFormat 'Json' -Option @{'Output.JsonIndent' = 5};
|
||||
$result | Should -MatchExactly "`"ruleId`": `"FromFile1`",$([Environment]::Newline) `"ruleName`": `"FromFile1`"";
|
||||
$result | Should -MatchExactly "`"ruleId`": `"\.\\\\FromFile1`",$([Environment]::Newline) `"ruleName`": `"FromFile1`"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ Describe 'Scenarios -- fruit' -Tag 'EndToEnd','fruit' {
|
|||
It 'Returns rules' {
|
||||
$result = @(Get-PSRule -Path (Join-Path -Path $rootPath -ChildPath docs/scenarios/fruit));
|
||||
$result.Count | Should -Be 1;
|
||||
$result | Should -BeOfType PSRule.Rules.Rule;
|
||||
$result | Should -BeOfType PSRule.Definitions.Rules.IRuleV1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -741,6 +741,45 @@ Describe 'New-PSRuleOption' -Tag 'Option','New-PSRuleOption' {
|
|||
}
|
||||
}
|
||||
|
||||
Context 'Read Execution.AliasReferenceWarning' {
|
||||
It 'from default' {
|
||||
$option = New-PSRuleOption -Default;
|
||||
$option.Execution.AliasReferenceWarning | Should -Be $True;
|
||||
}
|
||||
|
||||
It 'from Hashtable' {
|
||||
$option = New-PSRuleOption -Option @{ 'Execution.AliasReferenceWarning' = $False };
|
||||
$option.Execution.AliasReferenceWarning | Should -Be $False;
|
||||
}
|
||||
|
||||
It 'from YAML' {
|
||||
$option = New-PSRuleOption -Option (Join-Path -Path $here -ChildPath 'PSRule.Tests.yml');
|
||||
$option.Execution.AliasReferenceWarning | Should -Be $False;
|
||||
}
|
||||
|
||||
It 'from Environment' {
|
||||
try {
|
||||
# With bool
|
||||
$Env:PSRULE_EXECUTION_ALIASREFERENCEWARNING = 'false';
|
||||
$option = New-PSRuleOption;
|
||||
$option.Execution.AliasReferenceWarning | Should -Be $False;
|
||||
|
||||
# With int
|
||||
$Env:PSRULE_EXECUTION_ALIASREFERENCEWARNING = '0';
|
||||
$option = New-PSRuleOption;
|
||||
$option.Execution.AliasReferenceWarning | Should -Be $False;
|
||||
}
|
||||
finally {
|
||||
Remove-Item 'Env:PSRULE_EXECUTION_ALIASREFERENCEWARNING' -Force;
|
||||
}
|
||||
}
|
||||
|
||||
It 'from parameter' {
|
||||
$option = New-PSRuleOption -AliasReferenceWarning $False -Path $emptyOptionsFilePath;
|
||||
$option.Execution.AliasReferenceWarning | Should -Be $False;
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Read Include.Path' {
|
||||
It 'from default' {
|
||||
$option = New-PSRuleOption -Default;
|
||||
|
@ -1707,6 +1746,20 @@ Describe 'Set-PSRuleOption' -Tag 'Option','Set-PSRuleOption' {
|
|||
}
|
||||
}
|
||||
|
||||
Context 'Read Execution.SuppressedRuleWarning' {
|
||||
It 'from parameter' {
|
||||
$option = Set-PSRuleOption -SuppressedRuleWarning $False @optionParams;
|
||||
$option.Execution.SuppressedRuleWarning | Should -Be $False;
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Read Execution.AliasReferenceWarning' {
|
||||
It 'from parameter' {
|
||||
$option = Set-PSRuleOption -AliasReferenceWarning $False @optionParams;
|
||||
$option.Execution.AliasReferenceWarning | Should -Be $False;
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Read Input.Format' {
|
||||
It 'from parameter' {
|
||||
$option = Set-PSRuleOption -Format 'Yaml' @optionParams;
|
||||
|
|
|
@ -40,6 +40,15 @@
|
|||
<None Update="FromFile.Rule.yaml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="FromFileAlias.Rule.jsonc">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="FromFileAlias.Rule.ps1">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="FromFileAlias.Rule.yaml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="FromFileConventions.Rule.ps1">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
@ -73,6 +82,9 @@
|
|||
<None Update="PSRule.Tests.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="PSRule.Tests14.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="PSRule.Tests6.yml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
|
|
@ -45,6 +45,7 @@ binding:
|
|||
|
||||
# Configure execution options
|
||||
execution:
|
||||
aliasReferenceWarning: false
|
||||
languageMode: ConstrainedLanguage
|
||||
inconclusiveWarning: false
|
||||
notProcessedWarning: false
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# These are options for unit tests
|
||||
|
||||
suppression:
|
||||
YAML.RuleWithAlias1:
|
||||
- 'TestObject1'
|
||||
PSRZZ.0001:
|
||||
- 'TestObject1'
|
||||
JSON.AlternativeName:
|
||||
- 'TestObject2'
|
||||
YAML.AlternativeName:
|
||||
- 'TestObject2'
|
||||
.\PSRZZ.0003:
|
||||
- 'TestObject3'
|
||||
'.\PS.AlternativeName':
|
||||
- 'TestObject3'
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections;
|
||||
using PSRule.Definitions;
|
||||
using PSRule.Rules;
|
||||
using PSRule.Definitions.Rules;
|
||||
using Xunit;
|
||||
|
||||
namespace PSRule
|
||||
|
|
|
@ -24,9 +24,9 @@ namespace PSRule
|
|||
var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, null, null, null, new OptionContext(), null), new TestWriter(GetOption()));
|
||||
context.Init(GetSource());
|
||||
context.Begin();
|
||||
var rule = HostHelper.GetRuleYaml(GetSource(), context).ToArray();
|
||||
var rule = HostHelper.GetRule(GetSource(), context, includeDependencies: false);
|
||||
Assert.NotNull(rule);
|
||||
Assert.Equal("BasicRule", rule[0].RuleName);
|
||||
Assert.Equal("BasicRule", rule[0].Name);
|
||||
|
||||
var hashtable = rule[0].Tag.ToHashtable();
|
||||
Assert.Equal("tag", hashtable["feature"]);
|
||||
|
@ -120,6 +120,8 @@ namespace PSRule
|
|||
Assert.True(yamlObjectPath.Condition.If().AllOf());
|
||||
}
|
||||
|
||||
#region Helper methods
|
||||
|
||||
private static PSRuleOption GetOption()
|
||||
{
|
||||
return new PSRuleOption();
|
||||
|
@ -149,7 +151,7 @@ namespace PSRule
|
|||
private static RuleBlock GetRuleVisitor(RunspaceContext context, string name)
|
||||
{
|
||||
var block = HostHelper.GetRuleYamlBlocks(GetSource(), context);
|
||||
return block.FirstOrDefault(s => s.RuleName == name);
|
||||
return block.FirstOrDefault(s => s.Name == name);
|
||||
}
|
||||
|
||||
private static void ImportSelectors(RunspaceContext context)
|
||||
|
@ -163,5 +165,7 @@ namespace PSRule
|
|||
{
|
||||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
|
||||
}
|
||||
|
||||
#endregion Helper methods
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using PSRule.Configuration;
|
||||
using PSRule.Definitions;
|
||||
using PSRule.Host;
|
||||
using PSRule.Pipeline;
|
||||
using PSRule.Rules;
|
||||
using PSRule.Runtime;
|
||||
using Xunit;
|
||||
using Assert = Xunit.Assert;
|
||||
|
||||
namespace PSRule
|
||||
{
|
||||
public sealed class SuppressionFilterTests
|
||||
{
|
||||
[Fact]
|
||||
public void Match()
|
||||
{
|
||||
var option = GetOption();
|
||||
var context = new RunspaceContext(PipelineContext.New(option, null, null, null, null, null, new OptionContext(), null), new TestWriter(option));
|
||||
context.Init(GetSource());
|
||||
context.Begin();
|
||||
var rules = HostHelper.GetRule(GetSource(), context, includeDependencies: false);
|
||||
var filter = new SuppressionFilter(context, option.Suppression, rules);
|
||||
|
||||
Assert.True(filter.Match(new ResourceId(".", "YAML.RuleWithAlias1", ResourceIdKind.Unknown), "TestObject1"));
|
||||
Assert.False(filter.Match(new ResourceId(".", "JSON.RuleWithAlias1", ResourceIdKind.Unknown), "TestObject1"));
|
||||
Assert.True(filter.Match(new ResourceId(".", "PS.RuleWithAlias1", ResourceIdKind.Unknown), "TestObject1"));
|
||||
|
||||
Assert.True(filter.Match(new ResourceId(".", "YAML.RuleWithAlias1", ResourceIdKind.Unknown), "TestObject2"));
|
||||
Assert.True(filter.Match(new ResourceId(".", "JSON.RuleWithAlias1", ResourceIdKind.Unknown), "TestObject2"));
|
||||
Assert.False(filter.Match(new ResourceId(".", "PS.RuleWithAlias1", ResourceIdKind.Unknown), "TestObject2"));
|
||||
|
||||
Assert.False(filter.Match(new ResourceId(".", "YAML.RuleWithAlias1", ResourceIdKind.Unknown), "TestObject3"));
|
||||
Assert.True(filter.Match(new ResourceId(".", "JSON.RuleWithAlias1", ResourceIdKind.Unknown), "TestObject3"));
|
||||
Assert.True(filter.Match(new ResourceId(".", "PS.RuleWithAlias1", ResourceIdKind.Unknown), "TestObject3"));
|
||||
}
|
||||
|
||||
#region Helper methods
|
||||
|
||||
private Source[] GetSource()
|
||||
{
|
||||
var builder = new SourcePipelineBuilder(null, null);
|
||||
builder.Directory(GetSourcePath("FromFileAlias.Rule.yaml"));
|
||||
builder.Directory(GetSourcePath("FromFileAlias.Rule.jsonc"));
|
||||
builder.Directory(GetSourcePath("FromFileAlias.Rule.ps1"));
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
private static PSRuleOption GetOption(string path = "PSRule.Tests14.yml")
|
||||
{
|
||||
return PSRuleOption.FromFileOrEmpty(path);
|
||||
}
|
||||
|
||||
private static string GetSourcePath(string fileName)
|
||||
{
|
||||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
|
||||
}
|
||||
|
||||
#endregion Helper methods
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче