Skip to content

fix(discovery.relabel): Use the underlying string of secret target values#6605

Open
Churi12 wants to merge 1 commit into
grafana:mainfrom
Churi12:fix/relabel-target-secret-value
Open

fix(discovery.relabel): Use the underlying string of secret target values#6605
Churi12 wants to merge 1 commit into
grafana:mainfrom
Churi12:fix/relabel-target-secret-value

Conversation

@Churi12

@Churi12 Churi12 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Brief description of Pull Request

A secret-typed value such as local.file.<name>.content used as a target value in a discovery.relabel targets block was rendered as the Go struct's default formatting (for example {true value}) instead of the string it wraps.

Pull Request Details

Target.ConvertFrom builds the label set from a map[string]syntax.Value. It only special-cased plain strings and fell back to fmt.Sprintf("%v", ...) for every other value:

case v.IsString():
    strValue = v.Text()
case v.Reflect().CanInterface():
    strValue = fmt.Sprintf("%v", v.Reflect().Interface())

local.file exports its content as an alloytypes.OptionalSecret{IsSecret bool, Value string}, which is a capsule, not a string, so it hit the %v branch and printed as its struct representation: {false value}, or {true value} when is_secret = true. The reported workaround was to wrap it in convert.nonsensitive().

This unwraps the two secret capsule types to their underlying string before the %v fallback. The value returned is the same string the type already held, so nothing that was previously hidden becomes newly exposed: the old code already printed that string inside the {...} struct form.

Issue(s) fixed by this Pull Request

Fixes #6163

Notes to the Reviewer

I considered fixing this generically (attempting a capsule to string conversion for any capsule), but OptionalSecret.ConvertInto(*string) deliberately errors for actual secrets, so a generic path would either drop secret values or reintroduce the struct formatting. Special-casing the two secret types keeps the behavior explicit and matches how these values are already used as target values in practice.

Added TestDecodeMapWithSecretValues covering a non-secret OptionalSecret, a secret OptionalSecret, and a Secret. Confirmed the test fails on main (produces {true value}) and passes with this change.

PR Checklist

  • Documentation added
  • Tests updated
  • Config converters updated

@Churi12 Churi12 requested a review from a team as a code owner June 27, 2026 01:06
Copilot AI review requested due to automatic review settings June 27, 2026 01:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes discovery.relabel target decoding so secret-capsule values (notably local.file.<name>.content) are converted to their underlying string instead of Go’s default struct formatting (e.g., {true value}), aligning runtime behavior with user expectations and the referenced bug report.

Changes:

  • Special-case alloytypes.Secret and alloytypes.OptionalSecret in Target.ConvertFrom to unwrap to the underlying string before the %v fallback.
  • Add a regression test that covers non-secret OptionalSecret, secret OptionalSecret, and Secret decoding into a Target.

Reviewed changes

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

File Description
internal/component/discovery/target.go Unwraps secret capsule types to their wrapped string when converting map[string]syntax.Value into discovery target label values.
internal/component/discovery/target_test.go Adds regression coverage ensuring secret capsule target values decode to the expected string form.

@kalleep kalleep left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey it think it's and oversight that we even convert secrets here in the first place, even though the value is not usable.

IMO we should not convert Secrets at all and only convert optional secrets if IsSecretm == false.

…g them

When a value such as local.file.<name>.content was used as a target value in
a discovery.relabel targets block, the capsule was rendered via the Go structs
default formatting (for example "{false value}") instead of the string it wraps.

Target.ConvertFrom only special-cased plain strings and fell back to
fmt.Sprintf("%v", ...) for everything else, so the OptionalSecret and Secret
capsule types exported by components like local.file printed as their struct
representation.

A secret is never usable as a target value, so this now unwraps a non-secret
OptionalSecret to its underlying string and rejects a Secret or a secret
OptionalSecret with a clear error rather than exposing its contents in a label.

Fixes grafana#6163
@Churi12 Churi12 force-pushed the fix/relabel-target-secret-value branch from 52a9b2b to 7da2b4f Compare June 29, 2026 12:58
@Churi12

Churi12 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

Good call, you are right. Converting a secret here only produced a usable label by stripping the protection the type is meant to carry, and the capsule contract already forbids secret to string conversion everywhere else.

I pushed a new version: a non-secret OptionalSecret (the common local.file case with is_secret = false) is unwrapped to its underlying string, and a Secret or a secret OptionalSecret is now rejected with a clear error instead of being unwrapped. That keeps the struct formatting bug from #6163 fixed for the legitimate case while never leaking a secret into a target label.

Updated the test accordingly: the non-secret case still asserts the string, and the two secret cases now assert an error.

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.

discovery.relabel's targets doesn't parse local.file.target.content correctly

3 participants