Repository: opentofu/opentofu
File: internal/backend/remote-state/s3/client.go (Go)

Code before:
		i.SSEKMSKeyId = &c.kmsKeyID
		i.ServerSideEncryption = types.ServerSideEncryptionAwsKms
	} else if c.customerEncryptionKey != nil {
		i.SSECustomerKey = aws.String(base64.StdEncoding.EncodeToString(c.customerEncryptionKey))
		i.SSECustomerAlgorithm = aws.String(string(s3EncryptionAlgorithm))
		i.SSECustomerKeyMD5 = aws.String(c.getSSECustomerKeyMD5())
	} else {
		i.ServerSideEncryption = s3EncryptionAlgorithm
	}
}

func (c *RemoteClient) configurePutObjectACL(i *s3.PutObjectInput) {
	if c.acl == "" {
		return
	}
	i.ACL = types.ObjectCannedACL(c.acl)
}

func (c *RemoteClient) configurePutObjectTags(i *s3.PutObjectInput) {
	if len(c.tags) == 0 {
		return
	}
	tagPairsSlice := make([]string, len(c.tags))
	index := 0
	for k, v := range c.tags {
		tagPairsSlice[index] = fmt.Sprintf("%s=%s", url.QueryEscape(k), url.QueryEscape(v))
		index++
	}
	tagPairs := strings.Join(tagPairsSlice, "&")
	i.Tagging = aws.String(tagPairs)
}

const errBadChecksumFmt = `state data in S3 does not have the expected content.

This may be caused by unusually long delays in S3 processing a previous state
update.  Please wait for a minute or two and try again. If this problem
persists, and neither S3 nor DynamoDB are experiencing an outage, you may need
to manually verify the remote state and update the Digest value stored in the
DynamoDB table to the following value: %x
`

const errS3NoSuchBucket = `S3 bucket does not exist.

The referenced S3 bucket must have been previously created. If the S3 bucket
was created within the last minute, please wait for a minute or two and try
again.

Error: %w
`


Review comment (suggestion):
You could try using `url.Values` since the [`Encode`](https://pkg.go.dev/net/url#Values.Encode) method on that do already handle the escaping and it builds the content already as a URL query string.

Code after:
		i.SSEKMSKeyId = &c.kmsKeyID
		i.ServerSideEncryption = types.ServerSideEncryptionAwsKms
	} else if c.customerEncryptionKey != nil {
		i.SSECustomerKey = aws.String(base64.StdEncoding.EncodeToString(c.customerEncryptionKey))
		i.SSECustomerAlgorithm = aws.String(string(s3EncryptionAlgorithm))
		i.SSECustomerKeyMD5 = aws.String(c.getSSECustomerKeyMD5())
	} else {
		i.ServerSideEncryption = s3EncryptionAlgorithm
	}
}

func (c *RemoteClient) configurePutObjectACL(i *s3.PutObjectInput) {
	if c.acl == "" {
		return
	}
	i.ACL = types.ObjectCannedACL(c.acl)
}

func (c *RemoteClient) configurePutObjectTags(i *s3.PutObjectInput, tags map[string]string) {
	if len(tags) == 0 {
		return
	}
	headers := url.Values{}
	for k, v := range tags {
		headers.Add(k, v)
	}
	i.Tagging = aws.String(headers.Encode())
}

const errBadChecksumFmt = `state data in S3 does not have the expected content.

This may be caused by unusually long delays in S3 processing a previous state
update.  Please wait for a minute or two and try again. If this problem
persists, and neither S3 nor DynamoDB are experiencing an outage, you may need
to manually verify the remote state and update the Digest value stored in the
DynamoDB table to the following value: %x
`

const errS3NoSuchBucket = `S3 bucket does not exist.

The referenced S3 bucket must have been previously created. If the S3 bucket
was created within the last minute, please wait for a minute or two and try
again.

Error: %w
`