From 85fa989a41c24ea1e95539149f52d3df722520eb Mon Sep 17 00:00:00 2001 From: Bernie White Date: Fri, 26 Aug 2022 15:13:44 +1000 Subject: [PATCH] Added sub-selectors for YAML and JSON #1024 #1045 (#1238) --- .markdownlint.json | 2 +- .vscode/settings.json | 4 +- docs/CHANGELOG-v2.md | 19 +- docs/expressions/SubSelectors.Rule.jsonc | 74 + docs/expressions/SubSelectors.Rule.yaml | 57 + docs/expressions/functions.md | 12 +- docs/expressions/sub-selectors.md | 272 ++ docs/versioning.md | 37 + mkdocs.yml | 7 +- schemas/PSRule-language.schema.json | 3228 ++++++++++------- .../Common/ConditionResultExtensions.cs | 11 +- src/PSRule/Common/JsonConverters.cs | 70 +- src/PSRule/Common/PSObjectExtensions.cs | 5 - src/PSRule/Common/YamlConverters.cs | 43 +- src/PSRule/Configuration/PipelineHook.cs | 7 +- .../Expressions/ExpressionContext.cs | 16 +- .../Definitions/Expressions/Functions.cs | 2 +- .../Expressions/LanguageExpressions.cs | 110 +- .../Definitions/Expressions/Primitives.cs | 14 +- src/PSRule/Definitions/ILanguageBlock.cs | 17 + src/PSRule/Definitions/Resource.cs | 68 + src/PSRule/Definitions/Rules/Rule.cs | 75 +- src/PSRule/Definitions/Rules/RuleVisitor.cs | 14 +- .../Definitions/Selectors/SelectorVisitor.cs | 7 +- .../SuppressionGroupVisitor.cs | 7 +- src/PSRule/Host/HostHelper.cs | 11 +- src/PSRule/Pipeline/PipelineBuilder.cs | 4 +- src/PSRule/Pipeline/PipelineContext.cs | 5 +- src/PSRule/Pipeline/PipelineHookActions.cs | 61 +- src/PSRule/Pipeline/TargetBinder.cs | 142 +- .../Resources/PSRuleResources.Designer.cs | 764 ++-- src/PSRule/Resources/PSRuleResources.resx | 3 + src/PSRule/Runtime/LanguageScope.cs | 68 +- .../Runtime/ObjectPath/PathExpression.cs | 13 +- .../ObjectPath/PathExpressionBuilder.cs | 56 +- src/PSRule/Runtime/RunspaceContext.cs | 12 +- .../FromFileSubSelector.Rule.jsonc | 66 + .../FromFileSubSelector.Rule.yaml | 52 + tests/PSRule.Tests/FunctionBuilderTests.cs | 2 +- tests/PSRule.Tests/FunctionTests.cs | 2 +- tests/PSRule.Tests/PSRule.Tests.csproj | 6 + tests/PSRule.Tests/RulesTests.cs | 126 +- tests/PSRule.Tests/SelectorTests.cs | 2 +- tests/PSRule.Tests/TargetBinderTests.cs | 19 +- 44 files changed, 3669 insertions(+), 1923 deletions(-) create mode 100644 docs/expressions/SubSelectors.Rule.jsonc create mode 100644 docs/expressions/SubSelectors.Rule.yaml create mode 100644 docs/expressions/sub-selectors.md create mode 100644 docs/versioning.md create mode 100644 tests/PSRule.Tests/FromFileSubSelector.Rule.jsonc create mode 100644 tests/PSRule.Tests/FromFileSubSelector.Rule.yaml diff --git a/.markdownlint.json b/.markdownlint.json index 26eef824f2..e38419f104 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -20,7 +20,7 @@ "no-reversed-links": true, "no-multiple-blanks": true, "line-length": { - "line_length": 100, + "line_length": 120, "code_blocks": false, "tables": false, "headers": true diff --git a/.vscode/settings.json b/.vscode/settings.json index 75e83e463a..199be65f0d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,7 +22,8 @@ "./schemas/PSRule-language.schema.json": [ "/tests/PSRule.Tests/**.Rule.yaml", "/tests/PSRule.Tests/**/**.Rule.yaml", - "/docs/scenarios/*/*.Rule.yaml" + "/docs/scenarios/*/*.Rule.yaml", + "/docs/expressions/**/*.Rule.yaml" ] }, "json.schemas": [ @@ -30,6 +31,7 @@ "fileMatch": [ "/tests/PSRule.Tests/**.Rule.jsonc", "/tests/PSRule.Tests/**/**.Rule.jsonc", + "/docs/expressions/**/*.Rule.jsonc" ], "url": "./schemas/PSRule-resources.schema.json" } diff --git a/docs/CHANGELOG-v2.md b/docs/CHANGELOG-v2.md index 0c4296bd67..45bc126391 100644 --- a/docs/CHANGELOG-v2.md +++ b/docs/CHANGELOG-v2.md @@ -13,10 +13,26 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers **Experimental features**: -- Functions within YAML expressions can be used to perform manipulation prior to testing a condition. +- Functions within YAML and JSON expressions can be used to perform manipulation prior to testing a condition. + See [functions][3] for more information. +- Sub-selectors within YAML and JSON expressions can be used to filter rules and list properties. + See [sub-selectors][4] for more information. + + [3]: expressions/functions.md + [4]: expressions/sub-selectors.md ## Unreleased +What's changed since pre-release v2.4.0-B0039: + +- New features: + - **Experimental**: Added support for sub-selectors YAML and JSON expressions by @BernieWhite. + [#1024](https://github.com/microsoft/PSRule/issues/1024) + [#1045](https://github.com/microsoft/PSRule/issues/1045) + - Sub-selector pre-conditions add an additional expression to determine if a rule is executed. + - Sub-selector object filters provide an way to filter items from list properties. + - See [sub-selectors][4] for more information. + ## v2.4.0-B0039 (pre-release) What's changed since pre-release v2.4.0-B0022: @@ -27,6 +43,7 @@ What's changed since pre-release v2.4.0-B0022: - Added conversion functions `boolean`, `string`, and `integer`. - Added lookup functions `configuration`, and `path`. - Added string functions `concat`, `substring`. + - See [functions][3] for more information. - Bug fixes: - Fixed reporting of duplicate identifiers which were not generating an error for all cases by @BernieWhite. [#1229](https://github.com/microsoft/PSRule/issues/1229) diff --git a/docs/expressions/SubSelectors.Rule.jsonc b/docs/expressions/SubSelectors.Rule.jsonc new file mode 100644 index 0000000000..699cfca155 --- /dev/null +++ b/docs/expressions/SubSelectors.Rule.jsonc @@ -0,0 +1,74 @@ +[ + { + // Synopsis: A rule with a sub-selector precondition. + "apiVersion": "github.com/microsoft/PSRule/v1", + "kind": "Rule", + "metadata": { + "name": "Json.Subselector.Precondition" + }, + "spec": { + "where": { + "field": "kind", + "equals": "api" + }, + "condition": { + "field": "resources", + "count": 10 + } + } + }, + { + // Synopsis: A rule with a sub-selector filter. + "apiVersion": "github.com/microsoft/PSRule/v1", + "kind": "Rule", + "metadata": { + "name": "Json.Subselector.Filter" + }, + "spec": { + "condition": { + "field": "resources", + "where": { + "type": ".", + "equals": "Microsoft.Web/sites/config" + }, + "allOf": [ + { + "field": "properties.detailedErrorLoggingEnabled", + "equals": true + } + ] + } + } + }, + { + // Synopsis: A rule with a sub-selector filter. + "apiVersion": "github.com/microsoft/PSRule/v1", + "kind": "Rule", + "metadata": { + "name": "Json.Subselector.FilterOr" + }, + "spec": { + "condition": { + "anyOf": [ + { + "field": "resources", + "where": { + "type": ".", + "equals": "Microsoft.Web/sites/config" + }, + "allOf": [ + { + "field": "properties.detailedErrorLoggingEnabled", + "equals": true + } + ] + }, + { + "field": "resources", + "exists": false + } + ] + } + } + } +] diff --git a/docs/expressions/SubSelectors.Rule.yaml b/docs/expressions/SubSelectors.Rule.yaml new file mode 100644 index 0000000000..6d373392b3 --- /dev/null +++ b/docs/expressions/SubSelectors.Rule.yaml @@ -0,0 +1,57 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# +# YAML-based rules for documentation +# + +--- +# Synopsis: A rule with a sub-selector precondition. +apiVersion: github.com/microsoft/PSRule/v1 +kind: Rule +metadata: + name: Yaml.Subselector.Precondition +spec: + where: + field: 'kind' + equals: 'api' + condition: + field: resources + count: 10 + +--- +# Synopsis: A rule with a sub-selector filter. +apiVersion: github.com/microsoft/PSRule/v1 +kind: Rule +metadata: + name: Yaml.Subselector.Filter +spec: + condition: + field: resources + where: + type: '.' + equals: 'Microsoft.Web/sites/config' + allOf: + - field: properties.detailedErrorLoggingEnabled + equals: true + +--- +# Synopsis: A rule with a sub-selector filter. +apiVersion: github.com/microsoft/PSRule/v1 +kind: Rule +metadata: + name: Yaml.Subselector.FilterOr +spec: + condition: + anyOf: + + - field: resources + where: + type: '.' + equals: 'Microsoft.Web/sites/config' + allOf: + - field: properties.detailedErrorLoggingEnabled + equals: true + + - field: resources + exists: false diff --git a/docs/expressions/functions.md b/docs/expressions/functions.md index 124ed9f1d1..deab0deda1 100644 --- a/docs/expressions/functions.md +++ b/docs/expressions/functions.md @@ -1,16 +1,22 @@ -# Expression functions +# Functions !!! Abstract - Functions are an advanced lanaguage feature specific to YAML and JSON resources. + _Functions_ are an advanced lanaguage feature specific to YAML and JSON expressions. That extend the language to allow for more complex use cases with expressions. + Functions don't apply to script expressions because PowerShell already has rich support for complex manipulation. !!! Experimental - Functions are a work in progress and subject to change. + _Functions_ are a work in progress and subject to change. We hope to add more functions, broader support, and more detailed documentation in the future. [Join or start a disucssion][1] to let us know how we can improve this feature going forward. [1]: https://github.com/microsoft/PSRule/discussions +Functions cover two (2) main scenarios: + +- **Transformation** — you need to perform minor transformation before a condition. +- **Configuration** — you want to configure an input into a condition. + ## Using functions It may be necessary to perform minor transformation before evaluating a condition. diff --git a/docs/expressions/sub-selectors.md b/docs/expressions/sub-selectors.md new file mode 100644 index 0000000000..c502f90f20 --- /dev/null +++ b/docs/expressions/sub-selectors.md @@ -0,0 +1,272 @@ +# Sub-selectors + +!!! Abstract + This topic covers _sub-selectors_ which are a PSRule language feature specific to YAML and JSON expressions. + They are useful for filtering out objects that you do not want to evaluate. + Sub-selectors don't apply to script expressions because PowerShell already has rich support for filtering. + +!!! Experimental + _Sub-selectors_ are a work in progress and subject to change. + We hope to add broader support, and more detailed documentation in the future. + [Join or start a disucssion][1] to let us know how we can improve this feature going forward. + + [1]: https://github.com/microsoft/PSRule/discussions + +Sub-selectors cover two (2) main scenarios: + +- **Pre-conditions** — you want to filtering out objects before a rule is run. +- **Object filtering** — you want to limit a condition to specific elements in a list of items. + +## Pre-conditions + +PSRule can process many different types of objects. +Rules however, are normally written to test a specific property or type of object. +So it is important that rules only run on objects that you want to evaluate. +Pre-condition sub-selectors are one way you can determine if a rule should be run. + +To use a sub-selector as a pre-condition, use the `where` property, directly under the `spec`. +The expressions in the sub-selector follow the same form that you can use in rules. + +For example: + +=== "YAML" + + ```yaml hl_lines="8-10" + --- + # Synopsis: A rule with a sub-selector precondition. + apiVersion: github.com/microsoft/PSRule/v1 + kind: Rule + metadata: + name: Yaml.Subselector.Precondition + spec: + where: + field: 'kind' + equals: 'api' + condition: + field: resources + count: 10 + ``` + +=== "JSON" + + ```json hl_lines="9-12" + { + // Synopsis: A rule with a sub-selector precondition. + "apiVersion": "github.com/microsoft/PSRule/v1", + "kind": "Rule", + "metadata": { + "name": "Json.Subselector.Precondition" + }, + "spec": { + "where": { + "field": "kind", + "equals": "api" + }, + "condition": { + "field": "resources", + "count": 10 + } + } + } + ``` + +In the example: + +1. The `where` property is the start of a sub-selector. +2. The sub-selector checks if the `kind` property equals `api`. + +The rule does not run if the: + +- The object does not have a `kind` property. **OR** +- The value of the `kind` property is not `api`. + +!!! Tip + Other types of pre-conditions also exist that allow you to filter based on type or by a shared selector. + +## Object filter + +When you are evaluating an object, you can use sub-selectors to limit the condition. +This is helpful when dealing with properties that are a list of items. +Properties that contain a list of items may contain a sub-set of items that you want to evaluate. + +For example, the object may look like this: + +=== "YAML" + + ```yaml + name: app1 + type: Microsoft.Web/sites + resources: + - name: web + type: Microsoft.Web/sites/config + properties: + detailedErrorLoggingEnabled: true + ``` + +=== "JSON" + + ```json + { + "name": "app1", + "type": "Microsoft.Web/sites", + "resources": [ + { + "name": "web", + "type": "Microsoft.Web/sites/config", + "properties": { + "detailedErrorLoggingEnabled": true + } + } + ] + } + ``` + +A rule to test if any sub-resources with the `detailedErrorLoggingEnabled` set to `true` exist might look like this: + +=== "YAML" + + ```yaml hl_lines="10-12" + --- + # Synopsis: A rule with a sub-selector filter. + apiVersion: github.com/microsoft/PSRule/v1 + kind: Rule + metadata: + name: Yaml.Subselector.Filter + spec: + condition: + field: resources + where: + type: '.' + equals: 'Microsoft.Web/sites/config' + allOf: + - field: properties.detailedErrorLoggingEnabled + equals: true + ``` + +=== "JSON" + + ```json + { + // Synopsis: A rule with a sub-selector filter. + "apiVersion": "github.com/microsoft/PSRule/v1", + "kind": "Rule", + "metadata": { + "name": "Json.Subselector.Filter" + }, + "spec": { + "condition": { + "field": "resources", + "where": { + "type": ".", + "equals": "Microsoft.Web/sites/config" + }, + "allOf": [ + { + "field": "properties.detailedErrorLoggingEnabled", + "equals": true + } + ] + } + } + } + ``` + +In the example: + +- If the array property `resources` exists, any items with a type of `Microsoft.Web/sites/config` are evaluated. + - Each item must have the `properties.detailedErrorLoggingEnabled` property set to `true` to pass. + - Items without the `properties.detailedErrorLoggingEnabled` property fail. + - Items with the `properties.detailedErrorLoggingEnabled` property set to a value other then `true` fail. +- If the `resources` property does not exist, the rule fails. +- If the `resources` property exists but has 0 items of type `Microsoft.Web/sites/config`, the rule fails. +- If the `resources` property exists and has any items of type `Microsoft.Web/sites/config` but any fail, the rule fails. +- If the `resources` property exists and has any items of type `Microsoft.Web/sites/config` and all pass, the rule passes. + +### When there are no results + +Given the example, is important to understand what happens if: + +- The `resources` property doesn't exist. +- The `resources` property doesn't contain any items that match the sub-selector condition. + +In either of these two cases, the sub-selector will return `false` and fail the rule. +The rule fails because there is no secondary conditions that could be used instead. + +If this was not the desired behavior, you could: + +- Use a pre-condition to avoid running the rule. +- Group the sub-selector into a `anyOf`, and provide a secondary condition. + +For example: + +=== "YAML" + + ```yaml hl_lines="9 11-14 19-20" + --- + # Synopsis: A rule with a sub-selector filter. + apiVersion: github.com/microsoft/PSRule/v1 + kind: Rule + metadata: + name: Yaml.Subselector.FilterOr + spec: + condition: + anyOf: + + - field: resources + where: + type: '.' + equals: 'Microsoft.Web/sites/config' + allOf: + - field: properties.detailedErrorLoggingEnabled + equals: true + + - field: resources + exists: false + ``` + +=== "JSON" + + ```json hl_lines="10 12-16 25-26" + { + // Synopsis: A rule with a sub-selector filter. + "apiVersion": "github.com/microsoft/PSRule/v1", + "kind": "Rule", + "metadata": { + "name": "Json.Subselector.FilterOr" + }, + "spec": { + "condition": { + "anyOf": [ + { + "field": "resources", + "where": { + "type": ".", + "equals": "Microsoft.Web/sites/config" + }, + "allOf": [ + { + "field": "properties.detailedErrorLoggingEnabled", + "equals": true + } + ] + }, + { + "field": "resources", + "exists": false + } + ] + } + } + } + ``` + +In the example: + +- If the array property `resources` exists, any items with a type of `Microsoft.Web/sites/config` are evaluated. + - Each item must have the `properties.detailedErrorLoggingEnabled` property set to `true` to pass. + - Items without the `properties.detailedErrorLoggingEnabled` property fail. + - Items with the `properties.detailedErrorLoggingEnabled` property set to a value other then `true` fail. +- If the `resources` property does not exist, the rule passes. +- If the `resources` property exists but has 0 items of type `Microsoft.Web/sites/config`, the rule fails. +- If the `resources` property exists and has any items of type `Microsoft.Web/sites/config` but any fail, the rule fails. +- If the `resources` property exists and has any items of type `Microsoft.Web/sites/config` and all pass, the rule passes. diff --git a/docs/versioning.md b/docs/versioning.md new file mode 100644 index 0000000000..00bfa70464 --- /dev/null +++ b/docs/versioning.md @@ -0,0 +1,37 @@ +# Changes and versioning + +PSRule uses [semantic versioning][1] to declare breaking changes. +The latest module version can be installed from the PowerShell Gallery. +For a list of module changes please see the [change log][2]. + + [1]: https://semver.org/ + [2]: https://aka.ms/ps-rule/changelog + +## Pre-releases + +Pre-release module versions are created on major commits and can be installed from the PowerShell Gallery. +Module versions and change log details for pre-releases will be removed as stable releases are made available. + +!!! Important + Pre-release versions should be considered work in progress. + These releases should not be used in production. + We may introduce breaking changes between a pre-release as we work towards a stable version release. + +## Experimental features + +From time to time we may ship experimential features. +These features are generally marked experimential in the change log as these features ship. +Experimental features may ship in stable releases, however to use them you may need to: + +- Enabled or explictly reference them. + +!!! Important + Experimental features should be considered work in progress. + These features may be incomplete and should not be used in production. + We may introduce breaking changes for experimental features as we work towards a general release for the feature. + +## Reporting bugs + +If you experience an issue with an pre-release or experimental feature please let us know by logging an issue as a [bug][3]. + + [3]: https://github.com/microsoft/PSRule/issues diff --git a/mkdocs.yml b/mkdocs.yml index 240ab34e5f..2da2e1241e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,6 +29,7 @@ theme: level: 1 - navigation.tabs - content.code.annotate + - content.tabs.link nav: - Home: index.md @@ -55,8 +56,9 @@ nav: - Azure resource tagging example: scenarios/azure-tags/azure-tags.md - Kubernetes resource validation example: scenarios/kubernetes-resources/kubernetes-resources.md - Concepts: - - Expressions: - - Functions: expressions/functions.md + - Functions: expressions/functions.md + - Sub-selectors: expressions/sub-selectors.md + - Scenarios: - Using within continuous integration: scenarios/validation-pipeline/validation-pipeline.md # - Troubleshooting: troubleshooting.md - License and contributing: license-contributing.md @@ -68,6 +70,7 @@ nav: - v0: 'CHANGELOG-v0.md' - Upgrade notes: upgrade-notes.md - Deprecations: deprecations.md + - Changes and versioning: versioning.md - Support: support.md # - Setup: # - Configuring options: setup/configuring-options.md diff --git a/schemas/PSRule-language.schema.json b/schemas/PSRule-language.schema.json index e4346d5dac..e7bc131ad2 100644 --- a/schemas/PSRule-language.schema.json +++ b/schemas/PSRule-language.schema.json @@ -458,7 +458,7 @@ "title": "If", "description": "A condition is made up of one or more expressions that will determine if an object is selected by the selector.", "markdownDescription": "A condition is made up of one or more [expressions](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/) that will determine if an object is selected by the selector. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Selectors/)", - "$ref": "#/definitions/selectorExpression" + "$ref": "#/definitions/expressions" } }, "required": [ @@ -520,7 +520,7 @@ }, "if": { "type": "object", - "$ref": "#/definitions/selectorExpression" + "$ref": "#/definitions/expressions" } }, "required": [ @@ -577,8 +577,12 @@ "type": "object", "title": "Condition", "description": "A condition is made up of one or more expressions that will determine if the rule passes or fails.", - "markdownDescription": "A condition is made up of one or more [expressions](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/) that will determine if the rule passes or fails. [See help](https://microsoft.github.io/PSRule/v2/authoring/writing-rules/)", - "$ref": "#/definitions/selectorExpression" + "markdownDescription": "A condition is made up of one or more [expressions](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/) that will determine if the rule passes or fails.\n\n[See help](https://microsoft.github.io/PSRule/v2/authoring/writing-rules/)", + "oneOf": [ + { + "$ref": "#/definitions/expressions" + } + ] }, "level": { "type": "string", @@ -597,7 +601,8 @@ "title": "Type pre-condition", "description": "This rule only applies to objects that match the specifies types.", "items": { - "type": "string" + "type": "string", + "default": "" }, "uniqueItems": true }, @@ -606,15 +611,30 @@ "title": "Selector pre-condition", "description": "This rule only applies to objects that match the specified selectors.", "items": { - "type": "string" + "type": "string", + "default": "" }, "uniqueItems": true + }, + "where": { + "type": "object", + "title": "Sub-selector pre-condition", + "description": "The rule only applies to objects that match the sub-selector condition.", + "markdownDescription": "The rule only applies to objects that match the sub-selector condition.\n\n[See help](https://microsoft.github.io/PSRule/v2/expressions/sub-selectors/)", + "oneOf": [ + { + "$ref": "#/definitions/expressions" + } + ] } }, "required": [ "condition" ], - "additionalProperties": false + "additionalProperties": false, + "default": { + "condition": {} + } }, "ruleMetadata": { "type": "object", @@ -666,1338 +686,2122 @@ "name" ] }, - "selectorExpression": { - "type": "object", + "selectorExpressionValueMultiString": { "oneOf": [ { - "$ref": "#/definitions/selectorOperator" + "type": "string" }, { - "$ref": "#/definitions/selectorCondition" + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true } ] }, - "selectorOperator": { - "type": "object", + "selectorExpressionValue": { "oneOf": [ { - "$ref": "#/definitions/selectorOperatorAllOf" + "type": "string", + "ztitle": "Value from string", + "zdescription": "A value to compare.", + "default": "" + }, + { + "type": "boolean", + "ztitle": "Value from boolean", + "zdescription": "A value to compare.", + "default": true + }, + { + "type": "integer", + "ztitle": "Value from integer", + "zdescription": "A value to compare.", + "default": 0 }, { - "$ref": "#/definitions/selectorOperatorAnyOf" + "type": "object", + "ztitle": "Value for object", + "zdescription": "A value to compare.", + "not": { + "propertyNames": { + "enum": [ + "$" + ] + } + } }, { - "$ref": "#/definitions/selectorOperatorNot" + "$ref": "#/definitions/fn" } ] }, - "selectorCondition": { + "expressions": { "type": "object", "oneOf": [ { - "$ref": "#/definitions/selectorConditionExists" + "$ref": "#/definitions/expressions/definitions/operators/definitions/allOf" }, { - "$ref": "#/definitions/selectorConditionEquals" + "$ref": "#/definitions/expressions/definitions/operators/definitions/anyOf" }, { - "$ref": "#/definitions/selectorConditionNotEquals" + "$ref": "#/definitions/expressions/definitions/operators/definitions/not" }, { - "$ref": "#/definitions/selectorConditionHasValue" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/exists" }, { - "$ref": "#/definitions/selectorConditionMatch" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/equals" }, { - "$ref": "#/definitions/selectorConditionNotMatch" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notEquals" }, { - "$ref": "#/definitions/selectorConditionIn" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/hasValue" }, { - "$ref": "#/definitions/selectorConditionNotIn" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/match" }, { - "$ref": "#/definitions/selectorConditionSetOf" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notMatch" }, { - "$ref": "#/definitions/selectorConditionSubset" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/in" }, { - "$ref": "#/definitions/selectorConditionCount" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notIn" }, { - "$ref": "#/definitions/selectorConditionNotCount" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/setOf" }, { - "$ref": "#/definitions/selectorConditionLess" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/subset" }, { - "$ref": "#/definitions/selectorConditionLessOrEquals" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/count" }, { - "$ref": "#/definitions/selectorConditionGreater" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notCount" }, { - "$ref": "#/definitions/selectorConditionGreaterOrEquals" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/less" }, { - "$ref": "#/definitions/selectorConditionStartsWith" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/lessOrEquals" }, { - "$ref": "#/definitions/selectorConditionNotStartsWith" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/greater" }, { - "$ref": "#/definitions/selectorConditionEndsWith" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/greaterOrEquals" }, { - "$ref": "#/definitions/selectorConditionNotEndsWith" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/startsWith" }, { - "$ref": "#/definitions/selectorConditionContains" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notStartsWith" }, { - "$ref": "#/definitions/selectorConditionNotContains" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/endsWith" }, { - "$ref": "#/definitions/selectorConditionIsString" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notEndsWith" }, { - "$ref": "#/definitions/selectorConditionIsLower" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/contains" }, { - "$ref": "#/definitions/selectorConditionIsArray" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notContains" }, { - "$ref": "#/definitions/selectorConditionIsBoolean" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isString" }, { - "$ref": "#/definitions/selectorConditionIsDateTime" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isLower" }, { - "$ref": "#/definitions/selectorConditionIsInteger" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isArray" }, { - "$ref": "#/definitions/selectorConditionIsNumeric" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isBoolean" }, { - "$ref": "#/definitions/selectorConditionIsUpper" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isDateTime" }, { - "$ref": "#/definitions/selectorConditionHasSchema" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isInteger" }, { - "$ref": "#/definitions/selectorConditionVersion" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isNumeric" }, { - "$ref": "#/definitions/selectorConditionHasDefault" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isUpper" }, { - "$ref": "#/definitions/selectorConditionWithinPath" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/hasSchema" }, { - "$ref": "#/definitions/selectorConditionNotWithinPath" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/version" }, { - "$ref": "#/definitions/selectorConditionLike" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/hasDefault" }, { - "$ref": "#/definitions/selectorConditionNotLike" - } - ] - }, - "selectorProperties": { - "oneOf": [ + "$ref": "#/definitions/expressions/definitions/conditions/definitions/withinPath" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notWithinPath" + }, { - "$ref": "#/definitions/selectorPropertyField" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/like" }, { - "$ref": "#/definitions/selectorPropertyValue" + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notLike" } - ] - }, - "selectorPropertiesString": { - "oneOf": [ + ], + "defaultSnippets": [ { - "$ref": "#/definitions/selectorPropertyField" + "label": "field", + "description": "The object path of a field to compare.", + "markdownDescription": "The object path of a field to compare.", + "body": { + "field": "${1}" + } }, { - "$ref": "#/definitions/selectorPropertyValue" + "label": "value", + "body": { + "value": "${1}" + } }, { - "$ref": "#/definitions/selectorPropertyType" + "label": "name", + "body": { + "name": "." + } }, { - "$ref": "#/definitions/selectorPropertyName" + "label": "type", + "body": { + "type": "." + } }, { - "$ref": "#/definitions/selectorPropertySource" - } - ] - }, - "selectorPropertyField": { - "properties": { - "field": { - "type": "string", - "title": "Field", - "description": "The object path of a field to compare.", - "markdownDescription": "The object path of a field to compare. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#field)", - "default": "." - } - }, - "required": [ - "field" - ] - }, - "selectorPropertyValue": { - "properties": { - "value": { - "$ref": "#/definitions/selectorExpressionValue" - } - }, - "required": [ - "value" - ] - }, - "selectorPropertyType": { - "properties": { - "type": { - "type": "string", - "title": "Type", - "description": "The target type of the object.", - "markdownDescription": "The target type of the object. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#type)", - "default": "." - } - }, - "required": [ - "type" - ] - }, - "selectorPropertyName": { - "properties": { - "name": { - "type": "string", - "title": "Name", - "description": "The target name of the object.", - "markdownDescription": "The target name of the object. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#name)", - "default": "." - } - }, - "required": [ - "name" - ] - }, - "selectorPropertySource": { - "properties": { - "source": { - "type": "string", - "title": "Source", - "description": "The source of the object currently being processed by the pipeline.", - "markdownDescription": "The source of the object currently being processed by the pipeline. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#source)" - } - }, - "required": [ - "source" - ] - }, - "selectorOperatorAllOf": { - "type": "object", - "properties": { - "allOf": { - "type": "array", - "title": "All Of", - "description": "All of the expressions must be true.", - "markdownDescription": "All of the expressions must be true. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#allof)", - "items": { - "$ref": "#/definitions/selectorExpression" - } - } - }, - "required": [ - "allOf" - ], - "additionalProperties": false - }, - "selectorOperatorAnyOf": { - "type": "object", - "properties": { - "anyOf": { - "type": "array", - "title": "Any Of", - "description": "One of the expressions must be true.", - "markdownDescription": "All of the expressions must be true. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#anyof)", - "items": { - "$ref": "#/definitions/selectorExpression" + "label": "source", + "body": { + "source": "${1}" } - } - }, - "required": [ - "anyOf" - ], - "additionalProperties": false - }, - "selectorOperatorNot": { - "type": "object", - "properties": { - "not": { - "type": "object", - "title": "Not", - "description": "The nested expression must not be true.", - "markdownDescription": "The nested expression must not be true. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#not)", - "$ref": "#/definitions/selectorExpression" - } - }, - "required": [ - "not" - ] - }, - "selectorConditionExists": { - "type": "object", - "properties": { - "exists": { - "type": "boolean", - "title": "Exists", - "description": "Must have the named field.", - "markdownDescription": "Must have the named field. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#exists)" - }, - "field": { - "type": "string", - "title": "Field", - "description": "The path of the field.", - "markdownDescription": "The path of the field. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#field)" - } - }, - "required": [ - "exists", - "field" - ] - }, - "selectorConditionEquals": { - "type": "object", - "properties": { - "equals": { - "title": "Equals", - "description": "Must have the specified value.", - "markdownDescription": "Must have the specified value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#equals)", - "$ref": "#/definitions/selectorExpressionValue" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type of compared operand.", - "markdownDescription": "Convert type of compared operand. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#equals)", - "default": false }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive. Only applies to string values.", - "markdownDescription": "Determines if comparing values is case-sensitive. Only applies to string values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#equals)", - "default": false - } - }, - "required": [ - "equals" - ], - "oneOf": [ { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionNotEquals": { - "type": "object", - "properties": { - "notEquals": { - "title": "Not Equals", - "description": "Must not have the specified value.", - "markdownDescription": "Must not have the specified value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notequals)", - "$ref": "#/definitions/selectorExpressionValue" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type of compared operand.", - "markdownDescription": "Convert type of compared operand. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notequals)", - "default": false + "label": "allOf", + "body": { + "allOf": [ + "${1}" + ] + } }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive. Only applies to string values.", - "markdownDescription": "Determines if comparing values is case-sensitive. Only applies to string values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notequals)", - "default": false - } - }, - "required": [ - "notEquals" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionHasValue": { - "type": "object", - "properties": { - "hasValue": { - "type": "boolean", - "title": "Has Value", - "description": "Must have a non-empty value.", - "markdownDescription": "Must have a non-empty value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasvalue)" - } - }, - "required": [ - "hasValue" - ], - "oneOf": [ { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionMatch": { - "type": "object", - "properties": { - "match": { - "type": "string", - "title": "Match", - "description": "Must match the regular expression.", - "markdownDescription": "Must match the regular expression. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#match)" - } - }, - "required": [ - "match" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionNotMatch": { - "type": "object", - "properties": { - "notMatch": { - "type": "string", - "title": "Not Match", - "description": "Must not match the regular expression.", - "markdownDescription": "Must not match the regular expression. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notmatch)" - } - }, - "required": [ - "notMatch" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionIn": { - "type": "object", - "properties": { - "in": { - "type": "array", - "title": "In", - "description": "Must equal one of the specified values.", - "markdownDescription": "Must equal one of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#in)" - } - }, - "required": [ - "in" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionNotIn": { - "type": "object", - "properties": { - "notIn": { - "type": "array", - "title": "Not In", - "description": "Must not equal any of the specified values.", - "markdownDescription": "Must not equal any of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notin)" - } - }, - "required": [ - "notIn" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionSetOf": { - "type": "object", - "properties": { - "setOf": { - "type": "array", - "title": "SetOf", - "description": "Must include all of but only specified values.", - "markdownDescription": "Must include all of but only values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#setof)" + "label": "anyOf", + "body": { + "anyOf": [ + "${1}" + ] + } }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#setof)", - "default": false + { + "label": "not", + "body": { + "not": {} + } } - }, - "required": [ - "setOf" ], - "oneOf": [ - { - "$ref": "#/definitions/selectorProperties" - } - ] - }, - "selectorConditionSubset": { - "type": "object", - "properties": { - "subset": { - "type": "array", - "title": "Subset", - "description": "Must include all of the specified values.", - "markdownDescription": "Must include all of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#subset)" - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#subset)", - "default": false - }, - "unique": { - "type": "boolean", - "title": "Unique", - "description": "Determines if each of the field values must be unique.", - "markdownDescription": "Determines if each of the field values must be unique. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#subset)", - "default": false - } - }, - "required": [ - "subset" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorProperties" - } - ] - }, - "selectorConditionCount": { - "type": "object", - "properties": { - "count": { - "type": "integer", - "title": "Count", - "description": "Must include all of the specified values.", - "markdownDescription": "Must include all of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#count)", - "minimum": 0 - } - }, - "required": [ - "count" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorProperties" - } - ] - }, - "selectorConditionNotCount": { - "type": "object", - "properties": { - "notCount": { - "type": "integer", - "title": "NotCount", - "description": "Determines if operand does not have number of items.", - "markdownDescription": "Determines if operand does not have number of items. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notcount)", - "minimum": 0 - } - }, - "required": [ - "notCount" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorProperties" - } - ] - }, - "selectorConditionLess": { - "type": "object", - "properties": { - "less": { - "type": "integer", - "title": "Less", - "description": "Must be less then the specified value.", - "markdownDescription": "Must be less then the specified value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#less)" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type of compared operand.", - "markdownDescription": "Convert type of compared operand. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#less)", - "default": false - } - }, - "required": [ - "less" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionLessOrEquals": { - "type": "object", - "properties": { - "lessOrEquals": { - "type": "integer", - "title": "Less or Equal to", - "description": "Must be less or equal to the specified value.", - "markdownDescription": "Must be less or equal to the specified value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#lessorequals)" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type of compared operand.", - "markdownDescription": "Convert type of compared operand. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#less)", - "default": false - } - }, - "required": [ - "lessOrEquals" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionGreater": { - "type": "object", - "properties": { - "greater": { - "title": "Greater", - "description": "Must be greater then the specified value.", - "markdownDescription": "Must be greater then the specified value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#greater)", + "definitions": { + "operands": { "oneOf": [ { - "type": "integer" + "$ref": "#/definitions/expressions/definitions/operands/definitions/field" + }, + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/value" + }, + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/type" + }, + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/name" }, { + "$ref": "#/definitions/expressions/definitions/operands/definitions/source" + } + ], + "definitions": { + "string-only": { + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/field" + }, + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/value" + }, + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/type" + }, + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/name" + }, + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/source" + } + ] + }, + "field": { + "type": "object", + "properties": { + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "where": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/where" + } + }, + "required": [ + "field" + ] + }, + "value": { + "type": "object", + "properties": { + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "value" + ] + }, + "type": { + "type": "object", + "properties": { + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + } + }, + "required": [ + "type" + ] + }, + "name": { + "type": "object", + "properties": { + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + } + }, + "required": [ + "name" + ] + }, + "source": { "type": "object", - "$ref": "#/definitions/fn" + "properties": { + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "source" + ] } - ] - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type of compared operand.", - "markdownDescription": "Convert type of compared operand. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#less)", - "default": false - } - }, - "required": [ - "greater" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionGreaterOrEquals": { - "type": "object", - "properties": { - "greaterOrEquals": { - "type": "integer", - "title": "Greater or Equal to", - "description": "Must be greater or equal to the specified value.", - "markdownDescription": "Must be greater or equal to the specified value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#greaterorequals)" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type of compared operand.", - "markdownDescription": "Convert type of compared operand. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#less)", - "default": false - } - }, - "required": [ - "greaterOrEquals" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionStartsWith": { - "type": "object", - "properties": { - "startsWith": { - "title": "Starts with", - "description": "Must start with one of the specified values.", - "markdownDescription": "Must start with one of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#startswith)", - "$ref": "#/definitions/selectorExpressionValueMultiString" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to string.", - "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#startswith)", - "default": false - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#startswith)", - "default": false - } - }, - "required": [ - "startsWith" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionNotStartsWith": { - "type": "object", - "properties": { - "notStartsWith": { - "title": "Not starts with", - "description": "Must not start with any of the specified values.", - "markdownDescription": "Must not start with any of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notstartswith)", - "$ref": "#/definitions/selectorExpressionValueMultiString" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to string.", - "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notstartswith)", - "default": false - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notstartswith)", - "default": false - } - }, - "required": [ - "notStartsWith" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionEndsWith": { - "type": "object", - "properties": { - "endsWith": { - "title": "Ends with", - "description": "Must end with one of the specified values.", - "markdownDescription": "Must end with one of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#endswith)", - "$ref": "#/definitions/selectorExpressionValueMultiString" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to string.", - "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#endswith)", - "default": false - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#endswith)", - "default": false - } - }, - "required": [ - "endsWith" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionNotEndsWith": { - "type": "object", - "properties": { - "notEndsWith": { - "title": "Not Ends with", - "description": "Must not end with any of the specified values.", - "markdownDescription": "Must not end with any of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notendswith)", - "$ref": "#/definitions/selectorExpressionValueMultiString" + } }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to string.", - "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notendswith)", - "default": false + "properties": { + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/field" + }, + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/value" + }, + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/name" + }, + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/type" + }, + { + "$ref": "#/definitions/expressions/definitions/operands/definitions/source" + } + ], + "definitions": { + "field": { + "type": "string", + "title": "Field", + "description": "The object path of a field to compare.", + "markdownDescription": "The object path of a field to compare.\n\n[See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#field)", + "minLength": 1 + }, + "where": { + "type": "object", + "title": "Sub-selector filter", + "description": "Limits the condition to matching items.", + "markdownDescription": "Limits the condition to matching items.\n\n[See help](https://microsoft.github.io/PSRule/v2/expressions/sub-selectors/)", + "allOf": [ + { + "$ref": "#/definitions/expressions" + } + ] + }, + "value": { + "allOf": [ + { + "$ref": "#/definitions/selectorExpressionValue" + } + ] + }, + "name": { + "type": "string", + "title": "Name", + "description": "The target name of the object.", + "markdownDescription": "The target name of the object. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#name)", + "default": ".", + "enum": [ + "." + ] + }, + "type": { + "type": "string", + "title": "Type", + "description": "The target type of the object.", + "markdownDescription": "The target type of the object. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#type)", + "default": ".", + "enum": [ + "." + ] + }, + "source": { + "type": "string", + "title": "Source", + "description": "The source of the object currently being processed by the pipeline.", + "markdownDescription": "The source of the object currently being processed by the pipeline. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#source)", + "default": "" + } + } }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notendswith)", - "default": false - } - }, - "required": [ - "notEndsWith" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionContains": { - "type": "object", - "properties": { - "contains": { - "title": "Contains", - "description": "Must contain one of the specified values.", - "markdownDescription": "Must contain one of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#contains)", - "$ref": "#/definitions/selectorExpressionValueMultiString" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to string.", - "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#contains)", - "default": false - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#contains)", - "default": false - } - }, - "required": [ - "contains" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionNotContains": { - "type": "object", - "properties": { - "notContains": { - "title": "Not Contains", - "description": "Must not contain any of the specified values.", - "markdownDescription": "Must not contain any of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notcontains)", - "$ref": "#/definitions/selectorExpressionValueMultiString" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to string.", - "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notcontains)", - "default": false - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notcontains)", - "default": false - } - }, - "required": [ - "notContains" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionIsString": { - "type": "object", - "properties": { - "isString": { - "type": "boolean", - "title": "Is string", - "description": "Must be a string type.", - "markdownDescription": "Must be a string type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isstring)" - } - }, - "required": [ - "isString" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionIsArray": { - "type": "object", - "properties": { - "isArray": { - "type": "boolean", - "title": "Is array", - "description": "Must be an array type.", - "markdownDescription": "Must be an array type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isarray)" - } - }, - "required": [ - "isArray" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionIsBoolean": { - "type": "object", - "properties": { - "isBoolean": { - "type": "boolean", - "title": "Is boolean", - "description": "Must be a boolean type.", - "markdownDescription": "Must be a boolean type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isboolean)" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to boolean.", - "markdownDescription": "Convert type to boolean. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isboolean)", - "default": false - } - }, - "required": [ - "isBoolean" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionIsDateTime": { - "type": "object", - "properties": { - "isDateTime": { - "type": "boolean", - "title": "Is datetime", - "description": "Must be a datetime type.", - "markdownDescription": "Must be a datetime type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isdatetime)" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to datetime.", - "markdownDescription": "Convert type to datetime. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isdatetime)", - "default": false - } - }, - "required": [ - "isDateTime" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionIsInteger": { - "type": "object", - "properties": { - "isInteger": { - "type": "boolean", - "title": "Is integer", - "description": "Must be an integer type.", - "markdownDescription": "Must be an integer type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isinteger)" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to integer.", - "markdownDescription": "Convert type to integer. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isinteger)", - "default": false - } - }, - "required": [ - "isInteger" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionIsNumeric": { - "type": "object", - "properties": { - "isNumeric": { - "type": "boolean", - "title": "Is numeric", - "description": "Must be a numeric type.", - "markdownDescription": "Must be a numeric type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isnumeric)" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to numeric.", - "markdownDescription": "Convert type to numeric. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isnumeric)", - "default": false - } - }, - "required": [ - "isNumeric" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionIsLower": { - "type": "object", - "properties": { - "isLower": { - "type": "boolean", - "title": "Is Lowercase", - "description": "Must be a lowercase string.", - "markdownDescription": "Must be a lowercase string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#islower)" - } - }, - "required": [ - "isLower" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionIsUpper": { - "type": "object", - "properties": { - "isUpper": { - "type": "boolean", - "title": "Is Uppercase", - "description": "Must be an uppercase string.", - "markdownDescription": "Must be an uppercase string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isupper)" - } - }, - "required": [ - "isUpper" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionHasSchema": { - "type": "object", - "properties": { - "hasSchema": { - "type": "array", - "title": "Has schema", - "description": "Must use one of the specified schemas of the value of $schema. If an empty array is specified any non-empty $schema can be specified.", - "markdownDescription": "Must use one of the specified schemas of the value of `$schema`. If an empty array is specified any non-empty `$schema` can be specified [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasschema)", - "default": [], - "items": { - "type": "string", - "title": "Has schema", - "description": "Must use one of the specified schemas of the value of $schema. If an empty array is specified any non-empty $schema can be specified.", - "markdownDescription": "Must use one of the specified schemas of the value of `$schema`. If an empty array is specified any non-empty `$schema` can be specified [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasschema)", - "minLength": 1 - }, - "uniqueItems": true - }, - "ignoreScheme": { - "type": "boolean", - "title": "Ignore scheme", - "description": "Determines comparing values is case-sensitive.", - "markdownDescription": "Determines comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasschema)", - "default": false - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing schemas is case-sensitive.", - "markdownDescription": "Determines if comparing schemas is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasschema)", - "default": false - } - }, - "required": [ - "hasSchema" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorProperties" - } - ] - }, - "selectorConditionVersion": { - "type": "object", - "properties": { - "version": { - "type": "string", - "title": "Version", - "description": "Must be a valid semantic version. A constraint can optionally be provided to require the semantic version to be within a range.", - "markdownDescription": "Must be a valid semantic version. A constraint can optionally be provided to require the semantic version to be within a range. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#version)", - "default": "" - }, - "includePrerelease": { - "type": "boolean", - "title": "Include pre-release", - "description": "Determines if pre-release versions are included. By default, pre-release versions are not included.", - "markdownDescription": "Determines if pre-release versions are included. By default, pre-release versions are not included. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#version)", - "default": false - } - }, - "required": [ - "version" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorProperties" - } - ] - }, - "selectorConditionHasDefault": { - "type": "object", - "properties": { - "hasDefault": { - "title": "Has Default", - "description": "The field must either not exist or be set to the configured value.", - "markdownDescription": "The field must either not exist or be set to the configured value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasdefault)", - "$ref": "#/definitions/selectorExpressionValue" - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasdefault)", - "default": false - } - }, - "required": [ - "hasDefault" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorProperties" - } - ] - }, - "selectorConditionWithinPath": { - "type": "object", - "properties": { - "withinPath": { - "type": "array", - "title": "Within Path", - "description": "The file path must exist within the required paths.", - "markdownDescription": "The file path must exist within the required paths. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#withinpath)", - "default": [], - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#withinpath)", - "default": false - } - }, - "required": [ - "withinPath" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionNotWithinPath": { - "type": "object", - "properties": { - "notWithinPath": { - "type": "array", - "title": "Not Within Path", - "description": "The file path must not exist within the required paths.", - "markdownDescription": "The file path must not exist within the required paths. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notwithinpath)", - "default": [], - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notwithinpath)", - "default": false - } - }, - "required": [ - "notWithinPath" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionLike": { - "type": "object", - "properties": { - "like": { - "title": "Like", - "description": "Must match any of the specified wildcard patterns.", - "markdownDescription": "Must match any of the specified wildcard patterns. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#like)", - "$ref": "#/definitions/selectorExpressionValueMultiString" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to string.", - "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#like)", - "default": false - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#like)", - "default": false - } - }, - "required": [ - "like" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorConditionNotLike": { - "type": "object", - "properties": { - "notLike": { - "title": "Not like", - "description": "Must not match any of the specified wildcard patterns.", - "markdownDescription": "Must not match any of the specified wildcard patterns. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notlike)", - "$ref": "#/definitions/selectorExpressionValueMultiString" - }, - "convert": { - "type": "boolean", - "title": "Type conversion", - "description": "Convert type to string.", - "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notlike)", - "default": false - }, - "caseSensitive": { - "type": "boolean", - "title": "Case sensitive", - "description": "Determines if comparing values is case-sensitive.", - "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notlike)", - "default": false - } - }, - "required": [ - "notLike" - ], - "oneOf": [ - { - "$ref": "#/definitions/selectorPropertiesString" - } - ] - }, - "selectorExpressionValueMultiString": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - } - ] - }, - "selectorExpressionValue": { - "oneOf": [ - { - "type": "string", - "title": "Value from string", - "description": "A value to compare." - }, - { - "type": "boolean", - "title": "Value from boolean", - "description": "A value to compare." - }, - { - "type": "integer", - "title": "Value from integer", - "description": "A value to compare." + "conditions": { + "type": "object", + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/exists" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/equals" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notEquals" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/hasValue" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/match" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notMatch" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/in" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notIn" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/setOf" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/subset" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/count" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notCount" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/less" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/lessOrEquals" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/greater" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/greaterOrEquals" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/startsWith" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notStartsWith" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/endsWith" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notEndsWith" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/contains" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notContains" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isString" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isLower" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isArray" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isBoolean" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isDateTime" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isInteger" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isNumeric" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/isUpper" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/hasSchema" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/version" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/hasDefault" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/withinPath" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notWithinPath" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/like" + }, + { + "$ref": "#/definitions/expressions/definitions/conditions/definitions/notLike" + } + ], + "definitions": { + "equals": { + "type": "object", + "title": "equals", + "description": "Must have the specified value.", + "markdownDescription": "Must have the specified value.\n\n[See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#equals)", + "properties": { + "equals": { + "title": "Equals", + "description": "Must have the specified value.", + "markdownDescription": "Must have the specified value.\n\n[See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#equals)", + "default": "", + "oneOf": [ + { + "$ref": "#/definitions/selectorExpressionValue" + } + ] + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type of compared operand.", + "markdownDescription": "Convert type of compared operand.\n\n[See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#equals)", + "default": false + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive. Only applies to string values.", + "markdownDescription": "Determines if comparing values is case-sensitive. Only applies to string values.\n\n[See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#equals)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "equals" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ] + }, + "count": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "title": "Count", + "description": "Must include the specified number of values.", + "markdownDescription": "Must include the specified number of values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#count)", + "minimum": 0 + } + }, + "required": [ + "count" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ] + }, + "exists": { + "type": "object", + "properties": { + "exists": { + "type": "boolean", + "title": "Exists", + "description": "Must have the named field.", + "markdownDescription": "Must have the named field. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#exists)", + "default": true + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + } + }, + "required": [ + "exists", + "field" + ] + }, + "notEquals": { + "type": "object", + "properties": { + "notEquals": { + "title": "Not Equals", + "description": "Must not have the specified value.", + "markdownDescription": "Must not have the specified value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notequals)", + "default": "", + "oneOf": [ + { + "$ref": "#/definitions/selectorExpressionValue" + } + ] + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type of compared operand.", + "markdownDescription": "Convert type of compared operand. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notequals)", + "default": false + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive. Only applies to string values.", + "markdownDescription": "Determines if comparing values is case-sensitive. Only applies to string values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notequals)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "notEquals" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ] + }, + "hasValue": { + "type": "object", + "properties": { + "hasValue": { + "type": "boolean", + "title": "Has Value", + "description": "Must have a non-empty value.", + "markdownDescription": "Must have a non-empty value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasvalue)", + "default": true + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + } + }, + "required": [ + "hasValue" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ] + }, + "match": { + "type": "object", + "properties": { + "match": { + "type": "string", + "title": "Match", + "description": "Must match the regular expression.", + "markdownDescription": "Must match the regular expression. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#match)", + "default": "" + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "match" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ] + }, + "notMatch": { + "type": "object", + "properties": { + "notMatch": { + "type": "string", + "title": "Not Match", + "description": "Must not match the regular expression.", + "markdownDescription": "Must not match the regular expression. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notmatch)", + "default": "" + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "notMatch" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ] + }, + "in": { + "type": "object", + "properties": { + "in": { + "type": "array", + "title": "In", + "description": "Must equal one of the specified values.", + "markdownDescription": "Must equal one of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#in)", + "default": [ + "" + ], + "uniqueItems": true + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "in" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ] + }, + "notIn": { + "type": "object", + "properties": { + "notIn": { + "type": "array", + "title": "Not In", + "description": "Must not equal any of the specified values.", + "markdownDescription": "Must not equal any of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notin)", + "default": [ + "" + ], + "uniqueItems": true + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "notIn" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ] + }, + "setOf": { + "type": "object", + "properties": { + "setOf": { + "type": "array", + "title": "SetOf", + "description": "Must include all of but only specified values.", + "markdownDescription": "Must include all of but only values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#setof)", + "default": [ + "" + ], + "uniqueItems": true + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#setof)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "setOf" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/properties" + } + ] + }, + "subset": { + "type": "object", + "properties": { + "subset": { + "type": "array", + "title": "Subset", + "description": "Must include all of the specified values.", + "markdownDescription": "Must include all of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#subset)", + "default": [ + "" + ], + "uniqueItems": true + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#subset)", + "default": false + }, + "unique": { + "type": "boolean", + "title": "Unique", + "description": "Determines if each of the field values must be unique.", + "markdownDescription": "Determines if each of the field values must be unique. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#subset)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "subset" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/properties" + } + ] + }, + "notCount": { + "type": "object", + "properties": { + "notCount": { + "type": "integer", + "title": "NotCount", + "description": "Determines if operand does not have number of items.", + "markdownDescription": "Determines if operand does not have number of items. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notcount)", + "minimum": 0, + "default": 0 + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "notCount" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/properties" + } + ], + "additionalProperties": false + }, + "less": { + "type": "object", + "properties": { + "less": { + "title": "Less", + "description": "Must be less then the specified value.", + "markdownDescription": "Must be less then the specified value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#less)", + "default": 0, + "oneOf": [ + { + "type": "integer" + }, + { + "type": "object", + "$ref": "#/definitions/fn" + } + ] + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type of compared operand.", + "markdownDescription": "Convert type of compared operand. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#less)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "less" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "lessOrEquals": { + "type": "object", + "properties": { + "lessOrEquals": { + "title": "Less or Equal to", + "description": "Must be less or equal to the specified value.", + "markdownDescription": "Must be less or equal to the specified value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#lessorequals)", + "default": 0, + "oneOf": [ + { + "type": "integer" + }, + { + "type": "object", + "$ref": "#/definitions/fn" + } + ] + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type of compared operand.", + "markdownDescription": "Convert type of compared operand. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#less)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "lessOrEquals" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "greater": { + "type": "object", + "properties": { + "greater": { + "title": "Greater", + "description": "Must be greater then the specified value.", + "markdownDescription": "Must be greater then the specified value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#greater)", + "default": 0, + "oneOf": [ + { + "type": "integer" + }, + { + "type": "object", + "$ref": "#/definitions/fn" + } + ] + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type of compared operand.", + "markdownDescription": "Convert type of compared operand. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#less)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "greater" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "greaterOrEquals": { + "type": "object", + "properties": { + "greaterOrEquals": { + "title": "Greater or Equal to", + "description": "Must be greater or equal to the specified value.", + "markdownDescription": "Must be greater or equal to the specified value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#greaterorequals)", + "default": 0, + "oneOf": [ + { + "type": "integer" + }, + { + "type": "object", + "$ref": "#/definitions/fn" + } + ] + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type of compared operand.", + "markdownDescription": "Convert type of compared operand. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#less)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "greaterOrEquals" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "startsWith": { + "type": "object", + "properties": { + "startsWith": { + "title": "Starts with", + "description": "Must start with one of the specified values.", + "markdownDescription": "Must start with one of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#startswith)", + "$ref": "#/definitions/selectorExpressionValueMultiString", + "default": "" + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to string.", + "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#startswith)", + "default": false + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#startswith)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "startsWith" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "notStartsWith": { + "type": "object", + "properties": { + "notStartsWith": { + "title": "Not starts with", + "description": "Must not start with any of the specified values.", + "markdownDescription": "Must not start with any of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notstartswith)", + "$ref": "#/definitions/selectorExpressionValueMultiString" + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to string.", + "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notstartswith)", + "default": false + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notstartswith)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "notStartsWith" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "endsWith": { + "type": "object", + "properties": { + "endsWith": { + "title": "Ends with", + "description": "Must end with one of the specified values.", + "markdownDescription": "Must end with one of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#endswith)", + "$ref": "#/definitions/selectorExpressionValueMultiString", + "default": "" + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to string.", + "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#endswith)", + "default": false + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#endswith)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "endsWith" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "notEndsWith": { + "type": "object", + "properties": { + "notEndsWith": { + "title": "Not Ends with", + "description": "Must not end with any of the specified values.", + "markdownDescription": "Must not end with any of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notendswith)", + "$ref": "#/definitions/selectorExpressionValueMultiString" + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to string.", + "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notendswith)", + "default": false + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notendswith)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "notEndsWith" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "contains": { + "type": "object", + "title": "contains", + "description": "Must contain one of the specified values.", + "markdownDescription": "Must contain one of the specified values.\n\n[See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#contains)", + "properties": { + "contains": { + "title": "Contains", + "description": "Must contain one of the specified values.", + "markdownDescription": "Must contain one of the specified values.\n\n[See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#contains)", + "default": "", + "allOf": [ + { + "$ref": "#/definitions/selectorExpressionValueMultiString" + } + ] + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to string.", + "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#contains)", + "default": false + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#contains)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "contains" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false, + "minProperties": 2 + }, + "notContains": { + "type": "object", + "properties": { + "notContains": { + "title": "Not Contains", + "description": "Must not contain any of the specified values.", + "markdownDescription": "Must not contain any of the specified values. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notcontains)", + "$ref": "#/definitions/selectorExpressionValueMultiString" + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to string.", + "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notcontains)", + "default": false + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notcontains)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + }, + "type": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/type" + }, + "name": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/name" + }, + "source": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/source" + } + }, + "required": [ + "notContains" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "isString": { + "type": "object", + "properties": { + "isString": { + "type": "boolean", + "title": "Is string", + "description": "Must be a string type.", + "markdownDescription": "Must be a string type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isstring)", + "default": true + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "isString" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "isArray": { + "type": "object", + "properties": { + "isArray": { + "type": "boolean", + "title": "Is array", + "description": "Must be an array type.", + "markdownDescription": "Must be an array type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isarray)", + "default": true + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "isArray" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "isBoolean": { + "type": "object", + "properties": { + "isBoolean": { + "type": "boolean", + "title": "Is boolean", + "description": "Must be a boolean type.", + "markdownDescription": "Must be a boolean type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isboolean)", + "default": true + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to boolean.", + "markdownDescription": "Convert type to boolean. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isboolean)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "isBoolean" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "isDateTime": { + "type": "object", + "properties": { + "isDateTime": { + "type": "boolean", + "title": "Is datetime", + "description": "Must be a datetime type.", + "markdownDescription": "Must be a datetime type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isdatetime)", + "default": true + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to datetime.", + "markdownDescription": "Convert type to datetime. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isdatetime)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "isDateTime" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "isInteger": { + "type": "object", + "properties": { + "isInteger": { + "type": "boolean", + "title": "Is integer", + "description": "Must be an integer type.", + "markdownDescription": "Must be an integer type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isinteger)", + "default": true + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to integer.", + "markdownDescription": "Convert type to integer. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isinteger)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "isInteger" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false + }, + "isNumeric": { + "type": "object", + "properties": { + "isNumeric": { + "type": "boolean", + "title": "Is numeric", + "description": "Must be a numeric type.", + "markdownDescription": "Must be a numeric type. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isnumeric)", + "default": true + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to numeric.", + "markdownDescription": "Convert type to numeric. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isnumeric)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "isNumeric" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false, + "minProperties": 2 + }, + "isLower": { + "type": "object", + "properties": { + "isLower": { + "type": "boolean", + "title": "Is Lowercase", + "description": "Must be a lowercase string.", + "markdownDescription": "Must be a lowercase string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#islower)", + "default": true + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "isLower" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false, + "minProperties": 2 + }, + "isUpper": { + "type": "object", + "properties": { + "isUpper": { + "type": "boolean", + "title": "Is Uppercase", + "description": "Must be an uppercase string.", + "markdownDescription": "Must be an uppercase string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#isupper)", + "default": true + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "isUpper" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false, + "minProperties": 2 + }, + "hasSchema": { + "type": "object", + "properties": { + "hasSchema": { + "type": "array", + "title": "Has schema", + "description": "Must use one of the specified schemas of the value of $schema. If an empty array is specified any non-empty $schema can be specified.", + "markdownDescription": "Must use one of the specified schemas of the value of `$schema`. If an empty array is specified any non-empty `$schema` can be specified [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasschema)", + "default": [], + "items": { + "type": "string", + "title": "Has schema", + "description": "Must use one of the specified schemas of the value of $schema. If an empty array is specified any non-empty $schema can be specified.", + "markdownDescription": "Must use one of the specified schemas of the value of `$schema`. If an empty array is specified any non-empty `$schema` can be specified [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasschema)", + "minLength": 1 + }, + "uniqueItems": true + }, + "ignoreScheme": { + "type": "boolean", + "title": "Ignore scheme", + "description": "Determines comparing values is case-sensitive.", + "markdownDescription": "Determines comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasschema)", + "default": false + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing schemas is case-sensitive.", + "markdownDescription": "Determines if comparing schemas is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasschema)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "hasSchema" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/properties" + } + ], + "additionalProperties": false, + "minProperties": 2 + }, + "version": { + "type": "object", + "properties": { + "version": { + "type": "string", + "title": "Version", + "description": "Must be a valid semantic version. A constraint can optionally be provided to require the semantic version to be within a range.", + "markdownDescription": "Must be a valid semantic version. A constraint can optionally be provided to require the semantic version to be within a range. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#version)", + "default": "" + }, + "includePrerelease": { + "type": "boolean", + "title": "Include pre-release", + "description": "Determines if pre-release versions are included. By default, pre-release versions are not included.", + "markdownDescription": "Determines if pre-release versions are included. By default, pre-release versions are not included. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#version)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "version" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/properties" + } + ], + "additionalProperties": false, + "minProperties": 2 + }, + "hasDefault": { + "type": "object", + "properties": { + "hasDefault": { + "title": "Has Default", + "description": "The field must either not exist or be set to the configured value.", + "markdownDescription": "The field must either not exist or be set to the configured value. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasdefault)", + "default": "", + "oneOf": [ + { + "$ref": "#/definitions/selectorExpressionValue" + } + ] + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#hasdefault)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "hasDefault" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/properties" + } + ], + "additionalProperties": false, + "minProperties": 2 + }, + "withinPath": { + "type": "object", + "properties": { + "withinPath": { + "type": "array", + "title": "Within Path", + "description": "The file path must exist within the required paths.", + "markdownDescription": "The file path must exist within the required paths. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#withinpath)", + "default": [], + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#withinpath)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "withinPath" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false, + "maxProperties": 2 + }, + "notWithinPath": { + "type": "object", + "properties": { + "notWithinPath": { + "type": "array", + "title": "Not Within Path", + "description": "The file path must not exist within the required paths.", + "markdownDescription": "The file path must not exist within the required paths. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notwithinpath)", + "default": [], + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notwithinpath)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "notWithinPath" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false, + "minProperties": 2 + }, + "like": { + "type": "object", + "properties": { + "like": { + "title": "Like", + "description": "Must match any of the specified wildcard patterns.", + "markdownDescription": "Must match any of the specified wildcard patterns. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#like)", + "$ref": "#/definitions/selectorExpressionValueMultiString" + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to string.", + "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#like)", + "default": false + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#like)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "like" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false, + "minProperties": 2 + }, + "notLike": { + "type": "object", + "properties": { + "notLike": { + "title": "Not like", + "description": "Must not match any of the specified wildcard patterns.", + "markdownDescription": "Must not match any of the specified wildcard patterns. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notlike)", + "$ref": "#/definitions/selectorExpressionValueMultiString" + }, + "convert": { + "type": "boolean", + "title": "Type conversion", + "description": "Convert type to string.", + "markdownDescription": "Convert type to string. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notlike)", + "default": false + }, + "caseSensitive": { + "type": "boolean", + "title": "Case sensitive", + "description": "Determines if comparing values is case-sensitive.", + "markdownDescription": "Determines if comparing values is case-sensitive. [See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#notlike)", + "default": false + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "value": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/value" + } + }, + "required": [ + "notLike" + ], + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operands" + } + ], + "additionalProperties": false, + "minProperties": 2 + } + } }, - { + "operators": { "type": "object", - "title": "Value for object", - "description": "A value to compare.", - "not": { - "propertyNames": { - "enum": [ - "$" - ] + "oneOf": [ + { + "$ref": "#/definitions/expressions/definitions/operators/definitions/allOf" + }, + { + "$ref": "#/definitions/expressions/definitions/operators/definitions/anyOf" + }, + { + "$ref": "#/definitions/expressions/definitions/operators/definitions/not" + } + ], + "definitions": { + "allOf": { + "type": "object", + "title": "allOf", + "description": "All of the expressions must be true.", + "markdownDescription": "All of the expressions must be true.\n\n[See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#allof)", + "properties": { + "allOf": { + "type": "array", + "title": "AllOf", + "description": "All of the expressions must be true.", + "markdownDescription": "All of the expressions must be true.\n\n[See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#allof)", + "items": { + "$ref": "#/definitions/expressions" + } + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "where": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/where" + } + }, + "required": [ + "allOf" + ], + "oneOf": [ + { + "properties": { + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "where": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/where" + } + } + } + ], + "additionalProperties": false + }, + "anyOf": { + "type": "object", + "properties": { + "anyOf": { + "type": "array", + "title": "AnyOf", + "description": "One of the expressions must be true.", + "markdownDescription": "All of the expressions must be true.\n\n[See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#anyof)", + "items": { + "$ref": "#/definitions/expressions" + } + }, + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "where": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/where" + } + }, + "required": [ + "anyOf" + ], + "oneOf": [ + { + "properties": { + "field": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/field" + }, + "where": { + "$ref": "#/definitions/expressions/definitions/properties/definitions/where" + } + } + } + ], + "additionalProperties": false + }, + "not": { + "type": "object", + "properties": { + "not": { + "type": "object", + "title": "Not", + "description": "The nested expression must not be true.", + "markdownDescription": "The nested expression must not be true.\n\n[See help](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Expressions/#not)", + "$ref": "#/definitions/expressions" + } + }, + "required": [ + "not" + ], + "additionalProperties": false } } - }, - { - "$ref": "#/definitions/fn" } - ] + } }, "fn": { - "title": "Value from function", - "description": "A function expression that once evaluated specifies the value.", - "markdownDescription": "A function expression that once evaluated specifies the value.", + "ztitle": "Value from function", + "zdescription": "A function expression that once evaluated specifies the value.", + "zmarkdownDescription": "A function expression that once evaluated specifies the value.", "properties": { "$": { "type": "object", diff --git a/src/PSRule/Common/ConditionResultExtensions.cs b/src/PSRule/Common/ConditionResultExtensions.cs index 62523f3d24..5980c6dff5 100644 --- a/src/PSRule/Common/ConditionResultExtensions.cs +++ b/src/PSRule/Common/ConditionResultExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using PSRule.Definitions; @@ -9,12 +9,17 @@ internal static class ConditionResultExtensions { public static bool AllOf(this IConditionResult result) { - return result.Count > 0 && result.Pass == result.Count; + return result != null && result.Count > 0 && result.Pass == result.Count; } public static bool AnyOf(this IConditionResult result) { - return result.Pass > 0; + return result != null && result.Pass > 0; + } + + public static bool Skipped(this IConditionResult result) + { + return result == null; } } } diff --git a/src/PSRule/Common/JsonConverters.cs b/src/PSRule/Common/JsonConverters.cs index 6878446bd2..7f4c30afea 100644 --- a/src/PSRule/Common/JsonConverters.cs +++ b/src/PSRule/Common/JsonConverters.cs @@ -645,7 +645,7 @@ public override bool CanConvert(Type objectType) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { - var expression = MapOperator(OPERATOR_IF, reader); + var expression = MapOperator(OPERATOR_IF, null, null, reader); return new LanguageIf(expression); } @@ -657,40 +657,44 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s /// /// Map an operator. /// - private LanguageExpression MapOperator(string type, JsonReader reader) + private LanguageOperator MapOperator(string type, LanguageExpression.PropertyBag properties, LanguageExpression subselector, JsonReader reader) { - if (TryExpression(type, out LanguageOperator result)) + if (TryExpression(type, properties, out LanguageOperator result)) { // If and Not - if (reader.TokenType == JsonToken.StartObject) + if (reader.TryConsume(JsonToken.StartObject)) { result.Add(MapExpression(reader)); - reader.Read(); + //reader.Consume(JsonToken.EndObject); } // AllOf and AnyOf - else if (reader.TokenType == JsonToken.StartArray && reader.Read()) + else if (reader.TryConsume(JsonToken.StartArray)) { while (reader.TokenType != JsonToken.EndArray) { if (reader.SkipComments(out var hasComments) && hasComments) continue; - result.Add(MapExpression(reader)); - reader.Read(); + if (reader.TryConsume(JsonToken.StartObject)) + { + result.Add(MapExpression(reader)); + reader.Consume(JsonToken.EndObject); + } } - reader.Read(); + reader.Consume(JsonToken.EndArray); } + result.Subselector = subselector; } return result; } private LanguageExpression MapCondition(string type, LanguageExpression.PropertyBag properties, JsonReader reader) { - if (TryExpression(type, out LanguageCondition result)) + if (TryExpression(type, null, out LanguageCondition result)) { while (reader.TokenType != JsonToken.EndObject) { - MapProperty(properties, reader, out _); + MapProperty(properties, reader, out _, out _); } result.Add(properties); } @@ -701,7 +705,7 @@ private LanguageExpression MapExpression(JsonReader reader) { LanguageExpression result = null; var properties = new LanguageExpression.PropertyBag(); - MapProperty(properties, reader, out var key); + MapProperty(properties, reader, out var key, out var subselector); if (key != null && TryCondition(key)) { result = MapCondition(key, properties, reader); @@ -709,7 +713,12 @@ private LanguageExpression MapExpression(JsonReader reader) else if ((reader.TokenType == JsonToken.StartObject || reader.TokenType == JsonToken.StartArray) && TryOperator(key)) { - result = MapOperator(key, reader); + var op = MapOperator(key, properties, subselector, reader); + MapProperty(properties, reader, out _, out subselector); + if (subselector != null) + op.Subselector = subselector; + + result = op; } return result; } @@ -766,13 +775,14 @@ private object MapSequence(string name, JsonReader reader) return result.ToArray(); } - private void MapProperty(LanguageExpression.PropertyBag properties, JsonReader reader, out string name) + private void MapProperty(LanguageExpression.PropertyBag properties, JsonReader reader, out string name, out LanguageExpression subselector) { - if (reader.TokenType != JsonToken.StartObject || !reader.Read()) - throw new PipelineSerializationException(PSRuleResources.ReadJsonFailed); + //if (reader.TokenType != JsonToken.StartObject || !reader.Read()) + // throw new PipelineSerializationException(PSRuleResources.ReadJsonFailedExpectedToken, Enum.GetName(typeof(JsonToken), reader.TokenType)); name = null; - while (reader.TokenType != JsonToken.EndObject) + subselector = null; + while (reader.TokenType == JsonToken.PropertyName) { var key = reader.Value.ToString(); if (TryCondition(key) || TryOperator(key)) @@ -780,18 +790,29 @@ private void MapProperty(LanguageExpression.PropertyBag properties, JsonReader r if (reader.Read()) { + // value: if (TryValue(key, reader, out var value)) { properties[key] = value; + reader.Read(); } else if (TryCondition(key) && reader.TryConsume(JsonToken.StartObject)) { if (TryFunction(reader, key, out var fn)) properties.Add(key, fn); + + reader.Consume(JsonToken.EndObject); + } + // where: + else if (TrySubSelector(key) && reader.TryConsume(JsonToken.StartObject)) + { + subselector = MapExpression(reader); + reader.Consume(JsonToken.EndObject); } else if (reader.TokenType == JsonToken.StartObject) + { break; - + } else if (reader.TokenType == JsonToken.StartArray) { if (!TryCondition(key)) @@ -808,17 +829,22 @@ private void MapProperty(LanguageExpression.PropertyBag properties, JsonReader r objects.Add(item); } properties.Add(key, objects.ToArray()); + reader.Consume(JsonToken.EndArray); } - else { properties.Add(key, reader.Value); + reader.Read(); } } - reader.Read(); } } + private bool TrySubSelector(string key) + { + return _Factory.IsSubselector(key); + } + private bool TryOperator(string key) { return _Factory.IsOperator(key); @@ -867,7 +893,7 @@ reader.Value is string s && s == "$"; } - private bool TryExpression(string type, out T expression) where T : LanguageExpression + private bool TryExpression(string type, LanguageExpression.PropertyBag properties, out T expression) where T : LanguageExpression { expression = null; @@ -875,7 +901,7 @@ private bool TryExpression(string type, out T expression) where T : LanguageE { expression = (T)descriptor.CreateInstance( source: RunspaceContext.CurrentThread.Source.File, - properties: null + properties: properties ); return expression != null; diff --git a/src/PSRule/Common/PSObjectExtensions.cs b/src/PSRule/Common/PSObjectExtensions.cs index 595b67ede4..c310d417fb 100644 --- a/src/PSRule/Common/PSObjectExtensions.cs +++ b/src/PSRule/Common/PSObjectExtensions.cs @@ -34,11 +34,6 @@ public static PSObject PropertyValue(this PSObject o, string propertyName) : PSObject.AsPSObject(o.Properties[propertyName].Value); } - public static string ValueAsString(this PSObject o, string propertyName, bool caseSensitive) - { - return ObjectHelper.GetPath(o, propertyName, caseSensitive, out var value) && value != null ? value.ToString() : null; - } - public static bool HasProperty(this PSObject o, string propertyName) { return o.Properties[propertyName] != null; diff --git a/src/PSRule/Common/YamlConverters.cs b/src/PSRule/Common/YamlConverters.cs index 1027fc7d9b..c41a6d641c 100644 --- a/src/PSRule/Common/YamlConverters.cs +++ b/src/PSRule/Common/YamlConverters.cs @@ -620,7 +620,7 @@ bool INodeDeserializer.Deserialize(IParser reader, Type expectedType, Func /// Map an operator. /// - private LanguageExpression MapOperator(string type, IParser reader, Func nestedObjectDeserializer) + private LanguageOperator MapOperator(string type, LanguageExpression.PropertyBag properties, LanguageExpression subselector, IParser reader, Func nestedObjectDeserializer) { - if (TryExpression(reader, type, nestedObjectDeserializer, out LanguageOperator result)) + if (TryExpression(reader, type, properties, nestedObjectDeserializer, out LanguageOperator result)) { // If and Not if (reader.TryConsume(out _)) @@ -656,17 +656,18 @@ private LanguageExpression MapOperator(string type, IParser reader, Func(); reader.MoveNext(); } + result.Subselector = subselector; } return result; } private LanguageExpression MapCondition(string type, LanguageExpression.PropertyBag properties, IParser reader, Func nestedObjectDeserializer) { - if (TryExpression(reader, type, nestedObjectDeserializer, out LanguageCondition result)) + if (TryExpression(reader, type, null, nestedObjectDeserializer, out LanguageCondition result)) { while (!reader.Accept(out MappingEnd end)) { - MapProperty(properties, reader, nestedObjectDeserializer, out _); + MapProperty(properties, reader, nestedObjectDeserializer, out _, out _); } result.Add(properties); } @@ -677,18 +678,25 @@ private LanguageExpression MapExpression(IParser reader, Func(out _)) { - result = MapOperator(key, reader, nestedObjectDeserializer); + var op = MapOperator(key, properties, subselector, reader, nestedObjectDeserializer); + MapProperty(properties, reader, nestedObjectDeserializer, out _, out _); + result = op; } else if (TryOperator(key) && reader.Accept(out _)) { - result = MapOperator(key, reader, nestedObjectDeserializer); + var op = MapOperator(key, properties, subselector, reader, nestedObjectDeserializer); + MapProperty(properties, reader, nestedObjectDeserializer, out _, out subselector); + if (subselector != null) + op.Subselector = subselector; + + result = op; } return result; } @@ -754,9 +762,10 @@ private object MapSequence(string name, IParser reader, Func nestedObjectDeserializer, out string name) + private void MapProperty(LanguageExpression.PropertyBag properties, IParser reader, Func nestedObjectDeserializer, out string name, out LanguageExpression subselector) { name = null; + subselector = null; while (reader.TryConsume(out Scalar scalar)) { var key = scalar.Value; @@ -767,6 +776,7 @@ private void MapProperty(LanguageExpression.PropertyBag properties, IParser read { properties[key] = scalar.Value; } + // value: else if (TryValue(key, reader, nestedObjectDeserializer, out var value)) { properties[key] = value; @@ -788,9 +798,20 @@ private void MapProperty(LanguageExpression.PropertyBag properties, IParser read } properties[key] = objects.ToArray(); } + // where: + else if (TrySubSelector(key) && reader.TryConsume(out _)) + { + subselector = MapExpression(reader, nestedObjectDeserializer); + reader.Consume(); + } } } + private bool TrySubSelector(string key) + { + return _Factory.IsSubselector(key); + } + private bool TryOperator(string key) { return _Factory.IsOperator(key); @@ -835,12 +856,12 @@ private static bool IsFunction(IParser reader) return reader.Accept(out var scalar) || scalar.Value == "$"; } - private bool TryExpression(IParser reader, string type, Func nestedObjectDeserializer, out T expression) where T : LanguageExpression + private bool TryExpression(IParser reader, string type, LanguageExpression.PropertyBag properties, Func nestedObjectDeserializer, out T expression) where T : LanguageExpression { expression = null; if (_Factory.TryDescriptor(type, out var descriptor)) { - expression = (T)descriptor.CreateInstance(RunspaceContext.CurrentThread.Source.File, null); + expression = (T)descriptor.CreateInstance(RunspaceContext.CurrentThread.Source.File, properties); return expression != null; } return false; diff --git a/src/PSRule/Configuration/PipelineHook.cs b/src/PSRule/Configuration/PipelineHook.cs index fc04805172..3875a1eb80 100644 --- a/src/PSRule/Configuration/PipelineHook.cs +++ b/src/PSRule/Configuration/PipelineHook.cs @@ -2,17 +2,16 @@ // Licensed under the MIT License. using System.Collections.Generic; -using System.Management.Automation; namespace PSRule.Configuration { /// /// Used by custom binding functions. /// - public delegate string BindTargetName(PSObject targetObject); + public delegate string BindTargetName(object targetObject); - internal delegate string BindTargetMethod(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, PSObject targetObject, out string path); - internal delegate string BindTargetFunc(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, PSObject targetObject, BindTargetMethod next, out string path); + internal delegate string BindTargetMethod(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, object targetObject, out string path); + internal delegate string BindTargetFunc(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, object targetObject, BindTargetMethod next, out string path); /// /// Hooks that provide customize pipeline execution. diff --git a/src/PSRule/Definitions/Expressions/ExpressionContext.cs b/src/PSRule/Definitions/Expressions/ExpressionContext.cs index 1cc576be6f..d0687e6067 100644 --- a/src/PSRule/Definitions/Expressions/ExpressionContext.cs +++ b/src/PSRule/Definitions/Expressions/ExpressionContext.cs @@ -18,7 +18,7 @@ internal interface IExpressionContext : IBindingContext object Current { get; } - RunspaceContext GetContext(); + RunspaceContext Context { get; } } internal sealed class ExpressionContext : IExpressionContext, IBindingContext @@ -27,8 +27,9 @@ internal sealed class ExpressionContext : IExpressionContext, IBindingContext private List _Reason; - internal ExpressionContext(SourceFile source, ResourceKind kind, object current) + internal ExpressionContext(RunspaceContext context, SourceFile source, ResourceKind kind, object current) { + Context = context; Source = source; LanguageScope = source.Module; Kind = kind; @@ -44,6 +45,8 @@ internal ExpressionContext(SourceFile source, ResourceKind kind, object current) public object Current { get; } + public RunspaceContext Context { get; } + [DebuggerStepThrough] void IBindingContext.CachePathExpression(string path, PathExpression expression) { @@ -81,7 +84,7 @@ public void Reason(IOperand operand, string text, params object[] args) return; _Reason ??= new List(); - _Reason.Add(new ResultReason(RunspaceContext.CurrentThread?.TargetObject?.Path, operand, text, args)); + _Reason.Add(new ResultReason(Context.TargetObject?.Path, operand, text, args)); } public void Reason(string text, params object[] args) @@ -90,17 +93,12 @@ public void Reason(string text, params object[] args) return; _Reason ??= new List(); - _Reason.Add(new ResultReason(RunspaceContext.CurrentThread?.TargetObject?.Path, null, text, args)); + _Reason.Add(new ResultReason(Context.TargetObject?.Path, null, text, args)); } internal ResultReason[] GetReasons() { return _Reason == null || _Reason.Count == 0 ? Array.Empty() : _Reason.ToArray(); } - - public RunspaceContext GetContext() - { - return RunspaceContext.CurrentThread; - } } } diff --git a/src/PSRule/Definitions/Expressions/Functions.cs b/src/PSRule/Definitions/Expressions/Functions.cs index b369310a77..6262145d1b 100644 --- a/src/PSRule/Definitions/Expressions/Functions.cs +++ b/src/PSRule/Definitions/Expressions/Functions.cs @@ -91,7 +91,7 @@ private static ExpressionFnOuter Configuration(IExpressionContext context, Prope // Lookup a configuration value. return (context) => { - return context.GetContext().TryGetConfigurationValue(name, out var value) ? value : null; + return context.Context.TryGetConfigurationValue(name, out var value) ? value : null; }; } diff --git a/src/PSRule/Definitions/Expressions/LanguageExpressions.cs b/src/PSRule/Definitions/Expressions/LanguageExpressions.cs index 9a7dd18e13..1b1e0c28e4 100644 --- a/src/PSRule/Definitions/Expressions/LanguageExpressions.cs +++ b/src/PSRule/Definitions/Expressions/LanguageExpressions.cs @@ -54,6 +54,11 @@ public bool TryDescriptor(string name, out ILanguageExpresssionDescriptor descri _Descriptors.TryGetValue(name, out descriptor); } + public bool IsSubselector(string name) + { + return name == "where"; + } + public bool IsOperator(string name) { return TryDescriptor(name, out var d) && d != null && d.Type == LanguageExpressionType.Operator; @@ -80,12 +85,14 @@ internal sealed class LanguageExpressionBuilder { private const char Dot = '.'; private const char OpenBracket = '['; - private const char CloseBracket = '['; + private const char CloseBracket = ']'; + private const string Where = ".where"; private readonly bool _Debugger; private string[] _With; private string[] _Type; + private LanguageExpression _When; private string[] _Rule; public LanguageExpressionBuilder(bool debugger = true) @@ -111,6 +118,15 @@ public LanguageExpressionBuilder WithType(string[] type) return this; } + public LanguageExpressionBuilder WithSubselector(LanguageIf subselector) + { + if (subselector == null || subselector.Expression == null) + return this; + + _When = subselector.Expression; + return this; + } + public LanguageExpressionBuilder WithRule(string[] rule) { if (rule == null || rule.Length == 0) @@ -120,12 +136,12 @@ public LanguageExpressionBuilder WithRule(string[] rule) return this; } - public LanguageExpressionOuterFn Build(LanguageIf selectorIf) + public LanguageExpressionOuterFn Build(LanguageIf condition) { - return Precondition(Expression(string.Empty, selectorIf.Expression), _With, _Type, _Rule); + return Precondition(Expression(string.Empty, condition.Expression), _With, _Type, Expression(string.Empty, _When), _Rule); } - private static LanguageExpressionOuterFn Precondition(LanguageExpressionOuterFn expression, string[] with, string[] type, string[] rule) + private static LanguageExpressionOuterFn Precondition(LanguageExpressionOuterFn expression, string[] with, string[] type, LanguageExpressionOuterFn when, string[] rule) { var fn = expression; if (type != null) @@ -134,6 +150,9 @@ private static LanguageExpressionOuterFn Precondition(LanguageExpressionOuterFn if (with != null) fn = PreconditionSelector(with, fn); + if (when != null) + fn = PreconditionSubselector(when, fn); + if (rule != null) fn = PreconditionRule(rule, fn); @@ -182,8 +201,25 @@ private static LanguageExpressionOuterFn PreconditionType(string[] type, Languag }; } + private static LanguageExpressionOuterFn PreconditionSubselector(LanguageExpressionOuterFn subselector, LanguageExpressionOuterFn fn) + { + return (context, o) => + { + // Evalute sub-selector pre-condition + if (!AcceptsSubselector(context, subselector, o)) + { + context.Debug(PSRuleResources.DebugTargetSubselectorMismatch); + return null; + } + return fn(context, o); + }; + } + private LanguageExpressionOuterFn Expression(string path, LanguageExpression expression) { + if (expression == null) + return null; + path = Path(path, expression); if (expression is LanguageOperator selectorOperator) return Scope(Debugger(Operator(path, selectorOperator), path)); @@ -226,7 +262,38 @@ private LanguageExpressionOuterFn Operator(string path, LanguageOperator express } var innerA = inner.ToArray(); var info = new ExpressionInfo(path); - return (context, o) => expression.Descriptor.Fn(context, info, innerA, o); + + // Check for sub-selectors + if (expression.Property == null || expression.Property.Count == 0) + { + return (context, o) => expression.Descriptor.Fn(context, info, innerA, o); + } + else + { + var subselector = expression.Subselector != null ? Expression(string.Concat(path, Where), expression.Subselector) : null; + return (context, o) => + { + if (!ObjectHelper.GetPath(context, o, Value(context, expression.Property["field"]), caseSensitive: false, out object[] items) || + items == null || items.Length == 0) + return false; + + // If any fail, all fail + for (var i = 0; i < items.Length; i++) + { + if (subselector == null || subselector(context, items[i]).GetValueOrDefault(true)) + { + if (!expression.Descriptor.Fn(context, info, innerA, items[i])) + return false; + } + } + return true; + }; + } + } + + private string Value(ExpressionContext context, object v) + { + return v as string; } private LanguageExpressionOuterFn Debugger(LanguageExpressionOuterFn expression, string path) @@ -273,6 +340,11 @@ private static bool AcceptsWith(string[] with) return false; } + private static bool AcceptsSubselector(ExpressionContext context, LanguageExpressionOuterFn subselector, object o) + { + return subselector == null || subselector.Invoke(context, o).GetValueOrDefault(false); + } + private static bool AcceptsRule(string[] rule) { if (rule == null || rule.Length == 0) @@ -1520,38 +1592,36 @@ private static bool TryField(IExpressionContext context, LanguageExpression.Prop return operand != null || NotHasField(context, field); } - private static bool TryName(IExpressionContext context, LanguageExpression.PropertyBag properties, out IOperand operand) + private static bool TryName(IExpressionContext context, LanguageExpression.PropertyBag properties, object o, out IOperand operand) { operand = null; if (properties.TryGetString(NAME, out var svalue)) { - if (svalue != ".") + if (svalue != "." || context?.Context?.LanguageScope == null) return Invalid(context, svalue); - var binding = context.GetContext()?.TargetBinder?.Using(context.LanguageScope); - var name = binding?.TargetName; - if (string.IsNullOrEmpty(name)) + if (!context.Context.LanguageScope.TryGetName(o, out var name, out var path) || + string.IsNullOrEmpty(name)) return Invalid(context, svalue); - operand = Operand.FromName(name, binding.TargetNamePath); + operand = Operand.FromName(name, path); } return operand != null; } - private static bool TryType(IExpressionContext context, LanguageExpression.PropertyBag properties, out IOperand operand) + private static bool TryType(IExpressionContext context, LanguageExpression.PropertyBag properties, object o, out IOperand operand) { operand = null; if (properties.TryGetString(TYPE, out var svalue)) { - if (svalue != ".") + if (svalue != "." || context?.Context?.LanguageScope == null) return Invalid(context, svalue); - var binding = context.GetContext()?.TargetBinder?.Using(context.LanguageScope); - var type = binding?.TargetType; - if (string.IsNullOrEmpty(type)) + if (!context.Context.LanguageScope.TryGetType(o, out var type, out var path) || + string.IsNullOrEmpty(type)) return Invalid(context, svalue); - operand = Operand.FromType(type, binding.TargetTypePath); + operand = Operand.FromType(type, path); } return operand != null; } @@ -1561,7 +1631,7 @@ private static bool TrySource(IExpressionContext context, LanguageExpression.Pro operand = null; if (properties.TryGetString(SOURCE, out var sourceValue)) { - var source = context?.GetContext()?.TargetObject?.Source[sourceValue]; + var source = context?.Context?.TargetObject?.Source[sourceValue]; if (source == null) return Invalid(context, sourceValue); @@ -1627,8 +1697,8 @@ private static bool TryFieldNotExists(ExpressionContext context, object o, Langu private static bool TryOperand(ExpressionContext context, string name, object o, LanguageExpression.PropertyBag properties, out IOperand operand) { return TryField(context, properties, o, out operand) || - TryType(context, properties, out operand) || - TryName(context, properties, out operand) || + TryType(context, properties, o, out operand) || + TryName(context, properties, o, out operand) || TrySource(context, properties, out operand) || TryValue(context, properties, out operand) || Invalid(context, name); diff --git a/src/PSRule/Definitions/Expressions/Primitives.cs b/src/PSRule/Definitions/Expressions/Primitives.cs index 0f71649944..581c9e0d7b 100644 --- a/src/PSRule/Definitions/Expressions/Primitives.cs +++ b/src/PSRule/Definitions/Expressions/Primitives.cs @@ -34,7 +34,7 @@ public LanguageExpresssionDescriptor(string name, LanguageExpressionType type, L public LanguageExpression CreateInstance(SourceFile source, LanguageExpression.PropertyBag properties) { if (Type == LanguageExpressionType.Operator) - return new LanguageOperator(this); + return new LanguageOperator(this, properties); if (Type == LanguageExpressionType.Condition) return new LanguageCondition(this, properties); @@ -74,12 +74,17 @@ public LanguageIf(LanguageExpression expression) [DebuggerDisplay("Selector {Descriptor.Name}")] internal sealed class LanguageOperator : LanguageExpression { - internal LanguageOperator(LanguageExpresssionDescriptor descriptor) + internal LanguageOperator(LanguageExpresssionDescriptor descriptor, PropertyBag properties) : base(descriptor) { + Property = properties ?? new PropertyBag(); Children = new List(); } + public LanguageExpression Subselector { get; set; } + + public PropertyBag Property { get; } + public List Children { get; } public void Add(LanguageExpression item) @@ -109,9 +114,6 @@ internal void Add(PropertyBag properties) internal sealed class LanguageFunction : LanguageExpression { internal LanguageFunction(LanguageExpresssionDescriptor descriptor) - : base(descriptor) - { - - } + : base(descriptor) { } } } diff --git a/src/PSRule/Definitions/ILanguageBlock.cs b/src/PSRule/Definitions/ILanguageBlock.cs index bceec9ac20..ffa3491bd1 100644 --- a/src/PSRule/Definitions/ILanguageBlock.cs +++ b/src/PSRule/Definitions/ILanguageBlock.cs @@ -6,16 +6,33 @@ namespace PSRule.Definitions { + /// + /// A language block. + /// public interface ILanguageBlock { + /// + /// The unique identifier for the block. + /// ResourceId Id { get; } + /// + /// Obsolete. The source file path. + /// Replaced by . + /// [Obsolete("Use Source property instead.")] string SourcePath { get; } + /// + /// Obsolete. The source module. + /// Replaced by . + /// [Obsolete("Use Source property instead.")] string Module { get; } + /// + /// The source location for the block. + /// SourceFile Source { get; } } } diff --git a/src/PSRule/Definitions/Resource.cs b/src/PSRule/Definitions/Resource.cs index 9382bdaa15..408f455bc2 100644 --- a/src/PSRule/Definitions/Resource.cs +++ b/src/PSRule/Definitions/Resource.cs @@ -18,8 +18,14 @@ namespace PSRule.Definitions { + /// + /// The type of resource. + /// public enum ResourceKind { + /// + /// Unknown or empty. + /// None = 0, /// @@ -53,14 +59,26 @@ public enum ResourceKind SuppressionGroup = 6 } + /// + /// Additional flags that indicate the status of the resource. + /// [Flags] public enum ResourceFlags { + /// + /// No flags are set. + /// None = 0, + /// + /// The resource is obsolete. + /// Obsolete = 1 } + /// + /// A resource langange block. + /// public interface IResource : ILanguageBlock { /// @@ -142,6 +160,9 @@ internal sealed class ValidateResourceAnnotation : ResourceAnnotation } + /// + /// A resource object. + /// public sealed class ResourceObject { internal ResourceObject(IResource block) @@ -196,15 +217,24 @@ internal IEnumerable Build() } } + /// + /// Additional resource annotations. + /// public sealed class ResourceAnnotations : Dictionary { } + /// + /// Additional resource tags. + /// public sealed class ResourceTags : Dictionary { private Hashtable _Hashtable; + /// + /// Create an empty set of resource tags. + /// public ResourceTags() : base(StringComparer.OrdinalIgnoreCase) { } /// @@ -287,6 +317,10 @@ private static bool TryArray(object o, out string[] values) return false; } + /// + /// Convert the resourecs tags to a display string for PowerShell views. + /// + /// public string ToViewString() { var sb = new StringBuilder(); @@ -308,8 +342,14 @@ public string ToViewString() } } + /// + /// Additional resource metadata. + /// public sealed class ResourceMetadata { + /// + /// Create an empty set of metadata. + /// public ResourceMetadata() { Annotations = new ResourceAnnotations(); @@ -321,8 +361,14 @@ public ResourceMetadata() /// public string Name { get; set; } + /// + /// A opaque reference for the resource. + /// public string Ref { get; set; } + /// + /// Additional aliases for the resource. + /// public string[] Alias { get; set; } /// @@ -338,16 +384,32 @@ public ResourceMetadata() public ResourceTags Tags { get; set; } } + /// + /// The source location of the resource. + /// public sealed class ResourceExtent { + /// + /// The file where the resource is located. + /// public string File { get; set; } + /// + /// The name of the module if the resource is contained within a module. + /// public string Module { get; set; } } + /// + /// A base class for resources. + /// + /// The type for the resource specification. [DebuggerDisplay("Kind = {Kind}, Id = {Id}")] public abstract class Resource where TSpec : Spec, new() { + /// + /// Create a resource. + /// internal protected Resource(ResourceKind kind, string apiVersion, SourceFile source, ResourceMetadata metadata, IResourceHelpInfo info, ISourceExtent extent, TSpec spec) { Kind = kind; @@ -361,6 +423,9 @@ internal protected Resource(ResourceKind kind, string apiVersion, SourceFile sou Id = new ResourceId(source.Module, Name, ResourceIdKind.Id); } + /// + /// The resource identifier for the resource. + /// [YamlIgnore()] public ResourceId Id { get; } @@ -376,6 +441,9 @@ internal protected Resource(ResourceKind kind, string apiVersion, SourceFile sou [YamlIgnore()] public SourceFile Source { get; } + /// + /// Information about the resource. + /// [YamlIgnore()] public IResourceHelpInfo Info { get; } diff --git a/src/PSRule/Definitions/Rules/Rule.cs b/src/PSRule/Definitions/Rules/Rule.cs index 16aea06632..36dd2d9ec5 100644 --- a/src/PSRule/Definitions/Rules/Rule.cs +++ b/src/PSRule/Definitions/Rules/Rule.cs @@ -14,17 +14,36 @@ namespace PSRule.Definitions.Rules /// public enum SeverityLevel { + /// + /// Severity is unset. + /// None = 0, + /// + /// A failure generates an error. + /// Error = 1, + /// + /// A fiailure generates a warning. + /// Warning = 2, + /// + /// A failure generate an informational message. + /// Information = 3 } + /// + /// A rule resource V1. + /// public interface IRuleV1 : IResource, IDependencyTarget { + /// + /// Obsolete. The name of the rule. + /// Replaced by . + /// [Obsolete("Use Name instead.")] string RuleName { get; } @@ -33,16 +52,32 @@ public interface IRuleV1 : IResource, IDependencyTarget /// SeverityLevel Level { get; } + /// + /// A short description of the rule. + /// string Synopsis { get; } + /// + /// Obsolete. A short description of the rule. + /// Replaced by . + /// [Obsolete("Use Synopsis instead.")] string Description { get; } + /// + /// Any additional tags assigned to the rule. + /// ResourceTags Tag { get; } } + /// + /// A specification for a rule resource. + /// internal interface IRuleSpec { + /// + /// The of the rule condition that will be evaluated. + /// LanguageIf Condition { get; } /// @@ -50,9 +85,20 @@ internal interface IRuleSpec /// SeverityLevel? Level { get; } + /// + /// An optional type pre-condition before the rule is evaluated. + /// string[] Type { get; } + /// + /// An optional selector pre-condition before the rule is evaluated. + /// string[] With { get; } + + /// + /// An optional sub-selector pre-condition before the rule is evaluated. + /// + LanguageIf Where { get; } } [Spec(Specs.V1, Specs.Rule)] @@ -68,10 +114,12 @@ public RuleV1(string apiVersion, SourceFile source, ResourceMetadata metadata, I Level = ResourceHelper.GetLevel(spec.Level); } + /// [JsonIgnore] [YamlIgnore] public ResourceId? Ref { get; } + /// [JsonIgnore] [YamlIgnore] public ResourceId[] Alias { get; } @@ -90,43 +138,52 @@ public RuleV1(string apiVersion, SourceFile source, ResourceMetadata metadata, I [YamlIgnore] public string Synopsis => Info.Synopsis.Text; + /// ResourceId? IDependencyTarget.Ref => Ref; + /// ResourceId[] IDependencyTarget.Alias => Alias; // Not supported with resource rules. ResourceId[] IDependencyTarget.DependsOn => Array.Empty(); + /// bool IDependencyTarget.Dependency => Source.IsDependency(); + /// ResourceId? IResource.Ref => Ref; + /// ResourceId[] IResource.Alias => Alias; + /// string IRuleV1.RuleName => Name; + /// ResourceTags IRuleV1.Tag => Metadata.Tags; + /// string IRuleV1.Description => Info.Synopsis.Text; } + /// + /// A specification for a V1 rule resource. + /// internal sealed class RuleV1Spec : Spec, IRuleSpec { + /// public LanguageIf Condition { get; set; } - /// - /// If the rule fails, how serious is the result. - /// + /// public SeverityLevel? Level { get; set; } - /// - /// An optional type precondition before the rule is evaluated. - /// + /// public string[] Type { get; set; } - /// - /// An optional selector precondition before the rule is evaluated. - /// + /// public string[] With { get; set; } + + /// + public LanguageIf Where { get; set; } } } diff --git a/src/PSRule/Definitions/Rules/RuleVisitor.cs b/src/PSRule/Definitions/Rules/RuleVisitor.cs index 608d5ae9f9..511689b482 100644 --- a/src/PSRule/Definitions/Rules/RuleVisitor.cs +++ b/src/PSRule/Definitions/Rules/RuleVisitor.cs @@ -11,13 +11,18 @@ namespace PSRule.Definitions.Rules { + /// + /// A rule visitor. + /// [DebuggerDisplay("Id: {Id}")] internal sealed class RuleVisitor : ICondition { private readonly LanguageExpressionOuterFn _Condition; + private readonly RunspaceContext _Context; - public RuleVisitor(ResourceId id, SourceFile source, IRuleSpec spec) + public RuleVisitor(RunspaceContext context, ResourceId id, SourceFile source, IRuleSpec spec) { + _Context = context; ErrorAction = ActionPreference.Stop; Id = id; Source = source; @@ -26,6 +31,7 @@ public RuleVisitor(ResourceId id, SourceFile source, IRuleSpec spec) _Condition = builder .WithSelector(spec.With) .WithType(spec.Type) + .WithSubselector(spec.Where) .Build(spec.Condition); } @@ -50,14 +56,14 @@ public void Dispose() public IConditionResult If() { - var context = new ExpressionContext(Source, ResourceKind.Rule, RunspaceContext.CurrentThread.TargetObject.Value); + var context = new ExpressionContext(_Context, Source, ResourceKind.Rule, _Context.TargetObject.Value); context.Debug(PSRuleResources.RuleMatchTrace, Id); context.PushScope(RunspaceScope.Rule); try { - var result = _Condition(context, RunspaceContext.CurrentThread.TargetObject.Value); + var result = _Condition(context, _Context.TargetObject.Value); if (result.HasValue && !result.Value) - RunspaceContext.CurrentThread.WriteReason(context.GetReasons()); + _Context.WriteReason(context.GetReasons()); return result.HasValue ? new RuleConditionResult(result.Value ? 1 : 0, 1, false) : null; } diff --git a/src/PSRule/Definitions/Selectors/SelectorVisitor.cs b/src/PSRule/Definitions/Selectors/SelectorVisitor.cs index d7ab303dbf..34e766de3e 100644 --- a/src/PSRule/Definitions/Selectors/SelectorVisitor.cs +++ b/src/PSRule/Definitions/Selectors/SelectorVisitor.cs @@ -6,6 +6,7 @@ using PSRule.Definitions.Expressions; using PSRule.Pipeline; using PSRule.Resources; +using PSRule.Runtime; namespace PSRule.Definitions.Selectors { @@ -18,9 +19,11 @@ internal interface ISelector : ILanguageBlock internal sealed class SelectorVisitor : ISelector { private readonly LanguageExpressionOuterFn _Fn; + private readonly RunspaceContext _Context; - public SelectorVisitor(ResourceId id, SourceFile source, LanguageIf expression) + public SelectorVisitor(RunspaceContext context, ResourceId id, SourceFile source, LanguageIf expression) { + _Context = context; Id = id; Source = source; InstanceId = Guid.NewGuid(); @@ -42,7 +45,7 @@ public SelectorVisitor(ResourceId id, SourceFile source, LanguageIf expression) public bool Match(object o) { - var context = new ExpressionContext(Source, ResourceKind.Selector, o); + var context = new ExpressionContext(_Context, Source, ResourceKind.Selector, o); context.Debug(PSRuleResources.SelectorMatchTrace, Id); return _Fn(context, o).GetValueOrDefault(false); } diff --git a/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupVisitor.cs b/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupVisitor.cs index 9d8ace4efb..edda73213d 100644 --- a/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupVisitor.cs +++ b/src/PSRule/Definitions/SuppressionGroups/SuppressionGroupVisitor.cs @@ -5,6 +5,7 @@ using PSRule.Definitions.Expressions; using PSRule.Pipeline; using PSRule.Resources; +using PSRule.Runtime; namespace PSRule.Definitions.SuppressionGroups { @@ -12,9 +13,11 @@ internal sealed class SuppressionGroupVisitor { private readonly LanguageExpressionOuterFn _Fn; private readonly SuppressionInfo _Info; + private readonly RunspaceContext _Context; - public SuppressionGroupVisitor(ResourceId id, SourceFile source, ISuppressionGroupSpec spec, IResourceHelpInfo info) + public SuppressionGroupVisitor(RunspaceContext context, ResourceId id, SourceFile source, ISuppressionGroupSpec spec, IResourceHelpInfo info) { + _Context = context; Id = id; Source = source; InstanceId = Guid.NewGuid(); @@ -77,7 +80,7 @@ internal void Hit() public bool TryMatch(object o, out ISuppressionInfo suppression) { suppression = null; - var context = new ExpressionContext(Source, ResourceKind.SuppressionGroup, o); + var context = new ExpressionContext(_Context, Source, ResourceKind.SuppressionGroup, o); context.Debug(PSRuleResources.SelectorMatchTrace, Id); if (_Fn(context, o).GetValueOrDefault(false)) { diff --git a/src/PSRule/Host/HostHelper.cs b/src/PSRule/Host/HostHelper.cs index eea83a859a..f0036bcafe 100644 --- a/src/PSRule/Host/HostHelper.cs +++ b/src/PSRule/Host/HostHelper.cs @@ -56,11 +56,6 @@ internal static DependencyGraph GetRuleBlockGraph(Source[] source, Ru return builder.Build(); } - internal static IEnumerable GetRuleYamlBlocks(Source[] source, RunspaceContext context) - { - return ToRuleBlockV1(GetYamlLanguageBlocks(source, context), context, skipDuplicateName: true).GetAll(); - } - private static IEnumerable GetYamlJsonLanguageBlocks(Source[] source, RunspaceContext context) { var results = new List(); @@ -530,7 +525,7 @@ private static DependencyTargetCollection ToRuleBlockV1(ILanguageBloc @ref: block.Ref, level: block.Level, info: info, - condition: new RuleVisitor(block.Id, block.Source, block.Spec), + condition: new RuleVisitor(context, block.Id, block.Source, block.Spec), alias: block.Alias, tag: block.Metadata.Tags, dependsOn: null, // TODO: No support for DependsOn yet @@ -706,11 +701,11 @@ private static void Import(ILanguageBlock[] blocks, RunspaceContext context) // Process module configurations first foreach (var resource in resources.Where(r => r.Kind == ResourceKind.ModuleConfig).ToArray()) - context.Pipeline.Import(resource); + context.Pipeline.Import(context, resource); // Process other resources foreach (var resource in resources.Where(r => r.Kind != ResourceKind.ModuleConfig).ToArray()) - context.Pipeline.Import(resource); + context.Pipeline.Import(context, resource); } private static void Import(IConvention[] blocks, RunspaceContext context) diff --git a/src/PSRule/Pipeline/PipelineBuilder.cs b/src/PSRule/Pipeline/PipelineBuilder.cs index 1856753757..4564975132 100644 --- a/src/PSRule/Pipeline/PipelineBuilder.cs +++ b/src/PSRule/Pipeline/PipelineBuilder.cs @@ -476,7 +476,7 @@ private static BindTargetMethod AddBindTargetAction(BindTargetFunc action, BindT { // Nest the previous write action in the new supplied action // Execution chain will be: action -> previous -> previous..n - return (string[] propertyNames, bool caseSensitive, bool preferTargetInfo, PSObject targetObject, out string path) => + return (string[] propertyNames, bool caseSensitive, bool preferTargetInfo, object targetObject, out string path) => { return action(propertyNames, caseSensitive, preferTargetInfo, targetObject, previous, out path); }; @@ -484,7 +484,7 @@ private static BindTargetMethod AddBindTargetAction(BindTargetFunc action, BindT private static BindTargetMethod AddBindTargetAction(BindTargetName action, BindTargetMethod previous) { - return AddBindTargetAction((string[] propertyNames, bool caseSensitive, bool preferTargetInfo, PSObject targetObject, BindTargetMethod next, out string path) => + return AddBindTargetAction((string[] propertyNames, bool caseSensitive, bool preferTargetInfo, object targetObject, BindTargetMethod next, out string path) => { path = null; var targetType = action(targetObject); diff --git a/src/PSRule/Pipeline/PipelineContext.cs b/src/PSRule/Pipeline/PipelineContext.cs index c125a3fb0f..c769f1d713 100644 --- a/src/PSRule/Pipeline/PipelineContext.cs +++ b/src/PSRule/Pipeline/PipelineContext.cs @@ -161,7 +161,7 @@ internal Runspace GetRunspace() return _Runspace; } - internal void Import(IResource resource) + internal void Import(RunspaceContext context, IResource resource) { TrackIssue(resource); if (TryBaseline(resource, out var baseline) && TryBaselineRef(resource.Id, out var baselineRef)) @@ -170,7 +170,7 @@ internal void Import(IResource resource) Baseline.Add(new OptionContext.BaselineScope(baselineRef.Type, baseline.BaselineId, resource.Source.Module, baseline.Spec, baseline.Obsolete)); } else if (resource.Kind == ResourceKind.Selector && resource is SelectorV1 selector) - Selector[selector.Id.Value] = new SelectorVisitor(selector.Id, selector.Source, selector.Spec.If); + Selector[selector.Id.Value] = new SelectorVisitor(context, selector.Id, selector.Source, selector.Spec.If); else if (TryModuleConfig(resource, out var moduleConfig)) { if (!string.IsNullOrEmpty(moduleConfig?.Spec?.Rule?.Baseline)) @@ -184,6 +184,7 @@ internal void Import(IResource resource) else if (resource.Kind == ResourceKind.SuppressionGroup && resource is SuppressionGroupV1 suppressionGroup) { SuppressionGroup.Add(new SuppressionGroupVisitor( + context: context, id: suppressionGroup.Id, source: suppressionGroup.Source, spec: suppressionGroup.Spec, diff --git a/src/PSRule/Pipeline/PipelineHookActions.cs b/src/PSRule/Pipeline/PipelineHookActions.cs index 8f193f8455..0d3fcd82f2 100644 --- a/src/PSRule/Pipeline/PipelineHookActions.cs +++ b/src/PSRule/Pipeline/PipelineHookActions.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.Globalization; using System.Linq; using System.Management.Automation; @@ -19,9 +20,12 @@ internal static class PipelineHookActions private const string Property_TargetName = "TargetName"; private const string Property_Name = "Name"; - public static string BindTargetName(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, PSObject targetObject, out string path) + public static string BindTargetName(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, object targetObject, out string path) { path = null; + if (targetObject == null) + return null; + if (preferTargetInfo && TryGetInfoTargetName(targetObject, out var targetName)) return targetName; @@ -33,9 +37,12 @@ public static string BindTargetName(string[] propertyNames, bool caseSensitive, return DefaultTargetNameBinding(targetObject); } - public static string BindTargetType(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, PSObject targetObject, out string path) + public static string BindTargetType(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, object targetObject, out string path) { path = null; + if (targetObject == null) + return null; + if (preferTargetInfo && TryGetInfoTargetType(targetObject, out var targetType)) return targetType; @@ -47,9 +54,12 @@ public static string BindTargetType(string[] propertyNames, bool caseSensitive, return DefaultTargetTypeBinding(targetObject); } - public static string BindField(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, PSObject targetObject, out string path) + public static string BindField(string[] propertyNames, bool caseSensitive, bool preferTargetInfo, object targetObject, out string path) { path = null; + if (targetObject == null) + return null; + if (propertyNames != null) return propertyNames.Any(n => n.Contains('.')) ? NestedTargetPropertyBinding(propertyNames, caseSensitive, targetObject, DefaultFieldBinding, out path) @@ -63,7 +73,7 @@ public static string BindField(string[] propertyNames, bool caseSensitive, bool /// /// A PSObject to bind. /// The TargetName of the object. - private static string DefaultTargetNameBinding(PSObject targetObject) + private static string DefaultTargetNameBinding(object targetObject) { return TryGetInfoTargetName(targetObject, out var targetName) || TryGetTargetName(targetObject, propertyName: Property_TargetName, targetName: out targetName) || @@ -76,16 +86,17 @@ private static string DefaultTargetNameBinding(PSObject targetObject) /// Get the TargetName of the object by using any of the specified property names. /// /// One or more property names to use to bind TargetName. + /// Determines if binding properties are case-sensitive. /// A PSObject to bind. /// The next delegate function to check if all of the property names can not be found. /// The TargetName of the object. - private static string CustomTargetPropertyBinding(string[] propertyNames, bool caseSensitive, PSObject targetObject, BindTargetName next, out string path) + private static string CustomTargetPropertyBinding(string[] propertyNames, bool caseSensitive, object targetObject, BindTargetName next, out string path) { path = null; string targetName = null; for (var i = 0; i < propertyNames.Length && targetName == null; i++) { - targetName = targetObject.ValueAsString(propertyName: propertyNames[i], caseSensitive: caseSensitive); + targetName = ValueAsString(targetObject, propertyName: propertyNames[i], caseSensitive: caseSensitive); if (targetName != null) path = propertyNames[i]; } @@ -97,10 +108,11 @@ private static string CustomTargetPropertyBinding(string[] propertyNames, bool c /// Get the TargetName of the object by using any of the specified property names. /// /// One or more property names to use to bind TargetName. + /// Determines if binding properties are case-sensitive. /// A PSObject to bind. /// The next delegate function to check if all of the property names can not be found. /// The TargetName of the object. - private static string NestedTargetPropertyBinding(string[] propertyNames, bool caseSensitive, PSObject targetObject, BindTargetName next, out string path) + private static string NestedTargetPropertyBinding(string[] propertyNames, bool caseSensitive, object targetObject, BindTargetName next, out string path) { path = null; string targetName = null; @@ -128,7 +140,7 @@ private static string NestedTargetPropertyBinding(string[] propertyNames, bool c /// /// A PSObject to hash. /// The TargetName of the object. - private static string GetUnboundObjectTargetName(PSObject targetObject) + private static string GetUnboundObjectTargetName(object targetObject) { var settings = new JsonSerializerSettings { @@ -146,9 +158,9 @@ private static string GetUnboundObjectTargetName(PSObject targetObject) /// /// Try to get TargetName from specified property. /// - private static bool TryGetTargetName(PSObject targetObject, string propertyName, out string targetName) + private static bool TryGetTargetName(object targetObject, string propertyName, out string targetName) { - targetName = targetObject.ValueAsString(propertyName, false); + targetName = ValueAsString(targetObject, propertyName, false); return targetName != null; } @@ -157,34 +169,49 @@ private static bool TryGetTargetName(PSObject targetObject, string propertyName, /// /// A PSObject to bind. /// The TargetObject of the object. - private static string DefaultTargetTypeBinding(PSObject targetObject) + private static string DefaultTargetTypeBinding(object targetObject) + { + return TryGetInfoTargetType(targetObject, out var targetType) ? targetType : GetTypeNames(targetObject); + } + + private static string GetTypeNames(object targetObject) { - return TryGetInfoTargetType(targetObject, out var targetType) ? targetType : targetObject.TypeNames[0]; + if (targetObject == null) + return null; + + return targetObject is PSObject pso ? pso.TypeNames[0] : targetObject.GetType().FullName; } - private static string DefaultFieldBinding(PSObject targetObject) + private static string DefaultFieldBinding(object targetObject) { return null; } - private static bool TryGetInfoTargetName(PSObject targetObject, out string targetName) + private static bool TryGetInfoTargetName(object targetObject, out string targetName) { targetName = null; - if (!(targetObject.BaseObject is ITargetInfo info)) + var baseObject = ExpressionHelpers.GetBaseObject(targetObject); + if (baseObject is not ITargetInfo info) return false; targetName = info.TargetName; return true; } - private static bool TryGetInfoTargetType(PSObject targetObject, out string targetType) + private static bool TryGetInfoTargetType(object targetObject, out string targetType) { targetType = null; - if (!(targetObject.BaseObject is ITargetInfo info)) + var baseObject = ExpressionHelpers.GetBaseObject(targetObject); + if (baseObject is not ITargetInfo info) return false; targetType = info.TargetType; return true; } + + private static string ValueAsString(object o, string propertyName, bool caseSensitive) + { + return ObjectHelper.GetPath(bindingContext: null, targetObject: o, path: propertyName, caseSensitive: caseSensitive, value: out object value) && value != null ? value.ToString() : null; + } } } diff --git a/src/PSRule/Pipeline/TargetBinder.cs b/src/PSRule/Pipeline/TargetBinder.cs index df272a9099..6fc3aecfc0 100644 --- a/src/PSRule/Pipeline/TargetBinder.cs +++ b/src/PSRule/Pipeline/TargetBinder.cs @@ -4,8 +4,8 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Management.Automation; using PSRule.Configuration; +using static PSRule.Pipeline.TargetBinder; namespace PSRule.Pipeline { @@ -17,6 +17,8 @@ internal interface ITargetBinder void Bind(TargetObject targetObject); ITargetBindingContext Using(string languageScope); + + ITargetBindingResult Result(string languageScope); } /// @@ -26,6 +28,11 @@ internal interface ITargetBindingContext { string LanguageScope { get; } + ITargetBindingResult Bind(object o); + } + + internal interface ITargetBindingResult + { /// /// The bound TargetName of the target object. /// @@ -46,8 +53,6 @@ internal interface ITargetBindingContext Hashtable Field { get; } bool ShouldFilter { get; } - - void Bind(TargetObject targetObject, BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, HashSet typeFilter); } /// @@ -56,7 +61,7 @@ internal interface ITargetBindingContext internal sealed class TargetBinderBuilder { private readonly List _BindingContext; - private readonly string[] _TypeFilter; + private readonly HashSet _TypeFilter; private readonly BindTargetMethod _BindTargetName; private readonly BindTargetMethod _BindTargetType; private readonly BindTargetMethod _BindField; @@ -67,7 +72,8 @@ public TargetBinderBuilder(BindTargetMethod bindTargetName, BindTargetMethod bin _BindTargetType = bindTargetType; _BindField = bindField; _BindingContext = new List(); - _TypeFilter = typeFilter; + if (typeFilter != null && typeFilter.Length > 0) + _TypeFilter = new HashSet(typeFilter, StringComparer.OrdinalIgnoreCase); } /// @@ -75,7 +81,7 @@ public TargetBinderBuilder(BindTargetMethod bindTargetName, BindTargetMethod bin /// public ITargetBinder Build() { - return new TargetBinder(_BindingContext.ToArray(), _BindTargetName, _BindTargetType, _BindField, _TypeFilter); + return new TargetBinder(_BindingContext.ToArray()); } /// @@ -85,6 +91,14 @@ public void With(ITargetBindingContext bindingContext) { _BindingContext.Add(bindingContext); } + + /// + /// Add a target binding context. + /// + public void With(string languageScope, IBindingOption bindingOption) + { + _BindingContext.Add(new TargetBindingContext(languageScope, bindingOption, _BindTargetName, _BindTargetType, _BindField, _TypeFilter)); + } } /// @@ -94,23 +108,13 @@ internal sealed class TargetBinder : ITargetBinder { private const string STANDALONE_SCOPE = "."; - private readonly BindTargetMethod _BindTargetName; - - private readonly BindTargetMethod _BindTargetType; - private readonly BindTargetMethod _BindField; - private readonly HashSet _TypeFilter; - private readonly Dictionary _BindingContext; + private readonly Dictionary _BindingResult; - internal TargetBinder(ITargetBindingContext[] bindingContext, BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, string[] typeFilter) + internal TargetBinder(ITargetBindingContext[] bindingContext) { _BindingContext = new Dictionary(); - _BindTargetName = bindTargetName; - _BindTargetType = bindTargetType; - _BindField = bindField; - if (typeFilter != null && typeFilter.Length > 0) - _TypeFilter = new HashSet(typeFilter, StringComparer.OrdinalIgnoreCase); - + _BindingResult = new Dictionary(); for (var i = 0; bindingContext != null && i < bindingContext.Length; i++) _BindingContext.Add(bindingContext[i].LanguageScope ?? STANDALONE_SCOPE, bindingContext[i]); } @@ -166,56 +170,79 @@ internal void Protect() } } - internal sealed class TargetBindingContext : ITargetBindingContext + internal sealed class TargetBindingResult : ITargetBindingResult { - private readonly IBindingOption _BindingOption; - - public TargetBindingContext(string languageScope, IBindingOption bindingOption) + public TargetBindingResult(string targetName, string targetNamePath, string targetType, string targetTypePath, bool shouldFilter, Hashtable field) { - LanguageScope = languageScope; - _BindingOption = bindingOption; + TargetName = targetName; + TargetNamePath = targetNamePath; + TargetType = targetType; + TargetTypePath = targetTypePath; + ShouldFilter = shouldFilter; + Field = field; } - public string LanguageScope { get; } + /// + public string TargetName { get; } - /// - /// The bound TargetName of the target object. - /// - public string TargetName { get; private set; } + /// + public string TargetNamePath { get; } - public string TargetNamePath { get; private set; } + /// + public string TargetType { get; } - /// - /// The bound TargetType of the target object. - /// - public string TargetType { get; private set; } + /// + public string TargetTypePath { get; } - public string TargetTypePath { get; private set; } + /// + public bool ShouldFilter { get; } - /// - /// Additional bound fields of the target object. - /// - public Hashtable Field { get; private set; } + /// + public Hashtable Field { get; } + } - /// - /// Determines if the target object should be filtered. - /// - public bool ShouldFilter { get; private set; } + internal sealed class TargetBindingContext : ITargetBindingContext + { + private readonly IBindingOption _BindingOption; + private readonly BindTargetMethod _BindTargetName; + private readonly BindTargetMethod _BindTargetType; + private readonly BindTargetMethod _BindField; + private readonly HashSet _TypeFilter; - public void Bind(TargetObject targetObject, BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, HashSet typeFilter) + public TargetBindingContext(string languageScope, IBindingOption bindingOption, BindTargetMethod bindTargetName, BindTargetMethod bindTargetType, BindTargetMethod bindField, HashSet typeFilter) { - TargetName = bindTargetName(_BindingOption.TargetName, !_BindingOption.IgnoreCase, _BindingOption.PreferTargetInfo, targetObject.Value, out var targetNamePath); - TargetNamePath = targetNamePath; - TargetType = bindTargetType(_BindingOption.TargetType, !_BindingOption.IgnoreCase, _BindingOption.PreferTargetInfo, targetObject.Value, out var targetTypePath); - TargetTypePath = targetTypePath; - ShouldFilter = !(typeFilter == null || typeFilter.Contains(TargetType)); + LanguageScope = languageScope; + _BindingOption = bindingOption; + _BindTargetName = bindTargetName; + _BindTargetType = bindTargetType; + _BindField = bindField; + _TypeFilter = typeFilter; + } + + public string LanguageScope { get; } + + public ITargetBindingResult Bind(object o) + { + var targetName = _BindTargetName(_BindingOption.TargetName, !_BindingOption.IgnoreCase, _BindingOption.PreferTargetInfo, o, out var targetNamePath); + var targetType = _BindTargetType(_BindingOption.TargetType, !_BindingOption.IgnoreCase, _BindingOption.PreferTargetInfo, o, out var targetTypePath); + var shouldFilter = !(_TypeFilter == null || _TypeFilter.Contains(targetType)); // Use qualified name if (_BindingOption.UseQualifiedName) - TargetName = string.Concat(TargetType, _BindingOption.NameSeparator, TargetName); + targetName = string.Concat(targetType, _BindingOption.NameSeparator, targetName); // Bind custom fields - Field = BindField(bindField, _BindingOption.Field, !_BindingOption.IgnoreCase, targetObject.Value); + var field = BindField(_BindField, _BindingOption.Field, !_BindingOption.IgnoreCase, o); + + return new TargetBindingResult + ( + targetName: targetName, + targetNamePath: targetNamePath, + targetType: targetType, + targetTypePath: targetTypePath, + shouldFilter: shouldFilter, + field: field + ); } } @@ -227,7 +254,7 @@ public void Bind(TargetObject targetObject, BindTargetMethod bindTargetName, Bin public void Bind(TargetObject targetObject) { foreach (var bindingContext in _BindingContext.Values) - bindingContext.Bind(targetObject, _BindTargetName, _BindTargetType, _BindField, _TypeFilter); + _BindingResult[bindingContext.LanguageScope] = bindingContext.Bind(targetObject.Value); } public ITargetBindingContext Using(string languageScope) @@ -235,10 +262,15 @@ public ITargetBindingContext Using(string languageScope) return _BindingContext.TryGetValue(languageScope ?? STANDALONE_SCOPE, out var result) ? result : null; } + public ITargetBindingResult Result(string languageScope) + { + return _BindingResult.TryGetValue(languageScope ?? STANDALONE_SCOPE, out var result) ? result : null; + } + /// /// Bind additional fields. /// - private static Hashtable BindField(BindTargetMethod bindField, FieldMap[] map, bool caseSensitive, PSObject targetObject) + private static Hashtable BindField(BindTargetMethod bindField, FieldMap[] map, bool caseSensitive, object o) { if (map == null || map.Length == 0) return null; @@ -254,7 +286,7 @@ private static Hashtable BindField(BindTargetMethod bindField, FieldMap[] map, b if (hashtable.ContainsKey(field.Key)) continue; - hashtable.Add(field.Key, bindField(field.Value, caseSensitive, false, targetObject, out _)); + hashtable.Add(field.Key, bindField(field.Value, caseSensitive, false, o, out _)); } } hashtable.Protect(); diff --git a/src/PSRule/Resources/PSRuleResources.Designer.cs b/src/PSRule/Resources/PSRuleResources.Designer.cs index f6d2da4b2e..c3b501f2cb 100644 --- a/src/PSRule/Resources/PSRuleResources.Designer.cs +++ b/src/PSRule/Resources/PSRuleResources.Designer.cs @@ -8,11 +8,10 @@ // //------------------------------------------------------------------------------ -namespace PSRule.Resources -{ +namespace PSRule.Resources { using System; - - + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -23,980 +22,805 @@ namespace PSRule.Resources [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class PSRuleResources - { - + internal class PSRuleResources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal PSRuleResources() - { + internal PSRuleResources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [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.PSRuleResources", typeof(PSRuleResources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [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; } } - + + /// + /// 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.. + /// + internal static string AliasReference { + get { + return ResourceManager.GetString("AliasReference", resourceCulture); + } + } + + /// + /// 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.. + /// + internal static string AliasSuppression { + get { + return ResourceManager.GetString("AliasSuppression", resourceCulture); + } + } + /// /// Looks up a localized string similar to The arguments for '{0}' are not in the expected format or type.. /// - internal static string ArgumentFormatInvalid - { - get - { + internal static string ArgumentFormatInvalid { + get { return ResourceManager.GetString("ArgumentFormatInvalid", resourceCulture); } } - + /// /// Looks up a localized string similar to The argument '{0}' for '{1}' is not a valid boolean.. /// - internal static string ArgumentInvalidBoolean - { - get - { + internal static string ArgumentInvalidBoolean { + get { return ResourceManager.GetString("ArgumentInvalidBoolean", resourceCulture); } } - + /// /// Looks up a localized string similar to The argument '{0}' for '{1}' is not a valid integer.. /// - internal static string ArgumentInvalidInteger - { - get - { + internal static string ArgumentInvalidInteger { + get { return ResourceManager.GetString("ArgumentInvalidInteger", resourceCulture); } } - + /// /// Looks up a localized string similar to The argument '{0}' for '{1}' is not a valid string.. /// - internal static string ArgumentInvalidString - { - get - { + internal static string ArgumentInvalidString { + get { return ResourceManager.GetString("ArgumentInvalidString", resourceCulture); } } - + /// /// Looks up a localized string similar to The number of arguments '{1}' is not within the allowed range for '{0}'.. /// - internal static string ArgumentsOutOfRange - { - get - { + internal static string ArgumentsOutOfRange { + get { return ResourceManager.GetString("ArgumentsOutOfRange", resourceCulture); } } - + /// /// Looks up a localized string similar to The baseline '{0}' is obsolete. Consider switching to an alternative baseline.. /// - internal static string AliasReference - { - get - { - return ResourceManager.GetString("AliasReference", resourceCulture); - } - } - - /// - /// 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.. - /// - internal static string AliasSuppression - { - get - { - return ResourceManager.GetString("AliasSuppression", resourceCulture); + internal static string BaselineObsolete { + get { + return ResourceManager.GetString("BaselineObsolete", resourceCulture); } } - + /// /// Looks up a localized string similar to Binding functions are not supported in this language mode.. /// - internal static string ConstrainedTargetBinding - { - get - { + internal static string ConstrainedTargetBinding { + get { return ResourceManager.GetString("ConstrainedTargetBinding", resourceCulture); } } - + /// /// Looks up a localized string similar to {0}: The property '${1}.{2}' is obsolete and will be removed in the next major version.. /// - internal static string DebugPropertyObsolete - { - get - { + internal static string DebugPropertyObsolete { + get { return ResourceManager.GetString("DebugPropertyObsolete", resourceCulture); } } - + /// /// Looks up a localized string similar to Target failed If precondition. /// - internal static string DebugTargetIfMismatch - { - get - { + internal static string DebugTargetIfMismatch { + get { return ResourceManager.GetString("DebugTargetIfMismatch", resourceCulture); } } - + /// /// Looks up a localized string similar to Target failed Rule precondition. /// - internal static string DebugTargetRuleMismatch - { - get - { + internal static string DebugTargetRuleMismatch { + get { return ResourceManager.GetString("DebugTargetRuleMismatch", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Target failed sub-selector pre-condition.. + /// + internal static string DebugTargetSubselectorMismatch { + get { + return ResourceManager.GetString("DebugTargetSubselectorMismatch", resourceCulture); + } + } + /// /// Looks up a localized string similar to Target failed Type precondition. /// - internal static string DebugTargetTypeMismatch - { - get - { + internal static string DebugTargetTypeMismatch { + get { return ResourceManager.GetString("DebugTargetTypeMismatch", resourceCulture); } } - + /// /// Looks up a localized string similar to A circular rule dependency was detected. The rule '{0}' depends on '{1}' which also depend on '{0}'.. /// - internal static string DependencyCircularReference - { - get - { + internal static string DependencyCircularReference { + get { return ResourceManager.GetString("DependencyCircularReference", resourceCulture); } } - + /// /// Looks up a localized string similar to The dependency '{0}' for '{1}' could not be found. Check that the rule is defined in a .Rule.ps1 file within the search path.. /// - internal static string DependencyNotFound - { - get - { + internal static string DependencyNotFound { + get { return ResourceManager.GetString("DependencyNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to The resource '{0}' is using a duplicate resource identifier. A resource with the identifier '{1}' already exists. Each resource must have a unique name, ref, and aliases. See https://aka.ms/ps-rule/naming for guidance on naming within PSRule.. /// - internal static string DuplicateResourceId - { - get - { + internal static string DuplicateResourceId { + get { return ResourceManager.GetString("DuplicateResourceId", resourceCulture); } } - + /// /// Looks up a localized string similar to A rule with the same name '{0}' already exists.. /// - internal static string DuplicateRuleName - { - get - { + internal static string DuplicateRuleName { + get { return ResourceManager.GetString("DuplicateRuleName", resourceCulture); } } - + /// /// Looks up a localized string similar to {0} : Reported '{1}'. At {2}:{3} char:{4}. /// - internal static string ErrorDetailMessage - { - get - { + internal static string ErrorDetailMessage { + get { return ResourceManager.GetString("ErrorDetailMessage", resourceCulture); } } - + /// /// Looks up a localized string similar to One or more errors occured.. /// - internal static string ErrorPipelineException - { - get - { + internal static string ErrorPipelineException { + get { return ResourceManager.GetString("ErrorPipelineException", resourceCulture); } } - + /// /// Looks up a localized string similar to Exists: {0}. /// - internal static string ExistsTrue - { - get - { + internal static string ExistsTrue { + get { return ResourceManager.GetString("ExistsTrue", resourceCulture); } } - + /// - /// Looks up a localized string similar to File. + /// Looks up a localized string similar to Failed to parse expression. The expression may not be valid. Expression: "{0}". /// - internal static string FileSourceType - { - get - { - return ResourceManager.GetString("FileSourceType", resourceCulture); + internal static string ExpressionInvalid { + get { + return ResourceManager.GetString("ExpressionInvalid", resourceCulture); } } - + /// - /// Looks up a localized string similar to Failed to parse expression. The expression may not be valid. Expression: "{0}". + /// Looks up a localized string similar to File. /// - internal static string ExpressionInvalid - { - get - { - return ResourceManager.GetString("ExpressionInvalid", resourceCulture); + internal static string FileSourceType { + get { + return ResourceManager.GetString("FileSourceType", resourceCulture); } } - + /// /// Looks up a localized string similar to [PSRule][D] -- Found {0} PSRule module(s). /// - internal static string FoundModules - { - get - { + internal static string FoundModules { + get { return ResourceManager.GetString("FoundModules", resourceCulture); } } - + /// /// Looks up a localized string similar to The function "{0}" was not found.. /// - internal static string FunctionNotFound - { - get - { + internal static string FunctionNotFound { + get { return ResourceManager.GetString("FunctionNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to The language expression index '{0}' is not valid for the object.. /// - internal static string IndexInvalid - { - get - { + internal static string IndexInvalid { + get { return ResourceManager.GetString("IndexInvalid", resourceCulture); } } - + /// /// Looks up a localized string similar to Output written to the following file: '{0}'. /// - internal static string InfoOutputPath - { - get - { + internal static string InfoOutputPath { + get { return ResourceManager.GetString("InfoOutputPath", resourceCulture); } } - + /// /// Looks up a localized string similar to An invalid ErrorAction ({0}) was specified for rule at {1}. Ignore | Stop are supported.. /// - internal static string InvalidErrorAction - { - get - { + internal static string InvalidErrorAction { + get { return ResourceManager.GetString("InvalidErrorAction", resourceCulture); } } - + /// /// Looks up a localized string similar to The resource name '{0}' is not valid at {1}. Each resource name must be between 3-128 characters in length, must start and end with a letter or number, and only contain letters, numbers, hyphens, dots, or underscores. See https://aka.ms/ps-rule/naming for more information.. /// - internal static string InvalidResourceName - { - get - { + internal static string InvalidResourceName { + get { return ResourceManager.GetString("InvalidResourceName", resourceCulture); } } - + /// /// Looks up a localized string similar to Rule nesting was detected for rule at {0}. Rules must not be nested.. /// - internal static string InvalidRuleNesting - { - get - { + internal static string InvalidRuleNesting { + get { return ResourceManager.GetString("InvalidRuleNesting", resourceCulture); } } - + /// /// Looks up a localized string similar to An invalid rule result was returned for {0}. Conditions must return boolean $True or $False.. /// - internal static string InvalidRuleResult - { - get - { + internal static string InvalidRuleResult { + get { return ResourceManager.GetString("InvalidRuleResult", resourceCulture); } } - + /// /// Looks up a localized string similar to The keyword '{0}' can only be used within a Rule block.. /// - internal static string KeywordConditionScope - { - get - { + internal static string KeywordConditionScope { + get { return ResourceManager.GetString("KeywordConditionScope", resourceCulture); } } - + /// /// Looks up a localized string similar to The keyword '{0}' can only be used within a Rule block or script precondition.. /// - internal static string KeywordRuleScope - { - get - { + internal static string KeywordRuleScope { + get { return ResourceManager.GetString("KeywordRuleScope", resourceCulture); } } - + /// /// Looks up a localized string similar to The keyword '{0}' can not be nested in a Rule block.. /// - internal static string KeywordSourceScope - { - get - { + internal static string KeywordSourceScope { + get { return ResourceManager.GetString("KeywordSourceScope", resourceCulture); } } - + /// /// Looks up a localized string similar to [PSRule][{0}][Trace] -- {1}: {2}. /// - internal static string LanguageExpressionTraceP2 - { - get - { + internal static string LanguageExpressionTraceP2 { + get { return ResourceManager.GetString("LanguageExpressionTraceP2", resourceCulture); } } - + /// /// Looks up a localized string similar to [PSRule][{0}][Trace] -- {1}: {2} {1} {3}. /// - internal static string LanguageExpressionTraceP3 - { - get - { + internal static string LanguageExpressionTraceP3 { + get { return ResourceManager.GetString("LanguageExpressionTraceP3", resourceCulture); } } - - internal static string LanguageExpressionTrace - { - get - { - return ResourceManager.GetString("LanguageExpressionTrace", resourceCulture); - } - } - + /// /// Looks up a localized string similar to Please open your browser to the following location: {0}. /// - internal static string LaunchBrowser - { - get - { + internal static string LaunchBrowser { + get { return ResourceManager.GetString("LaunchBrowser", resourceCulture); } } - + /// /// Looks up a localized string similar to Wildcard match requires exactly one name.. /// - internal static string MatchSingleName - { - get - { + internal static string MatchSingleName { + get { return ResourceManager.GetString("MatchSingleName", resourceCulture); } } - + /// /// Looks up a localized string similar to Matches: {0}. /// - internal static string MatchTrue - { - get - { + internal static string MatchTrue { + get { return ResourceManager.GetString("MatchTrue", resourceCulture); } } - + /// /// Looks up a localized string similar to Update module '{0}' to set the default baseline using a module configuration resource instead. Configuring the default baseline via manifest will be removed in the next major version. See https://aka.ms/ps-rule/module-config.. /// - internal static string ModuleManifestBaseline - { - get - { + internal static string ModuleManifestBaseline { + get { return ResourceManager.GetString("ModuleManifestBaseline", resourceCulture); } } - + /// /// Looks up a localized string similar to No valid module can be found with that name.. /// - internal static string ModuleNotFound - { - get - { + internal static string ModuleNotFound { + get { return ResourceManager.GetString("ModuleNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to Target object '{0}' has not been processed because no matching rules were found.. /// - internal static string ObjectNotProcessed - { - get - { + internal static string ObjectNotProcessed { + get { return ResourceManager.GetString("ObjectNotProcessed", resourceCulture); } } - + /// /// Looks up a localized string similar to Object path not found.. /// - internal static string ObjectPathNotFound - { - get - { + internal static string ObjectPathNotFound { + get { return ResourceManager.GetString("ObjectPathNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to Options file does not exist.. /// - internal static string OptionsNotFound - { - get - { + internal static string OptionsNotFound { + get { return ResourceManager.GetString("OptionsNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to # Source: {0}. /// - internal static string OptionsSourceComment - { - get - { + internal static string OptionsSourceComment { + get { return ResourceManager.GetString("OptionsSourceComment", resourceCulture); } } - + /// /// Looks up a localized string similar to Error. /// - internal static string OutcomeError - { - get - { + internal static string OutcomeError { + get { return ResourceManager.GetString("OutcomeError", resourceCulture); } } - + /// /// Looks up a localized string similar to Fail. /// - internal static string OutcomeFail - { - get - { + internal static string OutcomeFail { + get { return ResourceManager.GetString("OutcomeFail", resourceCulture); } } - + /// /// Looks up a localized string similar to Pass. /// - internal static string OutcomePass - { - get - { + internal static string OutcomePass { + get { return ResourceManager.GetString("OutcomePass", resourceCulture); } } - + /// /// Looks up a localized string similar to [FAIL] -- {0}:: Reported for '{1}'. /// - internal static string OutcomeRuleFail - { - get - { + internal static string OutcomeRuleFail { + get { return ResourceManager.GetString("OutcomeRuleFail", resourceCulture); } } - + /// /// Looks up a localized string similar to [PASS] -- {0}:: Reported for '{1}'. /// - internal static string OutcomeRulePass - { - get - { + internal static string OutcomeRulePass { + get { return ResourceManager.GetString("OutcomeRulePass", resourceCulture); } } - + /// /// Looks up a localized string similar to Unknown. /// - internal static string OutcomeUnknown - { - get - { + internal static string OutcomeUnknown { + get { return ResourceManager.GetString("OutcomeUnknown", resourceCulture); } } - + /// /// Looks up a localized string similar to The language expression property '{0}' doesn't exist.. /// - internal static string PropertyNotFound - { - get - { + internal static string PropertyNotFound { + get { return ResourceManager.GetString("PropertyNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to The property '${0}.{1}' is obsolete and will be removed in the next major version.. /// - internal static string PropertyObsolete - { - get - { + internal static string PropertyObsolete { + get { return ResourceManager.GetString("PropertyObsolete", resourceCulture); } } - + /// /// Looks up a localized string similar to Failed to deserialize the file '{0}': {1}. /// - internal static string ReadFileFailed - { - get - { + internal static string ReadFileFailed { + get { return ResourceManager.GetString("ReadFileFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to Read JSON failed.. /// - internal static string ReadJsonFailed - { - get - { + internal static string ReadJsonFailed { + get { return ResourceManager.GetString("ReadJsonFailed", resourceCulture); } } - + /// /// Looks up a localized string similar to Read JSON failed because the token ({0}) was not expected.. /// - internal static string ReadJsonFailedExpectedToken - { - get - { + internal static string ReadJsonFailedExpectedToken { + get { return ResourceManager.GetString("ReadJsonFailedExpectedToken", resourceCulture); } } - + /// /// Looks up a localized string similar to The module version '{1}' for '{0}' does not match the required version '{2}'. To continue, first update the module to match the version requirement.. /// - internal static string RequiredVersionMismatch - { - get - { + internal static string RequiredVersionMismatch { + get { return ResourceManager.GetString("RequiredVersionMismatch", resourceCulture); } } - + /// /// Looks up a localized string similar to The {0} '{1}' is obsolete. Consider switching to an alternative {0}.. /// - internal static string ResourceObsolete - { - get - { + internal static string ResourceObsolete { + get { return ResourceManager.GetString("ResourceObsolete", resourceCulture); } } - + /// /// Looks up a localized string similar to {0} rule/s were suppressed for '{1}'.. /// - internal static string RuleCountSuppressed - { - get - { + internal static string RuleCountSuppressed { + get { return ResourceManager.GetString("RuleCountSuppressed", resourceCulture); } } - + /// /// Looks up a localized string similar to One or more rules reported errors.. /// - internal static string RuleErrorPipelineException - { - get - { + internal static string RuleErrorPipelineException { + get { return ResourceManager.GetString("RuleErrorPipelineException", resourceCulture); } } - + /// /// Looks up a localized string similar to One or more rules reported failure.. /// - internal static string RuleFailPipelineException - { - get - { + internal static string RuleFailPipelineException { + get { return ResourceManager.GetString("RuleFailPipelineException", resourceCulture); } } - + /// /// Looks up a localized string similar to Inconclusive result reported for '{1}' @{0}.. /// - internal static string RuleInconclusive - { - get - { + internal static string RuleInconclusive { + get { return ResourceManager.GetString("RuleInconclusive", resourceCulture); } } - + /// /// Looks up a localized string similar to [PSRule][R][Trace] -- {0}. /// - internal static string RuleMatchTrace - { - get - { + internal static string RuleMatchTrace { + get { return ResourceManager.GetString("RuleMatchTrace", resourceCulture); } } - + /// /// Looks up a localized string similar to Could not find a matching rule. Please check that Path, Name and Tag parameters are correct.. /// - internal static string RuleNotFound - { - get - { + internal static string RuleNotFound { + get { return ResourceManager.GetString("RuleNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to Could not find required rule definition parameter '{0}' on rule at {1}.. /// - internal static string RuleParameterNotFound - { - get - { + internal static string RuleParameterNotFound { + get { return ResourceManager.GetString("RuleParameterNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to No matching .Rule.ps1 files were found. Rule definitions should be saved into script files with the .Rule.ps1 extension.. /// - internal static string RulePathNotFound - { - get - { + internal static string RulePathNotFound { + get { return ResourceManager.GetString("RulePathNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to at Rule '{0}', {1}: line {2}. /// - internal static string RuleStackTrace - { - get - { + internal static string RuleStackTrace { + get { return ResourceManager.GetString("RuleStackTrace", resourceCulture); } } - + /// /// Looks up a localized string similar to Rule '{0}' was suppressed for '{1}'.. /// - internal static string RuleSuppressed - { - get - { + internal static string RuleSuppressed { + get { return ResourceManager.GetString("RuleSuppressed", resourceCulture); } } - + /// /// Looks up a localized string similar to Rule '{0}' was suppressed by suppression group '{1}' for '{2}'.. /// - internal static string RuleSuppressionGroup - { - get - { + internal static string RuleSuppressionGroup { + get { return ResourceManager.GetString("RuleSuppressionGroup", resourceCulture); } } - + /// /// Looks up a localized string similar to {0} rule/s were suppressed by suppression group '{1}' for '{2}'.. /// - internal static string RuleSuppressionGroupCount - { - get - { + internal static string RuleSuppressionGroupCount { + get { return ResourceManager.GetString("RuleSuppressionGroupCount", resourceCulture); } } - + /// /// Looks up a localized string similar to Rule '{0}' was suppressed by suppression group '{1}' for '{2}'. {3}. /// - internal static string RuleSuppressionGroupExtended - { - get - { + internal static string RuleSuppressionGroupExtended { + get { return ResourceManager.GetString("RuleSuppressionGroupExtended", resourceCulture); } } - + /// /// Looks up a localized string similar to {0} rule/s were suppressed by suppression group '{1}' for '{2}'. {3}. /// - internal static string RuleSuppressionGroupExtendedCount - { - get - { + internal static string RuleSuppressionGroupExtendedCount { + get { return ResourceManager.GetString("RuleSuppressionGroupExtendedCount", resourceCulture); } } - + /// /// Looks up a localized string similar to [PSRule][D] -- Scanning for source files in module: {0}. /// - internal static string ScanModule - { - get - { + internal static string ScanModule { + get { return ResourceManager.GetString("ScanModule", resourceCulture); } } - + /// /// Looks up a localized string similar to [PSRule][D] -- Scanning for source files: {0}. /// - internal static string ScanSource - { - get - { + internal static string ScanSource { + get { return ResourceManager.GetString("ScanSource", resourceCulture); } } - + /// /// Looks up a localized string similar to The script was not found.. /// - internal static string ScriptNotFound - { - get - { + internal static string ScriptNotFound { + get { return ResourceManager.GetString("ScriptNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to [PSRule][S][Trace] -- {0}. /// - internal static string SelectorMatchTrace - { - get - { + internal static string SelectorMatchTrace { + get { return ResourceManager.GetString("SelectorMatchTrace", resourceCulture); } } - - /// - /// Looks up a localized string similar to [PSRule][S][Trace] -- {0}: {1}. - /// - internal static string SelectorTrace - { - get - { - return ResourceManager.GetString("SelectorTrace", resourceCulture); - } - } - + /// /// Looks up a localized string similar to Can not serialize a null PSObject.. /// - internal static string SerializeNullPSObject - { - get - { + internal static string SerializeNullPSObject { + get { return ResourceManager.GetString("SerializeNullPSObject", resourceCulture); } } - + /// /// Looks up a localized string similar to Create path. /// - internal static string ShouldCreatePath - { - get - { + internal static string ShouldCreatePath { + get { return ResourceManager.GetString("ShouldCreatePath", resourceCulture); } } - + /// /// Looks up a localized string similar to Write file. /// - internal static string ShouldWriteFile - { - get - { + internal static string ShouldWriteFile { + get { return ResourceManager.GetString("ShouldWriteFile", resourceCulture); } } - + /// /// Looks up a localized string similar to The source was not found.. /// - internal static string SourceNotFound - { - get - { + internal static string SourceNotFound { + get { return ResourceManager.GetString("SourceNotFound", resourceCulture); } } - + /// /// Looks up a localized string similar to Using invariant culture may cause rule infomation to be displayed incorrectly. Consider using -Culture or set the Output.Culture option.. /// - internal static string UsingInvariantCulture - { - get - { + internal static string UsingInvariantCulture { + get { return ResourceManager.GetString("UsingInvariantCulture", resourceCulture); } } - + /// /// Looks up a localized string similar to The variable '${0}' can only be used within a Rule block.. /// - internal static string VariableConditionScope - { - get - { + internal static string VariableConditionScope { + get { return ResourceManager.GetString("VariableConditionScope", resourceCulture); } } - + /// /// Looks up a localized string similar to The version constraint '{0}' is not valid.. /// - internal static string VersionConstraintInvalid - { - get - { + internal static string VersionConstraintInvalid { + get { return ResourceManager.GetString("VersionConstraintInvalid", resourceCulture); } } - + /// /// Looks up a localized string similar to The Within parameter Value must be a string when the Like parameter is used.. /// - internal static string WithinLikeNotString - { - get - { + internal static string WithinLikeNotString { + get { return ResourceManager.GetString("WithinLikeNotString", resourceCulture); } } - + /// /// Looks up a localized string similar to Within: {0}. /// - internal static string WithinTrue - { - get - { + internal static string WithinTrue { + get { return ResourceManager.GetString("WithinTrue", resourceCulture); } } diff --git a/src/PSRule/Resources/PSRuleResources.resx b/src/PSRule/Resources/PSRuleResources.resx index 71168f0047..4097ade673 100644 --- a/src/PSRule/Resources/PSRuleResources.resx +++ b/src/PSRule/Resources/PSRuleResources.resx @@ -382,4 +382,7 @@ Read JSON failed because the token ({0}) was not expected. + + Target failed sub-selector pre-condition. + \ No newline at end of file diff --git a/src/PSRule/Runtime/LanguageScope.cs b/src/PSRule/Runtime/LanguageScope.cs index f94c4f4ccc..e9ad168350 100644 --- a/src/PSRule/Runtime/LanguageScope.cs +++ b/src/PSRule/Runtime/LanguageScope.cs @@ -12,6 +12,9 @@ namespace PSRule.Runtime /// internal interface ILanguageScope : IDisposable { + /// + /// The name of the scope. + /// string Name { get; } /// @@ -28,29 +31,42 @@ internal interface ILanguageScope : IDisposable IResourceFilter GetFilter(ResourceKind kind); + /// + /// Add a service to the scope. + /// void AddService(string name, object service); + /// + /// Get a previously added service. + /// object GetService(string name); + + bool TryGetType(object o, out string type, out string path); + + bool TryGetName(object o, out string name, out string path); } internal sealed class LanguageScope : ILanguageScope { private const string STANDALONE_SCOPENAME = "."; + private readonly RunspaceContext _Context; private readonly Dictionary _Configuration; private readonly Dictionary _Service; private readonly Dictionary _Filter; private bool _Disposed; - public LanguageScope(string name) + public LanguageScope(RunspaceContext context, string name) { + _Context = context; Name = Normalize(name); _Configuration = new Dictionary(); _Filter = new Dictionary(); _Service = new Dictionary(); } + /// public string Name { get; } public void Configure(Dictionary configuration) @@ -74,6 +90,7 @@ public IResourceFilter GetFilter(ResourceKind kind) return _Filter.TryGetValue(kind, out var filter) ? filter : null; } + /// public void AddService(string name, object service) { if (_Service.ContainsKey(name)) @@ -82,11 +99,54 @@ public void AddService(string name, object service) _Service.Add(name, service); } + /// public object GetService(string name) { return _Service.TryGetValue(name, out var service) ? service : null; } + public bool TryGetType(object o, out string type, out string path) + { + if (_Context != null && _Context.TargetObject.Value == o) + { + var binding = _Context.TargetBinder.Result(Name); + type = binding.TargetType; + path = binding.TargetTypePath; + return true; + } + else if (_Context != null) + { + var binding = _Context.TargetBinder.Using(Name).Bind(o); + type = binding.TargetType; + path = binding.TargetTypePath; + return true; + } + type = null; + path = null; + return false; + } + + public bool TryGetName(object o, out string name, out string path) + { + if (_Context != null && _Context.TargetObject.Value == o) + { + var binding = _Context.TargetBinder.Result(Name); + name = binding.TargetName; + path = binding.TargetNamePath; + return true; + } + else if (_Context != null) + { + var binding = _Context.TargetBinder.Using(Name).Bind(o); + name = binding.TargetName; + path = binding.TargetNamePath; + return true; + } + name = null; + path = null; + return false; + } + internal static string Normalize(string scope) { return string.IsNullOrEmpty(scope) ? STANDALONE_SCOPENAME : scope; @@ -123,13 +183,15 @@ public void Dispose() internal sealed class LanguageScopeSet : IDisposable { + private readonly RunspaceContext _Context; private readonly Dictionary _Scopes; private ILanguageScope _Current; private bool _Disposed; - public LanguageScopeSet() + public LanguageScopeSet(RunspaceContext context) { + _Context = context; _Scopes = new Dictionary(StringComparer.OrdinalIgnoreCase); Import(null, out _Current); } @@ -168,7 +230,7 @@ internal bool Import(string name, out ILanguageScope scope) if (_Scopes.TryGetValue(GetScopeName(name), out scope)) return false; - scope = new LanguageScope(name); + scope = new LanguageScope(_Context, name); Add(scope); return true; } diff --git a/src/PSRule/Runtime/ObjectPath/PathExpression.cs b/src/PSRule/Runtime/ObjectPath/PathExpression.cs index f8fd9fc9cb..bffc335af5 100644 --- a/src/PSRule/Runtime/ObjectPath/PathExpression.cs +++ b/src/PSRule/Runtime/ObjectPath/PathExpression.cs @@ -10,7 +10,7 @@ namespace PSRule.Runtime.ObjectPath /// /// An expression function that returns one or more values when successful. /// - internal delegate bool PathExpressionFn(IPathExpressionContext context, object input, out IEnumerable value); + internal delegate bool PathExpressionFn(IPathExpressionContext context, object input, out IEnumerable value, out bool enumerable); /// /// A function for filter objects that simply returns true or false. @@ -92,7 +92,7 @@ public static PathExpression Create(string path) public bool TryGet(object o, bool caseSensitive, out object[] value) { value = null; - if (!TryGet(o, caseSensitive, out IEnumerable result)) + if (!TryGet(o, caseSensitive, out var result, out var enumerable)) return false; value = result.ToArray(); @@ -106,10 +106,11 @@ public bool TryGet(object o, bool caseSensitive, out object[] value) public bool TryGet(object o, bool caseSensitive, out object value) { value = null; - if (!TryGet(o, caseSensitive, out object[] result)) + if (!TryGet(o, caseSensitive, out var result, out var enumerable)) return false; - value = IsArray ? result : result[0]; + var items = result.ToArray(); + value = IsArray || enumerable ? items : items[0]; return true; } @@ -120,10 +121,10 @@ public bool TryGet(object o, bool caseSensitive, out object value) /// Determines if member name matching is case-sensitive. /// The values selected from the object. /// Returns true when the path exists within the object. Returns false if the path does not exist. - private bool TryGet(object o, bool caseSensitive, out IEnumerable value) + private bool TryGet(object o, bool caseSensitive, out IEnumerable value, out bool enumerable) { var context = new PathExpressionContext(o, caseSensitive); - return _Expression.Invoke(context, o, out value); + return _Expression.Invoke(context, o, out value, out enumerable); } } } diff --git a/src/PSRule/Runtime/ObjectPath/PathExpressionBuilder.cs b/src/PSRule/Runtime/ObjectPath/PathExpressionBuilder.cs index 22f0a98319..a8a90d918f 100644 --- a/src/PSRule/Runtime/ObjectPath/PathExpressionBuilder.cs +++ b/src/PSRule/Runtime/ObjectPath/PathExpressionBuilder.cs @@ -95,7 +95,7 @@ private PathExpressionFn FilterSelector(ITokenReader reader) UseArray(); var filter = BuildExpression(reader, PathTokenType.EndFilter); var next = BuildSelector(reader); - return (IPathExpressionContext context, object input, out IEnumerable value) => + return (IPathExpressionContext context, object input, out IEnumerable value, out bool enumerable) => { var result = new List(); var success = 0; @@ -104,13 +104,14 @@ private PathExpressionFn FilterSelector(ITokenReader reader) if (!filter(context, i)) continue; - if (!next(context, i, out var items)) + if (!next(context, i, out var items, out _)) continue; success++; result.AddRange(items); } value = success > 0 ? result.ToArray() : null; + enumerable = value != null; return success > 0; }; } @@ -118,10 +119,11 @@ private PathExpressionFn FilterSelector(ITokenReader reader) private PathExpressionFn IndexSelector(ITokenReader reader, int index) { var next = BuildSelector(reader); - return (IPathExpressionContext context, object input, out IEnumerable value) => + return (IPathExpressionContext context, object input, out IEnumerable value, out bool enumerable) => { value = null; - return TryGetIndex(input, index, out var item) && next(context, item, out value); + enumerable = false; + return TryGetIndex(input, index, out var item) && next(context, item, out value, out enumerable); }; } @@ -129,19 +131,20 @@ private PathExpressionFn IndexWildSelector(ITokenReader reader) { UseArray(); var next = BuildSelector(reader); - return (IPathExpressionContext context, object input, out IEnumerable value) => + return (IPathExpressionContext context, object input, out IEnumerable value, out bool enumerable) => { var result = new List(); var success = 0; foreach (var i in GetAll(input)) { - if (!next(context, i, out var items)) + if (!next(context, i, out var items, out _)) continue; success++; result.AddRange(items); } value = success > 0 ? result.ToArray() : null; + enumerable = value != null; return success > 0; }; } @@ -153,18 +156,19 @@ private PathExpressionFn ArraySliceSelector(ITokenReader reader, int?[] arg) var step = arg[2].GetValueOrDefault(1); var start = arg[0].GetValueOrDefault(step >= 0 ? 0 : -1); var end = arg[1]; - return (IPathExpressionContext context, object input, out IEnumerable value) => + return (IPathExpressionContext context, object input, out IEnumerable value, out bool enumerable) => { var result = new List(); var currentIndex = start; while ((!end.HasValue || (step > 0 && currentIndex < end) || (step < 0 && currentIndex > end)) && TryGetIndex(input, currentIndex, out var slice)) { currentIndex += step; - if (!next(context, slice, out var items)) + if (!next(context, slice, out var items, out _)) continue; result.AddRange(items); } + enumerable = true; value = result.ToArray(); return true; }; @@ -174,11 +178,12 @@ private PathExpressionFn DotSelector(ITokenReader reader, string memberName, Pat { var caseSensitiveFlag = option == PathTokenOption.CaseSensitive; var next = BuildSelector(reader); - return (IPathExpressionContext context, object input, out IEnumerable value) => + return (IPathExpressionContext context, object input, out IEnumerable value, out bool enumerable) => { value = null; + enumerable = false; var caseSensitive = context.CaseSensitive != caseSensitiveFlag; - return TryGetField(input, memberName, caseSensitive, out var item) && next(context, item, out value); + return TryGetField(input, memberName, caseSensitive, out var item) && next(context, item, out value, out enumerable); }; } @@ -186,20 +191,21 @@ private PathExpressionFn DescendantSelector(ITokenReader reader, string memberNa { var caseSensitiveFlag = option == PathTokenOption.CaseSensitive; var next = BuildSelector(reader); - return (IPathExpressionContext context, object input, out IEnumerable value) => + return (IPathExpressionContext context, object input, out IEnumerable value, out bool enumerable) => { var caseSensitive = context.CaseSensitive != caseSensitiveFlag; var result = new List(); var success = 0; foreach (var i in GetAllRecurse(input, memberName, caseSensitive)) { - if (!next(context, i, out var items)) + if (!next(context, i, out var items, out _)) continue; success++; result.AddRange(items); } value = success > 0 ? result.ToArray() : null; + enumerable = value != null; return success > 0; }; } @@ -207,13 +213,13 @@ private PathExpressionFn DescendantSelector(ITokenReader reader, string memberNa private PathExpressionFn CurrentRef(ITokenReader reader) { var next = BuildSelector(reader); - return (IPathExpressionContext context, object input, out IEnumerable value) => next(context, input, out value); + return (IPathExpressionContext context, object input, out IEnumerable value, out bool enumerable) => next(context, input, out value, out enumerable); } private PathExpressionFn RootRef(ITokenReader reader) { var next = BuildSelector(reader); - return (IPathExpressionContext context, object input, out IEnumerable value) => next(context, context.Input, out value); + return (IPathExpressionContext context, object input, out IEnumerable value, out bool enumerable) => next(context, context.Input, out value, out enumerable); } private PathExpressionFilterFn BuildExpression(ITokenReader reader, PathTokenType stop) @@ -282,7 +288,7 @@ private PathExpressionFilterFn BuildRelationExpression(ITokenReader reader) private static PathExpressionFilterFn ExistCondition(PathExpressionFn next) { - return (IPathExpressionContext context, object input) => next(context, input, out _); + return (IPathExpressionContext context, object input) => next(context, input, out _, out _); } private static PathExpressionFilterFn NotCondition(PathExpressionFilterFn next) @@ -294,7 +300,7 @@ private static PathExpressionFilterFn BinaryCondition(PathExpressionFn left, Pat { return (IPathExpressionContext context, object input) => { - if (!left(context, input, out var leftValue) || !right(context, input, out var rightValue)) + if (!left(context, input, out var leftValue, out _) || !right(context, input, out var rightValue, out _)) return false; var operand1 = leftValue.FirstOrDefault(); @@ -328,22 +334,32 @@ private static PathExpressionFilterFn BinaryCondition(PathExpressionFn left, Pat }; } - private static bool Return(IPathExpressionContext context, object input, out IEnumerable value) + private static bool Return(IPathExpressionContext context, object input, out IEnumerable value, out bool enumerable) { // Unwrap primitive types if (input is JValue jValue && (jValue.Type == JTokenType.String || jValue.Type == JTokenType.Integer || jValue.Type == JTokenType.Boolean)) input = jValue.Value; - value = new object[] { input }; + enumerable = false; + if (input is object[] eo) + { + enumerable = true; + value = eo; + } + else + value = new object[] { input }; + return true; } private static PathExpressionFn Literal(object arg) { - var result = new object[] { arg }; - return (IPathExpressionContext context, object input, out IEnumerable value) => + var isEnumerable = arg is object[]; + var result = isEnumerable ? arg as object[] : new object[] { arg }; + return (IPathExpressionContext context, object input, out IEnumerable value, out bool enumerable) => { value = result; + enumerable = isEnumerable; return true; }; } diff --git a/src/PSRule/Runtime/RunspaceContext.cs b/src/PSRule/Runtime/RunspaceContext.cs index 31b5c2046e..f2a0a11508 100644 --- a/src/PSRule/Runtime/RunspaceContext.cs +++ b/src/PSRule/Runtime/RunspaceContext.cs @@ -70,7 +70,7 @@ internal sealed class RunspaceContext : IDisposable // Fields exposed to engine internal RuleRecord RuleRecord; internal RuleBlock RuleBlock; - internal ITargetBindingContext Binding; + internal ITargetBindingResult Binding; private readonly bool _InconclusiveWarning; private readonly bool _NotProcessedWarning; @@ -118,7 +118,7 @@ internal RunspaceContext(PipelineContext pipeline, IPipelineWriter writer) _RuleTimer = new Stopwatch(); _Reason = new List(); _Conventions = new List(); - _LanguageScopes = new LanguageScopeSet(); + _LanguageScopes = new LanguageScopeSet(this); _Scope = new Stack(); } @@ -643,7 +643,7 @@ public bool TrySelector(ResourceId id) /// public RuleRecord EnterRuleBlock(RuleBlock ruleBlock) { - Binding = TargetBinder.Using(ruleBlock.Info.ModuleName); + Binding = TargetBinder.Result(ruleBlock.Info.ModuleName); _RuleErrors = 0; RuleBlock = ruleBlock; @@ -791,10 +791,14 @@ public void Begin() Pipeline.BindField, Pipeline.Option.Input.TargetType); + HashSet _TypeFilter = null; + if (Pipeline.Option.Input.TargetType != null && Pipeline.Option.Input.TargetType.Length > 0) + _TypeFilter = new HashSet(Pipeline.Option.Input.TargetType, StringComparer.OrdinalIgnoreCase); + foreach (var languageScope in _LanguageScopes.Get()) { var targetBinding = Pipeline.Baseline.GetTargetBinding(); - builder.With(new TargetBinder.TargetBindingContext(languageScope.Name, targetBinding)); + builder.With(new TargetBinder.TargetBindingContext(languageScope.Name, targetBinding, Pipeline.BindTargetName, Pipeline.BindTargetType, Pipeline.BindField, _TypeFilter)); } TargetBinder = builder.Build(); RunConventionInitialize(); diff --git a/tests/PSRule.Tests/FromFileSubSelector.Rule.jsonc b/tests/PSRule.Tests/FromFileSubSelector.Rule.jsonc new file mode 100644 index 0000000000..f831aa0394 --- /dev/null +++ b/tests/PSRule.Tests/FromFileSubSelector.Rule.jsonc @@ -0,0 +1,66 @@ +[ + { + // Synopsis: A rule with sub-selector pre-condition. + "apiVersion": "github.com/microsoft/PSRule/v1", + "kind": "Rule", + "metadata": { + "name": "JsonRuleWithPrecondition" + }, + "spec": { + "where": { + "field": "kind", + "equals": "test" + }, + "condition": { + "field": "resources", + "count": 2 + } + } + }, + { + // Synopsis: A rule with sub-selector filter. + "apiVersion": "github.com/microsoft/PSRule/v1", + "kind": "Rule", + "metadata": { + "name": "JsonRuleWithSubselector" + }, + "spec": { + "condition": { + "field": "resources", + "where": { + "field": ".", + "isString": true + }, + "allOf": [ + { + "field": ".", + "equals": "abc" + } + ] + } + } + }, + { + // Synopsis: A rule with sub-selector filter. + "apiVersion": "github.com/microsoft/PSRule/v1", + "kind": "Rule", + "metadata": { + "name": "JsonRuleWithSubselectorReordered" + }, + "spec": { + "condition": { + "allOf": [ + { + "field": ".", + "equals": "abc" + } + ], + "field": "resources", + "where": { + "field": ".", + "equals": "abc" + } + } + } + } +] diff --git a/tests/PSRule.Tests/FromFileSubSelector.Rule.yaml b/tests/PSRule.Tests/FromFileSubSelector.Rule.yaml new file mode 100644 index 0000000000..9ff8d2b065 --- /dev/null +++ b/tests/PSRule.Tests/FromFileSubSelector.Rule.yaml @@ -0,0 +1,52 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# +# YAML-based rules for unit testing +# + +--- +# Synopsis: A rule with sub-selector pre-condition. +apiVersion: github.com/microsoft/PSRule/v1 +kind: Rule +metadata: + name: YamlRuleWithPrecondition +spec: + where: + field: 'kind' + equals: 'test' + condition: + field: resources + count: 2 + +--- +# Synopsis: A rule with sub-selector filter. +apiVersion: github.com/microsoft/PSRule/v1 +kind: Rule +metadata: + name: YamlRuleWithSubselector +spec: + condition: + field: resources + where: + field: '.' + isString: true + allOf: + - field: '.' + equals: abc + +--- +# Synopsis: A rule with sub-selector filter. +apiVersion: github.com/microsoft/PSRule/v1 +kind: Rule +metadata: + name: YamlRuleWithSubselectorReordered +spec: + condition: + allOf: + - field: '.' + equals: abc + field: resources + where: + field: '.' + equals: 'abc' diff --git a/tests/PSRule.Tests/FunctionBuilderTests.cs b/tests/PSRule.Tests/FunctionBuilderTests.cs index 963551a2c1..b2e4646a55 100644 --- a/tests/PSRule.Tests/FunctionBuilderTests.cs +++ b/tests/PSRule.Tests/FunctionBuilderTests.cs @@ -52,7 +52,7 @@ private static SelectorVisitor GetSelectorVisitor(string name, Source[] source, context.Init(source); context.Begin(); var selector = HostHelper.GetSelector(source, context).ToArray().FirstOrDefault(s => s.Name == name); - return new SelectorVisitor(selector.Id, selector.Source, selector.Spec.If); + return new SelectorVisitor(context, selector.Id, selector.Source, selector.Spec.If); } private static string GetSourcePath(string fileName) diff --git a/tests/PSRule.Tests/FunctionTests.cs b/tests/PSRule.Tests/FunctionTests.cs index 8dedd43b18..87772ce402 100644 --- a/tests/PSRule.Tests/FunctionTests.cs +++ b/tests/PSRule.Tests/FunctionTests.cs @@ -264,7 +264,7 @@ private static ExpressionContext GetContext() targetObject.Properties.Add(new PSNoteProperty("name", "TestObject1")); var context = new Runtime.RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, new OptionContextBuilder(GetOption(), null, null, null).Build(), null), null); var s = GetSource(); - var result = new ExpressionContext(s[0].File[0], Definitions.ResourceKind.Rule, targetObject); + var result = new ExpressionContext(context, s[0].File[0], Definitions.ResourceKind.Rule, targetObject); context.Init(s); context.Begin(); context.PushScope(Runtime.RunspaceScope.Precondition); diff --git a/tests/PSRule.Tests/PSRule.Tests.csproj b/tests/PSRule.Tests/PSRule.Tests.csproj index 3feade5467..d0bd6c0e44 100644 --- a/tests/PSRule.Tests/PSRule.Tests.csproj +++ b/tests/PSRule.Tests/PSRule.Tests.csproj @@ -70,6 +70,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/tests/PSRule.Tests/RulesTests.cs b/tests/PSRule.Tests/RulesTests.cs index ce11715c9d..abe32802c7 100644 --- a/tests/PSRule.Tests/RulesTests.cs +++ b/tests/PSRule.Tests/RulesTests.cs @@ -20,6 +20,9 @@ public sealed class RulesTests { #region Yaml rules + /// + /// Test that a YAML-based rule can be parsed. + /// [Fact] public void ReadYamlRule() { @@ -44,6 +47,61 @@ public void ReadYamlRule() Assert.Equal("tag", hashtable["feature"]); } + /// + /// Test that a YAML-based rule with sub-selectors can be parsed. + /// + [Fact] + public void ReadYamlSubSelectorRule() + { + var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, new OptionContext(), null), new TestWriter(GetOption())); + context.Init(GetSource("FromFileSubSelector.Rule.yaml")); + context.Begin(); + + // From current path + var rule = HostHelper.GetRule(GetSource("FromFileSubSelector.Rule.yaml"), context, includeDependencies: false); + Assert.NotNull(rule); + Assert.Equal("YamlRuleWithPrecondition", rule[0].Name); + Assert.Equal("YamlRuleWithSubselector", rule[1].Name); + Assert.Equal("YamlRuleWithSubselectorReordered", rule[2].Name); + + context.Init(GetSource("FromFileSubSelector.Rule.yaml")); + context.Begin(); + var subselector1 = GetRuleVisitor(context, "YamlRuleWithPrecondition", GetSource("FromFileSubSelector.Rule.yaml")); + var subselector2 = GetRuleVisitor(context, "YamlRuleWithSubselector", GetSource("FromFileSubSelector.Rule.yaml")); + var subselector3 = GetRuleVisitor(context, "YamlRuleWithSubselectorReordered", GetSource("FromFileSubSelector.Rule.yaml")); + context.EnterSourceScope(subselector1.Source); + + var actual1 = GetObject((name: "kind", value: "test"), (name: "resources", value: new string[] { "abc", "abc" })); + var actual2 = GetObject((name: "resources", value: new string[] { "abc", "123", "abc" })); + + // YamlRuleWithPrecondition + context.EnterTargetObject(actual1); + context.EnterRuleBlock(subselector1); + Assert.True(subselector1.Condition.If().AllOf()); + + context.EnterTargetObject(actual2); + context.EnterRuleBlock(subselector1); + Assert.True(subselector1.Condition.If().Skipped()); + + // YamlRuleWithSubselector + context.EnterTargetObject(actual1); + context.EnterRuleBlock(subselector2); + Assert.True(subselector2.Condition.If().AllOf()); + + context.EnterTargetObject(actual2); + context.EnterRuleBlock(subselector2); + Assert.False(subselector2.Condition.If().AllOf()); + + // YamlRuleWithSubselectorReordered + context.EnterTargetObject(actual1); + context.EnterRuleBlock(subselector3); + Assert.True(subselector3.Condition.If().AllOf()); + + context.EnterTargetObject(actual2); + context.EnterRuleBlock(subselector3); + Assert.True(subselector3.Condition.If().AllOf()); + } + [Fact] public void EvaluateYamlRule() { @@ -136,6 +194,9 @@ public void RuleWithObjectPath() #region Json rules + /// + /// Test that a JSON-based rule can be parsed. + /// [Fact] public void ReadJsonRule() { @@ -160,6 +221,61 @@ public void ReadJsonRule() Assert.Equal("tag", hashtable["feature"]); } + /// + /// Test that a JSON-based rule with sub-selectors can be parsed. + /// + [Fact] + public void ReadJsonSubSelectorRule() + { + var context = new RunspaceContext(PipelineContext.New(GetOption(), null, null, PipelineHookActions.BindTargetName, PipelineHookActions.BindTargetType, PipelineHookActions.BindField, new OptionContext(), null), new TestWriter(GetOption())); + context.Init(GetSource("FromFileSubSelector.Rule.jsonc")); + context.Begin(); + + // From current path + var rule = HostHelper.GetRule(GetSource("FromFileSubSelector.Rule.jsonc"), context, includeDependencies: false); + Assert.NotNull(rule); + Assert.Equal("JsonRuleWithPrecondition", rule[0].Name); + Assert.Equal("JsonRuleWithSubselector", rule[1].Name); + Assert.Equal("JsonRuleWithSubselectorReordered", rule[2].Name); + + context.Init(GetSource("FromFileSubSelector.Rule.yaml")); + context.Begin(); + var subselector1 = GetRuleVisitor(context, "JsonRuleWithPrecondition", GetSource("FromFileSubSelector.Rule.jsonc")); + var subselector2 = GetRuleVisitor(context, "JsonRuleWithSubselector", GetSource("FromFileSubSelector.Rule.jsonc")); + var subselector3 = GetRuleVisitor(context, "JsonRuleWithSubselectorReordered", GetSource("FromFileSubSelector.Rule.jsonc")); + context.EnterSourceScope(subselector1.Source); + + var actual1 = GetObject((name: "kind", value: "test"), (name: "resources", value: new string[] { "abc", "abc" })); + var actual2 = GetObject((name: "resources", value: new string[] { "abc", "123", "abc" })); + + // JsonRuleWithPrecondition + context.EnterTargetObject(actual1); + context.EnterRuleBlock(subselector1); + Assert.True(subselector1.Condition.If().AllOf()); + + context.EnterTargetObject(actual2); + context.EnterRuleBlock(subselector1); + Assert.True(subselector1.Condition.If().Skipped()); + + // JsonRuleWithSubselector + context.EnterTargetObject(actual1); + context.EnterRuleBlock(subselector2); + Assert.True(subselector2.Condition.If().AllOf()); + + context.EnterTargetObject(actual2); + context.EnterRuleBlock(subselector2); + Assert.False(subselector2.Condition.If().AllOf()); + + // JsonRuleWithSubselectorReordered + context.EnterTargetObject(actual1); + context.EnterRuleBlock(subselector3); + Assert.True(subselector3.Condition.If().AllOf()); + + context.EnterTargetObject(actual2); + context.EnterRuleBlock(subselector3); + Assert.True(subselector3.Condition.If().AllOf()); + } + #endregion Json rules #region Helper methods @@ -190,17 +306,17 @@ private static object[] GetObject(string path) return JsonConvert.DeserializeObject(File.ReadAllText(path)); } - private static RuleBlock GetRuleVisitor(RunspaceContext context, string name) + private static RuleBlock GetRuleVisitor(RunspaceContext context, string name, Source[] source = null) { - var block = HostHelper.GetRuleYamlBlocks(GetSource(), context); + var block = HostHelper.GetRuleBlockGraph(source ?? GetSource(), context).GetAll(); return block.FirstOrDefault(s => s.Name == name); } - private static void ImportSelectors(RunspaceContext context) + private static void ImportSelectors(RunspaceContext context, Source[] source = null) { - var selectors = HostHelper.GetSelector(GetSource(), context).ToArray(); + var selectors = HostHelper.GetSelector(source ?? GetSource(), context).ToArray(); foreach (var selector in selectors) - context.Pipeline.Import(selector); + context.Pipeline.Import(context, selector); } private static string GetSourcePath(string path) diff --git a/tests/PSRule.Tests/SelectorTests.cs b/tests/PSRule.Tests/SelectorTests.cs index 1f1c087178..145c5090d1 100644 --- a/tests/PSRule.Tests/SelectorTests.cs +++ b/tests/PSRule.Tests/SelectorTests.cs @@ -1712,7 +1712,7 @@ private static SelectorVisitor GetSelectorVisitor(string name, Source[] source, context.Init(source); context.Begin(); var selector = HostHelper.GetSelector(source, context).ToArray().FirstOrDefault(s => s.Name == name); - return new SelectorVisitor(selector.Id, selector.Source, selector.Spec.If); + return new SelectorVisitor(context, selector.Id, selector.Source, selector.Spec.If); } private static string GetSourcePath(string fileName) diff --git a/tests/PSRule.Tests/TargetBinderTests.cs b/tests/PSRule.Tests/TargetBinderTests.cs index adaeac92c6..9ea3867ec1 100644 --- a/tests/PSRule.Tests/TargetBinderTests.cs +++ b/tests/PSRule.Tests/TargetBinderTests.cs @@ -7,7 +7,6 @@ using PSRule.Definitions.Baselines; using PSRule.Pipeline; using Xunit; -using static PSRule.Pipeline.TargetBinder; namespace PSRule { @@ -20,15 +19,15 @@ public void BindTargetObject() var targetObject = GetTargetObject(); binder.Bind(targetObject); - var m1 = binder.Using("Module1"); + var m1 = binder.Result("Module1"); Assert.Equal("Name1", m1.TargetName); Assert.Equal("Type1", m1.TargetType); - var m2 = binder.Using("Module2"); + var m2 = binder.Result("Module2"); Assert.Equal("Name2", m2.TargetName); Assert.Equal("Type1", m2.TargetType); - var m0 = binder.Using("."); + var m0 = binder.Result("."); Assert.Equal("Name1", m0.TargetName); Assert.Equal("System.Management.Automation.PSCustomObject", m0.TargetType); } @@ -40,15 +39,15 @@ public void BindJObject() var targetObject = new TargetObject(PSObject.AsPSObject(JToken.Parse("{ \"name\": \"Name1\", \"type\": \"Type1\", \"AlternativeName\": \"Name2\", \"AlternativeType\": \"Type2\" }"))); binder.Bind(targetObject); - var m1 = binder.Using("Module1"); + var m1 = binder.Result("Module1"); Assert.Equal("Name1", m1.TargetName); Assert.Equal("Type1", m1.TargetType); - var m2 = binder.Using("Module2"); + var m2 = binder.Result("Module2"); Assert.Equal("Name2", m2.TargetName); Assert.Equal("Type1", m2.TargetType); - var m0 = binder.Using("."); + var m0 = binder.Result("."); Assert.Equal("Name1", m0.TargetName); Assert.Equal("System.Management.Automation.PSCustomObject", m0.TargetType); } @@ -93,13 +92,13 @@ private ITargetBinder GetBinder() )); option.UseScope("Module1"); - builder.With(new TargetBindingContext("Module1", option.GetTargetBinding())); + builder.With("Module1", option.GetTargetBinding()); option.UseScope("Module2"); - builder.With(new TargetBindingContext("Module2", option.GetTargetBinding())); + builder.With("Module2", option.GetTargetBinding()); option.UseScope(null); - builder.With(new TargetBindingContext(".", option.GetTargetBinding())); + builder.With(".", option.GetTargetBinding()); return builder.Build(); }