Repository: external-secrets/external-secrets (go)

Issue:
ESO keeps pushing the same parameter for every sync when it's type is `SecureString`
**Describe the bug**
When pushing an encrypted parameter (through KMS) to `AWS Parameter Store`, ESO currently creates different versions from the same parameter on each sync interval. This happens because ESO compares a remote encrypted parameter value with a non-encrypted one, resulting in a new push.

**To Reproduce**
Steps to reproduce the behavior:
1. Create a `PushSecret` to push a parameter with `SecureString` parameter type.
3. Watch the new version of the same secret to be created in every ESO iteration

**To Fix**
Guarantee that [ssm.GetParameterInput](https://github.com/external-secrets/external-secrets/blob/main/pkg/provider/aws/parameterstore/parameterstore.go#L195) is setting `WithDecryption`. Example:

```
secretValue := ssm.GetParameterInput{
  Name:           &secretName,
  WithDecryption: aws.Bool(true),
}
```

**Expected behavior**
I expect ESO to update the remote secret only when the secret value is different from the remote one. 

**Screenshots**
<img width="1552" alt="bug_evidence" src="https://github.com/user-attachments/assets/6bd17986-09a1-49fd-a936-2b32fcb1f477">

**Additional context**
This bug was introduced after this [PR](https://github.com/external-secrets/external-secrets/pull/3576) added support to push parameters with `SecureString` values.


Pull request:
fix: decrypt remote parameter for SecureString type
## Problem Statement

This PR fixes a `PushSecret` being updated remotely in every ESO cycle in case of a parameter with type `SecureString`.

## Related Issue

Fixes #3697

## Proposed Changes

Ensure that `WithDecryption` is enabled when fetching a remote parameter value for type `SecureString`.

## Checklist

- [X] I have read the [contribution guidelines](https://external-secrets.io/latest/contributing/process/#submitting-a-pull-request)
- [X] All commits are signed with `git commit --signoff`
- [X] My changes have reasonable test coverage
- [X] All tests pass with `make test`
- [X] I ensured my PR is ready for review with `make reviewable`


Patch:
diff --git a/pkg/provider/aws/parameterstore/parameterstore.go b/pkg/provider/aws/parameterstore/parameterstore.go
index c9307a02c..bda54c3ac 100644
--- a/pkg/provider/aws/parameterstore/parameterstore.go
+++ b/pkg/provider/aws/parameterstore/parameterstore.go
@@ -195,7 +195,8 @@ func (pm *ParameterStore) PushSecret(ctx context.Context, secret *corev1.Secret,
 	}
 
 	secretValue := ssm.GetParameterInput{
-		Name: &secretName,
+		Name:           &secretName,
+		WithDecryption: aws.Bool(true),
 	}
 
 	existing, err := pm.client.GetParameterWithContext(ctx, &secretValue)
@@ -219,6 +220,12 @@ func (pm *ParameterStore) PushSecret(ctx context.Context, secret *corev1.Secret,
 			return fmt.Errorf("secret not managed by external-secrets")
 		}
 
+		// When fetching a remote SecureString parameter without decrypting, the default value will always be 'sensitive'
+		// in this case, no updates will be pushed remotely
+		if existing.Parameter.Value != nil && *existing.Parameter.Value == "sensitive" {
+			return fmt.Errorf("unable to compare 'sensitive' result, ensure to request a decrypted value")
+		}
+
 		if existing.Parameter.Value != nil && *existing.Parameter.Value == string(value) {
 			return nil
 		}


Test patch:
diff --git a/pkg/provider/aws/parameterstore/parameterstore_test.go b/pkg/provider/aws/parameterstore/parameterstore_test.go
index 2e1ccfdf0..7da9356b2 100644
--- a/pkg/provider/aws/parameterstore/parameterstore_test.go
+++ b/pkg/provider/aws/parameterstore/parameterstore_test.go
@@ -495,6 +495,34 @@ func TestPushSecret(t *testing.T) {
 				err: fmt.Errorf("failed to parse metadata: failed to parse JSON raw data: invalid character 'f' looking for beginning of object key string"),
 			},
 		},
+		"GetRemoteSecretWithoutDecryption": {
+			reason: "test if push secret's get remote source is encrypted for valid comparison",
+			args: args{
+				store: makeValidParameterStore().Spec.Provider.AWS,
+				metadata: &apiextensionsv1.JSON{
+					Raw: []byte(`
+					{
+						"parameterStoreType": "SecureString",
+						"parameterStoreKeyID": "arn:aws:kms:sa-east-1:00000000000:key/bb123123-b2b0-4f60-ac3a-44a13f0e6b6c"
+					}
+					`),
+				},
+				client: fakeps.Client{
+					PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
+					GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(&ssm.GetParameterOutput{
+						Parameter: &ssm.Parameter{
+							Type:  aws.String("SecureString"),
+							Value: aws.String("sensitive"),
+						},
+					}, nil),
+					DescribeParametersWithContextFn:  fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
+					ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
+				},
+			},
+			want: want{
+				err: fmt.Errorf("unable to compare 'sensitive' result, ensure to request a decrypted value"),
+			},
+		},
 	}
 
 	for name, tc := range tests {
@@ -625,6 +653,7 @@ func TestGetSecret(t *testing.T) {
 		pstc.expectedSecret = "bang"
 		pstc.remoteRef.Property = "/shmoo"
 	}
+
 	// good case: extract property with `.`
 	setExtractPropertyWithDot := func(pstc *parameterstoreTestCase) {
 		pstc.apiOutput.Parameter.Value = aws.String(`{"/shmoo.boom": "bang"}`)