github.com/influxdata/telegraf@v1.30.3/config/secret_test.go (about)

     1  package config
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"testing"
     9  
    10  	"github.com/awnumar/memguard"
    11  	"github.com/stretchr/testify/require"
    12  	"github.com/stretchr/testify/suite"
    13  
    14  	"github.com/influxdata/telegraf"
    15  	"github.com/influxdata/telegraf/plugins/inputs"
    16  	"github.com/influxdata/telegraf/plugins/secretstores"
    17  )
    18  
    19  func TestSecretConstantManually(t *testing.T) {
    20  	mysecret := "a wonderful test"
    21  	s := NewSecret([]byte(mysecret))
    22  	defer s.Destroy()
    23  	retrieved, err := s.Get()
    24  	require.NoError(t, err)
    25  	defer retrieved.Destroy()
    26  	require.EqualValues(t, mysecret, retrieved.TemporaryString())
    27  }
    28  
    29  func TestLinking(t *testing.T) {
    30  	mysecret := "a @{referenced:secret}"
    31  	resolvers := map[string]telegraf.ResolveFunc{
    32  		"@{referenced:secret}": func() ([]byte, bool, error) {
    33  			return []byte("resolved secret"), false, nil
    34  		},
    35  	}
    36  	s := NewSecret([]byte(mysecret))
    37  	defer s.Destroy()
    38  	require.NoError(t, s.Link(resolvers))
    39  	retrieved, err := s.Get()
    40  	require.NoError(t, err)
    41  	defer retrieved.Destroy()
    42  	require.EqualValues(t, "a resolved secret", retrieved.TemporaryString())
    43  }
    44  
    45  func TestLinkingResolverError(t *testing.T) {
    46  	mysecret := "a @{referenced:secret}"
    47  	resolvers := map[string]telegraf.ResolveFunc{
    48  		"@{referenced:secret}": func() ([]byte, bool, error) {
    49  			return nil, false, errors.New("broken")
    50  		},
    51  	}
    52  	s := NewSecret([]byte(mysecret))
    53  	defer s.Destroy()
    54  	expected := `linking secrets failed: resolving "@{referenced:secret}" failed: broken`
    55  	require.EqualError(t, s.Link(resolvers), expected)
    56  }
    57  
    58  func TestGettingUnlinked(t *testing.T) {
    59  	mysecret := "a @{referenced:secret}"
    60  	s := NewSecret([]byte(mysecret))
    61  	defer s.Destroy()
    62  	_, err := s.Get()
    63  	require.ErrorContains(t, err, "unlinked parts in secret")
    64  }
    65  
    66  func TestGettingMissingResolver(t *testing.T) {
    67  	mysecret := "a @{referenced:secret}"
    68  	s := NewSecret([]byte(mysecret))
    69  	defer s.Destroy()
    70  	s.unlinked = []string{}
    71  	s.resolvers = map[string]telegraf.ResolveFunc{
    72  		"@{a:dummy}": func() ([]byte, bool, error) {
    73  			return nil, false, nil
    74  		},
    75  	}
    76  	_, err := s.Get()
    77  	expected := `replacing secrets failed: no resolver for "@{referenced:secret}"`
    78  	require.EqualError(t, err, expected)
    79  }
    80  
    81  func TestGettingResolverError(t *testing.T) {
    82  	mysecret := "a @{referenced:secret}"
    83  	s := NewSecret([]byte(mysecret))
    84  	defer s.Destroy()
    85  	s.unlinked = []string{}
    86  	s.resolvers = map[string]telegraf.ResolveFunc{
    87  		"@{referenced:secret}": func() ([]byte, bool, error) {
    88  			return nil, false, errors.New("broken")
    89  		},
    90  	}
    91  	_, err := s.Get()
    92  	expected := `replacing secrets failed: resolving "@{referenced:secret}" failed: broken`
    93  	require.EqualError(t, err, expected)
    94  }
    95  
    96  func TestUninitializedEnclave(t *testing.T) {
    97  	s := Secret{}
    98  	defer s.Destroy()
    99  	require.NoError(t, s.Link(map[string]telegraf.ResolveFunc{}))
   100  	retrieved, err := s.Get()
   101  	require.NoError(t, err)
   102  	defer retrieved.Destroy()
   103  	require.Empty(t, retrieved.Bytes())
   104  }
   105  
   106  func TestEnclaveOpenError(t *testing.T) {
   107  	mysecret := "a @{referenced:secret}"
   108  	s := NewSecret([]byte(mysecret))
   109  	defer s.Destroy()
   110  	memguard.Purge()
   111  	err := s.Link(map[string]telegraf.ResolveFunc{})
   112  	require.ErrorContains(t, err, "opening enclave failed")
   113  
   114  	s.unlinked = []string{}
   115  	_, err = s.Get()
   116  	require.ErrorContains(t, err, "opening enclave failed")
   117  }
   118  
   119  func TestMissingResolver(t *testing.T) {
   120  	mysecret := "a @{referenced:secret}"
   121  	s := NewSecret([]byte(mysecret))
   122  	defer s.Destroy()
   123  	err := s.Link(map[string]telegraf.ResolveFunc{})
   124  	require.ErrorContains(t, err, "linking secrets failed: unlinked part")
   125  }
   126  
   127  func TestSecretConstant(t *testing.T) {
   128  	tests := []struct {
   129  		name     string
   130  		cfg      []byte
   131  		expected string
   132  	}{
   133  		{
   134  			name: "simple string",
   135  			cfg: []byte(`
   136  				[[inputs.mockup]]
   137  				  secret = "a secret"
   138  			`),
   139  			expected: "a secret",
   140  		},
   141  		{
   142  			name: "mail address",
   143  			cfg: []byte(`
   144  				[[inputs.mockup]]
   145  				  secret = "someone@mock.org"
   146  			`),
   147  			expected: "someone@mock.org",
   148  		},
   149  	}
   150  
   151  	for _, tt := range tests {
   152  		t.Run(tt.name, func(t *testing.T) {
   153  			c := NewConfig()
   154  			require.NoError(t, c.LoadConfigData(tt.cfg))
   155  			require.Len(t, c.Inputs, 1)
   156  
   157  			// Create a mockup secretstore
   158  			store := &MockupSecretStore{
   159  				Secrets: map[string][]byte{"mock": []byte("fail")},
   160  			}
   161  			require.NoError(t, store.Init())
   162  			c.SecretStores["mock"] = store
   163  			require.NoError(t, c.LinkSecrets())
   164  
   165  			plugin := c.Inputs[0].Input.(*MockupSecretPlugin)
   166  			secret, err := plugin.Secret.Get()
   167  			require.NoError(t, err)
   168  			defer secret.Destroy()
   169  
   170  			require.EqualValues(t, tt.expected, secret.TemporaryString())
   171  		})
   172  	}
   173  }
   174  
   175  func TestSecretUnquote(t *testing.T) {
   176  	tests := []struct {
   177  		name string
   178  		cfg  []byte
   179  	}{
   180  		{
   181  			name: "single quotes",
   182  			cfg: []byte(`
   183  				[[inputs.mockup]]
   184  					secret = 'a secret'
   185  					expected = 'a secret'
   186  			`),
   187  		},
   188  		{
   189  			name: "double quotes",
   190  			cfg: []byte(`
   191  				[[inputs.mockup]]
   192  					secret = "a secret"
   193  					expected = "a secret"
   194  			`),
   195  		},
   196  		{
   197  			name: "triple single quotes",
   198  			cfg: []byte(`
   199  				[[inputs.mockup]]
   200  					secret = '''a secret'''
   201  					expected = '''a secret'''
   202  			`),
   203  		},
   204  		{
   205  			name: "triple double quotes",
   206  			cfg: []byte(`
   207  				[[inputs.mockup]]
   208  					secret = """a secret"""
   209  					expected = """a secret"""
   210  			`),
   211  		},
   212  		{
   213  			name: "escaped double quotes",
   214  			cfg: []byte(`
   215  				[[inputs.mockup]]
   216  					secret = "\"a secret\""
   217  					expected = "\"a secret\""
   218  			`),
   219  		},
   220  		{
   221  			name: "mix double-single quotes (single)",
   222  			cfg: []byte(`
   223  				[[inputs.mockup]]
   224  					secret = "'a secret'"
   225  					expected = "'a secret'"
   226  			`),
   227  		},
   228  		{
   229  			name: "mix single-double quotes (single)",
   230  			cfg: []byte(`
   231  				[[inputs.mockup]]
   232  				secret = '"a secret"'
   233  				expected = '"a secret"'
   234  			`),
   235  		},
   236  		{
   237  			name: "mix double-single quotes (triple-single)",
   238  			cfg: []byte(`
   239  				[[inputs.mockup]]
   240  					secret = """'a secret'"""
   241  					expected = """'a secret'"""
   242  			`),
   243  		},
   244  		{
   245  			name: "mix single-double quotes (triple-single)",
   246  			cfg: []byte(`
   247  				[[inputs.mockup]]
   248  					secret = '''"a secret"'''
   249  					expected = '''"a secret"'''
   250  			`),
   251  		},
   252  		{
   253  			name: "mix double-single quotes (triple)",
   254  			cfg: []byte(`
   255  				[[inputs.mockup]]
   256  					secret = """'''a secret'''"""
   257  					expected = """'''a secret'''"""
   258  			`),
   259  		},
   260  		{
   261  			name: "mix single-double quotes (triple)",
   262  			cfg: []byte(`
   263  				[[inputs.mockup]]
   264  					secret = '''"""a secret"""'''
   265  					expected = '''"""a secret"""'''
   266  			`),
   267  		},
   268  		{
   269  			name: "single quotes with backslashes",
   270  			cfg: []byte(`
   271  				[[inputs.mockup]]
   272  					secret = 'Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;'
   273  					expected = 'Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;'
   274  			`),
   275  		},
   276  		{
   277  			name: "double quotes with backslashes",
   278  			cfg: []byte(`
   279  				[[inputs.mockup]]
   280  					secret = "Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;"
   281  					expected = "Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;"
   282  			`),
   283  		},
   284  		{
   285  			name: "triple single quotes with backslashes",
   286  			cfg: []byte(`
   287  				[[inputs.mockup]]
   288  					secret = '''Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;'''
   289  					expected = '''Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;'''
   290  			`),
   291  		},
   292  		{
   293  			name: "triple double quotes with backslashes",
   294  			cfg: []byte(`
   295  				[[inputs.mockup]]
   296  					secret = """Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;"""
   297  					expected = """Server=SQLTELEGRAF\\SQL2022;app name=telegraf;log=1;"""
   298  			`),
   299  		},
   300  	}
   301  
   302  	for _, tt := range tests {
   303  		t.Run(tt.name, func(t *testing.T) {
   304  			c := NewConfig()
   305  			require.NoError(t, c.LoadConfigData(tt.cfg))
   306  			require.Len(t, c.Inputs, 1)
   307  
   308  			// Create a mockup secretstore
   309  			store := &MockupSecretStore{
   310  				Secrets: map[string][]byte{},
   311  			}
   312  			require.NoError(t, store.Init())
   313  			c.SecretStores["mock"] = store
   314  			require.NoError(t, c.LinkSecrets())
   315  
   316  			plugin := c.Inputs[0].Input.(*MockupSecretPlugin)
   317  			secret, err := plugin.Secret.Get()
   318  			require.NoError(t, err)
   319  			defer secret.Destroy()
   320  
   321  			require.EqualValues(t, plugin.Expected, secret.TemporaryString())
   322  		})
   323  	}
   324  }
   325  
   326  func TestSecretEnvironmentVariable(t *testing.T) {
   327  	cfg := []byte(`
   328  [[inputs.mockup]]
   329  	secret = "$SOME_ENV_SECRET"
   330  `)
   331  	t.Setenv("SOME_ENV_SECRET", "an env secret")
   332  
   333  	c := NewConfig()
   334  	err := c.LoadConfigData(cfg)
   335  	require.NoError(t, err)
   336  	require.Len(t, c.Inputs, 1)
   337  
   338  	// Create a mockup secretstore
   339  	store := &MockupSecretStore{
   340  		Secrets: map[string][]byte{},
   341  	}
   342  	require.NoError(t, store.Init())
   343  	c.SecretStores["mock"] = store
   344  	require.NoError(t, c.LinkSecrets())
   345  
   346  	plugin := c.Inputs[0].Input.(*MockupSecretPlugin)
   347  	secret, err := plugin.Secret.Get()
   348  	require.NoError(t, err)
   349  	defer secret.Destroy()
   350  
   351  	require.EqualValues(t, "an env secret", secret.TemporaryString())
   352  }
   353  
   354  func TestSecretCount(t *testing.T) {
   355  	secretCount.Store(0)
   356  	cfg := []byte(`
   357  [[inputs.mockup]]
   358  
   359  [[inputs.mockup]]
   360    secret = "a secret"
   361  
   362  [[inputs.mockup]]
   363    secret = "another secret"
   364  `)
   365  
   366  	c := NewConfig()
   367  	require.NoError(t, c.LoadConfigData(cfg))
   368  	require.Len(t, c.Inputs, 3)
   369  	require.Equal(t, int64(2), secretCount.Load())
   370  
   371  	// Remove all secrets and check
   372  	for _, ri := range c.Inputs {
   373  		input := ri.Input.(*MockupSecretPlugin)
   374  		input.Secret.Destroy()
   375  	}
   376  	require.Equal(t, int64(0), secretCount.Load())
   377  }
   378  
   379  func TestSecretStoreStatic(t *testing.T) {
   380  	cfg := []byte(
   381  		`
   382  [[inputs.mockup]]
   383  	secret = "@{mock:secret1}"
   384  [[inputs.mockup]]
   385  	secret = "@{mock:secret2}"
   386  [[inputs.mockup]]
   387  	secret = "@{mock:a_strange_secret}"
   388  [[inputs.mockup]]
   389  	secret = "@{mock:a_weird_secret}"
   390  `)
   391  
   392  	c := NewConfig()
   393  	err := c.LoadConfigData(cfg)
   394  	require.NoError(t, err)
   395  	require.Len(t, c.Inputs, 4)
   396  
   397  	// Create a mockup secretstore
   398  	store := &MockupSecretStore{
   399  		Secrets: map[string][]byte{
   400  			"secret1":          []byte("Ood Bnar"),
   401  			"secret2":          []byte("Thon"),
   402  			"a_strange_secret": []byte("Obi-Wan Kenobi"),
   403  			"a_weird_secret":   []byte("Arca Jeth"),
   404  		},
   405  	}
   406  	require.NoError(t, store.Init())
   407  	c.SecretStores["mock"] = store
   408  	require.NoError(t, c.LinkSecrets())
   409  
   410  	expected := []string{"Ood Bnar", "Thon", "Obi-Wan Kenobi", "Arca Jeth"}
   411  	for i, input := range c.Inputs {
   412  		plugin := input.Input.(*MockupSecretPlugin)
   413  		secret, err := plugin.Secret.Get()
   414  		require.NoError(t, err)
   415  		require.EqualValues(t, expected[i], secret.TemporaryString())
   416  		secret.Destroy()
   417  	}
   418  }
   419  
   420  func TestSecretStoreInvalidKeys(t *testing.T) {
   421  	cfg := []byte(
   422  		`
   423  [[inputs.mockup]]
   424  	secret = "@{mock:}"
   425  [[inputs.mockup]]
   426  	secret = "@{mock:wild?%go}"
   427  [[inputs.mockup]]
   428  	secret = "@{mock:a-strange-secret}"
   429  [[inputs.mockup]]
   430  	secret = "@{mock:a weird secret}"
   431  `)
   432  
   433  	c := NewConfig()
   434  	err := c.LoadConfigData(cfg)
   435  	require.NoError(t, err)
   436  	require.Len(t, c.Inputs, 4)
   437  
   438  	// Create a mockup secretstore
   439  	store := &MockupSecretStore{
   440  		Secrets: map[string][]byte{
   441  			"":                 []byte("Ood Bnar"),
   442  			"wild?%go":         []byte("Thon"),
   443  			"a-strange-secret": []byte("Obi-Wan Kenobi"),
   444  			"a weird secret":   []byte("Arca Jeth"),
   445  		},
   446  	}
   447  	require.NoError(t, store.Init())
   448  	c.SecretStores["mock"] = store
   449  	require.NoError(t, c.LinkSecrets())
   450  
   451  	expected := []string{
   452  		"@{mock:}",
   453  		"@{mock:wild?%go}",
   454  		"@{mock:a-strange-secret}",
   455  		"@{mock:a weird secret}",
   456  	}
   457  	for i, input := range c.Inputs {
   458  		plugin := input.Input.(*MockupSecretPlugin)
   459  		secret, err := plugin.Secret.Get()
   460  		require.NoError(t, err)
   461  		require.EqualValues(t, expected[i], secret.TemporaryString())
   462  		secret.Destroy()
   463  	}
   464  }
   465  
   466  func TestSecretStoreDeclarationMissingID(t *testing.T) {
   467  	defer func() { unlinkedSecrets = make([]*Secret, 0) }()
   468  
   469  	cfg := []byte(`[[secretstores.mockup]]`)
   470  
   471  	c := NewConfig()
   472  	err := c.LoadConfigData(cfg)
   473  	require.ErrorContains(t, err, `error parsing mockup, "mockup" secret-store without ID`)
   474  }
   475  
   476  func TestSecretStoreDeclarationInvalidID(t *testing.T) {
   477  	defer func() { unlinkedSecrets = make([]*Secret, 0) }()
   478  
   479  	invalidIDs := []string{"foo.bar", "dummy-123", "test!", "wohoo+"}
   480  	tmpl := `
   481    [[secretstores.mockup]]
   482      id = %q
   483  `
   484  	for _, id := range invalidIDs {
   485  		t.Run(id, func(t *testing.T) {
   486  			cfg := []byte(fmt.Sprintf(tmpl, id))
   487  			c := NewConfig()
   488  			err := c.LoadConfigData(cfg)
   489  			require.ErrorContains(t, err, `error parsing mockup, invalid secret-store ID`)
   490  		})
   491  	}
   492  }
   493  
   494  func TestSecretStoreDeclarationValidID(t *testing.T) {
   495  	defer func() { unlinkedSecrets = make([]*Secret, 0) }()
   496  
   497  	validIDs := []string{"foobar", "dummy123", "test_id", "W0Hoo_lala123"}
   498  	tmpl := `
   499    [[secretstores.mockup]]
   500      id = %q
   501  `
   502  	for _, id := range validIDs {
   503  		t.Run(id, func(t *testing.T) {
   504  			cfg := []byte(fmt.Sprintf(tmpl, id))
   505  			c := NewConfig()
   506  			err := c.LoadConfigData(cfg)
   507  			require.NoError(t, err)
   508  		})
   509  	}
   510  }
   511  
   512  type SecretImplTestSuite struct {
   513  	suite.Suite
   514  	protected bool
   515  }
   516  
   517  func (tsuite *SecretImplTestSuite) SetupSuite() {
   518  	if tsuite.protected {
   519  		EnableSecretProtection()
   520  	} else {
   521  		DisableSecretProtection()
   522  	}
   523  }
   524  
   525  func (*SecretImplTestSuite) TearDownSuite() {
   526  	EnableSecretProtection()
   527  }
   528  
   529  func (*SecretImplTestSuite) TearDownTest() {
   530  	unlinkedSecrets = make([]*Secret, 0)
   531  }
   532  
   533  func (tsuite *SecretImplTestSuite) TestSecretEqualTo() {
   534  	t := tsuite.T()
   535  	mysecret := "a wonderful test"
   536  	s := NewSecret([]byte(mysecret))
   537  	defer s.Destroy()
   538  
   539  	equal, err := s.EqualTo([]byte(mysecret))
   540  	require.NoError(t, err)
   541  	require.True(t, equal)
   542  
   543  	equal, err = s.EqualTo([]byte("some random text"))
   544  	require.NoError(t, err)
   545  	require.False(t, equal)
   546  }
   547  
   548  func (tsuite *SecretImplTestSuite) TestSecretStoreInvalidReference() {
   549  	t := tsuite.T()
   550  
   551  	cfg := []byte(
   552  		`
   553  [[inputs.mockup]]
   554  	secret = "@{mock:test}"
   555  `)
   556  
   557  	c := NewConfig()
   558  	require.NoError(t, c.LoadConfigData(cfg))
   559  	require.Len(t, c.Inputs, 1)
   560  
   561  	// Create a mockup secretstore
   562  	store := &MockupSecretStore{
   563  		Secrets: map[string][]byte{"test": []byte("Arca Jeth")},
   564  	}
   565  	require.NoError(t, store.Init())
   566  	c.SecretStores["foo"] = store
   567  	err := c.LinkSecrets()
   568  	require.EqualError(t, err, `unknown secret-store for "@{mock:test}"`)
   569  
   570  	for _, input := range c.Inputs {
   571  		plugin := input.Input.(*MockupSecretPlugin)
   572  		secret, err := plugin.Secret.Get()
   573  		require.EqualError(t, err, `unlinked parts in secret: @{mock:test}`)
   574  		require.Empty(t, secret)
   575  	}
   576  }
   577  
   578  func (tsuite *SecretImplTestSuite) TestSecretStoreStaticChanging() {
   579  	t := tsuite.T()
   580  
   581  	cfg := []byte(
   582  		`
   583  [[inputs.mockup]]
   584  	secret = "@{mock:secret}"
   585  `)
   586  
   587  	c := NewConfig()
   588  	err := c.LoadConfigData(cfg)
   589  	require.NoError(t, err)
   590  	require.Len(t, c.Inputs, 1)
   591  
   592  	// Create a mockup secretstore
   593  	store := &MockupSecretStore{
   594  		Secrets: map[string][]byte{"secret": []byte("Ood Bnar")},
   595  		Dynamic: false,
   596  	}
   597  	require.NoError(t, store.Init())
   598  	c.SecretStores["mock"] = store
   599  	require.NoError(t, c.LinkSecrets())
   600  
   601  	sequence := []string{"Ood Bnar", "Thon", "Obi-Wan Kenobi", "Arca Jeth"}
   602  	plugin := c.Inputs[0].Input.(*MockupSecretPlugin)
   603  	secret, err := plugin.Secret.Get()
   604  	require.NoError(t, err)
   605  	defer secret.Destroy()
   606  
   607  	require.EqualValues(t, "Ood Bnar", secret.TemporaryString())
   608  
   609  	for _, v := range sequence {
   610  		store.Secrets["secret"] = []byte(v)
   611  		secret, err := plugin.Secret.Get()
   612  		require.NoError(t, err)
   613  
   614  		// The secret should not change as the store is marked non-dyamic!
   615  		require.EqualValues(t, "Ood Bnar", secret.TemporaryString())
   616  		secret.Destroy()
   617  	}
   618  }
   619  
   620  func (tsuite *SecretImplTestSuite) TestSecretStoreDynamic() {
   621  	t := tsuite.T()
   622  
   623  	cfg := []byte(
   624  		`
   625  [[inputs.mockup]]
   626  	secret = "@{mock:secret}"
   627  `)
   628  
   629  	c := NewConfig()
   630  	err := c.LoadConfigData(cfg)
   631  	require.NoError(t, err)
   632  	require.Len(t, c.Inputs, 1)
   633  
   634  	// Create a mockup secretstore
   635  	store := &MockupSecretStore{
   636  		Secrets: map[string][]byte{"secret": []byte("Ood Bnar")},
   637  		Dynamic: true,
   638  	}
   639  	require.NoError(t, store.Init())
   640  	c.SecretStores["mock"] = store
   641  	require.NoError(t, c.LinkSecrets())
   642  
   643  	sequence := []string{"Ood Bnar", "Thon", "Obi-Wan Kenobi", "Arca Jeth"}
   644  	plugin := c.Inputs[0].Input.(*MockupSecretPlugin)
   645  	for _, v := range sequence {
   646  		store.Secrets["secret"] = []byte(v)
   647  		secret, err := plugin.Secret.Get()
   648  		require.NoError(t, err)
   649  
   650  		// The secret should not change as the store is marked non-dynamic!
   651  		require.EqualValues(t, v, secret.TemporaryString())
   652  		secret.Destroy()
   653  	}
   654  }
   655  
   656  func (tsuite *SecretImplTestSuite) TestSecretSet() {
   657  	t := tsuite.T()
   658  
   659  	cfg := []byte(`
   660        [[inputs.mockup]]
   661  	    secret = "a secret"
   662  	`)
   663  	c := NewConfig()
   664  	require.NoError(t, c.LoadConfigData(cfg))
   665  	require.Len(t, c.Inputs, 1)
   666  	require.NoError(t, c.LinkSecrets())
   667  
   668  	plugin := c.Inputs[0].Input.(*MockupSecretPlugin)
   669  
   670  	secret, err := plugin.Secret.Get()
   671  	require.NoError(t, err)
   672  	defer secret.Destroy()
   673  	require.EqualValues(t, "a secret", secret.TemporaryString())
   674  
   675  	require.NoError(t, plugin.Secret.Set([]byte("another secret")))
   676  	newsecret, err := plugin.Secret.Get()
   677  	require.NoError(t, err)
   678  	defer newsecret.Destroy()
   679  	require.EqualValues(t, "another secret", newsecret.TemporaryString())
   680  }
   681  
   682  func (tsuite *SecretImplTestSuite) TestSecretSetResolve() {
   683  	t := tsuite.T()
   684  	cfg := []byte(`
   685        [[inputs.mockup]]
   686  	    secret = "@{mock:secret}"
   687  	`)
   688  	c := NewConfig()
   689  	require.NoError(t, c.LoadConfigData(cfg))
   690  	require.Len(t, c.Inputs, 1)
   691  
   692  	// Create a mockup secretstore
   693  	store := &MockupSecretStore{
   694  		Secrets: map[string][]byte{"secret": []byte("Ood Bnar")},
   695  		Dynamic: true,
   696  	}
   697  	require.NoError(t, store.Init())
   698  	c.SecretStores["mock"] = store
   699  	require.NoError(t, c.LinkSecrets())
   700  
   701  	plugin := c.Inputs[0].Input.(*MockupSecretPlugin)
   702  
   703  	secret, err := plugin.Secret.Get()
   704  	require.NoError(t, err)
   705  	defer secret.Destroy()
   706  	require.EqualValues(t, "Ood Bnar", secret.TemporaryString())
   707  
   708  	require.NoError(t, plugin.Secret.Set([]byte("@{mock:secret} is cool")))
   709  	newsecret, err := plugin.Secret.Get()
   710  	require.NoError(t, err)
   711  	defer newsecret.Destroy()
   712  	require.EqualValues(t, "Ood Bnar is cool", newsecret.TemporaryString())
   713  }
   714  
   715  func (tsuite *SecretImplTestSuite) TestSecretSetResolveInvalid() {
   716  	t := tsuite.T()
   717  
   718  	cfg := []byte(`
   719        [[inputs.mockup]]
   720  	    secret = "@{mock:secret}"
   721  	`)
   722  	c := NewConfig()
   723  	require.NoError(t, c.LoadConfigData(cfg))
   724  	require.Len(t, c.Inputs, 1)
   725  
   726  	// Create a mockup secretstore
   727  	store := &MockupSecretStore{
   728  		Secrets: map[string][]byte{"secret": []byte("Ood Bnar")},
   729  		Dynamic: true,
   730  	}
   731  	require.NoError(t, store.Init())
   732  	c.SecretStores["mock"] = store
   733  	require.NoError(t, c.LinkSecrets())
   734  
   735  	plugin := c.Inputs[0].Input.(*MockupSecretPlugin)
   736  
   737  	secret, err := plugin.Secret.Get()
   738  	require.NoError(t, err)
   739  	defer secret.Destroy()
   740  	require.EqualValues(t, "Ood Bnar", secret.TemporaryString())
   741  
   742  	err = plugin.Secret.Set([]byte("@{mock:another_secret}"))
   743  	require.ErrorContains(t, err, `linking new secrets failed: unlinked part "@{mock:another_secret}"`)
   744  }
   745  
   746  func (tsuite *SecretImplTestSuite) TestSecretInvalidWarn() {
   747  	t := tsuite.T()
   748  
   749  	// Intercept the log output
   750  	var buf bytes.Buffer
   751  	backup := log.Writer()
   752  	log.SetOutput(&buf)
   753  	defer log.SetOutput(backup)
   754  
   755  	cfg := []byte(`
   756        [[inputs.mockup]]
   757  	    secret = "server=a user=@{mock:secret-with-invalid-chars} pass=@{mock:secret_pass}"
   758  	`)
   759  	c := NewConfig()
   760  	require.NoError(t, c.LoadConfigData(cfg))
   761  	require.Len(t, c.Inputs, 1)
   762  
   763  	require.Contains(t, buf.String(), `W! Secret "@{mock:secret-with-invalid-chars}" contains invalid character(s)`)
   764  	require.NotContains(t, buf.String(), "@{mock:secret_pass}")
   765  }
   766  
   767  func TestSecretImplUnprotected(t *testing.T) {
   768  	impl := &unprotectedSecretImpl{}
   769  	container := impl.Container([]byte("foobar"))
   770  	require.NotNil(t, container)
   771  	c, ok := container.(*unprotectedSecretContainer)
   772  	require.True(t, ok)
   773  	require.Equal(t, "foobar", string(c.buf.content))
   774  	buf, err := container.Buffer()
   775  	require.NoError(t, err)
   776  	require.NotNil(t, buf)
   777  	require.Equal(t, []byte("foobar"), buf.Bytes())
   778  	require.Equal(t, "foobar", buf.TemporaryString())
   779  	require.Equal(t, "foobar", buf.String())
   780  }
   781  
   782  func TestSecretImplTestSuiteUnprotected(t *testing.T) {
   783  	suite.Run(t, &SecretImplTestSuite{protected: false})
   784  }
   785  
   786  func TestSecretImplTestSuiteProtected(t *testing.T) {
   787  	suite.Run(t, &SecretImplTestSuite{protected: true})
   788  }
   789  
   790  /*** Mockup (input) plugin for testing to avoid cyclic dependencies ***/
   791  type MockupSecretPlugin struct {
   792  	Secret   Secret `toml:"secret"`
   793  	Expected string `toml:"expected"`
   794  }
   795  
   796  func (*MockupSecretPlugin) SampleConfig() string                { return "Mockup test secret plugin" }
   797  func (*MockupSecretPlugin) Gather(_ telegraf.Accumulator) error { return nil }
   798  
   799  type MockupSecretStore struct {
   800  	Secrets map[string][]byte
   801  	Dynamic bool
   802  }
   803  
   804  func (s *MockupSecretStore) Init() error {
   805  	return nil
   806  }
   807  func (*MockupSecretStore) SampleConfig() string {
   808  	return "Mockup test secret plugin"
   809  }
   810  
   811  func (s *MockupSecretStore) Get(key string) ([]byte, error) {
   812  	v, found := s.Secrets[key]
   813  	if !found {
   814  		return nil, errors.New("not found")
   815  	}
   816  	return v, nil
   817  }
   818  
   819  func (s *MockupSecretStore) Set(key, value string) error {
   820  	s.Secrets[key] = []byte(value)
   821  	return nil
   822  }
   823  
   824  func (s *MockupSecretStore) List() ([]string, error) {
   825  	keys := make([]string, 0, len(s.Secrets))
   826  	for k := range s.Secrets {
   827  		keys = append(keys, k)
   828  	}
   829  	return keys, nil
   830  }
   831  func (s *MockupSecretStore) GetResolver(key string) (telegraf.ResolveFunc, error) {
   832  	return func() ([]byte, bool, error) {
   833  		v, err := s.Get(key)
   834  		return v, s.Dynamic, err
   835  	}, nil
   836  }
   837  
   838  // Register the mockup plugin on loading
   839  func init() {
   840  	// Register the mockup input plugin for the required names
   841  	inputs.Add("mockup", func() telegraf.Input { return &MockupSecretPlugin{} })
   842  	secretstores.Add("mockup", func(string) telegraf.SecretStore {
   843  		return &MockupSecretStore{}
   844  	})
   845  }