Repository: aws-cloudformation/cfn-lint

Issue:
W8003 False positive when parameters have default values
### CloudFormation Lint Version

1.22.3

### What operating system are you using?

Ubuntu

### Describe the bug

The linter errors with W8003 on the following example, since it thinks the value of the parameter is always the default value, when there are multiple possible options that would lead to true or false

### Expected behavior

W8003 is not raised

### Reproduction template

```yaml
Parameters:
  Environment:
    Description: "The name of the environment to deploy to."
    Type: "String"
    Default: local
    AllowedValues:
      - "local"
      - "staging"
      - "integration"
      - "production"
    DeploymentName:
    Type: String
    MaxLength: 22
    AllowedPattern: ^.*[^-]$
    Default: test
    Description: A unique prefix to identify the deployment; used to distinguish variation between ephemeral stacks

Rules:
  DeploymentNameRequired:
    RuleCondition: !Equals [ !Ref Environment, "local" ]
    Assertions:
      - Assert: !Not [ !Equals [ !Ref DeploymentName, "" ] ]
        AssertDescription: >
          Must specify DeploymentName parameter when Environment is "local"
```

Discussion:
@kddejong: I'm not able to replicate this with the provided template.  How are you using these parameters?  Also do you have any conditions?

We do some function replacement but we make those substitutions against allowed values so this is possible.  We would iterate through all of the allowed values providing you an error when it is "local".  That being said we shouldn't be doing this substitution in this scenario and when I run the template above we aren't doing that substitution. Its possible something else in your template is triggering the substitution and evaluation of this rule so I'm trying to figure out what I'm missing. @CarlyG55: Hi @kddejong, thanks for the quick response!
I can point you to the exact failure if that's helpful - I thought it would be helpful to simplify it for the example but maybe it has lost some context. It's a codebase I've recently started maintaining, and the warning has just starting appearing.

The failure can be found [here](https://github.com/govuk-one-login/onboarding-self-service-experience/actions/runs/12670806700/job/35311219675), although I don't think you can see the logs - the relevant output is
```
W8003 ['local', 'local'] will always return True or False
backend/api/api.template.yml:40:5

W8003 ['self-service', ''] will always return False
backend/api/api.template.yml:42:24

W8003 ['local', 'local'] will always return True or False
backend/api/api.template.yml:47:27

W8003 ['self-service', 'self-service'] will always return True or False
backend/api/api.template.yml:49:9

W8003 [True, True] will always return True or False
backend/api/api.template.yml:100:3

W8003 ['local', 'local'] will always return True or False
backend/api/api.template.yml:105:3
```

The file that is failing can be found [here](https://github.com/govuk-one-login/onboarding-self-service-experience/blob/main/backend/api/api.template.yml). Thank you!@kddejong: Thanks @CarlyG55.  I'll look into it today.  I was able to replicate the issue with that template@kddejong: A quick analysis has this something to do with the transforms.  This is the result of the PR to fix #3857.  Some people are relying on the language extensions to replace certain Ref values before sending to SAM as SAM can't always handle not having a value. 

I think the easy fix here is to only do Ref replacements when in the Resources section and not in Rules/Conditions.@CarlyG55: Thank you @kddejong!

Solution patch:
diff --git a/src/cfnlint/template/transforms/_sam.py b/src/cfnlint/template/transforms/_sam.py
index 5580e82db..9acd580a1 100644
--- a/src/cfnlint/template/transforms/_sam.py
+++ b/src/cfnlint/template/transforms/_sam.py
@@ -158,7 +158,9 @@ class Transform:
                 if isinstance(v, dict) and v.get("Default"):
                     parameters[k] = v.get("Default")
 
-            self._template = self._find_and_replace(self._template, parameters)
+            self._template["Resources"] = self._find_and_replace(
+                self._template.get("Resources", {}), parameters
+            )
 
     def transform_template(self):
         """
diff --git a/src/cfnlint/template/transforms/transform.py b/src/cfnlint/template/transforms/transform.py
index 62d1f9b76..5db49c76c 100644
--- a/src/cfnlint/template/transforms/transform.py
+++ b/src/cfnlint/template/transforms/transform.py
@@ -49,6 +49,11 @@ class Transform:
                 continue
 
             matches, template = transform(cfn)
+            LOGGER.debug(
+                "Transformed template from %s: \n%s",
+                name,
+                format_json_string(cfn.template),
+            )
             if matches:
                 return matches
             cfn.template = template


Test patch:
diff --git a/test/fixtures/templates/good/transform/language_extension.yaml b/test/fixtures/templates/good/transform/language_extension.yaml
index f6f19e704..1ff2b2406 100644
--- a/test/fixtures/templates/good/transform/language_extension.yaml
+++ b/test/fixtures/templates/good/transform/language_extension.yaml
@@ -13,12 +13,21 @@ Parameters:
   VpcId:
     Type: "AWS::SSM::Parameter::Value<AWS::EC2::VPC::Id>"
     Default: "/network/vpc/primary/id"
+  Environment:
+    Type: String
+    Default: Prod
 Mappings:
   StackIdMap01:
     teststack1:
       DeletionPolicy: Retain
     teststack2:
       DeletionPolicy: Delete
+Rules:
+  IsAutPublishAliasParameterProd:
+    RuleCondition: !Equals [ !Ref Environment, "Prod" ]
+    Assertions:
+      - Assert: !Not
+          - !Equals [ !Ref DBPolicy, "" ]
 Resources:
   WaitHandleRef:
     Type: AWS::CloudFormation::WaitConditionHandle
diff --git a/test/unit/module/transform/test_transform.py b/test/unit/module/transform/test_transform.py
index a291a58b4..e114e3568 100644
--- a/test/unit/module/transform/test_transform.py
+++ b/test/unit/module/transform/test_transform.py
@@ -117,6 +117,21 @@ class TestTransform(BaseTestCase):
         template = cfn_yaml.load(filename)
         transformed_template = Transform(filename, template, region)
         results = transformed_template.transform_template()
+        self.assertDictEqual(
+            transformed_template._template.get("Rules"),
+            {
+                "IsAutPublishAliasParameterProd": {
+                    "Assertions": [
+                        {
+                            "Assert": {
+                                "Fn::Not": [{"Fn::Equals": [{"Ref": "DBPolicy"}, ""]}]
+                            }
+                        }
+                    ],
+                    "RuleCondition": {"Fn::Equals": [{"Ref": "Environment"}, "Prod"]},
+                }
+            },
+        )
         self.assertEqual(results, [])
 
     def test_parameter_for_autopublish_code_sha256(self):