Repository: aws-cloudformation/cfn-lint

Issue:
AWS::Events::Rule should allow both ['EventPattern', 'ScheduleExpression'] and should not throw an E3014
### CloudFormation Lint Version

1.12.4

### What operating system are you using?

ubnutu

### Describe the bug

a resource definition for AWS::Events::Rule which contains both ['EventPattern', 'ScheduleExpression'] deploys just fine and without issue in AWS and therefore, an E3014 should not be thrown by cfn-lint. 

This E3014 exception was introduced via https://github.com/aws-cloudformation/cfn-lint/blob/7b0ad9795df5f9a49a585b6c2368a476c286c217/src/cfnlint/data/schemas/patches/extensions/all/aws_events_rule/manual.json

### Expected behavior

do not throw an E3014 if both ['EventPattern', 'ScheduleExpression'] are present in AWS::Events::Rule

### Reproduction template

UpdateRule:
    Type: AWS::Events::Rule
    Properties: 
      Description: 'Rule to trigger subscription filter manager lambda on SSM parameter update events'
      EventPattern: 
        source: ["aws.ssm"]
        detail-type: ["Parameter Store Change"]
        detail:
          name: 
            - "include"
            - "exclude"
            - "disabled"
          operation:
            - "Create"
            - "Update"
            - "Delete"
        account: [!Sub "${AWS::AccountId}"]
      ScheduleExpression: rate(5 minutes)
      Targets: 
        - Arn: ARN1
          Id: LAMBDA1


Discussion:
Thanks! That was speedy!



Patch:
diff --git a/docs/cfn-schema-specification.md b/docs/cfn-schema-specification.md
index 57f550d3d9..5d991f7fbe 100644
--- a/docs/cfn-schema-specification.md
+++ b/docs/cfn-schema-specification.md
@@ -83,9 +83,9 @@ _properties_ provides the key names and a value that represents the schema to va
 
 _required_ defines a list of required properties. [JSON Schema docs](https://json-schema.org/understanding-json-schema/reference/object#required)
 
-##### requiredXor
+##### requiredOr
 
-_requiredXor_ is used to define when only one property from a set properties is required.
+_requiredOr_ is used to define when at least one property from a set properties is required.
 
 On the following defined object
 
@@ -104,7 +104,7 @@ The cfn-lint schema
 
 ```json
 {
-  "requiredXor": ["a", "b", "c"]
+  "requiredOr": ["a", "b", "c"]
 }
 ```
 
@@ -112,7 +112,7 @@ is equivalent to the JSON schema
 
 ```json
 {
-  "oneOf": [
+  "anyOf": [
     {
       "required": ["a"]
     },
@@ -126,9 +126,9 @@ is equivalent to the JSON schema
 }
 ```
 
-##### propertiesNand
+##### requiredXor
 
-_propertiesNand_ is used to define when none or only one property from a set properties can be defined.
+_requiredXor_ is used to define when only one property from a set properties is required.
 
 On the following defined object
 
@@ -147,7 +147,7 @@ The cfn-lint schema
 
 ```json
 {
-  "propertiesNand": ["a", "b", "c"]
+  "requiredXor": ["a", "b", "c"]
 }
 ```
 
@@ -164,13 +164,6 @@ is equivalent to the JSON schema
     },
     {
       "required": ["c"]
-    },
-    {
-      "properties": {
-        "a": false,
-        "b": false,
-        "c": false
-      }
     }
   ]
 }
