Skip to content

Expose plaintext template.data values in template rendering context#1940

Open
hamza-younas94 wants to merge 2 commits intobitnami-labs:mainfrom
hamza-younas94:fix-template-data-plaintext-reference
Open

Expose plaintext template.data values in template rendering context#1940
hamza-younas94 wants to merge 2 commits intobitnami-labs:mainfrom
hamza-younas94:fix-template-data-plaintext-reference

Conversation

@hamza-younas94
Copy link
Copy Markdown

@hamza-younas94 hamza-younas94 commented Apr 10, 2026

Summary

When a SealedSecret defines plaintext keys alongside encrypted keys inside spec.template.data, sibling templates in the same map could not reference those plaintext keys as {{ .key }} variables — the controller rendered <no value> instead of the configured plaintext value.

This PR fixes that by pre-populating the template execution context with the raw plaintext values from spec.template.data before rendering, so templates can reference them just like decrypted encryptedData keys.

Closes #1607

Reproducer (matches the issue)

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: mySecret
spec:
  encryptedData:
    password: <ciphertext>
  template:
    data:
      username: "myUsername"
      settings.xml: |-
        <server>
          <username>{{ .username }}</username>
          <password>{{ .password }}</password>
        </server>

Before this PR, the rendered settings.xml is:

<server>
  <username><no value></username>
  <password>plaintext password</password>
</server>

After this PR, it is:

<server>
  <username>myUsername</username>
  <password>plaintext password</password>
</server>

Root cause

In pkg/apis/sealedsecrets/v1alpha1/sealedsecret_expansion.go, Unseal() builds a data map that is used as the text/template execution context. That map was being populated only from decrypted entries of spec.encryptedData. The subsequent loop that renders each spec.template.data entry then ran with a context that did not contain any of the sibling plaintext values, so {{ .username }} resolved to a missing field and Go's text/template package emitted the literal string <no value>.

Fix

Add a small additional loop before the rendering loop that copies each spec.template.data entry into the context as a raw string, but only if the key does not already exist in the context. This means:

  • Plaintext keys defined in spec.template.data are now visible to all sibling templates as {{ .key }} variables.
  • Encrypted values always take precedence on key collision, so adding a plaintext key with the same name as an encrypted key can never silently shadow a real secret value. This is covered by a regression test.
  • Existing behavior for encrypted-only templating (e.g. the existing TestSealRoundTripTemplateData) is unchanged.

Tests

Three new tests in pkg/apis/sealedsecrets/v1alpha1/sealedsecret_test.go:

  1. TestTemplateDataPlaintextReference — pure plaintext template.data with cross-key references. Reproduces the exact symptom in the issue.
  2. TestTemplateDataMixedEncryptedAndPlaintext — mixed encrypted + plaintext template.data, with a template that references both at once.
  3. TestTemplateDataEncryptedTakesPrecedenceOverPlaintext — guards the precedence rule so future refactors cannot accidentally let a plaintext key shadow a decrypted one.

I followed TDD: tests 1 and 2 reproduce the bug and fail on main with the exact <no value> output users were seeing. Test 3 already passes on main and exists as a forward-looking guard.

Verification performed locally

  • go test ./pkg/apis/sealedsecrets/v1alpha1/... — all 11 tests in the package pass (3 new + 8 pre-existing)
  • go test ./pkg/... — every package green (controller, crypto, kubeseal, multidocyaml, pflagenv, flagenv)
  • go vet ./pkg/apis/sealedsecrets/v1alpha1/... — clean
  • gofmt -l on touched files — clean
  • Commit is signed off (DCO)

Test plan for reviewers

  • CI green
  • Apply the reproducer YAML above against a cluster running this build and confirm the rendered Secret's settings.xml contains both myUsername and the decrypted password.

When a SealedSecret defines plaintext keys alongside encrypted keys
inside `spec.template.data`, sibling templates in the same map could
not reference those plaintext keys as `{{ .key }}` variables and the
controller would render `<no value>` instead of the configured
plaintext value.

The template execution context was being built only from the
decrypted entries of `spec.encryptedData`, so any reference to a
plaintext key defined in `spec.template.data` was treated as a
missing field by `text/template`.

Pre-populate the template context with the raw values from
`spec.template.data` before rendering. Encrypted values take
precedence on key collision so a plaintext key can never silently
shadow a real secret value.

Add three regression tests covering:
- pure plaintext template.data with cross-key references
- mixed encrypted + plaintext template.data
- encryptedData precedence over a colliding template.data key

Closes bitnami-labs#1607

Signed-off-by: Hamza Younas <hamza.younas94@gmail.com>
Copilot AI review requested due to automatic review settings April 10, 2026 15:53
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes template rendering so plaintext keys in spec.template.data are available in the Go text/template execution context (alongside decrypted spec.encryptedData keys), addressing <no value> substitutions when sibling templates reference plaintext entries.

Changes:

  • Pre-populate the template execution context with raw spec.template.data values (without overriding decrypted values).
  • Add regression tests covering plaintext-only, mixed encrypted+plaintext templating, and collision precedence expectations.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
pkg/apis/sealedsecrets/v1alpha1/sealedsecret_expansion.go Populates the template rendering context with plaintext template.data entries before executing templates.
pkg/apis/sealedsecrets/v1alpha1/sealedsecret_test.go Adds regression tests for plaintext references, mixed encrypted/plaintext references, and precedence behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/apis/sealedsecrets/v1alpha1/sealedsecret_expansion.go
Comment thread pkg/apis/sealedsecrets/v1alpha1/sealedsecret_test.go
@hamza-younas94
Copy link
Copy Markdown
Author

Hi @agarcia-oss — following up on this PR. DCO is signed, the fix is additive (14 lines), and tests cover the 3 cases from the original issue. Happy to incorporate any feedback. Thanks for your time!

Previously, spec.template.data entries were written to secret.Data
unconditionally, silently overwriting decrypted encryptedData values
on key collision. Guard the assignment behind a check against
s.Spec.EncryptedData so encrypted values win in both the template
rendering context and the output Secret.

Add assertion to TestTemplateDataEncryptedTakesPrecedenceOverPlaintext
covering the output Secret's colliding key.

Signed-off-by: Hamza Younas <hamza.younas94@gmail.com>
@hamza-younas94 hamza-younas94 force-pushed the fix-template-data-plaintext-reference branch from 164b997 to fabb119 Compare April 21, 2026 08:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adding not encrypted data to a template does not work

2 participants