diff --git a/scripts/update_schemas_manually.py b/scripts/update_schemas_manually.py
index 2a95f29bac..daa5b49ce3 100755
--- a/scripts/update_schemas_manually.py
+++ b/scripts/update_schemas_manually.py
@@ -965,7 +965,7 @@
             resource_type="AWS::Events::Rule",
             patches=[
                 Patch(
-                    values={"requiredXor": ["EventPattern", "ScheduleExpression"]},
+                    values={"requiredOr": ["EventPattern", "ScheduleExpression"]},
                     path="/",
                 ),
                 Patch(
diff --git a/src/cfnlint/data/schemas/patches/extensions/all/aws_events_rule/manual.json b/src/cfnlint/data/schemas/patches/extensions/all/aws_events_rule/manual.json
index 8f262d3f79..ac3937a76f 100644
--- a/src/cfnlint/data/schemas/patches/extensions/all/aws_events_rule/manual.json
+++ b/src/cfnlint/data/schemas/patches/extensions/all/aws_events_rule/manual.json
@@ -1,7 +1,7 @@
 [
  {
   "op": "add",
-  "path": "/requiredXor",
+  "path": "/requiredOr",
   "value": [
    "EventPattern",
    "ScheduleExpression"
diff --git a/src/cfnlint/data/schemas/providers/us_east_1/aws-events-rule.json b/src/cfnlint/data/schemas/providers/us_east_1/aws-events-rule.json
index 1cfff71df2..20da1af917 100644
--- a/src/cfnlint/data/schemas/providers/us_east_1/aws-events-rule.json
+++ b/src/cfnlint/data/schemas/providers/us_east_1/aws-events-rule.json
@@ -535,7 +535,7 @@
  "readOnlyProperties": [
   "/properties/Arn"
  ],
- "requiredXor": [
+ "requiredOr": [
   "EventPattern",
   "ScheduleExpression"
  ],
diff --git a/src/cfnlint/data/schemas/providers/us_gov_east_1/aws-events-rule.json b/src/cfnlint/data/schemas/providers/us_gov_east_1/aws-events-rule.json
index c3561eff24..85e51ef330 100644
--- a/src/cfnlint/data/schemas/providers/us_gov_east_1/aws-events-rule.json
+++ b/src/cfnlint/data/schemas/providers/us_gov_east_1/aws-events-rule.json
@@ -496,7 +496,7 @@
   "/properties/Id",
   "/properties/Arn"
  ],
- "requiredXor": [
+ "requiredOr": [
   "EventPattern",
   "ScheduleExpression"
  ],
diff --git a/src/cfnlint/data/schemas/providers/us_gov_west_1/aws-events-rule.json b/src/cfnlint/data/schemas/providers/us_gov_west_1/aws-events-rule.json
index c3561eff24..85e51ef330 100644
--- a/src/cfnlint/data/schemas/providers/us_gov_west_1/aws-events-rule.json
+++ b/src/cfnlint/data/schemas/providers/us_gov_west_1/aws-events-rule.json
@@ -496,7 +496,7 @@
   "/properties/Id",
   "/properties/Arn"
  ],
- "requiredXor": [
+ "requiredOr": [
   "EventPattern",
   "ScheduleExpression"
  ],
diff --git a/src/cfnlint/jsonschema/_filter.py b/src/cfnlint/jsonschema/_filter.py
index 32855a8b56..5fa077897e 100644
--- a/src/cfnlint/jsonschema/_filter.py
+++ b/src/cfnlint/jsonschema/_filter.py
@@ -45,7 +45,7 @@ class FunctionFilter:
             "minItems",
             "minProperties",
             "required",
-            "requiredAtLeastOne",
+            "requiredOr",
             "requiredXor",
             "then",
             "uniqueItems",
diff --git a/src/cfnlint/jsonschema/_keywords.py b/src/cfnlint/jsonschema/_keywords.py
index c33dde4935..635057165b 100644
--- a/src/cfnlint/jsonschema/_keywords.py
+++ b/src/cfnlint/jsonschema/_keywords.py
@@ -541,6 +541,17 @@ def required(
             yield ValidationError(f"{property!r} is a required property")
 
 
+def requiredOr(
+    validator: Validator, required: Any, instance: Any, schema: dict[str, Any]
+) -> ValidationResult:
+    if not validator.is_type(instance, "object"):
+        return
+    matches = set(required).intersection(instance.keys())
+    if not matches:
+        yield ValidationError(f"One of {required!r} is a required property")
+        return
+
+
 def requiredXor(
     validator: Validator, required: Any, instance: Any, schema: dict[str, Any]
 ) -> ValidationResult:
diff --git a/src/cfnlint/jsonschema/validators.py b/src/cfnlint/jsonschema/validators.py
index ca6e16eefc..3e42afbd94 100644
--- a/src/cfnlint/jsonschema/validators.py
+++ b/src/cfnlint/jsonschema/validators.py
@@ -396,6 +396,7 @@ def extend(
     "properties": _keywords.properties,
     "propertyNames": _keywords.propertyNames,
     "required": _keywords.required,
+    "requiredOr": _keywords.requiredOr,
     "requiredXor": _keywords.requiredXor,
     "type": _keywords.type,
     "uniqueItems": _keywords.uniqueItems,
diff --git a/src/cfnlint/rules/resources/properties/Properties.py b/src/cfnlint/rules/resources/properties/Properties.py
index 94c91bcd09..90029b7758 100644
--- a/src/cfnlint/rules/resources/properties/Properties.py
+++ b/src/cfnlint/rules/resources/properties/Properties.py
@@ -37,6 +37,7 @@ def __init__(self):
             "dependentExcluded": "E3020",
             "dependentRequired": "E3021",
             "required": "E3003",
+            "requiredOr": "E3058",
             "requiredXor": "E3014",
             "enum": "E3030",
             "type": "E3012",
diff --git a/src/cfnlint/rules/resources/properties/RequiredOr.py b/src/cfnlint/rules/resources/properties/RequiredOr.py
new file mode 100644
index 0000000000..67724d4e83
--- /dev/null
+++ b/src/cfnlint/rules/resources/properties/RequiredOr.py
@@ -0,0 +1,14 @@
+"""
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+SPDX-License-Identifier: MIT-0
+"""
+
+from cfnlint.rules import CloudFormationLintRule
+
+
+class RequiredOr(CloudFormationLintRule):
+    id = "E3058"
+    shortdesc = "Validate at least one of the properties are required"
+    description = "Make sure at least one of the resource properties are included"
+    source_url = "https://github.com/aws-cloudformation/cfn-lint/blob/main/docs/cfn-schema-specification.md#requiredor"
+    tags = ["resources"]


Test patch:
diff --git a/test/unit/module/jsonschema/test_validator.py b/test/unit/module/jsonschema/test_validator.py
index f981aadd58..863195472d 100644
--- a/test/unit/module/jsonschema/test_validator.py
+++ b/test/unit/module/jsonschema/test_validator.py
@@ -947,6 +947,28 @@ def test_validator(name, schema, instance, expected, validator):
                 )
             ],
         ),
+        (
+            "valid requiredOr",
+            {"requiredOr": ["foo", "bar"]},
+            {"foo": {}},
+            [],
+        ),
+        (
+            "valid requiredOr with wrong type",
+            {"requiredOr": ["foo", "bar"]},
+            [],
+            [],
+        ),
+        (
+            "invalid requiredOr with empty object",
+            {"requiredOr": ["foo", "bar"]},
+            {},
+            [
+                ValidationError(
+                    "One of ['foo', 'bar'] is a required property",
+                )
+            ],
+        ),
         (
             "valid requiredXor",
             {"requiredXor": ["foo", "bar"]},